Compare commits

..

1 Commits

Author SHA1 Message Date
Horatiu Muresan a0af2de33c fix(native-notifications) Show notifications on max 2 lines 2022-05-16 15:12:33 +03:00
2828 changed files with 124656 additions and 140294 deletions

View File

@ -1,15 +0,0 @@
{
"name": "Jitsi Meet Dev Container",
"image": "mcr.microsoft.com/devcontainers/universal:2",
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "16"
}
},
"hostRequirements": {
"cpus": 4,
"memory": "8gb",
"storage": "32gb"
},
"postCreateCommand": "bash -i -c 'nvm use && npm install && cp tsconfig.web.json tsconfig.json'"
}

View File

@ -1,6 +1,5 @@
module.exports = { module.exports = {
'extends': [ 'extends': [
'@jitsi/eslint-config' '@jitsi/eslint-config'
], ]
'ignorePatterns': [ '*.d.ts' ]
}; };

View File

@ -1,25 +0,0 @@
name: Lua CI
on: [pull_request]
jobs:
luacheck:
name: Luacheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install luarocks
run: sudo apt-get --install-recommends -y install luarocks
- name: Install luacheck
run: sudo luarocks install luacheck
- name: Check lua codes
run: |
set -o pipefail && luacheck . | awk -F: '
{
print $0
printf "::warning file=%s,line=%s,col=%s::%s\n", $1, $2, $3, $4
}
'

View File

