Compare commits

..

1 Commits

Author SHA1 Message Date
Avram Tudor bf6e3ac58b
Create bla 2021-11-18 14:44:03 +02:00
3142 changed files with 165394 additions and 179176 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
# modify as little as possible.
flow-typed/*
libs/*
resources/*
react/features/stream-effects/virtual-background/vendor/*
react/features/face-landmarks/resources/*
load-test/*
react/features/facial-recognition/resources/*
# ESLint will by default ignore its own configuration file. However, there does
# not seem to be a reason why we will want to risk being inconsistent with our
@ -14,4 +16,4 @@ react/features/face-landmarks/resources/*
!.eslintrc.js
# Not worth it.
actionTypes.ts
actionTypes.js

View File

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

118
.flowconfig Normal file
View File

@ -0,0 +1,118 @@
[ignore]
; We fork some components by platform
.*/*[.]android.js
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
node_modules/react-native/Libraries/react-native/React.js
; Ignore duplicate module providers
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
node_modules/react-native/Libraries/react-native/React.js
; Flow doesn't support platforms
.*/Libraries/Utilities/LoadingView.js
; Ignore polyfills
node_modules/react-native/Libraries/polyfills/.*
; These should not be required directly
; require from fbjs/lib instead: require('fbjs/lib/warning')
node_modules/warning/.*
; Flow doesn't support platforms
.*/Libraries/Utilities/HMRLoadingView.js
[untyped]
.*/node_modules/@react-native-community/cli/.*/.*
; Ignore packages in node_modules which we (i.e. the jitsi-meet project) have
; seen to cause errors and we have chosen not to fix.
.*/node_modules/@atlaskit/.*/*.js.flow
.*/node_modules/react-native-keep-awake/.*
.*/node_modules/react-native-permissions/.*
.*/node_modules/styled-components/.*
.*/\.git/.*
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/
[options]
emoji=true
esproposal.optional_chaining=enable
esproposal.nullish_coalescing=enable
; We (i.e. the jitsi-meet project) are using the haste module system on Web as
; well, not only on React Native. Unfortunately, Flow does not support .web.js
; by default. Override Flow's defaults to include .web.js as well. Technically,
; we have .native.js as well so the choice of .web.js may lead to errors.
; Practically though, it is a potential future problem that we do not have at
; the time of this writing.
module.file_ext=.web.js
; Flow's defaults:
module.file_ext=.js
module.file_ext=.json
module.file_ext=.ios.js
module.system=haste
module.system.haste.use_name_reducers=true
# get basename
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
# strip .js or .js.flow suffix
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
# strip .ios suffix
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
module.system.haste.paths.blacklist=.*/__tests__/.*
module.system.haste.paths.blacklist=.*/__mocks__/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/RNTester/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/IntegrationTests/.*
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/react-native/react-native-implementation.js
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
munge_underscores=true
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
[lints]
sketchy-null-number=warn
sketchy-null-mixed=warn
sketchy-number=warn
untyped-type-import=warn
nonstrict-import=warn
deprecated-type=warn
unsafe-getters-setters=warn
inexact-spread=warn
unnecessary-invariant=warn
signature-verification-failure=warn
deprecated-utility=error
[strict]
deprecated-type
nonstrict-import
sketchy-null
unclear-type
unsafe-getters-setters
untyped-import
untyped-type-import
[version]
^0.104.0

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,42 +3,20 @@ name: Simple CI
on: [pull_request]
jobs:
lint:
name: Lint
run-ci:
name: Build Frontend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 16
cache: 'npm'
node-version: '16.x'
- run: npm install
- name: Check git status
run: git status
- name: Normalize lang files to ensure sorted
run: npm run lang-sort
- name: Check git diff
run: git diff
- name: Check if the git repository is clean
run: $(exit $(git status --porcelain --untracked-files=no | head -255 | wc -l)) || (echo "Dirty git tree"; git diff; exit 1)
- run: npm run lint:ci
linux-build:
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: exit $( git status --porcelain --untracked-files=no | head -255 | wc -l )
- run: npm run lint
- run: make

1
.gitignore vendored
View File

@ -92,4 +92,3 @@ twa/*.apk
twa/*.aab
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

@ -3,8 +3,8 @@ We would love to have your help. Before you start working however, please read
and follow this short guide.
# Reporting Issues
Provide as much information as possible. Mention the version of Jitsi Meet,
Jicofo and JVB you are using, and explain (as detailed as you can) how the
Provide as much information as possible. Mention the version of Jitsi Meet,
Jicofo and JVB you are using, and explain (as detailed as you can) how the
problem can be reproduced.
# Code contributions
@ -130,7 +130,7 @@ When adding a new feature, this would be the usual layout.
```
react/features/sample/
├── actionTypes.ts
├── actionTypes.js
├── actions.js
├── components
│   ├── AnotherComponent.js
@ -141,7 +141,7 @@ react/features/sample/
```
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.
An `index.js` file must not be provided for exporting actions, action types and

View File

@ -1,40 +1,35 @@
BUILD_DIR = build
CLEANCSS = ./node_modules/.bin/cleancss
DEPLOY_DIR = libs
LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet
LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet/
LIBFLAC_DIR = node_modules/libflacjs/dist/min/
OLM_DIR = node_modules/@matrix-org/olm
TF_WASM_DIR = node_modules/@tensorflow/tfjs-backend-wasm/dist/
RNNOISE_WASM_DIR = node_modules/@jitsi/rnnoise-wasm/dist
EXCALIDRAW_DIR = node_modules/@jitsi/excalidraw/dist/excalidraw-assets
EXCALIDRAW_DIR_DEV = node_modules/@jitsi/excalidraw/dist/excalidraw-assets-dev
RNNOISE_WASM_DIR = node_modules/rnnoise-wasm/dist/
TFLITE_WASM = react/features/stream-effects/virtual-background/vendor/tflite
MEET_MODELS_DIR = react/features/stream-effects/virtual-background/vendor/models
FACE_MODELS_DIR = node_modules/@vladmandic/human-models/models
MEET_MODELS_DIR = react/features/stream-effects/virtual-background/vendor/models/
FACIAL_MODELS_DIR = react/features/facial-recognition/resources
NODE_SASS = ./node_modules/.bin/sass
NPM = npm
OUTPUT_DIR = .
STYLES_BUNDLE = css/all.bundle.css
STYLES_DESTINATION = css/all.css
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_DEV_SERVER = ./node_modules/.bin/webpack serve --mode development
endif
WEBPACK = ./node_modules/.bin/webpack
WEBPACK_DEV_SERVER = ./node_modules/.bin/webpack serve --mode development
all: compile deploy clean
compile:
NODE_OPTIONS=--max-old-space-size=8192 \
compile: compile-load-test
$(WEBPACK)
compile-load-test:
${NPM} install --prefix resources/load-test && ${NPM} run build --prefix resources/load-test
clean:
rm -fr $(BUILD_DIR)
.NOTPARALLEL:
deploy: deploy-init deploy-appbundle deploy-rnnoise-binary deploy-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-css deploy-local deploy-facial-expressions
deploy-init:
rm -fr $(DEPLOY_DIR)
@ -44,17 +39,19 @@ deploy-appbundle:
cp \
$(BUILD_DIR)/app.bundle.min.js \
$(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.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.map \
$(OUTPUT_DIR)/analytics-ga.js \
$(BUILD_DIR)/analytics-ga.min.js \
$(BUILD_DIR)/analytics-ga.min.js.map \
$(BUILD_DIR)/face-landmarks-worker.min.js \
$(BUILD_DIR)/face-landmarks-worker.min.js.map \
$(BUILD_DIR)/noise-suppressor-worklet.min.js \
$(BUILD_DIR)/noise-suppressor-worklet.min.js.map \
$(DEPLOY_DIR)
cp \
$(BUILD_DIR)/close3.min.js \
@ -63,22 +60,24 @@ deploy-appbundle:
deploy-lib-jitsi-meet:
cp \
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.min.js \
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.min.map \
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.e2ee-worker.js \
$(LIBJITSIMEET_DIR)/lib-jitsi-meet.min.js \
$(LIBJITSIMEET_DIR)/lib-jitsi-meet.min.map \
$(LIBJITSIMEET_DIR)/lib-jitsi-meet.e2ee-worker.js \
$(LIBJITSIMEET_DIR)/connection_optimization/external_connect.js \
$(LIBJITSIMEET_DIR)/modules/browser/capabilities.json \
$(DEPLOY_DIR)
deploy-libflac:
cp \
$(LIBFLAC_DIR)/libflac4-1.3.2.min.js \
$(LIBFLAC_DIR)/libflac4-1.3.2.min.js.mem \
$(DEPLOY_DIR)
deploy-olm:
cp \
$(OLM_DIR)/olm.wasm \
$(DEPLOY_DIR)
deploy-tf-wasm:
cp \
$(TF_WASM_DIR)/*.wasm \
$(DEPLOY_DIR)
deploy-rnnoise-binary:
cp \
$(RNNOISE_WASM_DIR)/rnnoise.wasm \
@ -89,44 +88,33 @@ deploy-tflite:
$(TFLITE_WASM)/*.wasm \
$(DEPLOY_DIR)
deploy-excalidraw:
cp -R \
$(EXCALIDRAW_DIR) \
$(DEPLOY_DIR)/
deploy-excalidraw-dev:
cp -R \
$(EXCALIDRAW_DIR_DEV) \
$(DEPLOY_DIR)/
deploy-meet-models:
cp \
$(MEET_MODELS_DIR)/*.tflite \
$(DEPLOY_DIR)
deploy-face-landmarks:
deploy-facial-expressions:
cp \
$(FACE_MODELS_DIR)/blazeface-front.bin \
$(FACE_MODELS_DIR)/blazeface-front.json \
$(FACE_MODELS_DIR)/emotion.bin \
$(FACE_MODELS_DIR)/emotion.json \
$(FACIAL_MODELS_DIR)/* \
$(BUILD_DIR)/facial-expressions-worker.min.js \
$(BUILD_DIR)/facial-expressions-worker.min.js.map \
$(DEPLOY_DIR)
deploy-css:
$(NODE_SASS) $(STYLES_MAIN) $(STYLES_BUNDLE) && \
$(CLEANCSS) --skip-rebase $(STYLES_BUNDLE) > $(STYLES_DESTINATION) && \
$(CLEANCSS) --skip-rebase $(STYLES_BUNDLE) > $(STYLES_DESTINATION) ; \
rm $(STYLES_BUNDLE)
deploy-local:
([ ! -x deploy-local.sh ] || ./deploy-local.sh)
.NOTPARALLEL:
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-olm deploy-tf-wasm deploy-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-facial-expressions
$(WEBPACK_DEV_SERVER)
source-package:
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 && \
(cd source_package ; tar cjf ../jitsi-meet.tar.bz2 jitsi-meet) && \
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
* HD audio and video
* Content sharing
* End-to-End Encryption
* Raise hand and reactions
* Chat with private conversations
* Polls
@ -44,7 +45,7 @@ developed you can also sign up for our open beta testing here:
## Running your own instance
If you'd like to run your own Jitsi Meet installation head over to the [handbook](https://jitsi.github.io/handbook/docs/devops-guide/) to get started.
If you'd like to run your own Jitsi Meet installation head over to the [handbook](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-start) to get started.
We provide Debian packages and a comprehensive Docker setup to make deployments as simple as possible.
Advanced users also have the possibility of building all the components from source.

View File

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

View File

@ -85,9 +85,4 @@
# ^^^ We added the above when we switched minifyEnabled on.
# Rule to avoid build errors related to SVGs.
-keep public class com.horcrux.svg.** {*;}
# https://github.com/facebook/fresco/issues/2638
-keep public class com.facebook.imageutils.** {
public *;
}
-keep public class com.horcrux.svg.** {*;}

View File

@ -7,14 +7,13 @@
android:extractNativeLibs="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name=".MainApplication"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/AppTheme">
<meta-data
android:name="android.content.APP_RESTRICTIONS"
android:resource="@xml/app_restrictions" />
<activity
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize"
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleTask"

View File

@ -150,8 +150,8 @@ public class MainActivity extends JitsiMeetActivity {
// Set default options
JitsiMeetConferenceOptions defaultOptions
= new JitsiMeetConferenceOptions.Builder()
.setWelcomePageEnabled(true)
.setServerURL(buildURL(defaultURL))
.setFeatureFlag("welcomepage.enabled", true)
.setFeatureFlag("call-integration.enabled", false)
.setFeatureFlag("resolution", 360)
.setFeatureFlag("server-url-change.enabled", !configurationByRestrictions)
@ -182,6 +182,11 @@ public class MainActivity extends JitsiMeetActivity {
}
}
@Override
protected void onConferenceTerminated(HashMap<String, Object> extraData) {
Log.d(TAG, "Conference terminated: " + extraData);
}
// Activity lifecycle method overrides
//

View File

@ -1,47 +0,0 @@
/*
* Copyright @ 2022-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet;
import android.app.Application;
import android.util.Log;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import org.jitsi.meet.sdk.JitsiReactNativeHost;
/**
* Application class for Jitsi Meet. The only reason why this exists is for Detox
* to believe our app is a "greenfield" app. SDK users need not use this.
*/
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new JitsiReactNativeHost(this);
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
// Initialize RN
Log.d(this.getClass().getCanonicalName(), "app onCreate");
getReactNativeHost().getReactInstanceManager();
}
}

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">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
</adaptive-icon>

