Compare commits
1 Commits
8564
...
remote-con
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9235000a7f |
6
.buckconfig
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
[android]
|
||||||
|
target = Google Inc.:Google APIs:23
|
||||||
|
|
||||||
|
[maven_repositories]
|
||||||
|
central = https://repo1.maven.org/maven2
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Jitsi Meet Dev Container",
|
|
||||||
"image": "mcr.microsoft.com/devcontainers/universal:2",
|
|
||||||
"features": {
|
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
|
||||||
"version": "20"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hostRequirements": {
|
|
||||||
"cpus": 4,
|
|
||||||
"memory": "8gb",
|
|
||||||
"storage": "32gb"
|
|
||||||
},
|
|
||||||
"postCreateCommand": "bash -i -c 'nvm use && npm install && cp tsconfig.web.json tsconfig.json'"
|
|
||||||
}
|
|
||||||
@@ -6,11 +6,8 @@ charset = utf-8
|
|||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
indent_style = space
|
indent_style = space
|
||||||
max_line_length = 120
|
max_line_length = 80
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
[*.md]
|
|
||||||
trim_trailing_whitespace = false
|
|
||||||
|
|
||||||
[Makefile]
|
[Makefile]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
|||||||
@@ -1,24 +1,12 @@
|
|||||||
# The build artifacts of the jitsi-meet project.
|
# The build artifacts of the jitsi-meet project.
|
||||||
build/*
|
build/*
|
||||||
|
|
||||||
doc/*
|
|
||||||
|
|
||||||
# Third-party source code which we (1) do not want to modify or (2) try to
|
# Third-party source code which we (1) do not want to modify or (2) try to
|
||||||
# modify as little as possible.
|
# modify as little as possible.
|
||||||
|
flow-typed/*
|
||||||
libs/*
|
libs/*
|
||||||
resources/*
|
|
||||||
react/features/stream-effects/virtual-background/vendor/*
|
|
||||||
react/features/face-landmarks/resources/*
|
|
||||||
|
|
||||||
# ESLint will by default ignore its own configuration file. However, there does
|
# ESLint will by default ignore its own configuration file. However, there does
|
||||||
# not seem to be a reason why we will want to risk being inconsistent with our
|
# not seem to be a reason why we will want to risk being inconsistent with our
|
||||||
# remaining JavaScript source code.
|
# remaining JavaScript source code.
|
||||||
!.eslintrc.js
|
!.eslintrc.js
|
||||||
|
|
||||||
# Not worth it.
|
|
||||||
actionTypes.ts
|
|
||||||
|
|
||||||
# It's not complete until all files are copied at build time.
|
|
||||||
react-native-sdk/
|
|
||||||
|
|
||||||
*.d.ts
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
extends: [
|
'extends': [
|
||||||
'@jitsi/eslint-config'
|
'eslint-config-jitsi'
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
118
.flowconfig
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
[ignore]
|
||||||
|
; We fork some components by platform
|
||||||
|
.*/*[.]android.js
|
||||||
|
|
||||||
|
; Ignore unexpected extra "@providesModule"
|
||||||
|
.*/node_modules/.*/node_modules/fbjs/.*
|
||||||
|
node_modules/react-native/Libraries/react-native/React.js
|
||||||
|
|
||||||
|
; Ignore duplicate module providers
|
||||||
|
; For RN Apps installed via npm, "Libraries" folder is inside
|
||||||
|
; "node_modules/react-native" but in the source repo it is in the root
|
||||||
|
node_modules/react-native/Libraries/react-native/React.js
|
||||||
|
|
||||||
|
; Flow doesn't support platforms
|
||||||
|
.*/Libraries/Utilities/LoadingView.js
|
||||||
|
|
||||||
|
; Ignore polyfills
|
||||||
|
node_modules/react-native/Libraries/polyfills/.*
|
||||||
|
|
||||||
|
; These should not be required directly
|
||||||
|
; require from fbjs/lib instead: require('fbjs/lib/warning')
|
||||||
|
node_modules/warning/.*
|
||||||
|
|
||||||
|
; Flow doesn't support platforms
|
||||||
|
.*/Libraries/Utilities/HMRLoadingView.js
|
||||||
|
|
||||||
|
[untyped]
|
||||||
|
.*/node_modules/@react-native-community/cli/.*/.*
|
||||||
|
|
||||||
|
; Ignore packages in node_modules which we (i.e. the jitsi-meet project) have
|
||||||
|
; seen to cause errors and we have chosen not to fix.
|
||||||
|
.*/node_modules/@atlaskit/.*/*.js.flow
|
||||||
|
.*/node_modules/react-native-keep-awake/.*
|
||||||
|
.*/node_modules/react-native-permissions/.*
|
||||||
|
.*/node_modules/styled-components/.*
|
||||||
|
|
||||||
|
.*/\.git/.*
|
||||||
|
|
||||||
|
[include]
|
||||||
|
|
||||||
|
[libs]
|
||||||
|
node_modules/react-native/Libraries/react-native/react-native-interface.js
|
||||||
|
node_modules/react-native/flow/
|
||||||
|
|
||||||
|
[options]
|
||||||
|
emoji=true
|
||||||
|
|
||||||
|
esproposal.optional_chaining=enable
|
||||||
|
esproposal.nullish_coalescing=enable
|
||||||
|
|
||||||
|
; We (i.e. the jitsi-meet project) are using the haste module system on Web as
|
||||||
|
; well, not only on React Native. Unfortunately, Flow does not support .web.js
|
||||||
|
; by default. Override Flow's defaults to include .web.js as well. Technically,
|
||||||
|
; we have .native.js as well so the choice of .web.js may lead to errors.
|
||||||
|
; Practically though, it is a potential future problem that we do not have at
|
||||||
|
; the time of this writing.
|
||||||
|
module.file_ext=.web.js
|
||||||
|
; Flow's defaults:
|
||||||
|
module.file_ext=.js
|
||||||
|
module.file_ext=.json
|
||||||
|
module.file_ext=.ios.js
|
||||||
|
|
||||||
|
module.system=haste
|
||||||
|
module.system.haste.use_name_reducers=true
|
||||||
|
# get basename
|
||||||
|
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
|
||||||
|
# strip .js or .js.flow suffix
|
||||||
|
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
|
||||||
|
# strip .ios suffix
|
||||||
|
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
|
||||||
|
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
|
||||||
|
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
|
||||||
|
module.system.haste.paths.blacklist=.*/__tests__/.*
|
||||||
|
module.system.haste.paths.blacklist=.*/__mocks__/.*
|
||||||
|
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
|
||||||
|
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/RNTester/.*
|
||||||
|
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/IntegrationTests/.*
|
||||||
|
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/react-native/react-native-implementation.js
|
||||||
|
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
|
||||||
|
|
||||||
|
munge_underscores=true
|
||||||
|
|
||||||
|
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
|
||||||
|
|
||||||
|
suppress_type=$FlowIssue
|
||||||
|
suppress_type=$FlowFixMe
|
||||||
|
suppress_type=$FlowFixMeProps
|
||||||
|
suppress_type=$FlowFixMeState
|
||||||
|
|
||||||
|
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)
|
||||||
|
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||||
|
|
||||||
|
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
sketchy-null-number=warn
|
||||||
|
sketchy-null-mixed=warn
|
||||||
|
sketchy-number=warn
|
||||||
|
untyped-type-import=warn
|
||||||
|
nonstrict-import=warn
|
||||||
|
deprecated-type=warn
|
||||||
|
unsafe-getters-setters=warn
|
||||||
|
inexact-spread=warn
|
||||||
|
unnecessary-invariant=warn
|
||||||
|
signature-verification-failure=warn
|
||||||
|
deprecated-utility=error
|
||||||
|
|
||||||
|
[strict]
|
||||||
|
deprecated-type
|
||||||
|
nonstrict-import
|
||||||
|
sketchy-null
|
||||||
|
unclear-type
|
||||||
|
unsafe-getters-setters
|
||||||
|
untyped-import
|
||||||
|
untyped-type-import
|
||||||
|
|
||||||
|
[version]
|
||||||
|
^0.104.0
|
||||||
48
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
This issue tracker is only for reporting bugs and tracking issues related to the source code.
|
||||||
|
|
||||||
|
Before posting, please make sure to check if the same or similar bugs have already been discussed: https://github.com/jitsi/jitsi-meet/issues
|
||||||
|
|
||||||
|
General questions regarding usage, installation, etc. should be posted at https://community.jitsi.org. They will be closed if posted here.
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Description:
|
||||||
|
|
||||||
|
<!-- Please describe the bug clearly and concisely. -->
|
||||||
|
|
||||||
|
### Steps to reproduce:
|
||||||
|
|
||||||
|
1. <!-- Open '...' -->
|
||||||
|
2. <!-- Click on '...' -->
|
||||||
|
3. <!-- and so on... -->
|
||||||
|
|
||||||
|
### Expected behavior:
|
||||||
|
|
||||||
|
<!-- Please describe what should happen. -->
|
||||||
|
|
||||||
|
### Actual behavior:
|
||||||
|
|
||||||
|
<!-- Please describe what actually happens. -->
|
||||||
|
<!-- Please attach screenshot if possible. -->
|
||||||
|
|
||||||
|
### Server information:
|
||||||
|
|
||||||
|
- Jitsi Meet version:
|
||||||
|
- Operating System:
|
||||||
|
|
||||||
|
### Client information:
|
||||||
|
|
||||||
|
- Browser / app version:
|
||||||
|
- Operating System:
|
||||||
|
|
||||||
|
### Additional information:
|
||||||
|
|
||||||
|
<!-- Please provide additional information about the bug, if any. -->
|
||||||
53
.github/ISSUE_TEMPLATE/1-bug.yml
vendored
@@ -1,53 +0,0 @@
|
|||||||
name: Bug report
|
|
||||||
description: File a bug report and help us improve
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
This issue tracker is only for reporting bugs and tracking issues related to the source code.
|
|
||||||
|
|
||||||
**Before posting, please make sure to check if the same or similar bugs have already been reported.**
|
|
||||||
|
|
||||||
⚠️ General questions regarding usage, installation, etc. should be posted in our [community forum](https://community.jitsi.org).
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: What happened?
|
|
||||||
description: Please describe the problem. Be as detailed as possible.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Platform
|
|
||||||
description: On what platforms can you reproduce the problem?
|
|
||||||
options:
|
|
||||||
- label: Chrome (or Chromium based)
|
|
||||||
- label: Firefox
|
|
||||||
- label: Safari
|
|
||||||
- label: Other desktop browser
|
|
||||||
- label: Android browser
|
|
||||||
- label: iOS browser
|
|
||||||
- label: Electron app
|
|
||||||
- label: Android mobile app
|
|
||||||
- label: iOS mobile app
|
|
||||||
- label: Custom app using a mobile SDK
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: Browser / app / sdk version
|
|
||||||
description: Please provide the version of the browser / app / sdk where the problem manifests.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Relevant log output
|
|
||||||
description: Please copy and paste any relevant log output. The browser console JS logs (if applicable) is a good start. This will be automatically formatted into code, so no need for backticks.
|
|
||||||
render: shell
|
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Reproducibility
|
|
||||||
description: Does the problem reproduce on meet.jit.si using Chrome, Firefox or the official mobile apps?
|
|
||||||
options:
|
|
||||||
- label: The problem is reproducible on meet.jit.si
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: More details?
|
|
||||||
description: Please provide more details in case they apply (such as the Jitsi Meet version you are running, if you are hosting your own server).
|
|
||||||
25
.github/ISSUE_TEMPLATE/2-feature-request.md
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
name: "Feature request"
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: 'feature-request'
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Thank you for suggesting an idea to make Jitsi Meet better.
|
||||||
|
|
||||||
|
Please fill in as much of the template below as you're able.
|
||||||
|
|
||||||
|
Note that the ultimate decision for implementing features lies on the Jitsi team, not all feature requests shall be accepted.
|
||||||
|
-->
|
||||||
|
|
||||||
|
**Is your feature request related to a problem you are facing?**
|
||||||
|
Please describe the problem you are trying to solve.
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
Please describe the desired behavior.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
Please describe alternative solutions or features you have considered.
|
||||||
|
|
||||||
22
.github/ISSUE_TEMPLATE/2-feature.yml
vendored
@@ -1,22 +0,0 @@
|
|||||||
name: Feature request
|
|
||||||
description: Suggest an idea for Jitsi Meet
|
|
||||||
labels: ["feature-request"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thank you for suggesting an idea to make Jitsi Meet better.
|
|
||||||
|
|
||||||
**Note**: the ultimate decision for implementing features lies on the Jitsi team, not all feature requests shall be accepted.
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: What problem are you trying to solve?
|
|
||||||
description: Tell us what problem your feature request would solve.
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: What solution would you like to see?
|
|
||||||
description: Please describe the desired behavior or feature.
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Is there an alternative?
|
|
||||||
description: Please describe alternative solutions or features you have considered.
|
|
||||||
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,5 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Need help with your Jitsi Meet installation?
|
- name: Need help with Jitsi Meet?
|
||||||
url: https://community.jitsi.org
|
url: https://community.jitsi.org
|
||||||
about: Please ask it in our community forum.
|
about: Please ask it in our community.
|
||||||
|
|||||||
16
.github/stale.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Number of days of inactivity before an issue becomes stale
|
||||||
|
daysUntilStale: 90
|
||||||
|
# Number of days of inactivity before a stale issue is closed
|
||||||
|
daysUntilClose: 7
|
||||||
|
# Issues with these labels will never be considered stale
|
||||||
|
exemptLabels:
|
||||||
|
- confirmed
|
||||||
|
staleLabel: wontfix
|
||||||
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||||
|
markComment: >
|
||||||
|
This issue has been automatically marked as stale because it has not had
|
||||||
|
recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
|
for your contributions.
|
||||||
|
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||||
|
closeComment: false
|
||||||
|
|
||||||
26
.github/workflows/ci-lua.yml
vendored
@@ -1,26 +0,0 @@
|
|||||||
name: Lua CI
|
|
||||||
|
|
||||||
on: [pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
luacheck:
|
|
||||||
name: Luacheck
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- 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 . \
|
|
||||||
--exclude-files=resources/prosody-plugins/mod_firewall/mod_firewall.lua | awk -F: '
|
|
||||||
{
|
|
||||||
print $0
|
|
||||||
printf "::warning file=%s,line=%s,col=%s::%s\n", $1, $2, $3, $4
|
|
||||||
}
|
|
||||||
'
|
|
||||||
179
.github/workflows/ci.yml
vendored
@@ -3,183 +3,14 @@ name: Simple CI
|
|||||||
on: [pull_request]
|
on: [pull_request]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
run-ci:
|
||||||
name: Lint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
cache: 'npm'
|
|
||||||
- name: Check Node / npm versions
|
|
||||||
run: |
|
|
||||||
node -v
|
|
||||||
npm -v
|
|
||||||
- name: Get changed files
|
|
||||||
id: changed-files
|
|
||||||
uses: jitsi/changed-files@main
|
|
||||||
- name: Get changed lang files
|
|
||||||
id: lang-files
|
|
||||||
run: echo "all=$(echo "${{ steps.changed-files.outputs.all_changed_files }}" | grep -oE 'lang\/\S+' | tr '\n' ' ')" >> "$GITHUB_OUTPUT"
|
|
||||||
- run: npm install
|
|
||||||
- name: Check git status
|
|
||||||
run: git status
|
|
||||||
- name: Normalize lang files to ensure sorted
|
|
||||||
if: steps.lang-files.outputs.all
|
|
||||||
run: npm run lang-sort
|
|
||||||
- name: Check lang files are formatted correctly
|
|
||||||
if: steps.lang-files.outputs.all
|
|
||||||
run: npm run lint:lang
|
|
||||||
- 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 && npm run tsc:ci
|
|
||||||
frontend:
|
|
||||||
name: Build Frontend
|
name: Build Frontend
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [macos-latest, ubuntu-latest]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
cache: 'npm'
|
|
||||||
- name: Check Node / npm versions
|
|
||||||
run: |
|
|
||||||
node -v
|
|
||||||
npm -v
|
|
||||||
- run: npm install
|
|
||||||
- run: make
|
|
||||||
- name: Check config.js syntax
|
|
||||||
run: node config.js
|
|
||||||
android-rn-bundle-build:
|
|
||||||
name: Build mobile bundle (Android)
|
|
||||||
runs-on: macos-15
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
cache: 'npm'
|
|
||||||
- name: Check Node / npm versions
|
|
||||||
run: |
|
|
||||||
node -v
|
|
||||||
npm -v
|
|
||||||
- run: npm install
|
|
||||||
- run: npx react-native bundle --entry-file react/index.native.js --platform android --bundle-output /tmp/android.bundle --reset-cache
|
|
||||||
ios-rn-bundle-build:
|
|
||||||
name: Build mobile bundle (iOS)
|
|
||||||
runs-on: macos-15
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
cache: 'npm'
|
|
||||||
- name: Check Node / npm versions
|
|
||||||
run: |
|
|
||||||
node -v
|
|
||||||
npm -v
|
|
||||||
- run: npm install
|
|
||||||
- name: setup Xcode
|
|
||||||
run: |
|
|
||||||
uname -a
|
|
||||||
xcode-select -p
|
|
||||||
sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
|
|
||||||
xcodebuild -version
|
|
||||||
- name: setup-cocoapods
|
|
||||||
uses: ruby/setup-ruby@v1
|
|
||||||
with:
|
|
||||||
ruby-version: '3.4'
|
|
||||||
bundler-cache: true
|
|
||||||
- run: npx react-native info
|
|
||||||
- name: Install Pods
|
|
||||||
working-directory: ./ios
|
|
||||||
run: bundle exec pod install --repo-update --deployment
|
|
||||||
- run: npx react-native bundle --entry-file react/index.native.js --platform ios --bundle-output /tmp/ios.bundle --reset-cache
|
|
||||||
android-sdk-build:
|
|
||||||
name: Build mobile SDK (Android)
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: reactnativecommunity/react-native-android:v13.0
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
cache: 'npm'
|
|
||||||
- name: Check Node / npm versions
|
|
||||||
run: |
|
|
||||||
node -v
|
|
||||||
npm -v
|
|
||||||
- run: npm install
|
|
||||||
- run: |
|
|
||||||
cd android
|
|
||||||
./gradlew :sdk:clean
|
|
||||||
./gradlew :sdk:assembleRelease
|
|
||||||
ios-sdk-build:
|
|
||||||
name: Build mobile SDK (iOS)
|
|
||||||
runs-on: macos-15
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
cache: 'npm'
|
|
||||||
- name: Check Node / npm versions
|
|
||||||
run: |
|
|
||||||
node -v
|
|
||||||
npm -v
|
|
||||||
- run: npm install
|
|
||||||
- name: setup Xcode
|
|
||||||
run: |
|
|
||||||
uname -a
|
|
||||||
xcode-select -p
|
|
||||||
sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
|
|
||||||
xcodebuild -version
|
|
||||||
- name: setup-cocoapods
|
|
||||||
uses: ruby/setup-ruby@v1
|
|
||||||
with:
|
|
||||||
ruby-version: '3.4'
|
|
||||||
bundler-cache: true
|
|
||||||
- run: npx react-native info
|
|
||||||
- name: Install Pods
|
|
||||||
working-directory: ./ios
|
|
||||||
run: bundle exec pod install --repo-update --deployment
|
|
||||||
- run: |
|
|
||||||
xcodebuild clean \
|
|
||||||
-workspace ios/jitsi-meet.xcworkspace \
|
|
||||||
-scheme JitsiMeetSDK
|
|
||||||
xcodebuild archive \
|
|
||||||
-workspace ios/jitsi-meet.xcworkspace \
|
|
||||||
-scheme JitsiMeetSDK \
|
|
||||||
-configuration Release \
|
|
||||||
-sdk iphoneos \
|
|
||||||
-destination='generic/platform=iOS' \
|
|
||||||
-archivePath ios/sdk/out/ios-device \
|
|
||||||
SKIP_INSTALL=NO \
|
|
||||||
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
|
|
||||||
xcodebuild -create-xcframework \
|
|
||||||
-framework ios/sdk/out/ios-device.xcarchive/Products/Library/Frameworks/JitsiMeetSDK.framework \
|
|
||||||
-output ios/sdk/out/JitsiMeetSDK.xcframework
|
|
||||||
- run: ls -lR ios/sdk/out
|
|
||||||
debian-build:
|
|
||||||
name: Test Debian packages build
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version: '12.x'
|
||||||
cache: 'npm'
|
|
||||||
- name: Check Node / npm versions
|
|
||||||
run: |
|
|
||||||
node -v
|
|
||||||
npm -v
|
|
||||||
- run: npm install
|
- run: npm install
|
||||||
|
- run: npm run lint
|
||||||
- run: make
|
- run: make
|
||||||
- run: sudo apt-get install -y debhelper
|
|
||||||
- run: dpkg-buildpackage -A -rfakeroot -us -uc -d
|
|
||||||
- run: make source-package
|
|
||||||
|
|||||||
21
.github/workflows/stale.yml
vendored
@@ -1,21 +0,0 @@
|
|||||||
name: 'Close stale issues and PRs'
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '30 1 * * *'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
stale:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/stale@v8
|
|
||||||
with:
|
|
||||||
stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'
|
|
||||||
stale-pr-message: 'This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'
|
|
||||||
stale-issue-label: 'stale'
|
|
||||||
stale-pr-label: 'stale'
|
|
||||||
exempt-issue-labels: 'confirmed,help-needed'
|
|
||||||
exempt-pr-labels: 'confirmed'
|
|
||||||
days-before-issue-stale: 60
|
|
||||||
days-before-pr-stale: 90
|
|
||||||
days-before-issue-close: 10
|
|
||||||
days-before-pr-close: 10
|
|
||||||
31
.gitignore
vendored
@@ -38,7 +38,6 @@ DerivedData
|
|||||||
*.dSYM.zip
|
*.dSYM.zip
|
||||||
*.xcuserstate
|
*.xcuserstate
|
||||||
project.xcworkspace
|
project.xcworkspace
|
||||||
**/.xcode.env.local
|
|
||||||
|
|
||||||
# Android/IntelliJ
|
# Android/IntelliJ
|
||||||
#
|
#
|
||||||
@@ -62,17 +61,14 @@ buck-out/
|
|||||||
|
|
||||||
# fastlane
|
# fastlane
|
||||||
#
|
#
|
||||||
.bundle/
|
*/fastlane/report.xml
|
||||||
**/fastlane/report.xml
|
*/fastlane/Preview.html
|
||||||
**/fastlane/Preview.html
|
|
||||||
**/fastlane/test_output
|
|
||||||
|
|
||||||
# Build artifacts
|
# Build artifacts
|
||||||
*.jsbundle
|
*.jsbundle
|
||||||
*.framework
|
*.framework
|
||||||
android/app/debug
|
android/app/debug
|
||||||
android/app/release
|
android/app/release
|
||||||
ios/sdk/out
|
|
||||||
|
|
||||||
# precommit-hook
|
# precommit-hook
|
||||||
.jshintignore
|
.jshintignore
|
||||||
@@ -95,26 +91,3 @@ twa/*.apk
|
|||||||
twa/*.aab
|
twa/*.aab
|
||||||
twa/assetlinks.json
|
twa/assetlinks.json
|
||||||
|
|
||||||
tsconfig.json
|
|
||||||
|
|
||||||
# React Native SDK
|
|
||||||
#
|
|
||||||
react-native-sdk/*.tgz
|
|
||||||
react-native-sdk/android/src
|
|
||||||
!react-native-sdk/android/src/main/java/org/jitsi/meet/sdk/JitsiMeetReactNativePackage.java
|
|
||||||
!react-native-sdk/android/src/main/java/org/jitsi/meet/sdk/JitsiMeetOngoingConferenceService.java
|
|
||||||
!react-native-sdk/android/src/main/java/org/jitsi/meet/sdk/JMOngoingConferenceModule.java
|
|
||||||
!react-native-sdk/android/src/main/java/org/jitsi/meet/sdk/RNOngoingNotification.java
|
|
||||||
react-native-sdk/images
|
|
||||||
react-native-sdk/ios
|
|
||||||
react-native-sdk/lang
|
|
||||||
react-native-sdk/modules
|
|
||||||
react-native-sdk/node_modules
|
|
||||||
react-native-sdk/react
|
|
||||||
react-native-sdk/service
|
|
||||||
react-native-sdk/sounds
|
|
||||||
|
|
||||||
# tests
|
|
||||||
tests/.env
|
|
||||||
test-results
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
global = false
|
|
||||||
unused = false
|
|
||||||
redefined = false
|
|
||||||
ignore = { "581" }
|
|
||||||
max_line_length = false
|
|
||||||
color = false
|
|
||||||
formatter = "plain"
|
|
||||||
quiet = 1
|
|
||||||
2
.npmrc
@@ -1,3 +1 @@
|
|||||||
package-lock=true
|
package-lock=true
|
||||||
; FIXME Set legacy-peer-deps=false when we upgrade RN.
|
|
||||||
legacy-peer-deps=true
|
|
||||||
|
|||||||
6
.travis.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
osx_image: xcode11.1
|
||||||
|
language: objective-c
|
||||||
|
script:
|
||||||
|
- "./ios/travis-ci/build-ipa.sh"
|
||||||
|
after_script:
|
||||||
|
- sleep 10
|
||||||
156
CONTRIBUTING.md
@@ -1,20 +1,154 @@
|
|||||||
# Follow Our Updated Guide to See How You Can Contribute
|
# How to contribute
|
||||||
|
We would love to have your help. Before you start working however, please read
|
||||||
|
and follow this short guide.
|
||||||
|
|
||||||
**Hello there! 👋**
|
# 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
|
||||||
|
problem can be reproduced.
|
||||||
|
|
||||||
We're thrilled that you're eager to contribute to **Jitsi Meet! ❤️**
|
# Code contributions
|
||||||
|
Found a bug and know how to fix it? Great! Please read on.
|
||||||
|
|
||||||
Your interest in improving our platform means a lot to us. To ensure your contributions align seamlessly with our goals and processes, we've recently updated our guide. This guide will provide you with clear instructions on how to get involved effectively.
|
## Contributor License Agreement
|
||||||
|
While the Jitsi projects are released under the
|
||||||
|
[Apache License 2.0](https://github.com/jitsi/jitsi-meet/blob/master/LICENSE), the copyright
|
||||||
|
holder and principal creator is [8x8](https://www.8x8.com/). To
|
||||||
|
ensure that we can continue making these projects available under an Open Source license,
|
||||||
|
we need you to sign our Apache-based contributor
|
||||||
|
license agreement as either a [corporation](https://jitsi.org/ccla) or an
|
||||||
|
[individual](https://jitsi.org/icla). If you cannot accept the terms laid out
|
||||||
|
in the agreement, unfortunately, we cannot accept your contribution.
|
||||||
|
|
||||||
### 📖 Get Started
|
## Creating Pull Requests
|
||||||
|
- Make sure your code passes the linter rules beforehand. The linter is executed
|
||||||
|
automatically when committing code.
|
||||||
|
- Perform **one** logical change per pull request.
|
||||||
|
- Maintain a clean list of commits, squash them if necessary.
|
||||||
|
- Rebase your topic branch on top of the master branch before creating the pull
|
||||||
|
request.
|
||||||
|
|
||||||
Ready to get started? Head over to our [Jitsi Meet Handbook](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-contributing/) and let's make **Jitsi Meet** even better together!
|
## Coding style
|
||||||
|
|
||||||
### 💬 Join the Discussion
|
### Comments
|
||||||
|
|
||||||
Have questions or need help? Join our community discussions on the [Jitsi Forum](https://community.jitsi.org/) where contributors and maintainers can assist you.
|
* Comments documenting the source code are required.
|
||||||
|
|
||||||
### ❗️Additional Note
|
* Comments from which documentation is automatically generated are **not**
|
||||||
Before sending us your code, double-check that it meets our coding standards. You can do this by running a command: `npm run lint`. If there are any issues, don't worry! You can fix them by running: `npm run lint-fix`. Once your code passes these checks, feel free to submit your pull request.
|
subject to case-by-case decisions. Such comments are used, for example, on
|
||||||
|
types and their members. Examples of tools which automatically generate
|
||||||
|
documentation from such comments include JSDoc, Javadoc, Doxygen.
|
||||||
|
|
||||||
**Happy coding!**
|
* Comments which are not automatically processed are strongly encouraged. They
|
||||||
|
are subject to case-by-case decisions. Such comments are often observed in
|
||||||
|
function bodies.
|
||||||
|
|
||||||
|
* Comments should be formatted as proper English sentences. Such formatting pays
|
||||||
|
attention to, for example, capitalization and punctuation.
|
||||||
|
|
||||||
|
### Duplication
|
||||||
|
|
||||||
|
* Don't copy-paste source code. Reuse it.
|
||||||
|
|
||||||
|
### Formatting
|
||||||
|
|
||||||
|
* Line length is limited to 120 characters.
|
||||||
|
|
||||||
|
* Sort by alphabetical order in order to make the addition of new entities as
|
||||||
|
easy as looking a word up in a dictionary. Otherwise, one risks duplicate
|
||||||
|
entries (with conflicting values in the cases of key-value pairs). For
|
||||||
|
example:
|
||||||
|
|
||||||
|
* Within an `import` of multiple names from a module, sort the names in
|
||||||
|
alphabetical order. (Of course, the default name stays first as required by
|
||||||
|
the `import` syntax.)
|
||||||
|
|
||||||
|
````javascript
|
||||||
|
import {
|
||||||
|
DOMINANT_SPEAKER_CHANGED,
|
||||||
|
JITSI_CLIENT_CONNECTED,
|
||||||
|
JITSI_CLIENT_CREATED,
|
||||||
|
JITSI_CLIENT_DISCONNECTED,
|
||||||
|
JITSI_CLIENT_ERROR,
|
||||||
|
JITSI_CONFERENCE_JOINED,
|
||||||
|
MODERATOR_CHANGED,
|
||||||
|
PEER_JOINED,
|
||||||
|
PEER_LEFT,
|
||||||
|
RTC_ERROR
|
||||||
|
} from './actionTypes';
|
||||||
|
````
|
||||||
|
|
||||||
|
* Within a group of imports (e.g. groups of imports delimited by an empty line
|
||||||
|
may be: third-party modules, then project modules, and eventually the
|
||||||
|
private files of a module), sort the module names in alphabetical order.
|
||||||
|
|
||||||
|
````javascript
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
````
|
||||||
|
|
||||||
|
### Indentation
|
||||||
|
|
||||||
|
* Align `switch` and `case`/`default`. Don't indent the `case`/`default` more
|
||||||
|
than its `switch`.
|
||||||
|
|
||||||
|
````javascript
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
...
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
...
|
||||||
|
}
|
||||||
|
````
|
||||||
|
|
||||||
|
### Naming
|
||||||
|
|
||||||
|
* An abstraction should have one name within the project and across multiple
|
||||||
|
projects. For example:
|
||||||
|
|
||||||
|
* The instance of lib-jitsi-meet's `JitsiConnection` type should be named
|
||||||
|
`connection` or `jitsiConnection` in jitsi-meet, not `client`.
|
||||||
|
|
||||||
|
* The class `ReducerRegistry` should be defined in ReducerRegistry.js and its
|
||||||
|
imports in other files should use the same name. Don't define the class
|
||||||
|
`Registry` in ReducerRegistry.js and then import it as `Reducers` in other
|
||||||
|
files.
|
||||||
|
|
||||||
|
* The names of global constants (including ES6 module-global constants) should
|
||||||
|
be written in uppercase with underscores to separate words. For example,
|
||||||
|
`BACKGROUND_COLOR`.
|
||||||
|
|
||||||
|
* The underscore character at the beginning of a name signals that the
|
||||||
|
respective variable, function, property is non-public i.e. private, protected,
|
||||||
|
or internal. In contrast, the lack of an underscore at the beginning of a name
|
||||||
|
signals public API.
|
||||||
|
|
||||||
|
### Feature layout
|
||||||
|
|
||||||
|
When adding a new feature, this would be the usual layout.
|
||||||
|
|
||||||
|
```
|
||||||
|
react/features/sample/
|
||||||
|
├── actionTypes.js
|
||||||
|
├── actions.js
|
||||||
|
├── components
|
||||||
|
│ ├── AnotherComponent.js
|
||||||
|
│ ├── OneComponent.js
|
||||||
|
│ └── index.js
|
||||||
|
├── middleware.js
|
||||||
|
└── reducer.js
|
||||||
|
```
|
||||||
|
|
||||||
|
The middleware must be imported in `react/features/app/` specifically
|
||||||
|
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
|
||||||
|
component. Features / files requiring those must import them explicitly.
|
||||||
|
|
||||||
|
This has not always been the case and the entire codebase hasn't been migrated to
|
||||||
|
this model but new features should follow this new layout.
|
||||||
|
|
||||||
|
When working on an old feature, adding the necessary changes to migrate to the new
|
||||||
|
model is encouraged.
|
||||||
|
|||||||
16
Gemfile
@@ -1,16 +0,0 @@
|
|||||||
source "https://rubygems.org"
|
|
||||||
|
|
||||||
ruby ">= 3.4.0"
|
|
||||||
|
|
||||||
gem "cocoapods", "~> 1.16"
|
|
||||||
|
|
||||||
# (Optional) Fastlane for automation
|
|
||||||
gem "fastlane"
|
|
||||||
gem "abbrev"
|
|
||||||
gem "logger"
|
|
||||||
gem "mutex_m"
|
|
||||||
gem "csv"
|
|
||||||
gem "bigdecimal"
|
|
||||||
|
|
||||||
# (Optional) Bundler itself to ensure consistency
|
|
||||||
gem "bundler"
|
|
||||||
331
Gemfile.lock
@@ -1,331 +0,0 @@
|
|||||||
GEM
|
|
||||||
remote: https://rubygems.org/
|
|
||||||
specs:
|
|
||||||
CFPropertyList (3.0.7)
|
|
||||||
base64
|
|
||||||
nkf
|
|
||||||
rexml
|
|
||||||
abbrev (0.1.2)
|
|
||||||
activesupport (7.2.2.1)
|
|
||||||
base64
|
|
||||||
benchmark (>= 0.3)
|
|
||||||
bigdecimal
|
|
||||||
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
||||||
connection_pool (>= 2.2.5)
|
|
||||||
drb
|
|
||||||
i18n (>= 1.6, < 2)
|
|
||||||
logger (>= 1.4.2)
|
|
||||||
minitest (>= 5.1)
|
|
||||||
securerandom (>= 0.3)
|
|
||||||
tzinfo (~> 2.0, >= 2.0.5)
|
|
||||||
addressable (2.8.7)
|
|
||||||
public_suffix (>= 2.0.2, < 7.0)
|
|
||||||
algoliasearch (1.27.5)
|
|
||||||
httpclient (~> 2.8, >= 2.8.3)
|
|
||||||
json (>= 1.5.1)
|
|
||||||
artifactory (3.0.17)
|
|
||||||
atomos (0.1.3)
|
|
||||||
aws-eventstream (1.3.1)
|
|
||||||
aws-partitions (1.1050.0)
|
|
||||||
aws-sdk-core (3.218.1)
|
|
||||||
aws-eventstream (~> 1, >= 1.3.0)
|
|
||||||
aws-partitions (~> 1, >= 1.992.0)
|
|
||||||
aws-sigv4 (~> 1.9)
|
|
||||||
base64
|
|
||||||
jmespath (~> 1, >= 1.6.1)
|
|
||||||
aws-sdk-kms (1.98.0)
|
|
||||||
aws-sdk-core (~> 3, >= 3.216.0)
|
|
||||||
aws-sigv4 (~> 1.5)
|
|
||||||
aws-sdk-s3 (1.181.0)
|
|
||||||
aws-sdk-core (~> 3, >= 3.216.0)
|
|
||||||
aws-sdk-kms (~> 1)
|
|
||||||
aws-sigv4 (~> 1.5)
|
|
||||||
aws-sigv4 (1.11.0)
|
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
|
||||||
babosa (1.0.4)
|
|
||||||
base64 (0.2.0)
|
|
||||||
benchmark (0.4.0)
|
|
||||||
bigdecimal (3.1.9)
|
|
||||||
claide (1.1.0)
|
|
||||||
cocoapods (1.16.2)
|
|
||||||
addressable (~> 2.8)
|
|
||||||
claide (>= 1.0.2, < 2.0)
|
|
||||||
cocoapods-core (= 1.16.2)
|
|
||||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
|
||||||
cocoapods-downloader (>= 2.1, < 3.0)
|
|
||||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
|
||||||
cocoapods-search (>= 1.0.0, < 2.0)
|
|
||||||
cocoapods-trunk (>= 1.6.0, < 2.0)
|
|
||||||
cocoapods-try (>= 1.1.0, < 2.0)
|
|
||||||
colored2 (~> 3.1)
|
|
||||||
escape (~> 0.0.4)
|
|
||||||
fourflusher (>= 2.3.0, < 3.0)
|
|
||||||
gh_inspector (~> 1.0)
|
|
||||||
molinillo (~> 0.8.0)
|
|
||||||
nap (~> 1.0)
|
|
||||||
ruby-macho (>= 2.3.0, < 3.0)
|
|
||||||
xcodeproj (>= 1.27.0, < 2.0)
|
|
||||||
cocoapods-core (1.16.2)
|
|
||||||
activesupport (>= 5.0, < 8)
|
|
||||||
addressable (~> 2.8)
|
|
||||||
algoliasearch (~> 1.0)
|
|
||||||
concurrent-ruby (~> 1.1)
|
|
||||||
fuzzy_match (~> 2.0.4)
|
|
||||||
nap (~> 1.0)
|
|
||||||
netrc (~> 0.11)
|
|
||||||
public_suffix (~> 4.0)
|
|
||||||
typhoeus (~> 1.0)
|
|
||||||
cocoapods-deintegrate (1.0.5)
|
|
||||||
cocoapods-downloader (2.1)
|
|
||||||
cocoapods-plugins (1.0.0)
|
|
||||||
nap
|
|
||||||
cocoapods-search (1.0.1)
|
|
||||||
cocoapods-trunk (1.6.0)
|
|
||||||
nap (>= 0.8, < 2.0)
|
|
||||||
netrc (~> 0.11)
|
|
||||||
cocoapods-try (1.2.0)
|
|
||||||
colored (1.2)
|
|
||||||
colored2 (3.1.2)
|
|
||||||
commander (4.6.0)
|
|
||||||
highline (~> 2.0.0)
|
|
||||||
concurrent-ruby (1.3.5)
|
|
||||||
connection_pool (2.5.0)
|
|
||||||
csv (3.3.2)
|
|
||||||
declarative (0.0.20)
|
|
||||||
digest-crc (0.7.0)
|
|
||||||
rake (>= 12.0.0, < 14.0.0)
|
|
||||||
domain_name (0.6.20240107)
|
|
||||||
dotenv (2.8.1)
|
|
||||||
drb (2.2.1)
|
|
||||||
emoji_regex (3.2.3)
|
|
||||||
escape (0.0.4)
|
|
||||||
ethon (0.16.0)
|
|
||||||
ffi (>= 1.15.0)
|
|
||||||
excon (0.112.0)
|
|
||||||
faraday (1.10.4)
|
|
||||||
faraday-em_http (~> 1.0)
|
|
||||||
faraday-em_synchrony (~> 1.0)
|
|
||||||
faraday-excon (~> 1.1)
|
|
||||||
faraday-httpclient (~> 1.0)
|
|
||||||
faraday-multipart (~> 1.0)
|
|
||||||
faraday-net_http (~> 1.0)
|
|
||||||
faraday-net_http_persistent (~> 1.0)
|
|
||||||
faraday-patron (~> 1.0)
|
|
||||||
faraday-rack (~> 1.0)
|
|
||||||
faraday-retry (~> 1.0)
|
|
||||||
ruby2_keywords (>= 0.0.4)
|
|
||||||
faraday-cookie_jar (0.0.7)
|
|
||||||
faraday (>= 0.8.0)
|
|
||||||
http-cookie (~> 1.0.0)
|
|
||||||
faraday-em_http (1.0.0)
|
|
||||||
faraday-em_synchrony (1.0.0)
|
|
||||||
faraday-excon (1.1.0)
|
|
||||||
faraday-httpclient (1.0.1)
|
|
||||||
faraday-multipart (1.1.0)
|
|
||||||
multipart-post (~> 2.0)
|
|
||||||
faraday-net_http (1.0.2)
|
|
||||||
faraday-net_http_persistent (1.2.0)
|
|
||||||
faraday-patron (1.0.0)
|
|
||||||
faraday-rack (1.0.0)
|
|
||||||
faraday-retry (1.0.3)
|
|
||||||
faraday_middleware (1.2.1)
|
|
||||||
faraday (~> 1.0)
|
|
||||||
fastimage (2.4.0)
|
|
||||||
fastlane (2.226.0)
|
|
||||||
CFPropertyList (>= 2.3, < 4.0.0)
|
|
||||||
addressable (>= 2.8, < 3.0.0)
|
|
||||||
artifactory (~> 3.0)
|
|
||||||
aws-sdk-s3 (~> 1.0)
|
|
||||||
babosa (>= 1.0.3, < 2.0.0)
|
|
||||||
bundler (>= 1.12.0, < 3.0.0)
|
|
||||||
colored (~> 1.2)
|
|
||||||
commander (~> 4.6)
|
|
||||||
dotenv (>= 2.1.1, < 3.0.0)
|
|
||||||
emoji_regex (>= 0.1, < 4.0)
|
|
||||||
excon (>= 0.71.0, < 1.0.0)
|
|
||||||
faraday (~> 1.0)
|
|
||||||
faraday-cookie_jar (~> 0.0.6)
|
|
||||||
faraday_middleware (~> 1.0)
|
|
||||||
fastimage (>= 2.1.0, < 3.0.0)
|
|
||||||
fastlane-sirp (>= 1.0.0)
|
|
||||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
|
||||||
google-apis-androidpublisher_v3 (~> 0.3)
|
|
||||||
google-apis-playcustomapp_v1 (~> 0.1)
|
|
||||||
google-cloud-env (>= 1.6.0, < 2.0.0)
|
|
||||||
google-cloud-storage (~> 1.31)
|
|
||||||
highline (~> 2.0)
|
|
||||||
http-cookie (~> 1.0.5)
|
|
||||||
json (< 3.0.0)
|
|
||||||
jwt (>= 2.1.0, < 3)
|
|
||||||
mini_magick (>= 4.9.4, < 5.0.0)
|
|
||||||
multipart-post (>= 2.0.0, < 3.0.0)
|
|
||||||
naturally (~> 2.2)
|
|
||||||
optparse (>= 0.1.1, < 1.0.0)
|
|
||||||
plist (>= 3.1.0, < 4.0.0)
|
|
||||||
rubyzip (>= 2.0.0, < 3.0.0)
|
|
||||||
security (= 0.1.5)
|
|
||||||
simctl (~> 1.6.3)
|
|
||||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
|
||||||
terminal-table (~> 3)
|
|
||||||
tty-screen (>= 0.6.3, < 1.0.0)
|
|
||||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
|
||||||
word_wrap (~> 1.0.0)
|
|
||||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
|
||||||
xcpretty (~> 0.4.0)
|
|
||||||
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
|
|
||||||
fastlane-sirp (1.0.0)
|
|
||||||
sysrandom (~> 1.0)
|
|
||||||
ffi (1.17.1)
|
|
||||||
ffi (1.17.1-aarch64-linux-gnu)
|
|
||||||
ffi (1.17.1-aarch64-linux-musl)
|
|
||||||
ffi (1.17.1-arm-linux-gnu)
|
|
||||||
ffi (1.17.1-arm-linux-musl)
|
|
||||||
ffi (1.17.1-arm64-darwin)
|
|
||||||
ffi (1.17.1-x86-linux-gnu)
|
|
||||||
ffi (1.17.1-x86-linux-musl)
|
|
||||||
ffi (1.17.1-x86_64-darwin)
|
|
||||||
ffi (1.17.1-x86_64-linux-gnu)
|
|
||||||
ffi (1.17.1-x86_64-linux-musl)
|
|
||||||
fourflusher (2.3.1)
|
|
||||||
fuzzy_match (2.0.4)
|
|
||||||
gh_inspector (1.1.3)
|
|
||||||
google-apis-androidpublisher_v3 (0.54.0)
|
|
||||||
google-apis-core (>= 0.11.0, < 2.a)
|
|
||||||
google-apis-core (0.11.3)
|
|
||||||
addressable (~> 2.5, >= 2.5.1)
|
|
||||||
googleauth (>= 0.16.2, < 2.a)
|
|
||||||
httpclient (>= 2.8.1, < 3.a)
|
|
||||||
mini_mime (~> 1.0)
|
|
||||||
representable (~> 3.0)
|
|
||||||
retriable (>= 2.0, < 4.a)
|
|
||||||
rexml
|
|
||||||
google-apis-iamcredentials_v1 (0.17.0)
|
|
||||||
google-apis-core (>= 0.11.0, < 2.a)
|
|
||||||
google-apis-playcustomapp_v1 (0.13.0)
|
|
||||||
google-apis-core (>= 0.11.0, < 2.a)
|
|
||||||
google-apis-storage_v1 (0.31.0)
|
|
||||||
google-apis-core (>= 0.11.0, < 2.a)
|
|
||||||
google-cloud-core (1.7.1)
|
|
||||||
google-cloud-env (>= 1.0, < 3.a)
|
|
||||||
google-cloud-errors (~> 1.0)
|
|
||||||
google-cloud-env (1.6.0)
|
|
||||||
faraday (>= 0.17.3, < 3.0)
|
|
||||||
google-cloud-errors (1.4.0)
|
|
||||||
google-cloud-storage (1.47.0)
|
|
||||||
addressable (~> 2.8)
|
|
||||||
digest-crc (~> 0.4)
|
|
||||||
google-apis-iamcredentials_v1 (~> 0.1)
|
|
||||||
google-apis-storage_v1 (~> 0.31.0)
|
|
||||||
google-cloud-core (~> 1.6)
|
|
||||||
googleauth (>= 0.16.2, < 2.a)
|
|
||||||
mini_mime (~> 1.0)
|
|
||||||
googleauth (1.8.1)
|
|
||||||
faraday (>= 0.17.3, < 3.a)
|
|
||||||
jwt (>= 1.4, < 3.0)
|
|
||||||
multi_json (~> 1.11)
|
|
||||||
os (>= 0.9, < 2.0)
|
|
||||||
signet (>= 0.16, < 2.a)
|
|
||||||
highline (2.0.3)
|
|
||||||
http-cookie (1.0.8)
|
|
||||||
domain_name (~> 0.5)
|
|
||||||
httpclient (2.8.3)
|
|
||||||
i18n (1.14.7)
|
|
||||||
concurrent-ruby (~> 1.0)
|
|
||||||
jmespath (1.6.2)
|
|
||||||
json (2.10.1)
|
|
||||||
jwt (2.10.1)
|
|
||||||
base64
|
|
||||||
logger (1.6.6)
|
|
||||||
mini_magick (4.13.2)
|
|
||||||
mini_mime (1.1.5)
|
|
||||||
minitest (5.25.4)
|
|
||||||
molinillo (0.8.0)
|
|
||||||
multi_json (1.15.0)
|
|
||||||
multipart-post (2.4.1)
|
|
||||||
mutex_m (0.3.0)
|
|
||||||
nanaimo (0.4.0)
|
|
||||||
nap (1.1.0)
|
|
||||||
naturally (2.2.1)
|
|
||||||
netrc (0.11.0)
|
|
||||||
nkf (0.2.0)
|
|
||||||
optparse (0.6.0)
|
|
||||||
os (1.1.4)
|
|
||||||
plist (3.7.2)
|
|
||||||
public_suffix (4.0.7)
|
|
||||||
rake (13.2.1)
|
|
||||||
representable (3.2.0)
|
|
||||||
declarative (< 0.1.0)
|
|
||||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
|
||||||
uber (< 0.2.0)
|
|
||||||
retriable (3.1.2)
|
|
||||||
rexml (3.4.1)
|
|
||||||
rouge (3.28.0)
|
|
||||||
ruby-macho (2.5.1)
|
|
||||||
ruby2_keywords (0.0.5)
|
|
||||||
rubyzip (2.4.1)
|
|
||||||
securerandom (0.4.1)
|
|
||||||
security (0.1.5)
|
|
||||||
signet (0.19.0)
|
|
||||||
addressable (~> 2.8)
|
|
||||||
faraday (>= 0.17.5, < 3.a)
|
|
||||||
jwt (>= 1.5, < 3.0)
|
|
||||||
multi_json (~> 1.10)
|
|
||||||
simctl (1.6.10)
|
|
||||||
CFPropertyList
|
|
||||||
naturally
|
|
||||||
sysrandom (1.0.5)
|
|
||||||
terminal-notifier (2.0.0)
|
|
||||||
terminal-table (3.0.2)
|
|
||||||
unicode-display_width (>= 1.1.1, < 3)
|
|
||||||
trailblazer-option (0.1.2)
|
|
||||||
tty-cursor (0.7.1)
|
|
||||||
tty-screen (0.8.2)
|
|
||||||
tty-spinner (0.9.3)
|
|
||||||
tty-cursor (~> 0.7)
|
|
||||||
typhoeus (1.4.1)
|
|
||||||
ethon (>= 0.9.0)
|
|
||||||
tzinfo (2.0.6)
|
|
||||||
concurrent-ruby (~> 1.0)
|
|
||||||
uber (0.1.0)
|
|
||||||
unicode-display_width (2.6.0)
|
|
||||||
word_wrap (1.0.0)
|
|
||||||
xcodeproj (1.27.0)
|
|
||||||
CFPropertyList (>= 2.3.3, < 4.0)
|
|
||||||
atomos (~> 0.1.3)
|
|
||||||
claide (>= 1.0.2, < 2.0)
|
|
||||||
colored2 (~> 3.1)
|
|
||||||
nanaimo (~> 0.4.0)
|
|
||||||
rexml (>= 3.3.6, < 4.0)
|
|
||||||
xcpretty (0.4.0)
|
|
||||||
rouge (~> 3.28.0)
|
|
||||||
xcpretty-travis-formatter (1.0.1)
|
|
||||||
xcpretty (~> 0.2, >= 0.0.7)
|
|
||||||
|
|
||||||
PLATFORMS
|
|
||||||
aarch64-linux-gnu
|
|
||||||
aarch64-linux-musl
|
|
||||||
arm-linux-gnu
|
|
||||||
arm-linux-musl
|
|
||||||
arm64-darwin
|
|
||||||
ruby
|
|
||||||
x86-linux-gnu
|
|
||||||
x86-linux-musl
|
|
||||||
x86_64-darwin
|
|
||||||
x86_64-linux-gnu
|
|
||||||
x86_64-linux-musl
|
|
||||||
|
|
||||||
DEPENDENCIES
|
|
||||||
abbrev
|
|
||||||
bigdecimal
|
|
||||||
bundler
|
|
||||||
cocoapods (~> 1.16)
|
|
||||||
csv
|
|
||||||
fastlane
|
|
||||||
logger
|
|
||||||
mutex_m
|
|
||||||
|
|
||||||
RUBY VERSION
|
|
||||||
ruby 3.4.2p28
|
|
||||||
|
|
||||||
BUNDLED WITH
|
|
||||||
2.6.3
|
|
||||||
119
Makefile
@@ -1,40 +1,29 @@
|
|||||||
BUILD_DIR = build
|
BUILD_DIR = build
|
||||||
CLEANCSS = ./node_modules/.bin/cleancss
|
CLEANCSS = ./node_modules/.bin/cleancss
|
||||||
DEPLOY_DIR = libs
|
DEPLOY_DIR = libs
|
||||||
LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet
|
LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet/
|
||||||
OLM_DIR = node_modules/@matrix-org/olm
|
LIBFLAC_DIR = node_modules/libflacjs/dist/min/
|
||||||
TF_WASM_DIR = node_modules/@tensorflow/tfjs-backend-wasm/dist/
|
OLM_DIR = node_modules/olm
|
||||||
RNNOISE_WASM_DIR = node_modules/@jitsi/rnnoise-wasm/dist
|
RNNOISE_WASM_DIR = node_modules/rnnoise-wasm/dist/
|
||||||
EXCALIDRAW_DIR = node_modules/@jitsi/excalidraw/dist/excalidraw-assets
|
|
||||||
EXCALIDRAW_DIR_DEV = node_modules/@jitsi/excalidraw/dist/excalidraw-assets-dev
|
|
||||||
TFLITE_WASM = react/features/stream-effects/virtual-background/vendor/tflite
|
|
||||||
MEET_MODELS_DIR = react/features/stream-effects/virtual-background/vendor/models
|
|
||||||
FACE_MODELS_DIR = node_modules/@vladmandic/human-models/models
|
|
||||||
NODE_SASS = ./node_modules/.bin/sass
|
NODE_SASS = ./node_modules/.bin/sass
|
||||||
NPM = npm
|
NPM = npm
|
||||||
OUTPUT_DIR = .
|
OUTPUT_DIR = .
|
||||||
STYLES_BUNDLE = css/all.bundle.css
|
STYLES_BUNDLE = css/all.bundle.css
|
||||||
STYLES_DESTINATION = css/all.css
|
STYLES_DESTINATION = css/all.css
|
||||||
STYLES_MAIN = css/main.scss
|
STYLES_MAIN = css/main.scss
|
||||||
ifeq ($(OS),Windows_NT)
|
WEBPACK = ./node_modules/.bin/webpack
|
||||||
WEBPACK = .\node_modules\.bin\webpack --progress
|
WEBPACK_DEV_SERVER = ./node_modules/.bin/webpack-dev-server
|
||||||
WEBPACK_DEV_SERVER = .\node_modules\.bin\webpack serve --mode development --progress
|
|
||||||
else
|
|
||||||
WEBPACK = ./node_modules/.bin/webpack --progress
|
|
||||||
WEBPACK_DEV_SERVER = ./node_modules/.bin/webpack serve --mode development --progress
|
|
||||||
endif
|
|
||||||
|
|
||||||
all: compile deploy
|
all: compile deploy clean
|
||||||
|
|
||||||
compile: clean
|
compile:
|
||||||
NODE_OPTIONS=--max-old-space-size=8192 \
|
$(WEBPACK) -p
|
||||||
$(WEBPACK)
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -fr $(BUILD_DIR)
|
rm -fr $(BUILD_DIR)
|
||||||
|
|
||||||
.NOTPARALLEL:
|
.NOTPARALLEL:
|
||||||
deploy: deploy-init deploy-appbundle deploy-rnnoise-binary deploy-excalidraw deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-olm deploy-tf-wasm deploy-css deploy-local deploy-face-landmarks
|
deploy: deploy-init deploy-appbundle deploy-rnnoise-binary deploy-lib-jitsi-meet deploy-libflac deploy-olm deploy-css deploy-local
|
||||||
|
|
||||||
deploy-init:
|
deploy-init:
|
||||||
rm -fr $(DEPLOY_DIR)
|
rm -fr $(DEPLOY_DIR)
|
||||||
@@ -43,26 +32,43 @@ deploy-init:
|
|||||||
deploy-appbundle:
|
deploy-appbundle:
|
||||||
cp \
|
cp \
|
||||||
$(BUILD_DIR)/app.bundle.min.js \
|
$(BUILD_DIR)/app.bundle.min.js \
|
||||||
$(BUILD_DIR)/app.bundle.min.js.map \
|
$(BUILD_DIR)/app.bundle.min.map \
|
||||||
|
$(BUILD_DIR)/do_external_connect.min.js \
|
||||||
|
$(BUILD_DIR)/do_external_connect.min.map \
|
||||||
$(BUILD_DIR)/external_api.min.js \
|
$(BUILD_DIR)/external_api.min.js \
|
||||||
$(BUILD_DIR)/external_api.min.js.map \
|
$(BUILD_DIR)/external_api.min.map \
|
||||||
|
$(BUILD_DIR)/flacEncodeWorker.min.js \
|
||||||
|
$(BUILD_DIR)/flacEncodeWorker.min.map \
|
||||||
|
$(BUILD_DIR)/device_selection_popup_bundle.min.js \
|
||||||
|
$(BUILD_DIR)/device_selection_popup_bundle.min.map \
|
||||||
|
$(BUILD_DIR)/dial_in_info_bundle.min.js \
|
||||||
|
$(BUILD_DIR)/dial_in_info_bundle.min.map \
|
||||||
$(BUILD_DIR)/alwaysontop.min.js \
|
$(BUILD_DIR)/alwaysontop.min.js \
|
||||||
$(BUILD_DIR)/alwaysontop.min.js.map \
|
$(BUILD_DIR)/alwaysontop.min.map \
|
||||||
$(BUILD_DIR)/face-landmarks-worker.min.js \
|
$(OUTPUT_DIR)/analytics-ga.js \
|
||||||
$(BUILD_DIR)/face-landmarks-worker.min.js.map \
|
$(BUILD_DIR)/analytics-ga.min.js \
|
||||||
$(BUILD_DIR)/noise-suppressor-worklet.min.js \
|
$(BUILD_DIR)/analytics-ga.min.map \
|
||||||
$(BUILD_DIR)/noise-suppressor-worklet.min.js.map \
|
$(BUILD_DIR)/video-blur-effect.min.js \
|
||||||
$(BUILD_DIR)/screenshot-capture-worker.min.js \
|
$(BUILD_DIR)/video-blur-effect.min.map \
|
||||||
$(BUILD_DIR)/screenshot-capture-worker.min.js.map \
|
$(BUILD_DIR)/rnnoise-processor.min.js \
|
||||||
$(DEPLOY_DIR)
|
$(BUILD_DIR)/rnnoise-processor.min.map \
|
||||||
cp \
|
|
||||||
$(BUILD_DIR)/close3.min.js \
|
$(BUILD_DIR)/close3.min.js \
|
||||||
$(BUILD_DIR)/close3.min.js.map \
|
$(BUILD_DIR)/close3.min.map \
|
||||||
$(DEPLOY_DIR) || true
|
$(DEPLOY_DIR)
|
||||||
|
|
||||||
deploy-lib-jitsi-meet:
|
deploy-lib-jitsi-meet:
|
||||||
cp \
|
cp \
|
||||||
$(LIBJITSIMEET_DIR)/dist/umd/lib-jitsi-meet.* \
|
$(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_DIR)
|
||||||
|
|
||||||
deploy-olm:
|
deploy-olm:
|
||||||
@@ -70,59 +76,26 @@ deploy-olm:
|
|||||||
$(OLM_DIR)/olm.wasm \
|
$(OLM_DIR)/olm.wasm \
|
||||||
$(DEPLOY_DIR)
|
$(DEPLOY_DIR)
|
||||||
|
|
||||||
deploy-tf-wasm:
|
|
||||||
cp \
|
|
||||||
$(TF_WASM_DIR)/*.wasm \
|
|
||||||
$(DEPLOY_DIR)
|
|
||||||
|
|
||||||
deploy-rnnoise-binary:
|
deploy-rnnoise-binary:
|
||||||
cp \
|
cp \
|
||||||
$(RNNOISE_WASM_DIR)/rnnoise.wasm \
|
$(RNNOISE_WASM_DIR)/rnnoise.wasm \
|
||||||
$(DEPLOY_DIR)
|
$(DEPLOY_DIR)
|
||||||
|
|
||||||
deploy-tflite:
|
|
||||||
cp \
|
|
||||||
$(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:
|
|
||||||
cp \
|
|
||||||
$(FACE_MODELS_DIR)/blazeface-front.bin \
|
|
||||||
$(FACE_MODELS_DIR)/blazeface-front.json \
|
|
||||||
$(FACE_MODELS_DIR)/emotion.bin \
|
|
||||||
$(FACE_MODELS_DIR)/emotion.json \
|
|
||||||
$(DEPLOY_DIR)
|
|
||||||
|
|
||||||
deploy-css:
|
deploy-css:
|
||||||
$(NODE_SASS) $(STYLES_MAIN) $(STYLES_BUNDLE) && \
|
$(NODE_SASS) $(STYLES_MAIN) $(STYLES_BUNDLE) && \
|
||||||
$(CLEANCSS) --skip-rebase $(STYLES_BUNDLE) > $(STYLES_DESTINATION) && \
|
$(CLEANCSS) --skip-rebase $(STYLES_BUNDLE) > $(STYLES_DESTINATION) ; \
|
||||||
rm $(STYLES_BUNDLE)
|
rm $(STYLES_BUNDLE)
|
||||||
|
|
||||||
deploy-local:
|
deploy-local:
|
||||||
([ ! -x deploy-local.sh ] || ./deploy-local.sh)
|
([ ! -x deploy-local.sh ] || ./deploy-local.sh)
|
||||||
|
|
||||||
.NOTPARALLEL:
|
.NOTPARALLEL:
|
||||||
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-olm deploy-tf-wasm deploy-excalidraw-dev deploy-face-landmarks
|
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-lib-jitsi-meet deploy-libflac deploy-olm
|
||||||
$(WEBPACK_DEV_SERVER)
|
$(WEBPACK_DEV_SERVER) --detect-circular-deps
|
||||||
|
|
||||||
source-package: compile deploy
|
source-package:
|
||||||
mkdir -p source_package/jitsi-meet/css && \
|
mkdir -p source_package/jitsi-meet/css && \
|
||||||
cp -r *.js *.html resources/*.txt fonts images libs static sounds LICENSE lang source_package/jitsi-meet && \
|
cp -r *.js *.html resources/*.txt connection_optimization favicon.ico fonts images libs static sounds LICENSE lang source_package/jitsi-meet && \
|
||||||
cp css/all.css source_package/jitsi-meet/css && \
|
cp css/all.css source_package/jitsi-meet/css && \
|
||||||
(cd source_package ; tar cjf ../jitsi-meet.tar.bz2 jitsi-meet) && \
|
(cd source_package ; tar cjf ../jitsi-meet.tar.bz2 jitsi-meet) && \
|
||||||
rm -rf source_package
|
rm -rf source_package
|
||||||
|
|||||||
115
README.md
@@ -1,87 +1,80 @@
|
|||||||
# <p align="center">Jitsi Meet</p>
|
# Jitsi Meet - Secure, Simple and Scalable Video Conferences
|
||||||
|
|
||||||
Jitsi Meet is a set of Open Source projects which empower users to use and deploy
|
Jitsi Meet is an open-source (Apache) WebRTC JavaScript application that uses [Jitsi Videobridge](https://jitsi.org/videobridge) to provide high quality, [secure](https://jitsi.org/security) and scalable video conferences. Jitsi Meet in action can be seen at [here at the session #482 of the VoIP Users Conference](http://youtu.be/7vFUVClsNh0).
|
||||||
video conferencing platforms with state-of-the-art video quality and features.
|
|
||||||
|
|
||||||
<hr />
|
The Jitsi Meet client runs in your browser, without installing anything else on your computer. You can try it out at https://meet.jit.si.
|
||||||
|
|
||||||
<p align="center">
|
Jitsi Meet allows very efficient collaboration. Users can stream their desktop or only some windows. It also supports shared document editing with Etherpad.
|
||||||
<img src="https://raw.githubusercontent.com/jitsi/jitsi-meet/master/readme-img1.png" width="900" />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<hr />
|
## Installation
|
||||||
|
|
||||||
Amongst others here are the main features Jitsi Meet offers:
|
On the client side, no installation is necessary. You just point your browser to the URL of your deployment. This section is about installing a Jitsi Meet suite on your server and hosting your own conferencing service.
|
||||||
|
|
||||||
* Support for all current browsers
|
Installing Jitsi Meet is a simple experience. For Debian-based system, following the [quick install](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-quickstart) document, which uses the package system. You can also see a demonstration of the process in [this tutorial video](https://jitsi.org/tutorial).
|
||||||
* Mobile applications
|
|
||||||
* Web and native SDKs for integration
|
|
||||||
* HD audio and video
|
|
||||||
* Content sharing
|
|
||||||
* Raise hand and reactions
|
|
||||||
* Chat with private conversations
|
|
||||||
* Polls
|
|
||||||
* Virtual backgrounds
|
|
||||||
|
|
||||||
And many more!
|
For other systems, or if you wish to install all components manually, see the [detailed manual installation instructions](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-manual).
|
||||||
|
|
||||||
## Using Jitsi Meet
|
Installation with Docker is also available. Please see the [instruction](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker).
|
||||||
|
|
||||||
Using Jitsi Meet is straightforward, as it's browser based. Head over to [meet.jit.si](https://meet.jit.si) and give it a try. It's scalable and free to use. All you need is a Google, Facebook or GitHub account in order to start a meeting. All browsers are supported!
|
## Download
|
||||||
|
|
||||||
Using mobile? No problem, you can either use your mobile web browser or our fully-featured
|
| Latest stable release | [](https://github.com/jitsi/jitsi-meet/releases/latest) |
|
||||||
mobile apps:
|
|---|---|
|
||||||
|
|
||||||
| Android | Android (F-Droid) | iOS |
|
You can download Debian/Ubuntu binaries:
|
||||||
|:-:|:-:|:-:|
|
* [stable](https://download.jitsi.org/stable/) ([instructions](https://jitsi.org/downloads/ubuntu-debian-installations-instructions/))
|
||||||
| [<img src="resources/img/google-play-badge.png" height="50">](https://play.google.com/store/apps/details?id=org.jitsi.meet) | [<img src="resources/img/f-droid-badge.png" height="50">](https://f-droid.org/packages/org.jitsi.meet/) | [<img src="resources/img/appstore-badge.png" height="50">](https://itunes.apple.com/us/app/jitsi-meet/id1165103905) |
|
* [testing](https://download.jitsi.org/testing/) ([instructions](https://jitsi.org/downloads/ubuntu-debian-installations-instructions-for-testing/))
|
||||||
|
* [nightly](https://download.jitsi.org/unstable/) ([instructions](https://jitsi.org/downloads/ubuntu-debian-installations-instructions-nightly/))
|
||||||
|
|
||||||
If you are feeling adventurous and want to get an early scoop of the features as they are being
|
You can download source archives (produced by ```make source-package```):
|
||||||
developed you can also sign up for our open beta testing here:
|
* [source builds](https://download.jitsi.org/jitsi-meet/src/)
|
||||||
|
|
||||||
|
### Mobile apps
|
||||||
|
|
||||||
|
* [Android](https://play.google.com/store/apps/details?id=org.jitsi.meet)
|
||||||
|
|
||||||
|
[<img src="resources/img/google-play-badge.png" height="50">](https://play.google.com/store/apps/details?id=org.jitsi.meet)
|
||||||
|
|
||||||
|
* [Android (F-Droid)](https://f-droid.org/en/packages/org.jitsi.meet/)
|
||||||
|
|
||||||
|
[<img src="resources/img/f-droid-badge.png" height="50">](https://f-droid.org/en/packages/org.jitsi.meet/)
|
||||||
|
|
||||||
|
* [iOS](https://itunes.apple.com/us/app/jitsi-meet/id1165103905)
|
||||||
|
|
||||||
|
[<img src="resources/img/appstore-badge.png" height="50">](https://itunes.apple.com/us/app/jitsi-meet/id1165103905)
|
||||||
|
|
||||||
|
You can also sign up for our open beta testing here:
|
||||||
|
|
||||||
* [Android](https://play.google.com/apps/testing/org.jitsi.meet)
|
* [Android](https://play.google.com/apps/testing/org.jitsi.meet)
|
||||||
* [iOS](https://testflight.apple.com/join/isy6ja7S)
|
* [iOS](https://testflight.apple.com/join/isy6ja7S)
|
||||||
|
|
||||||
## Running your own instance
|
## Release notes
|
||||||
|
|
||||||
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.
|
Release notes for Jitsi Meet are maintained on [this repository](https://github.com/jitsi/jitsi-meet-release-notes).
|
||||||
|
|
||||||
We provide Debian packages and a comprehensive Docker setup to make deployments as simple as possible.
|
## Development
|
||||||
Advanced users also have the possibility of building all the components from source.
|
|
||||||
|
|
||||||
You can check the latest releases [here](https://jitsi.github.io/handbook/docs/releases).
|
For web development see [here](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-web), and for mobile see [here](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-mobile).
|
||||||
|
|
||||||
## Jitsi as a Service
|
|
||||||
|
|
||||||
If you like the branding capabilities of running your own instance but you'd like
|
|
||||||
to avoid dealing with the complexity of monitoring, scaling and updates, JaaS might be
|
|
||||||
for you.
|
|
||||||
|
|
||||||
[8x8 Jitsi as a Service (JaaS)](https://jaas.8x8.vc) is an enterprise-ready video meeting platform that allows developers, organizations and businesses to easily build and deploy video solutions. With Jitsi as a Service we now give you all the power of Jitsi running on our global platform so you can focus on building secure and branded video experiences.
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
All the Jitsi Meet documentation is available in [the handbook](https://jitsi.github.io/handbook/).
|
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
For a comprehensive description of all Jitsi Meet's security aspects, please check [this link](https://jitsi.org/security).
|
|
||||||
|
|
||||||
For a detailed description of Jitsi Meet's End-to-End Encryption (E2EE) implementation,
|
|
||||||
please check [this link](https://jitsi.org/e2ee-whitepaper/).
|
|
||||||
|
|
||||||
For information on reporting security vulnerabilities in Jitsi Meet, see [SECURITY.md](./SECURITY.md).
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
If you are looking to contribute to Jitsi Meet, first of all, thank you! Please
|
If you are looking to contribute to Jitsi Meet, first of all, thank you! Please
|
||||||
see our [guidelines for contributing](CONTRIBUTING.md).
|
see our [guidelines for contributing](CONTRIBUTING.md).
|
||||||
|
|
||||||
<br />
|
## Embedding in external applications
|
||||||
<br />
|
|
||||||
|
|
||||||
<footer>
|
Jitsi Meet provides a very flexible way of embedding in external applications by using the [Jitsi Meet API](doc/api.md).
|
||||||
<p align="center" style="font-size: smaller;">
|
|
||||||
Built with ❤️ by the Jitsi team at <a href="https://8x8.com" target="_blank">8x8</a> and our community.
|
## Security
|
||||||
</p>
|
|
||||||
</footer>
|
The security section here was starting to feel a bit too succinct for the complexity of the topic, so we created a post that covers the topic much more broadly here: https://jitsi.org/security
|
||||||
|
|
||||||
|
The section on end-to-end encryption in that document is likely going to be one of the key points of interest: https://jitsi.org/security/#e2ee
|
||||||
|
|
||||||
|
## Security issues
|
||||||
|
|
||||||
|
For information on reporting security vulnerabilities in Jitsi Meet, see [SECURITY.md](./SECURITY.md).
|
||||||
|
|
||||||
|
## Acknowledgements
|
||||||
|
|
||||||
|
Jitsi Meet started out as a sample conferencing application using Jitsi Videobridge. It was originally developed by ESTOS' developer Philipp Hancke who then contributed it to the community where development continues with joint forces!
|
||||||
|
|||||||
@@ -4,6 +4,6 @@
|
|||||||
|
|
||||||
We take security very seriously and develop all Jitsi projects to be secure and safe.
|
We take security very seriously and develop all Jitsi projects to be secure and safe.
|
||||||
|
|
||||||
If you find (or simply suspect) a security issue in any of the Jitsi projects, please report it to us via [HackerOne](https://hackerone.com/8x8-bounty) or send us an email to security@jitsi.org.
|
If you find (or simply suspect) a security issue in any of the Jitsi projects, please report it to us via [HackerOne](https://hackerone.com/8x8) or send us an email to security@jitsi.org.
|
||||||
|
|
||||||
**We encourage responsible disclosure for the sake of our users, so please reach out before posting in a public space.**
|
**We encourage responsible disclosure for the sake of our users, so please reach out before posting in a public space.**
|
||||||
|
|||||||
163
analytics-ga.js
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
/* global ga */
|
||||||
|
|
||||||
|
(function(ctx) {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function Analytics(options) {
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
if (!options.googleAnalyticsTrackingId) {
|
||||||
|
console.log(
|
||||||
|
'Failed to initialize Google Analytics handler, no tracking ID');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Google Analytics
|
||||||
|
* TODO: Keep this local, there's no need to add it to window.
|
||||||
|
*/
|
||||||
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||||
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||||
|
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||||
|
ga('create', options.googleAnalyticsTrackingId, 'auto');
|
||||||
|
ga('send', 'pageview');
|
||||||
|
|
||||||
|
/* eslint-enable */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the integer to use for a Google Analytics event's value field
|
||||||
|
* from a lib-jitsi-meet analytics event.
|
||||||
|
* @param {Object} event - The lib-jitsi-meet analytics event.
|
||||||
|
* @returns {Object} - The integer to use for the 'value' of a Google
|
||||||
|
* Analytics event.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
Analytics.prototype._extractAction = function(event) {
|
||||||
|
// Page events have a single 'name' field.
|
||||||
|
if (event.type === 'page') {
|
||||||
|
return event.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All other events have action, actionSubject, and source fields. All
|
||||||
|
// three fields are required, and the often jitsi-meet and
|
||||||
|
// lib-jitsi-meet use the same value when separate values are not
|
||||||
|
// necessary (i.e. event.action == event.actionSubject).
|
||||||
|
// Here we concatenate these three fields, but avoid adding the same
|
||||||
|
// value twice, because it would only make the GA event's action harder
|
||||||
|
// to read.
|
||||||
|
let action = event.action;
|
||||||
|
|
||||||
|
if (event.actionSubject && event.actionSubject !== event.action) {
|
||||||
|
// Intentionally use string concatenation as analytics needs to
|
||||||
|
// work on IE but this file does not go through babel. For some
|
||||||
|
// reason disabling this globally for the file does not have an
|
||||||
|
// effect.
|
||||||
|
// eslint-disable-next-line prefer-template
|
||||||
|
action = event.actionSubject + '.' + action;
|
||||||
|
}
|
||||||
|
if (event.source && event.source !== event.action
|
||||||
|
&& event.source !== event.action) {
|
||||||
|
// eslint-disable-next-line prefer-template
|
||||||
|
action = event.source + '.' + action;
|
||||||
|
}
|
||||||
|
|
||||||
|
return action;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the integer to use for a Google Analytics event's value field
|
||||||
|
* from a lib-jitsi-meet analytics event.
|
||||||
|
* @param {Object} event - The lib-jitsi-meet analytics event.
|
||||||
|
* @returns {Object} - The integer to use for the 'value' of a Google
|
||||||
|
* Analytics event, or NaN if the lib-jitsi-meet event doesn't contain a
|
||||||
|
* suitable value.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
Analytics.prototype._extractValue = function(event) {
|
||||||
|
let value = event && event.attributes && event.attributes.value;
|
||||||
|
|
||||||
|
// Try to extract an integer from the "value" attribute.
|
||||||
|
value = Math.round(parseFloat(value));
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the string to use for a Google Analytics event's label field
|
||||||
|
* from a lib-jitsi-meet analytics event.
|
||||||
|
* @param {Object} event - The lib-jitsi-meet analytics event.
|
||||||
|
* @returns {string} - The string to use for the 'label' of a Google
|
||||||
|
* Analytics event.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
Analytics.prototype._extractLabel = function(event) {
|
||||||
|
let label = '';
|
||||||
|
|
||||||
|
// The label field is limited to 500B. We will concatenate all
|
||||||
|
// attributes of the event, except the user agent because it may be
|
||||||
|
// lengthy and is probably included from elsewhere.
|
||||||
|
for (const property in event.attributes) {
|
||||||
|
if (property !== 'permanent_user_agent'
|
||||||
|
&& property !== 'permanent_callstats_name'
|
||||||
|
&& event.attributes.hasOwnProperty(property)) {
|
||||||
|
// eslint-disable-next-line prefer-template
|
||||||
|
label += property + '=' + event.attributes[property] + '&';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (label.length > 0) {
|
||||||
|
label = label.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return label;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the entry point of the API. The function sends an event to
|
||||||
|
* google analytics. The format of the event is described in
|
||||||
|
* AnalyticsAdapter in lib-jitsi-meet.
|
||||||
|
* @param {Object} event - the event in the format specified by
|
||||||
|
* lib-jitsi-meet.
|
||||||
|
*/
|
||||||
|
Analytics.prototype.sendEvent = function(event) {
|
||||||
|
if (!event || !ga) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ignoredEvents
|
||||||
|
= [ 'e2e_rtt', 'rtp.stats', 'rtt.by.region', 'available.device',
|
||||||
|
'stream.switch.delay', 'ice.state.changed', 'ice.duration' ];
|
||||||
|
|
||||||
|
// Temporary removing some of the events that are too noisy.
|
||||||
|
if (ignoredEvents.indexOf(event.action) !== -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gaEvent = {
|
||||||
|
'eventCategory': 'jitsi-meet',
|
||||||
|
'eventAction': this._extractAction(event),
|
||||||
|
'eventLabel': this._extractLabel(event)
|
||||||
|
};
|
||||||
|
const value = this._extractValue(event);
|
||||||
|
|
||||||
|
if (!isNaN(value)) {
|
||||||
|
gaEvent.eventValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ga('send', 'event', gaEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof ctx.JitsiMeetJS === 'undefined') {
|
||||||
|
ctx.JitsiMeetJS = {};
|
||||||
|
}
|
||||||
|
if (typeof ctx.JitsiMeetJS.app === 'undefined') {
|
||||||
|
ctx.JitsiMeetJS.app = {};
|
||||||
|
}
|
||||||
|
if (typeof ctx.JitsiMeetJS.app.analyticsHandlers === 'undefined') {
|
||||||
|
ctx.JitsiMeetJS.app.analyticsHandlers = [];
|
||||||
|
}
|
||||||
|
ctx.JitsiMeetJS.app.analyticsHandlers.push(Analytics);
|
||||||
|
})(window);
|
||||||
|
/* eslint-enable prefer-template */
|
||||||
@@ -16,6 +16,10 @@ android {
|
|||||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||||
|
|
||||||
|
packagingOptions {
|
||||||
|
exclude 'lib/*/libhermes*.so'
|
||||||
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId 'org.jitsi.meet'
|
applicationId 'org.jitsi.meet'
|
||||||
versionCode vcode
|
versionCode vcode
|
||||||
@@ -42,7 +46,6 @@ android {
|
|||||||
debug {
|
debug {
|
||||||
buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${googleServicesEnabled}"
|
buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${googleServicesEnabled}"
|
||||||
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
|
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
|
||||||
applicationIdSuffix ".debug"
|
|
||||||
}
|
}
|
||||||
release {
|
release {
|
||||||
// Uncomment the following line for singing a test release build.
|
// Uncomment the following line for singing a test release build.
|
||||||
@@ -69,24 +72,23 @@ android {
|
|||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
namespace 'org.jitsi.meet'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.appcompat:appcompat:1.5.1'
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
|
|
||||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.13'
|
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'
|
||||||
|
|
||||||
if (!rootProject.ext.libreBuild) {
|
if (!rootProject.ext.libreBuild) {
|
||||||
// Sync with react-native-google-signin
|
implementation 'com.google.android.gms:play-services-auth:16.0.1'
|
||||||
implementation 'com.google.android.gms:play-services-auth:20.5.0'
|
|
||||||
|
|
||||||
// Firebase
|
// Firebase
|
||||||
// - Crashlytics
|
// - Crashlytics
|
||||||
// - Dynamic Links
|
// - Dynamic Links
|
||||||
implementation 'com.google.firebase:firebase-analytics:21.3.0'
|
implementation 'com.google.firebase:firebase-analytics:17.5.0'
|
||||||
implementation 'com.google.firebase:firebase-crashlytics:18.4.3'
|
implementation 'com.google.firebase:firebase-crashlytics:17.2.1'
|
||||||
implementation 'com.google.firebase:firebase-dynamic-links:21.1.0'
|
implementation 'com.google.firebase:firebase-dynamic-links:19.1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation project(':sdk')
|
implementation project(':sdk')
|
||||||
@@ -107,7 +109,6 @@ gradle.projectsEvaluated {
|
|||||||
def dropboxActivity = """
|
def dropboxActivity = """
|
||||||
<activity
|
<activity
|
||||||
android:configChanges="keyboard|orientation"
|
android:configChanges="keyboard|orientation"
|
||||||
android:exported="true"
|
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:name="com.dropbox.core.android.AuthActivity">
|
android:name="com.dropbox.core.android.AuthActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
@@ -121,7 +122,7 @@ gradle.projectsEvaluated {
|
|||||||
android.applicationVariants.all { variant ->
|
android.applicationVariants.all { variant ->
|
||||||
variant.outputs.each { output ->
|
variant.outputs.each { output ->
|
||||||
output.getProcessManifestProvider().get().doLast {
|
output.getProcessManifestProvider().get().doLast {
|
||||||
def outputDir = multiApkManifestOutputDirectory.get().asFile
|
def outputDir = manifestOutputDirectory.get().asFile
|
||||||
def manifestPath = new File(outputDir, 'AndroidManifest.xml')
|
def manifestPath = new File(outputDir, 'AndroidManifest.xml')
|
||||||
def charset = 'UTF-8'
|
def charset = 'UTF-8'
|
||||||
def text
|
def text
|
||||||
|
|||||||
47
android/app/proguard-rules.pro
vendored
@@ -1,8 +1,3 @@
|
|||||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
||||||
#
|
|
||||||
# This source code is licensed under the MIT license found in the
|
|
||||||
# LICENSE file in the root directory of this source tree.
|
|
||||||
|
|
||||||
# Add project specific ProGuard rules here.
|
# Add project specific ProGuard rules here.
|
||||||
# By default, the flags in this file are appended to flags specified
|
# By default, the flags in this file are appended to flags specified
|
||||||
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
|
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
|
||||||
@@ -14,25 +9,20 @@
|
|||||||
|
|
||||||
# Add any project specific keep options here:
|
# Add any project specific keep options here:
|
||||||
|
|
||||||
# Disabling obfuscation is useful if you collect stack traces from production crashes
|
|
||||||
# (unless you are using a system that supports de-obfuscate the stack traces).
|
|
||||||
# -dontobfuscate
|
|
||||||
|
|
||||||
# React Native
|
# React Native
|
||||||
|
|
||||||
# Keep our interfaces so they can be used by other ProGuard rules.
|
# Keep our interfaces so they can be used by other ProGuard rules.
|
||||||
# See http://sourceforge.net/p/proguard/bugs/466/
|
# See http://sourceforge.net/p/proguard/bugs/466/
|
||||||
-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
|
-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
|
||||||
-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
|
-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
|
||||||
|
-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
|
||||||
|
|
||||||
# Do not strip any method/class that is annotated with @DoNotStrip
|
# Do not strip any method/class that is annotated with @DoNotStrip
|
||||||
-keep @com.facebook.proguard.annotations.DoNotStrip class *
|
-keep @com.facebook.proguard.annotations.DoNotStrip class *
|
||||||
|
-keep @com.facebook.common.internal.DoNotStrip class *
|
||||||
-keepclassmembers class * {
|
-keepclassmembers class * {
|
||||||
@com.facebook.proguard.annotations.DoNotStrip *;
|
@com.facebook.proguard.annotations.DoNotStrip *;
|
||||||
}
|
@com.facebook.common.internal.DoNotStrip *;
|
||||||
|
|
||||||
-keep @com.facebook.proguard.annotations.DoNotStripAny class * {
|
|
||||||
*;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
|
-keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
|
||||||
@@ -40,32 +30,32 @@
|
|||||||
*** get*();
|
*** get*();
|
||||||
}
|
}
|
||||||
|
|
||||||
-keep class * implements com.facebook.react.bridge.JavaScriptModule { *; }
|
-keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
|
||||||
-keep class * implements com.facebook.react.bridge.NativeModule { *; }
|
-keep class * extends com.facebook.react.bridge.NativeModule { *; }
|
||||||
-keepclassmembers,includedescriptorclasses class * { native <methods>; }
|
-keepclassmembers,includedescriptorclasses class * { native <methods>; }
|
||||||
|
-keepclassmembers class * { @com.facebook.react.uimanager.UIProp <fields>; }
|
||||||
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp <methods>; }
|
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp <methods>; }
|
||||||
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup <methods>; }
|
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup <methods>; }
|
||||||
|
|
||||||
-dontwarn com.facebook.react.**
|
-dontwarn com.facebook.react.**
|
||||||
-keep,includedescriptorclasses class com.facebook.react.bridge.** { *; }
|
-keep,includedescriptorclasses class com.facebook.react.bridge.** { *; }
|
||||||
-keep,includedescriptorclasses class com.facebook.react.turbomodule.core.** { *; }
|
|
||||||
|
|
||||||
# hermes
|
# okhttp
|
||||||
-keep class com.facebook.jni.** { *; }
|
|
||||||
|
-keepattributes Signature
|
||||||
|
-keepattributes *Annotation*
|
||||||
|
-keep class okhttp3.** { *; }
|
||||||
|
-keep interface okhttp3.** { *; }
|
||||||
|
-dontwarn okhttp3.**
|
||||||
|
|
||||||
# okio
|
# okio
|
||||||
|
|
||||||
-keep class sun.misc.Unsafe { *; }
|
-keep class sun.misc.Unsafe { *; }
|
||||||
-dontwarn java.nio.file.*
|
-dontwarn java.nio.file.*
|
||||||
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
||||||
|
-keep class okio.** { *; }
|
||||||
-dontwarn okio.**
|
-dontwarn okio.**
|
||||||
|
|
||||||
# yoga
|
|
||||||
-keep,allowobfuscation @interface com.facebook.yoga.annotations.DoNotStrip
|
|
||||||
-keep @com.facebook.yoga.annotations.DoNotStrip class *
|
|
||||||
-keepclassmembers class * {
|
|
||||||
@com.facebook.yoga.annotations.DoNotStrip *;
|
|
||||||
}
|
|
||||||
|
|
||||||
# WebRTC
|
# WebRTC
|
||||||
|
|
||||||
-keep class org.webrtc.** { *; }
|
-keep class org.webrtc.** { *; }
|
||||||
@@ -95,9 +85,4 @@
|
|||||||
# ^^^ We added the above when we switched minifyEnabled on.
|
# ^^^ We added the above when we switched minifyEnabled on.
|
||||||
|
|
||||||
# Rule to avoid build errors related to SVGs.
|
# Rule to avoid build errors related to SVGs.
|
||||||
-keep public class com.horcrux.svg.** {*;}
|
-keep public class com.horcrux.svg.** {*;}
|
||||||
|
|
||||||
# https://github.com/facebook/fresco/issues/2638
|
|
||||||
-keep public class com.facebook.imageutils.** {
|
|
||||||
public *;
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
<manifest
|
<manifest
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.jitsi.meet"
|
||||||
android:installLocation="auto">
|
android:installLocation="auto">
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:extractNativeLibs="true"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
@@ -12,11 +12,9 @@
|
|||||||
android:name="android.content.APP_RESTRICTIONS"
|
android:name="android.content.APP_RESTRICTIONS"
|
||||||
android:resource="@xml/app_restrictions" />
|
android:resource="@xml/app_restrictions" />
|
||||||
<activity
|
<activity
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize"
|
||||||
android:exported="true"
|
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="singleInstance"
|
android:launchMode="singleTask"
|
||||||
android:taskAffinity=""
|
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:resizeableActivity="true"
|
android:resizeableActivity="true"
|
||||||
android:supportsPictureInPicture="true"
|
android:supportsPictureInPicture="true"
|
||||||
|
|||||||
@@ -30,16 +30,15 @@ import android.view.KeyEvent;
|
|||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.oney.WebRTCModule.WebRTCModuleOptions;
|
|
||||||
|
|
||||||
import org.jitsi.meet.sdk.JitsiMeet;
|
import org.jitsi.meet.sdk.JitsiMeet;
|
||||||
import org.jitsi.meet.sdk.JitsiMeetActivity;
|
import org.jitsi.meet.sdk.JitsiMeetActivity;
|
||||||
import org.jitsi.meet.sdk.JitsiMeetConferenceOptions;
|
import org.jitsi.meet.sdk.JitsiMeetConferenceOptions;
|
||||||
import org.webrtc.Logging;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The one and only Activity that the Jitsi Meet app needs. The
|
* The one and only Activity that the Jitsi Meet app needs. The
|
||||||
@@ -76,17 +75,14 @@ public class MainActivity extends JitsiMeetActivity {
|
|||||||
*/
|
*/
|
||||||
private String defaultURL;
|
private String defaultURL;
|
||||||
|
|
||||||
|
|
||||||
// JitsiMeetActivity overrides
|
// JitsiMeetActivity overrides
|
||||||
//
|
//
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
JitsiMeet.showSplashScreen(this);
|
JitsiMeet.showSplashScreen(this);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
WebRTCModuleOptions options = WebRTCModuleOptions.getInstance();
|
|
||||||
options.loggingSeverity = Logging.Severity.LS_ERROR;
|
|
||||||
|
|
||||||
super.onCreate(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -151,12 +147,13 @@ public class MainActivity extends JitsiMeetActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setJitsiMeetConferenceDefaultOptions() {
|
private void setJitsiMeetConferenceDefaultOptions() {
|
||||||
|
|
||||||
// Set default options
|
// Set default options
|
||||||
JitsiMeetConferenceOptions defaultOptions
|
JitsiMeetConferenceOptions defaultOptions
|
||||||
= new JitsiMeetConferenceOptions.Builder()
|
= new JitsiMeetConferenceOptions.Builder()
|
||||||
|
.setWelcomePageEnabled(true)
|
||||||
.setServerURL(buildURL(defaultURL))
|
.setServerURL(buildURL(defaultURL))
|
||||||
.setFeatureFlag("welcomepage.enabled", true)
|
.setFeatureFlag("call-integration.enabled", false)
|
||||||
|
.setFeatureFlag("resolution", 360)
|
||||||
.setFeatureFlag("server-url-change.enabled", !configurationByRestrictions)
|
.setFeatureFlag("server-url-change.enabled", !configurationByRestrictions)
|
||||||
.build();
|
.build();
|
||||||
JitsiMeet.setDefaultConferenceOptions(defaultOptions);
|
JitsiMeet.setDefaultConferenceOptions(defaultOptions);
|
||||||
@@ -185,6 +182,11 @@ public class MainActivity extends JitsiMeetActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConferenceTerminated(Map<String, Object> data) {
|
||||||
|
Log.d(TAG, "Conference terminated: " + data);
|
||||||
|
}
|
||||||
|
|
||||||
// Activity lifecycle method overrides
|
// Activity lifecycle method overrides
|
||||||
//
|
//
|
||||||
|
|
||||||
@@ -218,6 +220,11 @@ public class MainActivity extends JitsiMeetActivity {
|
|||||||
super.onPictureInPictureModeChanged(isInPictureInPictureMode);
|
super.onPictureInPictureModeChanged(isInPictureInPictureMode);
|
||||||
|
|
||||||
Log.d(TAG, "Is in picture-in-picture mode: " + isInPictureInPictureMode);
|
Log.d(TAG, "Is in picture-in-picture mode: " + isInPictureInPictureMode);
|
||||||
|
|
||||||
|
if (!isInPictureInPictureMode) {
|
||||||
|
this.startActivity(new Intent(this, getClass())
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
@@ -226,7 +233,7 @@ public class MainActivity extends JitsiMeetActivity {
|
|||||||
private @Nullable URL buildURL(String urlStr) {
|
private @Nullable URL buildURL(String urlStr) {
|
||||||
try {
|
try {
|
||||||
return new URL(urlStr);
|
return new URL(urlStr);
|
||||||
} catch (Exception e) {
|
} catch (MalformedURLException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
android/app/src/main/res/drawable-hdpi/ic_notification.png
Normal file
|
After Width: | Height: | Size: 659 B |
BIN
android/app/src/main/res/drawable-mdpi/ic_notification.png
Normal file
|
After Width: | Height: | Size: 379 B |
BIN
android/app/src/main/res/drawable-xhdpi/ic_notification.png
Normal file
|
After Width: | Height: | Size: 960 B |
BIN
android/app/src/main/res/drawable-xxhdpi/ic_notification.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
android/app/src/main/res/drawable-xxxhdpi/ic_notification.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
@@ -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>
|
|
||||||
@@ -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>
|
|
||||||
@@ -2,5 +2,4 @@
|
|||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
@@ -2,5 +2,4 @@
|
|||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="colorPrimary">#17A0DB</color>
|
<color name="colorPrimary">#17A0DB</color>
|
||||||
<color name="navigationBarColor">#161618</color>
|
<color name="colorPrimaryDark">#1081B2</color>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
<resources>
|
<resources>
|
||||||
|
<!-- Base application theme. -->
|
||||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
|
<!-- Customize your theme here. -->
|
||||||
<item name="android:forceDarkAllowed">false</item>
|
<item name="android:navigationBarColor">@color/colorPrimaryDark</item>
|
||||||
<item name="android:navigationBarColor">@color/navigationBarColor</item>
|
</style>
|
||||||
<item name="android:windowDisablePreview">true</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
|
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<!-- Server URL configuration -->
|
<!-- Server URL configuration -->
|
||||||
<restriction
|
<restriction
|
||||||
android:description="@string/restriction_server_url_description"
|
android:defaultValue="https://meet.jit.si"
|
||||||
android:key="SERVER_URL"
|
android:description="@string/restriction_server_url_description"
|
||||||
android:restrictionType="string"
|
android:key="SERVER_URL"
|
||||||
android:title="@string/restriction_server_url_title"/>
|
android:restrictionType="string"
|
||||||
</restrictions>
|
android:title="@string/restriction_server_url_title"/>
|
||||||
|
</restrictions>
|
||||||
|
|||||||
@@ -7,25 +7,23 @@ import org.gradle.util.VersionNumber
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.4.2'
|
classpath 'com.android.tools.build:gradle:4.0.2'
|
||||||
classpath 'com.google.gms:google-services:4.4.0'
|
classpath 'com.google.gms:google-services:4.3.3'
|
||||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9'
|
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
kotlinVersion = "1.9.24"
|
buildToolsVersion = "29.0.3"
|
||||||
buildToolsVersion = "34.0.0"
|
compileSdkVersion = 29
|
||||||
compileSdkVersion = 34
|
minSdkVersion = 23
|
||||||
minSdkVersion = 26
|
targetSdkVersion = 29
|
||||||
targetSdkVersion = 34
|
|
||||||
supportLibVersion = "28.0.0"
|
supportLibVersion = "28.0.0"
|
||||||
ndkVersion = "26.1.10909125"
|
|
||||||
|
|
||||||
// The Maven artifact groupId of the third-party react-native modules which
|
// The Maven artifact groupdId of the third-party react-native modules which
|
||||||
// Jitsi Meet SDK for Android depends on and which are not available in
|
// Jitsi Meet SDK for Android depends on and which are not available in
|
||||||
// third-party Maven repositories so we have to deploy to a Maven repository
|
// third-party Maven repositories so we have to deploy to a Maven repository
|
||||||
// of ours.
|
// of ours.
|
||||||
@@ -40,16 +38,16 @@ ext {
|
|||||||
libreBuild = (System.env.LIBRE_BUILD ?: "false").toBoolean()
|
libreBuild = (System.env.LIBRE_BUILD ?: "false").toBoolean()
|
||||||
|
|
||||||
googleServicesEnabled = project.file('app/google-services.json').exists() && !libreBuild
|
googleServicesEnabled = project.file('app/google-services.json').exists() && !libreBuild
|
||||||
|
|
||||||
//React Native and Hermes Version
|
|
||||||
rnVersion = "0.75.5"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
|
||||||
google()
|
google()
|
||||||
maven { url 'https://www.jitpack.io' }
|
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") }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we use the react-native version in node_modules and not the one
|
// Make sure we use the react-native version in node_modules and not the one
|
||||||
@@ -57,41 +55,20 @@ allprojects {
|
|||||||
configurations.all {
|
configurations.all {
|
||||||
resolutionStrategy {
|
resolutionStrategy {
|
||||||
eachDependency { DependencyResolveDetails details ->
|
eachDependency { DependencyResolveDetails details ->
|
||||||
if (details.requested.group == 'com.facebook.react') {
|
if (details.requested.group == 'com.facebook.react'
|
||||||
if (details.requested.name == 'react-native') {
|
&& details.requested.name == 'react-native') {
|
||||||
details.useTarget "com.facebook.react:react-android:$rnVersion"
|
def file = new File("$rootDir/../node_modules/react-native/package.json")
|
||||||
}
|
def version = new JsonSlurper().parseText(file.text).version
|
||||||
if (details.requested.name == 'react-android') {
|
details.useVersion version
|
||||||
details.useVersion rootProject.ext.rnVersion
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Due to a dependency conflict between React Native and the Fresco library used by GiphySDK,
|
|
||||||
// GIFs appear as static images instead of animating
|
|
||||||
// https://github.com/Giphy/giphy-react-native-sdk/commit/7fe466ed6fddfaec95f9cbc959d33bd75ad8f900
|
|
||||||
|
|
||||||
configurations.configureEach {
|
|
||||||
resolutionStrategy {
|
|
||||||
forcedModules = [
|
|
||||||
'com.facebook.fresco:fresco:3.2.0',
|
|
||||||
'com.facebook.fresco:animated-gif:3.2.0',
|
|
||||||
'com.facebook.fresco:animated-base:3.2.0',
|
|
||||||
'com.facebook.fresco:animated-drawable:3.2.0',
|
|
||||||
'com.facebook.fresco:animated-webp:3.2.0',
|
|
||||||
'com.facebook.fresco:webpsupport:3.2.0',
|
|
||||||
'com.facebook.fresco:imagepipeline-okhttp3:3.2.0',
|
|
||||||
'com.facebook.fresco:middleware:3.2.0',
|
|
||||||
'com.facebook.fresco:nativeimagetranscoder:3.2.0'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Third-party react-native modules which Jitsi Meet SDK for Android depends
|
// Third-party react-native modules which Jitsi Meet SDK for Android depends
|
||||||
// on and which are not available in third-party Maven repositories need to
|
// on and which are not available in third-party Maven repositories need to
|
||||||
// be deployed in a Maven repository of ours.
|
// be deployed in a Maven repository of ours.
|
||||||
|
//
|
||||||
|
|
||||||
if (project.name.startsWith('react-native-')) {
|
if (project.name.startsWith('react-native-')) {
|
||||||
apply plugin: 'maven-publish'
|
apply plugin: 'maven-publish'
|
||||||
@@ -133,7 +110,7 @@ allprojects {
|
|||||||
|
|
||||||
project.version = "${json.version}-jitsi-${versionQualifierNumber}"
|
project.version = "${json.version}-jitsi-${versionQualifierNumber}"
|
||||||
|
|
||||||
task jitsiAndroidSourcesJar(type: Jar) {
|
task androidSourcesJar(type: Jar) {
|
||||||
classifier = 'sources'
|
classifier = 'sources'
|
||||||
from android.sourceSets.main.java.source
|
from android.sourceSets.main.java.source
|
||||||
}
|
}
|
||||||
@@ -147,7 +124,7 @@ allprojects {
|
|||||||
artifact("${project.buildDir}/outputs/aar/${project.name}-release.aar") {
|
artifact("${project.buildDir}/outputs/aar/${project.name}-release.aar") {
|
||||||
extension "aar"
|
extension "aar"
|
||||||
}
|
}
|
||||||
artifact(jitsiAndroidSourcesJar)
|
artifact(androidSourcesJar)
|
||||||
pom.withXml {
|
pom.withXml {
|
||||||
def pomXml = asNode()
|
def pomXml = asNode()
|
||||||
pomXml.appendNode('name', project.name)
|
pomXml.appendNode('name', project.name)
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
|
|
||||||
# Specifies the JVM arguments used for the daemon process.
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
# Default value: -Xmx1024m -XX:MaxPermSize=256m
|
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||||
|
|
||||||
org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||||
|
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
# This option should only be used with decoupled projects. More details, visit
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
@@ -24,7 +24,6 @@ android.enableDexingArtifactTransform.desugaring=false
|
|||||||
|
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
android.bundle.enableUncompressedNativeLibs=false
|
|
||||||
|
|
||||||
appVersion=99.0.0
|
appVersion=20.5.0
|
||||||
sdkVersion=0.0.0
|
sdkVersion=2.11.0
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
#Wed Sep 23 11:48:00 EEST 2020
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
|
||||||
|
|||||||
286
android/gradlew
vendored
@@ -1,129 +1,78 @@
|
|||||||
#!/bin/sh
|
#!/usr/bin/env 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.
|
|
||||||
#
|
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
##
|
||||||
# Gradle start up script for POSIX generated by Gradle.
|
## Gradle start up script for UN*X
|
||||||
#
|
##
|
||||||
# Important for running:
|
|
||||||
#
|
|
||||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
|
||||||
# noncompliant, but you have some other compliant shell such as ksh or
|
|
||||||
# bash, then to run this script, type that shell name before the whole
|
|
||||||
# command line, like:
|
|
||||||
#
|
|
||||||
# ksh Gradle
|
|
||||||
#
|
|
||||||
# Busybox and similar reduced shells will NOT work, because this script
|
|
||||||
# requires all of these POSIX shell features:
|
|
||||||
# * functions;
|
|
||||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
|
||||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
|
||||||
# * compound commands having a testable exit status, especially «case»;
|
|
||||||
# * various built-in commands including «command», «set», and «ulimit».
|
|
||||||
#
|
|
||||||
# Important for patching:
|
|
||||||
#
|
|
||||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
|
||||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
|
||||||
#
|
|
||||||
# The "traditional" practice of packing multiple parameters into a
|
|
||||||
# space-separated string is a well documented source of bugs and security
|
|
||||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
|
||||||
# options in "$@", and eventually passing that to Java.
|
|
||||||
#
|
|
||||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
|
||||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
|
||||||
# see the in-line comments for details.
|
|
||||||
#
|
|
||||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
|
||||||
# Darwin, MinGW, and NonStop.
|
|
||||||
#
|
|
||||||
# (3) This script is generated from the Groovy template
|
|
||||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
|
||||||
# within the Gradle project.
|
|
||||||
#
|
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
|
||||||
#
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
app_path=$0
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
# Need this for daisy-chained symlinks.
|
while [ -h "$PRG" ] ; do
|
||||||
while
|
ls=`ls -ld "$PRG"`
|
||||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
[ -h "$app_path" ]
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
do
|
PRG="$link"
|
||||||
ls=$( ls -ld "$app_path" )
|
else
|
||||||
link=${ls#*' -> '}
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
case $link in #(
|
fi
|
||||||
/*) app_path=$link ;; #(
|
|
||||||
*) app_path=$APP_HOME$link ;;
|
|
||||||
esac
|
|
||||||
done
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
APP_NAME="Gradle"
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD="maximum"
|
||||||
|
|
||||||
warn () {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
} >&2
|
}
|
||||||
|
|
||||||
die () {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
} >&2
|
}
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
nonstop=false
|
nonstop=false
|
||||||
case "$( uname )" in #(
|
case "`uname`" in
|
||||||
CYGWIN* ) cygwin=true ;; #(
|
CYGWIN* )
|
||||||
Darwin* ) darwin=true ;; #(
|
cygwin=true
|
||||||
MSYS* | MINGW* ) msys=true ;; #(
|
;;
|
||||||
NONSTOP* ) nonstop=true ;;
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
else
|
else
|
||||||
JAVACMD=$JAVA_HOME/bin/java
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
@@ -132,7 +81,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD=java
|
JAVACMD="java"
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
@@ -140,95 +89,84 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
case $MAX_FD in #(
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
max*)
|
if [ $? -eq 0 ] ; then
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
warn "Could not query maximum file descriptor limit"
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
esac
|
|
||||||
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" )
|
|
||||||
fi
|
fi
|
||||||
# Roll the args list around exactly as many times as the number of
|
ulimit -n $MAX_FD
|
||||||
# args, so each arg winds up back in the position where it started, but
|
if [ $? -ne 0 ] ; then
|
||||||
# possibly modified.
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
#
|
fi
|
||||||
# NB: a `for` loop captures its iteration list before it begins, so
|
else
|
||||||
# changing the positional parameters here affects neither the number of
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
# iterations, nor the values presented in `arg`.
|
fi
|
||||||
shift # remove old arg
|
|
||||||
set -- "$@" "$arg" # push replacement arg
|
|
||||||
done
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
if $darwin; then
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
# double quotes to make sure that they get re-expanded; and
|
fi
|
||||||
# * put everything else in single quotes, so that it's not re-expanded.
|
|
||||||
|
|
||||||
set -- \
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
if $cygwin ; then
|
||||||
-classpath "$CLASSPATH" \
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
org.gradle.wrapper.GradleWrapperMain \
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
"$@"
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
# Use "xargs" to parse quoted args.
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
#
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
SEP=""
|
||||||
#
|
for dir in $ROOTDIRSRAW ; do
|
||||||
# In Bash we could simply go:
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
#
|
SEP="|"
|
||||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
done
|
||||||
# set -- "${ARGS[@]}" "$@"
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
#
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
# character that might be a shell metacharacter, then use eval to reverse
|
fi
|
||||||
# that process (while maintaining the separation between arguments), and wrap
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
# the whole thing up as a single "set" statement.
|
i=0
|
||||||
#
|
for arg in "$@" ; do
|
||||||
# This will of course break if any of these variables contains a newline or
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
# an unmatched quote.
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
#
|
|
||||||
|
|
||||||
eval "set -- $(
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
xargs -n1 |
|
else
|
||||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
eval `echo args$i`="\"$arg\""
|
||||||
tr '\n' ' '
|
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" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|||||||
173
android/gradlew.bat
vendored
@@ -1,89 +1,84 @@
|
|||||||
@rem
|
@if "%DEBUG%" == "" @echo off
|
||||||
@rem Copyright 2015 the original author or authors.
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
@rem Gradle startup script for Windows
|
||||||
@rem you may not use this file except in compliance with the License.
|
@rem
|
||||||
@rem You may obtain a copy of the License at
|
@rem ##########################################################################
|
||||||
@rem
|
|
||||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
@rem Set local scope for the variables with windows NT shell
|
||||||
@rem
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
@rem Unless required by applicable law or agreed to in writing, software
|
|
||||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
set DIRNAME=%~dp0
|
||||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
@rem See the License for the specific language governing permissions and
|
set APP_BASE_NAME=%~n0
|
||||||
@rem limitations under the License.
|
set APP_HOME=%DIRNAME%
|
||||||
@rem
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
@if "%DEBUG%" == "" @echo off
|
set DEFAULT_JVM_OPTS=
|
||||||
@rem ##########################################################################
|
|
||||||
@rem
|
@rem Find java.exe
|
||||||
@rem Gradle startup script for Windows
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
@rem
|
|
||||||
@rem ##########################################################################
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
@rem Set local scope for the variables with windows NT shell
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
|
||||||
|
echo.
|
||||||
set DIRNAME=%~dp0
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
echo.
|
||||||
set APP_BASE_NAME=%~n0
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
set APP_HOME=%DIRNAME%
|
echo location of your Java installation.
|
||||||
|
|
||||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
goto fail
|
||||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
@rem Find java.exe
|
if exist "%JAVA_EXE%" goto init
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
|
||||||
|
echo.
|
||||||
set JAVA_EXE=java.exe
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
echo.
|
||||||
if "%ERRORLEVEL%" == "0" goto execute
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
echo.
|
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
goto fail
|
||||||
echo.
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
:init
|
||||||
echo location of your Java installation.
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
goto fail
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
|
||||||
:findJavaFromJavaHome
|
:win9xME_args
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
@rem Slurp the command line arguments.
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
if exist "%JAVA_EXE%" goto execute
|
|
||||||
|
:win9xME_args_slurp
|
||||||
echo.
|
if "x%~1" == "x" goto execute
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
|
||||||
echo.
|
set CMD_LINE_ARGS=%*
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
echo location of your Java installation.
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
goto fail
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
:execute
|
|
||||||
@rem Setup the command line
|
@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%
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
@rem Execute Gradle
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
|
||||||
|
:fail
|
||||||
:end
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
@rem End local scope for the variables with windows NT shell
|
rem the _cmd.exe /c_ return code!
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
:fail
|
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
:mainEnd
|
||||||
rem the _cmd.exe /c_ return code!
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
|
||||||
exit /b 1
|
:omega
|
||||||
|
|
||||||
:mainEnd
|
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
|
||||||
|
|
||||||
:omega
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -9,6 +9,9 @@ THE_MVN_REPO=${MVN_REPO:-${1:-$DEFAULT_MVN_REPO}}
|
|||||||
MVN_HTTP=0
|
MVN_HTTP=0
|
||||||
DEFAULT_SDK_VERSION=$(grep sdkVersion ${THIS_DIR}/../gradle.properties | cut -d"=" -f2)
|
DEFAULT_SDK_VERSION=$(grep sdkVersion ${THIS_DIR}/../gradle.properties | cut -d"=" -f2)
|
||||||
SDK_VERSION=${OVERRIDE_SDK_VERSION:-${DEFAULT_SDK_VERSION}}
|
SDK_VERSION=${OVERRIDE_SDK_VERSION:-${DEFAULT_SDK_VERSION}}
|
||||||
|
RN_VERSION=$(jq -r '.version' ${THIS_DIR}/../../node_modules/react-native/package.json)
|
||||||
|
JSC_VERSION="r"$(jq -r '.dependencies."jsc-android"' ${THIS_DIR}/../../node_modules/react-native/package.json | cut -d . -f 1 | cut -c 2-)
|
||||||
|
DO_GIT_TAG=${GIT_TAG:-0}
|
||||||
|
|
||||||
if [[ $THE_MVN_REPO == http* ]]; then
|
if [[ $THE_MVN_REPO == http* ]]; then
|
||||||
MVN_HTTP=1
|
MVN_HTTP=1
|
||||||
@@ -22,7 +25,60 @@ export MVN_REPO=$THE_MVN_REPO
|
|||||||
echo "Releasing Jitsi Meet SDK ${SDK_VERSION}"
|
echo "Releasing Jitsi Meet SDK ${SDK_VERSION}"
|
||||||
echo "Using ${MVN_REPO} as the Maven repo"
|
echo "Using ${MVN_REPO} as the Maven repo"
|
||||||
|
|
||||||
if [[ $MVN_HTTP == 0 ]]; then
|
if [[ $MVN_HTTP == 1 ]]; then
|
||||||
|
# Push React Native
|
||||||
|
echo "Pushing React Native ${RN_VERSION} to the Maven repo"
|
||||||
|
pushd ${THIS_DIR}/../../node_modules/react-native/android/com/facebook/react/react-native/${RN_VERSION}
|
||||||
|
mvn \
|
||||||
|
deploy:deploy-file \
|
||||||
|
-Durl=${MVN_REPO} \
|
||||||
|
-DrepositoryId=${MVN_REPO_ID} \
|
||||||
|
-Dfile=react-native-${RN_VERSION}.aar \
|
||||||
|
-Dpackaging=aar \
|
||||||
|
-DgeneratePom=false \
|
||||||
|
-DpomFile=react-native-${RN_VERSION}.pom || true
|
||||||
|
popd
|
||||||
|
# Push JSC
|
||||||
|
echo "Pushing JSC ${JSC_VERSION} to the Maven repo"
|
||||||
|
pushd ${THIS_DIR}/../../node_modules/jsc-android/dist/org/webkit/android-jsc/${JSC_VERSION}
|
||||||
|
mvn \
|
||||||
|
deploy:deploy-file \
|
||||||
|
-Durl=${MVN_REPO} \
|
||||||
|
-DrepositoryId=${MVN_REPO_ID} \
|
||||||
|
-Dfile=android-jsc-${JSC_VERSION}.aar \
|
||||||
|
-Dpackaging=aar \
|
||||||
|
-DgeneratePom=false \
|
||||||
|
-DpomFile=android-jsc-${JSC_VERSION}.pom || true
|
||||||
|
popd
|
||||||
|
else
|
||||||
|
# Push React Native, if necessary
|
||||||
|
if [[ ! -d ${MVN_REPO}/com/facebook/react/react-native/${RN_VERSION} ]]; then
|
||||||
|
echo "Pushing React Native ${RN_VERSION} to the Maven repo"
|
||||||
|
pushd ${THIS_DIR}/../../node_modules/react-native/android/com/facebook/react/react-native/${RN_VERSION}
|
||||||
|
mvn \
|
||||||
|
deploy:deploy-file \
|
||||||
|
-Durl=${MVN_REPO} \
|
||||||
|
-Dfile=react-native-${RN_VERSION}.aar \
|
||||||
|
-Dpackaging=aar \
|
||||||
|
-DgeneratePom=false \
|
||||||
|
-DpomFile=react-native-${RN_VERSION}.pom
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Push JSC, if necessary
|
||||||
|
if [[ ! -d ${MVN_REPO}/org/webkit/android-jsc/${JSC_VERSION} ]]; then
|
||||||
|
echo "Pushing JSC ${JSC_VERSION} to the Maven repo"
|
||||||
|
pushd ${THIS_DIR}/../../node_modules/jsc-android/dist/org/webkit/android-jsc/${JSC_VERSION}
|
||||||
|
mvn \
|
||||||
|
deploy:deploy-file \
|
||||||
|
-Durl=${MVN_REPO} \
|
||||||
|
-Dfile=android-jsc-${JSC_VERSION}.aar \
|
||||||
|
-Dpackaging=aar \
|
||||||
|
-DgeneratePom=false \
|
||||||
|
-DpomFile=android-jsc-${JSC_VERSION}.pom
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|
||||||
# Check if an SDK with that same version has already been released
|
# Check if an SDK with that same version has already been released
|
||||||
if [[ -d ${MVN_REPO}/org/jitsi/react/jitsi-meet-sdk/${SDK_VERSION} ]]; then
|
if [[ -d ${MVN_REPO}/org/jitsi/react/jitsi-meet-sdk/${SDK_VERSION} ]]; then
|
||||||
echo "There is already a release with that version in the Maven repo!"
|
echo "There is already a release with that version in the Maven repo!"
|
||||||
@@ -33,17 +89,20 @@ fi
|
|||||||
# Now build and publish the Jitsi Meet SDK and its dependencies
|
# Now build and publish the Jitsi Meet SDK and its dependencies
|
||||||
echo "Building and publishing the Jitsi Meet SDK"
|
echo "Building and publishing the Jitsi Meet SDK"
|
||||||
pushd ${THIS_DIR}/../
|
pushd ${THIS_DIR}/../
|
||||||
./gradlew clean
|
./gradlew clean
|
||||||
./gradlew assembleRelease
|
./gradlew assembleRelease
|
||||||
./gradlew publish
|
./gradlew publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
# The artifacts are now on the Maven repo, commit them
|
if [[ $DO_GIT_TAG == 1 ]]; then
|
||||||
if [[ $MVN_HTTP == 0 ]]; then
|
# The artifacts are now on the Maven repo, commit them
|
||||||
pushd ${MVN_REPO_PATH}
|
pushd ${MVN_REPO_PATH}
|
||||||
git add -A .
|
git add -A .
|
||||||
git commit -m "Jitsi Meet SDK + dependencies: ${SDK_VERSION}"
|
git commit -m "Jitsi Meet SDK + dependencies: ${SDK_VERSION}"
|
||||||
popd
|
popd
|
||||||
|
|
||||||
|
# Tag the release
|
||||||
|
git tag android-sdk-${SDK_VERSION}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Done!
|
# Done!
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
|
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
|
||||||
|
|
||||||
exec ${THIS_DIR}/../../node_modules/react-native/scripts/packager.sh --reset-cache
|
exec ${THIS_DIR}/../../node_modules/react-native/scripts/launchPackager.command --reset-cache
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ apply plugin: 'maven-publish'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||||
ndkVersion rootProject.ext.ndkVersion
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
buildConfigField "String", "SDK_VERSION", "\"$sdkVersion\""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -27,72 +25,53 @@ android {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
java {
|
java {
|
||||||
|
if (rootProject.ext.libreBuild) {
|
||||||
|
srcDir "src"
|
||||||
|
exclude "**/AmplitudeModule.java"
|
||||||
|
}
|
||||||
exclude "test/"
|
exclude "test/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
namespace 'org.jitsi.meet.sdk'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation 'androidx.appcompat:appcompat:1.4.1'
|
|
||||||
implementation 'androidx.fragment:fragment:1.4.1'
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
|
implementation 'androidx.fragment:fragment:1.2.5'
|
||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||||
|
|
||||||
api "com.facebook.react:react-android:$rootProject.ext.rnVersion"
|
//noinspection GradleDynamicVersion
|
||||||
api "com.facebook.react:hermes-android:$rootProject.ext.rnVersion"
|
api 'com.facebook.react:react-native:+'
|
||||||
|
|
||||||
//noinspection GradleDynamicVersion
|
//noinspection GradleDynamicVersion
|
||||||
implementation 'org.webkit:android-jsc:+'
|
implementation 'org.webkit:android-jsc:+'
|
||||||
|
|
||||||
implementation 'com.facebook.fresco:animated-gif:2.5.0'
|
implementation 'com.dropbox.core:dropbox-core-sdk:3.0.8'
|
||||||
implementation 'com.dropbox.core:dropbox-core-sdk:4.0.1'
|
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||||
implementation 'com.jakewharton.timber:timber:5.0.1'
|
|
||||||
implementation 'com.squareup.duktape:duktape-android:1.3.0'
|
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-amplitude')
|
implementation 'com.amplitude:android-sdk:2.14.1'
|
||||||
implementation project(':react-native-giphy')
|
implementation(project(":react-native-google-signin")) {
|
||||||
implementation(project(':react-native-google-signin')) {
|
|
||||||
exclude group: 'com.google.android.gms'
|
exclude group: 'com.google.android.gms'
|
||||||
exclude group: 'androidx'
|
exclude group: 'androidx'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation project(':react-native-async-storage')
|
|
||||||
implementation project(':react-native-background-timer')
|
implementation project(':react-native-background-timer')
|
||||||
implementation project(':react-native-calendar-events')
|
implementation project(':react-native-calendar-events')
|
||||||
implementation project(':react-native-community_clipboard')
|
implementation project(':react-native-community-async-storage')
|
||||||
implementation project(':react-native-community_netinfo')
|
implementation project(':react-native-community_netinfo')
|
||||||
implementation project(':react-native-default-preference')
|
implementation project(':react-native-default-preference')
|
||||||
implementation(project(':react-native-device-info')) {
|
implementation project(':react-native-immersive')
|
||||||
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-mode')
|
|
||||||
implementation project(':react-native-keep-awake')
|
implementation project(':react-native-keep-awake')
|
||||||
implementation project(':react-native-orientation-locker')
|
implementation project(':react-native-linear-gradient')
|
||||||
implementation project(':react-native-pager-view')
|
|
||||||
implementation project(':react-native-performance')
|
|
||||||
implementation project(':react-native-safe-area-context')
|
|
||||||
implementation project(':react-native-screens')
|
|
||||||
implementation project(':react-native-slider')
|
|
||||||
implementation project(':react-native-sound')
|
implementation project(':react-native-sound')
|
||||||
implementation project(':react-native-splash-screen')
|
|
||||||
implementation project(':react-native-svg')
|
implementation project(':react-native-svg')
|
||||||
implementation project(':react-native-video')
|
implementation project(':react-native-webrtc')
|
||||||
implementation project(':react-native-webview')
|
implementation project(':react-native-webview')
|
||||||
|
implementation project(':react-native-splash-screen')
|
||||||
// Use `api` here so consumers can use WebRTCModuleOptions.
|
|
||||||
api project(':react-native-webrtc')
|
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
}
|
}
|
||||||
@@ -142,7 +121,8 @@ android.libraryVariants.all { def variant ->
|
|||||||
// Run the bundler
|
// Run the bundler
|
||||||
commandLine(
|
commandLine(
|
||||||
"node",
|
"node",
|
||||||
"node_modules/react-native/scripts/bundle.js",
|
"node_modules/react-native/local-cli/cli.js",
|
||||||
|
"bundle",
|
||||||
"--platform", "android",
|
"--platform", "android",
|
||||||
"--dev", "${devEnabled}",
|
"--dev", "${devEnabled}",
|
||||||
"--reset-cache",
|
"--reset-cache",
|
||||||
@@ -170,9 +150,16 @@ android.libraryVariants.all { def variant ->
|
|||||||
// Bundle sounds
|
// Bundle sounds
|
||||||
//
|
//
|
||||||
copy {
|
copy {
|
||||||
from("${projectDir}/../../sounds")
|
from("${projectDir}/../../sounds/incomingMessage.wav")
|
||||||
include("*.wav")
|
from("${projectDir}/../../sounds/joined.wav")
|
||||||
include("*.mp3")
|
from("${projectDir}/../../sounds/left.wav")
|
||||||
|
from("${projectDir}/../../sounds/liveStreamingOn.mp3")
|
||||||
|
from("${projectDir}/../../sounds/liveStreamingOff.mp3")
|
||||||
|
from("${projectDir}/../../sounds/outgoingRinging.wav")
|
||||||
|
from("${projectDir}/../../sounds/outgoingStart.wav")
|
||||||
|
from("${projectDir}/../../sounds/recordingOn.mp3")
|
||||||
|
from("${projectDir}/../../sounds/recordingOff.mp3")
|
||||||
|
from("${projectDir}/../../sounds/rejected.wav")
|
||||||
into("${assetsDir}/sounds")
|
into("${assetsDir}/sounds")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +210,7 @@ publishing {
|
|||||||
def groupId = it.moduleGroup
|
def groupId = it.moduleGroup
|
||||||
def artifactId = it.moduleName
|
def artifactId = it.moduleName
|
||||||
|
|
||||||
if (artifactId.startsWith('react-native-')) {
|
if (artifactId.startsWith('react-native-') && groupId.equals('jitsi-meet')) {
|
||||||
groupId = rootProject.ext.moduleGroupId
|
groupId = rootProject.ext.moduleGroupId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
package="org.jitsi.meet.sdk">
|
||||||
<!-- XXX ACCESS_NETWORK_STATE is required by WebRTC. -->
|
<!-- XXX ACCESS_NETWORK_STATE is required by WebRTC. -->
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
@@ -9,13 +9,10 @@
|
|||||||
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
|
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
|
||||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
<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.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
|
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
||||||
|
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:glEsVersion="0x00020000"
|
android:glEsVersion="0x00020000"
|
||||||
@@ -33,17 +30,16 @@
|
|||||||
android:supportsRtl="true">
|
android:supportsRtl="true">
|
||||||
<activity
|
<activity
|
||||||
android:name=".JitsiMeetActivity"
|
android:name=".JitsiMeetActivity"
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:theme="@style/JitsiMeetActivityStyle"
|
|
||||||
android:resizeableActivity="true"
|
android:resizeableActivity="true"
|
||||||
android:supportsPictureInPicture="true"
|
android:supportsPictureInPicture="true"
|
||||||
android:windowSoftInputMode="adjustResize"/>
|
android:windowSoftInputMode="adjustResize"></activity>
|
||||||
|
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".ConnectionService"
|
android:name=".ConnectionService"
|
||||||
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
|
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.telecom.ConnectionService" />
|
<action android:name="android.telecom.ConnectionService" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -51,22 +47,7 @@
|
|||||||
|
|
||||||
<service
|
<service
|
||||||
android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService"
|
android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService"
|
||||||
android:foregroundServiceType="mediaPlayback|microphone" />
|
android:foregroundServiceType="mediaProjection" />
|
||||||
|
|
||||||
<provider
|
|
||||||
android:name="com.reactnativecommunity.webview.RNCWebViewFileProvider"
|
|
||||||
android:authorities="${applicationId}.fileprovider"
|
|
||||||
android:enabled="false"
|
|
||||||
tools:replace="android:authorities">
|
|
||||||
</provider>
|
|
||||||
<provider
|
|
||||||
android:name="androidx.startup.InitializationProvider"
|
|
||||||
android:authorities="${applicationId}.androidx-startup"
|
|
||||||
android:exported="false">
|
|
||||||
<meta-data android:name="org.jitsi.meet.sdk.JitsiInitializer"
|
|
||||||
android:value="androidx.startup" />
|
|
||||||
</provider>
|
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright @ 2019-present 8x8, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jitsi.meet.sdk;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||||
|
import com.facebook.react.bridge.ReactMethod;
|
||||||
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
|
|
||||||
|
import com.amplitude.api.Amplitude;
|
||||||
|
import com.facebook.react.module.annotations.ReactModule;
|
||||||
|
|
||||||
|
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the react-native module for the Amplitude integration.
|
||||||
|
*/
|
||||||
|
@ReactModule(name = AmplitudeModule.NAME)
|
||||||
|
class AmplitudeModule
|
||||||
|
extends ReactContextBaseJavaModule {
|
||||||
|
|
||||||
|
public static final String NAME = "Amplitude";
|
||||||
|
public static final String JITSI_PREFERENCES = "jitsi-preferences";
|
||||||
|
public static final String AMPLITUDE_DEVICE_ID_KEY = "amplitudeDeviceId";
|
||||||
|
|
||||||
|
public AmplitudeModule(ReactApplicationContext reactContext) {
|
||||||
|
super(reactContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the Amplitude SDK.
|
||||||
|
*
|
||||||
|
* @param instanceName The name of the Amplitude instance. Should
|
||||||
|
* be used only for multi-project logging.
|
||||||
|
* @param apiKey The API_KEY of the Amplitude project.
|
||||||
|
*/
|
||||||
|
@ReactMethod
|
||||||
|
@SuppressLint("HardwareIds")
|
||||||
|
public void init(String instanceName, String apiKey) {
|
||||||
|
Amplitude.getInstance(instanceName).initialize(getCurrentActivity(), apiKey);
|
||||||
|
|
||||||
|
// Set the device ID to something consistent.
|
||||||
|
SharedPreferences sharedPreferences = getReactApplicationContext().getSharedPreferences(JITSI_PREFERENCES, Context.MODE_PRIVATE);
|
||||||
|
String android_id = sharedPreferences.getString(AMPLITUDE_DEVICE_ID_KEY, "");
|
||||||
|
if (!TextUtils.isEmpty(android_id)) {
|
||||||
|
Amplitude.getInstance(instanceName).setDeviceId(android_id);
|
||||||
|
} else {
|
||||||
|
String amplitudeId = Amplitude.getInstance(instanceName).getDeviceId();
|
||||||
|
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||||
|
editor.putString(JITSI_PREFERENCES, amplitudeId).apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the user ID for an Amplitude instance.
|
||||||
|
*
|
||||||
|
* @param instanceName The name of the Amplitude instance.
|
||||||
|
* @param userId The new value for the user ID.
|
||||||
|
*/
|
||||||
|
@ReactMethod
|
||||||
|
public void setUserId(String instanceName, String userId) {
|
||||||
|
Amplitude.getInstance(instanceName).setUserId(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the user properties for an Amplitude instance.
|
||||||
|
*
|
||||||
|
* @param instanceName The name of the Amplitude instance.
|
||||||
|
* @param userProps JSON string with user properties to be set.
|
||||||
|
*/
|
||||||
|
@ReactMethod
|
||||||
|
public void setUserProperties(String instanceName, ReadableMap userProps) {
|
||||||
|
if (userProps != null) {
|
||||||
|
Amplitude.getInstance(instanceName).setUserProperties(
|
||||||
|
new JSONObject(userProps.toHashMap()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log an analytics event.
|
||||||
|
*
|
||||||
|
* @param instanceName The name of the Amplitude instance.
|
||||||
|
* @param eventType The event type.
|
||||||
|
* @param eventPropsString JSON string with the event properties.
|
||||||
|
*/
|
||||||
|
@ReactMethod
|
||||||
|
public void logEvent(String instanceName, String eventType, String eventPropsString) {
|
||||||
|
try {
|
||||||
|
JSONObject eventProps = new JSONObject(eventPropsString);
|
||||||
|
Amplitude.getInstance(instanceName).logEvent(eventType, eventProps);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
JitsiMeetLogger.e(e, "Error logging event");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright @ 2017-present 8x8, Inc.
|
* Copyright @ 2017-present Atlassian Pty Ltd
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -25,7 +25,6 @@ import com.facebook.react.bridge.ReactApplicationContext;
|
|||||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||||
import com.facebook.react.module.annotations.ReactModule;
|
import com.facebook.react.module.annotations.ReactModule;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -33,11 +32,7 @@ import java.util.Map;
|
|||||||
class AppInfoModule
|
class AppInfoModule
|
||||||
extends ReactContextBaseJavaModule {
|
extends ReactContextBaseJavaModule {
|
||||||
|
|
||||||
private static final String BUILD_CONFIG = "org.jitsi.meet.sdk.BuildConfig";
|
|
||||||
public static final String NAME = "AppInfo";
|
public static final String NAME = "AppInfo";
|
||||||
public static final boolean GOOGLE_SERVICES_ENABLED = getGoogleServicesEnabled();
|
|
||||||
public static final boolean LIBRE_BUILD = getLibreBuild();
|
|
||||||
public static final String SDK_VERSION = getSdkVersion();
|
|
||||||
|
|
||||||
public AppInfoModule(ReactApplicationContext reactContext) {
|
public AppInfoModule(ReactApplicationContext reactContext) {
|
||||||
super(reactContext);
|
super(reactContext);
|
||||||
@@ -80,9 +75,8 @@ class AppInfoModule
|
|||||||
constants.put(
|
constants.put(
|
||||||
"version",
|
"version",
|
||||||
packageInfo == null ? "" : packageInfo.versionName);
|
packageInfo == null ? "" : packageInfo.versionName);
|
||||||
constants.put("sdkVersion", SDK_VERSION);
|
constants.put("LIBRE_BUILD", BuildConfig.LIBRE_BUILD);
|
||||||
constants.put("LIBRE_BUILD", LIBRE_BUILD);
|
constants.put("GOOGLE_SERVICES_ENABLED", BuildConfig.GOOGLE_SERVICES_ENABLED);
|
||||||
constants.put("GOOGLE_SERVICES_ENABLED", GOOGLE_SERVICES_ENABLED);
|
|
||||||
|
|
||||||
return constants;
|
return constants;
|
||||||
}
|
}
|
||||||
@@ -91,60 +85,4 @@ class AppInfoModule
|
|||||||
public String getName() {
|
public String getName() {
|
||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if libre google services object is null based on build configuration.
|
|
||||||
*/
|
|
||||||
private static boolean getGoogleServicesEnabled() {
|
|
||||||
Object googleServicesEnabled = getBuildConfigValue("GOOGLE_SERVICES_ENABLED");
|
|
||||||
|
|
||||||
if (googleServicesEnabled !=null) {
|
|
||||||
return (Boolean) googleServicesEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if libre build field is null based on build configuration.
|
|
||||||
*/
|
|
||||||
private static boolean getLibreBuild() {
|
|
||||||
Object libreBuild = getBuildConfigValue("LIBRE_BUILD");
|
|
||||||
|
|
||||||
if (libreBuild !=null) {
|
|
||||||
return (Boolean) libreBuild;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the SDK version.
|
|
||||||
*/
|
|
||||||
private static String getSdkVersion() {
|
|
||||||
Object sdkVersion = getBuildConfigValue("SDK_VERSION");
|
|
||||||
|
|
||||||
if (sdkVersion !=null) {
|
|
||||||
return (String) sdkVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets build config value of a certain field.
|
|
||||||
*
|
|
||||||
* @param fieldName Field from build config.
|
|
||||||
*/
|
|
||||||
private static Object getBuildConfigValue(String fieldName) {
|
|
||||||
try {
|
|
||||||
Class<?> c = Class.forName(BUILD_CONFIG);
|
|
||||||
Field f = c.getDeclaredField(fieldName);
|
|
||||||
f.setAccessible(true);
|
|
||||||
return f.get(null);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,10 @@ package org.jitsi.meet.sdk;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
|
import android.os.Build;
|
||||||
import android.telecom.CallAudioState;
|
import android.telecom.CallAudioState;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
import com.facebook.react.bridge.ReactContext;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@@ -33,6 +32,7 @@ import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
|||||||
* {@link AudioModeModule.AudioDeviceHandlerInterface} module implementing device handling for
|
* {@link AudioModeModule.AudioDeviceHandlerInterface} module implementing device handling for
|
||||||
* Android versions >= O when ConnectionService is enabled.
|
* Android versions >= O when ConnectionService is enabled.
|
||||||
*/
|
*/
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
class AudioDeviceHandlerConnectionService implements
|
class AudioDeviceHandlerConnectionService implements
|
||||||
AudioModeModule.AudioDeviceHandlerInterface,
|
AudioModeModule.AudioDeviceHandlerInterface,
|
||||||
RNConnectionService.CallAudioStateListener {
|
RNConnectionService.CallAudioStateListener {
|
||||||
@@ -49,8 +49,6 @@ class AudioDeviceHandlerConnectionService implements
|
|||||||
*/
|
*/
|
||||||
private AudioModeModule module;
|
private AudioModeModule module;
|
||||||
|
|
||||||
private RNConnectionService rcs;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts any of the "DEVICE_" constants into the corresponding
|
* Converts any of the "DEVICE_" constants into the corresponding
|
||||||
* {@link android.telecom.CallAudioState} "ROUTE_" number.
|
* {@link android.telecom.CallAudioState} "ROUTE_" number.
|
||||||
@@ -143,8 +141,8 @@ class AudioDeviceHandlerConnectionService implements
|
|||||||
JitsiMeetLogger.i("Using " + TAG + " as the audio device handler");
|
JitsiMeetLogger.i("Using " + TAG + " as the audio device handler");
|
||||||
|
|
||||||
module = audioModeModule;
|
module = audioModeModule;
|
||||||
rcs = module.getContext().getNativeModule(RNConnectionService.class);
|
|
||||||
|
|
||||||
|
RNConnectionService rcs = ReactInstanceManagerHolder.getNativeModule(RNConnectionService.class);
|
||||||
if (rcs != null) {
|
if (rcs != null) {
|
||||||
rcs.setCallAudioStateListener(this);
|
rcs.setCallAudioStateListener(this);
|
||||||
} else {
|
} else {
|
||||||
@@ -154,9 +152,9 @@ class AudioDeviceHandlerConnectionService implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
|
RNConnectionService rcs = ReactInstanceManagerHolder.getNativeModule(RNConnectionService.class);
|
||||||
if (rcs != null) {
|
if (rcs != null) {
|
||||||
rcs.setCallAudioStateListener(null);
|
rcs.setCallAudioStateListener(null);
|
||||||
rcs = null;
|
|
||||||
} else {
|
} else {
|
||||||
JitsiMeetLogger.w(TAG + " Couldn't set call audio state listener, module is null");
|
JitsiMeetLogger.w(TAG + " Couldn't set call audio state listener, module is null");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,7 @@
|
|||||||
|
|
||||||
package org.jitsi.meet.sdk;
|
package org.jitsi.meet.sdk;
|
||||||
|
|
||||||
import android.media.AudioAttributes;
|
|
||||||
import android.media.AudioDeviceInfo;
|
import android.media.AudioDeviceInfo;
|
||||||
import android.media.AudioFocusRequest;
|
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -44,12 +42,6 @@ class AudioDeviceHandlerGeneric implements
|
|||||||
*/
|
*/
|
||||||
private AudioModeModule module;
|
private AudioModeModule module;
|
||||||
|
|
||||||
/**
|
|
||||||
* Constant defining a Hearing Aid. Only available on API level >= 28.
|
|
||||||
* The value of: AudioDeviceInfo.TYPE_HEARING_AID
|
|
||||||
*/
|
|
||||||
private static final int TYPE_HEARING_AID = 23;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constant defining a USB headset. Only available on API level >= 26.
|
* Constant defining a USB headset. Only available on API level >= 26.
|
||||||
* The value of: AudioDeviceInfo.TYPE_USB_HEADSET
|
* The value of: AudioDeviceInfo.TYPE_USB_HEADSET
|
||||||
@@ -68,7 +60,7 @@ class AudioDeviceHandlerGeneric implements
|
|||||||
private AudioManager audioManager;
|
private AudioManager audioManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Runnable} for running audio device detection in the audio thread.
|
* {@link Runnable} for running audio device detection the main thread.
|
||||||
* This is only used on Android >= M.
|
* This is only used on Android >= M.
|
||||||
*/
|
*/
|
||||||
private final Runnable onAudioDeviceChangeRunner = new Runnable() {
|
private final Runnable onAudioDeviceChangeRunner = new Runnable() {
|
||||||
@@ -86,12 +78,10 @@ class AudioDeviceHandlerGeneric implements
|
|||||||
devices.add(AudioModeModule.DEVICE_EARPIECE);
|
devices.add(AudioModeModule.DEVICE_EARPIECE);
|
||||||
break;
|
break;
|
||||||
case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
|
case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
|
||||||
case AudioDeviceInfo.TYPE_HDMI:
|
|
||||||
devices.add(AudioModeModule.DEVICE_SPEAKER);
|
devices.add(AudioModeModule.DEVICE_SPEAKER);
|
||||||
break;
|
break;
|
||||||
case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
|
case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
|
||||||
case AudioDeviceInfo.TYPE_WIRED_HEADSET:
|
case AudioDeviceInfo.TYPE_WIRED_HEADSET:
|
||||||
case TYPE_HEARING_AID:
|
|
||||||
case TYPE_USB_HEADSET:
|
case TYPE_USB_HEADSET:
|
||||||
devices.add(AudioModeModule.DEVICE_HEADPHONES);
|
devices.add(AudioModeModule.DEVICE_HEADPHONES);
|
||||||
break;
|
break;
|
||||||
@@ -152,7 +142,7 @@ class AudioDeviceHandlerGeneric implements
|
|||||||
// Some other application potentially stole our audio focus
|
// Some other application potentially stole our audio focus
|
||||||
// temporarily. Restore our mode.
|
// temporarily. Restore our mode.
|
||||||
if (audioFocusLost) {
|
if (audioFocusLost) {
|
||||||
module.resetAudioRoute();
|
module.updateAudioRoute();
|
||||||
}
|
}
|
||||||
audioFocusLost = false;
|
audioFocusLost = false;
|
||||||
break;
|
break;
|
||||||
@@ -226,19 +216,8 @@ class AudioDeviceHandlerGeneric implements
|
|||||||
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
||||||
audioManager.setMicrophoneMute(false);
|
audioManager.setMicrophoneMute(false);
|
||||||
|
|
||||||
int gotFocus = audioManager.requestAudioFocus(new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
|
if (audioManager.requestAudioFocus(this, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN)
|
||||||
.setAudioAttributes(
|
== AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
|
||||||
new AudioAttributes.Builder()
|
|
||||||
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
|
|
||||||
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.setAcceptsDelayedFocusGain(true)
|
|
||||||
.setOnAudioFocusChangeListener(this)
|
|
||||||
.build()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (gotFocus == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
|
|
||||||
JitsiMeetLogger.w(TAG + " Audio focus request failed");
|
JitsiMeetLogger.w(TAG + " Audio focus request failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,22 +16,18 @@
|
|||||||
|
|
||||||
package org.jitsi.meet.sdk;
|
package org.jitsi.meet.sdk;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
import com.facebook.react.bridge.Arguments;
|
import com.facebook.react.bridge.Arguments;
|
||||||
import com.facebook.react.bridge.Promise;
|
import com.facebook.react.bridge.Promise;
|
||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||||
import com.facebook.react.bridge.ReactContext;
|
|
||||||
import com.facebook.react.bridge.ReactMethod;
|
import com.facebook.react.bridge.ReactMethod;
|
||||||
import com.facebook.react.bridge.WritableArray;
|
import com.facebook.react.bridge.WritableArray;
|
||||||
import com.facebook.react.bridge.WritableMap;
|
import com.facebook.react.bridge.WritableMap;
|
||||||
import com.facebook.react.module.annotations.ReactModule;
|
import com.facebook.react.module.annotations.ReactModule;
|
||||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
||||||
|
|
||||||
|
|
||||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||||
|
|
||||||
@@ -82,10 +78,12 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
|||||||
/**
|
/**
|
||||||
* Whether or not the ConnectionService is used for selecting audio devices.
|
* Whether or not the ConnectionService is used for selecting audio devices.
|
||||||
*/
|
*/
|
||||||
private static boolean useConnectionService_ = true;
|
|
||||||
|
private static final boolean supportsConnectionService = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
||||||
|
private static boolean useConnectionService_ = supportsConnectionService;
|
||||||
|
|
||||||
static boolean useConnectionService() {
|
static boolean useConnectionService() {
|
||||||
return useConnectionService_;
|
return supportsConnectionService && useConnectionService_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -136,11 +134,6 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
|||||||
*/
|
*/
|
||||||
private String userSelectedDevice;
|
private String userSelectedDevice;
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not audio is disabled.
|
|
||||||
*/
|
|
||||||
private boolean audioDisabled;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new module instance. There shall be a single instance of
|
* Initializes a new module instance. There shall be a single instance of
|
||||||
* this module throughout the lifetime of the application.
|
* this module throughout the lifetime of the application.
|
||||||
@@ -154,16 +147,6 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
|||||||
audioManager = (AudioManager)reactContext.getSystemService(Context.AUDIO_SERVICE);
|
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.
|
* Gets a mapping with the constants this module is exporting.
|
||||||
*
|
*
|
||||||
@@ -201,7 +184,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
|||||||
deviceInfo.putBoolean("selected", device.equals(selectedDevice));
|
deviceInfo.putBoolean("selected", device.equals(selectedDevice));
|
||||||
data.pushMap(deviceInfo);
|
data.pushMap(deviceInfo);
|
||||||
}
|
}
|
||||||
getContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(DEVICE_CHANGE_EVENT, data);
|
ReactInstanceManagerHolder.emitEvent(DEVICE_CHANGE_EVENT, data);
|
||||||
JitsiMeetLogger.i(TAG + " Updating audio device list");
|
JitsiMeetLogger.i(TAG + " Updating audio device list");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -217,10 +200,6 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
|||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReactContext getContext(){
|
|
||||||
return this.getReactApplicationContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the audio device handler module. This function is called *after* all Catalyst
|
* Initializes the audio device handler module. This function is called *after* all Catalyst
|
||||||
* modules have been created, and that's why we use it, because {@link AudioDeviceHandlerConnectionService}
|
* modules have been created, and that's why we use it, because {@link AudioDeviceHandlerConnectionService}
|
||||||
@@ -241,12 +220,6 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
|||||||
audioDeviceHandler.stop();
|
audioDeviceHandler.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
audioDeviceHandler = null;
|
|
||||||
|
|
||||||
if (audioDisabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useConnectionService()) {
|
if (useConnectionService()) {
|
||||||
audioDeviceHandler = new AudioDeviceHandlerConnectionService(audioManager);
|
audioDeviceHandler = new AudioDeviceHandlerConnectionService(audioManager);
|
||||||
} else {
|
} else {
|
||||||
@@ -283,33 +256,12 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
|||||||
if (mode != -1) {
|
if (mode != -1) {
|
||||||
JitsiMeetLogger.i(TAG + " User selected device set to: " + device);
|
JitsiMeetLogger.i(TAG + " User selected device set to: " + device);
|
||||||
userSelectedDevice = device;
|
userSelectedDevice = device;
|
||||||
updateAudioRoute(mode, false);
|
updateAudioRoute(mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReactMethod
|
|
||||||
public void setDisabled(final boolean disabled, final Promise promise) {
|
|
||||||
if (audioDisabled == disabled) {
|
|
||||||
promise.resolve(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JitsiMeetLogger.i(TAG + " audio disabled: " + disabled);
|
|
||||||
|
|
||||||
audioDisabled = disabled;
|
|
||||||
setAudioDeviceHandler();
|
|
||||||
|
|
||||||
if (disabled) {
|
|
||||||
mode = -1;
|
|
||||||
availableDevices.clear();
|
|
||||||
resetSelectedDevice();
|
|
||||||
}
|
|
||||||
|
|
||||||
promise.resolve(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public method to set the current audio mode.
|
* Public method to set the current audio mode.
|
||||||
*
|
*
|
||||||
@@ -319,32 +271,18 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
|||||||
*/
|
*/
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void setMode(final int mode, final Promise promise) {
|
public void setMode(final int mode, final Promise promise) {
|
||||||
if (audioDisabled) {
|
if (mode != DEFAULT && mode != AUDIO_CALL && mode != VIDEO_CALL) {
|
||||||
promise.resolve(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode < DEFAULT || mode > VIDEO_CALL) {
|
|
||||||
promise.reject("setMode", "Invalid audio mode " + mode);
|
promise.reject("setMode", "Invalid audio mode " + mode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Activity currentActivity = getCurrentActivity();
|
|
||||||
if (currentActivity != null) {
|
|
||||||
if (mode == DEFAULT) {
|
|
||||||
currentActivity.setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
|
|
||||||
} else {
|
|
||||||
currentActivity.setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
runInAudioThread(new Runnable() {
|
runInAudioThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
boolean success;
|
boolean success;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
success = updateAudioRoute(mode, false);
|
success = updateAudioRoute(mode);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
success = false;
|
success = false;
|
||||||
JitsiMeetLogger.e(e, TAG + " Failed to update audio route for mode: " + mode);
|
JitsiMeetLogger.e(e, TAG + " Failed to update audio route for mode: " + mode);
|
||||||
@@ -383,7 +321,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
|||||||
* @return {@code true} if the audio route was updated successfully;
|
* @return {@code true} if the audio route was updated successfully;
|
||||||
* {@code false}, otherwise.
|
* {@code false}, otherwise.
|
||||||
*/
|
*/
|
||||||
private boolean updateAudioRoute(int mode, boolean force) {
|
private boolean updateAudioRoute(int mode) {
|
||||||
JitsiMeetLogger.i(TAG + " Update audio route for mode: " + mode);
|
JitsiMeetLogger.i(TAG + " Update audio route for mode: " + mode);
|
||||||
|
|
||||||
if (!audioDeviceHandler.setMode(mode)) {
|
if (!audioDeviceHandler.setMode(mode)) {
|
||||||
@@ -418,7 +356,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
|||||||
|
|
||||||
// If the previously selected device and the current default one
|
// If the previously selected device and the current default one
|
||||||
// match, do nothing.
|
// match, do nothing.
|
||||||
if (!force && selectedDevice != null && selectedDevice.equals(audioDevice)) {
|
if (selectedDevice != null && selectedDevice.equals(audioDevice)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -483,16 +421,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
|
|||||||
*/
|
*/
|
||||||
void updateAudioRoute() {
|
void updateAudioRoute() {
|
||||||
if (mode != -1) {
|
if (mode != -1) {
|
||||||
updateAudioRoute(mode, false);
|
updateAudioRoute(mode);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Re-sets the current audio route. Needed when focus is lost and regained.
|
|
||||||
*/
|
|
||||||
void resetAudioRoute() {
|
|
||||||
if (mode != -1) {
|
|
||||||
updateAudioRoute(mode, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
221
android/sdk/src/main/java/org/jitsi/meet/sdk/BaseReactView.java
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
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}.
|
||||||
|
*/
|
||||||
|
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}.
|
||||||
|
*/
|
||||||
|
protected abstract void onExternalAPIEvent(String name, ReadableMap data);
|
||||||
|
|
||||||
|
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}.
|
||||||
|
*/
|
||||||
|
public void setListener(ListenerT listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
package org.jitsi.meet.sdk;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps the name and extra data for events that were broadcasted locally.
|
|
||||||
*/
|
|
||||||
public class BroadcastAction {
|
|
||||||
private static final String TAG = BroadcastAction.class.getSimpleName();
|
|
||||||
|
|
||||||
private final Type type;
|
|
||||||
private final Bundle data;
|
|
||||||
|
|
||||||
public BroadcastAction(Intent intent) {
|
|
||||||
this.type = Type.buildTypeFromAction(intent.getAction());
|
|
||||||
this.data = intent.getExtras();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type getType() {
|
|
||||||
return this.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bundle getData() {
|
|
||||||
return this.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Type {
|
|
||||||
SET_AUDIO_MUTED("org.jitsi.meet.SET_AUDIO_MUTED"),
|
|
||||||
HANG_UP("org.jitsi.meet.HANG_UP"),
|
|
||||||
SEND_ENDPOINT_TEXT_MESSAGE("org.jitsi.meet.SEND_ENDPOINT_TEXT_MESSAGE"),
|
|
||||||
TOGGLE_SCREEN_SHARE("org.jitsi.meet.TOGGLE_SCREEN_SHARE"),
|
|
||||||
RETRIEVE_PARTICIPANTS_INFO("org.jitsi.meet.RETRIEVE_PARTICIPANTS_INFO"),
|
|
||||||
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"),
|
|
||||||
TOGGLE_CAMERA("org.jitsi.meet.TOGGLE_CAMERA"),
|
|
||||||
SHOW_NOTIFICATION("org.jitsi.meet.SHOW_NOTIFICATION"),
|
|
||||||
HIDE_NOTIFICATION("org.jitsi.meet.HIDE_NOTIFICATION"),
|
|
||||||
START_RECORDING("org.jitsi.meet.START_RECORDING"),
|
|
||||||
STOP_RECORDING("org.jitsi.meet.STOP_RECORDING"),
|
|
||||||
OVERWRITE_CONFIG("org.jitsi.meet.OVERWRITE_CONFIG"),
|
|
||||||
SEND_CAMERA_FACING_MODE_MESSAGE("org.jitsi.meet.SEND_CAMERA_FACING_MODE_MESSAGE");
|
|
||||||
|
|
||||||
private final String action;
|
|
||||||
|
|
||||||
Type(String action) {
|
|
||||||
this.action = action;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAction() {
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Type buildTypeFromAction(String action) {
|
|
||||||
for (Type type : Type.values()) {
|
|
||||||
if (type.action.equalsIgnoreCase(action)) {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package org.jitsi.meet.sdk;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
||||||
|
|
||||||
import com.facebook.react.bridge.ReadableMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class used to emit events through the LocalBroadcastManager, called when events
|
|
||||||
* from JS occurred. Takes an action name from JS, builds and broadcasts the {@link BroadcastEvent}
|
|
||||||
*/
|
|
||||||
public class BroadcastEmitter {
|
|
||||||
private final LocalBroadcastManager localBroadcastManager;
|
|
||||||
|
|
||||||
public BroadcastEmitter(Context context) {
|
|
||||||
localBroadcastManager = LocalBroadcastManager.getInstance(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendBroadcast(String name, ReadableMap data) {
|
|
||||||
BroadcastEvent event = new BroadcastEvent(name, data);
|
|
||||||
|
|
||||||
Intent intent = event.buildIntent();
|
|
||||||
|
|
||||||
if (intent != null) {
|
|
||||||
localBroadcastManager.sendBroadcast(intent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
package org.jitsi.meet.sdk;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
import com.facebook.react.bridge.ReadableMap;
|
|
||||||
|
|
||||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps the name and extra data for the events that occur on the JS side and are
|
|
||||||
* to be broadcasted.
|
|
||||||
*/
|
|
||||||
public class BroadcastEvent {
|
|
||||||
|
|
||||||
private static final String TAG = BroadcastEvent.class.getSimpleName();
|
|
||||||
|
|
||||||
private final Type type;
|
|
||||||
private final HashMap<String, Object> data;
|
|
||||||
|
|
||||||
public BroadcastEvent(String name, ReadableMap data) {
|
|
||||||
this.type = Type.buildTypeFromName(name);
|
|
||||||
this.data = data.toHashMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BroadcastEvent(Intent intent) {
|
|
||||||
this.type = Type.buildTypeFromAction(intent.getAction());
|
|
||||||
this.data = buildDataFromBundle(intent.getExtras());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type getType() {
|
|
||||||
return this.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashMap<String, Object> getData() {
|
|
||||||
return this.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Intent buildIntent() {
|
|
||||||
if (type != null && type.action != null) {
|
|
||||||
Intent intent = new Intent(type.action);
|
|
||||||
|
|
||||||
for (String key : this.data.keySet()) {
|
|
||||||
try {
|
|
||||||
intent.putExtra(key, this.data.get(key).toString());
|
|
||||||
} catch (Exception e) {
|
|
||||||
JitsiMeetLogger.w(TAG + " invalid extra data in event", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static HashMap<String, Object> buildDataFromBundle(Bundle bundle) {
|
|
||||||
if (bundle != null) {
|
|
||||||
try {
|
|
||||||
HashMap<String, Object> map = new HashMap<>();
|
|
||||||
|
|
||||||
for (String key : bundle.keySet()) {
|
|
||||||
map.put(key, bundle.get(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
} catch (Exception e) {
|
|
||||||
JitsiMeetLogger.w(TAG + " invalid extra data", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Type {
|
|
||||||
CONFERENCE_BLURRED("org.jitsi.meet.CONFERENCE_BLURRED"),
|
|
||||||
CONFERENCE_FOCUSED("org.jitsi.meet.CONFERENCE_FOCUSED"),
|
|
||||||
CONFERENCE_JOINED("org.jitsi.meet.CONFERENCE_JOINED"),
|
|
||||||
CONFERENCE_TERMINATED("org.jitsi.meet.CONFERENCE_TERMINATED"),
|
|
||||||
CONFERENCE_WILL_JOIN("org.jitsi.meet.CONFERENCE_WILL_JOIN"),
|
|
||||||
AUDIO_MUTED_CHANGED("org.jitsi.meet.AUDIO_MUTED_CHANGED"),
|
|
||||||
PARTICIPANT_JOINED("org.jitsi.meet.PARTICIPANT_JOINED"),
|
|
||||||
PARTICIPANT_LEFT("org.jitsi.meet.PARTICIPANT_LEFT"),
|
|
||||||
ENDPOINT_TEXT_MESSAGE_RECEIVED("org.jitsi.meet.ENDPOINT_TEXT_MESSAGE_RECEIVED"),
|
|
||||||
SCREEN_SHARE_TOGGLED("org.jitsi.meet.SCREEN_SHARE_TOGGLED"),
|
|
||||||
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"),
|
|
||||||
TRANSCRIPTION_CHUNK_RECEIVED("org.jitsi.meet.TRANSCRIPTION_CHUNK_RECEIVED"),
|
|
||||||
CUSTOM_BUTTON_PRESSED("org.jitsi.meet.CUSTOM_BUTTON_PRESSED"),
|
|
||||||
CONFERENCE_UNIQUE_ID_SET("org.jitsi.meet.CONFERENCE_UNIQUE_ID_SET"),
|
|
||||||
RECORDING_STATUS_CHANGED("org.jitsi.meet.RECORDING_STATUS_CHANGED");
|
|
||||||
|
|
||||||
private static final String CONFERENCE_BLURRED_NAME = "CONFERENCE_BLURRED";
|
|
||||||
private static final String CONFERENCE_FOCUSED_NAME = "CONFERENCE_FOCUSED";
|
|
||||||
private static final String CONFERENCE_WILL_JOIN_NAME = "CONFERENCE_WILL_JOIN";
|
|
||||||
private static final String CONFERENCE_JOINED_NAME = "CONFERENCE_JOINED";
|
|
||||||
private static final String CONFERENCE_TERMINATED_NAME = "CONFERENCE_TERMINATED";
|
|
||||||
private static final String AUDIO_MUTED_CHANGED_NAME = "AUDIO_MUTED_CHANGED";
|
|
||||||
private static final String PARTICIPANT_JOINED_NAME = "PARTICIPANT_JOINED";
|
|
||||||
private static final String PARTICIPANT_LEFT_NAME = "PARTICIPANT_LEFT";
|
|
||||||
private static final String ENDPOINT_TEXT_MESSAGE_RECEIVED_NAME = "ENDPOINT_TEXT_MESSAGE_RECEIVED";
|
|
||||||
private static final String SCREEN_SHARE_TOGGLED_NAME = "SCREEN_SHARE_TOGGLED";
|
|
||||||
private static final String PARTICIPANTS_INFO_RETRIEVED_NAME = "PARTICIPANTS_INFO_RETRIEVED";
|
|
||||||
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 static final String TRANSCRIPTION_CHUNK_RECEIVED_NAME = "TRANSCRIPTION_CHUNK_RECEIVED";
|
|
||||||
private static final String CUSTOM_BUTTON_PRESSED_NAME = "CUSTOM_BUTTON_PRESSED";
|
|
||||||
private static final String CONFERENCE_UNIQUE_ID_SET_NAME = "CONFERENCE_UNIQUE_ID_SET";
|
|
||||||
private static final String RECORDING_STATUS_CHANGED_NAME = "RECORDING_STATUS_CHANGED";
|
|
||||||
|
|
||||||
private final String action;
|
|
||||||
|
|
||||||
Type(String action) {
|
|
||||||
this.action = action;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAction() {
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Type buildTypeFromAction(String action) {
|
|
||||||
for (Type type : Type.values()) {
|
|
||||||
if (type.action.equalsIgnoreCase(action)) {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Type buildTypeFromName(String name) {
|
|
||||||
switch (name) {
|
|
||||||
case CONFERENCE_BLURRED_NAME:
|
|
||||||
return CONFERENCE_BLURRED;
|
|
||||||
case CONFERENCE_FOCUSED_NAME:
|
|
||||||
return CONFERENCE_FOCUSED;
|
|
||||||
case CONFERENCE_WILL_JOIN_NAME:
|
|
||||||
return CONFERENCE_WILL_JOIN;
|
|
||||||
case CONFERENCE_JOINED_NAME:
|
|
||||||
return CONFERENCE_JOINED;
|
|
||||||
case CONFERENCE_TERMINATED_NAME:
|
|
||||||
return CONFERENCE_TERMINATED;
|
|
||||||
case AUDIO_MUTED_CHANGED_NAME:
|
|
||||||
return AUDIO_MUTED_CHANGED;
|
|
||||||
case PARTICIPANT_JOINED_NAME:
|
|
||||||
return PARTICIPANT_JOINED;
|
|
||||||
case PARTICIPANT_LEFT_NAME:
|
|
||||||
return PARTICIPANT_LEFT;
|
|
||||||
case ENDPOINT_TEXT_MESSAGE_RECEIVED_NAME:
|
|
||||||
return ENDPOINT_TEXT_MESSAGE_RECEIVED;
|
|
||||||
case SCREEN_SHARE_TOGGLED_NAME:
|
|
||||||
return SCREEN_SHARE_TOGGLED;
|
|
||||||
case PARTICIPANTS_INFO_RETRIEVED_NAME:
|
|
||||||
return PARTICIPANTS_INFO_RETRIEVED;
|
|
||||||
case CHAT_MESSAGE_RECEIVED_NAME:
|
|
||||||
return CHAT_MESSAGE_RECEIVED;
|
|
||||||
case CHAT_TOGGLED_NAME:
|
|
||||||
return CHAT_TOGGLED;
|
|
||||||
case VIDEO_MUTED_CHANGED_NAME:
|
|
||||||
return VIDEO_MUTED_CHANGED;
|
|
||||||
case READY_TO_CLOSE_NAME:
|
|
||||||
return READY_TO_CLOSE;
|
|
||||||
case TRANSCRIPTION_CHUNK_RECEIVED_NAME:
|
|
||||||
return TRANSCRIPTION_CHUNK_RECEIVED;
|
|
||||||
case CUSTOM_BUTTON_PRESSED_NAME:
|
|
||||||
return CUSTOM_BUTTON_PRESSED;
|
|
||||||
case CONFERENCE_UNIQUE_ID_SET_NAME:
|
|
||||||
return CONFERENCE_UNIQUE_ID_SET;
|
|
||||||
case RECORDING_STATUS_CHANGED_NAME:
|
|
||||||
return RECORDING_STATUS_CHANGED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
package org.jitsi.meet.sdk;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
public class BroadcastIntentHelper {
|
|
||||||
public static Intent buildSetAudioMutedIntent(boolean muted) {
|
|
||||||
Intent intent = new Intent(BroadcastAction.Type.SET_AUDIO_MUTED.getAction());
|
|
||||||
intent.putExtra("muted", muted);
|
|
||||||
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent buildHangUpIntent() {
|
|
||||||
return new Intent(BroadcastAction.Type.HANG_UP.getAction());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent buildSendEndpointTextMessageIntent(String to, String message) {
|
|
||||||
Intent intent = new Intent(BroadcastAction.Type.SEND_ENDPOINT_TEXT_MESSAGE.getAction());
|
|
||||||
intent.putExtra("to", to);
|
|
||||||
intent.putExtra("message", message);
|
|
||||||
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent buildToggleScreenShareIntent(boolean enabled) {
|
|
||||||
Intent intent = new Intent(BroadcastAction.Type.TOGGLE_SCREEN_SHARE.getAction());
|
|
||||||
intent.putExtra("enabled", enabled);
|
|
||||||
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent buildOpenChatIntent(String participantId) {
|
|
||||||
Intent intent = new Intent(BroadcastAction.Type.OPEN_CHAT.getAction());
|
|
||||||
intent.putExtra("to", participantId);
|
|
||||||
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent buildCloseChatIntent() {
|
|
||||||
return new Intent(BroadcastAction.Type.CLOSE_CHAT.getAction());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent buildSendChatMessageIntent(String participantId, String message) {
|
|
||||||
Intent intent = new Intent(BroadcastAction.Type.SEND_CHAT_MESSAGE.getAction());
|
|
||||||
intent.putExtra("to", participantId);
|
|
||||||
intent.putExtra("message", message);
|
|
||||||
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent buildSetVideoMutedIntent(boolean muted) {
|
|
||||||
Intent intent = new Intent(BroadcastAction.Type.SET_VIDEO_MUTED.getAction());
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent buildRetrieveParticipantsInfo(String requestId) {
|
|
||||||
Intent intent = new Intent(BroadcastAction.Type.RETRIEVE_PARTICIPANTS_INFO.getAction());
|
|
||||||
intent.putExtra("requestId", requestId);
|
|
||||||
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent buildToggleCameraIntent() {
|
|
||||||
return new Intent(BroadcastAction.Type.TOGGLE_CAMERA.getAction());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent buildShowNotificationIntent(
|
|
||||||
String appearance, String description, String timeout, String title, String uid) {
|
|
||||||
Intent intent = new Intent(BroadcastAction.Type.SHOW_NOTIFICATION.getAction());
|
|
||||||
intent.putExtra("appearance", appearance);
|
|
||||||
intent.putExtra("description", description);
|
|
||||||
intent.putExtra("timeout", timeout);
|
|
||||||
intent.putExtra("title", title);
|
|
||||||
intent.putExtra("uid", uid);
|
|
||||||
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent buildHideNotificationIntent(String uid) {
|
|
||||||
Intent intent = new Intent(BroadcastAction.Type.HIDE_NOTIFICATION.getAction());
|
|
||||||
intent.putExtra("uid", uid);
|
|
||||||
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum RecordingMode {
|
|
||||||
FILE("file"),
|
|
||||||
STREAM("stream");
|
|
||||||
|
|
||||||
private final String mode;
|
|
||||||
|
|
||||||
RecordingMode(String mode) {
|
|
||||||
this.mode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMode() {
|
|
||||||
return mode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent buildStartRecordingIntent(
|
|
||||||
RecordingMode mode,
|
|
||||||
String dropboxToken,
|
|
||||||
boolean shouldShare,
|
|
||||||
String rtmpStreamKey,
|
|
||||||
String rtmpBroadcastID,
|
|
||||||
String youtubeStreamKey,
|
|
||||||
String youtubeBroadcastID,
|
|
||||||
Bundle extraMetadata,
|
|
||||||
boolean transcription) {
|
|
||||||
Intent intent = new Intent(BroadcastAction.Type.START_RECORDING.getAction());
|
|
||||||
intent.putExtra("mode", mode.getMode());
|
|
||||||
intent.putExtra("dropboxToken", dropboxToken);
|
|
||||||
intent.putExtra("shouldShare", shouldShare);
|
|
||||||
intent.putExtra("rtmpStreamKey", rtmpStreamKey);
|
|
||||||
intent.putExtra("rtmpBroadcastID", rtmpBroadcastID);
|
|
||||||
intent.putExtra("youtubeStreamKey", youtubeStreamKey);
|
|
||||||
intent.putExtra("youtubeBroadcastID", youtubeBroadcastID);
|
|
||||||
intent.putExtra("extraMetadata", extraMetadata);
|
|
||||||
intent.putExtra("transcription", transcription);
|
|
||||||
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent buildStopRecordingIntent(RecordingMode mode, boolean transcription) {
|
|
||||||
Intent intent = new Intent(BroadcastAction.Type.STOP_RECORDING.getAction());
|
|
||||||
intent.putExtra("mode", mode.getMode());
|
|
||||||
intent.putExtra("transcription", transcription);
|
|
||||||
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent buildOverwriteConfigIntent(Bundle config) {
|
|
||||||
Intent intent = new Intent(BroadcastAction.Type.OVERWRITE_CONFIG.getAction());
|
|
||||||
intent.putExtra("config", config);
|
|
||||||
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent buildSendCameraFacingModeMessageIntent(String to, String facingMode) {
|
|
||||||
Intent intent = new Intent(BroadcastAction.Type.SEND_CAMERA_FACING_MODE_MESSAGE.getAction());
|
|
||||||
intent.putExtra("to", to);
|
|
||||||
intent.putExtra("facingMode", facingMode);
|
|
||||||
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package org.jitsi.meet.sdk;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
import com.facebook.react.bridge.Arguments;
|
|
||||||
|
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listens for {@link BroadcastAction}s on LocalBroadcastManager. When one occurs,
|
|
||||||
* it emits it to JS.
|
|
||||||
*/
|
|
||||||
public class BroadcastReceiver extends android.content.BroadcastReceiver {
|
|
||||||
|
|
||||||
public BroadcastReceiver(Context context) {
|
|
||||||
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
|
|
||||||
|
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
|
||||||
|
|
||||||
for (BroadcastAction.Type type : BroadcastAction.Type.values()) {
|
|
||||||
intentFilter.addAction(type.getAction());
|
|
||||||
}
|
|
||||||
|
|
||||||
localBroadcastManager.registerReceiver(this, intentFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
BroadcastAction action = new BroadcastAction(intent);
|
|
||||||
String actionName = action.getType().getAction();
|
|
||||||
Bundle data = action.getData();
|
|
||||||
|
|
||||||
// For actions without data bundle (like hangup), we create an empty map
|
|
||||||
// instead of attempting to convert a null bundle to avoid crashes.
|
|
||||||
if (data != null) {
|
|
||||||
ReactInstanceManagerHolder.emitEvent(actionName, Arguments.fromBundle(data));
|
|
||||||
} else {
|
|
||||||
ReactInstanceManagerHolder.emitEvent(actionName, Arguments.createMap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,6 @@ import android.telecom.PhoneAccount;
|
|||||||
import android.telecom.PhoneAccountHandle;
|
import android.telecom.PhoneAccountHandle;
|
||||||
import android.telecom.TelecomManager;
|
import android.telecom.TelecomManager;
|
||||||
import android.telecom.VideoProfile;
|
import android.telecom.VideoProfile;
|
||||||
|
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
import com.facebook.react.bridge.Promise;
|
import com.facebook.react.bridge.Promise;
|
||||||
@@ -37,6 +36,7 @@ import java.util.Objects;
|
|||||||
*
|
*
|
||||||
* @author Pawel Domas
|
* @author Pawel Domas
|
||||||
*/
|
*/
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
public class ConnectionService extends android.telecom.ConnectionService {
|
public class ConnectionService extends android.telecom.ConnectionService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -357,7 +357,7 @@ public class ConnectionService extends android.telecom.ConnectionService {
|
|||||||
JitsiMeetLogger.i(TAG + " onDisconnect " + getCallUUID());
|
JitsiMeetLogger.i(TAG + " onDisconnect " + getCallUUID());
|
||||||
WritableNativeMap data = new WritableNativeMap();
|
WritableNativeMap data = new WritableNativeMap();
|
||||||
data.putString("callUUID", getCallUUID());
|
data.putString("callUUID", getCallUUID());
|
||||||
RNConnectionService.getInstance().emitEvent(
|
ReactInstanceManagerHolder.emitEvent(
|
||||||
"org.jitsi.meet:features/connection_service#disconnect",
|
"org.jitsi.meet:features/connection_service#disconnect",
|
||||||
data);
|
data);
|
||||||
// The JavaScript side will not go back to the native with
|
// The JavaScript side will not go back to the native with
|
||||||
@@ -377,7 +377,7 @@ public class ConnectionService extends android.telecom.ConnectionService {
|
|||||||
JitsiMeetLogger.i(TAG + " onAbort " + getCallUUID());
|
JitsiMeetLogger.i(TAG + " onAbort " + getCallUUID());
|
||||||
WritableNativeMap data = new WritableNativeMap();
|
WritableNativeMap data = new WritableNativeMap();
|
||||||
data.putString("callUUID", getCallUUID());
|
data.putString("callUUID", getCallUUID());
|
||||||
RNConnectionService.getInstance().emitEvent(
|
ReactInstanceManagerHolder.emitEvent(
|
||||||
"org.jitsi.meet:features/connection_service#abort",
|
"org.jitsi.meet:features/connection_service#abort",
|
||||||
data);
|
data);
|
||||||
// The JavaScript side will not go back to the native with
|
// The JavaScript side will not go back to the native with
|
||||||
@@ -406,7 +406,7 @@ public class ConnectionService extends android.telecom.ConnectionService {
|
|||||||
@Override
|
@Override
|
||||||
public void onCallAudioStateChanged(CallAudioState state) {
|
public void onCallAudioStateChanged(CallAudioState state) {
|
||||||
JitsiMeetLogger.d(TAG + " onCallAudioStateChanged: " + state);
|
JitsiMeetLogger.d(TAG + " onCallAudioStateChanged: " + state);
|
||||||
RNConnectionService module = RNConnectionService.getInstance();
|
RNConnectionService module = ReactInstanceManagerHolder.getNativeModule(RNConnectionService.class);
|
||||||
if (module != null) {
|
if (module != null) {
|
||||||
module.onCallAudioStateChange(state);
|
module.onCallAudioStateChange(state);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import android.text.TextUtils;
|
|||||||
|
|
||||||
import com.dropbox.core.DbxException;
|
import com.dropbox.core.DbxException;
|
||||||
import com.dropbox.core.DbxRequestConfig;
|
import com.dropbox.core.DbxRequestConfig;
|
||||||
import com.dropbox.core.android.Auth;
|
|
||||||
import com.dropbox.core.oauth.DbxCredential;
|
|
||||||
import com.dropbox.core.v2.DbxClientV2;
|
import com.dropbox.core.v2.DbxClientV2;
|
||||||
import com.dropbox.core.v2.users.FullAccount;
|
import com.dropbox.core.v2.users.FullAccount;
|
||||||
import com.dropbox.core.v2.users.SpaceAllocation;
|
import com.dropbox.core.v2.users.SpaceAllocation;
|
||||||
@@ -19,6 +17,7 @@ import com.facebook.react.bridge.LifecycleEventListener;
|
|||||||
import com.facebook.react.bridge.Promise;
|
import com.facebook.react.bridge.Promise;
|
||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||||
|
import com.dropbox.core.android.Auth;
|
||||||
import com.facebook.react.bridge.ReactMethod;
|
import com.facebook.react.bridge.ReactMethod;
|
||||||
import com.facebook.react.bridge.WritableMap;
|
import com.facebook.react.bridge.WritableMap;
|
||||||
import com.facebook.react.module.annotations.ReactModule;
|
import com.facebook.react.module.annotations.ReactModule;
|
||||||
@@ -67,7 +66,7 @@ class DropboxModule
|
|||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void authorize(final Promise promise) {
|
public void authorize(final Promise promise) {
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
Auth.startOAuth2PKCE(this.getCurrentActivity(), appKey, DbxRequestConfig.newBuilder(clientId).build());
|
Auth.startOAuth2Authentication(this.getCurrentActivity(), appKey);
|
||||||
this.promise = promise;
|
this.promise = promise;
|
||||||
} else {
|
} else {
|
||||||
promise.reject(
|
promise.reject(
|
||||||
@@ -182,23 +181,11 @@ class DropboxModule
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onHostResume() {
|
public void onHostResume() {
|
||||||
DbxCredential credential = Auth.getDbxCredential();
|
String token = Auth.getOAuth2Token();
|
||||||
|
|
||||||
if (this.promise != null ) {
|
|
||||||
if (credential != null) {
|
|
||||||
WritableMap result = Arguments.createMap();
|
|
||||||
result.putString("token", credential.getAccessToken());
|
|
||||||
result.putString("rToken", credential.getRefreshToken());
|
|
||||||
result.putDouble("expireDate", credential.getExpiresAt());
|
|
||||||
|
|
||||||
this.promise.resolve(result);
|
|
||||||
this.promise = null;
|
|
||||||
} else {
|
|
||||||
this.promise.reject("Invalid dropbox credentials");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (token != null && this.promise != null) {
|
||||||
|
this.promise.resolve(token);
|
||||||
this.promise = null;
|
this.promise = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright @ 2017-present 8x8, Inc.
|
* Copyright @ 2017-present Atlassian Pty Ltd
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -24,22 +24,17 @@ import com.facebook.react.module.annotations.ReactModule;
|
|||||||
|
|
||||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module implementing an API for sending events from JavaScript to native code.
|
* Module implementing an API for sending events from JavaScript to native code.
|
||||||
*/
|
*/
|
||||||
@ReactModule(name = ExternalAPIModule.NAME)
|
@ReactModule(name = ExternalAPIModule.NAME)
|
||||||
class ExternalAPIModule extends ReactContextBaseJavaModule {
|
class ExternalAPIModule
|
||||||
|
extends ReactContextBaseJavaModule {
|
||||||
|
|
||||||
public static final String NAME = "ExternalAPI";
|
public static final String NAME = "ExternalAPI";
|
||||||
|
|
||||||
private static final String TAG = NAME;
|
private static final String TAG = NAME;
|
||||||
|
|
||||||
private final BroadcastEmitter broadcastEmitter;
|
|
||||||
private final BroadcastReceiver broadcastReceiver;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new module instance. There shall be a single instance of
|
* Initializes a new module instance. There shall be a single instance of
|
||||||
* this module throughout the lifetime of the app.
|
* this module throughout the lifetime of the app.
|
||||||
@@ -49,21 +44,6 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
|
|||||||
*/
|
*/
|
||||||
public ExternalAPIModule(ReactApplicationContext reactContext) {
|
public ExternalAPIModule(ReactApplicationContext reactContext) {
|
||||||
super(reactContext);
|
super(reactContext);
|
||||||
|
|
||||||
broadcastEmitter = new BroadcastEmitter(reactContext);
|
|
||||||
broadcastReceiver = new BroadcastReceiver(reactContext);
|
|
||||||
|
|
||||||
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.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,51 +56,32 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
|
|||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a mapping with the constants this module is exporting.
|
|
||||||
*
|
|
||||||
* @return a {@link Map} mapping the constants to be exported with their
|
|
||||||
* values.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Map<String, Object> getConstants() {
|
|
||||||
Map<String, Object> constants = new HashMap<>();
|
|
||||||
|
|
||||||
constants.put("SET_AUDIO_MUTED", BroadcastAction.Type.SET_AUDIO_MUTED.getAction());
|
|
||||||
constants.put("HANG_UP", BroadcastAction.Type.HANG_UP.getAction());
|
|
||||||
constants.put("SEND_ENDPOINT_TEXT_MESSAGE", BroadcastAction.Type.SEND_ENDPOINT_TEXT_MESSAGE.getAction());
|
|
||||||
constants.put("TOGGLE_SCREEN_SHARE", BroadcastAction.Type.TOGGLE_SCREEN_SHARE.getAction());
|
|
||||||
constants.put("RETRIEVE_PARTICIPANTS_INFO", BroadcastAction.Type.RETRIEVE_PARTICIPANTS_INFO.getAction());
|
|
||||||
constants.put("OPEN_CHAT", BroadcastAction.Type.OPEN_CHAT.getAction());
|
|
||||||
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());
|
|
||||||
constants.put("TOGGLE_CAMERA", BroadcastAction.Type.TOGGLE_CAMERA.getAction());
|
|
||||||
constants.put("SHOW_NOTIFICATION", BroadcastAction.Type.SHOW_NOTIFICATION.getAction());
|
|
||||||
constants.put("HIDE_NOTIFICATION", BroadcastAction.Type.HIDE_NOTIFICATION.getAction());
|
|
||||||
constants.put("START_RECORDING", BroadcastAction.Type.START_RECORDING.getAction());
|
|
||||||
constants.put("STOP_RECORDING", BroadcastAction.Type.STOP_RECORDING.getAction());
|
|
||||||
constants.put("OVERWRITE_CONFIG", BroadcastAction.Type.OVERWRITE_CONFIG.getAction());
|
|
||||||
constants.put("SEND_CAMERA_FACING_MODE_MESSAGE", BroadcastAction.Type.SEND_CAMERA_FACING_MODE_MESSAGE.getAction());
|
|
||||||
|
|
||||||
return constants;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatches an event that occurred on the JavaScript side of the SDK to
|
* Dispatches an event that occurred on the JavaScript side of the SDK to
|
||||||
* the native side.
|
* the specified {@link BaseReactView}'s listener.
|
||||||
*
|
*
|
||||||
* @param name The name of the event.
|
* @param name The name of the event.
|
||||||
* @param data The details/specifics of the event to send determined
|
* @param data The details/specifics of the event to send determined
|
||||||
* by/associated with the specified {@code name}.
|
* by/associated with the specified {@code name}.
|
||||||
|
* @param scope
|
||||||
*/
|
*/
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void sendEvent(String name, ReadableMap data) {
|
public void sendEvent(String name, ReadableMap data, String scope) {
|
||||||
// Keep track of the current ongoing conference.
|
// Keep track of the current ongoing conference.
|
||||||
OngoingConferenceTracker.getInstance().onExternalAPIEvent(name, data);
|
OngoingConferenceTracker.getInstance().onExternalAPIEvent(name, data);
|
||||||
|
|
||||||
JitsiMeetLogger.d(TAG + " Sending event: " + name + " with data: " + data);
|
// The JavaScript App needs to provide uniquely identifying information
|
||||||
broadcastEmitter.sendBroadcast(name, data);
|
// to the native ExternalAPI module so that the latter may match the
|
||||||
|
// former to the native BaseReactView which hosts it.
|
||||||
|
BaseReactView view = BaseReactView.findViewByExternalAPIScope(scope);
|
||||||
|
|
||||||
|
if (view != null) {
|
||||||
|
JitsiMeetLogger.d(TAG + " Sending event: " + name + " with data: " + data);
|
||||||
|
try {
|
||||||
|
view.onExternalAPIEvent(name, data);
|
||||||
|
} catch(Exception e) {
|
||||||
|
JitsiMeetLogger.e(e, TAG + " onExternalAPIEvent: error sending event");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright @ 2021-present 8x8, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.jitsi.meet.sdk;
|
|
||||||
|
|
||||||
import 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.List;
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,58 +16,33 @@
|
|||||||
|
|
||||||
package org.jitsi.meet.sdk;
|
package org.jitsi.meet.sdk;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
||||||
|
|
||||||
import com.facebook.react.modules.core.PermissionListener;
|
import com.facebook.react.modules.core.PermissionListener;
|
||||||
|
|
||||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A base activity for SDK users to embed. It contains all the required wiring
|
* A base activity for SDK users to embed. It uses {@link JitsiMeetFragment} to do the heavy
|
||||||
* between the {@code JitsiMeetView} and the Activity lifecycle methods.
|
* lifting and wires the remaining Activity lifecycle methods so it works out of the box.
|
||||||
*
|
|
||||||
* In this activity we use a single {@code JitsiMeetView} instance. This
|
|
||||||
* instance gives us access to a view which displays the welcome page and the
|
|
||||||
* conference itself. All lifecycle methods associated with this Activity are
|
|
||||||
* hooked to the React Native subsystem via proxy calls through the
|
|
||||||
* {@code JitsiMeetActivityDelegate} static methods.
|
|
||||||
*/
|
*/
|
||||||
public class JitsiMeetActivity extends AppCompatActivity
|
public class JitsiMeetActivity extends FragmentActivity
|
||||||
implements JitsiMeetActivityInterface {
|
implements JitsiMeetActivityInterface, JitsiMeetViewListener {
|
||||||
|
|
||||||
protected static final String TAG = JitsiMeetActivity.class.getSimpleName();
|
protected static final String TAG = JitsiMeetActivity.class.getSimpleName();
|
||||||
|
|
||||||
private static final String ACTION_JITSI_MEET_CONFERENCE = "org.jitsi.meet.CONFERENCE";
|
private static final String ACTION_JITSI_MEET_CONFERENCE = "org.jitsi.meet.CONFERENCE";
|
||||||
private static final String JITSI_MEET_CONFERENCE_OPTIONS = "JitsiMeetConferenceOptions";
|
private static final String JITSI_MEET_CONFERENCE_OPTIONS = "JitsiMeetConferenceOptions";
|
||||||
|
|
||||||
private boolean isReadyToClose;
|
|
||||||
|
|
||||||
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
onBroadcastReceived(intent);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instance of the {@link JitsiMeetView} which this activity will display.
|
|
||||||
*/
|
|
||||||
private JitsiMeetView jitsiView;
|
|
||||||
|
|
||||||
// Helpers for starting the activity
|
// Helpers for starting the activity
|
||||||
//
|
//
|
||||||
|
|
||||||
@@ -75,9 +50,6 @@ public class JitsiMeetActivity extends AppCompatActivity
|
|||||||
Intent intent = new Intent(context, JitsiMeetActivity.class);
|
Intent intent = new Intent(context, JitsiMeetActivity.class);
|
||||||
intent.setAction(ACTION_JITSI_MEET_CONFERENCE);
|
intent.setAction(ACTION_JITSI_MEET_CONFERENCE);
|
||||||
intent.putExtra(JITSI_MEET_CONFERENCE_OPTIONS, options);
|
intent.putExtra(JITSI_MEET_CONFERENCE_OPTIONS, options);
|
||||||
if (!(context instanceof Activity)) {
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
}
|
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,44 +62,22 @@ public class JitsiMeetActivity extends AppCompatActivity
|
|||||||
// Overrides
|
// Overrides
|
||||||
//
|
//
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfigurationChanged(Configuration newConfig) {
|
|
||||||
super.onConfigurationChanged(newConfig);
|
|
||||||
Intent intent = new Intent("onConfigurationChanged");
|
|
||||||
intent.putExtra("newConfig", newConfig);
|
|
||||||
this.sendBroadcast(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
setContentView(R.layout.activity_jitsi_meet);
|
setContentView(R.layout.activity_jitsi_meet);
|
||||||
this.jitsiView = findViewById(R.id.jitsiView);
|
|
||||||
|
|
||||||
registerForBroadcastMessages();
|
// Listen for conference events.
|
||||||
|
getJitsiView().setListener(this);
|
||||||
|
|
||||||
if (!extraInitialize()) {
|
if (!extraInitialize()) {
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
JitsiMeetActivityDelegate.onHostResume(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStop() {
|
|
||||||
JitsiMeetActivityDelegate.onHostPause(this);
|
|
||||||
super.onStop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
JitsiMeetLogger.i("onDestroy()");
|
|
||||||
|
|
||||||
// Here we are trying to handle the following corner case: an application using the SDK
|
// Here we are trying to handle the following corner case: an application using the SDK
|
||||||
// is using this Activity for displaying meetings, but there is another "main" Activity
|
// is using this Activity for displaying meetings, but there is another "main" Activity
|
||||||
// with other content. If this Activity is "swiped out" from the recent list we will get
|
// with other content. If this Activity is "swiped out" from the recent list we will get
|
||||||
@@ -135,33 +85,19 @@ public class JitsiMeetActivity extends AppCompatActivity
|
|||||||
// current meeting, but when our view is detached from React the JS <-> Native bridge won't
|
// current meeting, but when our view is detached from React the JS <-> Native bridge won't
|
||||||
// be operational so the external API won't be able to notify the native side that the
|
// be operational so the external API won't be able to notify the native side that the
|
||||||
// conference terminated. Thus, try our best to clean up.
|
// conference terminated. Thus, try our best to clean up.
|
||||||
if (!isReadyToClose) {
|
leave();
|
||||||
JitsiMeetLogger.i("onDestroy(): leaving...");
|
|
||||||
leave();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.jitsiView = null;
|
|
||||||
|
|
||||||
if (AudioModeModule.useConnectionService()) {
|
if (AudioModeModule.useConnectionService()) {
|
||||||
ConnectionService.abortConnections();
|
ConnectionService.abortConnections();
|
||||||
}
|
}
|
||||||
JitsiMeetOngoingConferenceService.abort(this);
|
JitsiMeetOngoingConferenceService.abort(this);
|
||||||
|
|
||||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
|
|
||||||
|
|
||||||
JitsiMeetActivityDelegate.onHostDestroy(this);
|
|
||||||
|
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void finish() {
|
public void finish() {
|
||||||
if (!isReadyToClose) {
|
leave();
|
||||||
JitsiMeetLogger.i("finish(): leaving...");
|
|
||||||
leave();
|
|
||||||
}
|
|
||||||
|
|
||||||
JitsiMeetLogger.i("finish(): finishing...");
|
|
||||||
super.finish();
|
super.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,35 +105,28 @@ public class JitsiMeetActivity extends AppCompatActivity
|
|||||||
//
|
//
|
||||||
|
|
||||||
protected JitsiMeetView getJitsiView() {
|
protected JitsiMeetView getJitsiView() {
|
||||||
return jitsiView;
|
JitsiMeetFragment fragment
|
||||||
|
= (JitsiMeetFragment) getSupportFragmentManager().findFragmentById(R.id.jitsiFragment);
|
||||||
|
return fragment.getJitsiView();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void join(@Nullable String url) {
|
public void join(@Nullable String url) {
|
||||||
JitsiMeetConferenceOptions options
|
JitsiMeetConferenceOptions options
|
||||||
= new JitsiMeetConferenceOptions.Builder()
|
= new JitsiMeetConferenceOptions.Builder()
|
||||||
.setRoom(url)
|
.setRoom(url)
|
||||||
.build();
|
.build();
|
||||||
join(options);
|
join(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void join(JitsiMeetConferenceOptions options) {
|
public void join(JitsiMeetConferenceOptions options) {
|
||||||
if (this.jitsiView != null) {
|
getJitsiView().join(options);
|
||||||
this.jitsiView.join(options);
|
|
||||||
} else {
|
|
||||||
JitsiMeetLogger.w("Cannot join, view is null");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void leave() {
|
public void leave() {
|
||||||
if (this.jitsiView != null) {
|
getJitsiView().leave();
|
||||||
this.jitsiView.abort();
|
|
||||||
} else {
|
|
||||||
JitsiMeetLogger.w("Cannot leave, view is null");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private @Nullable
|
private @Nullable JitsiMeetConferenceOptions getConferenceOptions(Intent intent) {
|
||||||
JitsiMeetConferenceOptions getConferenceOptions(Intent intent) {
|
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
|
|
||||||
if (Intent.ACTION_VIEW.equals(action)) {
|
if (Intent.ACTION_VIEW.equals(action)) {
|
||||||
@@ -216,7 +145,7 @@ public class JitsiMeetActivity extends AppCompatActivity
|
|||||||
* Helper function called during activity initialization. If {@code true} is returned, the
|
* Helper function called during activity initialization. If {@code true} is returned, the
|
||||||
* initialization is delayed and the {@link JitsiMeetActivity#initialize()} method is not
|
* initialization is delayed and the {@link JitsiMeetActivity#initialize()} method is not
|
||||||
* called. In this case, it's up to the subclass to call the initialize method when ready.
|
* called. In this case, it's up to the subclass to call the initialize method when ready.
|
||||||
* <p>
|
*
|
||||||
* This is mainly required so we do some extra initialization in the Jitsi Meet app.
|
* This is mainly required so we do some extra initialization in the Jitsi Meet app.
|
||||||
*
|
*
|
||||||
* @return {@code true} if the initialization will be delayed, {@code false} otherwise.
|
* @return {@code true} if the initialization will be delayed, {@code false} otherwise.
|
||||||
@@ -231,58 +160,6 @@ public class JitsiMeetActivity extends AppCompatActivity
|
|||||||
join(getConferenceOptions(getIntent()));
|
join(getConferenceOptions(getIntent()));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onConferenceJoined(HashMap<String, Object> extraData) {
|
|
||||||
JitsiMeetLogger.i("Conference joined: " + extraData);
|
|
||||||
// Launch the service for the ongoing notification.
|
|
||||||
JitsiMeetOngoingConferenceService.launch(this, extraData);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onConferenceTerminated(HashMap<String, Object> extraData) {
|
|
||||||
JitsiMeetLogger.i("Conference terminated: " + extraData);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onConferenceWillJoin(HashMap<String, Object> extraData) {
|
|
||||||
JitsiMeetLogger.i("Conference will join: " + extraData);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onParticipantJoined(HashMap<String, Object> extraData) {
|
|
||||||
try {
|
|
||||||
JitsiMeetLogger.i("Participant joined: ", extraData);
|
|
||||||
} catch (Exception e) {
|
|
||||||
JitsiMeetLogger.w("Invalid participant joined extraData", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onParticipantLeft(HashMap<String, Object> extraData) {
|
|
||||||
try {
|
|
||||||
JitsiMeetLogger.i("Participant left: ", extraData);
|
|
||||||
} catch (Exception e) {
|
|
||||||
JitsiMeetLogger.w("Invalid participant left extraData", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onReadyToClose() {
|
|
||||||
JitsiMeetLogger.i("SDK is ready to close");
|
|
||||||
isReadyToClose = true;
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
// protected void onTranscriptionChunkReceived(HashMap<String, Object> extraData) {
|
|
||||||
// JitsiMeetLogger.i("Transcription chunk received: " + extraData);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// protected void onCustomButtonPressed(HashMap<String, Object> extraData) {
|
|
||||||
// JitsiMeetLogger.i("Custom button pressed: " + extraData);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// protected void onConferenceUniqueIdSet(HashMap<String, Object> extraData) {
|
|
||||||
// JitsiMeetLogger.i("Conference unique id set: " + extraData);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// protected void onRecordingStatusChanged(HashMap<String, Object> extraData) {
|
|
||||||
// JitsiMeetLogger.i("Recording status changed: " + extraData);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Activity lifecycle methods
|
// Activity lifecycle methods
|
||||||
//
|
//
|
||||||
|
|
||||||
@@ -314,9 +191,7 @@ public class JitsiMeetActivity extends AppCompatActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onUserLeaveHint() {
|
protected void onUserLeaveHint() {
|
||||||
if (this.jitsiView != null) {
|
getJitsiView().enterPictureInPicture();
|
||||||
this.jitsiView.enterPictureInPicture();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// JitsiMeetActivityInterface
|
// JitsiMeetActivityInterface
|
||||||
@@ -327,58 +202,29 @@ public class JitsiMeetActivity extends AppCompatActivity
|
|||||||
JitsiMeetActivityDelegate.requestPermissions(this, permissions, requestCode, listener);
|
JitsiMeetActivityDelegate.requestPermissions(this, permissions, requestCode, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("MissingSuperCall")
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||||
JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerForBroadcastMessages() {
|
// JitsiMeetViewListener
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
//
|
||||||
|
|
||||||
for (BroadcastEvent.Type type : BroadcastEvent.Type.values()) {
|
@Override
|
||||||
intentFilter.addAction(type.getAction());
|
public void onConferenceJoined(Map<String, Object> data) {
|
||||||
}
|
JitsiMeetLogger.i("Conference joined: " + data);
|
||||||
|
// Launch the service for the ongoing notification.
|
||||||
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, intentFilter);
|
JitsiMeetOngoingConferenceService.launch(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onBroadcastReceived(Intent intent) {
|
@Override
|
||||||
if (intent != null) {
|
public void onConferenceTerminated(Map<String, Object> data) {
|
||||||
BroadcastEvent event = new BroadcastEvent(intent);
|
JitsiMeetLogger.i("Conference terminated: " + data);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
switch (event.getType()) {
|
@Override
|
||||||
case CONFERENCE_JOINED:
|
public void onConferenceWillJoin(Map<String, Object> data) {
|
||||||
onConferenceJoined(event.getData());
|
JitsiMeetLogger.i("Conference will join: " + data);
|
||||||
break;
|
|
||||||
case CONFERENCE_WILL_JOIN:
|
|
||||||
onConferenceWillJoin(event.getData());
|
|
||||||
break;
|
|
||||||
case CONFERENCE_TERMINATED:
|
|
||||||
onConferenceTerminated(event.getData());
|
|
||||||
break;
|
|
||||||
case PARTICIPANT_JOINED:
|
|
||||||
onParticipantJoined(event.getData());
|
|
||||||
break;
|
|
||||||
case PARTICIPANT_LEFT:
|
|
||||||
onParticipantLeft(event.getData());
|
|
||||||
break;
|
|
||||||
case READY_TO_CLOSE:
|
|
||||||
onReadyToClose();
|
|
||||||
break;
|
|
||||||
// case TRANSCRIPTION_CHUNK_RECEIVED:
|
|
||||||
// onTranscriptionChunkReceived(event.getData());
|
|
||||||
// break;
|
|
||||||
// case CUSTOM_BUTTON_PRESSED:
|
|
||||||
// onCustomButtonPressed(event.getData());
|
|
||||||
// break;
|
|
||||||
// case CONFERENCE_UNIQUE_ID_SET:
|
|
||||||
// onConferenceUniqueIdSet(event.getData());
|
|
||||||
// break;
|
|
||||||
// case RECORDING_STATUS_CHANGED:
|
|
||||||
// onRecordingStatusChanged(event.getData());
|
|
||||||
// break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public class JitsiMeetActivityDelegate {
|
|||||||
/**
|
/**
|
||||||
* Tells whether or not the permissions request is currently in progress.
|
* Tells whether or not the permissions request is currently in progress.
|
||||||
*
|
*
|
||||||
* @return {@code true} if the permissions are being requested or {@code false} otherwise.
|
* @return {@code true} if the permssions are being requested or {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
static boolean arePermissionsBeingRequested() {
|
static boolean arePermissionsBeingRequested() {
|
||||||
return permissionListener != null;
|
return permissionListener != null;
|
||||||
@@ -116,15 +116,12 @@ public class JitsiMeetActivityDelegate {
|
|||||||
= ReactInstanceManagerHolder.getReactInstanceManager();
|
= ReactInstanceManagerHolder.getReactInstanceManager();
|
||||||
|
|
||||||
if (reactInstanceManager != null) {
|
if (reactInstanceManager != null) {
|
||||||
try {
|
// Try to avoid a crash because some devices trip on this assert:
|
||||||
|
// https://github.com/facebook/react-native/blob/df4e67fe75d781d1eb264128cadf079989542755/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java#L512
|
||||||
|
// Why this happens is a mystery wrapped in an enigma.
|
||||||
|
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
|
||||||
|
if (reactContext != null && activity == reactContext.getCurrentActivity()) {
|
||||||
reactInstanceManager.onHostPause(activity);
|
reactInstanceManager.onHostPause(activity);
|
||||||
} catch (AssertionError e) {
|
|
||||||
// There seems to be a problem in RN when resuming an Activity when
|
|
||||||
// rotation is involved and the planets align. There doesn't seem to
|
|
||||||
// be a proper solution, but since the activity is going away anyway,
|
|
||||||
// we'll YOLO-ignore the exception and hope fo the best.
|
|
||||||
// Ref: https://github.com/facebook/react-native/search?q=Pausing+an+activity+that+is+not+the+current+activity%2C+this+is+incorrect%21&type=issues
|
|
||||||
JitsiMeetLogger.e(e, "Error running onHostPause, ignoring");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import android.os.Parcel;
|
|||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,21 +40,33 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
|||||||
* Room name.
|
* Room name.
|
||||||
*/
|
*/
|
||||||
private String room;
|
private String room;
|
||||||
|
/**
|
||||||
|
* Conference subject.
|
||||||
|
*/
|
||||||
|
private String subject;
|
||||||
/**
|
/**
|
||||||
* JWT token used for authentication.
|
* JWT token used for authentication.
|
||||||
*/
|
*/
|
||||||
private String token;
|
private String token;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config. See: https://github.com/jitsi/jitsi-meet/blob/master/config.js
|
* Color scheme override, see: https://github.com/jitsi/jitsi-meet/blob/dbedee5e22e5dcf9c92db96ef5bb3c9982fc526d/react/features/base/color-scheme/defaultScheme.js
|
||||||
*/
|
*/
|
||||||
private Bundle config;
|
private Bundle colorScheme;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Feature flags. See: https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.js
|
* Feature flags. See: https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.js
|
||||||
*/
|
*/
|
||||||
private Bundle featureFlags;
|
private Bundle featureFlags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to {@code true} to join the conference with audio / video muted or to start in audio
|
||||||
|
* only mode respectively.
|
||||||
|
*/
|
||||||
|
private Boolean audioMuted;
|
||||||
|
private Boolean audioOnly;
|
||||||
|
private Boolean videoMuted;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* USer information, to be used when no token is specified.
|
* USer information, to be used when no token is specified.
|
||||||
*/
|
*/
|
||||||
@@ -69,14 +80,34 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
|||||||
return room;
|
return room;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSubject() {
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
|
||||||
public String getToken() {
|
public String getToken() {
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Bundle getColorScheme() {
|
||||||
|
return colorScheme;
|
||||||
|
}
|
||||||
|
|
||||||
public Bundle getFeatureFlags() {
|
public Bundle getFeatureFlags() {
|
||||||
return featureFlags;
|
return featureFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getAudioMuted() {
|
||||||
|
return audioMuted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getAudioOnly() {
|
||||||
|
return audioOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getVideoMuted() {
|
||||||
|
return videoMuted;
|
||||||
|
}
|
||||||
|
|
||||||
public JitsiMeetUserInfo getUserInfo() {
|
public JitsiMeetUserInfo getUserInfo() {
|
||||||
return userInfo;
|
return userInfo;
|
||||||
}
|
}
|
||||||
@@ -87,15 +118,19 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
|||||||
public static class Builder {
|
public static class Builder {
|
||||||
private URL serverURL;
|
private URL serverURL;
|
||||||
private String room;
|
private String room;
|
||||||
|
private String subject;
|
||||||
private String token;
|
private String token;
|
||||||
|
|
||||||
private Bundle config;
|
private Bundle colorScheme;
|
||||||
private Bundle featureFlags;
|
private Bundle featureFlags;
|
||||||
|
|
||||||
|
private Boolean audioMuted;
|
||||||
|
private Boolean audioOnly;
|
||||||
|
private Boolean videoMuted;
|
||||||
|
|
||||||
private JitsiMeetUserInfo userInfo;
|
private JitsiMeetUserInfo userInfo;
|
||||||
|
|
||||||
public Builder() {
|
public Builder() {
|
||||||
config = new Bundle();
|
|
||||||
featureFlags = new Bundle();
|
featureFlags = new Bundle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +162,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
|||||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||||
*/
|
*/
|
||||||
public Builder setSubject(String subject) {
|
public Builder setSubject(String subject) {
|
||||||
setConfigOverride("subject", subject);
|
this.subject = subject;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -144,12 +179,25 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates the conference will be joined with the microphone muted.
|
* Sets the color scheme override so the app is themed. See:
|
||||||
* @param audioMuted - Muted indication.
|
* 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.
|
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||||
*/
|
*/
|
||||||
public Builder setAudioMuted(boolean audioMuted) {
|
public Builder setColorScheme(Bundle colorScheme) {
|
||||||
setConfigOverride("startWithAudioMuted", audioMuted);
|
this.colorScheme = colorScheme;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates the conference will be joined with the microphone muted.
|
||||||
|
* @param muted - Muted indication.
|
||||||
|
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||||
|
*/
|
||||||
|
public Builder setAudioMuted(boolean muted) {
|
||||||
|
this.audioMuted = muted;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -161,7 +209,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
|||||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||||
*/
|
*/
|
||||||
public Builder setAudioOnly(boolean audioOnly) {
|
public Builder setAudioOnly(boolean audioOnly) {
|
||||||
setConfigOverride("startAudioOnly", audioOnly);
|
this.audioOnly = audioOnly;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -171,7 +219,20 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
|||||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||||
*/
|
*/
|
||||||
public Builder setVideoMuted(boolean videoMuted) {
|
public Builder setVideoMuted(boolean videoMuted) {
|
||||||
setConfigOverride("startWithVideoMuted", videoMuted);
|
this.videoMuted = videoMuted;
|
||||||
|
|
||||||
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -200,42 +261,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setConfigOverride(String config, String value) {
|
|
||||||
this.config.putString(config, value);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setConfigOverride(String config, int value) {
|
|
||||||
this.config.putInt(config, value);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setConfigOverride(String config, boolean value) {
|
|
||||||
this.config.putBoolean(config, value);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setConfigOverride(String config, Bundle bundle) {
|
|
||||||
this.config.putBundle(config, bundle);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setConfigOverride(String config, String[] list) {
|
|
||||||
this.config.putStringArray(config, list);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setConfigOverride(String config, ArrayList<Bundle> arrayList) {
|
|
||||||
this.config.putParcelableArrayList(config, arrayList);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the immutable {@link JitsiMeetConferenceOptions} object with the configuration
|
* Builds the immutable {@link JitsiMeetConferenceOptions} object with the configuration
|
||||||
* that this {@link Builder} instance specified.
|
* that this {@link Builder} instance specified.
|
||||||
@@ -246,9 +271,13 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
|||||||
|
|
||||||
options.serverURL = this.serverURL;
|
options.serverURL = this.serverURL;
|
||||||
options.room = this.room;
|
options.room = this.room;
|
||||||
|
options.subject = this.subject;
|
||||||
options.token = this.token;
|
options.token = this.token;
|
||||||
options.config = this.config;
|
options.colorScheme = this.colorScheme;
|
||||||
options.featureFlags = this.featureFlags;
|
options.featureFlags = this.featureFlags;
|
||||||
|
options.audioMuted = this.audioMuted;
|
||||||
|
options.audioOnly = this.audioOnly;
|
||||||
|
options.videoMuted = this.videoMuted;
|
||||||
options.userInfo = this.userInfo;
|
options.userInfo = this.userInfo;
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
@@ -261,17 +290,48 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
|||||||
private JitsiMeetConferenceOptions(Parcel in) {
|
private JitsiMeetConferenceOptions(Parcel in) {
|
||||||
serverURL = (URL) in.readSerializable();
|
serverURL = (URL) in.readSerializable();
|
||||||
room = in.readString();
|
room = in.readString();
|
||||||
|
subject = in.readString();
|
||||||
token = in.readString();
|
token = in.readString();
|
||||||
config = in.readBundle();
|
colorScheme = in.readBundle();
|
||||||
featureFlags = in.readBundle();
|
featureFlags = in.readBundle();
|
||||||
userInfo = new JitsiMeetUserInfo(in.readBundle());
|
userInfo = new JitsiMeetUserInfo(in.readBundle());
|
||||||
|
byte tmpAudioMuted = in.readByte();
|
||||||
|
audioMuted = tmpAudioMuted == 0 ? null : tmpAudioMuted == 1;
|
||||||
|
byte tmpAudioOnly = in.readByte();
|
||||||
|
audioOnly = tmpAudioOnly == 0 ? null : tmpAudioOnly == 1;
|
||||||
|
byte tmpVideoMuted = in.readByte();
|
||||||
|
videoMuted = tmpVideoMuted == 0 ? null : tmpVideoMuted == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bundle asProps() {
|
Bundle asProps() {
|
||||||
Bundle props = new Bundle();
|
Bundle props = new Bundle();
|
||||||
|
|
||||||
|
// Android always has the PiP flag set by default.
|
||||||
|
if (!featureFlags.containsKey("pip.enabled")) {
|
||||||
|
featureFlags.putBoolean("pip.enabled", true);
|
||||||
|
}
|
||||||
|
|
||||||
props.putBundle("flags", featureFlags);
|
props.putBundle("flags", featureFlags);
|
||||||
|
|
||||||
|
if (colorScheme != null) {
|
||||||
|
props.putBundle("colorScheme", colorScheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bundle config = new Bundle();
|
||||||
|
|
||||||
|
if (audioMuted != null) {
|
||||||
|
config.putBoolean("startWithAudioMuted", audioMuted);
|
||||||
|
}
|
||||||
|
if (audioOnly != null) {
|
||||||
|
config.putBoolean("startAudioOnly", audioOnly);
|
||||||
|
}
|
||||||
|
if (videoMuted != null) {
|
||||||
|
config.putBoolean("startWithVideoMuted", videoMuted);
|
||||||
|
}
|
||||||
|
if (subject != null) {
|
||||||
|
config.putString("subject", subject);
|
||||||
|
}
|
||||||
|
|
||||||
Bundle urlProps = new Bundle();
|
Bundle urlProps = new Bundle();
|
||||||
|
|
||||||
// The room is fully qualified
|
// The room is fully qualified
|
||||||
@@ -319,10 +379,14 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
|||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeSerializable(serverURL);
|
dest.writeSerializable(serverURL);
|
||||||
dest.writeString(room);
|
dest.writeString(room);
|
||||||
|
dest.writeString(subject);
|
||||||
dest.writeString(token);
|
dest.writeString(token);
|
||||||
dest.writeBundle(config);
|
dest.writeBundle(colorScheme);
|
||||||
dest.writeBundle(featureFlags);
|
dest.writeBundle(featureFlags);
|
||||||
dest.writeBundle(userInfo != null ? userInfo.asBundle() : new Bundle());
|
dest.writeBundle(userInfo != null ? userInfo.asBundle() : new Bundle());
|
||||||
|
dest.writeByte((byte) (audioMuted == null ? 0 : audioMuted ? 1 : 2));
|
||||||
|
dest.writeByte((byte) (audioOnly == null ? 0 : audioOnly ? 1 : 2));
|
||||||
|
dest.writeByte((byte) (videoMuted == null ? 0 : videoMuted ? 1 : 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,33 +16,16 @@
|
|||||||
|
|
||||||
package org.jitsi.meet.sdk;
|
package org.jitsi.meet.sdk;
|
||||||
|
|
||||||
import static android.Manifest.permission.POST_NOTIFICATIONS;
|
|
||||||
import static android.Manifest.permission.RECORD_AUDIO;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.ServiceInfo;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
||||||
|
|
||||||
import com.facebook.react.modules.core.PermissionListener;
|
|
||||||
|
|
||||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class implements an Android {@link Service}, a foreground one specifically, and it's
|
* This class implements an Android {@link Service}, a foreground one specifically, and it's
|
||||||
@@ -51,101 +34,33 @@ import java.util.Random;
|
|||||||
*
|
*
|
||||||
* See: https://developer.android.com/guide/components/services
|
* See: https://developer.android.com/guide/components/services
|
||||||
*/
|
*/
|
||||||
public class JitsiMeetOngoingConferenceService extends Service implements OngoingConferenceTracker.OngoingConferenceListener {
|
public class JitsiMeetOngoingConferenceService extends Service
|
||||||
|
implements OngoingConferenceTracker.OngoingConferenceListener {
|
||||||
private static final String TAG = JitsiMeetOngoingConferenceService.class.getSimpleName();
|
private static final String TAG = JitsiMeetOngoingConferenceService.class.getSimpleName();
|
||||||
private static final String ACTIVITY_DATA_KEY = "activityDataKey";
|
|
||||||
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 static final int PERMISSIONS_REQUEST_CODE = (int) (Math.random() * Short.MAX_VALUE);
|
static final class Actions {
|
||||||
|
static final String START = TAG + ":START";
|
||||||
|
static final String HANGUP = TAG + ":HANGUP";
|
||||||
|
}
|
||||||
|
|
||||||
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver();
|
static void launch(Context context) {
|
||||||
|
OngoingNotification.createOngoingConferenceNotificationChannel();
|
||||||
private boolean isAudioMuted;
|
|
||||||
private Class tapBackActivity;
|
|
||||||
|
|
||||||
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
|
|
||||||
|
|
||||||
private static void doLaunch(Context context, HashMap<String, Object> extraData) {
|
|
||||||
Activity activity = (Activity) context;
|
|
||||||
|
|
||||||
OngoingNotification.createNotificationChannel(activity);
|
|
||||||
|
|
||||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||||
|
intent.setAction(Actions.START);
|
||||||
Bundle extraDataBundle = new Bundle();
|
|
||||||
extraDataBundle.putSerializable(EXTRA_DATA_KEY, extraData);
|
|
||||||
|
|
||||||
intent.putExtra(EXTRA_DATA_BUNDLE_KEY, extraDataBundle);
|
|
||||||
intent.putExtra(ACTIVITY_DATA_KEY, activity.getClass().getCanonicalName());
|
|
||||||
|
|
||||||
ComponentName componentName;
|
ComponentName componentName;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
try {
|
|
||||||
componentName = context.startForegroundService(intent);
|
componentName = context.startForegroundService(intent);
|
||||||
} catch (RuntimeException e) {
|
} else {
|
||||||
// Avoid crashing due to ForegroundServiceStartNotAllowedException (API level 31).
|
componentName = context.startService(intent);
|
||||||
// See: https://developer.android.com/guide/components/foreground-services#background-start-restrictions
|
|
||||||
JitsiMeetLogger.w(TAG + " Ongoing conference service not started", e);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (componentName == null) {
|
if (componentName == null) {
|
||||||
JitsiMeetLogger.w(TAG + " Ongoing conference service not started");
|
JitsiMeetLogger.w(TAG + " Ongoing conference service not started");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void abort(Context context) {
|
||||||
public static void launch(Context context, HashMap<String, Object> extraData) {
|
|
||||||
List<String> permissionsList = new ArrayList<>();
|
|
||||||
|
|
||||||
PermissionListener listener = new PermissionListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onRequestPermissionsResult(int i, String[] strings, int[] results) {
|
|
||||||
int counter = 0;
|
|
||||||
|
|
||||||
if (results.length > 0) {
|
|
||||||
for (int result : results) {
|
|
||||||
if (result == PackageManager.PERMISSION_GRANTED) {
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (counter == results.length){
|
|
||||||
doLaunch(context, extraData);
|
|
||||||
JitsiMeetLogger.w(TAG + " Service launched, permissions were granted");
|
|
||||||
} else {
|
|
||||||
JitsiMeetLogger.w(TAG + " Couldn't launch service, permissions were not granted");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
||||||
permissionsList.add(POST_NOTIFICATIONS);
|
|
||||||
permissionsList.add(RECORD_AUDIO);
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] permissionsArray = new String[ permissionsList.size() ];
|
|
||||||
permissionsArray = permissionsList.toArray( permissionsArray );
|
|
||||||
|
|
||||||
if (permissionsArray.length > 0) {
|
|
||||||
JitsiMeetActivityDelegate.requestPermissions(
|
|
||||||
(Activity) context,
|
|
||||||
permissionsArray,
|
|
||||||
PERMISSIONS_REQUEST_CODE,
|
|
||||||
listener
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
doLaunch(context, extraData);
|
|
||||||
JitsiMeetLogger.w(TAG + " Service launched");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void abort(Context context) {
|
|
||||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||||
context.stopService(intent);
|
context.stopService(intent);
|
||||||
}
|
}
|
||||||
@@ -154,31 +69,12 @@ public class JitsiMeetOngoingConferenceService extends Service implements Ongoin
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, this, tapBackActivity);
|
|
||||||
if (notification == null) {
|
|
||||||
stopSelf();
|
|
||||||
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
|
||||||
} else {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
||||||
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK | ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE);
|
|
||||||
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
|
|
||||||
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK);
|
|
||||||
} else {
|
|
||||||
startForeground(NOTIFICATION_ID, notification);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OngoingConferenceTracker.getInstance().addListener(this);
|
OngoingConferenceTracker.getInstance().addListener(this);
|
||||||
|
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
|
||||||
intentFilter.addAction(BroadcastEvent.Type.AUDIO_MUTED_CHANGED.getAction());
|
|
||||||
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(broadcastReceiver, intentFilter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
OngoingConferenceTracker.getInstance().removeListener(this);
|
OngoingConferenceTracker.getInstance().removeListener(this);
|
||||||
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(broadcastReceiver);
|
|
||||||
|
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
@@ -190,57 +86,26 @@ public class JitsiMeetOngoingConferenceService extends Service implements Ongoin
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
final String actionName = intent.getAction();
|
final String action = intent.getAction();
|
||||||
final Action action = Action.fromName(actionName);
|
if (Actions.START.equals(action)) {
|
||||||
|
Notification notification = OngoingNotification.buildOngoingConferenceNotification();
|
||||||
if (action != Action.HANGUP) {
|
|
||||||
Boolean isAudioMuted = tryParseIsAudioMuted(intent);
|
|
||||||
|
|
||||||
if (isAudioMuted != null) {
|
|
||||||
this.isAudioMuted = Boolean.parseBoolean(intent.getStringExtra("muted"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tapBackActivity == null) {
|
|
||||||
String targetActivityName = intent.getExtras().getString(ACTIVITY_DATA_KEY);
|
|
||||||
Class<? extends Activity> targetActivity = null;
|
|
||||||
try {
|
|
||||||
targetActivity = Class.forName(targetActivityName).asSubclass(Activity.class);
|
|
||||||
tapBackActivity = targetActivity;
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
JitsiMeetLogger.w(TAG + " Could not find target Activity: " + targetActivityName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification(this.isAudioMuted, this, tapBackActivity);
|
|
||||||
if (notification == null) {
|
if (notification == null) {
|
||||||
stopSelf();
|
stopSelf();
|
||||||
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
||||||
} else {
|
} else {
|
||||||
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
startForeground(OngoingNotification.NOTIFICATION_ID, notification);
|
||||||
notificationManager.notify(NOTIFICATION_ID, notification);
|
JitsiMeetLogger.i(TAG + " Service started");
|
||||||
}
|
}
|
||||||
}
|
} else if (Actions.HANGUP.equals(action)) {
|
||||||
|
JitsiMeetLogger.i(TAG + " Hangup requested");
|
||||||
// When starting the service, there is no action passed in the intent
|
// Abort all ongoing calls
|
||||||
if (action != null) {
|
if (AudioModeModule.useConnectionService()) {
|
||||||
switch (action) {
|
ConnectionService.abortConnections();
|
||||||
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);
|
|
||||||
|
|
||||||
stopSelf();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
JitsiMeetLogger.w(TAG + " Unknown action received: " + action);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
stopSelf();
|
||||||
|
} else {
|
||||||
|
JitsiMeetLogger.w(TAG + " Unknown action received: " + action);
|
||||||
|
stopSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
@@ -250,61 +115,7 @@ public class JitsiMeetOngoingConferenceService extends Service implements Ongoin
|
|||||||
public void onCurrentConferenceChanged(String conferenceUrl) {
|
public void onCurrentConferenceChanged(String conferenceUrl) {
|
||||||
if (conferenceUrl == null) {
|
if (conferenceUrl == null) {
|
||||||
stopSelf();
|
stopSelf();
|
||||||
OngoingNotification.resetStartingtime();
|
|
||||||
JitsiMeetLogger.i(TAG + "Service stopped");
|
JitsiMeetLogger.i(TAG + "Service stopped");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Action {
|
|
||||||
HANGUP(TAG + ":HANGUP"),
|
|
||||||
MUTE(TAG + ":MUTE"),
|
|
||||||
UNMUTE(TAG + ":UNMUTE");
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
Action(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Action fromName(String name) {
|
|
||||||
for (Action action : Action.values()) {
|
|
||||||
if (action.name.equalsIgnoreCase(name)) {
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
Class tapBackActivity = JitsiMeetOngoingConferenceService.this.tapBackActivity;
|
|
||||||
isAudioMuted = Boolean.parseBoolean(intent.getStringExtra("muted"));
|
|
||||||
Notification notification = OngoingNotification.buildOngoingConferenceNotification(isAudioMuted, context, tapBackActivity);
|
|
||||||
if (notification == null) {
|
|
||||||
stopSelf();
|
|
||||||
JitsiMeetLogger.w(TAG + " Couldn't update service, notification is null");
|
|
||||||
} else {
|
|
||||||
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
|
||||||
notificationManager.notify(NOTIFICATION_ID, notification);
|
|
||||||
|
|
||||||
JitsiMeetLogger.i(TAG + " audio muted changed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,31 +16,35 @@
|
|||||||
|
|
||||||
package org.jitsi.meet.sdk;
|
package org.jitsi.meet.sdk;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.facebook.react.ReactRootView;
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
|
|
||||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class JitsiMeetView extends FrameLayout {
|
|
||||||
|
public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
|
||||||
|
implements OngoingConferenceTracker.OngoingConferenceListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Background color. Should match the background color set in JS.
|
* The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
|
||||||
|
* redux action types.
|
||||||
*/
|
*/
|
||||||
private static final int BACKGROUND_COLOR = 0xFF040404;
|
private static final Map<String, Method> LISTENER_METHODS
|
||||||
|
= ListenerUtils.mapListenerMethods(JitsiMeetViewListener.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* React Native root view.
|
* The URL of the current conference.
|
||||||
*/
|
*/
|
||||||
private ReactRootView reactRootView;
|
// XXX Currently, one thread writes and one thread reads, so it should be
|
||||||
|
// fine to have this field volatile without additional synchronization.
|
||||||
|
private volatile String url;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to recursively merge 2 {@link Bundle} objects representing React Native props.
|
* Helper method to recursively merge 2 {@link Bundle} objects representing React Native props.
|
||||||
@@ -79,8 +83,6 @@ public class JitsiMeetView extends FrameLayout {
|
|||||||
result.putBoolean(key, (Boolean)bValue);
|
result.putBoolean(key, (Boolean)bValue);
|
||||||
} else if (valueType.contentEquals("String")) {
|
} else if (valueType.contentEquals("String")) {
|
||||||
result.putString(key, (String)bValue);
|
result.putString(key, (String)bValue);
|
||||||
} else if (valueType.contentEquals("Integer")) {
|
|
||||||
result.putInt(key, (int)bValue);
|
|
||||||
} else if (valueType.contentEquals("Bundle")) {
|
} else if (valueType.contentEquals("Bundle")) {
|
||||||
result.putBundle(key, mergeProps((Bundle)aValue, (Bundle)bValue));
|
result.putBundle(key, mergeProps((Bundle)aValue, (Bundle)bValue));
|
||||||
} else {
|
} else {
|
||||||
@@ -93,32 +95,20 @@ public class JitsiMeetView extends FrameLayout {
|
|||||||
|
|
||||||
public JitsiMeetView(@NonNull Context context) {
|
public JitsiMeetView(@NonNull Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
initialize(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JitsiMeetView(Context context, AttributeSet attrs) {
|
// Check if the parent Activity implements JitsiMeetActivityInterface,
|
||||||
super(context, attrs);
|
// otherwise things may go wrong.
|
||||||
initialize(context);
|
if (!(context instanceof JitsiMeetActivityInterface)) {
|
||||||
}
|
throw new RuntimeException("Enclosing Activity must implement JitsiMeetActivityInterface");
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OngoingConferenceTracker.getInstance().addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
OngoingConferenceTracker.getInstance().removeListener(this);
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -136,7 +126,8 @@ public class JitsiMeetView extends FrameLayout {
|
|||||||
PictureInPictureModule.class);
|
PictureInPictureModule.class);
|
||||||
if (pipModule != null
|
if (pipModule != null
|
||||||
&& pipModule.isPictureInPictureSupported()
|
&& pipModule.isPictureInPictureSupported()
|
||||||
&& !JitsiMeetActivityDelegate.arePermissionsBeingRequested()) {
|
&& !JitsiMeetActivityDelegate.arePermissionsBeingRequested()
|
||||||
|
&& this.url != null) {
|
||||||
try {
|
try {
|
||||||
pipModule.enterPictureInPicture();
|
pipModule.enterPictureInPicture();
|
||||||
} catch (RuntimeException re) {
|
} catch (RuntimeException re) {
|
||||||
@@ -156,50 +147,12 @@ public class JitsiMeetView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal method which aborts running RN by passing empty props.
|
* Leaves the currently active conference.
|
||||||
* This is only meant to be used from the enclosing Activity's onDestroy.
|
|
||||||
*/
|
*/
|
||||||
public void abort() {
|
public void leave() {
|
||||||
setProps(new Bundle());
|
setProps(new Bundle());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to set the React Native props.
|
* Helper method to set the React Native props.
|
||||||
* @param newProps - New props to be set on the React Native view.
|
* @param newProps - New props to be set on the React Native view.
|
||||||
@@ -214,7 +167,7 @@ public class JitsiMeetView extends FrameLayout {
|
|||||||
// by leaving the conference. However, React and, respectively,
|
// by leaving the conference. However, React and, respectively,
|
||||||
// appProperties/initialProperties are declarative expressions i.e. one
|
// appProperties/initialProperties are declarative expressions i.e. one
|
||||||
// and the same URL will not trigger an automatic re-render in the
|
// and the same URL will not trigger an automatic re-render in the
|
||||||
// JavaScript source code. The workaround implemented below introduces
|
// JavaScript source code. The workaround implemented bellow introduces
|
||||||
// "imperativeness" in React Component props by defining a unique value
|
// "imperativeness" in React Component props by defining a unique value
|
||||||
// per setProps() invocation.
|
// per setProps() invocation.
|
||||||
props.putLong("timestamp", System.currentTimeMillis());
|
props.putLong("timestamp", System.currentTimeMillis());
|
||||||
@@ -222,6 +175,32 @@ public class JitsiMeetView extends FrameLayout {
|
|||||||
createReactRootView("App", props);
|
createReactRootView("App", props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for {@link OngoingConferenceTracker} events.
|
||||||
|
* @param conferenceUrl
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onCurrentConferenceChanged(String conferenceUrl) {
|
||||||
|
// This property was introduced in order to address
|
||||||
|
// an exception in the Picture-in-Picture functionality which arose
|
||||||
|
// because of delays related to bridging between JavaScript and Java. To
|
||||||
|
// reduce these delays do not wait for the call to be transferred to the
|
||||||
|
// UI thread.
|
||||||
|
this.url = conferenceUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for {@link ExternalAPIModule} events.
|
||||||
|
*
|
||||||
|
* @param name The name of the event.
|
||||||
|
* @param data The details/specifics of the event to send determined
|
||||||
|
* by/associated with the specified {@code name}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void onExternalAPIEvent(String name, ReadableMap data) {
|
||||||
|
onExternalAPIEvent(LISTENER_METHODS, name, data);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDetachedFromWindow() {
|
protected void onDetachedFromWindow() {
|
||||||
dispose();
|
dispose();
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
public interface JitsiMeetViewListener {
|
||||||
|
/**
|
||||||
|
* Called when a conference was joined.
|
||||||
|
*
|
||||||
|
* @param data Map with a "url" key with the conference URL.
|
||||||
|
*/
|
||||||
|
void onConferenceJoined(Map<String, Object> data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the active conference ends, be it because of user choice or
|
||||||
|
* because of a failure.
|
||||||
|
*
|
||||||
|
* @param data Map with an "error" key with the error and a "url" key with
|
||||||
|
* the conference URL. If the conference finished gracefully no `error`
|
||||||
|
* key will be present. The possible values for "error" are described here:
|
||||||
|
* https://github.com/jitsi/lib-jitsi-meet/blob/master/JitsiConnectionErrors.js
|
||||||
|
* https://github.com/jitsi/lib-jitsi-meet/blob/master/JitsiConferenceErrors.js
|
||||||
|
*/
|
||||||
|
void onConferenceTerminated(Map<String, Object> data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called before the conference is joined.
|
||||||
|
*
|
||||||
|
* @param data Map with a "url" key with the conference URL.
|
||||||
|
*/
|
||||||
|
void onConferenceWillJoin(Map<String, Object> data);
|
||||||
|
}
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
package org.jitsi.meet.sdk;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.oney.WebRTCModule.webrtcutils.SoftwareVideoDecoderFactoryProxy;
|
|
||||||
|
|
||||||
import org.webrtc.EglBase;
|
|
||||||
import org.webrtc.HardwareVideoDecoderFactory;
|
|
||||||
import org.webrtc.PlatformSoftwareVideoDecoderFactory;
|
|
||||||
import org.webrtc.VideoCodecInfo;
|
|
||||||
import org.webrtc.VideoDecoder;
|
|
||||||
import org.webrtc.VideoDecoderFactory;
|
|
||||||
import org.webrtc.VideoDecoderFallback;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom decoder factory which uses HW decoders and falls back to SW.
|
|
||||||
*/
|
|
||||||
public class JitsiVideoDecoderFactory implements VideoDecoderFactory {
|
|
||||||
private final VideoDecoderFactory hardwareVideoDecoderFactory;
|
|
||||||
private final VideoDecoderFactory softwareVideoDecoderFactory = new SoftwareVideoDecoderFactoryProxy();
|
|
||||||
private final @Nullable VideoDecoderFactory platformSoftwareVideoDecoderFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create decoder factory using default hardware decoder factory.
|
|
||||||
*/
|
|
||||||
public JitsiVideoDecoderFactory(@Nullable EglBase.Context eglContext) {
|
|
||||||
this.hardwareVideoDecoderFactory = new HardwareVideoDecoderFactory(eglContext);
|
|
||||||
this.platformSoftwareVideoDecoderFactory = new PlatformSoftwareVideoDecoderFactory(eglContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create decoder factory using explicit hardware decoder factory.
|
|
||||||
*/
|
|
||||||
JitsiVideoDecoderFactory(VideoDecoderFactory hardwareVideoDecoderFactory) {
|
|
||||||
this.hardwareVideoDecoderFactory = hardwareVideoDecoderFactory;
|
|
||||||
this.platformSoftwareVideoDecoderFactory = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable VideoDecoder createDecoder(VideoCodecInfo codecType) {
|
|
||||||
VideoDecoder softwareDecoder = softwareVideoDecoderFactory.createDecoder(codecType);
|
|
||||||
final VideoDecoder hardwareDecoder = hardwareVideoDecoderFactory.createDecoder(codecType);
|
|
||||||
if (softwareDecoder == null && platformSoftwareVideoDecoderFactory != null) {
|
|
||||||
softwareDecoder = platformSoftwareVideoDecoderFactory.createDecoder(codecType);
|
|
||||||
}
|
|
||||||
if (hardwareDecoder != null && softwareDecoder != null) {
|
|
||||||
// Both hardware and software supported, wrap it in a software fallback
|
|
||||||
return new VideoDecoderFallback(
|
|
||||||
/* fallback= */ softwareDecoder, /* primary= */ hardwareDecoder);
|
|
||||||
}
|
|
||||||
return hardwareDecoder != null ? hardwareDecoder : softwareDecoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public VideoCodecInfo[] getSupportedCodecs() {
|
|
||||||
LinkedHashSet<VideoCodecInfo> supportedCodecInfos = new LinkedHashSet<>();
|
|
||||||
|
|
||||||
supportedCodecInfos.addAll(Arrays.asList(softwareVideoDecoderFactory.getSupportedCodecs()));
|
|
||||||
supportedCodecInfos.addAll(Arrays.asList(hardwareVideoDecoderFactory.getSupportedCodecs()));
|
|
||||||
if (platformSoftwareVideoDecoderFactory != null) {
|
|
||||||
supportedCodecInfos.addAll(
|
|
||||||
Arrays.asList(platformSoftwareVideoDecoderFactory.getSupportedCodecs()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return supportedCodecInfos.toArray(new VideoCodecInfo[supportedCodecInfos.size()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package org.jitsi.meet.sdk;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.oney.WebRTCModule.webrtcutils.H264AndSoftwareVideoEncoderFactory;
|
|
||||||
|
|
||||||
import org.webrtc.EglBase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom encoder factory which uses HW for H.264 and SW for everything else.
|
|
||||||
*/
|
|
||||||
public class JitsiVideoEncoderFactory extends H264AndSoftwareVideoEncoderFactory {
|
|
||||||
public JitsiVideoEncoderFactory(@Nullable EglBase.Context eglContext) {
|
|
||||||
super(eglContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
166
android/sdk/src/main/java/org/jitsi/meet/sdk/ListenerUtils.java
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* 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}.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,7 +47,7 @@ class LocaleDetector extends ReactContextBaseJavaModule {
|
|||||||
public Map<String, Object> getConstants() {
|
public Map<String, Object> getConstants() {
|
||||||
Context context = getReactApplicationContext();
|
Context context = getReactApplicationContext();
|
||||||
HashMap<String,Object> constants = new HashMap<>();
|
HashMap<String,Object> constants = new HashMap<>();
|
||||||
constants.put("locale", context.getResources().getConfiguration().locale.toLanguageTag());
|
constants.put("locale", context.getResources().getConfiguration().locale.toString());
|
||||||
return constants;
|
return constants;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,4 +55,4 @@ class LocaleDetector extends ReactContextBaseJavaModule {
|
|||||||
public String getName() {
|
public String getName() {
|
||||||
return "LocaleDetector";
|
return "LocaleDetector";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,19 +16,19 @@
|
|||||||
|
|
||||||
package org.jitsi.meet.sdk;
|
package org.jitsi.meet.sdk;
|
||||||
|
|
||||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
import android.app.Notification;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.Notification;
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
|
|
||||||
|
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for creating the ongoing notification which is used with
|
* Helper class for creating the ongoing notification which is used with
|
||||||
@@ -38,28 +38,34 @@ import androidx.core.app.NotificationCompat;
|
|||||||
class OngoingNotification {
|
class OngoingNotification {
|
||||||
private static final String TAG = OngoingNotification.class.getSimpleName();
|
private static final String TAG = OngoingNotification.class.getSimpleName();
|
||||||
|
|
||||||
private static long startingTime = 0;
|
private static final String CHANNEL_ID = "JitsiNotificationChannel";
|
||||||
|
private static final String CHANNEL_NAME = "Ongoing Conference Notifications";
|
||||||
|
|
||||||
static final String ONGOING_CONFERENCE_CHANNEL_ID = "JitsiOngoingConferenceChannel";
|
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
|
||||||
|
|
||||||
static void createNotificationChannel(Activity context) {
|
|
||||||
|
static void createOngoingConferenceNotificationChannel() {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Context context = ReactInstanceManagerHolder.getCurrentActivity();
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
JitsiMeetLogger.w(TAG + " Cannot create notification channel: no current context");
|
JitsiMeetLogger.w(TAG + " Cannot create notification channel: no current context");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationManager notificationManager
|
NotificationManager notificationManager
|
||||||
= (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
= (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
|
||||||
NotificationChannel channel
|
NotificationChannel channel
|
||||||
= notificationManager.getNotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID);
|
= notificationManager.getNotificationChannel(CHANNEL_ID);
|
||||||
|
|
||||||
if (channel != null) {
|
if (channel != null) {
|
||||||
// The channel was already created, no need to do it again.
|
// The channel was already created, no need to do it again.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
channel = new NotificationChannel(ONGOING_CONFERENCE_CHANNEL_ID, context.getString(R.string.ongoing_notification_channel_name), NotificationManager.IMPORTANCE_DEFAULT);
|
channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
|
||||||
channel.enableLights(false);
|
channel.enableLights(false);
|
||||||
channel.enableVibration(false);
|
channel.enableVibration(false);
|
||||||
channel.setShowBadge(false);
|
channel.setShowBadge(false);
|
||||||
@@ -67,19 +73,21 @@ class OngoingNotification {
|
|||||||
notificationManager.createNotificationChannel(channel);
|
notificationManager.createNotificationChannel(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Notification buildOngoingConferenceNotification(Boolean isMuted, Context context, Class tapBackActivity) {
|
static Notification buildOngoingConferenceNotification() {
|
||||||
|
Context context = ReactInstanceManagerHolder.getCurrentActivity();
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
JitsiMeetLogger.w(TAG + " Cannot create notification: no current context");
|
JitsiMeetLogger.w(TAG + " Cannot create notification: no current context");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent notificationIntent = new Intent(context, tapBackActivity == null ? context.getClass() : tapBackActivity);
|
Intent notificationIntent = new Intent(context, context.getClass());
|
||||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
|
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, ONGOING_CONFERENCE_CHANNEL_ID);
|
NotificationCompat.Builder builder;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
if (startingTime == 0) {
|
builder = new NotificationCompat.Builder(context, CHANNEL_ID);
|
||||||
startingTime = System.currentTimeMillis();
|
} else {
|
||||||
|
builder = new NotificationCompat.Builder(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder
|
builder
|
||||||
@@ -89,36 +97,23 @@ class OngoingNotification {
|
|||||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
.setContentIntent(pendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
.setWhen(startingTime)
|
|
||||||
.setUsesChronometer(true)
|
|
||||||
.setAutoCancel(false)
|
.setAutoCancel(false)
|
||||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
|
.setUsesChronometer(true)
|
||||||
.setOnlyAlertOnce(true)
|
.setOnlyAlertOnce(true)
|
||||||
.setSmallIcon(context.getResources().getIdentifier("ic_notification", "drawable", context.getPackageName()));
|
.setSmallIcon(context.getResources().getIdentifier("ic_notification", "drawable", context.getPackageName()));
|
||||||
|
|
||||||
NotificationCompat.Action hangupAction = createAction(context, JitsiMeetOngoingConferenceService.Action.HANGUP, R.string.ongoing_notification_action_hang_up);
|
// Add a "hang-up" action only if we are using ConnectionService.
|
||||||
|
if (AudioModeModule.useConnectionService()) {
|
||||||
|
Intent hangupIntent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||||
|
hangupIntent.setAction(JitsiMeetOngoingConferenceService.Actions.HANGUP);
|
||||||
|
PendingIntent hangupPendingIntent
|
||||||
|
= PendingIntent.getService(context, 0, hangupIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
NotificationCompat.Action hangupAction = new NotificationCompat.Action(0, "Hang up", hangupPendingIntent);
|
||||||
|
|
||||||
JitsiMeetOngoingConferenceService.Action toggleAudioAction = isMuted
|
builder.addAction(hangupAction);
|
||||||
? JitsiMeetOngoingConferenceService.Action.UNMUTE : JitsiMeetOngoingConferenceService.Action.MUTE;
|
}
|
||||||
int toggleAudioTitle = isMuted ? R.string.ongoing_notification_action_unmute : R.string.ongoing_notification_action_mute;
|
|
||||||
NotificationCompat.Action audioAction = createAction(context, toggleAudioAction, toggleAudioTitle);
|
|
||||||
|
|
||||||
builder.addAction(hangupAction);
|
|
||||||
builder.addAction(audioAction);
|
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resetStartingtime() {
|
|
||||||
startingTime = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static NotificationCompat.Action createAction(Context context, JitsiMeetOngoingConferenceService.Action action, @StringRes int titleId) {
|
|
||||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
|
||||||
intent.setAction(action.getName());
|
|
||||||
PendingIntent pendingIntent
|
|
||||||
= PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
|
|
||||||
String title = context.getString(titleId);
|
|
||||||
return new NotificationCompat.Action(0, title, pendingIntent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
package org.jitsi.meet.sdk;
|
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
|
||||||
|
|
||||||
public class ParticipantInfo {
|
|
||||||
|
|
||||||
@SerializedName("participantId")
|
|
||||||
public String id;
|
|
||||||
|
|
||||||
@SerializedName("displayName")
|
|
||||||
public String displayName;
|
|
||||||
|
|
||||||
@SerializedName("avatarUrl")
|
|
||||||
public String avatarUrl;
|
|
||||||
|
|
||||||
@SerializedName("email")
|
|
||||||
public String email;
|
|
||||||
|
|
||||||
@SerializedName("name")
|
|
||||||
public String name;
|
|
||||||
|
|
||||||
@SerializedName("isLocal")
|
|
||||||
public boolean isLocal;
|
|
||||||
|
|
||||||
@SerializedName("role")
|
|
||||||
public String role;
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
package org.jitsi.meet.sdk;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
|
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
||||||
|
|
||||||
import com.facebook.react.bridge.Arguments;
|
|
||||||
import com.facebook.react.bridge.WritableMap;
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
|
|
||||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
public class ParticipantsService extends android.content.BroadcastReceiver {
|
|
||||||
|
|
||||||
private static final String TAG = ParticipantsService.class.getSimpleName();
|
|
||||||
private static final String REQUEST_ID = "requestId";
|
|
||||||
|
|
||||||
private final Map<String, WeakReference<ParticipantsInfoCallback>> participantsInfoCallbackMap = new HashMap<>();
|
|
||||||
|
|
||||||
private static ParticipantsService instance;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static ParticipantsService getInstance() {
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ParticipantsService(Context context) {
|
|
||||||
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
|
|
||||||
|
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
|
||||||
intentFilter.addAction(BroadcastEvent.Type.PARTICIPANTS_INFO_RETRIEVED.getAction());
|
|
||||||
localBroadcastManager.registerReceiver(this, intentFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void init(Context context) {
|
|
||||||
instance = new ParticipantsService(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void retrieveParticipantsInfo(ParticipantsInfoCallback participantsInfoCallback) {
|
|
||||||
String callbackKey = UUID.randomUUID().toString();
|
|
||||||
this.participantsInfoCallbackMap.put(callbackKey, new WeakReference<>(participantsInfoCallback));
|
|
||||||
|
|
||||||
String actionName = BroadcastAction.Type.RETRIEVE_PARTICIPANTS_INFO.getAction();
|
|
||||||
WritableMap data = Arguments.createMap();
|
|
||||||
data.putString(REQUEST_ID, callbackKey);
|
|
||||||
ReactInstanceManagerHolder.emitEvent(actionName, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
BroadcastEvent event = new BroadcastEvent(intent);
|
|
||||||
|
|
||||||
switch (event.getType()) {
|
|
||||||
case PARTICIPANTS_INFO_RETRIEVED:
|
|
||||||
try {
|
|
||||||
List<ParticipantInfo> participantInfoList = new Gson().fromJson(
|
|
||||||
event.getData().get("participantsInfo").toString(),
|
|
||||||
new TypeToken<ArrayList<ParticipantInfo>>() {
|
|
||||||
}.getType());
|
|
||||||
|
|
||||||
ParticipantsInfoCallback participantsInfoCallback = this.participantsInfoCallbackMap.get(event.getData().get(REQUEST_ID).toString()).get();
|
|
||||||
|
|
||||||
if (participantsInfoCallback != null) {
|
|
||||||
participantsInfoCallback.onReceived(participantInfoList);
|
|
||||||
this.participantsInfoCallbackMap.remove(participantsInfoCallback);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
JitsiMeetLogger.w(TAG + "error parsing participantsList", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ParticipantsInfoCallback {
|
|
||||||
void onReceived(List<ParticipantInfo> participantInfoList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,6 +20,7 @@ import android.annotation.TargetApi;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.PictureInPictureParams;
|
import android.app.PictureInPictureParams;
|
||||||
|
import android.os.Build;
|
||||||
import android.util.Rational;
|
import android.util.Rational;
|
||||||
|
|
||||||
import com.facebook.react.bridge.Promise;
|
import com.facebook.react.bridge.Promise;
|
||||||
@@ -42,7 +43,7 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
|
|||||||
private static final String TAG = NAME;
|
private static final String TAG = NAME;
|
||||||
|
|
||||||
private static boolean isSupported;
|
private static boolean isSupported;
|
||||||
private boolean isEnabled;
|
private boolean isDisabled;
|
||||||
|
|
||||||
public PictureInPictureModule(ReactApplicationContext reactContext) {
|
public PictureInPictureModule(ReactApplicationContext reactContext) {
|
||||||
super(reactContext);
|
super(reactContext);
|
||||||
@@ -52,7 +53,7 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
|
|||||||
// Android Go devices don't support PiP. There doesn't seem to be a better way to detect it than
|
// Android Go devices don't support PiP. There doesn't seem to be a better way to detect it than
|
||||||
// to use ActivityManager.isLowRamDevice().
|
// to use ActivityManager.isLowRamDevice().
|
||||||
// https://stackoverflow.com/questions/58340558/how-to-detect-android-go
|
// https://stackoverflow.com/questions/58340558/how-to-detect-android-go
|
||||||
isSupported = !am.isLowRamDevice();
|
isSupported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !am.isLowRamDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,8 +82,9 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
|
|||||||
* including when the activity is not visible (paused or stopped), if the
|
* including when the activity is not visible (paused or stopped), if the
|
||||||
* screen is locked or if the user has an activity pinned.
|
* screen is locked or if the user has an activity pinned.
|
||||||
*/
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.O)
|
||||||
public void enterPictureInPicture() {
|
public void enterPictureInPicture() {
|
||||||
if (!isEnabled) {
|
if (isDisabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,8 +132,8 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void setPictureInPictureEnabled(Boolean enabled) {
|
public void setPictureInPictureDisabled(Boolean disabled) {
|
||||||
this.isEnabled = enabled;
|
this.isDisabled = disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPictureInPictureSupported() {
|
public boolean isPictureInPictureSupported() {
|
||||||
|
|||||||
@@ -3,25 +3,21 @@ package org.jitsi.meet.sdk;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.telecom.DisconnectCause;
|
import android.telecom.DisconnectCause;
|
||||||
import android.telecom.PhoneAccount;
|
import android.telecom.PhoneAccount;
|
||||||
import android.telecom.PhoneAccountHandle;
|
import android.telecom.PhoneAccountHandle;
|
||||||
import android.telecom.TelecomManager;
|
import android.telecom.TelecomManager;
|
||||||
import android.telecom.VideoProfile;
|
import android.telecom.VideoProfile;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
import com.facebook.react.bridge.NativeModule;
|
|
||||||
import com.facebook.react.bridge.Promise;
|
import com.facebook.react.bridge.Promise;
|
||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
import com.facebook.react.bridge.ReactContext;
|
|
||||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||||
import com.facebook.react.bridge.ReactMethod;
|
import com.facebook.react.bridge.ReactMethod;
|
||||||
import com.facebook.react.bridge.ReadableMap;
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
import com.facebook.react.module.annotations.ReactModule;
|
import com.facebook.react.module.annotations.ReactModule;
|
||||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
||||||
|
|
||||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||||
|
|
||||||
@@ -31,6 +27,7 @@ import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
|||||||
*
|
*
|
||||||
* @author Pawel Domas
|
* @author Pawel Domas
|
||||||
*/
|
*/
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
@ReactModule(name = RNConnectionService.NAME)
|
@ReactModule(name = RNConnectionService.NAME)
|
||||||
class RNConnectionService extends ReactContextBaseJavaModule {
|
class RNConnectionService extends ReactContextBaseJavaModule {
|
||||||
|
|
||||||
@@ -38,7 +35,6 @@ class RNConnectionService extends ReactContextBaseJavaModule {
|
|||||||
|
|
||||||
private static final String TAG = ConnectionService.TAG;
|
private static final String TAG = ConnectionService.TAG;
|
||||||
|
|
||||||
private static RNConnectionService sRNConnectionServiceInstance;
|
|
||||||
/**
|
/**
|
||||||
* Handler for dealing with call state changes. We are acting as a proxy between ConnectionService
|
* Handler for dealing with call state changes. We are acting as a proxy between ConnectionService
|
||||||
* and other modules such as {@link AudioModeModule}.
|
* and other modules such as {@link AudioModeModule}.
|
||||||
@@ -51,6 +47,7 @@ class RNConnectionService extends ReactContextBaseJavaModule {
|
|||||||
* @param audioRoute the new audio route to be set. See
|
* @param audioRoute the new audio route to be set. See
|
||||||
* {@link android.telecom.CallAudioState} constants prefixed with "ROUTE_".
|
* {@link android.telecom.CallAudioState} constants prefixed with "ROUTE_".
|
||||||
*/
|
*/
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
static void setAudioRoute(int audioRoute) {
|
static void setAudioRoute(int audioRoute) {
|
||||||
for (ConnectionService.ConnectionImpl c
|
for (ConnectionService.ConnectionImpl c
|
||||||
: ConnectionService.getConnections()) {
|
: ConnectionService.getConnections()) {
|
||||||
@@ -60,21 +57,6 @@ class RNConnectionService extends ReactContextBaseJavaModule {
|
|||||||
|
|
||||||
RNConnectionService(ReactApplicationContext reactContext) {
|
RNConnectionService(ReactApplicationContext reactContext) {
|
||||||
super(reactContext);
|
super(reactContext);
|
||||||
sRNConnectionServiceInstance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
static RNConnectionService getInstance() {
|
|
||||||
return sRNConnectionServiceInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -201,7 +183,7 @@ class RNConnectionService extends ReactContextBaseJavaModule {
|
|||||||
* Called by the JS side to update the call's state.
|
* Called by the JS side to update the call's state.
|
||||||
*
|
*
|
||||||
* @param callUUID - the call's UUID.
|
* @param callUUID - the call's UUID.
|
||||||
* @param callState - the map which carries info about the current call's
|
* @param callState - the map which carries infor about the current call's
|
||||||
* state. See static fields in {@link ConnectionService.ConnectionImpl}
|
* state. See static fields in {@link ConnectionService.ConnectionImpl}
|
||||||
* prefixed with "KEY_" for the values supported by the Android
|
* prefixed with "KEY_" for the values supported by the Android
|
||||||
* implementation.
|
* implementation.
|
||||||
@@ -234,22 +216,4 @@ class RNConnectionService extends ReactContextBaseJavaModule {
|
|||||||
interface CallAudioStateListener {
|
interface CallAudioStateListener {
|
||||||
void onCallAudioStateChange(android.telecom.CallAudioState callAudioState);
|
void onCallAudioStateChange(android.telecom.CallAudioState callAudioState);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to send an event to JavaScript.
|
|
||||||
*
|
|
||||||
* @param eventName {@code String} containing the event name.
|
|
||||||
* @param data {@code Object} optional ancillary data for the event.
|
|
||||||
*/
|
|
||||||
void emitEvent(
|
|
||||||
String eventName,
|
|
||||||
@Nullable Object data) {
|
|
||||||
ReactContext reactContext = getReactApplicationContext();
|
|
||||||
|
|
||||||
if (reactContext != null) {
|
|
||||||
reactContext
|
|
||||||
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
||||||
.emit(eventName, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,36 +16,36 @@
|
|||||||
|
|
||||||
package org.jitsi.meet.sdk;
|
package org.jitsi.meet.sdk;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.facebook.hermes.reactexecutor.HermesExecutorFactory;
|
|
||||||
import com.facebook.react.ReactInstanceManager;
|
import com.facebook.react.ReactInstanceManager;
|
||||||
import com.facebook.react.ReactPackage;
|
import com.facebook.react.ReactPackage;
|
||||||
import com.facebook.react.bridge.NativeModule;
|
import com.facebook.react.bridge.NativeModule;
|
||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
import com.facebook.react.bridge.ReactContext;
|
import com.facebook.react.bridge.ReactContext;
|
||||||
import com.facebook.react.common.LifecycleState;
|
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.modules.core.DeviceEventManagerModule;
|
||||||
import com.facebook.react.uimanager.ViewManager;
|
import com.facebook.react.uimanager.ViewManager;
|
||||||
import com.oney.WebRTCModule.EglUtils;
|
import com.facebook.soloader.SoLoader;
|
||||||
import com.oney.WebRTCModule.WebRTCModuleOptions;
|
import com.oney.WebRTCModule.RTCVideoViewManager;
|
||||||
|
import com.oney.WebRTCModule.WebRTCModule;
|
||||||
|
|
||||||
import org.devio.rn.splashscreen.SplashScreenModule;
|
import org.devio.rn.splashscreen.SplashScreenModule;
|
||||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
import org.webrtc.SoftwareVideoDecoderFactory;
|
||||||
import org.webrtc.EglBase;
|
import org.webrtc.SoftwareVideoEncoderFactory;
|
||||||
|
import org.webrtc.audio.AudioDeviceModule;
|
||||||
|
import org.webrtc.audio.JavaAudioDeviceModule;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
class ReactInstanceManagerHolder {
|
class ReactInstanceManagerHolder {
|
||||||
private static final String TAG = ReactInstanceManagerHolder.class.getSimpleName();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FIXME (from linter): Do not place Android context classes in static
|
* FIXME (from linter): Do not place Android context classes in static
|
||||||
* fields (static reference to ReactInstanceManager which has field
|
* fields (static reference to ReactInstanceManager which has field
|
||||||
@@ -71,87 +71,42 @@ class ReactInstanceManagerHolder {
|
|||||||
new SplashScreenModule(reactContext),
|
new SplashScreenModule(reactContext),
|
||||||
new PictureInPictureModule(reactContext),
|
new PictureInPictureModule(reactContext),
|
||||||
new ProximityModule(reactContext),
|
new ProximityModule(reactContext),
|
||||||
|
new WiFiStatsModule(reactContext),
|
||||||
new org.jitsi.meet.sdk.net.NAT64AddrInfoModule(reactContext)));
|
new org.jitsi.meet.sdk.net.NAT64AddrInfoModule(reactContext)));
|
||||||
|
|
||||||
if (AudioModeModule.useConnectionService()) {
|
if (AudioModeModule.useConnectionService()) {
|
||||||
nativeModules.add(new RNConnectionService(reactContext));
|
nativeModules.add(new RNConnectionService(reactContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize the WebRTC module by hand, since we want to override some
|
||||||
|
// initialization options.
|
||||||
|
WebRTCModule.Options options = new WebRTCModule.Options();
|
||||||
|
|
||||||
|
AudioDeviceModule adm = JavaAudioDeviceModule.builder(reactContext)
|
||||||
|
.createAudioDeviceModule();
|
||||||
|
options.setAudioDeviceModule(adm);
|
||||||
|
|
||||||
|
options.setVideoDecoderFactory(new SoftwareVideoDecoderFactory());
|
||||||
|
options.setVideoEncoderFactory(new SoftwareVideoEncoderFactory());
|
||||||
|
|
||||||
|
nativeModules.add(new WebRTCModule(reactContext, options));
|
||||||
|
|
||||||
|
try {
|
||||||
|
Class<?> amplitudeModuleClass = Class.forName("org.jitsi.meet.sdk.AmplitudeModule");
|
||||||
|
Constructor constructor = amplitudeModuleClass.getConstructor(ReactApplicationContext.class);
|
||||||
|
nativeModules.add((NativeModule)constructor.newInstance(reactContext));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
|
||||||
|
}
|
||||||
|
|
||||||
return nativeModules;
|
return nativeModules;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
private static List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||||
return Collections.emptyList();
|
return Arrays.<ViewManager>asList(
|
||||||
}
|
// WebRTC, see createNativeModules for details.
|
||||||
|
new RTCVideoViewManager()
|
||||||
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.oney.WebRTCModule.WebRTCModulePackage(),
|
|
||||||
new com.swmansion.gesturehandler.RNGestureHandlerPackage(),
|
|
||||||
new org.linusu.RNGetRandomValuesPackage(),
|
|
||||||
new com.rnimmersivemode.RNImmersiveModePackage(),
|
|
||||||
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.
|
|
||||||
JitsiMeetLogger.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.
|
|
||||||
JitsiMeetLogger.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.
|
|
||||||
JitsiMeetLogger.d(TAG, "Not loading RNGoogleSignInPackage");
|
|
||||||
}
|
|
||||||
|
|
||||||
return packages;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -167,7 +122,7 @@ class ReactInstanceManagerHolder {
|
|||||||
= ReactInstanceManagerHolder.getReactInstanceManager();
|
= ReactInstanceManagerHolder.getReactInstanceManager();
|
||||||
|
|
||||||
if (reactInstanceManager != null) {
|
if (reactInstanceManager != null) {
|
||||||
@SuppressLint("VisibleForTests") ReactContext reactContext
|
ReactContext reactContext
|
||||||
= reactInstanceManager.getCurrentReactContext();
|
= reactInstanceManager.getCurrentReactContext();
|
||||||
|
|
||||||
if (reactContext != null) {
|
if (reactContext != null) {
|
||||||
@@ -190,7 +145,7 @@ class ReactInstanceManagerHolder {
|
|||||||
*/
|
*/
|
||||||
static <T extends NativeModule> T getNativeModule(
|
static <T extends NativeModule> T getNativeModule(
|
||||||
Class<T> nativeModuleClass) {
|
Class<T> nativeModuleClass) {
|
||||||
@SuppressLint("VisibleForTests") ReactContext reactContext
|
ReactContext reactContext
|
||||||
= reactInstanceManager != null
|
= reactInstanceManager != null
|
||||||
? reactInstanceManager.getCurrentReactContext() : null;
|
? reactInstanceManager.getCurrentReactContext() : null;
|
||||||
|
|
||||||
@@ -198,6 +153,18 @@ class ReactInstanceManagerHolder {
|
|||||||
? reactContext.getNativeModule(nativeModuleClass) : null;
|
? reactContext.getNativeModule(nativeModuleClass) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current {@link Activity} linked to React Native.
|
||||||
|
*
|
||||||
|
* @return An activity attached to React Native.
|
||||||
|
*/
|
||||||
|
static Activity getCurrentActivity() {
|
||||||
|
ReactContext reactContext
|
||||||
|
= reactInstanceManager != null
|
||||||
|
? reactInstanceManager.getCurrentReactContext() : null;
|
||||||
|
return reactContext != null ? reactContext.getCurrentActivity() : null;
|
||||||
|
}
|
||||||
|
|
||||||
static ReactInstanceManager getReactInstanceManager() {
|
static ReactInstanceManager getReactInstanceManager() {
|
||||||
return reactInstanceManager;
|
return reactInstanceManager;
|
||||||
}
|
}
|
||||||
@@ -215,20 +182,44 @@ class ReactInstanceManagerHolder {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the WebRTC module options.
|
SoLoader.init(activity, /* native exopackage */ false);
|
||||||
WebRTCModuleOptions options = WebRTCModuleOptions.getInstance();
|
|
||||||
options.enableMediaProjectionService = true;
|
List<ReactPackage> packages
|
||||||
if (options.videoDecoderFactory == null || options.videoEncoderFactory == null) {
|
= new ArrayList<>(Arrays.asList(
|
||||||
EglBase.Context eglContext = EglUtils.getRootEglBaseContext();
|
new com.BV.LinearGradient.LinearGradientPackage(),
|
||||||
if (options.videoDecoderFactory == null) {
|
new com.calendarevents.CalendarEventsPackage(),
|
||||||
options.videoDecoderFactory = new JitsiVideoDecoderFactory(eglContext);
|
new com.corbt.keepawake.KCKeepAwakePackage(),
|
||||||
}
|
new com.facebook.react.shell.MainReactPackage(),
|
||||||
if (options.videoEncoderFactory == null) {
|
new com.horcrux.svg.SvgPackage(),
|
||||||
options.videoEncoderFactory = new JitsiVideoEncoderFactory(eglContext);
|
new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
|
||||||
}
|
new com.ocetnik.timer.BackgroundTimerPackage(),
|
||||||
|
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
|
||||||
|
new com.reactnativecommunity.netinfo.NetInfoPackage(),
|
||||||
|
new com.reactnativecommunity.webview.RNCWebViewPackage(),
|
||||||
|
new com.rnimmersive.RNImmersivePackage(),
|
||||||
|
new com.zmxv.RNSound.RNSoundPackage(),
|
||||||
|
new ReactPackageAdapter() {
|
||||||
|
@Override
|
||||||
|
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||||
|
return ReactInstanceManagerHolder.createNativeModules(reactContext);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||||
|
return ReactInstanceManagerHolder.createViewManagers(reactContext);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
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.
|
||||||
}
|
}
|
||||||
|
|
||||||
JitsiMeetLogger.d(TAG, "initializing RN");
|
// Keep on using JSC, the jury is out on Hermes.
|
||||||
|
JSCExecutorFactory jsFactory
|
||||||
|
= new JSCExecutorFactory("", "");
|
||||||
|
|
||||||
reactInstanceManager
|
reactInstanceManager
|
||||||
= ReactInstanceManager.builder()
|
= ReactInstanceManager.builder()
|
||||||
@@ -236,10 +227,20 @@ class ReactInstanceManagerHolder {
|
|||||||
.setCurrentActivity(activity)
|
.setCurrentActivity(activity)
|
||||||
.setBundleAssetName("index.android.bundle")
|
.setBundleAssetName("index.android.bundle")
|
||||||
.setJSMainModulePath("index.android")
|
.setJSMainModulePath("index.android")
|
||||||
.setJavaScriptExecutorFactory(new HermesExecutorFactory())
|
.setJavaScriptExecutorFactory(jsFactory)
|
||||||
.addPackages(getReactNativePackages())
|
.addPackages(packages)
|
||||||
.setUseDeveloperSupport(BuildConfig.DEBUG)
|
.setUseDeveloperSupport(BuildConfig.DEBUG)
|
||||||
.setInitialLifecycleState(LifecycleState.RESUMED)
|
.setInitialLifecycleState(LifecycleState.RESUMED)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
// Disable delta updates on Android, they have caused trouble.
|
||||||
|
DevInternalSettings devSettings
|
||||||
|
= (DevInternalSettings)reactInstanceManager.getDevSupportManager().getDevSettings();
|
||||||
|
if (devSettings != null) {
|
||||||
|
devSettings.setBundleDeltasEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register our uncaught exception handler.
|
||||||
|
JitsiMeetUncaughtExceptionHandler.register();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright @ 2018-present Atlassian Pty Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jitsi.meet.sdk.incoming_call;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public class IncomingCallInfo {
|
||||||
|
/**
|
||||||
|
* URL for the caller avatar.
|
||||||
|
*/
|
||||||
|
private final String callerAvatarURL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caller's name.
|
||||||
|
*/
|
||||||
|
private final String callerName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this is a regular call or a video call.
|
||||||
|
*/
|
||||||
|
private final boolean hasVideo;
|
||||||
|
|
||||||
|
public IncomingCallInfo(
|
||||||
|
@NonNull String callerName,
|
||||||
|
@NonNull String callerAvatarURL,
|
||||||
|
boolean hasVideo) {
|
||||||
|
this.callerName = callerName;
|
||||||
|
this.callerAvatarURL = callerAvatarURL;
|
||||||
|
this.hasVideo = hasVideo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the caller's avatar URL.
|
||||||
|
*
|
||||||
|
* @return - The URL as a string.
|
||||||
|
*/
|
||||||
|
public String getCallerAvatarURL() {
|
||||||
|
return callerAvatarURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the caller's name.
|
||||||
|
*
|
||||||
|
* @return - The caller's name.
|
||||||
|
*/
|
||||||
|
public String getCallerName() {
|
||||||
|
return callerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether the call is a video call or not.
|
||||||
|
*
|
||||||
|
* @return - {@code true} if this call has video; {@code false}, otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasVideo() {
|
||||||
|
return hasVideo;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright @ 2018-present Atlassian Pty Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jitsi.meet.sdk.incoming_call;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
|
|
||||||
|
import org.jitsi.meet.sdk.BaseReactView;
|
||||||
|
import org.jitsi.meet.sdk.ListenerUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class IncomingCallView
|
||||||
|
extends BaseReactView<IncomingCallViewListener> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
|
||||||
|
* redux action types.
|
||||||
|
*/
|
||||||
|
private static final Map<String, Method> LISTENER_METHODS
|
||||||
|
= ListenerUtils.mapListenerMethods(IncomingCallViewListener.class);
|
||||||
|
|
||||||
|
public IncomingCallView(@NonNull Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for {@link ExternalAPIModule} events.
|
||||||
|
*
|
||||||
|
* @param name The name of the event.
|
||||||
|
* @param data The details/specifics of the event to send determined
|
||||||
|
* by/associated with the specified {@code name}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void onExternalAPIEvent(String name, ReadableMap data) {
|
||||||
|
onExternalAPIEvent(LISTENER_METHODS, name, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the information for the incoming call this {@code IncomingCallView}
|
||||||
|
* represents.
|
||||||
|
*
|
||||||
|
* @param callInfo - {@link IncomingCallInfo} object representing the caller
|
||||||
|
* information.
|
||||||
|
*/
|
||||||
|
public void setIncomingCallInfo(IncomingCallInfo callInfo) {
|
||||||
|
Bundle props = new Bundle();
|
||||||
|
|
||||||
|
props.putString("callerAvatarURL", callInfo.getCallerAvatarURL());
|
||||||
|
props.putString("callerName", callInfo.getCallerName());
|
||||||
|
props.putBoolean("hasVideo", callInfo.hasVideo());
|
||||||
|
|
||||||
|
createReactRootView("IncomingCallApp", props);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright @ 2018-present Atlassian Pty Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jitsi.meet.sdk.incoming_call;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for listening to events coming from Jitsi Meet, related to
|
||||||
|
* {@link IncomingCallView}.
|
||||||
|
*/
|
||||||
|
public interface IncomingCallViewListener {
|
||||||
|
/**
|
||||||
|
* Called when the user presses the "Answer" button on the
|
||||||
|
* {@link IncomingCallView}.
|
||||||
|
*
|
||||||
|
* @param data - Unused at the moment.
|
||||||
|
*/
|
||||||
|
void onIncomingCallAnswered(Map<String, Object> data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the user presses the "Decline" button on the
|
||||||
|
* {@link IncomingCallView}.
|
||||||
|
*
|
||||||
|
* @param data - Unused at the moment.
|
||||||
|
*/
|
||||||
|
void onIncomingCallDeclined(Map<String, Object> data);
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 699 B |
|
Before Width: | Height: | Size: 406 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,13 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/jitsi_layout"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".JitsiMeetActivity">
|
tools:context=".JitsiMeetActivity">
|
||||||
|
<fragment
|
||||||
<org.jitsi.meet.sdk.JitsiMeetView
|
|
||||||
android:id="@+id/jitsiView"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent"
|
||||||
|
android:name="org.jitsi.meet.sdk.JitsiMeetFragment"
|
||||||
|
android:id="@+id/jitsiFragment"/>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<string name="ongoing_notification_title">Текущая встреча</string>
|
|
||||||
<string name="ongoing_notification_text">Нажмите, чтобы вернуться к встрече.</string>
|
|
||||||
<string name="ongoing_notification_action_hang_up">Отключиться</string>
|
|
||||||
<string name="ongoing_notification_action_mute">Отключить звук</string>
|
|
||||||
<string name="ongoing_notification_action_unmute">Включить звук</string>
|
|
||||||
<string name="ongoing_notification_channel_name">Ongoing Conference Notifications</string>
|
|
||||||
</resources>
|
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Jitsi Meet SDK</string>
|
<string name="app_name">Jitsi Meet SDK</string>
|
||||||
<string name="dropbox_app_key"></string>
|
<string name="dropbox_app_key"></string>
|
||||||
<string name="media_projection_notification_title">Media projection</string>
|
|
||||||
<string name="media_projection_notification_text">You are currently sharing your screen.</string>
|
|
||||||
<string name="ongoing_notification_title">Ongoing meeting</string>
|
<string name="ongoing_notification_title">Ongoing meeting</string>
|
||||||
<string name="ongoing_notification_text">You are currently in a meeting. Tap to return to it.</string>
|
<string name="ongoing_notification_text">You are currently in a meeting. Tap to return to it.</string>
|
||||||
<string name="ongoing_notification_action_hang_up">Hang up</string>
|
|
||||||
<string name="ongoing_notification_action_mute">Mute</string>
|
|
||||||
<string name="ongoing_notification_action_unmute">Unmute</string>
|
|
||||||
<string name="ongoing_notification_channel_name">Ongoing Conference Notifications</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
<resources>
|
|
||||||
<style name="JitsiMeetActivityStyle" parent="Theme.AppCompat.Light.NoActionBar"/>
|
|
||||||
</resources>
|
|
||||||