@ -3,15 +3,14 @@ name: Simple CI
on: [pull_request] on: [pull_request]
jobs: jobs:
lint: run-ci:
name: Lint name: Build Frontend
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- uses: actions/setup-node@v3 - uses: actions/setup-node@v1
with: with:
node-version: 16 node-version: '16.x'
cache: 'npm'
- run: npm install - run: npm install
- name: Check git status - name: Check git status
run: git status run: git status
@ -19,26 +18,6 @@ jobs:
run: npm run lang-sort run: npm run lang-sort
- name: Check if the git repository is clean - name: Check if the git repository is clean
run: $(exit $(git status --porcelain --untracked-files=no | head -255 | wc -l)) || (echo "Dirty git tree"; git diff; exit 1) run: $(exit $(git status --porcelain --untracked-files=no | head -255 | wc -l)) || (echo "Dirty git tree"; git diff; exit 1)
- run: npm run lint:ci - run: npm run lint
linux-build: - run: for file in lang/*.json; do npx --yes jsonlint -q $file || exit 1; done
name: Build Frontend (Linux)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
cache: 'npm'
- run: npm install
- run: make
macos-ci:
name: Build Frontend (macOS)
runs-on: macOS-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
cache: 'npm'
- run: npm install
- run: make - run: make

1
.gitignore vendored
View File

@ -92,4 +92,3 @@ twa/*.apk
twa/*.aab twa/*.aab
twa/assetlinks.json twa/assetlinks.json
tsconfig.json

View File

@ -1,8 +0,0 @@
global = false
unused = false
redefined = false
ignore = { "581" }
max_line_length = false
color = false
formatter = "plain"
quiet = 1

View File

@ -141,7 +141,7 @@ react/features/sample/
``` ```
The middleware must be imported in `react/features/app/` specifically The middleware must be imported in `react/features/app/` specifically
in `middlewares.any.ts`, `middlewares.native.ts` or `middlewares.web.ts` where appropriate. in `middlewares.any`, `middlewares.native.js` or `middlewares.web.js` where appropriate.
Likewise for the reducer. Likewise for the reducer.
An `index.js` file must not be provided for exporting actions, action types and An `index.js` file must not be provided for exporting actions, action types and

View File

@ -4,9 +4,7 @@ DEPLOY_DIR = libs
LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet
OLM_DIR = node_modules/@matrix-org/olm OLM_DIR = node_modules/@matrix-org/olm
TF_WASM_DIR = node_modules/@tensorflow/tfjs-backend-wasm/dist/ TF_WASM_DIR = node_modules/@tensorflow/tfjs-backend-wasm/dist/
RNNOISE_WASM_DIR = node_modules/@jitsi/rnnoise-wasm/dist RNNOISE_WASM_DIR = node_modules/rnnoise-wasm/dist
EXCALIDRAW_DIR = node_modules/@jitsi/excalidraw/dist/excalidraw-assets
EXCALIDRAW_DIR_DEV = node_modules/@jitsi/excalidraw/dist/excalidraw-assets-dev
TFLITE_WASM = react/features/stream-effects/virtual-background/vendor/tflite TFLITE_WASM = react/features/stream-effects/virtual-background/vendor/tflite
MEET_MODELS_DIR = react/features/stream-effects/virtual-background/vendor/models MEET_MODELS_DIR = react/features/stream-effects/virtual-background/vendor/models
FACE_MODELS_DIR = node_modules/@vladmandic/human-models/models FACE_MODELS_DIR = node_modules/@vladmandic/human-models/models
@ -16,25 +14,19 @@ OUTPUT_DIR = .
STYLES_BUNDLE = css/all.bundle.css STYLES_BUNDLE = css/all.bundle.css
STYLES_DESTINATION = css/all.css STYLES_DESTINATION = css/all.css
STYLES_MAIN = css/main.scss STYLES_MAIN = css/main.scss
ifeq ($(OS),Windows_NT) WEBPACK = ./node_modules/.bin/webpack
WEBPACK = .\node_modules\.bin\webpack WEBPACK_DEV_SERVER = ./node_modules/.bin/webpack serve --mode development
WEBPACK_DEV_SERVER = .\node_modules\.bin\webpack serve --mode development
else
WEBPACK = ./node_modules/.bin/webpack
WEBPACK_DEV_SERVER = ./node_modules/.bin/webpack serve --mode development
endif
all: compile deploy clean all: compile deploy clean
compile: compile:
NODE_OPTIONS=--max-old-space-size=8192 \
$(WEBPACK) $(WEBPACK)
clean: clean:
rm -fr $(BUILD_DIR) rm -fr $(BUILD_DIR)
.NOTPARALLEL: .NOTPARALLEL:
deploy: deploy-init deploy-appbundle deploy-rnnoise-binary deploy-excalidraw deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-olm deploy-tf-wasm deploy-css deploy-local deploy-face-landmarks deploy: deploy-init deploy-appbundle deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-olm deploy-tf-wasm deploy-css deploy-local deploy-face-landmarks
deploy-init: deploy-init:
rm -fr $(DEPLOY_DIR) rm -fr $(DEPLOY_DIR)
@ -44,8 +36,12 @@ deploy-appbundle:
cp \ cp \
$(BUILD_DIR)/app.bundle.min.js \ $(BUILD_DIR)/app.bundle.min.js \
$(BUILD_DIR)/app.bundle.min.js.map \ $(BUILD_DIR)/app.bundle.min.js.map \
$(BUILD_DIR)/do_external_connect.min.js \
$(BUILD_DIR)/do_external_connect.min.js.map \
$(BUILD_DIR)/external_api.min.js \ $(BUILD_DIR)/external_api.min.js \
$(BUILD_DIR)/external_api.min.js.map \ $(BUILD_DIR)/external_api.min.js.map \
$(BUILD_DIR)/dial_in_info_bundle.min.js \
$(BUILD_DIR)/dial_in_info_bundle.min.js.map \
$(BUILD_DIR)/alwaysontop.min.js \ $(BUILD_DIR)/alwaysontop.min.js \
$(BUILD_DIR)/alwaysontop.min.js.map \ $(BUILD_DIR)/alwaysontop.min.js.map \
$(OUTPUT_DIR)/analytics-ga.js \ $(OUTPUT_DIR)/analytics-ga.js \
@ -53,8 +49,6 @@ deploy-appbundle:
$(BUILD_DIR)/analytics-ga.min.js.map \ $(BUILD_DIR)/analytics-ga.min.js.map \
$(BUILD_DIR)/face-landmarks-worker.min.js \ $(BUILD_DIR)/face-landmarks-worker.min.js \
$(BUILD_DIR)/face-landmarks-worker.min.js.map \ $(BUILD_DIR)/face-landmarks-worker.min.js.map \
$(BUILD_DIR)/noise-suppressor-worklet.min.js \
$(BUILD_DIR)/noise-suppressor-worklet.min.js.map \
$(DEPLOY_DIR) $(DEPLOY_DIR)
cp \ cp \
$(BUILD_DIR)/close3.min.js \ $(BUILD_DIR)/close3.min.js \
@ -66,6 +60,7 @@ deploy-lib-jitsi-meet:
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.min.js \ $(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.min.js \
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.min.map \ $(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.min.map \
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.e2ee-worker.js \ $(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.e2ee-worker.js \
$(LIBJITSIMEET_DIR)/connection_optimization/external_connect.js \
$(LIBJITSIMEET_DIR)/modules/browser/capabilities.json \ $(LIBJITSIMEET_DIR)/modules/browser/capabilities.json \
$(DEPLOY_DIR) $(DEPLOY_DIR)
@ -89,16 +84,6 @@ deploy-tflite:
$(TFLITE_WASM)/*.wasm \ $(TFLITE_WASM)/*.wasm \
$(DEPLOY_DIR) $(DEPLOY_DIR)
deploy-excalidraw:
cp -R \
$(EXCALIDRAW_DIR) \
$(DEPLOY_DIR)/
deploy-excalidraw-dev:
cp -R \
$(EXCALIDRAW_DIR_DEV) \
$(DEPLOY_DIR)/
deploy-meet-models: deploy-meet-models:
cp \ cp \
$(MEET_MODELS_DIR)/*.tflite \ $(MEET_MODELS_DIR)/*.tflite \
@ -121,12 +106,12 @@ deploy-local:
([ ! -x deploy-local.sh ] || ./deploy-local.sh) ([ ! -x deploy-local.sh ] || ./deploy-local.sh)
.NOTPARALLEL: .NOTPARALLEL:
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-olm deploy-tf-wasm deploy-excalidraw-dev deploy-face-landmarks dev: deploy-init deploy-css deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-olm deploy-tf-wasm deploy-face-landmarks
$(WEBPACK_DEV_SERVER) $(WEBPACK_DEV_SERVER)
source-package: source-package:
mkdir -p source_package/jitsi-meet/css && \ mkdir -p source_package/jitsi-meet/css && \
cp -r *.js *.html resources/*.txt favicon.ico fonts images libs static sounds LICENSE lang source_package/jitsi-meet && \ cp -r *.js *.html resources/*.txt connection_optimization favicon.ico fonts images libs static sounds LICENSE lang source_package/jitsi-meet && \
cp css/all.css source_package/jitsi-meet/css && \ cp css/all.css source_package/jitsi-meet/css && \
(cd source_package ; tar cjf ../jitsi-meet.tar.bz2 jitsi-meet) && \ (cd source_package ; tar cjf ../jitsi-meet.tar.bz2 jitsi-meet) && \
rm -rf source_package rm -rf source_package

View File

@ -18,6 +18,7 @@ Amongst others here are the main features Jitsi Meet offers:
* Web and native SDKs for integration * Web and native SDKs for integration
* HD audio and video * HD audio and video
* Content sharing * Content sharing
* End-to-End Encryption
* Raise hand and reactions * Raise hand and reactions
* Chat with private conversations * Chat with private conversations
* Polls * Polls

View File

@ -76,7 +76,7 @@ android {
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.5.1' implementation 'androidx.appcompat:appcompat:1.4.1'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 960 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,70 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="262.91376dp"
android:height="262.91376dp"
android:viewportWidth="262.91376"
android:viewportHeight="262.91376">
<group android:scaleX="0.75" android:scaleY="0.75" android:translateX="35" android:translateY="35">
<clip-path
android:pathData="m0,0 l262.914,-0L262.914,262.914 0,262.914 0,0Z"/>
<path
android:pathData="m142.646,105.099c0.117,0.026 0.255,0.036 0.406,0.036 3.186,-0 10.297,-4.615 11.617,-6.721l0.1,-0.17 0.153,-0.135c0.451,-0.441 1.746,-2.773 2.374,-4.17 -6.751,-2.023 -7.49,-5.677 -8.153,-8.919 -0.069,-0.376 -0.138,-0.717 -0.204,-1.019 -0.074,-0.397 -0.153,-0.8 -0.226,-1.112C138.668,86.221 135.593,88.094 133.921,89.483 133.056,90.201 132.542,92.251 135.042,97.926 136.323,100.816 140.727,104.733 142.646,105.099"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m115.413,146.042c5.934,-0 18.464,-3.543 26.748,-5.887 1.21,-0.336 2.33,-0.66 3.351,-0.944 0.166,-0.046 0.321,-0.091 0.472,-0.124 -0.463,-0.461 -1.239,-1.159 -2.497,-2.216 -5.521,-3.741 -10.736,-5.484 -16.403,-5.484 -1.237,-0 -2.522,0.071 -3.923,0.231 -4.801,0.55 -8.8,1.69 -10.722,2.237 -0.967,0.284 -1.263,0.366 -1.567,0.366 -0.58,-0 -1.079,-0.341 -1.273,-0.878 -0.194,-0.534 -0.027,-1.121 0.425,-1.507l0.024,-0.011c3.316,-2.784 9.489,-7.951 21.198,-10.256 2.027,-0.401 4.202,-0.605 6.454,-0.605 5.242,-0 10.67,1.086 16.125,3.219 7.436,2.899 12.521,6.625 16.602,9.62 2.199,1.609 4.105,3.007 5.755,3.771 0.421,0.2 0.637,0.255 0.746,0.265 0.074,-0.095 0.23,-0.365 0.474,-1.069 0.066,-0.185 0.529,-2.161 -2.806,-13.374 -1.931,-6.51 -4.264,-13.156 -5.479,-16.104 -2.356,-5.711 -1.778,-9.76 -1.051,-12.125 -1.999,0.735 -4.033,1.87 -6.174,3.446L161.758,98.711C160.694,99.506 159.599,100.404 158.426,101.454 151.517,107.64 146.344,110.864 143.035,111.04l-0.093,0.004 -0.093,-0.009c-2.912,-0.245 -7.324,-4.489 -9.133,-6.634 -0.373,-0.251 -0.8,-0.366 -1.366,-0.366 -0.564,-0 -1.202,0.116 -1.82,0.235C130.086,104.354 129.623,104.441 129.167,104.489 127.708,104.632 125.668,105.106 123.694,105.561 122.746,105.777 121.762,106.005 120.864,106.189 120.851,106.19 120.463,106.272 119.774,106.454 114.903,107.891 111.228,109.55 109.432,111.111 109.414,111.127 109.352,111.174 109.266,111.242 108.048,112.105 105.124,114.567 104.248,118.762L104.237,118.795C102.398,126.516 105.187,136.087 108.892,141.554 110.636,144.125 112.513,145.727 114.048,145.959 114.437,146.015 114.891,146.042 115.413,146.042"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m90.093,173.175c-1.252,-1.472 -1.783,-3.324 -1.574,-5.521 0.884,-10.642 -0.329,-13.215 -0.891,-13.829 -0.131,-0.144 -0.207,-0.144 -0.265,-0.144 -0.022,-0 -0.041,0.003 -0.064,0.003 -1.044,0.248 -8.066,5.002 -9.615,19.171 -0.749,6.845 0.561,15.63 1.679,20.974 0.897,-3.155 2.314,-6.624 5.057,-10.204 2.556,-3.326 5.345,-5.955 8.801,-8.253C92.143,174.93 90.991,174.235 90.093,173.175"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m94.906,156.389c-0.03,2.229 -0.326,4.36 -0.61,6.445 -0.151,1.119 -0.314,2.286 -0.434,3.46 -0.161,2.341 0.346,3.166 0.571,3.406 0.127,0.136 0.326,0.287 0.76,0.287 0.339,-0 0.741,-0.091 1.161,-0.268 4.202,-1.756 8.195,-4.815 10.115,-6.515C103.522,161.892 98.995,159.058 94.906,156.389"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m154.002,81.595c-0.031,0.074 -0.065,0.148 -0.101,0.216 -0.821,2.403 0.306,5.664 2.419,6.898 0.561,0.327 1.106,0.526 1.624,0.596 0.072,0.006 0.148,0.009 0.219,0.009 1.645,-0 2.971,-1.199 3.961,-3.561C162.752,83.959 162.836,81.827 162.37,79.904 162.003,78.409 161.057,76.627 160.453,75.738 159.332,76.509 157.111,78.207 155.585,79.553 154.518,80.582 154.136,81.229 154.002,81.595"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="M148.97,77.699C153.957,73.194 156.988,65.754 158.253,61.334 153.915,65.513 148.633,67.758 145.25,69.198 144.084,69.695 143.08,70.124 142.477,70.476 142.224,70.623 141.965,70.77 141.708,70.919 139.654,72.109 136.55,73.905 136.1,75.011l-0.012,0.036 -0.012,0.034c-1.406,2.956 -2.199,7.401 -2.457,9.95 3.266,-1.99 6.625,-3.322 9.416,-4.42C145.628,79.585 147.863,78.703 148.97,77.699"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m164.464,51.921c-0.84,5.539 -2.205,10.799 -4.751,16.347 2.781,-3.144 4.396,-6.568 4.941,-10.401C164.886,56.275 165.097,54.756 164.464,51.921"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="M148.749,142.639C148.718,142.598 148.684,142.56 148.658,142.519 148.523,142.539 148.307,142.584 147.972,142.683l-0.14,0.04c-1.726,0.644 -4.899,1.708 -8.556,2.946 -4.396,1.479 -9.365,3.154 -13.526,4.649 -5.297,1.975 -7.021,2.755 -7.557,3.024 -0.098,0.266 -0.203,0.599 -0.327,0.965 -1.254,3.816 -4.125,12.541 -18.276,18.653 2.928,2.956 9.289,8.27 21.809,8.27 1.082,-0 2.21,-0.036 3.341,-0.12 9.451,-0.666 18.342,-4.855 25.026,-11.78 6.087,-6.291 9.538,-14.136 9.585,-21.7C157.876,147.509 155.367,147.135 153.043,146.033 153.014,146.02 150.361,144.745 148.749,142.639"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m189.478,117.853c-0.523,9.749 -2.122,18.424 -4.744,25.8 -2.128,5.988 -4.94,11.134 -8.356,15.316 -5.676,6.931 -11.555,9.256 -12.804,9.304 -0.866,-0 -1.313,-0.309 -3.046,-1.528 -0.17,-0.114 -0.37,-0.252 -0.581,-0.4 -3.313,5.953 -8.505,11.097 -15.065,14.959 -7.079,4.144 -15.297,6.423 -23.157,6.423 -9.078,-0 -17.13,-2.924 -23.341,-8.456 -7.467,4.799 -12.31,9.074 -16.267,27.005l-1.363,6.17 -2.971,-5.564c-0.424,-0.786 -1.929,-3.731 -3.332,-8.887 -1.934,-7.104 -2.86,-15.181 -2.758,-24.01 0.117,-10.049 3.154,-16.526 5.68,-20.186 2.98,-4.314 6.837,-6.994 10.076,-6.994 0.216,-0 0.428,0.006 0.616,0.035 5.159,0.575 8.435,2.75 14.396,6.686l1.899,1.252c2.059,1.344 4.481,2.7 5.259,2.989 0.54,-0.284 1.749,-2.3 2.155,-5.271l0.069,-0.451c0.005,-0.045 0.009,-0.091 0.014,-0.131 -0.036,-0.02 -0.065,-0.029 -0.094,-0.041 -4.008,-1.375 -9.539,-7.7 -12.364,-17.134 -2.684,-9.382 -2.129,-17.185 1.644,-23.193 6.12,-9.736 19.198,-11.974 23.466,-12.702 1.331,-0.266 2.716,-0.511 4.041,-0.717 0.255,-0.061 0.469,-0.121 0.642,-0.168 -0.031,-0.126 -0.071,-0.265 -0.114,-0.43 -0.108,-0.417 -0.23,-0.891 -0.354,-1.447 -1.345,-6.035 -0.664,-11.069 0.181,-15.193 0.928,-4.546 1.489,-7.287 3.747,-9.936 3.029,-4.165 8.319,-5.936 11.479,-6.991 0.746,-0.249 1.511,-0.509 1.894,-0.689 8.988,-4.31 11.82,-8.739 12.615,-11.694 0.656,-2.451 1.699,-8.884 1.251,-13.335 -0.085,-0.805 0.129,-1.521 0.621,-2.065 0.45,-0.505 1.101,-0.794 1.778,-0.794 1.515,-0 2.82,-0 7.511,14.598 2.481,7.698 0.645,14.903 -5.45,21.424l-0.226,0.231c0.024,0.044 0.049,0.09 0.08,0.144 2.57,4.236 3.963,9.54 3.553,13.51 -0.099,0.906 -0.265,1.775 -0.419,2.549 -0.003,0.01 -0.003,0.016 -0.004,0.029 0.516,-0.032 1.119,-0.055 1.775,-0.055 3.052,-0 7.435,0.474 10.989,2.735 2.135,1.352 4.845,3.439 6.835,7.615C189.223,102.942 190.076,109.575 189.478,117.853m4.77,-23.191c-2.916,-6.1 -6.989,-9.177 -9.793,-10.96 -2.355,-1.494 -5.064,-2.584 -8.077,-3.24l-0.676,-0.146 -0.111,-0.689c-0.339,-2.119 -0.918,-4.275 -1.715,-6.406l-0.185,-0.49 0.292,-0.434c5.095,-7.594 6.323,-16.17 3.54,-24.802 -2.191,-6.824 -3.895,-11.211 -5.341,-13.799 -2.954,-5.305 -7.006,-6.417 -9.891,-6.417 -2.964,-0 -5.8,1.261 -7.789,3.457 -2.043,2.254 -2.993,5.207 -2.678,8.31 0.316,3.134 -0.494,8.516 -1.014,10.439 -0.04,0.117 -0.975,2.929 -8.201,6.428 -0.162,0.056 -0.512,0.179 -1.053,0.359 -3.729,1.246 -10.666,3.571 -15.258,9.64 -3.465,4.205 -4.332,8.441 -5.338,13.346 -0.586,2.865 -1.236,6.744 -1.079,11.344l0.026,0.841 -0.824,0.188c-11.646,2.585 -20.025,7.835 -24.909,15.605 -5.054,8.04 -5.919,18.055 -2.543,29.853 0.063,0.204 0.126,0.407 0.189,0.615l0.527,1.608 -1.665,-0.286c-0.561,-0.101 -1.135,-0.18 -1.729,-0.241 -0.493,-0.06 -1.001,-0.082 -1.509,-0.082 -5.633,-0 -11.663,3.585 -16.128,9.592 -3.451,4.641 -7.588,12.849 -7.735,25.601 -0.114,9.573 0.906,18.401 3.038,26.228 1.581,5.795 3.326,9.329 4.004,10.577l13.306,24.94 6.096,-27.619c2.454,-11.09 4.864,-15.262 7.725,-18.111l0.561,-0.563 0.679,0.411c6.605,3.977 14.466,6.084 22.73,6.084 9.286,-0 18.965,-2.682 27.259,-7.551 5.38,-3.16 9.974,-7.036 13.649,-11.531l0.45,-0.369 0.85,-0.02c2.156,-0.068 5.16,-1.164 8.222,-3.004 2.6,-1.555 6.543,-4.428 10.501,-9.262 3.997,-4.884 7.274,-10.854 9.716,-17.734 2.876,-8.073 4.625,-17.489 5.204,-28.004 0.689,-9.668 -0.434,-17.641 -3.327,-23.704"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m180.026,98.414c-1.67,-2.596 -3.771,-4.206 -5.475,-4.206 -0.313,-0 -0.613,0.051 -0.895,0.161 -0.911,0.361 -2.356,4.532 -1.714,7.566 0.434,2.066 2.938,9.04 4.151,12.394 0.456,1.281 0.68,1.91 0.754,2.142 0.064,0.183 0.145,0.448 0.256,0.774 0.97,2.971 3.467,10.586 4.206,16.761 1.549,-6.579 2.424,-14.512 2.085,-23.997C183.235,105.662 182.04,101.538 180.026,98.414"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="M168.088,142.604C169.896,142.111 171.33,141.705 172.398,141.395 170.213,139.874 167.689,137.979 164.247,135.304c-8.418,-6.546 -17.449,-9.87 -26.839,-9.87 -5.135,-0 -9.611,0.991 -13.156,2.186 0.882,-0.05 1.779,-0.079 2.7,-0.079 1.1,-0 2.247,0.04 3.411,0.119 3.652,0.246 13.061,1.901 21.565,12.047 1.714,2.039 3.559,3.73 8.794,3.73 1.873,-0 4.051,-0.207 6.662,-0.645C167.544,142.751 167.793,142.678 168.088,142.604"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
<path
android:pathData="m164.3,147.583c-0.122,1.563 -0.376,4.509 -0.782,6.76 -0.495,2.719 -1.31,5.02 -1.791,6.226 0.85,0.786 1.694,1.553 2.247,2.043 2.214,-1.447 9.47,-6.96 14.483,-19.474C176.847,144.229 174.59,145.178 171.671,146.018 168.701,146.861 165.82,147.357 164.3,147.583"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillType="nonZero"/>
</group>
</vector>

View File

@ -2,5 +2,4 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/> <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
</adaptive-icon> </adaptive-icon>

View File

@ -2,5 +2,4 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/> <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
</adaptive-icon> </adaptive-icon>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="colorPrimary">#17A0DB</color> <color name="colorPrimary">#17A0DB</color>
<color name="navigationBarColor">#161618</color> <color name="colorPrimaryDark">#1081B2</color>
</resources> </resources>

View File

@ -1,8 +1,9 @@
<resources> <resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item> <item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
<item name="android:forceDarkAllowed">false</item> <item name="android:navigationBarColor">@color/colorPrimaryDark</item>
<item name="android:navigationBarColor">@color/navigationBarColor</item>
<item name="android:windowDisablePreview">true</item> <item name="android:windowDisablePreview">true</item>
</style> </style>
</resources> </resources>

View File

@ -1,4 +1,5 @@
import groovy.json.JsonSlurper import groovy.json.JsonSlurper
import org.apache.tools.ant.taskdefs.condition.Os
import org.gradle.util.VersionNumber import org.gradle.util.VersionNumber
// Top-level build file where you can add configuration options common to all // Top-level build file where you can add configuration options common to all
@ -11,21 +12,25 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.0.4' classpath 'com.android.tools.build:gradle:7.0.4'
classpath 'com.google.gms:google-services:4.3.14' classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
} }
} }
ext { ext {
buildToolsVersion = "31.0.0" buildToolsVersion = "31.0.0"
compileSdkVersion = 32 compileSdkVersion = 31
minSdkVersion = 23 minSdkVersion = 23
targetSdkVersion = 32 targetSdkVersion = 31
supportLibVersion = "28.0.0" supportLibVersion = "28.0.0"
if (System.properties['os.arch'] == "aarch64") { if (System.properties['os.arch'] == "aarch64") {
// For M1 Users we need to use the NDK 24 which added support for aarch64 // For M1 Users we need to use the NDK 24 which added support for aarch64
ndkVersion = "24.0.8215888" ndkVersion = "24.0.8215888"
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
// For Android Users, we need to use NDK 23, otherwise the build will
// fail due to paths longer than the OS limit
ndkVersion = "23.1.7779620"
} else { } else {
// Otherwise we default to the side-by-side NDK version from AGP. // Otherwise we default to the side-by-side NDK version from AGP.
ndkVersion = "21.4.7075529" ndkVersion = "21.4.7075529"

View File

@ -26,5 +26,5 @@ android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
android.bundle.enableUncompressedNativeLibs=false android.bundle.enableUncompressedNativeLibs=false
appVersion=99.0.0 appVersion=22.2.0
sdkVersion=99.0.0 sdkVersion=5.1.0

View File

@ -1,11 +0,0 @@
#!/bin/bash
PKG_NAME=${1:-org.jitsi.meet}
APP_PID=$(adb shell ps | grep $PKG_NAME | awk '{print $2}')
if [[ -z "$APP_PID" ]]; then
echo "App is not running"
exit 1
fi
exec adb logcat --pid=$APP_PID

View File

@ -51,10 +51,15 @@ dependencies {
implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.code.gson:gson:2.8.6'
implementation "androidx.startup:startup-runtime:1.1.0" implementation "androidx.startup:startup-runtime:1.1.0"
// Only add these packages if we are NOT doing a LIBRE_BUILD if (rootProject.ext.libreBuild) {
if (!rootProject.ext.libreBuild) { implementation(project(':react-native-device-info')) {
exclude group: 'com.google.firebase'
exclude group: 'com.google.android.gms'
exclude group: 'com.android.installreferrer'
}
} else {
implementation project(':react-native-amplitude') implementation project(':react-native-amplitude')
implementation project(':react-native-giphy') implementation project(':react-native-device-info')
implementation(project(":react-native-google-signin")) { implementation(project(":react-native-google-signin")) {
exclude group: 'com.google.android.gms' exclude group: 'com.google.android.gms'
exclude group: 'androidx' exclude group: 'androidx'
@ -67,18 +72,15 @@ dependencies {
implementation project(':react-native-community_clipboard') implementation project(':react-native-community_clipboard')
implementation project(':react-native-community_netinfo') implementation project(':react-native-community_netinfo')
implementation project(':react-native-default-preference') implementation project(':react-native-default-preference')
implementation(project(':react-native-device-info')) {
exclude group: 'com.google.firebase'
exclude group: 'com.google.android.gms'
exclude group: 'com.android.installreferrer'
}
implementation project(':react-native-gesture-handler') implementation project(':react-native-gesture-handler')
implementation project(':react-native-get-random-values') implementation project(':react-native-get-random-values')
implementation project(':react-native-giphy')
implementation project(':react-native-immersive') implementation project(':react-native-immersive')
implementation project(':react-native-keep-awake') implementation project(':react-native-keep-awake')
implementation project(':react-native-orientation-locker') implementation project(':react-native-masked-view_masked-view')
implementation project(':react-native-pager-view') implementation project(':react-native-pager-view')
implementation project(':react-native-performance') implementation project(':react-native-performance')
implementation project(':react-native-reanimated')
implementation project(':react-native-safe-area-context') implementation project(':react-native-safe-area-context')
implementation project(':react-native-screens') implementation project(':react-native-screens')
implementation project(':react-native-slider') implementation project(':react-native-slider')

View File

@ -32,7 +32,6 @@
android:name=".JitsiMeetActivity" android:name=".JitsiMeetActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/JitsiMeetActivityStyle"
android:resizeableActivity="true" android:resizeableActivity="true"
android:supportsPictureInPicture="true" android:supportsPictureInPicture="true"
android:windowSoftInputMode="adjustResize"/> android:windowSoftInputMode="adjustResize"/>

View File

@ -22,8 +22,6 @@ import android.os.Build;
import android.telecom.CallAudioState; import android.telecom.CallAudioState;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import com.facebook.react.bridge.ReactContext;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -51,8 +49,6 @@ class AudioDeviceHandlerConnectionService implements
*/ */
private AudioModeModule module; private AudioModeModule module;
private RNConnectionService rcs;
/** /**
* Converts any of the "DEVICE_" constants into the corresponding * Converts any of the "DEVICE_" constants into the corresponding
* {@link android.telecom.CallAudioState} "ROUTE_" number. * {@link android.telecom.CallAudioState} "ROUTE_" number.
@ -145,8 +141,8 @@ class AudioDeviceHandlerConnectionService implements
JitsiMeetLogger.i("Using " + TAG + " as the audio device handler"); JitsiMeetLogger.i("Using " + TAG + " as the audio device handler");
module = audioModeModule; module = audioModeModule;
rcs = module.getContext().getNativeModule(RNConnectionService.class);
RNConnectionService rcs = ReactInstanceManagerHolder.getNativeModule(RNConnectionService.class);
if (rcs != null) { if (rcs != null) {
rcs.setCallAudioStateListener(this); rcs.setCallAudioStateListener(this);
} else { } else {
@ -156,9 +152,9 @@ class AudioDeviceHandlerConnectionService implements
@Override @Override
public void stop() { public void stop() {
RNConnectionService rcs = ReactInstanceManagerHolder.getNativeModule(RNConnectionService.class);
if (rcs != null) { if (rcs != null) {
rcs.setCallAudioStateListener(null); rcs.setCallAudioStateListener(null);
rcs = null;
} else { } else {
JitsiMeetLogger.w(TAG + " Couldn't set call audio state listener, module is null"); JitsiMeetLogger.w(TAG + " Couldn't set call audio state listener, module is null");
} }

View File

@ -26,13 +26,10 @@ import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableMap;
import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger; import org.jitsi.meet.sdk.log.JitsiMeetLogger;
@ -199,7 +196,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
deviceInfo.putBoolean("selected", device.equals(selectedDevice)); deviceInfo.putBoolean("selected", device.equals(selectedDevice));
data.pushMap(deviceInfo); data.pushMap(deviceInfo);
} }
getContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(DEVICE_CHANGE_EVENT, data); ReactInstanceManagerHolder.emitEvent(DEVICE_CHANGE_EVENT, data);
JitsiMeetLogger.i(TAG + " Updating audio device list"); JitsiMeetLogger.i(TAG + " Updating audio device list");
} }
}); });
@ -215,10 +212,6 @@ class AudioModeModule extends ReactContextBaseJavaModule {
return NAME; return NAME;
} }
public ReactContext getContext(){
return this.getReactApplicationContext();
}
/** /**
* Initializes the audio device handler module. This function is called *after* all Catalyst * Initializes the audio device handler module. This function is called *after* all Catalyst
* modules have been created, and that's why we use it, because {@link AudioDeviceHandlerConnectionService} * modules have been created, and that's why we use it, because {@link AudioDeviceHandlerConnectionService}

View File

@ -0,0 +1,240 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.ReadableMap;
import com.rnimmersive.RNImmersiveModule;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.WeakHashMap;
/**
* Base class for all views which are backed by a React Native view.
*/
public abstract class BaseReactView<ListenerT>
extends FrameLayout {
/**
* Background color used by {@code BaseReactView} and the React Native root
* view.
*/
protected static int BACKGROUND_COLOR = 0xFF111111;
/**
* The collection of all existing {@code BaseReactView}s. Used to find the
* {@code BaseReactView} when delivering events coming from
* {@link ExternalAPIModule}.
*/
static final Set<BaseReactView> views
= Collections.newSetFromMap(new WeakHashMap<BaseReactView, Boolean>());
/**
* Finds a {@code BaseReactView} which matches a specific external API
* scope.
*
* @param externalAPIScope - The external API scope associated with the
* {@code BaseReactView} to find.
* @return The {@code BaseReactView}, if any, associated with the specified
* {@code externalAPIScope}; otherwise, {@code null}.
*/
public static BaseReactView findViewByExternalAPIScope(
String externalAPIScope) {
synchronized (views) {
for (BaseReactView view : views) {
if (view.externalAPIScope.equals(externalAPIScope)) {
return view;
}
}
}
return null;
}
/**
* Gets all registered React views.
*
* @return An {@link ArrayList} containing all views currently held by React.
*/
static ArrayList<BaseReactView> getViews() {
return new ArrayList<>(views);
}
/**
* The unique identifier of this {@code BaseReactView} within the process
* for the purposes of {@link ExternalAPIModule}. The name scope was
* inspired by postis which we use on Web for the similar purposes of the
* iframe-based external API.
*/
protected String externalAPIScope;
/**
* The listener (e.g. {@link JitsiMeetViewListener}) instance for reporting
* events occurring in Jitsi Meet.
*/
@Deprecated
private ListenerT listener;
/**
* React Native root view.
*/
private ReactRootView reactRootView;
public BaseReactView(@NonNull Context context) {
super(context);
initialize((Activity)context);
}
public BaseReactView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize((Activity)context);
}
public BaseReactView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize((Activity)context);
}
/**
* Creates the {@code ReactRootView} for the given app name with the given
* props. Once created it's set as the view of this {@code FrameLayout}.
*
* @param appName - The name of the "app" (in React Native terms) to load.
* @param props - The React Component props to pass to the app.
*/
public void createReactRootView(String appName, @Nullable Bundle props) {
if (props == null) {
props = new Bundle();
}
props.putString("externalAPIScope", externalAPIScope);
if (reactRootView == null) {
reactRootView = new ReactRootView(getContext());
reactRootView.startReactApplication(
ReactInstanceManagerHolder.getReactInstanceManager(),
appName,
props);
reactRootView.setBackgroundColor(BACKGROUND_COLOR);
addView(reactRootView);
} else {
reactRootView.setAppProperties(props);
}
}
/**
* Releases the React resources (specifically the {@link ReactRootView})
* associated with this view.
*
* MUST be called when the {@link Activity} holding this view is destroyed,
* typically in the {@code onDestroy} method.
*/
public void dispose() {
if (reactRootView != null) {
removeView(reactRootView);
reactRootView.unmountReactApplication();
reactRootView = null;
}
}
/**
* Gets the listener set on this {@code BaseReactView}.
*
* @return The listener set on this {@code BaseReactView}.
*/
@Deprecated
public ListenerT getListener() {
return listener;
}
/**
* Abstract method called by {@link ExternalAPIModule} when an event is
* received for this view.
*
* @param name - The name of the event.
* @param data - The details of the event associated with/specific to the
* specified {@code name}.
*/
@Deprecated
protected abstract void onExternalAPIEvent(String name, ReadableMap data);
@Deprecated
protected void onExternalAPIEvent(
Map<String, Method> listenerMethods,
String name, ReadableMap data) {
ListenerT listener = getListener();
if (listener != null) {
ListenerUtils.runListenerMethod(
listener, listenerMethods, name, data);
}
}
/**
* Called when the window containing this view gains or loses focus.
*
* @param hasFocus If the window of this view now has focus, {@code true};
* otherwise, {@code false}.
*/
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
// https://github.com/mockingbot/react-native-immersive#restore-immersive-state
RNImmersiveModule immersive = RNImmersiveModule.getInstance();
if (hasFocus && immersive != null) {
immersive.emitImmersiveStateChangeEvent();
}
}
/**
* Sets a specific listener on this {@code BaseReactView}.
*
* @param listener The listener to set on this {@code BaseReactView}.
*/
@Deprecated
public void setListener(ListenerT listener) {
this.listener = listener;
}
private void initialize(Activity activity) {
setBackgroundColor(BACKGROUND_COLOR);
ReactInstanceManagerHolder.initReactInstanceManager(activity);
// Hook this BaseReactView into ExternalAPI.
externalAPIScope = UUID.randomUUID().toString();
synchronized (views) {
views.add(this);
}
}
}

