Compare commits

..

1 Commits

2991 changed files with 146790 additions and 146908 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

@ -3,10 +3,12 @@ build/*
# Third-party source code which we (1) do not want to modify or (2) try to # Third-party source code which we (1) do not want to modify or (2) try to
# modify as little as possible. # modify as little as possible.
flow-typed/*
libs/* libs/*
resources/* resources/*
react/features/stream-effects/virtual-background/vendor/* react/features/stream-effects/virtual-background/vendor/*
react/features/face-landmarks/resources/* load-test/*
react/features/facial-recognition/resources/*
# ESLint will by default ignore its own configuration file. However, there does # ESLint will by default ignore its own configuration file. However, there does
# not seem to be a reason why we will want to risk being inconsistent with our # not seem to be a reason why we will want to risk being inconsistent with our
@ -14,4 +16,4 @@ react/features/face-landmarks/resources/*
!.eslintrc.js !.eslintrc.js
# Not worth it. # Not worth it.
actionTypes.ts actionTypes.js

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

@ -130,7 +130,7 @@ When adding a new feature, this would be the usual layout.
``` ```
react/features/sample/ react/features/sample/
├── actionTypes.ts ├── actionTypes.js
├── actions.js ├── actions.js
├── components ├── components
│   ├── AnotherComponent.js │   ├── AnotherComponent.js
@ -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

@ -2,39 +2,35 @@ BUILD_DIR = build
CLEANCSS = ./node_modules/.bin/cleancss CLEANCSS = ./node_modules/.bin/cleancss
DEPLOY_DIR = libs DEPLOY_DIR = libs
LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet
LIBFLAC_DIR = node_modules/libflacjs/dist/min
OLM_DIR = node_modules/@matrix-org/olm 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 FACIAL_MODELS_DIR = react/features/facial-recognition/resources
NODE_SASS = ./node_modules/.bin/sass NODE_SASS = ./node_modules/.bin/sass
NPM = npm NPM = npm
OUTPUT_DIR = . 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_DEV_SERVER = .\node_modules\.bin\webpack serve --mode development
else
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
endif
all: compile deploy clean all: compile deploy clean
compile: compile: compile-load-test
NODE_OPTIONS=--max-old-space-size=8192 \
$(WEBPACK) $(WEBPACK)
compile-load-test:
${NPM} install --prefix resources/load-test && ${NPM} run build --prefix resources/load-test
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-libflac deploy-olm deploy-tf-wasm deploy-css deploy-local deploy-facial-expressions
deploy-init: deploy-init:
rm -fr $(DEPLOY_DIR) rm -fr $(DEPLOY_DIR)
@ -44,17 +40,23 @@ 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)/flacEncodeWorker.min.js \
$(BUILD_DIR)/flacEncodeWorker.min.js.map \
$(BUILD_DIR)/dial_in_info_bundle.min.js \
$(BUILD_DIR)/dial_in_info_bundle.min.js.map \
$(BUILD_DIR)/alwaysontop.min.js \ $(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 \
$(BUILD_DIR)/analytics-ga.min.js \ $(BUILD_DIR)/analytics-ga.min.js \
$(BUILD_DIR)/analytics-ga.min.js.map \ $(BUILD_DIR)/analytics-ga.min.js.map \
$(BUILD_DIR)/face-landmarks-worker.min.js \ $(BUILD_DIR)/face-centering-worker.min.js \
$(BUILD_DIR)/face-landmarks-worker.min.js.map \ $(BUILD_DIR)/face-centering-worker.min.js.map \
$(BUILD_DIR)/noise-suppressor-worklet.min.js \ $(BUILD_DIR)/facial-expressions-worker.min.js \
$(BUILD_DIR)/noise-suppressor-worklet.min.js.map \ $(BUILD_DIR)/facial-expressions-worker.min.js.map \
$(DEPLOY_DIR) $(DEPLOY_DIR)
cp \ cp \
$(BUILD_DIR)/close3.min.js \ $(BUILD_DIR)/close3.min.js \
@ -66,9 +68,16 @@ 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)
deploy-libflac:
cp \
$(LIBFLAC_DIR)/libflac4-1.3.2.min.js \
$(LIBFLAC_DIR)/libflac4-1.3.2.min.js.mem \
$(DEPLOY_DIR)
deploy-olm: deploy-olm:
cp \ cp \
$(OLM_DIR)/olm.wasm \ $(OLM_DIR)/olm.wasm \
@ -89,27 +98,14 @@ 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 \
$(DEPLOY_DIR) $(DEPLOY_DIR)
deploy-face-landmarks: deploy-facial-expressions:
cp \ cp \
$(FACE_MODELS_DIR)/blazeface-front.bin \ $(FACIAL_MODELS_DIR)/* \
$(FACE_MODELS_DIR)/blazeface-front.json \
$(FACE_MODELS_DIR)/emotion.bin \
$(FACE_MODELS_DIR)/emotion.json \
$(DEPLOY_DIR) $(DEPLOY_DIR)
deploy-css: deploy-css:
@ -121,12 +117,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-libflac deploy-olm deploy-tf-wasm deploy-facial-expressions
$(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,13 @@ android {
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.5.1' // https://github.com/facebook/react-native/issues/31572
// We can update past 1.4.0 on RN 0.68
implementation ('androidx.appcompat:appcompat:1.3.1') {
version {
strictly '1.3.1'
}
}
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'

View File

@ -14,7 +14,7 @@
android:name="android.content.APP_RESTRICTIONS" android:name="android.content.APP_RESTRICTIONS"
android:resource="@xml/app_restrictions" /> android:resource="@xml/app_restrictions" />
<activity <activity
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize"
android:exported="true" android:exported="true"
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleTask" android:launchMode="singleTask"

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

@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material"
android:insetRight="@dimen/abc_edit_text_inset_horizontal_material"
android:insetTop="@dimen/abc_edit_text_inset_top_material"
android:insetBottom="@dimen/abc_edit_text_inset_bottom_material">
<selector>
<!--
This file is a copy of abc_edit_text_material (https://bit.ly/3k8fX7I).
The item below with state_pressed="false" and state_focused="false" causes a NullPointerException.
NullPointerException:tempt to invoke virtual method 'android.graphics.drawable.Drawable android.graphics.drawable.Drawable$ConstantState.newDrawable(android.content.res.Resources)'
<item android:state_pressed="false" android:state_focused="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
For more info, see https://bit.ly/3CdLStv (react-native/pull/29452) and https://bit.ly/3nxOMoR.
-->
<item android:state_enabled="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
<item android:drawable="@drawable/abc_textfield_activated_mtrl_alpha"/>
</selector>
</inset>

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,8 @@
<resources> <resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item> <!-- Customize your theme here. -->
<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

@ -10,26 +10,19 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.0.4' classpath 'com.android.tools.build:gradle:4.2.2'
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 = "30.0.3"
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") {
// For M1 Users we need to use the NDK 24 which added support for aarch64
ndkVersion = "24.0.8215888"
} else {
// Otherwise we default to the side-by-side NDK version from AGP.
ndkVersion = "21.4.7075529" ndkVersion = "21.4.7075529"
}
// The Maven artifact groupdId of the third-party react-native modules which // The Maven artifact groupdId of the third-party react-native modules which
// Jitsi Meet SDK for Android depends on and which are not available in // Jitsi Meet SDK for Android depends on and which are not available in

View File

@ -9,7 +9,7 @@
# Specifies the JVM arguments used for the daemon process. # Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings. # The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx1024m -XX:MaxPermSize=256m # Default value: -Xmx10248m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
@ -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.1.0
sdkVersion=99.0.0 sdkVersion=5.0.0

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

257
android/gradlew vendored
View File

@ -1,7 +1,7 @@
#!/bin/sh #!/usr/bin/env sh
# #
# Copyright © 2015-2021 the original authors. # Copyright 2015 the original author or authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -17,101 +17,67 @@
# #
############################################################################## ##############################################################################
# ##
# Gradle start up script for POSIX generated by Gradle. ## Gradle start up script for UN*X
# ##
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
############################################################################## ##############################################################################
# Attempt to set APP_HOME # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
app_path=$0 PRG="$0"
# Need this for relative symlinks.
# Need this for daisy-chained symlinks. while [ -h "$PRG" ] ; do
while ls=`ls -ld "$PRG"`
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path link=`expr "$ls" : '.*-> \(.*\)$'`
[ -h "$app_path" ] if expr "$link" : '/.*' > /dev/null; then
do PRG="$link"
ls=$( ls -ld "$app_path" ) else
link=${ls#*' -> '} PRG=`dirname "$PRG"`"/$link"
case $link in #( fi
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" APP_NAME="Gradle"
APP_BASE_NAME=${0##*/} APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD="maximum"
warn () { warn () {
echo "$*" echo "$*"
} >&2 }
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} >&2 }
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "$( uname )" in #( case "`uname`" in
CYGWIN* ) cygwin=true ;; #( CYGWIN* )
Darwin* ) darwin=true ;; #( cygwin=true
MSYS* | MINGW* ) msys=true ;; #( ;;
NONSTOP* ) nonstop=true ;; Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@ -121,9 +87,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java JAVACMD="$JAVA_HOME/jre/sh/java"
else else
JAVACMD=$JAVA_HOME/bin/java JAVACMD="$JAVA_HOME/bin/java"
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -132,7 +98,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD=java JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
@ -140,95 +106,80 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
case $MAX_FD in #( MAX_FD_LIMIT=`ulimit -H -n`
max*) if [ $? -eq 0 ] ; then
MAX_FD=$( ulimit -H -n ) || if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
warn "Could not query maximum file descriptor limit" MAX_FD="$MAX_FD_LIMIT"
esac fi
case $MAX_FD in #( ulimit -n $MAX_FD
'' | soft) :;; #( if [ $? -ne 0 ] ; then
*) warn "Could not set maximum file descriptor limit: $MAX_FD"
ulimit -n "$MAX_FD" || fi
warn "Could not set maximum file descriptor limit to $MAX_FD" else
esac warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi fi
# Collect all arguments for the java command, stacking in reverse order: # For Darwin, add options to specify how the application appears in the dock
# * args from the command line if $darwin; then
# * the main class name GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
# * -classpath fi
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=$( cygpath --unix "$JAVACMD" ) JAVACMD=`cygpath --unix "$JAVACMD"`
# Now convert the arguments - kludge to limit ourselves to /bin/sh # We build the pattern for arguments to be converted via cygpath
for arg do ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
if SEP=""
case $arg in #( for dir in $ROOTDIRSRAW ; do
-*) false ;; # don't mess with options #( ROOTDIRS="$ROOTDIRS$SEP$dir"
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath SEP="|"
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi fi
# Collect all arguments for the java command; # Escape application args
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of save () {
# shell script including quotes and variable substitutions, so put them in for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
# double quotes to make sure that they get re-expanded; and echo " "
# * put everything else in single quotes, so that it's not re-expanded. }
APP_ARGS=`save "$@"`
set -- \ # Collect all arguments for the java command, following the shell quoting and substitution rules
"-Dorg.gradle.appname=$APP_BASE_NAME" \ eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"

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

@ -34,8 +34,15 @@ android {
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.fragment:fragment:1.4.1' // https://github.com/facebook/react-native/issues/31572
// We can update past 1.4.0 on RN 0.68
implementation ('androidx.appcompat:appcompat:1.3.1') {
version {
strictly '1.3.1'
}
}
implementation 'androidx.fragment:fragment:1.4.0'
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
@ -51,10 +58,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 +79,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

@ -3,6 +3,6 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application android:usesCleartextTraffic="true"> <application android:usesCleartextTraffic="true">
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false"/> <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application> </application>
</manifest> </manifest>

View File

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

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);
// The JavaScript App needs to provide uniquely identifying information
// to the native ExternalAPI module so that the latter may match the
// former to the native BaseReactView which hosts it.
BaseReactView view = BaseReactView.findViewByExternalAPIScope(scope);
if (view != null) {
JitsiMeetLogger.d(TAG + " Sending event: " + name + " with data: " + data); JitsiMeetLogger.d(TAG + " Sending event: " + name + " with data: " + data);
try {
view.onExternalAPIEvent(name, data);
broadcastEmitter.sendBroadcast(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

@ -16,12 +16,10 @@
package org.jitsi.meet.sdk; package org.jitsi.meet.sdk;
import android.app.Activity;
import android.content.BroadcastReceiver; 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;
@ -34,16 +32,11 @@ import com.facebook.react.modules.core.PermissionListener;
import org.jitsi.meet.sdk.log.JitsiMeetLogger; import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.HashMap; import java.util.HashMap;
import android.app.Activity;
/** /**
* A base activity for SDK users to embed. It contains all the required wiring * A base activity for SDK users to embed. It uses {@link JitsiMeetFragment} to do the heavy
* between the {@code JitsiMeetView} and the Activity lifecycle methods. * lifting and wires the remaining Activity lifecycle methods so it works out of the box.
*
* In this activity we use a single {@code JitsiMeetView} instance. This
* instance gives us access to a view which displays the welcome page and the
* conference itself. All lifecycle methods associated with this Activity are
* hooked to the React Native subsystem via proxy calls through the
* {@code JitsiMeetActivityDelegate} static methods.
*/ */
public class JitsiMeetActivity extends AppCompatActivity public class JitsiMeetActivity extends AppCompatActivity
implements JitsiMeetActivityInterface { implements JitsiMeetActivityInterface {
@ -59,12 +52,6 @@ public class JitsiMeetActivity extends AppCompatActivity
onBroadcastReceived(intent); onBroadcastReceived(intent);
} }
}; };
/**
* Instance of the {@link JitsiMeetView} which this activity will display.
*/
private JitsiMeetView jitsiView;
// Helpers for starting the activity // Helpers for starting the activity
// //
@ -87,20 +74,11 @@ 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);
setContentView(R.layout.activity_jitsi_meet); setContentView(R.layout.activity_jitsi_meet);
this.jitsiView = findViewById(R.id.jitsiView);
registerForBroadcastMessages(); registerForBroadcastMessages();
@ -109,18 +87,6 @@ public class JitsiMeetActivity extends AppCompatActivity
} }
} }
@Override
public void onResume() {
super.onResume();
JitsiMeetActivityDelegate.onHostResume(this);
}
@Override
public void onStop() {
JitsiMeetActivityDelegate.onHostPause(this);
super.onStop();
}
@Override @Override
public void onDestroy() { public void onDestroy() {
// Here we are trying to handle the following corner case: an application using the SDK // Here we are trying to handle the following corner case: an application using the SDK
@ -131,9 +97,6 @@ public class JitsiMeetActivity extends AppCompatActivity
// be operational so the external API won't be able to notify the native side that the // be operational so the external API won't be able to notify the native side that the
// conference terminated. Thus, try our best to clean up. // conference terminated. Thus, try our best to clean up.
leave(); leave();
this.jitsiView = null;
if (AudioModeModule.useConnectionService()) { if (AudioModeModule.useConnectionService()) {
ConnectionService.abortConnections(); ConnectionService.abortConnections();
} }
@ -141,8 +104,6 @@ public class JitsiMeetActivity extends AppCompatActivity
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver); LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
JitsiMeetActivityDelegate.onHostDestroy(this);
super.onDestroy(); super.onDestroy();
} }
@ -157,7 +118,9 @@ public class JitsiMeetActivity extends AppCompatActivity
// //
protected JitsiMeetView getJitsiView() { protected JitsiMeetView getJitsiView() {
return jitsiView; JitsiMeetFragment fragment
= (JitsiMeetFragment) getSupportFragmentManager().findFragmentById(R.id.jitsiFragment);
return fragment != null ? fragment.getJitsiView() : null;
} }
public void join(@Nullable String url) { public void join(@Nullable String url) {
@ -169,16 +132,23 @@ public class JitsiMeetActivity extends AppCompatActivity
} }
public void join(JitsiMeetConferenceOptions options) { public void join(JitsiMeetConferenceOptions options) {
if (this.jitsiView != null) { JitsiMeetView view = getJitsiView();
this.jitsiView .join(options);
if (view != null) {
view.join(options);
} else { } else {
JitsiMeetLogger.w("Cannot join, view is null"); JitsiMeetLogger.w("Cannot join, view is null");
} }
} }
protected void leave() { public void leave() {
Intent hangupBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent(); JitsiMeetView view = getJitsiView();
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(hangupBroadcastIntent);
if (view != null) {
view.leave();
} else {
JitsiMeetLogger.w("Cannot leave, view is null");
}
} }
private @Nullable private @Nullable
@ -219,7 +189,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) {
@ -282,8 +252,10 @@ public class JitsiMeetActivity extends AppCompatActivity
@Override @Override
protected void onUserLeaveHint() { protected void onUserLeaveHint() {
if (this.jitsiView != null) { JitsiMeetView view = getJitsiView();
this.jitsiView .enterPictureInPicture();
if (view != null) {
view.enterPictureInPicture();
} }
} }

View File

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

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,44 +39,29 @@ 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;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
componentName = context.startForegroundService(intent); componentName = context.startForegroundService(intent);
} else { } else {
componentName = context.startService(intent); componentName = context.startService(intent);
} }
} catch (RuntimeException e) {
// Avoid crashing due to ForegroundServiceStartNotAllowedException (API level 31).
// See: https://developer.android.com/guide/components/foreground-services#background-start-restrictions
JitsiMeetLogger.w(TAG + " Ongoing conference service not started", e);
return;
}
if (componentName == null) { if (componentName == null) {
JitsiMeetLogger.w(TAG + " Ongoing conference service not started"); JitsiMeetLogger.w(TAG + " Ongoing conference service not started");
} }
} }
public static void abort(Context context) { static void abort(Context context) {
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class); Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
context.stopService(intent); context.stopService(intent);
} }
@ -89,15 +70,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,33 +92,25 @@ 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
if (action != null) {
switch (action) { switch (action) {
case UNMUTE: case UNMUTE:
case MUTE: case MUTE:
Intent muteBroadcastIntent = BroadcastIntentHelper.buildSetAudioMutedIntent(action == Action.MUTE); Intent muteBroadcastIntent = BroadcastIntentHelper.buildSetAudioMutedIntent(action == Action.MUTE);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(muteBroadcastIntent); LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(muteBroadcastIntent);
break; break;
case START:
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");
}
break;
case HANGUP: case HANGUP:
JitsiMeetLogger.i(TAG + " Hangup requested"); JitsiMeetLogger.i(TAG + " Hangup requested");
@ -157,9 +121,9 @@ public class JitsiMeetOngoingConferenceService extends Service
break; break;
default: default:
JitsiMeetLogger.w(TAG + " Unknown action received: " + action); JitsiMeetLogger.w(TAG + " Unknown action received: " + action);
stopSelf();
break; break;
} }
}
return START_NOT_STICKY; return START_NOT_STICKY;
} }
@ -174,6 +138,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 +163,6 @@ public class JitsiMeetOngoingConferenceService extends Service
} }
} }
private Boolean tryParseIsAudioMuted(Intent intent) {
try {
HashMap<String, Object> extraData = (HashMap<String, Object>) intent.getBundleExtra(EXTRA_DATA_BUNDLE_KEY).getSerializable(EXTRA_DATA_KEY);
return Boolean.parseBoolean((String) extraData.get(IS_AUDIO_MUTED_KEY));
} catch (Exception ignored) {
}
return null;
}
private class BroadcastReceiver extends android.content.BroadcastReceiver { private class BroadcastReceiver extends android.content.BroadcastReceiver {
@Override @Override
@ -215,12 +171,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,35 @@
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.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 +83,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 {
@ -95,32 +95,20 @@ public class JitsiMeetView extends FrameLayout {
public JitsiMeetView(@NonNull Context context) { public JitsiMeetView(@NonNull Context context) {
super(context); super(context);
initialize(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");
} }
public JitsiMeetView(Context context, AttributeSet attrs) { OngoingConferenceTracker.getInstance().addListener(this);
super(context, attrs);
initialize(context);
} }
public JitsiMeetView(Context context, AttributeSet attrs, int defStyle) { @Override
super(context, attrs, defStyle);
initialize(context);
}
/**
* Releases the React resources (specifically the {@link ReactRootView})
* associated with this view.
*
* MUST be called when the {@link Activity} holding this view is destroyed,
* typically in the {@code onDestroy} method.
*/
public void dispose() { public void dispose() {
if (reactRootView != null) { OngoingConferenceTracker.getInstance().removeListener(this);
removeView(reactRootView); super.dispose();
reactRootView.unmountReactApplication();
reactRootView = null;
}
} }
/** /**
@ -138,7 +126,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 +147,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 +167,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 +175,36 @@ 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();
} }
/**
* 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();
}
}
} }

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,13 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/jitsi_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".JitsiMeetActivity"> tools:context=".JitsiMeetActivity">
<fragment
<org.jitsi.meet.sdk.JitsiMeetView
android:id="@+id/jitsiView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent"
android:name="org.jitsi.meet.sdk.JitsiMeetFragment"
android:id="@+id/jitsiFragment"/>
</FrameLayout> </FrameLayout>

View File

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

View File

@ -1,8 +1,6 @@
rootProject.name = 'jitsi-meet' rootProject.name = 'jitsi-meet'
include ':app', ':sdk' include ':app', ':sdk'
includeBuild('../node_modules/react-native-gradle-plugin')
include ':react-native-amplitude' include ':react-native-amplitude'
project(':react-native-amplitude').projectDir = new File(rootProject.projectDir, '../node_modules/@amplitude/react-native//android') project(':react-native-amplitude').projectDir = new File(rootProject.projectDir, '../node_modules/@amplitude/react-native//android')
include ':react-native-async-storage' include ':react-native-async-storage'
@ -31,12 +29,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

632
config.js

File diff suppressed because it is too large Load Diff

View File

@ -8,21 +8,18 @@ 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 {
isFatalJitsiConnectionError,
JitsiConnectionErrors, JitsiConnectionErrors,
JitsiConnectionEvents JitsiConnectionEvents
} 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 { 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

@ -36,22 +36,19 @@
overflow-y: auto; overflow-y: auto;
} }
/**
* Remove background color and box-shadow for the context menu container.
*/
.toolbox-button-wth-dialog.context-menu > div:nth-child(2) {
background: transparent;
box-shadow: none;
overflow-y: initial;
}
.audio-preview > div:nth-child(2), .audio-preview > div:nth-child(2),
.video-preview > div:nth-child(2) { .video-preview > div:nth-child(2),
.reactions-menu-popup > div:nth-child(2) {
margin-bottom: 4px; margin-bottom: 4px;
outline: none; outline: none;
padding: 0; padding: 0;
} }
.reactions-menu-popup > div:nth-child(2) {
margin-bottom: 6px;
box-shadow: none;
}
/** /**
* The following selectors keep the chat modal full-size anywhere between 100px * The following selectors keep the chat modal full-size anywhere between 100px
* and 580px for desktop or 680px for mobile. * and 580px for desktop or 680px for mobile.

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;
}
} }
&-entry-text { &--bordered {
max-width: 213px; border-bottom: 1px solid #4C4D50;
}
&.left-margin { &-text {
margin-left: 36px; margin-left: 12px;
}
}
&-entry {
align-items: center;
color: #fff;
cursor: pointer;
display: flex;
padding: 8px 0;
margin-left: 48px;
&--selected {
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;
} }
.chat-input { * {
flex: 1; background-color: transparent;
margin-right: 8px; }
}
.send-button-container {
display: flex;
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;
@ -104,7 +102,7 @@
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 {
@ -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

@ -104,10 +104,6 @@
} }
} }
.reactions-menu-container {
padding-bottom: 6px;
}
.reactions-animations-container { .reactions-animations-container {
position: absolute; position: absolute;
width: 20%; width: 20%;
@ -116,7 +112,8 @@
height: 0; height: 0;
} }
.reactions-menu-popup-container { .reactions-menu-popup-container,
.reactions-menu-popup {
display: inline-block; display: inline-block;
position: relative; position: relative;
} }

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,7 +25,7 @@
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;
@ -35,7 +35,7 @@
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 {
@ -60,15 +60,3 @@
} }
} }
} }
.settings-button-small-icon-container {
position: absolute;
right: -4px;
top: -3px;
& .settings-button-small-icon {
position: relative;
top: 0;
right: 0;
}
}

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,6 +79,7 @@ $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);
@ -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,7 +123,11 @@ 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;
p {
color: $welcomePageDescriptionColor;
float: left;
text-align: $welcomePageHeaderTextAlign;
a { a {
color: inherit; color: inherit;
@ -141,6 +135,7 @@ body.welcome-page {
} }
} }
} }
}
.tab-container { .tab-container {
font-size: 16px; font-size: 16px;
@ -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

@ -48,8 +48,7 @@
/** /**
* The local video identifier. * The local video identifier.
*/ */
&#filmstripLocalVideo, &#filmstripLocalVideo {
&#filmstripLocalScreenShare {
align-self: flex-end; align-self: flex-end;
display: block; display: block;
margin-bottom: 8px; margin-bottom: 8px;

View File

@ -5,6 +5,8 @@
.remote-videos { .remote-videos {
align-items: center; align-items: center;
box-sizing: border-box; box-sizing: border-box;
display: flex;
flex-direction: column;
overscroll-behavior: contain; overscroll-behavior: contain;
} }

View File

@ -2,9 +2,7 @@
* Various overrides outside of the filmstrip to style the app to support a * Various overrides outside of the filmstrip to style the app to support a
* tiled thumbnail experience. * tiled thumbnail experience.
*/ */
.tile-view, .tile-view, .stage-filmstrip {
.whiteboard-container,
.stage-filmstrip {
/** /**
* Let the avatar grow with the tile. * Let the avatar grow with the tile.
*/ */

View File

@ -1,5 +1,4 @@
.vertical-filmstrip, .stage-filmstrip { .vertical-filmstrip span:not(.tile-view) .filmstrip {
span:not(.tile-view) .filmstrip {
&.hide-videos { &.hide-videos {
.remote-videos { .remote-videos {
& > div { & > div {
@ -90,33 +89,14 @@
width: 100%; width: 100%;
} }
} }
}
#filmstripLocalScreenShare {
align-self: initial;
margin-bottom: 5px;
display: flex;
flex-direction: column-reverse;
height: auto;
justify-content: flex-start;
width: 100%;
#filmstripLocalScreenShareThumbnail {
width: calc(100% - 15px);
.videocontainer {
height: 0px;
width: 100%;
}
}
} }
/** /**
* Remove 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,
#filmstripLocalScreenShare,
.remote-videos { .remote-videos {
padding: 0; padding: 0;
} }
@ -174,4 +154,3 @@
} }
} }
} }
}

View File

@ -3,17 +3,14 @@
* clashing with the filmstrip. * clashing with the filmstrip.
*/ */
.vertical-filmstrip #etherpad, .vertical-filmstrip #etherpad,
.stage-filmstrip #etherpad, .vertical-filmstrip #sharedvideo {
.vertical-filmstrip #sharedvideo,
.stage-filmstrip #sharedvideo {
text-align: left; text-align: left;
} }
/** /**
* Overrides for small videos in vertical filmstrip mode. * Overrides for small videos in vertical filmstrip mode.
*/ */
.vertical-filmstrip .filmstrip__videos .videocontainer, .vertical-filmstrip .filmstrip__videos .videocontainer {
.stage-filmstrip .filmstrip__videos .videocontainer {
.self-view-mobile-portrait video { .self-view-mobile-portrait video {
object-fit: contain; object-fit: contain;
} }
@ -30,8 +27,7 @@
* The class opening is for when the filmstrip is transitioning from hidden * The class opening is for when the filmstrip is transitioning from hidden
* to visible. * to visible.
*/ */
.vertical-filmstrip .large-video-labels, .vertical-filmstrip .large-video-labels {
.stage-filmstrip .large-video-labels {
&.with-filmstrip { &.with-filmstrip {
right: 150px; right: 150px;
} }
@ -51,7 +47,6 @@
* Overrides for self view when in portrait mode on mobile. * Overrides for self view when in portrait mode on mobile.
* This is done in order to keep the aspect ratio. * This is done in order to keep the aspect ratio.
*/ */
.vertical-filmstrip .self-view-mobile-portrait #localVideo_container, .vertical-filmstrip .self-view-mobile-portrait #localVideo_container {
.stage-filmstrip .self-view-mobile-portrait #localVideo_container {
object-fit: contain; object-fit: contain;
} }

View File

@ -33,12 +33,14 @@ $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';
@import 'modals/invite/info'; @import 'modals/invite/info';
@import 'modals/screen-share/share-audio'; @import 'modals/screen-share/share-audio';
@import 'modals/screen-share/share-screen-warning'; @import 'modals/screen-share/share-screen-warning';
@import 'modals/local-recording/local-recording';
@import 'videolayout_default'; @import 'videolayout_default';
@import 'notice'; @import 'notice';
@import 'subject'; @import 'subject';
@ -94,9 +96,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;
}
}
}

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