View File

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

View File

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

View File

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

View File

@ -7,30 +7,22 @@ import org.gradle.util.VersionNumber
buildscript {
repositories {
google()
mavenCentral()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.4'
classpath 'com.google.gms:google-services:4.3.14'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2'
classpath 'com.android.tools.build:gradle:4.1.2'
classpath 'com.google.gms:google-services:4.3.4'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
}
}
ext {
buildToolsVersion = "31.0.0"
compileSdkVersion = 32
buildToolsVersion = "30.0.3"
compileSdkVersion = 31
minSdkVersion = 23
targetSdkVersion = 32
targetSdkVersion = 31
supportLibVersion = "28.0.0"
if (System.properties['os.arch'] == "aarch64") {
// For M1 Users we need to use the NDK 24 which added support for aarch64
ndkVersion = "24.0.8215888"
} else {
// Otherwise we default to the side-by-side NDK version from AGP.
ndkVersion = "21.4.7075529"
}
// The Maven artifact groupdId of the third-party react-native modules which
// Jitsi Meet SDK for Android depends on and which are not available in
// third-party Maven repositories so we have to deploy to a Maven repository
@ -50,19 +42,12 @@ ext {
allprojects {
repositories {
google()
jcenter()
// React Native (JS, Obj-C sources, Android binaries) is installed from npm.
maven { url "$rootDir/../node_modules/react-native/android" }
// Android JSC is installed from npm.
maven { url("$rootDir/../node_modules/jsc-android/dist") }
mavenCentral {
// We don't want to fetch react-native from Maven Central as there are
// older versions over there.
content {
excludeGroup "com.facebook.react"
}
}
google()
maven { url 'https://www.jitpack.io' }
}
// Make sure we use the react-native version in node_modules and not the one

View File

@ -9,7 +9,7 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx1024m -XX:MaxPermSize=256m
# Default value: -Xmx10248m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
@ -26,5 +26,5 @@ android.useAndroidX=true
android.enableJetifier=true
android.bundle.enableUncompressedNativeLibs=false
appVersion=99.0.0
sdkVersion=99.0.0
appVersion=21.5.0
sdkVersion=4.0.0

View File

@ -1,5 +1,6 @@
#Wed Sep 23 11:48:00 EEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip

286
android/gradlew vendored
View File

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

173
android/gradlew.bat vendored
View File

@ -1,89 +1,84 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

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

@ -3,7 +3,6 @@ apply plugin: 'maven-publish'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
ndkVersion rootProject.ext.ndkVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
@ -34,8 +33,9 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.fragment:fragment:1.4.1'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.fragment:fragment:1.2.5'
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
@ -44,17 +44,21 @@ dependencies {
//noinspection GradleDynamicVersion
implementation 'org.webkit:android-jsc:+'
implementation 'com.facebook.fresco:animated-gif:2.5.0'
implementation 'com.dropbox.core:dropbox-core-sdk:4.0.1'
implementation 'com.jakewharton.timber:timber:4.7.1'
implementation 'com.squareup.duktape:duktape-android:1.3.0'
implementation 'com.google.code.gson:gson:2.8.6'
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-giphy')
implementation project(':react-native-device-info')
implementation(project(":react-native-google-signin")) {
exclude group: 'com.google.android.gms'
exclude group: 'androidx'
@ -64,21 +68,15 @@ dependencies {
implementation project(':react-native-async-storage')
implementation project(':react-native-background-timer')
implementation project(':react-native-calendar-events')
implementation project(':react-native-community_clipboard')
implementation project(':react-native-community_netinfo')
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-get-random-values')
implementation project(':react-native-immersive')
implementation project(':react-native-keep-awake')
implementation project(':react-native-orientation-locker')
implementation project(':react-native-pager-view')
implementation project(':react-native-masked-view_masked-view')
implementation project(':react-native-performance')
implementation project(':react-native-reanimated')
implementation project(':react-native-safe-area-context')
implementation project(':react-native-screens')
implementation project(':react-native-slider')
@ -219,7 +217,7 @@ publishing {
def groupId = it.moduleGroup
def artifactId = it.moduleName
if (artifactId.startsWith('react-native-')) {
if (artifactId.startsWith('react-native-') && groupId.equals('jitsi-meet')) {
groupId = rootProject.ext.moduleGroupId
}

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application android:usesCleartextTraffic="true">
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false"/>
</application>
</manifest>

View File

@ -10,6 +10,7 @@
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
@ -30,12 +31,12 @@
android:supportsRtl="true">
<activity
android:name=".JitsiMeetActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize"
android:launchMode="singleTask"
android:theme="@style/JitsiMeetActivityStyle"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:windowSoftInputMode="adjustResize"/>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<service
android:name=".ConnectionService"

View File

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

View File

@ -16,7 +16,6 @@
package org.jitsi.meet.sdk;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.media.AudioManager;
@ -26,13 +25,10 @@ import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
@ -58,7 +54,6 @@ import java.util.concurrent.Executors;
* Before a call has started and after it has ended the
* {@code AudioModeModule.DEFAULT} mode should be used.
*/
@SuppressLint("AnnotateVersionCheck")
@ReactModule(name = AudioModeModule.NAME)
class AudioModeModule extends ReactContextBaseJavaModule {
public static final String NAME = "AudioMode";
@ -84,6 +79,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
/**
* Whether or not the ConnectionService is used for selecting audio devices.
*/
private static final boolean supportsConnectionService = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
private static boolean useConnectionService_ = supportsConnectionService;
@ -152,16 +148,6 @@ class AudioModeModule extends ReactContextBaseJavaModule {
audioManager = (AudioManager)reactContext.getSystemService(Context.AUDIO_SERVICE);
}
@ReactMethod
public void addListener(String eventName) {
// Keep: Required for RN built in Event Emitter Calls.
}
@ReactMethod
public void removeListeners(Integer count) {
// Keep: Required for RN built in Event Emitter Calls.
}
/**
* Gets a mapping with the constants this module is exporting.
*
@ -199,7 +185,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
deviceInfo.putBoolean("selected", device.equals(selectedDevice));
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");
}
});
@ -215,10 +201,6 @@ class AudioModeModule extends ReactContextBaseJavaModule {
return NAME;
}
public ReactContext getContext(){
return this.getReactApplicationContext();
}
/**
* 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}

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"),
CLOSE_CHAT("org.jitsi.meet.CLOSE_CHAT"),
SEND_CHAT_MESSAGE("org.jitsi.meet.SEND_CHAT_MESSAGE"),
SET_VIDEO_MUTED("org.jitsi.meet.SET_VIDEO_MUTED"),
SET_CLOSED_CAPTIONS_ENABLED("org.jitsi.meet.SET_CLOSED_CAPTIONS_ENABLED");
SET_VIDEO_MUTED("org.jitsi.meet.SET_VIDEO_MUTED");
private final String action;

View File

@ -86,8 +86,8 @@ public class BroadcastEvent {
PARTICIPANTS_INFO_RETRIEVED("org.jitsi.meet.PARTICIPANTS_INFO_RETRIEVED"),
CHAT_MESSAGE_RECEIVED("org.jitsi.meet.CHAT_MESSAGE_RECEIVED"),
CHAT_TOGGLED("org.jitsi.meet.CHAT_TOGGLED"),
VIDEO_MUTED_CHANGED("org.jitsi.meet.VIDEO_MUTED_CHANGED"),
READY_TO_CLOSE("org.jitsi.meet.READY_TO_CLOSE");
VIDEO_MUTED_CHANGED("org.jitsi.meet.VIDEO_MUTED_CHANGED");
private static final String CONFERENCE_WILL_JOIN_NAME = "CONFERENCE_WILL_JOIN";
private static final String CONFERENCE_JOINED_NAME = "CONFERENCE_JOINED";
@ -101,7 +101,6 @@ public class BroadcastEvent {
private static final String CHAT_MESSAGE_RECEIVED_NAME = "CHAT_MESSAGE_RECEIVED";
private static final String CHAT_TOGGLED_NAME = "CHAT_TOGGLED";
private static final String VIDEO_MUTED_CHANGED_NAME = "VIDEO_MUTED_CHANGED";
private static final String READY_TO_CLOSE_NAME = "READY_TO_CLOSE";
private final String action;
@ -148,8 +147,6 @@ public class BroadcastEvent {
return CHAT_TOGGLED;
case VIDEO_MUTED_CHANGED_NAME:
return VIDEO_MUTED_CHANGED;
case READY_TO_CLOSE_NAME:
return READY_TO_CLOSE;
}
return null;

View File

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

View File

@ -31,7 +31,8 @@ import java.util.Map;
* Module implementing an API for sending events from JavaScript to native code.
*/
@ReactModule(name = ExternalAPIModule.NAME)
class ExternalAPIModule extends ReactContextBaseJavaModule {
class ExternalAPIModule
extends ReactContextBaseJavaModule {
public static final String NAME = "ExternalAPI";
@ -56,16 +57,6 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
ParticipantsService.init(reactContext);
}
@ReactMethod
public void addListener(String eventName) {
// Keep: Required for RN built in Event Emitter Calls.
}
@ReactMethod
public void removeListeners(Integer count) {
// Keep: Required for RN built in Event Emitter Calls.
}
/**
* Gets the name of this module to be used in the React Native bridge.
*
@ -95,25 +86,37 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
constants.put("CLOSE_CHAT", BroadcastAction.Type.CLOSE_CHAT.getAction());
constants.put("SEND_CHAT_MESSAGE", BroadcastAction.Type.SEND_CHAT_MESSAGE.getAction());
constants.put("SET_VIDEO_MUTED", BroadcastAction.Type.SET_VIDEO_MUTED.getAction());
constants.put("SET_CLOSED_CAPTIONS_ENABLED", BroadcastAction.Type.SET_CLOSED_CAPTIONS_ENABLED.getAction());
return constants;
}
/**
* Dispatches an event that occurred on the JavaScript side of the SDK to
* the native side.
* the specified {@link BaseReactView}'s listener.
*
* @param name The name of the event.
* @param data The details/specifics of the event to send determined
* by/associated with the specified {@code name}.
* @param scope
*/
@ReactMethod
public void sendEvent(String name, ReadableMap data) {
public void sendEvent(String name, ReadableMap data, String scope) {
// Keep track of the current ongoing conference.
OngoingConferenceTracker.getInstance().onExternalAPIEvent(name, data);
JitsiMeetLogger.d(TAG + " Sending event: " + name + " with data: " + data);
broadcastEmitter.sendBroadcast(name, data);
// The JavaScript App needs to provide uniquely identifying information
// to the native ExternalAPI module so that the latter may match the
// former to the native BaseReactView which hosts it.
BaseReactView view = BaseReactView.findViewByExternalAPIScope(scope);
if (view != null) {
JitsiMeetLogger.d(TAG + " Sending event: " + name + " with data: " + data);
try {
view.onExternalAPIEvent(name, data);
broadcastEmitter.sendBroadcast(name, data);
} catch (Exception e) {
JitsiMeetLogger.e(e, TAG + " onExternalAPIEvent: error sending event");
}
}
}
}

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,17 +15,14 @@
*/
package org.jitsi.meet.sdk;
import android.app.Application;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.startup.Initializer;
import com.facebook.soloader.SoLoader;
import org.wonday.orientation.OrientationActivityLifecycle;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
public class JitsiInitializer implements Initializer<Boolean> {
@ -33,22 +30,13 @@ public class JitsiInitializer implements Initializer<Boolean> {
@NonNull
@Override
public Boolean create(@NonNull Context context) {
Log.d(this.getClass().getCanonicalName(), "create");
SoLoader.init(context, /* native exopackage */ false);
// Register our uncaught exception handler.
JitsiMeetUncaughtExceptionHandler.register();
// Register activity lifecycle handler for the orientation locker module.
((Application) context).registerActivityLifecycleCallbacks(OrientationActivityLifecycle.getInstance());
return true;
}
@NonNull
@Override
public List<Class<? extends Initializer<?>>> dependencies() {
return Collections.emptyList();
return new ArrayList<>();
}
}

View File

@ -16,12 +16,10 @@
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
@ -34,16 +32,11 @@ import com.facebook.react.modules.core.PermissionListener;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.HashMap;
import android.app.Activity;
/**
* A base activity for SDK users to embed. It contains all the required wiring
* between the {@code JitsiMeetView} and the Activity lifecycle methods.
*
* In this activity we use a single {@code JitsiMeetView} instance. This
* instance gives us access to a view which displays the welcome page and the
* conference itself. All lifecycle methods associated with this Activity are
* hooked to the React Native subsystem via proxy calls through the
* {@code JitsiMeetActivityDelegate} static methods.
* A base activity for SDK users to embed. It uses {@link JitsiMeetFragment} to do the heavy
* lifting and wires the remaining Activity lifecycle methods so it works out of the box.
*/
public class JitsiMeetActivity extends AppCompatActivity
implements JitsiMeetActivityInterface {
@ -59,12 +52,6 @@ public class JitsiMeetActivity extends AppCompatActivity
onBroadcastReceived(intent);
}
};
/**
* Instance of the {@link JitsiMeetView} which this activity will display.
*/
private JitsiMeetView jitsiView;
// Helpers for starting the activity
//
@ -87,20 +74,11 @@ public class JitsiMeetActivity extends AppCompatActivity
// Overrides
//
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Intent intent = new Intent("onConfigurationChanged");
intent.putExtra("newConfig", newConfig);
this.sendBroadcast(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jitsi_meet);
this.jitsiView = findViewById(R.id.jitsiView);
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
public void onDestroy() {
// 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
// conference terminated. Thus, try our best to clean up.
leave();
this.jitsiView = null;
if (AudioModeModule.useConnectionService()) {
ConnectionService.abortConnections();
}
@ -141,8 +104,6 @@ public class JitsiMeetActivity extends AppCompatActivity
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
JitsiMeetActivityDelegate.onHostDestroy(this);
super.onDestroy();
}
@ -157,7 +118,9 @@ public class JitsiMeetActivity extends AppCompatActivity
//
protected JitsiMeetView getJitsiView() {
return jitsiView;
JitsiMeetFragment fragment
= (JitsiMeetFragment) getSupportFragmentManager().findFragmentById(R.id.jitsiFragment);
return fragment != null ? fragment.getJitsiView() : null;
}
public void join(@Nullable String url) {
@ -169,16 +132,23 @@ public class JitsiMeetActivity extends AppCompatActivity
}
public void join(JitsiMeetConferenceOptions options) {
if (this.jitsiView != null) {
this.jitsiView .join(options);
JitsiMeetView view = getJitsiView();
if (view != null) {
view.join(options);
} else {
JitsiMeetLogger.w("Cannot join, view is null");
}
}
protected void leave() {
Intent hangupBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent();
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(hangupBroadcastIntent);
public void leave() {
JitsiMeetView view = getJitsiView();
if (view != null) {
view.leave();
} else {
JitsiMeetLogger.w("Cannot leave, view is null");
}
}
private @Nullable
@ -219,11 +189,12 @@ public class JitsiMeetActivity extends AppCompatActivity
protected void onConferenceJoined(HashMap<String, Object> extraData) {
JitsiMeetLogger.i("Conference joined: " + extraData);
// Launch the service for the ongoing notification.
JitsiMeetOngoingConferenceService.launch(this, extraData);
JitsiMeetOngoingConferenceService.launch(this);
}
protected void onConferenceTerminated(HashMap<String, Object> extraData) {
JitsiMeetLogger.i("Conference terminated: " + extraData);
finish();
}
protected void onConferenceWillJoin(HashMap<String, Object> extraData) {
@ -246,11 +217,6 @@ public class JitsiMeetActivity extends AppCompatActivity
}
}
protected void onReadyToClose() {
JitsiMeetLogger.i("SDK is ready to close");
finish();
}
// Activity lifecycle methods
//
@ -282,8 +248,10 @@ public class JitsiMeetActivity extends AppCompatActivity
@Override
protected void onUserLeaveHint() {
if (this.jitsiView != null) {
this.jitsiView .enterPictureInPicture();
JitsiMeetView view = getJitsiView();
if (view != null) {
view.enterPictureInPicture();
}
}
@ -330,9 +298,6 @@ public class JitsiMeetActivity extends AppCompatActivity
case PARTICIPANT_LEFT:
onParticipantLeft(event.getData());
break;
case READY_TO_CLOSE:
onReadyToClose();
break;
}
}
}

View File

@ -45,6 +45,11 @@ public class JitsiMeetConferenceOptions implements Parcelable {
*/
private String token;
/**
* Color scheme override, see: https://github.com/jitsi/jitsi-meet/blob/dbedee5e22e5dcf9c92db96ef5bb3c9982fc526d/react/features/base/color-scheme/defaultScheme.js
*/
private Bundle colorScheme;
/**
* Config. See: https://github.com/jitsi/jitsi-meet/blob/master/config.js
*/
@ -72,6 +77,10 @@ public class JitsiMeetConferenceOptions implements Parcelable {
return token;
}
public Bundle getColorScheme() {
return colorScheme;
}
public Bundle getFeatureFlags() {
return featureFlags;
}
@ -88,6 +97,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
private String room;
private String token;
private Bundle colorScheme;
private Bundle config;
private Bundle featureFlags;
@ -142,6 +152,19 @@ public class JitsiMeetConferenceOptions implements Parcelable {
return this;
}
/**
* Sets the color scheme override so the app is themed. See:
* https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/color-scheme/defaultScheme.js
* for the structure.
* @param colorScheme - A color scheme to be applied to the app.
* @return - The {@link Builder} object itself so the method calls can be chained.
*/
public Builder setColorScheme(Bundle colorScheme) {
this.colorScheme = colorScheme;
return this;
}
/**
* Indicates the conference will be joined with the microphone muted.
* @param audioMuted - Muted indication.
@ -175,6 +198,19 @@ public class JitsiMeetConferenceOptions implements Parcelable {
return this;
}
/**
* Sets the welcome page enabled / disabled. The welcome page lists recent meetings and
* calendar appointments and it's meant to be used by standalone applications. Defaults to
* false.
* @param enabled - Whether the welcome page should be enabled or not.
* @return - The {@link Builder} object itself so the method calls can be chained.
*/
public Builder setWelcomePageEnabled(boolean enabled) {
this.featureFlags.putBoolean("welcomepage.enabled", enabled);
return this;
}
public Builder setFeatureFlag(String flag, boolean value) {
this.featureFlags.putBoolean(flag, value);
@ -240,6 +276,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
options.serverURL = this.serverURL;
options.room = this.room;
options.token = this.token;
options.colorScheme = this.colorScheme;
options.config = this.config;
options.featureFlags = this.featureFlags;
options.userInfo = this.userInfo;
@ -255,6 +292,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
serverURL = (URL) in.readSerializable();
room = in.readString();
token = in.readString();
colorScheme = in.readBundle();
config = in.readBundle();
featureFlags = in.readBundle();
userInfo = new JitsiMeetUserInfo(in.readBundle());
@ -270,6 +308,10 @@ public class JitsiMeetConferenceOptions implements Parcelable {
props.putBundle("flags", featureFlags);
if (colorScheme != null) {
props.putBundle("colorScheme", colorScheme);
}
Bundle urlProps = new Bundle();
// The room is fully qualified
@ -318,6 +360,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
dest.writeSerializable(serverURL);
dest.writeString(room);
dest.writeString(token);
dest.writeBundle(colorScheme);
dest.writeBundle(config);
dest.writeBundle(featureFlags);
dest.writeBundle(userInfo != null ? userInfo.asBundle() : new Bundle());

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

View File

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

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

@ -1,41 +0,0 @@
package org.jitsi.meet.sdk;
import android.app.Application;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import java.util.List;
/**
* This is the minimal implementation of ReactNativeHost that will make things like the
* Detox testing framework believe we are a "greenfield" app.
*
* Generally speaking, apps using the SDK (other than the Jitsi Meet app itself) should not
* need to use this because the
*/
public class JitsiReactNativeHost extends ReactNativeHost {
public JitsiReactNativeHost(Application application) {
super(application);
}
@Override
public boolean getUseDeveloperSupport() {
// Unused since we override `createReactInstanceManager`.
return false;
}
@Override
protected List<ReactPackage> getPackages() {
// Unused since we override `createReactInstanceManager`.
return null;
}
@Override
protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManagerHolder.initReactInstanceManager(this.getApplication());
return ReactInstanceManagerHolder.getReactInstanceManager();
}
}

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 boolean isSupported;
private boolean isEnabled;
private boolean isDisabled;
public PictureInPictureModule(ReactApplicationContext reactContext) {
super(reactContext);
@ -84,7 +84,7 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
*/
@TargetApi(Build.VERSION_CODES.O)
public void enterPictureInPicture() {
if (!isEnabled) {
if (isDisabled) {
return;
}
@ -132,8 +132,8 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
}
@ReactMethod
public void setPictureInPictureEnabled(Boolean enabled) {
this.isEnabled = enabled;
public void setPictureInPictureDisabled(Boolean disabled) {
this.isDisabled = disabled;
}
public boolean isPictureInPictureSupported() {

View File

@ -59,16 +59,6 @@ class RNConnectionService extends ReactContextBaseJavaModule {
super(reactContext);
}
@ReactMethod
public void addListener(String eventName) {
// Keep: Required for RN built in Event Emitter Calls.
}
@ReactMethod
public void removeListeners(Integer count) {
// Keep: Required for RN built in Event Emitter Calls.
}
/**
* Starts a new outgoing call.
*
@ -193,7 +183,7 @@ class RNConnectionService extends ReactContextBaseJavaModule {
* Called by the JS side to update the call's state.
*
* @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}
* prefixed with "KEY_" for the values supported by the Android
* implementation.

View File

@ -17,8 +17,6 @@
package org.jitsi.meet.sdk;
import android.app.Activity;
import android.app.Application;
import android.util.Log;
import androidx.annotation.Nullable;
@ -28,15 +26,16 @@ import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.devsupport.DevInternalSettings;
import com.facebook.react.jscexecutor.JSCExecutorFactory;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.oney.WebRTCModule.EglUtils;
import com.oney.WebRTCModule.RTCVideoViewManager;
import com.oney.WebRTCModule.WebRTCModule;
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.JavaAudioDeviceModule;
@ -46,8 +45,6 @@ import java.util.Arrays;
import java.util.List;
class ReactInstanceManagerHolder {
private static final String TAG = ReactInstanceManagerHolder.class.getSimpleName();
/**
* FIXME (from linter): Do not place Android context classes in static
* fields (static reference to ReactInstanceManager which has field
@ -73,6 +70,7 @@ class ReactInstanceManagerHolder {
new SplashScreenModule(reactContext),
new PictureInPictureModule(reactContext),
new ProximityModule(reactContext),
new WiFiStatsModule(reactContext),
new org.jitsi.meet.sdk.net.NAT64AddrInfoModule(reactContext)));
if (AudioModeModule.useConnectionService()) {
@ -84,14 +82,11 @@ class ReactInstanceManagerHolder {
WebRTCModule.Options options = new WebRTCModule.Options();
AudioDeviceModule adm = JavaAudioDeviceModule.builder(reactContext)
.setEnableVolumeLogger(false)
.createAudioDeviceModule();
options.setAudioDeviceModule(adm);
EglBase.Context eglContext = EglUtils.getRootEglBaseContext();
options.setVideoDecoderFactory(new WebRTCVideoDecoderFactory(eglContext));
options.setVideoEncoderFactory(new WebRTCVideoEncoderFactory(eglContext));
options.setVideoDecoderFactory(new SoftwareVideoDecoderFactory());
options.setVideoEncoderFactory(new SoftwareVideoEncoderFactory());
nativeModules.add(new WebRTCModule(reactContext, options));
@ -105,80 +100,6 @@ class ReactInstanceManagerHolder {
);
}
static List<ReactPackage> getReactNativePackages() {
List<ReactPackage> packages
= new ArrayList<>(Arrays.asList(
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
new com.ocetnik.timer.BackgroundTimerPackage(),
new com.calendarevents.RNCalendarEventsPackage(),
new com.corbt.keepawake.KCKeepAwakePackage(),
new com.facebook.react.shell.MainReactPackage(),
new com.reactnativecommunity.clipboard.ClipboardPackage(),
new com.reactnativecommunity.netinfo.NetInfoPackage(),
new com.reactnativepagerview.PagerViewPackage(),
new com.oblador.performance.PerformancePackage(),
new com.reactnativecommunity.slider.ReactSliderPackage(),
new com.brentvatne.react.ReactVideoPackage(),
new com.reactnativecommunity.webview.RNCWebViewPackage(),
new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
new com.learnium.RNDeviceInfo.RNDeviceInfo(),
new com.swmansion.gesturehandler.RNGestureHandlerPackage(),
new org.linusu.RNGetRandomValuesPackage(),
new com.rnimmersive.RNImmersivePackage(),
new com.swmansion.rnscreens.RNScreensPackage(),
new com.zmxv.RNSound.RNSoundPackage(),
new com.th3rdwave.safeareacontext.SafeAreaContextPackage(),
new com.horcrux.svg.SvgPackage(),
new org.wonday.orientation.OrientationPackage(),
new ReactPackageAdapter() {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return ReactInstanceManagerHolder.createNativeModules(reactContext);
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return ReactInstanceManagerHolder.createViewManagers(reactContext);
}
}));
// AmplitudeReactNativePackage
try {
Class<?> amplitudePackageClass = Class.forName("com.amplitude.reactnative.AmplitudeReactNativePackage");
Constructor constructor = amplitudePackageClass.getConstructor();
packages.add((ReactPackage)constructor.newInstance());
} catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
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
try {
Class<?> googlePackageClass = Class.forName("com.reactnativegooglesignin.RNGoogleSigninPackage");
Constructor constructor = googlePackageClass.getConstructor();
packages.add((ReactPackage)constructor.newInstance());
} catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
Log.d(TAG, "Not loading RNGoogleSignInPackage");
}
return packages;
}
static JSCExecutorFactory getReactNativeJSFactory() {
// Keep on using JSC, the jury is out on Hermes.
return new JSCExecutorFactory("", "");
}
/**
* Helper function to send an event to JavaScript.
*
@ -239,35 +160,6 @@ class ReactInstanceManagerHolder {
return reactInstanceManager;
}
/**
* Internal method to initialize the React Native instance manager. We
* create a single instance in order to load the JavaScript bundle a single
* time. All {@code ReactRootView} instances will be tied to the one and
* only {@code ReactInstanceManager}.
*
* This method is only meant to be called when integrating with {@code JitsiReactNativeHost}.
*
* @param app {@code Application} current running Application.
*/
static void initReactInstanceManager(Application app) {
if (reactInstanceManager != null) {
return;
}
Log.d(TAG, "initializing RN with Application");
reactInstanceManager
= ReactInstanceManager.builder()
.setApplication(app)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index.android")
.setJavaScriptExecutorFactory(getReactNativeJSFactory())
.addPackages(getReactNativePackages())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE)
.build();
}
/**
* Internal method to initialize the React Native instance manager. We
* create a single instance in order to load the JavaScript bundle a single
@ -281,7 +173,61 @@ class ReactInstanceManagerHolder {
return;
}
Log.d(ReactInstanceManagerHolder.class.getCanonicalName(), "initializing RN with Activity");
List<ReactPackage> packages
= new ArrayList<>(Arrays.asList(
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
new com.ocetnik.timer.BackgroundTimerPackage(),
new com.calendarevents.CalendarEventsPackage(),
new com.corbt.keepawake.KCKeepAwakePackage(),
new com.facebook.react.shell.MainReactPackage(),
new com.reactnativecommunity.netinfo.NetInfoPackage(),
new com.oblador.performance.PerformancePackage(),
new com.reactnativecommunity.slider.ReactSliderPackage(),
new com.brentvatne.react.ReactVideoPackage(),
new com.swmansion.reanimated.ReanimatedPackage(),
new org.reactnative.maskedview.RNCMaskedViewPackage(),
new com.reactnativecommunity.webview.RNCWebViewPackage(),
new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
new com.learnium.RNDeviceInfo.RNDeviceInfo(),
new com.swmansion.gesturehandler.react.RNGestureHandlerPackage(),
new org.linusu.RNGetRandomValuesPackage(),
new com.rnimmersive.RNImmersivePackage(),
new com.swmansion.rnscreens.RNScreensPackage(),
new com.zmxv.RNSound.RNSoundPackage(),
new com.th3rdwave.safeareacontext.SafeAreaContextPackage(),
new com.horcrux.svg.SvgPackage(),
new ReactPackageAdapter() {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return ReactInstanceManagerHolder.createNativeModules(reactContext);
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return ReactInstanceManagerHolder.createViewManagers(reactContext);
}
}));
// AmplitudeReactNativePackage
try {
Class<?> amplitudePackageClass = Class.forName("com.amplitude.reactnative.AmplitudeReactNativePackage");
Constructor constructor = amplitudePackageClass.getConstructor();
packages.add((ReactPackage)constructor.newInstance());
} catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
}
// RNGoogleSigninPackage
try {
Class<?> googlePackageClass = Class.forName("co.apptailor.googlesignin.RNGoogleSigninPackage");
Constructor constructor = googlePackageClass.getConstructor();
packages.add((ReactPackage)constructor.newInstance());
} catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
}
// Keep on using JSC, the jury is out on Hermes.
JSCExecutorFactory jsFactory
= new JSCExecutorFactory("", "");
reactInstanceManager
= ReactInstanceManager.builder()
@ -289,10 +235,20 @@ class ReactInstanceManagerHolder {
.setCurrentActivity(activity)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index.android")
.setJavaScriptExecutorFactory(getReactNativeJSFactory())
.addPackages(getReactNativePackages())
.setJavaScriptExecutorFactory(jsFactory)
.addPackages(packages)
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
// Disable delta updates on Android, they have caused trouble.
DevInternalSettings devSettings
= (DevInternalSettings)reactInstanceManager.getDevSupportManager().getDevSettings();
if (devSettings != null) {
devSettings.setBundleDeltasEnabled(false);
}
// Register our uncaught exception handler.
JitsiMeetUncaughtExceptionHandler.register();
}
}

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);
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright @ 2018-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk.incoming_call;
import androidx.annotation.NonNull;
public class IncomingCallInfo {
/**
* URL for the caller avatar.
*/
private final String callerAvatarURL;
/**
* Caller's name.
*/
private final String callerName;
/**
* Whether this is a regular call or a video call.
*/
private final boolean hasVideo;
public IncomingCallInfo(
@NonNull String callerName,
@NonNull String callerAvatarURL,
boolean hasVideo) {
this.callerName = callerName;
this.callerAvatarURL = callerAvatarURL;
this.hasVideo = hasVideo;
}
/**
* Gets the caller's avatar URL.
*
* @return - The URL as a string.
*/
public String getCallerAvatarURL() {
return callerAvatarURL;
}
/**
* Gets the caller's name.
*
* @return - The caller's name.
*/
public String getCallerName() {
return callerName;
}
/**
* Gets whether the call is a video call or not.
*
* @return - {@code true} if this call has video; {@code false}, otherwise.
*/
public boolean hasVideo() {
return hasVideo;
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright @ 2018-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk.incoming_call;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.ReadableMap;
import org.jitsi.meet.sdk.BaseReactView;
import org.jitsi.meet.sdk.ListenerUtils;
import java.lang.reflect.Method;
import java.util.Map;
public class IncomingCallView
extends BaseReactView<IncomingCallViewListener> {
/**
* The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
* redux action types.
*/
private static final Map<String, Method> LISTENER_METHODS
= ListenerUtils.mapListenerMethods(IncomingCallViewListener.class);
public IncomingCallView(@NonNull Context context) {
super(context);
}
/**
* Handler for {@link ExternalAPIModule} events.
*
* @param name The name of the event.
* @param data The details/specifics of the event to send determined
* by/associated with the specified {@code name}.
*/
@Override
protected void onExternalAPIEvent(String name, ReadableMap data) {
onExternalAPIEvent(LISTENER_METHODS, name, data);
}
/**
* Sets the information for the incoming call this {@code IncomingCallView}
* represents.
*
* @param callInfo - {@link IncomingCallInfo} object representing the caller
* information.
*/
public void setIncomingCallInfo(IncomingCallInfo callInfo) {
Bundle props = new Bundle();
props.putString("callerAvatarURL", callInfo.getCallerAvatarURL());
props.putString("callerName", callInfo.getCallerName());
props.putBoolean("hasVideo", callInfo.hasVideo());
createReactRootView("IncomingCallApp", props);
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright @ 2018-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk.incoming_call;
import java.util.Map;
/**
* Interface for listening to events coming from Jitsi Meet, related to
* {@link IncomingCallView}.
*/
public interface IncomingCallViewListener {
/**
* Called when the user presses the "Answer" button on the
* {@link IncomingCallView}.
*
* @param data - Unused at the moment.
*/
void onIncomingCallAnswered(Map<String, Object> data);
/**
* Called when the user presses the "Decline" button on the
* {@link IncomingCallView}.
*
* @param data - Unused at the moment.
*/
void onIncomingCallDeclined(Map<String, Object> data);
}

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

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'
include ':app', ':sdk'
includeBuild('../node_modules/react-native-gradle-plugin')
include ':react-native-amplitude'
project(':react-native-amplitude').projectDir = new File(rootProject.projectDir, '../node_modules/@amplitude/react-native//android')
include ':react-native-async-storage'
@ -11,8 +9,6 @@ include ':react-native-background-timer'
project(':react-native-background-timer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-background-timer/android')
include ':react-native-calendar-events'
project(':react-native-calendar-events').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-calendar-events/android')
include ':react-native-community_clipboard'
project(':react-native-community_clipboard').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/clipboard/android')
include ':react-native-community_netinfo'
project(':react-native-community_netinfo').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/netinfo/android')
include ':react-native-default-preference'
@ -23,20 +19,18 @@ include ':react-native-gesture-handler'
project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
include ':react-native-get-random-values'
project(':react-native-get-random-values').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-get-random-values/android')
include ':react-native-giphy'
project(':react-native-giphy').projectDir = new File(rootProject.projectDir, '../node_modules/@giphy/react-native-sdk/android')
include ':react-native-google-signin'
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-google-signin/google-signin/android')
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/google-signin/android')
include ':react-native-immersive'
project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android')
include ':react-native-keep-awake'
project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keep-awake/android')
include ':react-native-orientation-locker'
project(':react-native-orientation-locker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-orientation-locker/android')
include ':react-native-pager-view'
project(':react-native-pager-view').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-pager-view/android')
include ':react-native-masked-view_masked-view'
project(':react-native-masked-view_masked-view').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-masked-view/masked-view/android')
include ':react-native-performance'
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'
project(':react-native-safe-area-context').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-safe-area-context/android')
include ':react-native-screens'

17
app.js
View File

@ -1,10 +1,6 @@
/* application specific logic */
// Re-export jQuery
// FIXME: Remove this requirement from torture tests.
import $ from 'jquery';
window.$ = window.jQuery = $;
import 'jquery';
import '@matrix-org/olm';
@ -33,6 +29,17 @@ window.APP = {
API,
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.
connectionTimes: {
'index.loaded': window.indexLoadedTime

1
bla Normal file
View File

@ -0,0 +1 @@

File diff suppressed because it is too large Load Diff

861
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 {
connectionEstablished,
connectionFailed,
constructOptions
} from './react/features/base/connection/actions.web';
connectionFailed
} from './react/features/base/connection/actions';
import { openDialog } from './react/features/base/dialog/actions';
import { setJWT } from './react/features/base/jwt';
import {
isFatalJitsiConnectionError,
JitsiConnectionErrors,
JitsiConnectionEvents
} from './react/features/base/lib-jitsi-meet';
import { isFatalJitsiConnectionError } from './react/features/base/lib-jitsi-meet/functions';
import { getCustomerDetails } from './react/features/jaas/actions.any';
import { getJaasJWT, isVpaasMeeting } from './react/features/jaas/functions';
import {
setPrejoinDisplayNameRequired
} from './react/features/prejoin/actions';
import { isVpaasMeeting, getJaasJWT } from './react/features/jaas/functions';
import { setPrejoinDisplayNameRequired } from './react/features/prejoin/actions';
const logger = Logger.getLogger(__filename);
/**
@ -32,14 +29,64 @@ const logger = Logger.getLogger(__filename);
*/
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.
* @param {string} [id]
* @param {string} [password]
* @param {string} [roomName]
* @returns {Promise<JitsiConnection>} connection if
* everything is ok, else error.
*/
export async function connect(id, password) {
export async function connect(id, password, roomName) {
const connectionConfig = Object.assign({}, config);
const state = APP.store.getState();
let { jwt } = state['features/base/jwt'];
const { iAmRecorder, iAmSipGateway } = state['features/base/config'];
@ -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) {
connection.addFeature(DISCO_JIBRI_FEATURE);
@ -134,10 +193,7 @@ export async function connect(id, password) {
APP.store.dispatch(setPrejoinDisplayNameRequired());
}
connection.connect({
id,
password
});
checkForAttachParametersAndConnect(id, password, connection);
});
}

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

@ -4,12 +4,11 @@
text-align: center;
h2 {
font-size: 48px;
color : #f2f2f2;
font-size: 36px;
}
&__message {
font-size: 24px;
margin-top: 20px;
}
}
}

View File

@ -24,11 +24,19 @@
bottom: calc(#{$newToolbarSizeWithPadding}) !important;
}
/**
* Override @atlaskit/theme styling for the top toolbar so it displays over
* the video thumbnail while obscuring as little as possible.
*/
.videocontainer__toptoolbar > div > div {
background: none;
}
/**
* Keep overflow menu within screen vertical bounds and make it scrollable.
*/
.toolbox-button-wth-dialog > div:nth-child(2) {
.toolbox-button-wth-dialog > div:nth-child(2) {
background: $menuBG;
max-height: calc(100vh - #{$newToolbarSizeWithPadding} - 46px);
margin-bottom: 4px;
@ -36,22 +44,19 @@
overflow-y: auto;
}
/**
* Remove background color and box-shadow for the context menu container.
*/
.toolbox-button-wth-dialog.context-menu > div:nth-child(2) {
background: transparent;
box-shadow: none;
overflow-y: initial;
}
.audio-preview > div:nth-child(2),
.video-preview > div:nth-child(2) {
.video-preview > div:nth-child(2),
.reactions-menu-popup > div:nth-child(2) {
margin-bottom: 4px;
outline: none;
padding: 0;
}
.reactions-menu-popup > div:nth-child(2) {
margin-bottom: 6px;
box-shadow: none;
}
/**
* The following selectors keep the chat modal full-size anywhere between 100px
* and 580px for desktop or 680px for mobile.

View File

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

230
css/_aui_reset.scss Normal file
View File

@ -0,0 +1,230 @@
/* Fonts and line heights */
/**
* RESET
*/
html,
body,
p,
div,
h1,
h2,
h3,
h4,
h5,
h6,
img,
pre,
form,
fieldset {
margin: 0;
padding: 0;
}
ul,
ol,
dl {
margin: 0;
}
img,
fieldset {
border: 0;
}
@-moz-document url-prefix() {
img {
font-size: 0;
}
img:-moz-broken {
font-size: inherit;
}
}
/* https://github.com/necolas/normalize.css */
/* Customised to remove styles for unsupported browsers */
details,
main,
summary {
display: block;
}
audio,
canvas,
progress,
video {
display: inline-block;
vertical-align: baseline;
}
audio:not([controls]) {
display: none;
height: 0;
}
[hidden],
template {
display: none;
}
input[type="button"],
input[type="submit"],
input[type="reset"] {
-webkit-appearance: button;
}
/**
* TYPOGRAPHY - 14px base font size, agnostic font stack
*/
body {
color: #333;
font-family: Arial, sans-serif;
font-size: 14px;
line-height: 1.42857142857143;
}
/* International Font Stacks*/
[lang|=en] {
font-family: Arial, sans-serif;
}
[lang|=ja] {
font-family: "Hiragino Kaku Gothic Pro", "ヒラギノ角ゴ Pro W3", "メイリオ", Meiryo, " Pゴシック", Verdana, Arial, sans-serif;
}
/* Default margins */
p,
ul,
ol,
dl,
h1,
h2,
h3,
h4,
h5,
h6,
blockquote,
pre {
margin: 10px 0 0 0;
}
/* No top margin to interfere with box padding */
p:first-child,
ul:first-child,
ol:first-child,
dl:first-child,
h1:first-child,
h2:first-child,
h3:first-child,
h4:first-child,
h5:first-child,
h6:first-child,
blockquote:first-child,
pre:first-child {
margin-top: 0;
}
/* Headings: desired line height in px / font size = unitless line height */
h1 {
color: #333;
font-size: 32px;
font-weight: normal;
line-height: 1.25;
text-transform: none;
margin: 30px 0 0 0;
}
h2 {
color: #333;
font-size: 24px;
font-weight: normal;
line-height: 1.25;
text-transform: none;
margin: 30px 0 0 0;
}
h3 {
color: #333;
font-size: 20px;
font-weight: normal;
line-height: 1.5;
text-transform: none;
margin: 30px 0 0 0;
}
h4 {
font-size: 16px;
font-weight: bold;
line-height: 1.25;
text-transform: none;
margin: 20px 0 0 0;
}
h5 {
color: #333;
font-size: 14px;
font-weight: bold;
line-height: 1.42857143;
text-transform: none;
margin: 20px 0 0 0;
}
h6 {
color: #707070;
font-size: 12px;
font-weight: bold;
line-height: 1.66666667;
text-transform: uppercase;
margin: 20px 0 0 0;
}
h1:first-child,
h2:first-child,
h3:first-child,
h4:first-child,
h5:first-child,
h6:first-child {
margin-top: 0;
}
/* Nice styles for using subheadings */
h1 + h2,
h2 + h3,
h3 + h4,
h4 + h5,
h5 + h6 {
margin-top: 10px;
}
/* Other typographical elements */
small {
color: #707070;
font-size: 12px;
line-height: 1.33333333333333;
}
code,
kbd {
font-family: monospace;
}
var,
address,
dfn,
cite {
font-style: italic;
}
cite:before {
content: "\2014 \2009";
}
blockquote {
border-left: 1px solid #ccc;
color: #707070;
margin-left: 19px;
padding: 10px 20px;
}
blockquote > cite {
display: block;
margin-top: 10px;
}
q {
color: #707070;
}
q:before {
content: open-quote;
}
q:after {
content: close-quote;
}
abbr {
border-bottom: 1px #707070 dotted;
cursor: help;
}
a {
color: #44A5FF;
text-decoration: none;
font-weight: bold;
}
a:focus,
a:hover,
a:active {
text-decoration: underline;
}

50
css/_avatar.scss Normal file
View File

@ -0,0 +1,50 @@
.avatar {
background-color: #AAA;
border-radius: 50%;
color: rgba(255, 255, 255, 1);
font-weight: 100;
object-fit: cover;
&.avatar-small {
height: 28px !important;
width: 28px !important;
}
&.avatar-xsmall {
height: 16px !important;
width: 16px !important;
}
.jitsi-icon {
transform: translateY(50%);
}
}
.avatar-svg {
height: 100%;
width: 100%;
}
.avatar-badge {
position: relative;
&-available::after {
@include avatarBadge;
background-color: $presence-available;
}
&-away::after {
@include avatarBadge;
background-color: $presence-away;
}
&-busy::after {
@include avatarBadge;
background-color: $presence-busy;
}
&-idle::after {
@include avatarBadge;
background-color: $presence-idle;
}
}

View File

@ -6,10 +6,6 @@
* {
-webkit-user-select: none;
user-select: none;
// Firefox only
scrollbar-width: thin;
scrollbar-color: rgba(0, 0, 0, .5) transparent;
}
input,
@ -43,6 +39,18 @@ body {
outline: none;
}
/**
* AtlasKit sets a default margin on the rendered modals, so
* when the shift-right class is set when the chat opens, we
* pad the modal container in order for the modals to be centered
* while also taking the chat size into consideration.
*/
@media (min-width: 581px) {
.shift-right .atlaskit-portal > div:not(.Tooltip) {
padding-left: $sidebarWidth;
}
}
.jitsi-icon {
&-default svg {
fill: white;
@ -126,12 +134,6 @@ form {
background-size: contain;
}
.leftwatermarknomargin {
background-position: center left;
background-repeat: no-repeat;
background-size: contain;
}
.rightwatermark {
right: 32px;
top: 32px;
@ -158,6 +160,16 @@ form {
font-size: 12px;
}
/**
* Dialogs fade
*/
.aui-blanket {
background: #000;
transition: opacity 0.2s, visibility 0.2s;
transition-delay: 0.1s;
visibility: visible;
}
#inviteLinkRef {
-webkit-user-select: text;
user-select: text;
@ -188,8 +200,3 @@ form {
background: rgba(0, 0, 0, .5);
border-radius: 4px;
}
/* Necessary for the new icons to work properly. */
.jitsi-icon svg path {
fill: inherit !important;
}

View File

@ -1,20 +1,15 @@
#sideToolbarContainer {
background-color: $chatBackgroundColor;
flex-shrink: 0;
overflow: hidden;
position: relative;
transition: width .16s ease-in-out;
box-sizing: border-box;
color: #FFF;
height: 100%;
position: absolute;
top: 0;
width: $sidebarWidth;
z-index: $sideToolbarContainerZ;
@media (max-width: 580px) {
height: 100vh;
height: -webkit-fill-available;
left: 0;
position: fixed;
right: 0;
top: 0;
width: auto;
width: 100%;
}
}
@ -22,7 +17,7 @@
display: flex;
flex-direction: column;
// extract header + tabs height
height: calc(100% - 119px);
height: calc(100% - 102px);
}
.chat-panel-no-tabs {
@ -30,18 +25,12 @@
height: calc(100% - 70px);
}
#chat-conversation-container {
// extract message input height
height: calc(100% - 64px);
overflow: hidden;
position: relative;
}
#chatconversation {
box-sizing: border-box;
flex: 1;
font-size: 10pt;
height: 100%;
// extract message input height
height: calc(100% - 68px);
line-height: 20px;
overflow: auto;
padding: 16px;
@ -76,6 +65,28 @@
}
}
#chat-recipient {
align-items: center;
background-color: $chatPrivateMessageBackgroundColor;
display: flex;
flex-direction: row;
font-weight: 100;
padding: 10px;
span {
color: white;
display: flex;
flex: 1;
}
div {
svg {
cursor: pointer;
fill: white;
}
}
}
.chat-header {
height: 70px;
@ -98,18 +109,64 @@
}
.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 {
border: 1px solid $chatInputSeparatorColor;
display: flex;
align-items: flex-end;
position: relative;
padding: 4px;
border-radius: 3px;
&:focus-within {
border: 1px solid #619CF4;
}
* {
background-color: transparent;
}
}
.chat-input {
flex: 1;
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 {
@ -236,6 +293,15 @@
-webkit-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 {
@ -252,11 +318,24 @@
}
.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 {
background-color: $chatLocalMessageBackgroundColor;
border-radius: 6px 0px 6px 6px;
}
.usermessage {
white-space: pre-wrap;
font-size: 14px;
}
&.error {
border-radius: 0px;
@ -271,12 +350,22 @@
}
}
.privatemessagenotice {
font-size: 11px;
font-weight: 100;
}
.messagecontent {
margin: 8px;
max-width: 100%;
overflow: hidden;
}
}
.timestamp {
color: #757575;
}
#smileys {
font-size: 20pt;
margin: auto;
@ -297,9 +386,7 @@
.smiley-input {
display: flex;
position: absolute;
top: 0;
left: 0;
position: relative;
}
.smileys-panel {
@ -307,7 +394,7 @@
box-sizing: border-box;
background-color: rgba(0, 0, 0, .6) !important;
height: auto;
display: flex;
display: none;
overflow: hidden;
position: absolute;
width: calc(#{$sidebarWidth} - 32px);
@ -322,6 +409,11 @@
*/
transition: max-height 0.3s;
&.show-smileys {
display: flex;
max-height: 500%;
}
#smileysContainer {
background-color: $chatBackgroundColor;
border-top: 1px solid $chatInputSeparatorColor;
@ -350,9 +442,21 @@
}
.chat-message-group {
display: flex;
flex-direction: column;
&.local {
align-items: flex-end;
.chatmessage {
background-color: $chatLocalMessageBackgroundColor;
border-radius: 6px 0px 6px 6px;
&.privatemessage {
background-color: $chatPrivateMessageBackgroundColor;
}
}
.display-name {
display: none;
}
@ -363,10 +467,51 @@
}
&.error {
.chatmessage {
background-color: $defaultWarningColor;
border-radius: 0px;
font-weight: 100;
}
.display-name {
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;
.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;
}
}
}
.chat-dialog {
@ -409,3 +554,41 @@
background: #36383C;
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;
}

50
css/_connection-info.scss Normal file
View File

@ -0,0 +1,50 @@
%connection-info {
font-size: 12px;
font-weight: 400;
td {
padding: 2px 0;
}
}
.connection-info
{
@extend %connection-info;
> table {
white-space: nowrap;
@extend %connection-info;
}
td:nth-child(n-1) {
padding-left: 5px;
}
&__icon {
margin-right: 2px;
}
&__download
{
@extend .connection-info__icon;
}
&__status
{
font-weight: bold;
}
&__upload
{
@extend .connection-info__icon;
}
&__mobile {
margin: 15px;
}
.connection-actions {
margin: 10px auto;
text-align: center;
}
}

View File

@ -24,7 +24,10 @@
}
.drawer-menu {
overflow-y: auto;
max-height: calc(80vh - 64px);
background: #242528;
border-radius: 16px 16px 0 0;
overflow-y: scroll;
margin-bottom: env(safe-area-inset-bottom, 0);
width: 100%;
@ -40,6 +43,15 @@
}
}
.popupmenu {
margin: auto;
width: 100%;
}
.popupmenu__item {
height: 48px;
}
&#{&} .overflow-menu {
margin: auto;
font-size: 1.2em;

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

@ -0,0 +1,10 @@
.shortcuts-list {
list-style-type: none;
padding: 0;
&__item {
display: flex;
justify-content: space-between;
margin-bottom: em(7, 14);
}
}

View File

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

View File

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

View File

@ -195,15 +195,14 @@
}
/**
* Change the direction of the current element to LTR, but do not change the direction
* of its children; Keep them RTL.
* Avatar status badge mixin
*/
@mixin ltr {
body[dir=rtl] & {
direction: ltr;
& > * {
direction: rtl;
}
}
@mixin avatarBadge {
border-radius: 50%;
content: '';
display: block;
height: 35%;
position: absolute;
bottom: 0;
width: 35%;
}

View File

@ -6,6 +6,10 @@
transition: width .16s ease-in-out;
width: 315px;
z-index: $zindex0;
&--closed {
width: 0;
}
}
.participants_pane-content {
@ -21,6 +25,10 @@
}
}
.participant-avatar {
margin: 8px 16px 8px 0;
}
@media (max-width: 580px) {
.participants_pane {
height: 100vh;
@ -30,6 +38,11 @@
right: 0;
top: 0;
width: auto;
&--closed {
display: none;
width: auto;
}
}
.participants_pane-content {

View File

@ -1,3 +1,444 @@
.polls-panel {
height: calc(100% - 119px);
.poll-dialog {
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

@ -43,9 +43,10 @@
.popover {
margin: -16px -24px;
padding: 16px 24px;
z-index: $popoverZ;
}
.excalidraw .popover {
margin: 0;
.padded-content {
padding: 4px 8px;
}

View File

@ -2,18 +2,122 @@
* Initialize
**/
.popupmenu__contents {
.popupmenu__volume-slider {
&::-webkit-slider-runnable-track {
background-color: #246FE5;
}
.popupmenu {
background-color: $menuBG;
border-radius: 3px;
list-style-type: none;
min-width: 150px;
text-align: left;
padding: 0px;
white-space: nowrap;
&::-moz-range-track {
background-color: #246FE5;
}
&__item {
list-style-type: none;
height: 35px;
}
&::-ms-fill-lower {
background-color: #246FE5;
// Link Appearance
&__link,
&__contents {
display: block;
box-sizing: border-box;
text-decoration: none;
height: 100%;
font-size: 9pt;
width: 100%;
cursor: pointer;
padding: 0 5px;
color: $popupMenuColor;
&:hover {
background-color: $popupMenuHoverBackground;
color: $popupMenuHoverColor;
}
&.disabled {
pointer-events: none;
}
}
&__list {
margin: 0;
padding: 0;
}
&__text {
display: inline-block;
margin-left: 8px;
vertical-align: middle;
}
&__link {
i {
cursor: pointer;
}
}
&__contents {
display: flex;
/**
* Positioning styles on the slider and its container are used to make
* the container fit the popup width, by removing the slider from the
* page flow, and then making the slider fit the container.
*/
.popupmenu__slider_container {
position: relative;
width: 100%;
.popupmenu__slider {
position: absolute;
top: 50%;
transform: translate(0, -50%);
width: 100%;
&::-webkit-slider-runnable-track {
background-color: $popupSliderColor;
}
&::-moz-range-track {
background-color: $popupSliderColor;
}
&::-ms-fill-lower {
background-color: $popupSliderColor;
}
}
}
}
&__icon {
vertical-align: middle;
position: relative;
display: inline-block;
min-width: 20px;
height: 100%;
padding-right: 10px;
> * {
@include absoluteAligning();
}
}
.icon-kick,
.icon-play,
.icon-stop {
font-size: 8pt;
}
}
/**
* Override reset css styling modifying all lists and set negative margin to
* reduce the visibility of padding on AtlasKit
* InlineDialogs.
*/
ul.popupmenu {
margin: -16px -24px;
}
span.localvideomenu:hover ul.popupmenu, span.remotevideomenu:hover ul.popupmenu, ul.popupmenu:hover {
display:block !important;
}

View File

@ -7,20 +7,13 @@
border-radius: 3px;
padding: 16px;
&.with-gif {
width: 328px;
.reactions-row .toolbox-button:last-of-type {
top: 3px;
& .toolbox-icon.toggled {
background-color: #000000;
}
}
}
&.overflow {
width: 100%;
width: auto;
padding-bottom: max(env(safe-area-inset-bottom, 0), 16px);
background-color: #141414;
box-shadow: none;
border-radius: 0;
position: relative;
.toolbox-icon {
width: 48px;
@ -40,10 +33,6 @@
.toolbox-button {
margin-right: 0;
}
.toolbox-button:last-of-type {
top: 0;
}
}
}
@ -73,7 +62,6 @@
.toolbox-button {
margin-right: 8px;
touch-action: manipulation;
position: relative;
}
.toolbox-button:last-of-type {
@ -104,10 +92,6 @@
}
}
.reactions-menu-container {
padding-bottom: 6px;
}
.reactions-animations-container {
position: absolute;
width: 20%;
@ -116,7 +100,8 @@
height: 0;
}
.reactions-menu-popup-container {
.reactions-menu-popup-container,
.reactions-menu-popup {
display: inline-block;
position: relative;
}

View File

@ -7,47 +7,22 @@
flex-direction: column;
.recording-header {
align-items: center;
display: flex;
flex: 0;
flex-direction: row;
justify-content: space-between;
padding-top: 32px;
.recording-title {
display: inline-flex;
align-items: center;
font-size: 14px;
font-size: 16px;
margin-left: 16px;
max-width: 70%;
}
&.space-top {
margin-top: 10px;
}
}
.recording-header-line {
border-top: 1px solid #5e6d7a;
padding-top: 16px;
margin-top: 16px;
}
.local-recording-warning {
margin-top: 8px;
display: block;
font-size: 14px;
line-height: 20px;
padding: 8px 16px;
&.text {
color: #fff;
background-color: #3D3D3D;
}
&.notification {
color: #040404;
background-color: #F8AE1A;
}
}
.recording-switch-disabled {
@ -59,79 +34,10 @@
align-items: center;
}
.file-sharing-icon-container {
background-color: #525252;
border-radius: 4px;
height: 40px;
justify-content: center;
width: 42px;
}
.cloud-content-recording-icon-container {
background-color: #FFFFFF;
border-radius: 4px;
height: 40px;
justify-content: center;
width: 40px;
}
.jitsi-recording-header {
margin-bottom: 16px;
}
.jitsi-content-recording-icon-container-with-switch {
background-color: #FFFFFF;
border-radius: 4px;
height: 40px;
width: 40px;
}
.jitsi-content-recording-icon-container-without-switch {
background-color: #FFFFFF;
border-radius: 4px;
height: 40px;
width: 46px;
}
.recording-icon {
height: 40px;
width: 32px;
height: 32px;
object-fit: contain;
width: 40px;
}
.content-recording-icon {
height: 18px;
margin: 10px 0 0 10px;
object-fit: contain;
width: 18px;
}
.recording-file-sharing-icon {
height: 18px;
object-fit: contain;
width: 18px;
}
.recording-info{
background-color: #FFD740;
color: black;
display: inline-flex;
margin: 32px 0;
width: 100%;
}
.recording-info-icon {
align-self: center;
height: 14px;
margin: 0 24px 0 16px;
object-fit: contain;
width: 14px;
}
.recording-info-title {
display: inline-flex;
font-size: 14px;
width: 290px
}
.recording-switch {

View File

@ -1,231 +0,0 @@
/* Fonts and line heights */
/**
* RESET
*/
html,
body,
p,
div,
h1,
h2,
h3,
h4,
h5,
h6,
img,
pre,
form,
fieldset {
margin: 0;
padding: 0;
}
ul,
ol,
dl {
margin: 0;
}
img,
fieldset {
border: 0;
}
@-moz-document url-prefix() {
img {
font-size: 0;
}
img:-moz-broken {
font-size: inherit;
}
}
/* https://github.com/necolas/normalize.css */
/* Customised to remove styles for unsupported browsers */
details,
main,
summary {
display: block;
}
audio,
canvas,
progress,
video {
display: inline-block;
transition: object-position 0.5s ease 0s;
vertical-align: baseline;
}
audio:not([controls]) {
display: none;
height: 0;
}
[hidden],
template {
display: none;
}
input[type="button"],
input[type="submit"],
input[type="reset"] {
-webkit-appearance: button;
}
/**
* TYPOGRAPHY - 14px base font size, agnostic font stack
*/
body {
color: #333;
font-family: Arial, sans-serif;
font-size: 14px;
line-height: 1.42857142857143;
}
/* International Font Stacks*/
[lang|=en] {
font-family: Arial, sans-serif;
}
[lang|=ja] {
font-family: "Hiragino Kaku Gothic Pro", "ヒラギノ角ゴ Pro W3", "メイリオ", Meiryo, " Pゴシック", Verdana, Arial, sans-serif;
}
/* Default margins */
p,
ul,
ol,
dl,
h1,
h2,
h3,
h4,
h5,
h6,
blockquote,
pre {
margin: 10px 0 0 0;
}
/* No top margin to interfere with box padding */
p:first-child,
ul:first-child,
ol:first-child,
dl:first-child,
h1:first-child,
h2:first-child,
h3:first-child,
h4:first-child,
h5:first-child,
h6:first-child,
blockquote:first-child,
pre:first-child {
margin-top: 0;
}
/* Headings: desired line height in px / font size = unitless line height */
h1 {
color: #333;
font-size: 32px;
font-weight: normal;
line-height: 1.25;
text-transform: none;
margin: 30px 0 0 0;
}
h2 {
color: #333;
font-size: 24px;
font-weight: normal;
line-height: 1.25;
text-transform: none;
margin: 30px 0 0 0;
}
h3 {
color: #333;
font-size: 20px;
font-weight: normal;
line-height: 1.5;
text-transform: none;
margin: 30px 0 0 0;
}
h4 {
font-size: 16px;
font-weight: bold;
line-height: 1.25;
text-transform: none;
margin: 20px 0 0 0;
}
h5 {
color: #333;
font-size: 14px;
font-weight: bold;
line-height: 1.42857143;
text-transform: none;
margin: 20px 0 0 0;
}
h6 {
color: #707070;
font-size: 12px;
font-weight: bold;
line-height: 1.66666667;
text-transform: uppercase;
margin: 20px 0 0 0;
}
h1:first-child,
h2:first-child,
h3:first-child,
h4:first-child,
h5:first-child,
h6:first-child {
margin-top: 0;
}
/* Nice styles for using subheadings */
h1 + h2,
h2 + h3,
h3 + h4,
h4 + h5,
h5 + h6 {
margin-top: 10px;
}
/* Other typographical elements */
small {
color: #707070;
font-size: 12px;
line-height: 1.33333333333333;
}
code,
kbd {
font-family: monospace;
}
var,
address,
dfn,
cite {
font-style: italic;
}
cite:before {
content: "\2014 \2009";
}
blockquote {
border-left: 1px solid #ccc;
color: #707070;
margin-left: 19px;
padding: 10px 20px;
}
blockquote > cite {
display: block;
margin-top: 10px;
}
q {
color: #707070;
}
q:before {
content: open-quote;
}
q:after {
content: close-quote;
}
abbr {
border-bottom: 1px #707070 dotted;
cursor: help;
}
a {
color: #44A5FF;
text-decoration: none;
font-weight: bold;
}
a:focus,
a:hover,
a:active {
text-decoration: underline;
}

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