View File

@ -76,8 +76,7 @@ public class BroadcastAction {
OPEN_CHAT("org.jitsi.meet.OPEN_CHAT"), OPEN_CHAT("org.jitsi.meet.OPEN_CHAT"),
CLOSE_CHAT("org.jitsi.meet.CLOSE_CHAT"), CLOSE_CHAT("org.jitsi.meet.CLOSE_CHAT"),
SEND_CHAT_MESSAGE("org.jitsi.meet.SEND_CHAT_MESSAGE"), SEND_CHAT_MESSAGE("org.jitsi.meet.SEND_CHAT_MESSAGE"),
SET_VIDEO_MUTED("org.jitsi.meet.SET_VIDEO_MUTED"), SET_VIDEO_MUTED("org.jitsi.meet.SET_VIDEO_MUTED");
SET_CLOSED_CAPTIONS_ENABLED("org.jitsi.meet.SET_CLOSED_CAPTIONS_ENABLED");
private final String action; private final String action;

View File

@ -48,10 +48,4 @@ public class BroadcastIntentHelper {
intent.putExtra("muted", muted); intent.putExtra("muted", muted);
return intent; return intent;
} }
public static Intent buildSetClosedCaptionsEnabledIntent(boolean enabled) {
Intent intent = new Intent(BroadcastAction.Type.SET_CLOSED_CAPTIONS_ENABLED.getAction());
intent.putExtra("enabled", enabled);
return intent;
}
} }

View File

@ -95,25 +95,37 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
constants.put("CLOSE_CHAT", BroadcastAction.Type.CLOSE_CHAT.getAction()); constants.put("CLOSE_CHAT", BroadcastAction.Type.CLOSE_CHAT.getAction());
constants.put("SEND_CHAT_MESSAGE", BroadcastAction.Type.SEND_CHAT_MESSAGE.getAction()); constants.put("SEND_CHAT_MESSAGE", BroadcastAction.Type.SEND_CHAT_MESSAGE.getAction());
constants.put("SET_VIDEO_MUTED", BroadcastAction.Type.SET_VIDEO_MUTED.getAction()); constants.put("SET_VIDEO_MUTED", BroadcastAction.Type.SET_VIDEO_MUTED.getAction());
constants.put("SET_CLOSED_CAPTIONS_ENABLED", BroadcastAction.Type.SET_CLOSED_CAPTIONS_ENABLED.getAction());
return constants; return constants;
} }
/** /**
* Dispatches an event that occurred on the JavaScript side of the SDK to * Dispatches an event that occurred on the JavaScript side of the SDK to
* the native side. * the specified {@link BaseReactView}'s listener.
* *
* @param name The name of the event. * @param name The name of the event.
* @param data The details/specifics of the event to send determined * @param data The details/specifics of the event to send determined
* by/associated with the specified {@code name}. * by/associated with the specified {@code name}.
* @param scope
*/ */
@ReactMethod @ReactMethod
public void sendEvent(String name, ReadableMap data) { public void sendEvent(String name, ReadableMap data, String scope) {
// Keep track of the current ongoing conference. // Keep track of the current ongoing conference.
OngoingConferenceTracker.getInstance().onExternalAPIEvent(name, data); OngoingConferenceTracker.getInstance().onExternalAPIEvent(name, data);
JitsiMeetLogger.d(TAG + " Sending event: " + name + " with data: " + data); // The JavaScript App needs to provide uniquely identifying information
broadcastEmitter.sendBroadcast(name, data); // to the native ExternalAPI module so that the latter may match the
// former to the native BaseReactView which hosts it.
BaseReactView view = BaseReactView.findViewByExternalAPIScope(scope);
if (view != null) {
JitsiMeetLogger.d(TAG + " Sending event: " + name + " with data: " + data);
try {
view.onExternalAPIEvent(name, data);
broadcastEmitter.sendBroadcast(name, data);
} catch (Exception e) {
JitsiMeetLogger.e(e, TAG + " onExternalAPIEvent: error sending event");
}
}
} }
} }

View File

@ -1,46 +0,0 @@
/*
* Copyright 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
package org.jitsi.meet.sdk;
import org.webrtc.VideoCodecInfo;
import java.util.Map;
import java.util.HashMap;
/** Container for static helper functions related to dealing with H264 codecs. */
class H264Utils {
public static final String H264_FMTP_PROFILE_LEVEL_ID = "profile-level-id";
public static final String H264_FMTP_LEVEL_ASYMMETRY_ALLOWED = "level-asymmetry-allowed";
public static final String H264_FMTP_PACKETIZATION_MODE = "packetization-mode";
public static final String H264_PROFILE_CONSTRAINED_BASELINE = "42e0";
public static final String H264_PROFILE_CONSTRAINED_HIGH = "640c";
public static final String H264_LEVEL_3_1 = "1f"; // 31 in hex.
public static final String H264_CONSTRAINED_HIGH_3_1 =
H264_PROFILE_CONSTRAINED_HIGH + H264_LEVEL_3_1;
public static final String H264_CONSTRAINED_BASELINE_3_1 =
H264_PROFILE_CONSTRAINED_BASELINE + H264_LEVEL_3_1;
public static Map<String, String> getDefaultH264Params(boolean isHighProfile) {
final Map<String, String> params = new HashMap<>();
params.put(VideoCodecInfo.H264_FMTP_LEVEL_ASYMMETRY_ALLOWED, "1");
params.put(VideoCodecInfo.H264_FMTP_PACKETIZATION_MODE, "1");
params.put(VideoCodecInfo.H264_FMTP_PROFILE_LEVEL_ID,
isHighProfile ? VideoCodecInfo.H264_CONSTRAINED_HIGH_3_1
: VideoCodecInfo.H264_CONSTRAINED_BASELINE_3_1);
return params;
}
public static VideoCodecInfo DEFAULT_H264_BASELINE_PROFILE_CODEC =
new VideoCodecInfo("H264", getDefaultH264Params(/* isHighProfile= */ false));
public static VideoCodecInfo DEFAULT_H264_HIGH_PROFILE_CODEC =
new VideoCodecInfo("H264", getDefaultH264Params(/* isHighProfile= */ true));
}

View File

@ -15,7 +15,6 @@
*/ */
package org.jitsi.meet.sdk; package org.jitsi.meet.sdk;
import android.app.Application;
import android.content.Context; import android.content.Context;
import android.util.Log; import android.util.Log;
@ -23,7 +22,6 @@ import androidx.annotation.NonNull;
import androidx.startup.Initializer; import androidx.startup.Initializer;
import com.facebook.soloader.SoLoader; import com.facebook.soloader.SoLoader;
import org.wonday.orientation.OrientationActivityLifecycle;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -39,10 +37,6 @@ public class JitsiInitializer implements Initializer<Boolean> {
// Register our uncaught exception handler. // Register our uncaught exception handler.
JitsiMeetUncaughtExceptionHandler.register(); JitsiMeetUncaughtExceptionHandler.register();
// Register activity lifecycle handler for the orientation locker module.
((Application) context).registerActivityLifecycleCallbacks(OrientationActivityLifecycle.getInstance());
return true; return true;
} }

View File

@ -21,7 +21,6 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.res.Configuration;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
@ -87,14 +86,6 @@ public class JitsiMeetActivity extends AppCompatActivity
// Overrides // Overrides
// //
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Intent intent = new Intent("onConfigurationChanged");
intent.putExtra("newConfig", newConfig);
this.sendBroadcast(intent);
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -176,9 +167,12 @@ public class JitsiMeetActivity extends AppCompatActivity
} }
} }
protected void leave() { public void leave() {
Intent hangupBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent(); if (this.jitsiView != null) {
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(hangupBroadcastIntent); this.jitsiView .leave();
} else {
JitsiMeetLogger.w("Cannot leave, view is null");
}
} }
private @Nullable private @Nullable
@ -219,7 +213,7 @@ public class JitsiMeetActivity extends AppCompatActivity
protected void onConferenceJoined(HashMap<String, Object> extraData) { protected void onConferenceJoined(HashMap<String, Object> extraData) {
JitsiMeetLogger.i("Conference joined: " + extraData); JitsiMeetLogger.i("Conference joined: " + extraData);
// Launch the service for the ongoing notification. // Launch the service for the ongoing notification.
JitsiMeetOngoingConferenceService.launch(this, extraData); JitsiMeetOngoingConferenceService.launch(this);
} }
protected void onConferenceTerminated(HashMap<String, Object> extraData) { protected void onConferenceTerminated(HashMap<String, Object> extraData) {

View File

@ -0,0 +1,82 @@
/*
* Copyright @ 2019-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* Base {@link Fragment} for applications integrating Jitsi Meet at a higher level. It
* contains all the required wiring between the {@code JitsiMeetView} and
* the Fragment lifecycle methods already implemented.
*
* In this fragment we use a single {@code JitsiMeetView} instance. This
* instance gives us access to a view which displays the welcome page and the
* conference itself. All lifecycle methods associated with this Fragment are
* hooked to the React Native subsystem via proxy calls through the
* {@code JitsiMeetActivityDelegate} static methods.
*
* @deprecated use {@link JitsiMeetActivity} or directly {@link JitsiMeetView}
*/
@Deprecated
public class JitsiMeetFragment extends Fragment {
/**
* Instance of the {@link JitsiMeetView} which this activity will display.
*/
private JitsiMeetView view;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return this.view = new JitsiMeetView(getActivity());
}
public JitsiMeetView getJitsiView() {
return view;
}
@Override
public void onDestroy() {
super.onDestroy();
JitsiMeetActivityDelegate.onHostDestroy(getActivity());
}
@Override
public void onResume() {
super.onResume();
JitsiMeetActivityDelegate.onHostResume(getActivity());
}
@Override
public void onStop() {
super.onStop();
JitsiMeetActivityDelegate.onHostPause(getActivity());
}
}

View File

@ -17,22 +17,18 @@
package org.jitsi.meet.sdk; package org.jitsi.meet.sdk;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service; import android.app.Service;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.os.Build; import android.os.Build;
import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.jitsi.meet.sdk.log.JitsiMeetLogger; import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.HashMap;
/** /**
* This class implements an Android {@link Service}, a foreground one specifically, and it's * This class implements an Android {@link Service}, a foreground one specifically, and it's
* responsible for presenting an ongoing notification when a conference is in progress. * responsible for presenting an ongoing notification when a conference is in progress.
@ -43,22 +39,16 @@ import java.util.HashMap;
public class JitsiMeetOngoingConferenceService extends Service public class JitsiMeetOngoingConferenceService extends Service
implements OngoingConferenceTracker.OngoingConferenceListener { implements OngoingConferenceTracker.OngoingConferenceListener {
private static final String TAG = JitsiMeetOngoingConferenceService.class.getSimpleName(); private static final String TAG = JitsiMeetOngoingConferenceService.class.getSimpleName();
private static final String EXTRA_DATA_KEY = "extraDataKey";
private static final String EXTRA_DATA_BUNDLE_KEY = "extraDataBundleKey";
private static final String IS_AUDIO_MUTED_KEY = "isAudioMuted";
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver(); private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver();
private boolean isAudioMuted; private boolean isAudioMuted;
public static void launch(Context context, HashMap<String, Object> extraData) { static void launch(Context context) {
OngoingNotification.createOngoingConferenceNotificationChannel(); OngoingNotification.createOngoingConferenceNotificationChannel();
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class); Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
intent.setAction(Action.START.getName());
Bundle extraDataBundle = new Bundle();
extraDataBundle.putSerializable(EXTRA_DATA_KEY, extraData);
intent.putExtra(EXTRA_DATA_BUNDLE_KEY, extraDataBundle);
ComponentName componentName; ComponentName componentName;
@ -80,7 +70,7 @@ public class JitsiMeetOngoingConferenceService extends Service
} }
} }
public static void abort(Context context) { static void abort(Context context) {
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class); Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
context.stopService(intent); context.stopService(intent);
} }
@ -89,15 +79,6 @@ public class JitsiMeetOngoingConferenceService extends Service
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted);
if (notification == null) {
stopSelf();
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
} else {
startForeground(OngoingNotification.NOTIFICATION_ID, notification);
JitsiMeetLogger.i(TAG + " Service started");
}
OngoingConferenceTracker.getInstance().addListener(this); OngoingConferenceTracker.getInstance().addListener(this);
IntentFilter intentFilter = new IntentFilter(); IntentFilter intentFilter = new IntentFilter();
@ -120,45 +101,37 @@ public class JitsiMeetOngoingConferenceService extends Service
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
Boolean isAudioMuted = tryParseIsAudioMuted(intent);
if (isAudioMuted != null) {
this.isAudioMuted = Boolean.parseBoolean(intent.getStringExtra("muted"));
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted);
if (notification == null) {
stopSelf();
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
} else {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(OngoingNotification.NOTIFICATION_ID, notification);
}
}
final String actionName = intent.getAction(); final String actionName = intent.getAction();
final Action action = Action.fromName(actionName); final Action action = Action.fromName(actionName);
// When starting the service, there is no action passed in the intent switch (action) {
if (action != null) { case UNMUTE:
switch (action) { case MUTE:
case UNMUTE: Intent muteBroadcastIntent = BroadcastIntentHelper.buildSetAudioMutedIntent(action == Action.MUTE);
case MUTE: LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(muteBroadcastIntent);
Intent muteBroadcastIntent = BroadcastIntentHelper.buildSetAudioMutedIntent(action == Action.MUTE); break;
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(muteBroadcastIntent); case START:
break; Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted);
case HANGUP: if (notification == null) {
JitsiMeetLogger.i(TAG + " Hangup requested");
Intent hangupBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent();
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(hangupBroadcastIntent);
stopSelf(); stopSelf();
break; JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
default: } else {
JitsiMeetLogger.w(TAG + " Unknown action received: " + action); startForeground(OngoingNotification.NOTIFICATION_ID, notification);
break; JitsiMeetLogger.i(TAG + " Service started");
} }
break;
case HANGUP:
JitsiMeetLogger.i(TAG + " Hangup requested");
Intent hangupBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent();
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(hangupBroadcastIntent);
stopSelf();
break;
default:
JitsiMeetLogger.w(TAG + " Unknown action received: " + action);
stopSelf();
break;
} }
return START_NOT_STICKY; return START_NOT_STICKY;
@ -174,6 +147,7 @@ public class JitsiMeetOngoingConferenceService extends Service
} }
public enum Action { public enum Action {
START(TAG + ":START"),
HANGUP(TAG + ":HANGUP"), HANGUP(TAG + ":HANGUP"),
MUTE(TAG + ":MUTE"), MUTE(TAG + ":MUTE"),
UNMUTE(TAG + ":UNMUTE"); UNMUTE(TAG + ":UNMUTE");
@ -198,15 +172,6 @@ public class JitsiMeetOngoingConferenceService extends Service
} }
} }
private Boolean tryParseIsAudioMuted(Intent intent) {
try {
HashMap<String, Object> extraData = (HashMap<String, Object>) intent.getBundleExtra(EXTRA_DATA_BUNDLE_KEY).getSerializable(EXTRA_DATA_KEY);
return Boolean.parseBoolean((String) extraData.get(IS_AUDIO_MUTED_KEY));
} catch (Exception ignored) {
}
return null;
}
private class BroadcastReceiver extends android.content.BroadcastReceiver { private class BroadcastReceiver extends android.content.BroadcastReceiver {
@Override @Override
@ -215,12 +180,10 @@ public class JitsiMeetOngoingConferenceService extends Service
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted); Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted);
if (notification == null) { if (notification == null) {
stopSelf(); stopSelf();
JitsiMeetLogger.w(TAG + " Couldn't update service, notification is null"); JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
} else { } else {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); startForeground(OngoingNotification.NOTIFICATION_ID, notification);
notificationManager.notify(OngoingNotification.NOTIFICATION_ID, notification); JitsiMeetLogger.i(TAG + " Service started");
JitsiMeetLogger.i(TAG + " audio muted changed");
} }
} }
} }

View File

@ -16,33 +16,36 @@
package org.jitsi.meet.sdk; package org.jitsi.meet.sdk;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.widget.FrameLayout;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.facebook.react.ReactRootView; import com.facebook.react.bridge.ReadableMap;
import com.rnimmersive.RNImmersiveModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger; import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.lang.reflect.Method;
import java.util.Map;
public class JitsiMeetView extends FrameLayout { public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
implements OngoingConferenceTracker.OngoingConferenceListener {
/** /**
* Background color used by {@code BaseReactView} and the React Native root * The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
* view. * redux action types.
*/ */
private static final int BACKGROUND_COLOR = 0xFF111111; private static final Map<String, Method> LISTENER_METHODS
= ListenerUtils.mapListenerMethods(JitsiMeetViewListener.class);
/** /**
* React Native root view. * The URL of the current conference.
*/ */
private ReactRootView reactRootView; // XXX Currently, one thread writes and one thread reads, so it should be
// fine to have this field volatile without additional synchronization.
private volatile String url;
/** /**
* Helper method to recursively merge 2 {@link Bundle} objects representing React Native props. * Helper method to recursively merge 2 {@link Bundle} objects representing React Native props.
@ -81,8 +84,6 @@ public class JitsiMeetView extends FrameLayout {
result.putBoolean(key, (Boolean)bValue); result.putBoolean(key, (Boolean)bValue);
} else if (valueType.contentEquals("String")) { } else if (valueType.contentEquals("String")) {
result.putString(key, (String)bValue); result.putString(key, (String)bValue);
} else if (valueType.contentEquals("Integer")) {
result.putInt(key, (int)bValue);
} else if (valueType.contentEquals("Bundle")) { } else if (valueType.contentEquals("Bundle")) {
result.putBundle(key, mergeProps((Bundle)aValue, (Bundle)bValue)); result.putBundle(key, mergeProps((Bundle)aValue, (Bundle)bValue));
} else { } else {
@ -108,19 +109,10 @@ public class JitsiMeetView extends FrameLayout {
initialize(context); initialize(context);
} }
/** @Override
* Releases the React resources (specifically the {@link ReactRootView})
* associated with this view.
*
* MUST be called when the {@link Activity} holding this view is destroyed,
* typically in the {@code onDestroy} method.
*/
public void dispose() { public void dispose() {
if (reactRootView != null) { OngoingConferenceTracker.getInstance().removeListener(this);
removeView(reactRootView); super.dispose();
reactRootView.unmountReactApplication();
reactRootView = null;
}
} }
/** /**
@ -138,7 +130,8 @@ public class JitsiMeetView extends FrameLayout {
PictureInPictureModule.class); PictureInPictureModule.class);
if (pipModule != null if (pipModule != null
&& pipModule.isPictureInPictureSupported() && pipModule.isPictureInPictureSupported()
&& !JitsiMeetActivityDelegate.arePermissionsBeingRequested()) { && !JitsiMeetActivityDelegate.arePermissionsBeingRequested()
&& this.url != null) {
try { try {
pipModule.enterPictureInPicture(); pipModule.enterPictureInPicture();
} catch (RuntimeException re) { } catch (RuntimeException re) {
@ -158,40 +151,10 @@ public class JitsiMeetView extends FrameLayout {
} }
/** /**
* Creates the {@code ReactRootView} for the given app name with the given * Leaves the currently active conference.
* props. Once created it's set as the view of this {@code FrameLayout}.
*
* @param appName - The name of the "app" (in React Native terms) to load.
* @param props - The React Component props to pass to the app.
*/ */
private void createReactRootView(String appName, @Nullable Bundle props) { public void leave() {
if (props == null) { setProps(new Bundle());
props = new Bundle();
}
if (reactRootView == null) {
reactRootView = new ReactRootView(getContext());
reactRootView.startReactApplication(
ReactInstanceManagerHolder.getReactInstanceManager(),
appName,
props);
reactRootView.setBackgroundColor(BACKGROUND_COLOR);
addView(reactRootView);
} else {
reactRootView.setAppProperties(props);
}
}
private void initialize(@NonNull Context context) {
// Check if the parent Activity implements JitsiMeetActivityInterface,
// otherwise things may go wrong.
if (!(context instanceof JitsiMeetActivityInterface)) {
throw new RuntimeException("Enclosing Activity must implement JitsiMeetActivityInterface");
}
setBackgroundColor(BACKGROUND_COLOR);
ReactInstanceManagerHolder.initReactInstanceManager((Activity) context);
} }
/** /**
@ -208,7 +171,7 @@ public class JitsiMeetView extends FrameLayout {
// by leaving the conference. However, React and, respectively, // by leaving the conference. However, React and, respectively,
// appProperties/initialProperties are declarative expressions i.e. one // appProperties/initialProperties are declarative expressions i.e. one
// and the same URL will not trigger an automatic re-render in the // and the same URL will not trigger an automatic re-render in the
// JavaScript source code. The workaround implemented below introduces // JavaScript source code. The workaround implemented bellow introduces
// "imperativeness" in React Component props by defining a unique value // "imperativeness" in React Component props by defining a unique value
// per setProps() invocation. // per setProps() invocation.
props.putLong("timestamp", System.currentTimeMillis()); props.putLong("timestamp", System.currentTimeMillis());
@ -216,27 +179,46 @@ public class JitsiMeetView extends FrameLayout {
createReactRootView("App", props); createReactRootView("App", props);
} }
/**
* Handler for {@link OngoingConferenceTracker} events.
* @param conferenceUrl
*/
@Override
public void onCurrentConferenceChanged(String conferenceUrl) {
// This property was introduced in order to address
// an exception in the Picture-in-Picture functionality which arose
// because of delays related to bridging between JavaScript and Java. To
// reduce these delays do not wait for the call to be transferred to the
// UI thread.
this.url = conferenceUrl;
}
/**
* Handler for {@link ExternalAPIModule} events.
*
* @param name The name of the event.
* @param data The details/specifics of the event to send determined
* by/associated with the specified {@code name}.
*/
@Override
@Deprecated
protected void onExternalAPIEvent(String name, ReadableMap data) {
onExternalAPIEvent(LISTENER_METHODS, name, data);
}
@Override @Override
protected void onDetachedFromWindow() { protected void onDetachedFromWindow() {
dispose(); dispose();
super.onDetachedFromWindow(); super.onDetachedFromWindow();
} }
/** private void initialize(@NonNull Context context) {
* Called when the window containing this view gains or loses focus. // Check if the parent Activity implements JitsiMeetActivityInterface,
* // otherwise things may go wrong.
* @param hasFocus If the window of this view now has focus, {@code true}; if (!(context instanceof JitsiMeetActivityInterface)) {
* otherwise, {@code false}. throw new RuntimeException("Enclosing Activity must implement JitsiMeetActivityInterface");
*/
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
// https://github.com/mockingbot/react-native-immersive#restore-immersive-state
RNImmersiveModule immersive = RNImmersiveModule.getInstance();
if (hasFocus && immersive != null) {
immersive.emitImmersiveStateChangeEvent();
} }
OngoingConferenceTracker.getInstance().addListener(this);
} }
} }

View File

@ -0,0 +1,51 @@
/*
* Copyright @ 2017-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import java.util.Map;
/**
* Interface for listening to events coming from Jitsi Meet.
*/
@Deprecated
public interface JitsiMeetViewListener {
/**
* Called when a conference was joined.
*
* @param data Map with a "url" key with the conference URL.
*/
void onConferenceJoined(Map<String, Object> data);
/**
* Called when the active conference ends, be it because of user choice or
* because of a failure.
*
* @param data Map with an "error" key with the error and a "url" key with
* the conference URL. If the conference finished gracefully no `error`
* key will be present. The possible values for "error" are described here:
* https://github.com/jitsi/lib-jitsi-meet/blob/master/JitsiConnectionErrors.js
* https://github.com/jitsi/lib-jitsi-meet/blob/master/JitsiConferenceErrors.js
*/
void onConferenceTerminated(Map<String, Object> data);
/**
* Called before the conference is joined.
*
* @param data Map with a "url" key with the conference URL.
*/
void onConferenceWillJoin(Map<String, Object> data);
}

View File

@ -0,0 +1,167 @@
/*
* Copyright @ 2018-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.UiThreadUtil;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
/**
* Utility methods for helping with transforming {@link ExternalAPIModule}
* events into listener methods. Used with descendants of {@link BaseReactView}.
*/
@Deprecated
public final class ListenerUtils {
/**
* Extracts the methods defined in a listener and creates a mapping of this
* form: event name -> method.
*
* @param listener - The listener whose methods we want to slurp.
* @return A mapping with event names - methods.
*/
public static Map<String, Method> mapListenerMethods(Class listener) {
Map<String, Method> methods = new HashMap<>();
// Figure out the mapping between the listener methods
// and the events i.e. redux action types.
Pattern onPattern = Pattern.compile("^on[A-Z]+");
Pattern camelcasePattern = Pattern.compile("([a-z0-9]+)([A-Z0-9]+)");
for (Method method : listener.getDeclaredMethods()) {
// * The method must be public (because it is declared by an
// interface).
// * The method must be/return void.
if (!Modifier.isPublic(method.getModifiers())
|| !Void.TYPE.equals(method.getReturnType())) {
continue;
}
// * The method name must start with "on" followed by a
// capital/uppercase letter (in agreement with the camelcase
// coding style customary to Java in general and the projects of
// the Jitsi community in particular).
String name = method.getName();
if (!onPattern.matcher(name).find()) {
continue;
}
// * The method must accept/have exactly 1 parameter of a type
// assignable from HashMap.
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 1
|| !parameterTypes[0].isAssignableFrom(HashMap.class)) {
continue;
}
// Convert the method name to an event name.
name
= camelcasePattern.matcher(name.substring(2))
.replaceAll("$1_$2")
.toUpperCase(Locale.ROOT);
methods.put(name, method);
}
return methods;
}
/**
* Executes the right listener method for the given event.
* NOTE: This function will run asynchronously on the UI thread.
*
* @param listener - The listener on which the method will be called.
* @param listenerMethods - Mapping with event names and the matching
* methods.
* @param eventName - Name of the event.
* @param eventData - Data associated with the event.
*/
public static void runListenerMethod(
final Object listener,
final Map<String, Method> listenerMethods,
final String eventName,
final ReadableMap eventData) {
// Make sure listener methods are invoked on the UI thread. It
// was requested by SDK consumers.
if (UiThreadUtil.isOnUiThread()) {
runListenerMethodOnUiThread(
listener, listenerMethods, eventName, eventData);
} else {
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
runListenerMethodOnUiThread(
listener, listenerMethods, eventName, eventData);
}
});
}
}
/**
* Helper companion for {@link ListenerUtils#runListenerMethod} which runs
* in the UI thread.
*/
private static void runListenerMethodOnUiThread(
Object listener,
Map<String, Method> listenerMethods,
String eventName,
ReadableMap eventData) {
UiThreadUtil.assertOnUiThread();
Method method = listenerMethods.get(eventName);
if (method != null) {
try {
method.invoke(listener, toHashMap(eventData));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
/**
* Initializes a new {@code HashMap} instance with the key-value
* associations of a specific {@code ReadableMap}.
*
* @param readableMap the {@code ReadableMap} specifying the key-value
* associations with which the new {@code HashMap} instance is to be
* initialized.
* @return a new {@code HashMap} instance initialized with the key-value
* associations of the specified {@code readableMap}.
*/
private static HashMap<String, Object> toHashMap(ReadableMap readableMap) {
HashMap<String, Object> hashMap = new HashMap<>();
for (ReadableMapKeySetIterator i = readableMap.keySetIterator();
i.hasNextKey();) {
String key = i.nextKey();
hashMap.put(key, readableMap.getString(key));
}
return hashMap;
}
}

View File

@ -43,7 +43,7 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
private static final String TAG = NAME; private static final String TAG = NAME;
private static boolean isSupported; private static boolean isSupported;
private boolean isEnabled; private boolean isDisabled;
public PictureInPictureModule(ReactApplicationContext reactContext) { public PictureInPictureModule(ReactApplicationContext reactContext) {
super(reactContext); super(reactContext);
@ -84,7 +84,7 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
*/ */
@TargetApi(Build.VERSION_CODES.O) @TargetApi(Build.VERSION_CODES.O)
public void enterPictureInPicture() { public void enterPictureInPicture() {
if (!isEnabled) { if (isDisabled) {
return; return;
} }
@ -132,8 +132,8 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
} }
@ReactMethod @ReactMethod
public void setPictureInPictureEnabled(Boolean enabled) { public void setPictureInPictureDisabled(Boolean disabled) {
this.isEnabled = enabled; this.isDisabled = disabled;
} }
public boolean isPictureInPictureSupported() { public boolean isPictureInPictureSupported() {

View File

@ -193,7 +193,7 @@ class RNConnectionService extends ReactContextBaseJavaModule {
* Called by the JS side to update the call's state. * Called by the JS side to update the call's state.
* *
* @param callUUID - the call's UUID. * @param callUUID - the call's UUID.
* @param callState - the map which carries info about the current call's * @param callState - the map which carries infor about the current call's
* state. See static fields in {@link ConnectionService.ConnectionImpl} * state. See static fields in {@link ConnectionService.ConnectionImpl}
* prefixed with "KEY_" for the values supported by the Android * prefixed with "KEY_" for the values supported by the Android
* implementation. * implementation.

View File

@ -31,12 +31,12 @@ import com.facebook.react.common.LifecycleState;
import com.facebook.react.jscexecutor.JSCExecutorFactory; import com.facebook.react.jscexecutor.JSCExecutorFactory;
import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.ViewManager; import com.facebook.react.uimanager.ViewManager;
import com.oney.WebRTCModule.EglUtils;
import com.oney.WebRTCModule.RTCVideoViewManager; import com.oney.WebRTCModule.RTCVideoViewManager;
import com.oney.WebRTCModule.WebRTCModule; import com.oney.WebRTCModule.WebRTCModule;
import org.devio.rn.splashscreen.SplashScreenModule; import org.devio.rn.splashscreen.SplashScreenModule;
import org.webrtc.EglBase; import org.webrtc.SoftwareVideoDecoderFactory;
import org.webrtc.SoftwareVideoEncoderFactory;
import org.webrtc.audio.AudioDeviceModule; import org.webrtc.audio.AudioDeviceModule;
import org.webrtc.audio.JavaAudioDeviceModule; import org.webrtc.audio.JavaAudioDeviceModule;
@ -46,8 +46,6 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
class ReactInstanceManagerHolder { class ReactInstanceManagerHolder {
private static final String TAG = ReactInstanceManagerHolder.class.getSimpleName();
/** /**
* FIXME (from linter): Do not place Android context classes in static * FIXME (from linter): Do not place Android context classes in static
* fields (static reference to ReactInstanceManager which has field * fields (static reference to ReactInstanceManager which has field
@ -73,6 +71,7 @@ class ReactInstanceManagerHolder {
new SplashScreenModule(reactContext), new SplashScreenModule(reactContext),
new PictureInPictureModule(reactContext), new PictureInPictureModule(reactContext),
new ProximityModule(reactContext), new ProximityModule(reactContext),
new WiFiStatsModule(reactContext),
new org.jitsi.meet.sdk.net.NAT64AddrInfoModule(reactContext))); new org.jitsi.meet.sdk.net.NAT64AddrInfoModule(reactContext)));
if (AudioModeModule.useConnectionService()) { if (AudioModeModule.useConnectionService()) {
@ -84,14 +83,11 @@ class ReactInstanceManagerHolder {
WebRTCModule.Options options = new WebRTCModule.Options(); WebRTCModule.Options options = new WebRTCModule.Options();
AudioDeviceModule adm = JavaAudioDeviceModule.builder(reactContext) AudioDeviceModule adm = JavaAudioDeviceModule.builder(reactContext)
.setEnableVolumeLogger(false)
.createAudioDeviceModule(); .createAudioDeviceModule();
options.setAudioDeviceModule(adm); options.setAudioDeviceModule(adm);
EglBase.Context eglContext = EglUtils.getRootEglBaseContext(); options.setVideoDecoderFactory(new SoftwareVideoDecoderFactory());
options.setVideoEncoderFactory(new SoftwareVideoEncoderFactory());
options.setVideoDecoderFactory(new WebRTCVideoDecoderFactory(eglContext));
options.setVideoEncoderFactory(new WebRTCVideoEncoderFactory(eglContext));
nativeModules.add(new WebRTCModule(reactContext, options)); nativeModules.add(new WebRTCModule(reactContext, options));
@ -114,22 +110,24 @@ class ReactInstanceManagerHolder {
new com.corbt.keepawake.KCKeepAwakePackage(), new com.corbt.keepawake.KCKeepAwakePackage(),
new com.facebook.react.shell.MainReactPackage(), new com.facebook.react.shell.MainReactPackage(),
new com.reactnativecommunity.clipboard.ClipboardPackage(), new com.reactnativecommunity.clipboard.ClipboardPackage(),
new com.giphyreactnativesdk.GiphyReactNativeSdkPackage(),
new com.reactnativecommunity.netinfo.NetInfoPackage(), new com.reactnativecommunity.netinfo.NetInfoPackage(),
new com.reactnativepagerview.PagerViewPackage(), new com.reactnativepagerview.PagerViewPackage(),
new com.oblador.performance.PerformancePackage(), new com.oblador.performance.PerformancePackage(),
new com.reactnativecommunity.slider.ReactSliderPackage(), new com.reactnativecommunity.slider.ReactSliderPackage(),
new com.brentvatne.react.ReactVideoPackage(), new com.brentvatne.react.ReactVideoPackage(),
new com.swmansion.reanimated.ReanimatedPackage(),
new org.reactnative.maskedview.RNCMaskedViewPackage(),
new com.reactnativecommunity.webview.RNCWebViewPackage(), new com.reactnativecommunity.webview.RNCWebViewPackage(),
new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(), new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
new com.learnium.RNDeviceInfo.RNDeviceInfo(), new com.learnium.RNDeviceInfo.RNDeviceInfo(),
new com.swmansion.gesturehandler.RNGestureHandlerPackage(), new com.swmansion.gesturehandler.react.RNGestureHandlerPackage(),
new org.linusu.RNGetRandomValuesPackage(), new org.linusu.RNGetRandomValuesPackage(),
new com.rnimmersive.RNImmersivePackage(), new com.rnimmersive.RNImmersivePackage(),
new com.swmansion.rnscreens.RNScreensPackage(), new com.swmansion.rnscreens.RNScreensPackage(),
new com.zmxv.RNSound.RNSoundPackage(), new com.zmxv.RNSound.RNSoundPackage(),
new com.th3rdwave.safeareacontext.SafeAreaContextPackage(), new com.th3rdwave.safeareacontext.SafeAreaContextPackage(),
new com.horcrux.svg.SvgPackage(), new com.horcrux.svg.SvgPackage(),
new org.wonday.orientation.OrientationPackage(),
new ReactPackageAdapter() { new ReactPackageAdapter() {
@Override @Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
@ -148,17 +146,6 @@ class ReactInstanceManagerHolder {
packages.add((ReactPackage)constructor.newInstance()); packages.add((ReactPackage)constructor.newInstance());
} catch (Exception e) { } catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled. // Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
Log.d(TAG, "Not loading AmplitudeReactNativePackage");
}
// GiphyReactNativeSdkPackage
try {
Class<?> giphyPackageClass = Class.forName("com.giphyreactnativesdk.GiphyReactNativeSdkPackage");
Constructor constructor = giphyPackageClass.getConstructor();
packages.add((ReactPackage)constructor.newInstance());
} catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
Log.d(TAG, "Not loading GiphyReactNativeSdkPackage");
} }
// RNGoogleSignInPackage // RNGoogleSignInPackage
@ -168,7 +155,6 @@ class ReactInstanceManagerHolder {
packages.add((ReactPackage)constructor.newInstance()); packages.add((ReactPackage)constructor.newInstance());
} catch (Exception e) { } catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled. // Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
Log.d(TAG, "Not loading RNGoogleSignInPackage");
} }
return packages; return packages;
@ -254,7 +240,7 @@ class ReactInstanceManagerHolder {
return; return;
} }
Log.d(TAG, "initializing RN with Application"); Log.d(ReactInstanceManagerHolder.class.getCanonicalName(), "initializing RN with Application");
reactInstanceManager reactInstanceManager
= ReactInstanceManager.builder() = ReactInstanceManager.builder()

View File

@ -1,19 +0,0 @@
package org.jitsi.meet.sdk;
/** Enumeration of supported video codec types. */
public enum VideoCodecMimeType {
VP8("video/x-vnd.on2.vp8"),
VP9("video/x-vnd.on2.vp9"),
H264("video/avc"),
AV1("video/av01");
private final String mimeType;
private VideoCodecMimeType(String mimeType) {
this.mimeType = mimeType;
}
String mimeType() {
return mimeType;
}
}

View File

@ -1,52 +0,0 @@
package org.jitsi.meet.sdk;
import androidx.annotation.Nullable;
import org.webrtc.EglBase;
import org.webrtc.HardwareVideoDecoderFactory;
import org.webrtc.SoftwareVideoDecoderFactory;
import org.webrtc.VideoCodecInfo;
import org.webrtc.VideoDecoder;
import org.webrtc.VideoDecoderFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* This is a custom video decoder factory for WebRTC which behaves similarly
* to the default one in iOS. It supports the following codecs:
*
* - In hardware: H.264 (baseline)
* - In software: VP8, VP9, AV1
*/
public class WebRTCVideoDecoderFactory implements VideoDecoderFactory {
private final VideoDecoderFactory hardwareVideoDecoderFactory;
private final VideoDecoderFactory softwareVideoDecoderFactory = new SoftwareVideoDecoderFactory();
public WebRTCVideoDecoderFactory(@Nullable EglBase.Context eglContext) {
this.hardwareVideoDecoderFactory = new HardwareVideoDecoderFactory(eglContext);
}
@Nullable
@Override
public VideoDecoder createDecoder(VideoCodecInfo codecInfo) {
if (codecInfo.name.equalsIgnoreCase(VideoCodecMimeType.H264.name())) {
return this.hardwareVideoDecoderFactory.createDecoder(codecInfo);
}
return this.softwareVideoDecoderFactory.createDecoder(codecInfo);
}
@Override
public VideoCodecInfo[] getSupportedCodecs() {
List<VideoCodecInfo> codecs = new ArrayList<>();
codecs.add(H264Utils.DEFAULT_H264_BASELINE_PROFILE_CODEC);
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP8.name(), new HashMap<>()));
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP9.name(), new HashMap<>()));
codecs.add(new VideoCodecInfo(VideoCodecMimeType.AV1.name(), new HashMap<>()));
return codecs.toArray(new VideoCodecInfo[codecs.size()]);
}
}

View File

@ -1,53 +0,0 @@
package org.jitsi.meet.sdk;
import androidx.annotation.Nullable;
import org.webrtc.EglBase;
import org.webrtc.HardwareVideoEncoderFactory;
import org.webrtc.SoftwareVideoEncoderFactory;
import org.webrtc.VideoCodecInfo;
import org.webrtc.VideoEncoder;
import org.webrtc.VideoEncoderFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* This is a custom video encoder factory for WebRTC which behaves similarly
* to the default one in iOS. It supports the following codecs:
*
* - In hardware: H.264 (baseline)
* - In software: VP8, VP9, AV1
*/
public class WebRTCVideoEncoderFactory implements VideoEncoderFactory {
private final VideoEncoderFactory hardwareVideoEncoderFactory;
private final VideoEncoderFactory softwareVideoEncoderFactory = new SoftwareVideoEncoderFactory();
public WebRTCVideoEncoderFactory(@Nullable EglBase.Context eglContext) {
this.hardwareVideoEncoderFactory =
new HardwareVideoEncoderFactory(eglContext, false, false);
}
@Nullable
@Override
public VideoEncoder createEncoder(VideoCodecInfo codecInfo) {
if (codecInfo.name.equalsIgnoreCase(VideoCodecMimeType.H264.name())) {
return this.hardwareVideoEncoderFactory.createEncoder(codecInfo);
}
return this.softwareVideoEncoderFactory.createEncoder(codecInfo);
}
@Override
public VideoCodecInfo[] getSupportedCodecs() {
List<VideoCodecInfo> codecs = new ArrayList<>();
codecs.add(H264Utils.DEFAULT_H264_BASELINE_PROFILE_CODEC);
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP8.name(), new HashMap<>()));
codecs.add(new VideoCodecInfo(VideoCodecMimeType.VP9.name(), new HashMap<>()));
codecs.add(new VideoCodecInfo(VideoCodecMimeType.AV1.name(), new HashMap<>()));
return codecs.toArray(new VideoCodecInfo[codecs.size()]);
}
}

View File

@ -0,0 +1,203 @@
/*
* Copyright @ 2017-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
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 org.json.JSONArray;
import org.json.JSONObject;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Module exposing WiFi statistics.
*
* Gathers rssi, signal in percentage, timestamp and the addresses of the wifi
* device.
*/
@ReactModule(name = WiFiStatsModule.NAME)
class WiFiStatsModule
extends ReactContextBaseJavaModule {
public static final String NAME = "WiFiStats";
/**
* The {@code Log} tag {@code WiFiStatsModule} is to log messages with.
*/
static final String TAG = NAME;
/**
* The scale used for the signal value. A level of the signal, given in the
* range of 0 to SIGNAL_LEVEL_SCALE-1 (both inclusive).
*/
public final static int SIGNAL_LEVEL_SCALE = 101;
/**
* {@link ExecutorService} for running all operations on a dedicated thread.
*/
private static final ExecutorService executor
= Executors.newSingleThreadExecutor();
/**
* Initializes a new module instance. There shall be a single instance of
* this module throughout the lifetime of the application.
*
* @param reactContext the {@link ReactApplicationContext} where this module
* is created.
*/
public WiFiStatsModule(ReactApplicationContext reactContext) {
super(reactContext);
}
/**
* Gets the name for this module to be used in the React Native bridge.
*
* @return a string with the module name.
*/
@Override
public String getName() {
return NAME;
}
/**
* Returns the {@link InetAddress} represented by this int.
*
* @param value the int representation of the ip address.
* @return the {@link InetAddress}.
* @throws UnknownHostException - if IP address is of illegal length.
*/
public static InetAddress toInetAddress(int value)
throws UnknownHostException {
return InetAddress.getByAddress(
new byte[] {
(byte) value,
(byte) (value >> 8),
(byte) (value >> 16),
(byte) (value >> 24)
});
}
/**
* Public method to retrieve WiFi stats.
*
* @param promise a {@link Promise} which will be resolved if WiFi stats are
* retrieved successfully, and it will be rejected otherwise.
*/
@ReactMethod
public void getWiFiStats(final Promise promise) {
Runnable r = new Runnable() {
@Override
public void run() {
try {
Context context
= getReactApplicationContext().getApplicationContext();
WifiManager wifiManager
= (WifiManager) context
.getSystemService(Context.WIFI_SERVICE);
if (!wifiManager.isWifiEnabled()) {
promise.reject(new Exception("Wifi not enabled"));
return;
}
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
if (wifiInfo.getNetworkId() == -1) {
promise.reject(new Exception("Wifi not connected"));
return;
}
int rssi = wifiInfo.getRssi();
int signalLevel
= WifiManager.calculateSignalLevel(
rssi, SIGNAL_LEVEL_SCALE);
JSONObject result = new JSONObject();
result.put("rssi", rssi)
.put("signal", signalLevel)
.put("timestamp", System.currentTimeMillis());
JSONArray addresses = new JSONArray();
InetAddress wifiAddress
= toInetAddress(wifiInfo.getIpAddress());
try {
Enumeration<NetworkInterface> e
= NetworkInterface.getNetworkInterfaces();
while (e.hasMoreElements()) {
NetworkInterface networkInterface = e.nextElement();
boolean found = false;
// first check whether this is the desired interface
Enumeration<InetAddress> as
= networkInterface.getInetAddresses();
while (as.hasMoreElements()) {
InetAddress a = as.nextElement();
if(a.equals(wifiAddress)) {
found = true;
break;
}
}
if (found) {
// interface found let's put addresses
// to the result object
as = networkInterface.getInetAddresses();
while (as.hasMoreElements()) {
InetAddress a = as.nextElement();
if (a.isLinkLocalAddress())
continue;
addresses.put(a.getHostAddress());
}
}
}
} catch (SocketException e) {
JitsiMeetLogger.e(e, TAG + " Unable to NetworkInterface.getNetworkInterfaces()");
}
result.put("addresses", addresses);
promise.resolve(result.toString());
JitsiMeetLogger.d(TAG + " WiFi stats: " + result.toString());
} catch (Throwable e) {
JitsiMeetLogger.e(e, TAG + " Failed to obtain wifi stats");
promise.reject(
new Exception("Failed to obtain wifi stats"));
}
}
};
executor.execute(r);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 699 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -1,3 +0,0 @@
<resources>
<style name="JitsiMeetActivityStyle" parent="Theme.AppCompat.Light.NoActionBar"/>
</resources>

View File

@ -31,12 +31,14 @@ include ':react-native-immersive'
project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android') project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android')
include ':react-native-keep-awake' include ':react-native-keep-awake'
project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keep-awake/android') project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keep-awake/android')
include ':react-native-orientation-locker' include ':react-native-masked-view_masked-view'
project(':react-native-orientation-locker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-orientation-locker/android') project(':react-native-masked-view_masked-view').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-masked-view/masked-view/android')
include ':react-native-pager-view' include ':react-native-pager-view'
project(':react-native-pager-view').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-pager-view/android') project(':react-native-pager-view').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-pager-view/android')
include ':react-native-performance' include ':react-native-performance'
project(':react-native-performance').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-performance/android') project(':react-native-performance').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-performance/android')
include ':react-native-reanimated'
project(':react-native-reanimated').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-reanimated/android')
include ':react-native-safe-area-context' include ':react-native-safe-area-context'
project(':react-native-safe-area-context').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-safe-area-context/android') project(':react-native-safe-area-context').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-safe-area-context/android')
include ':react-native-screens' include ':react-native-screens'

17
app.js
View File

@ -1,10 +1,6 @@
/* application specific logic */ /* application specific logic */
// Re-export jQuery import 'jquery';
// FIXME: Remove this requirement from torture tests.
import $ from 'jquery';
window.$ = window.jQuery = $;
import '@matrix-org/olm'; import '@matrix-org/olm';
@ -33,6 +29,17 @@ window.APP = {
API, API,
conference, conference,
// Used by do_external_connect.js if we receive the attach data after
// connect was already executed. status property can be 'initialized',
// 'ready', or 'connecting'. We are interested in 'ready' status only which
// means that connect was executed but we have to wait for the attach data.
// In status 'ready' handler property will be set to a function that will
// finish the connect process when the attach data or error is received.
connect: {
handler: null,
status: 'initialized'
},
// Used for automated performance tests. // Used for automated performance tests.
connectionTimes: { connectionTimes: {
'index.loaded': window.indexLoadedTime 'index.loaded': window.indexLoadedTime

File diff suppressed because it is too large Load Diff

612
config.js

File diff suppressed because it is too large Load Diff

View File

@ -8,9 +8,8 @@ import { LoginDialog } from './react/features/authentication/components';
import { isTokenAuthEnabled } from './react/features/authentication/functions'; import { isTokenAuthEnabled } from './react/features/authentication/functions';
import { import {
connectionEstablished, connectionEstablished,
connectionFailed, connectionFailed
constructOptions } from './react/features/base/connection/actions';
} from './react/features/base/connection/actions.web';
import { openDialog } from './react/features/base/dialog/actions'; import { openDialog } from './react/features/base/dialog/actions';
import { setJWT } from './react/features/base/jwt'; import { setJWT } from './react/features/base/jwt';
import { import {
@ -19,10 +18,8 @@ import {
} from './react/features/base/lib-jitsi-meet'; } from './react/features/base/lib-jitsi-meet';
import { isFatalJitsiConnectionError } from './react/features/base/lib-jitsi-meet/functions'; import { isFatalJitsiConnectionError } from './react/features/base/lib-jitsi-meet/functions';
import { getCustomerDetails } from './react/features/jaas/actions.any'; import { getCustomerDetails } from './react/features/jaas/actions.any';
import { getJaasJWT, isVpaasMeeting } from './react/features/jaas/functions'; import { isVpaasMeeting, getJaasJWT } from './react/features/jaas/functions';
import { import { setPrejoinDisplayNameRequired } from './react/features/prejoin/actions';
setPrejoinDisplayNameRequired
} from './react/features/prejoin/actions';
const logger = Logger.getLogger(__filename); const logger = Logger.getLogger(__filename);
/** /**
@ -32,14 +29,64 @@ const logger = Logger.getLogger(__filename);
*/ */
export const DISCO_JIBRI_FEATURE = 'http://jitsi.org/protocol/jibri'; export const DISCO_JIBRI_FEATURE = 'http://jitsi.org/protocol/jibri';
/**
* Checks if we have data to use attach instead of connect. If we have the data
* executes attach otherwise check if we have to wait for the data. If we have
* to wait for the attach data we are setting handler to APP.connect.handler
* which is going to be called when the attach data is received otherwise
* executes connect.
*
* @param {string} [id] user id
* @param {string} [password] password
* @param {string} [roomName] the name of the conference.
*/
function checkForAttachParametersAndConnect(id, password, connection) {
if (window.XMPPAttachInfo) {
APP.connect.status = 'connecting';
// When connection optimization is not deployed or enabled the default
// value will be window.XMPPAttachInfo.status = "error"
// If the connection optimization is deployed and enabled and there is
// a failure the value will be window.XMPPAttachInfo.status = "error"
if (window.XMPPAttachInfo.status === 'error') {
connection.connect({
id,
password
});
return;
}
const attachOptions = window.XMPPAttachInfo.data;
if (attachOptions) {
connection.attach(attachOptions);
delete window.XMPPAttachInfo.data;
} else {
connection.connect({
id,
password
});
}
} else {
APP.connect.status = 'ready';
APP.connect.handler
= checkForAttachParametersAndConnect.bind(
null,
id, password, connection);
}
}
/** /**
* Try to open connection using provided credentials. * Try to open connection using provided credentials.
* @param {string} [id] * @param {string} [id]
* @param {string} [password] * @param {string} [password]
* @param {string} [roomName]
* @returns {Promise<JitsiConnection>} connection if * @returns {Promise<JitsiConnection>} connection if
* everything is ok, else error. * everything is ok, else error.
*/ */
export async function connect(id, password) { export async function connect(id, password, roomName) {
const connectionConfig = Object.assign({}, config);
const state = APP.store.getState(); const state = APP.store.getState();
let { jwt } = state['features/base/jwt']; let { jwt } = state['features/base/jwt'];
const { iAmRecorder, iAmSipGateway } = state['features/base/config']; const { iAmRecorder, iAmSipGateway } = state['features/base/config'];
@ -53,7 +100,19 @@ export async function connect(id, password) {
} }
} }
const connection = new JitsiMeetJS.JitsiConnection(null, jwt, constructOptions(state)); // Use Websocket URL for the web app if configured. Note that there is no 'isWeb' check, because there's assumption
// that this code executes only on web browsers/electron. This needs to be changed when mobile and web are unified.
let serviceUrl = connectionConfig.websocket || connectionConfig.bosh;
serviceUrl += `?room=${roomName}`;
connectionConfig.serviceUrl = serviceUrl;
if (connectionConfig.websocketKeepAliveUrl) {
connectionConfig.websocketKeepAliveUrl += `?room=${roomName}`;
}
const connection = new JitsiMeetJS.JitsiConnection(null, jwt, connectionConfig);
if (config.iAmRecorder) { if (config.iAmRecorder) {
connection.addFeature(DISCO_JIBRI_FEATURE); connection.addFeature(DISCO_JIBRI_FEATURE);
@ -134,10 +193,7 @@ export async function connect(id, password) {
APP.store.dispatch(setPrejoinDisplayNameRequired()); APP.store.dispatch(setPrejoinDisplayNameRequired());
} }
connection.connect({ checkForAttachParametersAndConnect(id, password, connection);
id,
password
});
}); });
} }

View File

@ -0,0 +1,3 @@
module.exports = {
'extends': '../react/.eslintrc.js'
};

View File

@ -0,0 +1,86 @@
/* global config, createConnectionExternally */
import getRoomName from '../react/features/base/config/getRoomName';
import { parseURLParams } from '../react/features/base/util/parseURLParams';
/**
* Implements external connect using createConnectionExternally function defined
* in external_connect.js for Jitsi Meet. Parses the room name and JSON Web
* Token (JWT) from the URL and executes createConnectionExternally.
*
* NOTE: If you are using lib-jitsi-meet without Jitsi Meet, you should use this
* file as reference only because the implementation is Jitsi Meet-specific.
*
* NOTE: For optimal results this file should be included right after
* external_connect.js.
*/
if (typeof createConnectionExternally === 'function') {
// URL params have higher priority than config params.
// Do not use external connect if websocket is enabled.
let url
= parseURLParams(window.location, true, 'hash')[
'config.externalConnectUrl']
|| config.websocket ? undefined : config.externalConnectUrl;
const isRecorder
= parseURLParams(window.location, true, 'hash')['config.iAmRecorder'];
let roomName;
if (url && (roomName = getRoomName()) && !isRecorder) {
url += `?room=${roomName}`;
const token = parseURLParams(window.location, true, 'search').jwt;
if (token) {
url += `&token=${token}`;
}
createConnectionExternally(
url,
connectionInfo => {
// Sets that global variable to be used later by connect method
// in connection.js.
window.XMPPAttachInfo = {
status: 'success',
data: connectionInfo
};
checkForConnectHandlerAndConnect();
},
errorCallback);
} else {
errorCallback();
}
} else {
errorCallback();
}
/**
* Check if connect from connection.js was executed and executes the handler
* that is going to finish the connect work.
*
* @returns {void}
*/
function checkForConnectHandlerAndConnect() {
window.APP
&& window.APP.connect.status === 'ready'
&& window.APP.connect.handler();
}
/**
* Implements a callback to be invoked if anything goes wrong.
*
* @param {Error} error - The specifics of what went wrong.
* @returns {void}
*/
function errorCallback(error) {
// The value of error is undefined if external connect is disabled.
error && console.warn(error);
// Sets that global variable to be used later by connect method in
// connection.js.
window.XMPPAttachInfo = {
status: 'error'
};
checkForConnectHandlerAndConnect();
}

View File

@ -2,13 +2,13 @@
display: inline-block; display: inline-block;
&-content { &-content {
position: relative; background: $menuBG;
right: auto; border-radius: 3px;
margin-bottom: 4px; font-size: 14px;
line-height: 24px;
max-height: 456px; max-height: 456px;
overflow: auto; overflow: auto;
width: 300px; width: 300px;
&-ul { &-ul {
margin:0; margin:0;
padding:0; padding:0;
@ -16,33 +16,90 @@
} }
} }
&-header:hover { &-header {
background-color: initial; color: #fff;
cursor: initial; align-items: center;
display: flex;
margin-top: 8px;
padding: 8px 16px;
&-icon {
display: inline-block;
svg {
fill: #fff;
}
}
&--bordered {
border-bottom: 1px solid #4C4D50;
}
&-text {
margin-left: 12px;
}
} }
&-entry-text { &-entry {
max-width: 213px; align-items: center;
color: #fff;
cursor: pointer;
display: flex;
padding: 8px 0;
margin-left: 48px;
&.left-margin { &--selected {
margin-left: 36px; background: #131519;
cursor: initial;
margin-left: 0;
padding-left: 18px;
}
&-text {
color: #fff;
display: inline-block;
line-height: 24px;
text-overflow: ellipsis;
max-width: 213px;
overflow: hidden;
white-space: nowrap;
} }
} }
&-speaker { &-speaker {
position: relative; position: relative;
&-ul {
margin:0;
padding:0;
list-style-type: none;
}
&:hover, &:focus-within, &:focus { &:hover, &:focus-within, &:focus {
.audio-preview-entry {
background: #36383C;
margin-left: 0;
padding-left: 48px;
&--selected {
padding-left: 18px;
background: $newToolbarBackgroundColor;
}
}
.audio-preview-test-button { .audio-preview-test-button {
display: inline-block; display: inline-block;
} }
.audio-preview-entry-text { .audio-preview-entry-text {
max-width: 178px; max-width: 178px;
margin-right: 0;
} }
} }
&:last-child {
padding-bottom: 8px;
}
.audio-preview-entry-text { .audio-preview-entry-text {
max-width: 238px; max-width: 238px;
} }
@ -51,6 +108,19 @@
&-microphone { &-microphone {
position: relative; position: relative;
&:hover {
.audio-preview-entry {
background: #36383C;
margin-left: 0;
padding-left: 48px;
&--selected {
background: $newToolbarBackgroundColor;
padding-left: 18px;
}
}
}
&--nometer { &--nometer {
.audio-preview-entry-text { .audio-preview-entry-text {
max-width: 238px; max-width: 238px;
@ -70,21 +140,42 @@
display: inline-block; display: inline-block;
width: 14px; width: 14px;
& svg {
fill: #1C2025;
}
&--check {
background: #31B76A;
margin-right: 16px;
}
&--exclamation { &--exclamation {
margin-left: 6px; margin-left: 6px;
& svg { & svg {
fill: #E54B4B; fill: #E54B4B;
} }
} }
} }
&-hr {
border-top: 1px solid #4C4D50;
border-bottom: 0;
}
&-test-button { &-test-button {
display: none; display: none;
padding: 4px 10px; background: #FFF;
border: 1px solid #D1DBE8;
border-radius: 3px;
color: #1C2025;
cursor: pointer;
font-weight: 600;
font-size: 0.8rem;
line-height: 24px;
padding: 2px 8px;
position: absolute; position: absolute;
right: 16px; right: 16px;
top: 6px; top: 5px;
} }
&-meter-mic { &-meter-mic {
@ -93,7 +184,9 @@
top: 14px; top: 14px;
} }
&-checkbox-container { // Override @atlaskit/InlineDialog container which is made with styled components
padding: 10px 16px; & > div:nth-child(2) {
outline: none;
padding: 0;
} }
} }

View File

@ -126,12 +126,6 @@ form {
background-size: contain; background-size: contain;
} }
.leftwatermarknomargin {
background-position: center left;
background-repeat: no-repeat;
background-size: contain;
}
.rightwatermark { .rightwatermark {
right: 32px; right: 32px;
top: 32px; top: 32px;
@ -188,8 +182,3 @@ form {
background: rgba(0, 0, 0, .5); background: rgba(0, 0, 0, .5);
border-radius: 4px; border-radius: 4px;
} }
/* Necessary for the new icons to work properly. */
.jitsi-icon svg path {
fill: inherit !important;
}

View File

@ -22,7 +22,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
// extract header + tabs height // extract header + tabs height
height: calc(100% - 119px); height: calc(100% - 102px);
} }
.chat-panel-no-tabs { .chat-panel-no-tabs {
@ -30,18 +30,12 @@
height: calc(100% - 70px); height: calc(100% - 70px);
} }
#chat-conversation-container {
// extract message input height
height: calc(100% - 64px);
overflow: hidden;
position: relative;
}
#chatconversation { #chatconversation {
box-sizing: border-box; box-sizing: border-box;
flex: 1; flex: 1;
font-size: 10pt; font-size: 10pt;
height: 100%; // extract message input height
height: calc(100% - 68px);
line-height: 20px; line-height: 20px;
overflow: auto; overflow: auto;
padding: 16px; padding: 16px;
@ -76,6 +70,32 @@
} }
} }
#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;
}
}
&.lobby-chat-recipient {
background-color: $chatLobbyMessageBackgroundColor;
}
}
.chat-header { .chat-header {
height: 70px; height: 70px;
@ -98,18 +118,64 @@
} }
.chat-input-container { .chat-input-container {
padding: 0 16px 24px; padding: 0 16px 16px;
&.populated {
#chat-input {
.send-button {
background: #1B67EC;
cursor: pointer;
margin-left: 0.3rem;
@media (hover: hover) and (pointer: fine) {
&:hover {
background: #3D82FB;
}
}
&:active {
background: #0852D4;
}
path {
fill: #fff;
}
}
}
}
} }
#chat-input { #chat-input {
border: 1px solid $chatInputSeparatorColor;
display: flex; display: flex;
align-items: flex-end; padding: 4px;
position: relative; border-radius: 3px;
&:focus-within {
border: 1px solid #619CF4;
}
* {
background-color: transparent;
}
} }
.chat-input { .send-button-container {
flex: 1; display: flex;
margin-right: 8px; align-items: center;
}
.send-button {
display: flex;
align-items: center;
justify-content: center;
height: 40px;
width: 40px;
border-radius: 3px;
path {
fill: $chatInputSeparatorColor;
}
} }
.smiley-button { .smiley-button {
@ -236,6 +302,15 @@
-webkit-user-select: text; -webkit-user-select: text;
user-select: text; user-select: text;
} }
.display-name {
font-size: 12px;
font-weight: 600;
margin-bottom: 5px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
} }
.sr-only { .sr-only {
@ -252,11 +327,24 @@
} }
.chatmessage { .chatmessage {
background-color: $chatRemoteMessageBackgroundColor;
border-radius: 0px 6px 6px 6px;
box-sizing: border-box;
color: white;
margin-top: 3px;
max-width: 100%;
position: relative;
&.localuser { &.localuser {
background-color: $chatLocalMessageBackgroundColor; background-color: $chatLocalMessageBackgroundColor;
border-radius: 6px 0px 6px 6px; border-radius: 6px 0px 6px 6px;
} }
.usermessage {
white-space: pre-wrap;
font-size: 14px;
}
&.error { &.error {
border-radius: 0px; border-radius: 0px;
@ -271,12 +359,22 @@
} }
} }
.privatemessagenotice {
font-size: 11px;
font-weight: 100;
}
.messagecontent { .messagecontent {
margin: 8px;
max-width: 100%; max-width: 100%;
overflow: hidden; overflow: hidden;
} }
} }
.timestamp {
color: #757575;
}
#smileys { #smileys {
font-size: 20pt; font-size: 20pt;
margin: auto; margin: auto;
@ -297,9 +395,7 @@
.smiley-input { .smiley-input {
display: flex; display: flex;
position: absolute; position: relative;
top: 0;
left: 0;
} }
.smileys-panel { .smileys-panel {
@ -307,7 +403,7 @@
box-sizing: border-box; box-sizing: border-box;
background-color: rgba(0, 0, 0, .6) !important; background-color: rgba(0, 0, 0, .6) !important;
height: auto; height: auto;
display: flex; display: none;
overflow: hidden; overflow: hidden;
position: absolute; position: absolute;
width: calc(#{$sidebarWidth} - 32px); width: calc(#{$sidebarWidth} - 32px);
@ -322,6 +418,11 @@
*/ */
transition: max-height 0.3s; transition: max-height 0.3s;
&.show-smileys {
display: flex;
max-height: 500%;
}
#smileysContainer { #smileysContainer {
background-color: $chatBackgroundColor; background-color: $chatBackgroundColor;
border-top: 1px solid $chatInputSeparatorColor; border-top: 1px solid $chatInputSeparatorColor;
@ -350,9 +451,24 @@
} }
.chat-message-group { .chat-message-group {
display: flex;
flex-direction: column;
&.local { &.local {
align-items: flex-end; align-items: flex-end;
.chatmessage {
background-color: $chatLocalMessageBackgroundColor;
border-radius: 6px 0px 6px 6px;
&.privatemessage {
background-color: $chatPrivateMessageBackgroundColor;
}
&.lobbymessage {
background-color: $chatLobbyMessageBackgroundColor;
}
}
.display-name { .display-name {
display: none; display: none;
} }
@ -363,10 +479,58 @@
} }
&.error { &.error {
.chatmessage {
background-color: $defaultWarningColor;
border-radius: 0px;
font-weight: 100;
}
.display-name { .display-name {
display: none; display: none;
} }
} }
.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;
&.lobbychatmessageactions {
border-left-color: $chatLobbyActionsSeparatorColor;
}
.toolbox-icon {
cursor: pointer;
}
}
}
}
.chatmessage {
background-color: $chatRemoteMessageBackgroundColor;
border-radius: 0px 6px 6px 6px;
display: inline-block;
margin-top: 3px;
color: white;
&.privatemessage {
background-color: $chatPrivateMessageBackgroundColor;
}
&.lobbymessage {
background-color: $chatLobbyMessageBackgroundColor;
}
}
} }
.chat-dialog { .chat-dialog {
@ -409,3 +573,41 @@
background: #36383C; background: #36383C;
border-radius: 3px; border-radius: 3px;
} }
.chat-tabs-container {
width: 100%;
border-bottom: thin solid #292929;
display: flex;
justify-content: space-around;
}
.chat-tab {
font-size: 1.2em;
padding-bottom: 0.5em;
width: 50%;
text-align: center;
color: #8B8B8B;
cursor: pointer;
}
.chat-tab-focus {
border-bottom-style: solid;
color: #FFF;
}
.chat-tab-title {
margin-right: 8px;
}
.chat-tab-badge {
background-color: #165ecc;
border-radius: 50%;
box-sizing: border-box;
font-weight: 700;
overflow: hidden;
text-align: center;
text-overflow: ellipsis;
vertical-align: middle;
padding: 0 4px;
color: #FFF;
}

View File

@ -24,6 +24,8 @@
} }
.drawer-menu { .drawer-menu {
background: #242528;
border-radius: 16px 16px 0 0;
overflow-y: auto; overflow-y: auto;
margin-bottom: env(safe-area-inset-bottom, 0); margin-bottom: env(safe-area-inset-bottom, 0);
width: 100%; width: 100%;

View File

@ -1,67 +0,0 @@
@keyframes rotateAroundY {
from { transform: rotateY(0deg); }
to { transform: rotateY(360deg); }
}
@keyframes rainbowRoad {
to {
background-position: 400% 0 !important;
}
}
body {
font-family: "Comic Sans MS", "Comic Sans", sans-serif !important;
}
a {
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
-webkit-background-clip: text !important;
-webkit-text-fill-color: transparent !important;
-moz-background-clip: text !important;
-moz-text-fill-color: transparent !important;
animation: rainbowRoad 8s linear infinite !important;
}
a:hover {
}
.dominant-speaker {
box-shadow: inset 0px 0px 0px 4px rgba(255,0,255,0.33) !important;
}
.display-avatar-only {
background-image: url("");
}
.videocontainer:nth-child(odd) {
transform: rotate(1.5deg);
}
.videocontainer:nth-child(even) {
transform: rotate(-1.3deg);
}
#largeVideoContainer.videocontainer {
transform: none;
}
.sideToolbarContainer {
transform: rotate(-1.1deg);
}
.displayname:before {
content: "♡︎ ";
}
.displayname:after {
content: " ♡︎";
}
.avatar, .userAvatar {
transform: rotateY(0deg);
}
.avatar:hover, .userAvatar:hover {
animation: rotateAroundY 3.6s linear infinite;
}

View File

@ -82,7 +82,6 @@
} }
.left-column { .left-column {
order: -1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-grow: 0; flex-grow: 0;
@ -93,7 +92,6 @@
.right-column { .right-column {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-start;
flex-grow: 1; flex-grow: 1;
padding-left: 16px; padding-left: 16px;
padding-top: 13px; padding-top: 13px;
@ -101,11 +99,11 @@
} }
.title { .title {
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
line-height: 16px; line-height: 16px;
margin-bottom: 4px; padding-bottom: 4px;
} }
.subtitle { .subtitle {
color: #5E6D7A; color: #5E6D7A;
@ -127,7 +125,8 @@
cursor: pointer; cursor: pointer;
} }
&.with-click-handler:hover { &.with-click-handler:hover,
&.with-click-handler:focus {
background-color: #c7ddff; background-color: #c7ddff;
} }

View File

@ -3,28 +3,28 @@
display: inline-block; display: inline-block;
& > svg { & > svg {
fill: #525252; fill: #4E5E6C;
width: 38px; width: 38px;
} }
} }
&.metr--disabled { &.metr--disabled {
& > svg { & > svg {
fill: #525252; fill: #4E5E6C;
} }
} }
} }
.metr-l-0 { .metr-l-0 {
rect:first-child { rect:first-child {
fill: #1EC26A; fill: #31B76A;
} }
} }
@for $i from 1 through 7 { @for $i from 1 through 7 {
.metr-l-#{$i} { .metr-l-#{$i} {
rect:nth-child(-n+#{$i+1}) { rect:nth-child(-n+#{$i+1}) {
fill: #1EC26A; fill: #31B76A;
} }
} }
} }

View File

@ -1,3 +1,444 @@
.polls-panel { .poll-dialog {
height: calc(100% - 119px); font-size: 14px;
font-weight: 400;
line-height: 20px;
h1, span, li, strong {
color: #bce;
}
ol {
margin: 0;
}
}
.poll-question-field {
padding: 8px 16px;
padding-bottom: 24px;
border-bottom: 1px solid #525252;
}
.poll-header {
margin-bottom: 8px;
}
.poll-creator {
color: #C2C2C2;
font-weight: 600;
margin: 4px 0 16px 0;
}
.poll-answer-container {
background: #3D3D3D;
border-radius: 3px;
margin-bottom: 8px;
@media (max-width: 580px) {
&> span {
padding: 8px 0;
}
svg {
margin-top: 6px;
}
}
}
.poll-answer-option {
font-weight: 400;
display: block;
margin: 4px;
@media (max-width: 580px) {
font-size: 16px;
margin: 11px 8px
}
}
.poll-answer-field-list, .poll-answer-list, .poll-result-list {
list-style-type: none;
padding: 0;
margin: 0;
}
.poll-answer-field-list {
padding: 0 16px;
}
ol.poll-result-list {
margin-bottom: 1.5em;
}
.poll-result-list > li {
margin-bottom: 16px;
}
.poll-answer-field {
flex-direction: column;
align-items: stretch;
margin-bottom: 16;
}
.poll-answer-field:last-child {
margin-bottom: 0;
}
.poll-create-option-row {
display: 'flex';
margin-bottom: 4;
}
// Needeed to override atlaskit default blue color
.poll-create-container .jsYMHu {
background: #292929;
border-color: #808090;
color: #fff // #808090
}
.poll-add-button {
display: flex;
justify-content: center;
padding: 8px 16px;
}
.poll-remove-option-button {
background: 0 0;
border: none;
color: #E04757;
padding-left: 0;
}
.poll-create-add-option {
border: none;
background-color: #292929;
padding: 3px;
width: 100%;
}
.poll-icon-button, .poll-drag-handle {
.jitsi-icon svg {
fill: #929292;
}
}
.poll-drag-handle {
background-color: transparent;
border: none;
cursor: grab;
padding-left: 8;
padding-top: 8px;
display: flex;
}
.poll-dragged {
opacity: 0.5;
* {
cursor: grabbing !important;
}
}
.poll-question {
font-size: 16px;
font-weight: 600;
line-height: 26px;
}
.poll-answer-voters {
font-weight: lighter;
list-style-type: none;
border: #616161 solid 1px;
border-radius: 3px;
padding: 2px 6px;
margin: 4px 0px 12px;
background-color: #616161;
}
.poll-answer-header {
display: flex;
justify-content: space-between;
}
.poll-answer-vote-name {
flex-shrink: 1;
overflow-wrap: anywhere
}
.poll-answer-vote-count-container{
display: flex;
}
.poll-answer-vote-count {
margin-left: 10px;
white-space: nowrap;
flex: 1;
text-align: right;
}
.poll-answer-short-results{
display: flex;
min-width: 10em;
justify-content: space-between;
align-items: center;
}
.poll-bar-container, .poll-bar {
border-radius: 3px;
height: 6px;
}
.poll-bar-container {
background-color: #616161;
max-width: 160px;
margin-top: 3px;
flex: 1;
}
.poll-bar {
background-color: #246FE5;
}
.poll-message-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
margin-top: 5px;
}
.poll-notice {
font-weight: 100;
margin-right: 10px;
}
.poll-show-details {
background-color: transparent;
border: none;
&:hover {
text-decoration: underline;
}
}
.poll-result-links {
display: flex;
flex-direction: row;
justify-content: space-between;
a.poll-detail-link, a.poll-change-vote-link {
color: #669AEC;
cursor: pointer;
font-weight: 600;
text-decoration: none;
&:hover {
color: #669AEC;
}
&:visited {
color: #669AEC;
}
}
}
.polls-pane-content {
height: 100%;
position: relative;
}
.pane-content{
display: flex;
flex-direction: column;
height: 100%;
justify-content: center;
align-items: center;
width: 100%;
}
.empty-pane-icon {
width: 50%;
padding: 24px;
}
.empty-pane-icon svg {
fill: #3D3D3D;
width: 100%;
height: auto;
}
.empty-pane-message {
color: #fff;
padding: 0 16px;
text-align: center;
}
.poll-results, .poll-answer {
background: #292929;
border-radius: 8px;
border: 1px solid #666666;
margin: 16px;
padding: 16px;
word-break: break-word;
}
.poll-results {
color: #fff;
}
.poll-answer {
h1, strong ,span {
color: #fff;
}
}
.poll-create-label {
color: #C2C2C2;
display: flex;
font-weight: 400;
margin-bottom: 4;
}
.expandable-input{
line-height: 18px;
resize: none;
width: 100%;
height: 40px;
box-sizing: border-box;
overflow: hidden;
border: 1px solid #666666;
background-color: #141414;
color: #FFF;
border-radius: 6px;
padding: 10px 16px;
}
#polls-panel {
height: calc(100% - 102px);
}
.poll-container {
font-size: 14px;
font-weight: 600;
height: calc(100% - 88px);
line-height: 20px;
overflow-y: auto;
position: relative;
& > * + *:not(.ignore-child) {
margin-top: 16px;
}
@media (max-width: 580px) {
height: calc(100% - 102px);
}
}
.poll-create-header {
color: #fff;
font-size: 20px;
line-height: 28px;
margin: 20px 16px;
font-weight: 600;
}
.poll-create-container {
padding: 8px 0;
}
.poll-create-footer {
background-color: #141414;
bottom: 0;
position: absolute;
width: calc(100% - 32px);
}
.poll-footer {
display: flex;
justify-content: space-between;
padding: 0 16px 16px 16px;
}
.poll-answer-footer {
padding: 8px 0 0 0;
}
.poll-button {
align-items: center;
border: 0;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
display: flex;
justify-content: center;
min-height: 40px;
width: 100%;
&:disabled {
cursor: initial;
}
@media (max-width: 580px) {
min-height: 48px;
}
}
.poll-button-primary {
background-color: #0056E0;
&:hover {
background-color: #246FE5;
}
&:active {
background-color: #0045B3;
}
&:focus {
background-color: #0045B3;
border: 3px solid #99BBF3;
}
&:disabled {
background-color: #00225A;
color: #858585;
}
}
.poll-button-secondary {
background-color: #3D3D3D;
&:hover {
background-color: #525252;
}
&:active {
background-color: #292929;
}
&:focus {
background-color: #292929;
border: 3px solid #858585;
}
&:disabled {
background-color: #141414;
color: #858585;
}
}
.poll-button-short {
max-width: 132px;
}
.poll-button-shortest {
max-width: 117px;
}
.poll-button-short,
.poll-button-shortest {
@media (max-width: 580px) {
min-width: 49%;
}
} }

View File

@ -45,7 +45,3 @@
margin: -16px -24px; margin: -16px -24px;
z-index: $popoverZ; z-index: $popoverZ;
} }
.excalidraw .popover {
margin: 0;
}

View File

@ -5,15 +5,15 @@
.popupmenu__contents { .popupmenu__contents {
.popupmenu__volume-slider { .popupmenu__volume-slider {
&::-webkit-slider-runnable-track { &::-webkit-slider-runnable-track {
background-color: #246FE5; background-color: $popupSliderColor;
} }
&::-moz-range-track { &::-moz-range-track {
background-color: #246FE5; background-color: $popupSliderColor;
} }
&::-ms-fill-lower { &::-ms-fill-lower {
background-color: #246FE5; background-color: $popupSliderColor;
} }
} }
} }

View File

@ -18,36 +18,12 @@
align-items: center; align-items: center;
font-size: 14px; font-size: 14px;
margin-left: 16px; margin-left: 16px;
max-width: 70%;
}
&.space-top {
margin-top: 10px;
} }
} }
.recording-header-line { .recording-header-line {
border-top: 1px solid #5e6d7a; border-top: 1px solid #5e6d7a;
padding-top: 16px; padding-top: 32px;
margin-top: 16px;
}
.local-recording-warning {
margin-top: 8px;
display: block;
font-size: 14px;
line-height: 20px;
padding: 8px 16px;
&.text {
color: #fff;
background-color: #3D3D3D;
}
&.notification {
color: #040404;
background-color: #F8AE1A;
}
} }
.recording-switch-disabled { .recording-switch-disabled {
@ -64,7 +40,7 @@
border-radius: 4px; border-radius: 4px;
height: 40px; height: 40px;
justify-content: center; justify-content: center;
width: 42px; width: 56px;
} }
.cloud-content-recording-icon-container { .cloud-content-recording-icon-container {
@ -76,14 +52,14 @@
} }
.jitsi-recording-header { .jitsi-recording-header {
margin-bottom: 16px; margin-bottom: 32px;
} }
.jitsi-content-recording-icon-container-with-switch { .jitsi-content-recording-icon-container-with-switch {
background-color: #FFFFFF; background-color: #FFFFFF;
border-radius: 4px; border-radius: 4px;
height: 40px; height: 40px;
width: 40px; width: 56px;
} }
.jitsi-content-recording-icon-container-without-switch { .jitsi-content-recording-icon-container-without-switch {

View File

@ -31,6 +31,10 @@
} }
} }
.welcome-tabs {
display: none;
}
.header-text-title { .header-text-title {
text-align: center; text-align: center;
} }
@ -52,6 +56,13 @@
.welcome-footer-row-block { .welcome-footer-row-block {
display: block; display: block;
} }
.welcome-badge {
margin-right: 16px;
}
.welcome-footer {
display: none;
}
} }
} }

View File

@ -25,29 +25,29 @@
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.1); box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
border-radius: 3px; border-radius: 3px;
cursor: pointer; cursor: pointer;
padding: 1px; padding: 4px;
position: absolute; position: absolute;
right: -4px; right: -4px;
top: -3px; top: -3px;
&:hover { &:hover {
background: #F2F3F4; background: #F2F3F4;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.1); box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
& > svg { &> svg {
fill: #040404; fill: #000;
} }
&.settings-button-small-icon--disabled { &.settings-button-small-icon--disabled {
background: #36383C; background: #36383C;
&> svg { &> svg {
fill: #929292; fill: #929292;
} }
} }
} }
& > svg { &> svg {
fill: #fff; fill: #fff;
} }

View File

@ -42,6 +42,42 @@
height: 28px; height: 28px;
} }
.subject-text {
background: rgba(0, 0, 0, 0.6);
border-radius: 3px 0px 0px 3px;
box-sizing: border-box;
font-size: 14px;
line-height: 28px;
padding: 0 16px;
height: 28px;
max-width: 324px;
@media (max-width: 300px) {
display: none;
}
&--content {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.subject-timer {
background: rgba(0, 0, 0, 0.8);
border-radius: 0px 3px 3px 0px;
box-sizing: border-box;
font-size: 12px;
line-height: 28px;
min-width: 34px;
padding: 0 8px;
height: 28px;
@media (max-width: 300px) {
display: none;
}
}
.details-container { .details-container {
width: 100%; width: 100%;
display: flex; display: flex;

View File

@ -120,34 +120,12 @@
margin: 8px 0; margin: 8px 0;
} }
div.hangup-button { .hangup-button {
background-color: #CB2233; background-color: $hangupColor;
@media (hover: hover) and (pointer: fine) { @media (hover: hover) and (pointer: fine) {
&:hover { &:hover {
background-color: #E04757; background-color: $hangupHoverColor;
}
&:active {
background-color: #A21B29;
}
}
svg {
fill: #fff;
}
}
div.hangup-menu-button {
background-color: #CB2233;
@media (hover: hover) and (pointer: fine) {
&:hover {
background-color: #E04757;
}
&:active {
background-color: #A21B29;
} }
} }

View File

@ -4,6 +4,9 @@
* Style variables * Style variables
*/ */
$baseFontFamily: -apple-system, BlinkMacSystemFont, 'open_sanslight', 'Helvetica Neue', Helvetica, Arial, sans-serif; $baseFontFamily: -apple-system, BlinkMacSystemFont, 'open_sanslight', 'Helvetica Neue', Helvetica, Arial, sans-serif;
$hangupColor:#DD3849;
$hangupHoverColor: #F25363;
$hangupFontSize: 2em;
/** /**
* Size variables. * Size variables.
@ -40,6 +43,7 @@ $newToolbarSizeMobile: 60px;
$newToolbarSizeWithPadding: calc(#{$newToolbarSize} + 24px); $newToolbarSizeWithPadding: calc(#{$newToolbarSize} + 24px);
$toolbarTitleFontSize: 19px; $toolbarTitleFontSize: 19px;
$overflowMenuItemColor: #fff; $overflowMenuItemColor: #fff;
$overflowMenuItemBackground: #36383C;
/** /**
@ -75,13 +79,14 @@ $modalTextColor: #333;
$chatActionsSeparatorColor: rgb(173, 105, 112); $chatActionsSeparatorColor: rgb(173, 105, 112);
$chatBackgroundColor: #131519; $chatBackgroundColor: #131519;
$chatInputSeparatorColor: #A4B8D1; $chatInputSeparatorColor: #A4B8D1;
$chatLobbyMessageBackgroundColor: #6A50D3;
$chatLobbyActionsSeparatorColor: #6A50D3; $chatLobbyActionsSeparatorColor: #6A50D3;
$chatLocalMessageBackgroundColor: #484A4F; $chatLocalMessageBackgroundColor: #484A4F;
$chatPrivateMessageBackgroundColor: rgb(153, 69, 77); $chatPrivateMessageBackgroundColor: rgb(153, 69, 77);
$chatRemoteMessageBackgroundColor: #242528; $chatRemoteMessageBackgroundColor: #242528;
$sidebarWidth: 315px; $sidebarWidth: 315px;
/** /**
* Misc. * Misc.
*/ */
$borderRadius: 4px; $borderRadius: 4px;
@ -96,6 +101,8 @@ $zindex0: 0;
$zindex1: 1; $zindex1: 1;
$zindex2: 2; $zindex2: 2;
$zindex3: 3; $zindex3: 3;
$toolbarBackgroundZ: 4;
$labelsZ: 5;
$subtitlesZ: 7; $subtitlesZ: 7;
$popoverZ: 8; $popoverZ: 8;
$reloadZ: 20; $reloadZ: 20;
@ -104,7 +111,10 @@ $ringingZ: 300;
$sideToolbarContainerZ: 300; $sideToolbarContainerZ: 300;
$toolbarZ: 250; $toolbarZ: 250;
$drawerZ: 351; $drawerZ: 351;
$tooltipsZ: 401;
$dropdownMaskZ: 900;
$dropdownZ: 901; $dropdownZ: 901;
$centeredVideoLabelZ: 1010;
$overlayZ: 1016; $overlayZ: 1016;
// Place filmstrip videos over toolbar in order // Place filmstrip videos over toolbar in order
// to make connection info visible. // to make connection info visible.
@ -165,9 +175,8 @@ $welcomePageHeaderPaddingBottom: 0px;
$welcomePageHeaderTitleMaxWidth: initial; $welcomePageHeaderTitleMaxWidth: initial;
$welcomePageHeaderTextAlign: center; $welcomePageHeaderTextAlign: center;
$welcomePageHeaderContainerMarginTop: 104px;
$welcomePageHeaderContainerDisplay: flex; $welcomePageHeaderContainerDisplay: flex;
$welcomePageHeaderContainerMargin: $welcomePageHeaderContainerMarginTop auto 0; $welcomePageHeaderContainerMargin: 104px 32px 0 32px;
$welcomePageHeaderTextTitleMarginBottom: 0; $welcomePageHeaderTextTitleMarginBottom: 0;
$welcomePageHeaderTextTitleFontSize: 42px; $welcomePageHeaderTextTitleFontSize: 42px;
@ -201,6 +210,11 @@ $deepLinkingDialInConferenceIdPadding: inherit;
$deepLinkingDialInConferenceIdBackgroundColor: inherit; $deepLinkingDialInConferenceIdBackgroundColor: inherit;
$deepLinkingDialInConferenceIdBorderRadius: inherit; $deepLinkingDialInConferenceIdBorderRadius: inherit;
$deepLinkingDialInConferenceNameFontSize: inherit;
$deepLinkingDialInConferenceNameLineHeight: inherit;
$deepLinkingDialInConferenceNameMarginBottom: none;
$deepLinkingDialInConferenceNameFontWeight: inherit;
$deepLinkingDialInConferenceDescriptionFontSize: 0.8em; $deepLinkingDialInConferenceDescriptionFontSize: 0.8em;
$deepLinkingDialInConferenceDescriptionLineHeight: inherit; $deepLinkingDialInConferenceDescriptionLineHeight: inherit;
$deepLinkingDialInConferenceDescriptionMarginBottom: none; $deepLinkingDialInConferenceDescriptionMarginBottom: none;
@ -234,6 +248,7 @@ $chromeExtensionBannerRightInMeeeting: 10px;
/** /**
* media type thresholds * media type thresholds
*/ */
$smallScreen: 700px;
$verySmallScreen: 500px; $verySmallScreen: 500px;
/** /**

View File

@ -3,38 +3,48 @@
display: inline-block; display: inline-block;
&-container { &-container {
max-height: 456px; max-height: 344px;
background: $menuBG;
border-radius: 3px;
overflow: auto; overflow: auto;
margin-bottom: 4px; padding: 8px;
position: relative;
right: auto;
} }
&-entry { &-entry {
cursor: pointer; cursor: pointer;
height: 138px; height: 168px;
width: 244px; margin-bottom: 8px;
position: relative; position: relative;
margin: 0 7px 4px; width: 284px;
border-radius: 6px;
box-sizing: border-box;
overflow: hidden;
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
&--selected { &--selected {
border: 2px solid #4687ED; border: 3px solid #31B76A;
border-radius: 3px;
cursor: default;
height: 162px;
width: 278px;
} }
} }
&-video { &-video {
border-radius: 3px;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
width: 100%; width: 100%;
} }
&-overlay {
background: rgba(42, 58, 75, 0.6);
height: 100%;
position: absolute;
width: 100%;
z-index: 1;
}
&-error { &-error {
align-items: center; align-items: center;
display: flex; display: flex;
@ -45,22 +55,23 @@
} }
&-label { &-label {
bottom: 8px;
color: #fff;
position: absolute; position: absolute;
bottom: 0; width: 100%;
left: 0;
right: 0;
max-width: 100%;
padding: 8px;
z-index: 2; z-index: 2;
&-container {
margin: 0 16px;
}
&-text { &-text {
background-color: rgba(0, 0, 0, 0.7); background-color: #131519;
border-radius: 4px; border-radius: 3px;
padding: 4px 8px; padding: 2px 8px;
color: #fff; font-size: 13px;
font-size: 12px; line-height: 20px;
line-height: 16px; margin: 0 auto;
font-weight: 600;
max-width: calc(100% - 16px); max-width: calc(100% - 16px);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -68,8 +79,8 @@
white-space: nowrap; white-space: nowrap;
} }
} }
// Override @atlaskit/InlineDialog container which is made with styled components
&-checkbox-container { & > div:nth-child(2) {
padding: 10px 14px; padding: 0;
} }
} }

View File

@ -29,16 +29,6 @@ body.welcome-page {
flex-direction: column; flex-direction: column;
margin: $welcomePageHeaderContainerMargin; margin: $welcomePageHeaderContainerMargin;
z-index: $zindex2; z-index: $zindex2;
align-items: center;
position: relative;
max-width: 688px;
}
.header-watermark-container {
position: absolute;
width: 100%;
height: 100%;
margin-top: calc(20px - #{$welcomePageHeaderContainerMarginTop});
} }
.header-text-title { .header-text-title {
@ -90,7 +80,7 @@ body.welcome-page {
font-size: 14px; font-size: 14px;
padding-left: 10px; padding-left: 10px;
&.focus-visible { &:focus {
outline: auto 2px #005fcc; outline: auto 2px #005fcc;
} }
} }
@ -133,11 +123,16 @@ body.welcome-page {
max-width: calc(100% - 40px); max-width: calc(100% - 40px);
padding: 16px 0 39px 0; padding: 16px 0 39px 0;
width: $welcomePageEnterRoomWidth; width: $welcomePageEnterRoomWidth;
text-align: center;
a { p {
color: inherit; color: $welcomePageDescriptionColor;
font-weight: 600; float: left;
text-align: $welcomePageHeaderTextAlign;
a {
color: inherit;
font-weight: 600;
}
} }
} }
} }
@ -167,7 +162,7 @@ body.welcome-page {
margin: 4px; margin: 4px;
display: $welcomePageTabButtonsDisplay; display: $welcomePageTabButtonsDisplay;
[role="tab"] { .tab {
background-color: #c7ddff; background-color: #c7ddff;
border-radius: 7px; border-radius: 7px;
cursor: pointer; cursor: pointer;
@ -176,10 +171,8 @@ body.welcome-page {
margin: 2px; margin: 2px;
padding: 7px 0; padding: 7px 0;
text-align: center; text-align: center;
color: inherit;
border: 0;
&[aria-selected="true"] { &.selected {
background-color: #FFF; background-color: #FFF;
} }
} }
@ -207,8 +200,8 @@ body.welcome-page {
color: $welcomePageDescriptionColor; color: $welcomePageDescriptionColor;
padding: 4px; padding: 4px;
position: absolute; position: absolute;
top: calc(35px - #{$welcomePageHeaderContainerMarginTop}); top: 32px;
right: 0; right: 32px;
z-index: $zindex2; z-index: $zindex2;
* { * {
@ -231,11 +224,6 @@ body.welcome-page {
width: $welcomePageWatermarkWidth; width: $welcomePageWatermarkWidth;
height: $welcomePageWatermarkHeight; height: $welcomePageWatermarkHeight;
} }
.watermark.leftwatermarknomargin {
width: $welcomePageWatermarkWidth;
height: $welcomePageWatermarkHeight;
}
} }
&.without-content { &.without-content {
@ -254,17 +242,10 @@ body.welcome-page {
padding-top: 40px; padding-top: 40px;
} }
.welcome-card-column { .welcome-card-row {
display: flex; display: flex;
justify-content: center; justify-content: center;
flex-direction: column; padding: 0 32px;
align-items: center;
max-width: 688px;
margin: auto;
> div {
margin-bottom: 16px;
}
} }
.welcome-card-text { .welcome-card-text {
@ -272,7 +253,7 @@ body.welcome-page {
} }
.welcome-card { .welcome-card {
width: 100%; width: 49%;
border-radius: 8px; border-radius: 8px;
&--dark { &--dark {
@ -287,6 +268,10 @@ body.welcome-page {
&--grey { &--grey {
background: #F2F3F4; background: #F2F3F4;
} }
&--shadow {
box-shadow: 0px 4px 30px rgba(0, 0, 0, 0.15);
}
} }
.welcome-footer { .welcome-footer {

View File

@ -68,12 +68,6 @@
} }
.buttons { .buttons {
margin-top: 16px; margin-top: 16px;
display: flex;
align-items: center;
&>button:first-child {
margin-right: 8px;
}
} }
} }
} }

View File

@ -67,13 +67,6 @@
font-size: 1em; font-size: 1em;
} }
.dial-in-conference-id {
text-align: center;
min-width: 200px;
margin-top: 40px;
}
.dial-in-conference-id { .dial-in-conference-id {
margin: $deepLinkingDialInConferenceIdMargin; margin: $deepLinkingDialInConferenceIdMargin;
padding: $deepLinkingDialInConferenceIdPadding; padding: $deepLinkingDialInConferenceIdPadding;
@ -81,12 +74,24 @@
border-radius: $deepLinkingDialInConferenceIdBorderRadius; border-radius: $deepLinkingDialInConferenceIdBorderRadius;
} }
.dial-in-conference-name {
font-size: $deepLinkingDialInConferenceNameFontSize;
line-height: $deepLinkingDialInConferenceNameLineHeight;
margin-bottom: $deepLinkingDialInConferenceNameMarginBottom;
font-weight: $deepLinkingDialInConferenceNameFontWeight;
}
.dial-in-conference-description { .dial-in-conference-description {
font-size: $deepLinkingDialInConferenceDescriptionFontSize; font-size: $deepLinkingDialInConferenceDescriptionFontSize;
line-height: $deepLinkingDialInConferenceDescriptionLineHeight; line-height: $deepLinkingDialInConferenceDescriptionLineHeight;
margin-bottom: $deepLinkingDialInConferenceDescriptionMarginBottom; margin-bottom: $deepLinkingDialInConferenceDescriptionMarginBottom;
} }
.dial-in-conference-pin {
font-size: $deepLinkingDialInConferencePinFontSize;
line-height: $deepLinkingDialInConferencePinLineHeight;
}
.toll-free-list { .toll-free-list {
min-width: 80px; min-width: 80px;
} }

View File

@ -3,7 +3,6 @@
* tiled thumbnail experience. * tiled thumbnail experience.
*/ */
.tile-view, .tile-view,
.whiteboard-container,
.stage-filmstrip { .stage-filmstrip {
/** /**
* Let the avatar grow with the tile. * Let the avatar grow with the tile.

View File

@ -112,7 +112,7 @@
} }
/** /**
* Remove unnecessary padding that is normally used to prevent horizontal * Remove unnecssary padding that is normally used to prevent horizontal
* filmstrip from overlapping the left edge of the screen. * filmstrip from overlapping the left edge of the screen.
*/ */
#filmstripLocalVideo, #filmstripLocalVideo,

View File

@ -33,6 +33,7 @@ $flagsImagePath: "../images/";
@import 'reload_overlay/reload_overlay'; @import 'reload_overlay/reload_overlay';
@import 'mini_toolbox'; @import 'mini_toolbox';
@import 'modals/desktop-picker/desktop-picker'; @import 'modals/desktop-picker/desktop-picker';
@import 'modals/device-selection/device-selection';
@import 'modals/dialog'; @import 'modals/dialog';
@import 'modals/embed-meeting/embed-meeting'; @import 'modals/embed-meeting/embed-meeting';
@import 'modals/feedback/feedback'; @import 'modals/feedback/feedback';
@ -94,9 +95,3 @@ $flagsImagePath: "../images/";
@import 'notifications'; @import 'notifications';
/* Modules END */ /* Modules END */
/* Jeet crew BEGIN */
@import 'jiti';
/* Jeet crew END */

View File

@ -38,7 +38,3 @@
margin-top: 2px; margin-top: 2px;
display: block; display: block;
} }
.dialog-bottom-margin {
margin-bottom: 5px;
}

View File

@ -63,8 +63,3 @@
.desktop-source-preview-image-container { .desktop-source-preview-image-container {
padding: 10px; padding: 10px;
} }
.desktop-picker-tabs-container {
width: 65%;
margin-top: 3px;
}

View File

@ -0,0 +1,148 @@
.device-selection {
.device-selectors {
font-size: 14px;
> div {
display: block;
margin-bottom: 4px;
}
.device-selector-icon {
align-self: center;
color: inherit;
font-size: 20px;
margin-left: 3px;
}
.device-selector-label {
margin-bottom: 1px;
}
/* device-selector-trigger stylings attempt to mimic AtlasKit button */
.device-selector-trigger {
background-color: #0E1624;
border: 1px solid #455166;
border-radius: 5px;
display: flex;
height: 2.3em;
justify-content: space-between;
line-height: 2.3em;
overflow: hidden;
padding: 0 8px;
}
.device-selector-trigger-disabled {
.device-selector-trigger {
color: #a5adba;
cursor: default;
}
}
.device-selector-trigger-text {
overflow: hidden;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
}
.device-selection-column {
box-sizing: border-box;
display: inline-block;
vertical-align: top;
&.column-selectors {
margin-left: 15px;
width: 45%;
}
&.column-video {
width: 50%;
}
}
.device-selection-video-container {
border-radius: 3px;
margin-bottom: 5px;
.video-input-preview {
margin-top: 2px;
position: relative;
> video {
border-radius: 3px;
}
.video-input-preview-error {
color: $participantNameColor;
display: none;
left: 0;
position: absolute;
right: 0;
text-align: center;
top: 50%;
}
&.video-preview-has-error {
background: black;
.video-input-preview-error {
display: block;
}
}
.video-input-preview-display {
height: auto;
overflow: hidden;
width: 100%;
}
}
}
.audio-output-preview {
font-size: 14px;
a {
color: #6FB1EA;
cursor: pointer;
text-decoration: none;
}
a:hover {
color: #B3D4FF;
}
}
.audio-input-preview {
background: #1B2638;
border-radius: 5px;
height: 8px;
.audio-input-preview-level {
background: #75B1FF;
border-radius: 5px;
height: 100%;
-webkit-transition: width .1s ease-in-out;
-moz-transition: width .1s ease-in-out;
-o-transition: width .1s ease-in-out;
transition: width .1s ease-in-out;
}
}
}
.device-selection.video-hidden {
display: flex;
flex-direction: column;
width: 100%;
.column-selectors {
width: 100%;
margin-left: 0;
}
.column-video {
order: 1;
width: 100%;
margin-top: 8px;
}
}

View File

@ -9,6 +9,7 @@
font-size: 15px; font-size: 15px;
margin-left: auto; margin-left: auto;
margin-top: 16px; margin-top: 16px;
width: auto;
} }
&-code { &-code {

View File

@ -44,3 +44,59 @@
-webkit-animation-timing-function: ease-in-out; -webkit-animation-timing-function: ease-in-out;
animation-timing-function: ease-in-out animation-timing-function: ease-in-out
} }
.feedback-dialog {
.details {
textarea {
min-height: 100px;
}
}
.input-control {
background-color: $feedbackInputBg;
color: $feedbackInputTextColor;
&::-webkit-input-placeholder {
color: $feedbackInputPlaceholderColor;
}
&::-moz-placeholder { /* Firefox 19+ */
color: $feedbackInputPlaceholderColor;
}
&:-ms-input-placeholder {
color: $feedbackInputPlaceholderColor;
}
}
.rating {
line-height: 1.2;
margin-top: 10px;
text-align: center;
.star-label {
font-size: 14px;
height: 16px;
}
.star-btn {
color: inherit;
cursor: pointer;
display: inline-block;
font-size: 34px;
outline: none;
position: relative;
text-decoration: none;
@include transition(all .2s ease);
&.active,
&:hover,
&.starHover {
color: #36B37E;
};
}
.star-btn:focus,
.star-btn:active {
outline: 1px solid #B8C7E0;
}
}
}

View File

@ -38,6 +38,19 @@
color: #fff; color: #fff;
} }
.info-password-input {
width: 100%;
background-color: #0E1624;
border-radius: 3px;
border: 2px solid #202B3D;
color: inherit;
padding-left: 0;
}
.info-password-input:focus ,
.info-password-input:active {
border: 2px solid #B8C7E0;
}
.info-password-local { .info-password-local {
user-select: text; user-select: text;
} }
@ -50,8 +63,6 @@
} }
.dial-in-numbers-list { .dial-in-numbers-list {
max-width: 334px;
width: 100%;
margin-top: 20px; margin-top: 20px;
font-size: 12px; font-size: 12px;
line-height: 24px; line-height: 24px;
@ -61,6 +72,10 @@
text-align: left; text-align: left;
} }
tr {
border-bottom: 1px solid #d1dbe8;
}
.flag-cell { .flag-cell {
vertical-align: top; vertical-align: top;
width: 30px; width: 30px;
@ -89,7 +104,6 @@
font-weight: bold; font-weight: bold;
list-style: none; list-style: none;
vertical-align: top; vertical-align: top;
text-align: right;
} }
li.toll-free:empty:before { li.toll-free:empty:before {
@ -118,6 +132,11 @@
margin-top: 40px; margin-top: 40px;
} }
.dial-in-conference-name,
.dial-in-conference-pin {
font-size: 18px;
}
.dial-in-conference-description { .dial-in-conference-description {
margin: 12px; margin: 12px;
} }

View File

@ -84,6 +84,56 @@
} }
} }
&.icon-container {
display: none;
&.active {
display: flex;
width: calc(100% - 26px);
padding: 8px 8px 8px 16px;
background: #2A3A4B;
border: 1px solid #5E6D7A;
border-top: none;
border-radius: 0 0 3px 3px;
.copy-invite-icon, .provider-icon {
align-items: center;
cursor: pointer;
display: flex;
height: 40px;
place-content: center;
width: 40px;
}
&:hover > div:hover {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 4px;
}
& > :not(:last-child) {
margin-right: 16px;
}
.copy-invite-icon > div > svg > path {
fill: #A4B8D1;
}
}
}
&.dial-in-display {
.info-label {
color: #A4B8D1;
}
.dial-in-copy {
display: inline-block;
vertical-align: middle;
cursor: pointer;
height: 24px;
}
}
&.invite-buttons { &.invite-buttons {
width: 100%; width: 100%;
text-align: right; text-align: right;

View File

@ -1,13 +1,11 @@
.share-audio-dialog-container {
max-height: none;
}
.share-audio-dialog { .share-audio-dialog {
.share-audio-animation { .share-audio-animation {
width: 100%; width: 100%;
height: 90%; height: 90%;
object-fit: contain; object-fit: contain;
margin-bottom: 10px; }
input[type="checkbox"] + svg + span {
color: #9FB0CC;
} }
.separator-line { .separator-line {

View File

@ -13,14 +13,12 @@
} }
.password { .password {
align-items: flex-start; align-items: center;
display: flex; display: flex;
justify-content: flex-start; justify-content: space-between;
margin-top: 15px; margin-top: 15px;
flex-direction: column;
&-actions { &-actions {
margin-top: 10px;
a { a {
cursor: pointer; cursor: pointer;
text-decoration: none; text-decoration: none;
@ -28,8 +26,8 @@
color: #6FB1EA; color: #6FB1EA;
} }
& > :not(:last-child) { & > :first-child:not(:last-child) {
margin-right: 24px; margin-right: 24px;
} }
} }
} }

View File

@ -204,7 +204,3 @@
} }
} }
} }
.lobby-button-margin {
margin-bottom: 16px;
}

View File

@ -1,4 +1,5 @@
@import 'lobby'; @import 'lobby';
@import 'premeeting-screens'; @import 'premeeting-screens';
@import 'prejoin'; @import 'prejoin';
@import 'prejoin-dialog';
@import 'prejoin-third-party'; @import 'prejoin-third-party';

View File

@ -0,0 +1,118 @@
.prejoin-dialog {
background: #1C2025;
box-shadow: 0px 2px 20px rgba(0, 0, 0, 0.5);
border-radius: 5px;
color: #fff;
height: 400px;
width: 375px;
&--small {
height: 300;
width: 400;
}
&-label {
font-size: 15px;
line-height: 24px;
&-num {
background: #2b3b4b;
border: 1px solid #A4B8D1;
border-radius: 50%;
color: #fff;
display: inline-block;
height: 24px;
margin-right: 8px;
width: 24px;
}
}
&-container {
align-items: center;
background: rgba(0,0,0,0.6);
display: flex;
height: 100vh;
justify-content: center;
left: 0;
position: absolute;
top: 0;
width: 100vw;
z-index: 3;
}
&-flag {
display: inline-block;
margin-right: 8px;
transform: scale(1.2);
}
&-title {
display: inline-block;
font-size: 24px;
line-height: 32px;
}
&-icon {
cursor: pointer;
> svg {
fill: #A4B8D1;
}
}
&-btn {
width: 309px;
}
&-dialin-container {
text-align: center;
}
&-delimiter {
background: #5f6266;
border: 0;
height: 1px;
margin: 0;
padding: 0;
width: 100%;
&-container {
margin: 16px 0 24px 0;
position: relative;
}
&-txt-container {
position: absolute;
text-align: center;
top: -8px;
width: 100%;
}
&-txt {
background: #1C2025;
color: #5f6266;
font-size: 11px;
text-transform: uppercase;
padding: 0 8px;
}
}
.prejoin-dialog-btn.primary,
.action-btn.prejoin-dialog-btn.text {
width: 310px;
}
}
.prejoin-dialog-callout {
padding: 16px;
&-header {
display: flex;
justify-content: space-between;
margin-bottom: 24px;
}
&-picker {
margin: 8px 0 16px 0;
}
}

View File

@ -2,8 +2,7 @@ $sidePanelWidth: 300px;
.prejoin-third-party { .prejoin-third-party {
flex-direction: column-reverse; flex-direction: column-reverse;
z-index: auto;
.content { .content {
height: auto; height: auto;
margin: 0 auto; margin: 0 auto;

View File

@ -3,25 +3,6 @@
width: 100%; width: 100%;
} }
&-avatar {
margin: 8px auto 16px;
&-name {
color: white;
font-size: 16px;
font-weight: 600;
line-height: 26px;
margin-bottom: 32px;
text-align: center;
}
&-container {
align-items: center;
display: flex;
flex-direction: column;
}
}
&-error { &-error {
background-color: #E04757; background-color: #E04757;
border-radius: 6px; border-radius: 6px;
@ -41,28 +22,20 @@
&-dropdown-btns { &-dropdown-btns {
padding: 8px 0; padding: 8px 0;
} }
&-dropdown-container { &-dropdown-container {
position: relative; position: relative;
width: 100%; width: 100%;
/** /**
* Override default InlineDialog behaviour, since it does not play nicely with relative widths * Override default InlineDialog behaviour, since it does not play nicely with relative widths
*/ */
& > div:nth-child(2) { & > div:nth-child(2) {
background: #E0E0E0; background: #fff;
padding: 0; padding: 0;
position: absolute !important; position: absolute !important;
width: 100%; width: 100%;
} }
} }
} }
.prejoin-input {
margin-bottom: 16px;
width: 100%;
& input {
text-align: center;
}
}

View File

@ -1,4 +1,14 @@
.premeeting-screen { .premeeting-screen {
background: #292929;
bottom: 0;
display: flex;
font-size: 1.3em;
left: 0;
position: absolute;
right: 0;
top: 0;
z-index: $toolbarZ + 2;
.action-btn { .action-btn {
border-radius: 6px; border-radius: 6px;
box-sizing: border-box; box-sizing: border-box;
@ -65,44 +75,139 @@
} }
} }
#new-toolbox { .content {
bottom: 0; align-items: center;
box-sizing: border-box;
display: flex;
flex-direction: column;
flex-shrink: 0;
height: 100%;
margin: 0 30px;
padding: 24px 0 16px;
position: relative; position: relative;
transition: none; width: $prejoinDefaultContentWidth;
z-index: $toolbarZ + 2;
.toolbox-content { &-controls {
margin-bottom: 4px; align-items: center;
}
.toolbox-content-items {
@include ltr;
background: transparent;
box-shadow: none;
display: flex; display: flex;
justify-content: space-between; flex-direction: column;
padding: 8px 0; margin: auto;
}
.toolbox-content,
.toolbox-content-wrapper,
.toolbox-content-items {
box-sizing: border-box;
width: 100%; width: 100%;
.title {
color: #fff;
font-size: 28px;
font-weight: 600;
letter-spacing: -0.015;
line-height: 36px;
margin-bottom: 32px;
text-align: center;
}
input.field {
background-color: white;
border: none;
outline: none;
border-radius: 6px;
font-size: 14px;
line-height: 20px;
margin-bottom: 16px;
color: #1C2025;
padding: 10px 16px;
text-align: center;
width: 100%;
&.error {
border: 1px solid #E04757;
}
&.focused {
box-shadow: 0px 0px 1px 1.5px black, 0px 0px 1.3px 4px white;
}
}
#new-toolbox {
bottom: 0;
position: relative;
transition: none;
.toolbox-content {
margin-bottom: 4px;
}
.toolbox-content-items {
@include ltr;
background: transparent;
box-shadow: none;
display: flex;
justify-content: space-evenly;
padding: 8px 0;
}
.toolbox-content,
.toolbox-content-wrapper,
.toolbox-content-items {
box-sizing: border-box;
width: 100%;
}
}
}
}
@media (max-width: 720px) {
flex-direction: column-reverse;
.content {
height: auto;
margin: 0 auto;
}
}
// mobile phone landscape
@media (max-height: 420px) {
div.content {
padding: 16px 16px 0 16px;
} }
} }
@media (max-width: 400px) { @media (max-width: 400px) {
.content {
padding: 16px;
width: 100%;
&-controls {
input.field {
font-size: 16px;
padding: 14px 16px;
}
}
.title {
display: none;
}
}
.device-status-error { .device-status-error {
border-radius: 0; border-radius: 0;
margin: 0 -16px; margin: 0 -16px;
} }
input.field {
font-size: 16px;
padding: 14px 16px;
}
.action-btn { .action-btn {
font-size: 16px; font-size: 16px;
margin-bottom: 8px; margin-bottom: 8px;
padding: 11px 16px; padding: 11px 16px;
} }
} }
input::placeholder {
color: #040404;
}
} }
#preview { #preview {

View File

@ -6,6 +6,8 @@ $baseLight: #FFFFFF;
/** /**
* Controls * Controls
*/ */
$controlBackground: $baseLight;
$controlColor: #333333;
$sliderTrackBackground: #474747; $sliderTrackBackground: #474747;
$sliderThumbBackground: #3572b0; $sliderThumbBackground: #3572b0;
@ -58,20 +60,42 @@ $readOnlyInputColor: #a7a7a7;
$defaultDarkSelectionColor: #ccc; $defaultDarkSelectionColor: #ccc;
$buttonFontWeight: 400; $buttonFontWeight: 400;
$labelFontWeight: 400; $labelFontWeight: 400;
$hintFontSize: em(13, 14);
$linkFontColor: #3572b0; $linkFontColor: #3572b0;
$linkHoverFontColor: darken(#3572b0, 10%); $linkHoverFontColor: darken(#3572b0, 10%);
$dropdownColor: #333;
$errorColor: #c61600; $errorColor: #c61600;
// Feedback colors
$feedbackCancelFontColor: #333;
// Popover colors // Popover colors
$popoverFontColor: #ffffff !important; $popoverFontColor: #ffffff !important;
$popupSliderColor: #0376da;
// Toolbar // Toolbar
$secondaryToolbarBg: rgba(0, 0, 0, 0.5);
// TOFIX: Once moved to react rename to match the side panel class name.
$sideToolbarContainerBg: rgba(0, 0, 0, 0.75);
$toolbarBackground: rgba(0, 0, 0, 0.5); $toolbarBackground: rgba(0, 0, 0, 0.5);
$toolbarBadgeBackground: #165ECC; $toolbarBadgeBackground: #165ECC;
$toolbarBadgeColor: #FFFFFF; $toolbarBadgeColor: #FFFFFF;
$toolbarButtonColor: #FFFFFF;
$toolbarSelectBackground: rgba(0, 0, 0, .6);
$toolbarTitleColor: #FFFFFF;
$toolbarToggleBackground: #12499C;
/** /**
* Forms * Forms
*/ */
$selectBg: $baseLight; //dropdown
$selectFontColor: $controlColor;
$selectBg: $controlBackground;
$selectActiveBg: darken($controlBackground, 5%);
$selectActiveItemBg: darken($controlBackground, 20%);
/**
* TODO: Replace by themed component.
*/
$videoQualityActive: #57A0ff;

4
custom.d.ts vendored
View File

@ -1,4 +0,0 @@
declare module '*.svg' {
const content: any;
export default content;
}

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