Compare commits

..

357 Commits

Author SHA1 Message Date
Hristo Terezov
45aa8606e6 ref: Use openURLInBrowser whenever possible 2019-10-15 18:50:54 +01:00
Mihai Uscat
8be02f9ca1 Implement review changes 2019-10-15 06:54:54 -07:00
Mihai Uscat
3c25a4c08c Naming conventions; Add variables 2019-10-15 06:54:54 -07:00
Mihai Uscat
5ade0cad8b feat(welcome): add posibility to extend settings toolbar 2019-10-15 06:54:54 -07:00
Saúl Ibarra Corretgé
0fa6ffc439 deps: react-native-google-signin@3.0.1 2019-10-14 19:12:45 +02:00
Saúl Ibarra Corretgé
c2ed296178 android: make check more resilient
If action is null (observed on some old devices) we'll get an exception.
Reversing the check fixes it since Actions.XXX is statically defined.
2019-10-14 17:45:43 +02:00
Saúl Ibarra Corretgé
febd12b871 ci: use Xcode 11.1 on Travis 2019-10-14 16:47:07 +02:00
Hristo Terezov
0a06e256b7 feat(HelpButton): Mobile support. 2019-10-14 07:35:39 -07:00
Hristo Terezov
f295f60bea feat(HelpOverflowButton): Implement. 2019-10-14 07:35:39 -07:00
Saúl Ibarra Corretgé
4a8f787519 rn: evaluate config.js in a sandboxed environment
We are downloading code off the Internet and executing it on the user's device,
so run it sandboxed to avoid potential bad actors.

Since it's impossible to eval() safely in JS and React Native doesn't offer
something akin to Node's vm module, here we are rolling our own.

On Android it uses the Duktape JavaScript engine and on iOS the builtin
JavaScriptCore engine. The extra JS engine is *only* used for evaluating the
downloaded code and returning a JSON string which is then passed back to RN.
2019-10-14 12:20:58 +02:00
Saúl Ibarra Corretgé
d85b869934 rn: skip loading configured scriptUrls
None of them work on mobile.
2019-10-14 12:20:58 +02:00
Saúl Ibarra Corretgé
35130f0736 rn: refactor loadScript
- use AbortController for setting the fetch timeout
- use async / await syntax for clarify
- set the default timeout to 5s (previously non-existent, aka 0)
- add ability to load but not evaluate a script
2019-10-14 12:20:58 +02:00
Saúl Ibarra Corretgé
1feff9709c config: drop configLocation and getroomnode options
They never worked on mobile and pose an impediment for makinf config.js more
future proof. Specially if we want to move to a non-executable form of
configuration.
2019-10-14 12:20:58 +02:00
Leonard Kim
1010f53a84 fix(config): add whitelisting for interface config
For now all keys are whitelisted.
2019-10-11 09:38:56 -07:00
Saúl Ibarra Corretgé
f7a526f488 rn: fix rendering unnecessary stuff when in PiP mode 2019-10-11 17:17:53 +02:00
Bettenbuk Zoltan
245eb89b85 fix BottomSheet shaking 2019-10-11 15:14:51 +02:00
Hristo Terezov
99de9d0bfa fix(remoteVideo): Attaching video stream. 2019-10-11 04:58:01 -07:00
Saúl Ibarra Corretgé
98698ba89a etherpad: refactor to share code with mobile
- simplify initialization procedure
- set user display name as the Etherpad name\
- use SharedDocumentButton
2019-10-10 11:19:38 +02:00
Saúl Ibarra Corretgé
19d1e3829d rn: add shared document support using Etherpad 2019-10-10 11:19:38 +02:00
Saúl Ibarra Corretgé
612586ed1f deps: react-native-webview@7.4.1 2019-10-10 11:19:38 +02:00
Saúl Ibarra Corretgé
2609e43f29 ios: misc Xcode changes due to an update 2019-10-10 11:19:38 +02:00
Saúl Ibarra Corretgé
c5cd4f534c dial-in-summary: center the loading indicator 2019-10-10 11:19:38 +02:00
Bettenbuk Zoltan
6e10ca5dd2 fix: chat error message 2019-10-09 18:35:09 +02:00
Bettenbuk Zoltan
0fff1c3534 ref: serve makefile libs locally 2019-10-09 11:56:06 +02:00
Bettenbuk Zoltan
42271b1b89 feat: private messages 2019-10-08 18:22:45 +02:00
Bettenbuk Zoltan
f270b50972 fix: remove unnecessary escaping 2019-10-08 18:22:45 +02:00
Saúl Ibarra Corretgé
ab4b6be9d7 rn: throw exception if default conference options set the room
See: https://github.com/jitsi/jitsi-meet/issues/4720
2019-10-08 14:31:13 +02:00
Saúl Ibarra Corretgé
c45ee0230f android: add getters to JitsiMeetConferenceOptions 2019-10-08 14:31:13 +02:00
George Politis
d210f2f2e7 Adds a noAutoPlayVideo configuration option (used in testing). (#4714)
This adds an option to disable video autoplay that will be used mostly with maleus (our selenium-based load testing tool for testing the new bridge). Disabling video rendering lowers the resource utilisation of the selenium nodes.
2019-10-08 11:34:25 +02:00
Bettenbuk Zoltan
13d78d6b49 fix: utf-8 room name case sensitivity 2019-10-08 10:35:19 +02:00
damencho
34a71042c6 Enables Hungarian translation. 2019-10-05 00:11:25 +01:00
damencho
47ecf7d035 Commit from translate.jitsi.org by user damencho.: 601 of 601 strings translated (0 fuzzy). 2019-10-04 23:15:36 +00:00
damencho
bbe8c52778 Commit from translate.jitsi.org by user damencho.: 600 of 601 strings translated (1 fuzzy). 2019-10-04 23:15:24 +00:00
damencho
2011421e9d Commit from translate.jitsi.org by user damencho.: 582 of 601 strings translated (3 fuzzy). 2019-10-04 23:15:08 +00:00
damencho
ce55952ca9 Commit from translate.jitsi.org by user damencho.: 524 of 601 strings translated (5 fuzzy). 2019-10-04 23:14:58 +00:00
damencho
f93482e815 Commit from translate.jitsi.org by user damencho.: 582 of 601 strings translated (0 fuzzy). 2019-10-04 23:14:45 +00:00
Andrei Gavrilescu
761ac6a730 feat: integrate rnnoise based service for voice activity (VAD) detection 2019-10-04 12:55:18 +02:00
Djorkaeff Alexandre
11d3a343e5 ios: use iPhone 8 simulator when archiving JitsiMeet framework
It's available on both Xcode 10 and 11.
2019-10-04 11:00:31 +02:00
Saúl Ibarra Corretgé
9666bf836e ios: update CocoaPods to version 1.8 2019-10-04 11:00:31 +02:00
Saúl Ibarra Corretgé
a6d3b09796 ios: support building with Xcode 11 2019-10-04 11:00:31 +02:00
Дамян Минков
bb0036fdab Adds notifications for who stop/start recording/live streaming. (#4708)
* Adds notifications for who stop/start recording/live streaming.

* Updates to latest lib-jitsi-meet.
2019-10-03 20:35:21 +01:00
paweldomas
8dc0f30a49 ref(NAT64AddrInfoModule): use 'ipv4only.arpa' well known host
defined in RFC7050 instead of 'nat64.jitsi.net' as suggested by
Jonathan Lennox.
2019-10-02 10:49:39 -05:00
Saúl Ibarra Corretgé
1aed08f460 deps: react-native-webrtc@latest
Fixes iOS crashes when restarting the AudioUnit in case of interruption.
2019-10-02 14:44:15 +02:00
Saúl Ibarra Corretgé
afccf6f06d rn: disable H.264 on iOS 10 devices
It crashes like hell. See:
https://bugs.chromium.org/p/webrtc/issues/detail?id=11002
2019-10-02 14:10:37 +02:00
damencho
5cd351a46f Updates rayo filter to add user token info to dial messages.
Adds option to limit number of outgoing calls per user.
2019-09-30 16:53:38 +01:00
damencho
51f257e894 chore(deps): update LJM to 3f7613748d7669cd3fd031bbdf9069e4309f6f56 2019-09-30 11:53:30 +01:00
Saúl Ibarra Corretgé
ac06892bb4 android,ios: now working on versions 19.4 / 2.4 2019-09-26 17:33:52 +02:00
Saúl Ibarra Corretgé
fd8473cb52 deps: react-native-webrtc@latest
Avoid Android crashes.
2019-09-26 17:33:52 +02:00
damencho
3f40257f89 chore(deps): update LJM to a61941fc9a1927daedc1c9446e4036702964b38a
Uses sendBeacon to send xmpp presence unavailable on unloading the window(leaving the conference).
2019-09-26 16:20:57 +01:00
paweldomas
14509adff2 chore(deps): update LJM to dda16f607ed190a0664d643d511cc87f9bfceaf1
It was referencing LJM from a PR branch instead of the master.
2019-09-25 16:57:06 -05:00
Bettenbuk Zoltan
c472537ecf update RN to 61 2019-09-25 17:31:52 +02:00
Jaya Allamsetty
d40fce741a Fix the WebGL memory leak for Blur effect in Chrome 77 and up (#4652) 2019-09-24 06:50:10 -07:00
Saúl Ibarra Corretgé
944e8f8353 android: fix NPE when handling onHostPause
If the Activity is put into the background before the ReactContext is created we
get an NPE here. While the window might be short, it's thechnically possible to
hit this, as our Crashlytics reports show.
2019-09-24 09:49:52 +02:00
Saúl Ibarra Corretgé
7d972a50f2 ios: set logger subsystem and category
The subsystem is set to the bundle ID and the category to "JitsiMeetSDK".
2019-09-24 09:49:33 +02:00
Leonard Kim
92e7be34e3 fix(icons): scope white svgs to Icon components 2019-09-20 07:50:41 -07:00
Bettenbuk Zoltan
f5dba929a5 fix: add support for functional component type icons 2019-09-20 16:35:56 +02:00
Saúl Ibarra Corretgé
64d2885233 android: raise SDK version 2019-09-20 10:25:42 +02:00
Saúl Ibarra Corretgé
4ce65ae7a7 android: make reportConnectedOutgoingCall return a Promise
The call-integration middleware relies on it returning it, as iOS does.
2019-09-20 10:25:42 +02:00
Saúl Ibarra Corretgé
07bf95f838 doc: add info on reporting security issues 2019-09-19 13:49:46 +02:00
Saúl Ibarra Corretgé
3469d5dc4f doc: move development sections to standalone file 2019-09-19 13:49:46 +02:00
Leonard Kim
8c0f942ae1 add more logging around attaching videos to thumbnails 2019-09-19 10:46:36 +02:00
Leonard Kim
b8aa74f212 fix(large-video): bring back workaround for selecting on conference join
Bring back the workaround introduced in afd2aea7
but removed in 21dcc41d. On conference join,
several other actions have already been fired
that try to set the large video participant
and select the participant on the bridge.
The problem is there is no conference during
these actions so the select participant
never fires. Then subsequent actions do not
fire select participant because the large
video participant has not changed.
2019-09-18 15:00:13 -07:00
George Politis
2a7c6681ad ref: Changes how isVideoPlayable is computed.
This commit changes how the SmallVideo.isVideoPlayable method works.

1st we remove the check on the video stream muted field (materialized with the
!this.videoStream.isMuted() guard). This check is redundant as it is
already materialized in the !this.isVideoMuted check (the isVideoMuted
field is updated with the return value of the videoStream.isMuted()
method).

2nd we return false if we're in audio only mode, because it's
(obviously) undesirable to have a playable video when in audio only
mode.
2019-09-17 18:18:44 +02:00
George Politis
324a9eba91 minor change in debug log wording 2019-09-17 18:18:44 +02:00
George Politis
fb1ed22c6c ref: Tweak logging logic for thumbnail display mode and tile view. 2019-09-17 18:18:44 +02:00
Saúl Ibarra Corretgé
00c8409e31 ios,android: update SDK version to 2.3.0 2019-09-17 13:42:08 +02:00
Leonard Kim
1b43c22940 fix(chat): update thumbs emoji strings
react-emoji-renderer 1.0.0 removed the
thumbsup and thumbsdown aliases.
2019-09-16 09:55:36 -07:00
Saúl Ibarra Corretgé
efddb36164 thumbnail: fix accessing props 2019-09-16 13:27:10 +02:00
Saúl Ibarra Corretgé
385e1c1047 media: fix creating video track when toggling the welcome page switch 2019-09-13 19:07:26 +02:00
Saúl Ibarra Corretgé
3cc181a2e5 rn,config: create a fake config if we cannot load one on the welcome page
We try to load the configuration with every room change, even when there is no
room. There is a bad (corner) case: when we have no config cached (first boot or
wiped app data). In such case the user is trapped in an infinite loop because we
require the config to show the welcome page, oh well.

Pretend we have a configuration by creating the most minimal one to at least get
to the welcome page.
2019-09-13 19:07:26 +02:00
Saúl Ibarra Corretgé
bcc1be675f thumbnail: use a functional component
Simplifies the code a bit, and we use no lifecycle methods.
2019-09-13 17:37:23 +02:00
Saúl Ibarra Corretgé
d1be5742ba thumbnail: remove dead code
Audio streams are automatically played by WebRTC and this won't change, probably
ever. There is no point in having checks and an Audio component which does
nothing.
2019-09-13 17:37:23 +02:00
Saúl Ibarra Corretgé
1b27e331da thumbnail: use a more explicit prop 2019-09-13 17:37:23 +02:00
Saúl Ibarra Corretgé
c1f7bf75c1 thumbnail: don't render dominant speaker indicator on 1-1 calls 2019-09-13 17:37:23 +02:00
Bettenbuk Zoltan
382ec011eb ref: reduce device popup bundle size 2019-09-13 17:25:32 +02:00
Bettenbuk Zoltan
8a3ddd8596 feat: SVG icons 2019-09-13 14:07:53 +02:00
Saúl Ibarra Corretgé
738a199b4c welcome: tame the linter 2019-09-13 12:28:53 +02:00
Saúl Ibarra Corretgé
40a1af5302 dev: use alpha as the default proxy target 2019-09-13 11:51:22 +02:00
Maximilian Ruta
be5dba7eea welcome: add room validation pattern
Fixes: #312
2019-09-13 11:18:58 +02:00
Saúl Ibarra Corretgé
eb15f73e59 android: don't use proguard on debug builds
It's not necessary and makes the build faster.
2019-09-12 19:43:05 +02:00
Saúl Ibarra Corretgé
2f7b485b8f android: remove unneded code 2019-09-12 19:43:05 +02:00
Saúl Ibarra Corretgé
3a885c893a android: fix running on Android 5
For some reason ART complains about these methods being overrides of package
private ones from Timber.
2019-09-12 19:43:05 +02:00
Saúl Ibarra Corretgé
6c4901a826 ios,callkit: delay updating the muted state until conference starts
In iOS 13 if the call is not unmuted when we report it to the system as started,
an action to unmute it is dispatched automagically. Thanks, Apple.

So, delay synchronizing the muted state until the conference is started (after
the join action). This creates a small window for de-synchronization, but it's
very short and it seems unavoidable.

This change is only applied to operating systems built by the fruit company in
Cupertino.
2019-09-12 12:55:02 +02:00
Saúl Ibarra Corretgé
91c1c91950 android: fix log formatting issues 2019-09-11 14:29:37 +02:00
Saúl Ibarra Corretgé
1091ac7e7d log: fix log formatting 2019-09-10 10:34:52 +02:00
damencho
0c0bd001e5 Removes disableSuspendVideo option. Updates @jitsi/lib-jitsi-meet. 2019-09-10 08:58:55 +02:00
Bettenbuk Zoltan
256994e1f8 fix: webpack proxy fix 2019-09-09 18:27:30 +02:00
Saúl Ibarra Corretgé
f6fb859531 build: exit with an error if bundle sizes increase too much
The currently selected values are a bit above the actual sizes, so if a PR
increases the bundle size enough to trigger the failure, it should bump it.
It better have a good reason for it though!
2019-09-06 17:46:25 +02:00
Saúl Ibarra Corretgé
7deb2006c3 build: add integration with webpack-bundle-analyzer
Build as follows to build (production) bundle size stats:

npx webpack -p --progress --analyze-bundle

Then open the report:

npx webpack-bundle-analyzer build/stats.json build/
2019-09-06 17:46:25 +02:00
Saúl Ibarra Corretgé
2aea24ffad dial-in-info: fix bundle bloat
The direct import sidesteps many chained-effect imports, halving the bundle
size.
2019-09-06 16:29:23 +02:00
Saúl Ibarra Corretgé
b5aae0b58d build: style 2019-09-06 16:29:23 +02:00
Saúl Ibarra Corretgé
d436825a45 flacworker: don't use the Grand Unified Logger
There are just a couple of logs in this feature, and it's a standalone bundle,
which would bloat it due to cascaded dependencis.
2019-09-06 16:29:23 +02:00
Saúl Ibarra Corretgé
5276cb6bc8 alwaysontop: don't use the Grand Unified Logger
There are just a couple of logs in this feature, and it's a standalone bundle,
which would bloat it due to cascaded dependencis.
2019-09-06 16:29:23 +02:00
Saúl Ibarra Corretgé
f863733dd3 base/util: don't use the Grand Unified Logger
There are just a couple of logs in this feature, and it's included in bundles
like external_api, which would bloat it due to cascaded dependencis.
2019-09-06 16:29:23 +02:00
Saúl Ibarra Corretgé
2ccd4968a4 external_api: don't use the Jitsi Meet logger
This is for other applications to use, and it's currently only used for logging
1 line, so use console.error.
2019-09-06 16:29:23 +02:00
Saúl Ibarra Corretgé
c233433243 deps: react-native-webrtc@latest 2019-09-05 16:37:51 +02:00
Saúl Ibarra Corretgé
6861f463b3 android: update SDK release script to publish JSC artifacts
In
b53a034aaf (diff-0339cf92cc68bc5981fe6df601316c1c)
I removed this, because RN has updated the builtin JSC version. On the next
release, however, RN introduced a new JS interpreter (Hermes) so JSC is now a RN
dependency. Thus, add the magic spells to publish the AARs to Maven.
2019-09-05 16:32:53 +02:00
Hristo Terezov
ac065f0225 chore(package.json): Update LJM 2019-09-05 04:33:02 -07:00
Saúl Ibarra Corretgé
b4b33c94dd ios: update Podfile.lock 2019-09-05 12:53:26 +02:00
Saúl Ibarra Corretgé
c8753230a4 deps: react-native-webrtc@latest 2019-09-04 20:27:33 +02:00
paweldomas
dbf569e29e chore(deps): update LJM to 00920c0f655a74c757c7fef2cdbd053ae4d1f485 2019-09-04 10:46:18 -05:00
Saúl Ibarra Corretgé
27205e3119 ios: divert RN logs to our logger 2019-09-04 17:45:18 +02:00
Saúl Ibarra Corretgé
f2fdef8361 ios: log fatal errors, don't swallow exceptions 2019-09-04 17:45:18 +02:00
George Politis
d4dd5ab46a deps: lib-jitsi-meet@latest 2019-09-04 17:29:28 +02:00
Saúl Ibarra Corretgé
902da8cc4f rn: add native loggers
These provide the ability to integrate the SDK with some other application
loggers.

At the time this was written we use Timber on Android and CocoaLumberjack on iOS.

In addition to the integration capabilities, a LogBridge React Native module
provides log transports for JavaScript code, thus centralizing all logs on the
native loggers.
2019-09-04 10:50:30 +02:00
Saúl Ibarra Corretgé
c0a5e4f203 rn: fix crash when serializing calendar entries on iOS
Original PR: https://github.com/wmcmahan/react-native-calendar-events/pull/253
2019-09-03 12:37:31 +02:00
virtuacoplenny
55ff9dbe80 feat(api): expose method for playing touch tones (#4584) 2019-08-30 14:17:22 -07:00
Hristo Terezov
bd99108e8e feat(analytics):Add white/black list functionality 2019-08-30 10:26:22 -07:00
damencho
0c042b4078 Adds config to auto turn on captions when recording is started. 2019-08-30 15:46:29 +01:00
damencho
743bbcb846 Commit from translate.jitsi.org by user damencho.: 548 of 596 strings translated (3 fuzzy). 2019-08-30 14:47:00 +00:00
damencho
4ef2f0211a Commit from translate.jitsi.org by user damencho.: 436 of 596 strings translated (18 fuzzy). 2019-08-30 14:46:52 +00:00
damencho
86f765a01f Commit from translate.jitsi.org by user damencho.: 596 of 596 strings translated (0 fuzzy). 2019-08-30 14:46:23 +00:00
damencho
9e8b8313e0 Commit from translate.jitsi.org by user damencho.: 438 of 586 strings translated (16 fuzzy). 2019-08-30 14:46:11 +00:00
damencho
f4a8115b00 Commit from translate.jitsi.org by user damencho.: 552 of 586 strings translated (3 fuzzy). 2019-08-30 14:46:01 +00:00
Bettenbuk Zoltan
a93bd422d3 feat: new invite layout 2019-08-28 13:57:33 +02:00
Bettenbuk Zoltan
c1598b7376 feat: make display name prompt platform independent 2019-08-26 22:20:22 +02:00
Leonard Kim
bc403adb46 feat(api): allow for explicit screenshare state toggling 2019-08-26 06:53:28 -07:00
Bettenbuk Zoltan
1941275f93 feat: mobile chat emojis 2019-08-26 15:37:58 +02:00
paweldomas
6ae9bbe0c5 feat: report analytics for the network connection
Will emit new 'network.info' action with the online/offline status and
extra details for native like the network type and
'isConnectionExpensive' flag.
2019-08-23 13:36:33 -05:00
Jip-Hop
2c70388a9e Get participant specific video element
* Get participant specific video element

We now have the ability to select the video element for specific participants. I'm tweaking the jitsi-meet-electron app for my use case. I need to open Always On Top windows for specific participants, so the current _getLargeVideo() wont suffice.

I made a post about this in the Developers section on the Jitsi Community Forum, but it got blocked by Akismet.

* Add dots at end of sentence.

* Fixed ESlint errors and add additional check for iframe.

* Use _myUserID instead of string.

* Return the local video by default if participantId is undefined.

* Fixed mistake in string template.
2019-08-23 08:35:10 -07:00
Saúl Ibarra Corretgé
e0815de2ad rn,welcome: fix accessing local participant
It need not always exist, since it's created asynchronousluy on app
initiualization. Make sure we are ready for it.

I've seen backtraces because of this.
2019-08-23 17:16:22 +02:00
Saúl Ibarra Corretgé
02e058370e logging: disable caller info globally 2019-08-23 17:11:29 +02:00
Saúl Ibarra Corretgé
8a7b795d37 deps: lib-jitsi-meet@latest 2019-08-23 17:11:29 +02:00
Saúl Ibarra Corretgé
d24ca796ad deps: jitsi-meet-logger@latest 2019-08-23 17:11:29 +02:00
Leonard Kim
af2c61fd96 fix(pinning): dynamically check auto-pin setting
Any overrides set on interfaceConfig are not
applied on module load. As such, call to get
the value of the auto pin setting, providing
time for the bootstrapping to set overrides.
Otherwise iframe api users cannot override
the setting.
2019-08-23 07:36:27 -07:00
Saúl Ibarra Corretgé
5a934c071a logging: use individual, names loggers
React Native doesn't define __filename nor __dirname so do it artisanally. In
addition, this helps with centralizing the configuration passed to loggers.
2019-08-23 10:57:38 +02:00
Saúl Ibarra Corretgé
bfe4237430 deps: update jitsi-meet-logger 2019-08-23 10:57:38 +02:00
damencho
84c60a5fdf Removes i18n strings for p2p and turn. 2019-08-22 15:44:13 +01:00
Bettenbuk Zoltan
cfc7210ac8 feat: add send message button 2019-08-22 12:03:39 +02:00
damencho
57ac951192 Commit from translate.jitsi.org by user damencho.: 533 of 597 strings translated (3 fuzzy). 2019-08-21 12:26:23 +00:00
Saúl Ibarra Corretgé
36fee03d5e deps: update lib-jitsi-meet 2019-08-21 12:52:09 +02:00
paweldomas
14b747e0a4 fix(ios/xcode): increase node heap space for the bundle JS step 2019-08-21 11:12:56 +02:00
Saúl Ibarra Corretgé
c113b2e765 ios: update Podfile.lock 2019-08-21 11:12:56 +02:00
Saúl Ibarra Corretgé
560e65da37 deps: update lib-jitsi-meet 2019-08-21 11:12:56 +02:00
Saúl Ibarra Corretgé
861cf7d842 deps: react-redux@7.1.0
RN 0.60 loudly complains (loudly) about deprecated methods which react-redux was using.
2019-08-21 11:12:56 +02:00
Saúl Ibarra Corretgé
dd23ed09ad deps: react-native@0.60 2019-08-21 11:12:56 +02:00
Saúl Ibarra Corretgé
64897b9c91 rn,toolbox: simplify logic for showing Toolbox on mobile 2019-08-20 20:04:27 +02:00
Saúl Ibarra Corretgé
0dc8c687f2 rn,filmstrip: ignore the 'visible' parameter on mobile
Mobile uses a different logic for deciding whether to show the filmstrip or not:
if there are more than 1 participants or not, and there is no way to manually
toggle it.
2019-08-20 20:04:27 +02:00
paweldomas
c65d29d1a7 travis: wait 10 seconds after script to catch the error logged
Adds wait time suggested by Travis support in order to see the last
error logged.
2019-08-20 10:20:59 -05:00
Saúl Ibarra Corretgé
6aa895b679 android: attempt to fix assertion errors in ReactInstanceManager
This is the main one:
df4e67fe75/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java (L512)

Why this happens is a mystery wrapped in an enigma.
2019-08-16 17:59:47 +02:00
Saúl Ibarra Corretgé
dae451e6fa android: fix crash when parsing calendar events
Upstream PR: https://github.com/wmcmahan/react-native-calendar-events/pull/268
2019-08-16 17:46:53 +02:00
Hristo Terezov
3c1f056d2a fix(dialog): Don't steal focus 2019-08-16 07:40:47 -07:00
Leonard Kim
663e0a6693 fix(deep-linking): add back description copy 2019-08-16 16:30:16 +03:00
Saúl Ibarra Corretgé
53f98df8f3 deps: react-native@0.59.10 2019-08-16 07:26:54 +02:00
Saúl Ibarra Corretgé
6616f728f6 proximity: enable the proximity sensor when the device is set to earpiece 2019-08-14 18:57:03 +02:00
Saúl Ibarra Corretgé
1c1e8a942b audio-mode: refactor device handling
This commit refactors device selection (more heavily on iOS) to make it
consistent across platforms.

Due to its complexity I couldn't break out each step into separate commits,
apologies to the reviewer.

Changes made to device handling:

- speaker is always the default, regardless of the mode
- "Phone" shows as a selectable option, even in video call mode
- "Phone" is not displayed when wired headphones are present
- Shared device picker between iOS and Android
- Runtime device updates while the picker is open
2019-08-14 18:57:03 +02:00
George Politis
9721d99918 Updates lib-jitsi-meet. 2019-08-14 16:00:46 +02:00
Saúl Ibarra Corretgé
69c21cbb11 avatar: make code a bit clearer
Uses nullish coalescing and optional chaining.
2019-08-14 15:36:40 +02:00
Saúl Ibarra Corretgé
abefc56750 deps: add babel plugins for optional chaining and nullish coalescing
They are in Stage 3 and are already dependencies or the React Native preset.

References:
https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining
https://babeljs.io/docs/en/babel-plugin-proposal-nullish-coalescing-operator
2019-08-14 15:36:40 +02:00
damencho
a4a1685224 chore(lib-jitsi-meet): Update. 2019-08-13 10:16:20 -07:00
damencho
21fb225726 Adds a retry logic when fetching conference numbers and pin. 2019-08-13 19:00:18 +03:00
damencho
75ab890707 Uses the wrapped fetch from base/util. 2019-08-13 19:00:18 +03:00
damencho
2ded8363ad Leaves room only when it is already joined.
In case of hitting errors like max participant limit reached and when clicking hangup, the attempt to leave room second time results error and reload screen.
2019-08-13 16:04:43 +03:00
Leonard Kim
c0a8a386a5 fix(pinning): debounce autopin subscriber
The store's remote tracks get cleared
and re-added when switching in and out
of p2p, which can cause pinning thrashing.
Avoid that with a debounce.
2019-08-12 17:31:53 -07:00
Leonard Kim
7af081ea99 fix(avatars): re-render avatar on any resize 2019-08-12 12:41:57 -07:00
Дамян Минков
8800cb4580 Adds live streaming sound notification. (#4532)
* Adds live streaming sound notification.

* Adds ios resources for the new files.
2019-08-12 18:34:38 +03:00
Karthik Muralidharan
b658f20a30 feat(API): add dominant speaker changed event
Fixes: https://github.com/jitsi/jitsi-meet/issues/4049
2019-08-09 10:09:33 +02:00
Hristo Terezov
c3e52f32f9 feat: Add logging for thumbnail display mode and tile view. 2019-08-09 01:08:19 -07:00
George Politis
ab73d808fe Updates lib-jitsi-meet. 2019-08-08 18:05:50 +02:00
dependabot[bot]
e110460793 Bump jquery from 3.3.1 to 3.4.0
Bumps [jquery](https://github.com/jquery/jquery) from 3.3.1 to 3.4.0.
- [Release notes](https://github.com/jquery/jquery/releases)
- [Commits](https://github.com/jquery/jquery/compare/3.3.1...3.4.0)

Signed-off-by: dependabot[bot] <support@github.com>
2019-08-08 14:37:18 +02:00
dependabot[bot]
c0ee451ca1 Bump lodash from 4.17.11 to 4.17.13
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.11 to 4.17.13.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.11...4.17.13)

Signed-off-by: dependabot[bot] <support@github.com>
2019-08-08 10:34:51 +02:00
Saúl Ibarra Corretgé
67dca97d1d rn,participants: don't render thumbnnail for screen-shares 2019-08-07 20:16:13 +02:00
Saúl Ibarra Corretgé
fd0ca76255 web,avatar: fix setting avatar size
Use the width of its container as the size, so the font icons can be rendered at
a good size.
2019-08-07 20:16:13 +02:00
Saúl Ibarra Corretgé
149e53af76 avatar: render a special avatar if the user is sharing their screen 2019-08-07 20:16:13 +02:00
Saúl Ibarra Corretgé
f3e7952e51 audio-only: implement initial "low bandwidth mode"
It's an evolution of audio-only mode, where we also allow for receiving a remote
screen-share.

Diving deeper: this basically sets last N to 1 or 0 depending on the
availability of a screen-share.
2019-08-07 20:16:13 +02:00
Saúl Ibarra Corretgé
0f77cf9e0c android: use adaptive icons 2019-08-07 14:40:20 +02:00
damencho
66eb09fb63 Revert raisedHand thumb tooltip i18n string. 2019-08-07 14:59:24 +03:00
Hristo Terezov
b7f2c7d487 chore(lib-jitsi-meet): Update. 2019-08-06 01:59:01 -07:00
Bettenbuk Zoltan
bed3f62536 feat: make links in chat clickable 2019-08-06 08:48:37 +02:00
Leonard Kim
e8eb7e1e1f fix(suboptimal-browser): remove inserting of app name in notification
This prevents inserting any user overridden APP_NAME
values into html. A new translation key is being used
to immediately stop non-english languages from using the
problematic string.

Also tweaked the copy to remove the "eer" and fix
some grammar.
2019-08-05 06:47:44 -07:00
Leonard Kim
1409d6fb5e fix(deep-linking): do not accept native app name as raw html
There is no need to display the native app name as
raw html.
2019-08-04 06:45:00 -07:00
Bettenbuk Zoltan
a2cbd9229a fix: render notification description on mobile 2019-08-02 21:14:19 +02:00
Saúl Ibarra Corretgé
bf50a110c7 lastn: simplify computing the effective last N value 2019-08-02 15:54:47 +02:00
Saúl Ibarra Corretgé
467c9d36cf audio-only,lastn: move audio-only and last N handling to standalone features
This refactors all handling of audio-only and last N to 2 features in preparation
for "low bandwidth mode".

The main motivation to do this is that lastN is a "global" setting so it helps
to have all processing for it in a single place.
2019-08-02 15:54:47 +02:00
damencho
6ddd17c769 Removes resolveAppName postprocess we do not need.
i18next.options are initialized before interfaceConfig overwrites are processed which leads to wrong translations.
2019-08-01 12:47:58 +01:00
virtuacoplenny
732f2c1963 ref(feedback): emit api feedback submitted on completion (#4499)
* ref(feedback): emit api feedback submitted on completion

Compared to firing the event on submission because
the submission ajax will not be completed at that
time..

* squash: update package.json
2019-07-31 10:59:22 -07:00
Bettenbuk Zoltan
e7144eb674 Get chat message display name from backend 2019-07-31 10:48:22 +02:00
Saúl Ibarra Corretgé
b5489b7c81 rn,filmstrip: don't unpin participant when hiding filmstrip
The filmstrip is currently only hidden when PiP mode is entered. There is no
real reason not to leave the large view as it was.
2019-07-30 18:51:40 +02:00
Saúl Ibarra Corretgé
4d5332cadf rn,conference: log pin participant analytics events also on mobile 2019-07-30 17:11:43 +02:00
Saúl Ibarra Corretgé
5261f61110 conference: don't pin participants on the JVB
When a participant is pinned in the UI we then proceed to mark it as selected on
the JVB. This will make the participant part of the last N set and will receive
the highest (or configured highest) video quality.

Pinning a participant at the JVB level just makes sure it will be part of the
last N set.

Since only one participant can be pinned in the UI, there is no point in pinning
it at the JVB level, since selecting it already achieved the same result.
2019-07-30 17:11:43 +02:00
paweldomas
8c7d6da5d5 deps: update LJM to get analytics fixes
Bumps LJM to 3c8058743177b6b6963d3bc3df14f4ea650ef310.
2019-07-30 15:45:43 +02:00
Leonard Kim
6d91728a74 fix(proxy): stop screenshare when the connection stops 2019-07-30 06:40:57 -07:00
Hristo Terezov
9b32fc95eb chore(package): update lib-jitsi-meet version. 2019-07-30 01:55:02 -07:00
Leonard Kim
6bc1d87753 feat(shortcuts): add shortcut for opening video quality modal 2019-07-29 11:40:07 -07:00
damencho
a5fc62b920 Updates correct loading and fix checking is dominant speaker. 2019-07-26 11:18:55 +01:00
Saúl Ibarra Corretgé
49f6010905 pagedlist: fix refresh on wrapped components 2019-07-26 10:38:54 +02:00
paweldomas
1c27f567ee feat: send analytics event when conference is rejoined 2019-07-26 08:25:53 +02:00
paweldomas
7684b2bf98 ref: move getCurrentConferenceUrl to base/connection
Moves getCurrentConferenceUrl method to base/connection to allow reuse.
The new location is not ideal, but looks the best based on the imports
required (trying to avoid circular dependencies).
2019-07-26 08:25:53 +02:00
damencho
8886bcdb73 Fixes missing strings. 2019-07-26 07:07:31 +01:00
Saúl Ibarra Corretgé
437bdb92be deps: update react-native-webrtc and lib-jitsi-meet
Support for the MediaStream constructor has been added to react-native-webrtc.
2019-07-25 23:52:54 +02:00
Saúl Ibarra Corretgé
6943659dc9 rn: enable auto-pinning on screenshare on mobile 2019-07-25 14:27:04 +02:00
Saúl Ibarra Corretgé
35f934e197 config: remove old backwards compatibility shim
It was added for
3ea2f00578
over 2 years ago. That's long enough!
2019-07-25 11:45:16 +02:00
damencho
aca0469bd0 Fixes mobile wifi stats timestamp value type. 2019-07-25 10:20:48 +01:00
Дамян Минков
11f2e7291e Talk while muted sound notification (#4473)
* Moves talk while muted as a new feature.

* Adds sound notification for talk while muted.

* Reorder imports and changes the dispatch of the notification.

* Introduces sounds.js for talk while muted.
2019-07-23 21:56:05 +01:00
Bettenbuk Zoltan
7a985dd843 fix: disable reduced UI on welcome page 2019-07-23 17:52:03 +02:00
damencho
e09ea36055 Maps available locales for countries to the doubled languages.
This maps the tow letter languages as enGB to the country file for en.
Copies countries-en.json as countries-enGB.json.
2019-07-23 10:10:03 +01:00
damencho
ad3a087091 Commit from translate.jitsi.org by user damencho.: 532 of 586 strings translated (9 fuzzy). 2019-07-22 23:06:01 +00:00
damencho
d379b4d53b Commit from translate.jitsi.org by user damencho.: 546 of 586 strings translated (0 fuzzy). 2019-07-22 23:05:43 +00:00
damencho
7a99d57274 Commit from translate.jitsi.org by user damencho.: 478 of 586 strings translated (6 fuzzy). 2019-07-22 23:00:06 +00:00
damencho
85bc0ed5fc Commit from translate.jitsi.org by user damencho.: 535 of 586 strings translated (6 fuzzy). 2019-07-22 22:57:36 +00:00
damencho
f4dda6e7ce Commit from translate.jitsi.org by user damencho.: 434 of 586 strings translated (15 fuzzy). 2019-07-22 22:54:29 +00:00
damencho
5c8dfabf7c Commit from translate.jitsi.org by user damencho.: 538 of 586 strings translated (0 fuzzy). 2019-07-22 22:50:47 +00:00
damencho
20d03f3228 Commit from translate.jitsi.org by user damencho.: 343 of 586 strings translated (18 fuzzy). 2019-07-22 22:46:35 +00:00
damencho
268137bb97 Commit from translate.jitsi.org by user damencho.: 342 of 586 strings translated (19 fuzzy). 2019-07-22 22:44:50 +00:00
damencho
f87463780c Commit from translate.jitsi.org by user damencho.: 506 of 586 strings translated (1 fuzzy). 2019-07-22 22:42:16 +00:00
damencho
6b2ced63c7 Commit from translate.jitsi.org by user damencho.: 506 of 586 strings translated (0 fuzzy). 2019-07-22 22:40:00 +00:00
damencho
d6fbe55360 Commit from translate.jitsi.org by user damencho.: 411 of 586 strings translated (9 fuzzy). 2019-07-22 22:38:36 +00:00
damencho
0feeb94fc7 Commit from translate.jitsi.org by user damencho.: 491 of 586 strings translated (5 fuzzy). 2019-07-22 22:36:34 +00:00
damencho
c9c509ec51 Commit from translate.jitsi.org by user damencho.: 539 of 586 strings translated (4 fuzzy). 2019-07-22 22:34:45 +00:00
damencho
f7ff457a3b Commit from translate.jitsi.org by user damencho.: 536 of 586 strings translated (3 fuzzy). 2019-07-22 22:32:29 +00:00
damencho
72127aaa0b Commit from translate.jitsi.org by user damencho.: 505 of 586 strings translated (4 fuzzy). 2019-07-22 22:30:42 +00:00
damencho
b8854a3b45 Commit from translate.jitsi.org by user damencho.: 538 of 586 strings translated (4 fuzzy). 2019-07-22 22:27:01 +00:00
damencho
e6e7001857 Commit from translate.jitsi.org by user damencho.: 535 of 586 strings translated (7 fuzzy). 2019-07-22 22:19:53 +00:00
damencho
6d4601fe66 Updates used languages and fixes loading them. 2019-07-22 17:48:44 +01:00
damencho
8ecb01ec21 Commit from translate.jitsi.org by user damencho.: 545 of 586 strings translated (1 fuzzy). 2019-07-22 14:48:34 +00:00
damencho
14ccb41015 Commit from translate.jitsi.org by user damencho.: 545 of 586 strings translated (1 fuzzy). 2019-07-22 14:29:08 +00:00
damencho
348cde54e7 Commit from translate.jitsi.org by user damencho.: 545 of 586 strings translated (1 fuzzy). 2019-07-22 14:24:36 +00:00
damencho
430bd3f141 Commit from translate.jitsi.org by user damencho.: 535 of 586 strings translated (7 fuzzy). 2019-07-22 13:03:23 +00:00
damencho
b36f419e86 Commit from translate.jitsi.org by user damencho.: 122 of 586 strings translated (28 fuzzy). 2019-07-22 13:03:11 +00:00
damencho
ed1d03db71 Commit from translate.jitsi.org by user damencho.: 524 of 586 strings translated (5 fuzzy). 2019-07-22 13:02:58 +00:00
damencho
5d837571c0 Commit from translate.jitsi.org by user damencho.: 538 of 586 strings translated (5 fuzzy). 2019-07-22 13:02:46 +00:00
damencho
f23532d7f4 Commit from translate.jitsi.org by user damencho.: 78 of 586 strings translated (12 fuzzy). 2019-07-22 13:02:33 +00:00
damencho
0280294a8f Commit from translate.jitsi.org by user damencho.: 80 of 586 strings translated (12 fuzzy). 2019-07-22 13:02:19 +00:00
damencho
fdd1418236 Commit from translate.jitsi.org by user damencho.: 433 of 586 strings translated (16 fuzzy). 2019-07-22 13:02:05 +00:00
damencho
bae47434b1 Commit from translate.jitsi.org by user damencho.: 531 of 586 strings translated (10 fuzzy). 2019-07-22 13:01:52 +00:00
damencho
3c98cfc136 Commit from translate.jitsi.org by user damencho.: 216 of 586 strings translated (36 fuzzy). 2019-07-22 13:01:25 +00:00
damencho
17e5bc7b73 Commit from translate.jitsi.org by user damencho.: 89 of 586 strings translated (5 fuzzy). 2019-07-22 13:01:11 +00:00
damencho
6092655099 Commit from translate.jitsi.org by user damencho.: 503 of 586 strings translated (6 fuzzy). 2019-07-22 13:00:59 +00:00
damencho
721cca669f Commit from translate.jitsi.org by user damencho.: 0 of 586 strings translated (0 fuzzy). 2019-07-22 13:00:49 +00:00
damencho
c62ff0939d Commit from translate.jitsi.org by user damencho.: 505 of 586 strings translated (1 fuzzy). 2019-07-22 12:58:57 +00:00
damencho
ecf050c2d9 Commit from translate.jitsi.org by user damencho.: 344 of 586 strings translated (15 fuzzy). 2019-07-22 12:58:46 +00:00
damencho
069137b02a Commit from translate.jitsi.org by user damencho.: 25 of 586 strings translated (0 fuzzy). 2019-07-22 12:58:34 +00:00
damencho
f053cad66c Commit from translate.jitsi.org by user damencho.: 123 of 586 strings translated (20 fuzzy). 2019-07-22 12:58:22 +00:00
damencho
4be3042cb0 Commit from translate.jitsi.org by user damencho.: 445 of 586 strings translated (25 fuzzy). 2019-07-22 12:58:09 +00:00
damencho
593235d683 Commit from translate.jitsi.org by user damencho.: 490 of 586 strings translated (6 fuzzy). 2019-07-22 12:57:56 +00:00
damencho
7bd8ddbba2 Commit from translate.jitsi.org by user damencho.: 39 of 586 strings translated (2 fuzzy). 2019-07-22 12:57:44 +00:00
damencho
14232b1d93 Commit from translate.jitsi.org by user damencho.: 515 of 586 strings translated (17 fuzzy). 2019-07-22 12:57:25 +00:00
damencho
ff4887a3c8 Commit from translate.jitsi.org by user damencho.: 535 of 586 strings translated (4 fuzzy). 2019-07-22 12:57:12 +00:00
damencho
0289aacd4f Commit from translate.jitsi.org by user damencho.: 505 of 586 strings translated (2 fuzzy). 2019-07-22 12:57:01 +00:00
damencho
99c13eaec0 Commit from translate.jitsi.org by user damencho.: 254 of 586 strings translated (22 fuzzy). 2019-07-22 12:56:48 +00:00
damencho
1a713dfa27 Commit from translate.jitsi.org by user damencho.: 536 of 586 strings translated (4 fuzzy). 2019-07-22 12:56:36 +00:00
damencho
7e84b51ee0 Commit from translate.jitsi.org by user damencho.: 534 of 586 strings translated (4 fuzzy). 2019-07-22 12:56:22 +00:00
damencho
86fa442d0e Commit from translate.jitsi.org by user damencho.: 148 of 586 strings translated (18 fuzzy). 2019-07-22 12:55:58 +00:00
damencho
93b88f1a16 Commit from translate.jitsi.org by user damencho.: 342 of 586 strings translated (19 fuzzy). 2019-07-22 12:55:46 +00:00
damencho
2041d4fffb Commit from translate.jitsi.org by user damencho.: 534 of 586 strings translated (0 fuzzy). 2019-07-22 12:54:35 +00:00
jitsi-pootle
83c3e7c725 New files added from translate.jitsi.org based on templates 2019-07-22 12:53:40 +00:00
damencho
a34331891a Commit from translate.jitsi.org by user damencho.: 477 of 586 strings translated (7 fuzzy). 2019-07-22 12:46:37 +00:00
damencho
56dd83740c Commit from translate.jitsi.org by user damencho.: 533 of 586 strings translated (8 fuzzy). 2019-07-22 12:46:23 +00:00
damencho
debe50c3f4 Commit from translate.jitsi.org by user damencho.: 227 of 586 strings translated (22 fuzzy). 2019-07-22 12:46:08 +00:00
damencho
d9985c786c Commit from translate.jitsi.org by user damencho.: 5 of 586 strings translated (0 fuzzy). 2019-07-22 12:42:06 +00:00
damencho
ed910947d4 Commit from translate.jitsi.org by user damencho.: 268 of 586 strings translated (27 fuzzy). 2019-07-22 12:41:32 +00:00
damencho
c4c5eace25 Commit from translate.jitsi.org by user damencho.: 206 of 586 strings translated (29 fuzzy). 2019-07-22 12:40:07 +00:00
damencho
c6bf646fe8 Commit from translate.jitsi.org by user damencho.: 410 of 586 strings translated (10 fuzzy). 2019-07-22 12:37:36 +00:00
jitsi-pootle
ab2bb7686e New files added from translate.jitsi.org based on templates 2019-07-22 10:14:05 +00:00
jitsi-pootle
12b53c38b9 New files added from translate.jitsi.org based on templates 2019-07-22 10:14:05 +00:00
jitsi-pootle
ca31c8b199 New files added from translate.jitsi.org based on templates 2019-07-22 10:14:05 +00:00
jitsi-pootle
1b331ce090 New files added from translate.jitsi.org based on templates 2019-07-22 10:14:05 +00:00
jitsi-pootle
edd55e7798 New files added from translate.jitsi.org based on templates 2019-07-22 10:14:05 +00:00
Дамян Минков
9f4da84701 I18next update (#4456)
* Removes unused translations.

* Fixes using translated strings.

* Moves using latest i18next versions and stop using compatibility modes.

* Sorts i18next options.

* Fixes defaultNS used by i18next.

This is used when translating html tags with data-i18n keys as attributes, used by jQuery-Impromptu.
2019-07-22 11:10:25 +01:00
Leonard Kim
0097360396 feat(deep-linking): allow disabling through override 2019-07-19 14:48:46 -07:00
Hristo Terezov
b5cef05941 fix(AOT): imports. 2019-07-19 07:17:15 -07:00
George Politis
ddab9224b2 Merge pull request #4460 from jitsi/analytics-event-for-connectivity-issues
deps: update lib-jitsi-meet
2019-07-18 18:26:28 +02:00
George Politis
a5693c6e96 updates package-lock.json 2019-07-18 17:25:46 +02:00
George Politis
8dd345b192 deps: update lib-jitsi-meet
With the update we get a new analytics event that sends connectivity
issues events to the analytics backend.
2019-07-18 17:10:29 +02:00
Leonard Kim
130ab886ee fix(chat): show toolbar on open 2019-07-18 06:08:27 -07:00
Leonard Kim
c0e9f2b95a fix(chat): workaround for chat scroll causing layout misalignment 2019-07-18 06:08:27 -07:00
Leonard Kim
011972872e fix(blur): add beta label to toolbar button 2019-07-16 10:35:31 -07:00
Saúl Ibarra Corretgé
ef5720a3f0 deps: update react-native-webrtc
Fixes: https://github.com/jitsi/jitsi-meet/issues/4436
2019-07-16 17:55:37 +01:00
Bettenbuk Zoltan
ec30af2844 feat: always show labels 2019-07-16 17:32:58 +02:00
Bettenbuk Zoltan
e08aeca28c feat: use css to place the toolbox buttons 2019-07-16 17:01:24 +02:00
Bettenbuk Zoltan
ad7892ebce aot: jigasi participant icon 2019-07-16 13:04:17 +02:00
Saúl Ibarra Corretgé
cbc7e1b6be android: fix crash if UserInfo is not set
Fixes: https://github.com/jitsi/jitsi-meet-sdk-samples/issues/11
2019-07-16 07:29:07 +01:00
Saúl Ibarra Corretgé
da7d959b9a android: raise SDK version 2019-07-16 07:29:07 +01:00
virtuacoplenny
ada57ebcd0 ref(user-interaction): do not store listener, move browser check to lib (#4441)
* ref(user-interaction): remove storing of listener

* ref(user-interaction): move browser requirement check to lib-jitsi-meet

* ref(user-interaction): no inner function for listener, use module scope
2019-07-13 06:59:58 -07:00
Bettenbuk Zoltan
1993ad10eb feat: apply color brand guidelines 2019-07-12 20:48:49 +02:00
Bettenbuk Zoltan
d305caf910 feat: borderless toolbox icons 2019-07-12 20:48:49 +02:00
Bettenbuk Zoltan
a25a504a59 feat: add bottom gradient 2019-07-12 20:48:49 +02:00
damencho
250c61279b Updates react-native callstats dependency, to use siteID on mobile. 2019-07-12 18:24:44 +01:00
Saúl Ibarra Corretgé
1e18af2d1a rn: fix not showing a prompt when permissions have been denied
This only shows up if we cannot prompt again, like when turning permissions off
in the Settings app in iOS.
2019-07-12 17:50:54 +02:00
Дамян Минков
eb1fd4fd88 Merge pull request #4438 from jitsi/cs-siteid
Passes confID when initializing JitsiConference.
2019-07-12 14:25:35 +01:00
Matthias Herzog
e0c8b6b3c0 fix welcome page title fixes #4273 2019-07-12 14:25:26 +01:00
Дамян Минков
9071cd4813 Listens for suspend events from jitsi-power-monitor on postis channel. (#4428)
* Listens for suspend events from jitsi-power-monitor on postis channel.

* Removes duplicated type and actions.

* Moves suspendDetected state from overlay to power-monitor feature.
2019-07-12 14:08:34 +01:00
Saúl Ibarra Corretgé
9bf650c700 android: keep okio classes
Fixes running profile builds.
2019-07-12 14:22:36 +02:00
Saúl Ibarra Corretgé
49e3b03885 android: custom initialization of the WebRTC module
Set our own audio device manager so we can tweak it if need be (enabling /
disabling the HW AEC on specific devices).

Switch to using the software video encoder / decoder. This may feel like a
downgrade, but it has advantages:

- simulcast is now working (on par with iOS)
- certain devices have broken VP8 HW encoders (I'm looking at you Samsung Galaxy
S7) so this fixes that
2019-07-12 14:22:36 +02:00
Saúl Ibarra Corretgé
31e996ac3f android: always run adb reverse when starting the packager
It tends to close, so always open the reverse tunnel.
2019-07-12 14:22:36 +02:00
Saúl Ibarra Corretgé
d1c447e97b deps: update react-native-webrtc
- Fix crash in PeerConnection.close (Android)
- Add ability to customize PeerConnectionFactory (Android)
2019-07-12 14:22:36 +02:00
damencho
8a34c0a80f Updates lib-jitsi-meet. 2019-07-12 11:17:53 +01:00
virtuacoplenny
2f626ea474 ref(api): move participant join and left to middleware (#4365) 2019-07-11 12:44:27 -07:00
Bettenbuk Zoltan
0a76eebca7 feat: central back button registry 2019-07-11 16:14:08 +02:00
damencho
0e9e695251 Passes confID when initializing JitsiConference.
The confID is domain.com/RoomName or domain.com/tenant/RoomName, adds the tenant if any and also keeps the room name case.
2019-07-11 14:58:14 +01:00
Leonard Kim
b86df7a8e3 fix(remote-control): do not assume failed query is missing support
Multiple requests for checkUserRemoteControlSupport can be in
flight simultaneously. Order of promise resolution is not
guaranteed. It is possible for Request A and Request B to be
in flight and then Request B's promise chain resolves first.
Request A could have encountered errors and then resolve. Then
what could happen is checkUserRemoteControlSupport returns true
for remote control support due to Request B and the UI updates.
But then checkUserRemoteControlSupport returns false for
remote control support due to Request A's error and the UI
updates to hide remote control.
2019-07-11 07:25:08 +01:00
Bettenbuk Zoltan
5b25e02e26 feat: use dialog instead of alert when inviting 2019-07-10 20:50:14 +02:00
Bettenbuk Zoltan
0e6f14bb7c fix: avoid false triggering CDU in SlidingView 2019-07-10 20:49:50 +02:00
Leonard Kim
57b9954d9c fix(api): support params with value of undefined 2019-07-10 11:09:11 -07:00
virtuacoplenny
249dd7b8b8 fix(invite): decode the meeting name (#4411)
* fix(invite): decode the meeting name

* squash: try to make mobile join same encoded meeting name as web

* Decodes and generated texts for share and copy meeting info.

Decodes in all cases except when it contains a space, as it will generate wrong links when pasted/shared in external applications.
2019-07-10 10:27:11 -07:00
Bettenbuk Zoltan
b31d7b4451 ref: remove createStyleSheet from dialog styles 2019-07-10 16:42:56 +02:00
Bettenbuk Zoltan
8dea3389ee fix: avoid clicking behind dialogs 2019-07-10 16:42:56 +02:00
virtuacoplenny
e7f9e8e7f7 fix(conference): require user interaction to unmute in iframe (#4429)
* fix(conference): require user interaction to unmute in iframe

* squash: explicitly whitelist react native
2019-07-10 12:02:27 +01:00
Bettenbuk Zoltan
a53a86ee7a feat: add room lock pwd error message 2019-07-10 11:01:49 +02:00
Saúl Ibarra Corretgé
598b6f0598 deps: update react-native-webrtc
WebRTC is now at M75
2019-07-09 17:22:32 +02:00
Saúl Ibarra Corretgé
b245945c4a android: be explicit about starting a foreground service
After calling startService we are supposed to have a bit of time before turning
the service into a foreground service, but certain devices seem to be more
spartan and we've seen the following failure:

Caused by java.lang.IllegalStateException: Not allowed to start service Intent { act=JitsiMeetOngoingConferenceService:START cmp=org.jitsi.meet/.sdk.JitsiMeetOngoingConferenceService }: app is in background uid UidRecord{f6778d5 u0a220 CAC  bg:+1m1s417ms idle change:idle procs:1 proclist:15604, seq(0,0,0)}
       at android.app.ContextImpl.startServiceCommon + 1600(ContextImpl.java:1600)
       at android.app.ContextImpl.startService + 1546(ContextImpl.java:1546)
       at android.content.ContextWrapper.startService + 669(ContextWrapper.java:669)
       at org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService.launch + 50(JitsiMeetOngoingConferenceService.java:50)

Be expliocit and call startForegroundService, on supported platforms.
2019-07-09 16:14:39 +02:00
Bettenbuk Zoltan
301a8e319a fix: reduce avatar font size 2019-07-09 15:45:38 +02:00
Saúl Ibarra Corretgé
820abfd059 ios: sync Podfile.lock 2019-07-09 12:58:07 +02:00
Hristo Terezov
1d42420a36 fix(font): Bring back the missing icons. 2019-07-08 20:03:04 +01:00
Hristo Terezov
1b4bdb5142 fix(blur): on FF 2019-07-08 20:03:04 +01:00
Hristo Terezov
f030a3f1fb fix(blur): when switching video tracks. 2019-07-08 20:03:04 +01:00
Hristo Terezov
8f79779ca7 fix(blur): Disable for SS 2019-07-08 20:03:04 +01:00
Saúl Ibarra Corretgé
c5111bb359 rn: update default color scheme 2019-07-08 20:27:44 +02:00
Bettenbuk Zoltan
42814eac7d feat: add icon based avatar and icon for pstn 2019-07-08 16:53:30 +02:00
Bettenbuk Zoltan
74d0013acc feat: use participant id for avatar color 2019-07-08 16:53:30 +02:00
Bettenbuk Zoltan
88e4850c4d fix: remove locally generated avatar ID 2019-07-08 16:53:30 +02:00
Bettenbuk Zoltan
87f9b1cf92 ref: remove unnecesary functions 2019-07-08 16:53:30 +02:00
Bettenbuk Zoltan
fe1187d7b7 ref: remove unused libs 2019-07-08 16:53:30 +02:00
Bettenbuk Zoltan
a35b36d6df feat: dynamic avatar font size 2019-07-08 16:53:30 +02:00
Bettenbuk Zoltan
a04982fd96 fix: AlwaysOnTop avatar 2019-07-08 16:53:30 +02:00
Bettenbuk Zoltan
658679f89e fix: undefined participant in avatar call 2019-07-08 16:53:30 +02:00
Bettenbuk Zoltan
f90fc665f8 ui: bigger padding to default avatar 2019-07-08 16:53:30 +02:00
Bettenbuk Zoltan
0c8130af41 fix: default avatar relative url 2019-07-08 16:53:30 +02:00
paweldomas
e4c5968459 fix: bring back the add meeting URL button
Brings back the button for generating meeting URLs for calendar events.
2019-07-08 09:26:33 -05:00
damencho
a148cd41a4 Fixes loading wifi-stats when used from jitsi-meet electron utils. 2019-07-08 14:30:20 +01:00
Saúl Ibarra Corretgé
f8a049759d android: fix crash if notification is null
I cannot see a path leading to it being null, but Crashlytics demonstrated it's
possible (so far in a small subset of old devices). Be defensive then.
2019-07-05 10:11:00 +02:00
Hristo Terezov
4b4225e14f chore(lib-jitsi-meet): Update. 2019-07-04 06:26:32 -07:00
Hristo Terezov
3b0c5d0b6a fix(blur): many small issues. 2019-07-04 06:26:32 -07:00
Cristian Florin Ghita
3b750ddd5a Add video background blur 2019-07-04 06:26:32 -07:00
Saúl Ibarra Corretgé
6383d000a9 rn: raise version to 19.3 2019-07-03 21:09:15 +02:00
Bettenbuk Zoltan
a48d67bdc7 fix: inconsistent state when quickly closing a sliding view 2019-07-03 13:43:56 +02:00
Saúl Ibarra Corretgé
2f92e72858 android: raise SDK version 2019-07-03 13:28:31 +02:00
Saúl Ibarra Corretgé
d2c85ada1b android: fix deadlock in uncaught exception handler
The app is about to crash at that stage so it was a moot point to try to leave
the conference anyway.

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

~~~
    java.util.ConcurrentModificationException
        at java.util.HashMap$HashIterator.nextNode(HashMap.java:1441)
        at java.util.HashMap$KeyIterator.next(HashMap.java:1465)
        at org.jitsi.meet.sdk.OngoingConferenceTracker.updateListeners(OngoingConferenceTracker.java:89)
        at org.jitsi.meet.sdk.OngoingConferenceTracker.onExternalAPIEvent(OngoingConferenceTracker.java:74)
        at org.jitsi.meet.sdk.ExternalAPIModule.sendEvent(ExternalAPIModule.java:71)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)
        at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:158)
        at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:29)
        at android.os.Looper.loop(Looper.java:214)
        at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:232)
        at java.lang.Thread.run(Thread.java:764)
~~~
2019-07-03 13:28:18 +02:00
Chris Hansen
52362c4675 Fix spelling mistake on pragma mark
It causes a compiler error in Xcode 11 because it doesn't know how to parse it.
2019-07-02 14:11:01 -07:00
netaskd
8da0552541 microsoftCalendar: add showing calendar events in local timezone. 2019-07-02 21:20:00 +01:00
Hristo Terezov
7ce0def995 fix(mobile): After PR #4396 2019-07-02 10:36:37 -07:00
Hristo Terezov
48285e8a2d fix(conference): Don't use this._room.
this._room should be used only by jitsi-meet-torture and for test purposes. Also this._room is assigned later than room. This may cause some issues, for example conference.getMyUserId() may return undefined while the user id is already available beacuse this._room hasn't been assigned yet.
2019-07-02 07:38:57 -07:00
Hristo Terezov
21dcc41d31 ref(large-video): switch LargeVideo logic to react 2019-07-02 07:38:57 -07:00
Дамян Минков
625d268373 Room lock update (#4394)
* Adds a notification when remote lock happens.

* Updates translations.

Removes unused strings and extracts room password to separate translation, to be able to change it when deployment uses only digits.

* Formats the conference pin when showing it.

* Removes member from translation in favour of participant.

* Updates formatting of the pin.

* Adds a notification when password is remotely removed.
2019-07-02 14:14:58 +01:00
Дамян Минков
681782ed20 Adds back talk while muted notification. (#4392)
* Adds back talk while muted notification.

* Adds unmute button to the notification.
2019-07-02 12:59:25 +01:00
Saúl Ibarra Corretgé
1baa85b649 rn: hide invite button if the functionality is not available 2019-07-02 12:30:50 +02:00
Bettenbuk Zoltan
72137a2811 feat: initial based avatars 2019-07-01 23:59:16 +02:00
Leonard Kim
0734ce7ae3 feat(api): add notifications for kicked participants 2019-07-01 12:53:25 -07:00
Дамян Минков
2dc06c28e3 Adds option to be able to cancel locked rooms and leave. (#4391)
* Adds option to be able to cancel locked rooms and leave.

* Removes not needed operations when canceling password prompt.
2019-07-01 13:02:25 +01:00
paweldomas
5848669552 feat(analytics): time since last success in connection dropped
Update LJM to 9bcc2a26cc94683b8ed302418695a331b450df97 in order to bring
in the analytics update which will add a property indicating how much
time has passed since the last successful XMPP request came through.
2019-06-28 13:30:50 -05:00
Leonard Kim
c0376d238a ref(notifications): do not notify of local participant left
Join notifications are already supressed for the local
participant, so hide the left notification. For now
the notification is not being shown on mobile to keep
the same existing behavior and because a copy change
will be needed, but will be added once batching is
implemented.
2019-06-28 09:18:18 -07:00
Leonard Kim
979b773c3c ref(notifications): move join notification firing to notifications feature 2019-06-27 17:29:02 -07:00
Leonard Kim
3195a449ca ref(conference): web and native exercise same redux flow for kicked out 2019-06-27 09:34:05 -07:00
Bettenbuk Zoltan
d7483f07e3 feat: add possibility to clear invite search field 2019-06-27 18:17:37 +02:00
Дамян Минков
9c1b802997 Device selection now always shows the device that it managed to open. (#4384)
* Device selection now always shows the device that managed to open.

* Simplifies implementation, skips using state.
2019-06-27 17:04:47 +01:00
damencho
bb3a10b0fc Safe guard for removed parent node of the iframe. 2019-06-27 14:23:59 +01:00
paweldomas
97e8b31cee fix: update LJM to fix reload on hangup
Updates LJM to c0af82a215d0893f1999df299cfdfcbc9ce9e72a
2019-06-26 22:11:12 +02:00
Saúl Ibarra Corretgé
55218de779 rn: add a reduced UI mode for the welcome page
The only way to render the welcome page in reduced UI mode currently is to
hangup a call from the Android ongoing notification while in PiP mode.
2019-06-26 21:45:27 +02:00
Saúl Ibarra Corretgé
714e0e045d android: add notification while there is an ongoing meeting
The notification is posted by a foreground service, which also has the nice
side-effect of keeping the app alive for a long time.
2019-06-26 21:45:27 +02:00
Saúl Ibarra Corretgé
0bc369afb4 android: add ability to get the current Activity running RN
This helper method gets the current Activity attached to React Native (via the
ReactContext). This is useful for modules which need access to it, without being
actual React Native modules.
2019-06-26 21:45:27 +02:00
Saúl Ibarra Corretgé
f71ec55170 android: add ability to keep track of the current ongoing conference 2019-06-26 21:45:27 +02:00
yanas
760885437a Merge pull request #4369 from jitsi/quality-text-change
Changes call quality strings to video quality.
2019-06-26 17:24:34 +01:00
damencho
f77976b742 Notify for detecting suspend. 2019-06-26 17:10:34 +01:00
Saúl Ibarra Corretgé
9e95e7cd97 rn: bump SDK version to 2.2.0 2019-06-26 12:04:17 +02:00
Leonard Kim
9d94257e79 ref(api): move conference join notification to middleware 2019-06-26 10:47:18 +02:00
Yana Stamcheva
fc897b9bac Changes call quality to video quality. 2019-06-24 14:39:22 +01:00
766 changed files with 39599 additions and 20640 deletions

View File

@@ -2,22 +2,30 @@
; We fork some components by platform
.*/*[.]android.js
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
node_modules/react-native/Libraries/react-native/React.js
; Ignore duplicate module providers
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
.*/Libraries/react-native/React.js
node_modules/react-native/Libraries/react-native/React.js
; Flow doesn't support platforms
.*/Libraries/Utilities/LoadingView.js
; Ignore polyfills
.*/Libraries/polyfills/.*
node_modules/react-native/Libraries/polyfills/.*
; Ignore metro
.*/node_modules/metro/.*
; 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.
@@ -40,6 +48,18 @@ 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
@@ -52,8 +72,11 @@ 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.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
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
@@ -64,22 +87,32 @@ suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$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
; We (i.e. the jitsi-meet project) are using the haste module system on Web as
; well, not only on React Native. Unfortunately, Flow does not support .web.js
; by default. Override Flow's defaults to include .web.js as well. Technically,
; we have .native.js as well so the choice of .web.js may lead to errors.
; Practically though, it is a potential future problem that we do not have at
; the time of this writing.
module.file_ext=.web.js
; Flow's defaults:
module.file_ext=.js
module.file_ext=.jsx
module.file_ext=.json
[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.92.0
^0.104.0

View File

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

View File

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

View File

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

View File

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

View File

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

6
android/app/.classpath Normal file
View File

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

23
android/app/.project Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 960 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

6
android/sdk/.classpath Normal file
View File

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

23
android/sdk/.project Normal file
View File

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

View File

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

View File

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

View File

@@ -12,6 +12,7 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-feature
android:glEsVersion="0x00020000"
@@ -44,6 +45,8 @@
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>
<service android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService" />
</application>
</manifest>

View File

@@ -24,6 +24,7 @@ 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;
@@ -90,7 +91,7 @@ class AmplitudeModule
JSONObject eventProps = new JSONObject(eventPropsString);
Amplitude.getInstance(instanceName).logEvent(eventType, eventProps);
} catch (JSONException e) {
e.printStackTrace();
JitsiMeetLogger.e(e, "Error logging event");
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,14 +16,14 @@
package org.jitsi.meet.sdk;
import android.util.Log;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.module.annotations.ReactModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
/**
* Module implementing an API for sending events from JavaScript to native code.
*/
@@ -67,17 +67,20 @@ class ExternalAPIModule
*/
@ReactMethod
public void sendEvent(String name, ReadableMap data, String scope) {
// Keep track of the current ongoing conference.
OngoingConferenceTracker.getInstance().onExternalAPIEvent(name, data);
// The JavaScript App needs to provide uniquely identifying information
// to the native ExternalAPI module so that the latter may match the
// former to the native BaseReactView which hosts it.
BaseReactView view = BaseReactView.findViewByExternalAPIScope(scope);
if (view != null) {
Log.d(TAG, "Sending event: " + name + " with data: " + data);
JitsiMeetLogger.d(TAG + " Sending event: " + name + " with data: " + data);
try {
view.onExternalAPIEvent(name, data);
} catch(Exception e) {
Log.e(TAG, "onExternalAPIEvent: error sending event", e);
JitsiMeetLogger.e(e, TAG + " onExternalAPIEvent: error sending event");
}
}
}

View File

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

View File

@@ -33,9 +33,21 @@ public class JitsiMeet {
}
public static void setDefaultConferenceOptions(JitsiMeetConferenceOptions options) {
if (options != null && options.getRoom() != null) {
throw new RuntimeException("'room' must be null in the default conference options");
}
defaultConferenceOptions = options;
}
/**
* Returns the current conference URL as a string.
*
* @return the current conference URL.
*/
public static String getCurrentConference() {
return OngoingConferenceTracker.getInstance().getCurrentConference();
}
/**
* Helper to get the default conference options as a {@link Bundle}.
*

View File

@@ -20,12 +20,13 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import com.facebook.react.modules.core.PermissionListener;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.Map;
@@ -73,7 +74,18 @@ public class JitsiMeetActivity extends FragmentActivity
@Override
public void onDestroy() {
// 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
// with other content. If this Activity is "swiped out" from the recent list we will get
// Activity#onDestroy() called without warning. At this point we can try to leave the
// 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
// conference terminated. Thus, try our best to clean up.
leave();
if (AudioModeModule.useConnectionService()) {
ConnectionService.abortConnections();
}
JitsiMeetOngoingConferenceService.abort(this);
super.onDestroy();
}
@@ -197,17 +209,19 @@ public class JitsiMeetActivity extends FragmentActivity
@Override
public void onConferenceJoined(Map<String, Object> data) {
Log.d(TAG, "Conference joined: " + data);
JitsiMeetLogger.i("Conference joined: " + data);
// Launch the service for the ongoing notification.
JitsiMeetOngoingConferenceService.launch(this);
}
@Override
public void onConferenceTerminated(Map<String, Object> data) {
Log.d(TAG, "Conference terminated: " + data);
JitsiMeetLogger.i("Conference terminated: " + data);
finish();
}
@Override
public void onConferenceWillJoin(Map<String, Object> data) {
Log.d(TAG, "Conference will join: " + data);
JitsiMeetLogger.i("Conference will join: " + data);
}
}

View File

@@ -25,6 +25,7 @@ import android.os.Build;
import com.calendarevents.CalendarEventsPackage;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.core.PermissionListener;
/**
@@ -117,7 +118,13 @@ public class JitsiMeetActivityDelegate {
= ReactInstanceManagerHolder.getReactInstanceManager();
if (reactInstanceManager != null) {
reactInstanceManager.onHostPause(activity);
// 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);
}
}
}

View File

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

View File

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

View File

@@ -19,9 +19,10 @@ package org.jitsi.meet.sdk;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
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;

View File

@@ -0,0 +1,121 @@
/*
* 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.app.Notification;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
/**
* This class implements an Android {@link Service}, a foreground one specifically, and it's
* responsible for presenting an ongoing notification when a conference is in progress.
* The service will help keep the app running while in the background.
*
* See: https://developer.android.com/guide/components/services
*/
public class JitsiMeetOngoingConferenceService extends Service
implements OngoingConferenceTracker.OngoingConferenceListener {
private static final String TAG = JitsiMeetOngoingConferenceService.class.getSimpleName();
static final class Actions {
static final String START = TAG + ":START";
static final String HANGUP = TAG + ":HANGUP";
}
static void launch(Context context) {
OngoingNotification.createOngoingConferenceNotificationChannel();
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
intent.setAction(Actions.START);
ComponentName componentName;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
componentName = context.startForegroundService(intent);
} else {
componentName = context.startService(intent);
}
if (componentName == null) {
JitsiMeetLogger.w(TAG + " Ongoing conference service not started");
}
}
static void abort(Context context) {
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
context.stopService(intent);
}
@Override
public void onCreate() {
super.onCreate();
OngoingConferenceTracker.getInstance().addListener(this);
}
@Override
public void onDestroy() {
OngoingConferenceTracker.getInstance().removeListener(this);
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
final String action = intent.getAction();
if (Actions.START.equals(action)) {
Notification notification = OngoingNotification.buildOngoingConferenceNotification();
if (notification == null) {
stopSelf();
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
} else {
startForeground(OngoingNotification.NOTIFICATION_ID, notification);
JitsiMeetLogger.i(TAG + " Service started");
}
} else if (Actions.HANGUP.equals(action)) {
JitsiMeetLogger.i(TAG + " Hangup requested");
// Abort all ongoing calls
if (AudioModeModule.useConnectionService()) {
ConnectionService.abortConnections();
}
stopSelf();
} else {
JitsiMeetLogger.w(TAG + " Unknown action received: " + action);
stopSelf();
}
return START_NOT_STICKY;
}
@Override
public void onCurrentConferenceChanged(String conferenceUrl) {
if (conferenceUrl == null) {
stopSelf();
JitsiMeetLogger.i(TAG + "Service stopped");
}
}
}

View File

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

View File

@@ -19,17 +19,19 @@ package org.jitsi.meet.sdk;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ReadableMap;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.lang.reflect.Method;
import java.util.Map;
public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener> {
public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
implements OngoingConferenceTracker.OngoingConferenceListener {
/**
* The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
@@ -38,12 +40,6 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener> {
private static final Map<String, Method> LISTENER_METHODS
= ListenerUtils.mapListenerMethods(JitsiMeetViewListener.class);
/**
* The {@link Log} tag which identifies the source of the log messages of
* {@code JitsiMeetView}.
*/
private static final String TAG = JitsiMeetView.class.getSimpleName();
/**
* The URL of the current conference.
*/
@@ -106,6 +102,14 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener> {
if (!(context instanceof JitsiMeetActivityInterface)) {
throw new RuntimeException("Enclosing Activity must implement JitsiMeetActivityInterface");
}
OngoingConferenceTracker.getInstance().addListener(this);
}
@Override
public void dispose() {
OngoingConferenceTracker.getInstance().removeListener(this);
super.dispose();
}
/**
@@ -128,7 +132,7 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener> {
try {
pipModule.enterPictureInPicture();
} catch (RuntimeException re) {
Log.e(TAG, "failed to enter PiP mode", re);
JitsiMeetLogger.e(re, "Failed to enter PiP mode");
}
}
}
@@ -173,27 +177,17 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener> {
}
/**
* The internal processing for the URL of the current conference set on the
* associated {@link JitsiMeetView}.
*
* @param eventName the name of the external API event to be processed
* @param eventData the details/specifics of the event to process determined
* by/associated with the specified {@code eventName}.
* Handler for {@link OngoingConferenceTracker} events.
* @param conferenceUrl
*/
private void maybeSetViewURL(String eventName, ReadableMap eventData) {
String url = eventData.hasKey("url") ? eventData.getString("url") : null;
switch(eventName) {
case "CONFERENCE_WILL_JOIN":
this.url = url;
break;
case "CONFERENCE_TERMINATED":
if (url != null && url.equals(this.url)) {
this.url = null;
}
break;
}
@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;
}
/**
@@ -205,13 +199,6 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener> {
*/
@Override
protected void onExternalAPIEvent(String name, ReadableMap data) {
// XXX The JitsiMeetView property URL 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.
maybeSetViewURL(name, data);
onExternalAPIEvent(LISTENER_METHODS, name, data);
}
}

View File

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

View File

@@ -0,0 +1,99 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import com.facebook.react.bridge.ReadableMap;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
/**
* Helper class to keep track of what the current conference is.
*/
class OngoingConferenceTracker {
private static final OngoingConferenceTracker instance = new OngoingConferenceTracker();
private static final String CONFERENCE_WILL_JOIN = "CONFERENCE_WILL_JOIN";
private static final String CONFERENCE_TERMINATED = "CONFERENCE_TERMINATED";
private final Collection<OngoingConferenceListener> listeners =
Collections.synchronizedSet(new HashSet<OngoingConferenceListener>());
private String currentConference;
public OngoingConferenceTracker() {
}
public static OngoingConferenceTracker getInstance() {
return instance;
}
/**
* Gets the current active conference URL.
*
* @return - The current conference URL as a String.
*/
synchronized String getCurrentConference() {
return currentConference;
}
synchronized void onExternalAPIEvent(String name, ReadableMap data) {
if (!data.hasKey("url")) {
return;
}
String url = data.getString("url");
if (url == null) {
return;
}
switch(name) {
case CONFERENCE_WILL_JOIN:
currentConference = url;
updateListeners();
break;
case CONFERENCE_TERMINATED:
if (url.equals(currentConference)) {
currentConference = null;
updateListeners();
}
break;
}
}
void addListener(OngoingConferenceListener listener) {
listeners.add(listener);
}
void removeListener(OngoingConferenceListener listener) {
listeners.remove(listener);
}
private void updateListeners() {
synchronized (listeners) {
for (OngoingConferenceListener listener : listeners) {
listener.onCurrentConferenceChanged(currentConference);
}
}
}
public interface OngoingConferenceListener {
void onCurrentConferenceChanged(String conferenceUrl);
}
}

View File

@@ -0,0 +1,119 @@
/*
* 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.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import androidx.core.app.NotificationCompat;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import java.util.Random;
/**
* Helper class for creating the ongoing notification which is used with
* {@link JitsiMeetOngoingConferenceService}. It allows the user to easily get back to the app
* and to hangup from within the notification itself.
*/
class OngoingNotification {
private static final String TAG = OngoingNotification.class.getSimpleName();
private static final String CHANNEL_ID = "JitsiNotificationChannel";
private static final String CHANNEL_NAME = "Ongoing Conference Notifications";
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
static void createOngoingConferenceNotificationChannel() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
Context context = ReactInstanceManagerHolder.getCurrentActivity();
if (context == null) {
JitsiMeetLogger.w(TAG + " Cannot create notification channel: no current context");
return;
}
NotificationManager notificationManager
= (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel
= notificationManager.getNotificationChannel(CHANNEL_ID);
if (channel != null) {
// The channel was already created, no need to do it again.
return;
}
channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(false);
channel.enableVibration(false);
channel.setShowBadge(false);
notificationManager.createNotificationChannel(channel);
}
static Notification buildOngoingConferenceNotification() {
Context context = ReactInstanceManagerHolder.getCurrentActivity();
if (context == null) {
JitsiMeetLogger.w(TAG + " Cannot create notification: no current context");
return null;
}
Intent notificationIntent = new Intent(context, context.getClass());
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
NotificationCompat.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder = new NotificationCompat.Builder(context, CHANNEL_ID);
} else {
builder = new NotificationCompat.Builder(context);
}
builder
.setCategory(NotificationCompat.CATEGORY_CALL)
.setContentTitle(context.getString(R.string.ongoing_notification_title))
.setContentText(context.getString(R.string.ongoing_notification_text))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setOngoing(true)
.setAutoCancel(false)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setUsesChronometer(true)
.setOnlyAlertOnce(true)
.setSmallIcon(context.getResources().getIdentifier("ic_notification", "drawable", context.getPackageName()));
// 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);
builder.addAction(hangupAction);
}
return builder.build();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,6 @@
<resources>
<string name="app_name">Jitsi Meet SDK</string>
<string name="dropbox_app_key"></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>
</resources>

View File

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

View File

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

View File

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

31
css/_avatar.scss Normal file
View File

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

View File

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

View File

@@ -80,6 +80,27 @@
}
}
#chat-recipient {
align-items: center;
background-color: $defaultWarningColor;
display: flex;
flex-direction: row;
padding: 10px;
span {
color: white;
display: flex;
flex: 1;
}
div {
svg {
cursor: pointer;
fill: white
}
}
}
.chat-header {
background-color: $chatHeaderBackgroundColor;
height: 70px;
@@ -196,6 +217,11 @@
padding: 0;
}
}
.privatemessagenotice {
color: $defaultWarningColor;
font-style: italic;
}
}
.smiley {
@@ -228,6 +254,7 @@
.smileys-panel {
bottom: 100%;
box-sizing: border-box;
background-color: rgba(0, 0, 0, .6) !important;
height: auto;
max-height: 0;
overflow: hidden;
@@ -312,6 +339,16 @@
.chatmessage-wrapper {
max-width: 100%;
.replywrapper {
display: flex;
flex-direction: row;
align-items: center;
.toolbox-icon {
cursor: pointer;
}
}
}
.chatmessage {

View File

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

View File

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

View File

@@ -73,8 +73,62 @@
.button-group-center {
justify-content: center;
.toolbox-icon {
margin: 0px 4px;
.toolbox-button {
.toolbox-icon {
background-color: #fff;
border-radius: 50%;
border: 1px solid #d1dbe8;
margin: 0px 4px;
width: 38px;
height: 38px;
&:hover {
background-color: #daebfa;
border: 1px solid #daebfa;
}
&.toggled {
background: #2a3a4b;
border: 1px solid #5e6d7a;
svg {
fill: #fff;
}
&:hover {
background-color: #5e6d7a;
}
}
&.disabled, .disabled & {
cursor: initial;
color: #fff;
background-color: #a4b8d1;
}
svg {
fill: #5e6d7a;
}
}
&:nth-child(2) {
.toolbox-icon {
background-color: $hangupColor;
border: 1px solid $hangupColor;
width: 40px;
height: 40px;
&:hover {
background-color: $hangupColor;
}
svg {
fill: #fff;
}
}
}
}
}
@@ -82,75 +136,6 @@
justify-content: flex-end;
}
i {
border-radius: 5px;
cursor: pointer;
display: block;
font-size: inherit;
height: 100%;
line-height: inherit;
width: 100%;
}
i:hover {
background: $newToolbarButtonHoverColor;
}
i.toggled {
background: $newToolbarButtonToggleColor;
}
i.toggled:hover {
background: $newToolbarButtonHoverColor;
}
.icon-hangup {
background-color: #e12d2d;
color: #fff;
border-radius: 50%;
width: 40px;
height: 40px;
&:hover {
background-color: #e54b4b;
}
}
i.disabled, .disabled i {
cursor: initial !important;
color: #fff !important;
background-color: #a4b8d1 !important;
}
.icon-mic-disabled, .icon-microphone, .icon-camera-disabled, .icon-camera {
background-color: #fff;
color: #5e6d7a;
border-radius: 50%;
border: 1px solid #d1dbe8;
width: 38px;
height: 38px;
&:hover {
background-color: #daebfa;
border: 1px solid #daebfa;
}
&.toggled {
background: #2a3a4b;
color: #fff;
border: 1px solid #5e6d7a;
&:hover {
background-color: #5e6d7a;
}
}
&.disabled, .disabled & {
cursor: initial;
color: #fff;
background-color: #a4b8d1;
}
}
.overflow-menu {
font-size: 1.2em;
list-style-type: none;
@@ -191,14 +176,6 @@
cursor: initial;
color: #3b475c;
}
i.toggled {
background: inherit;
}
i.toggled:hover {
background: inherit;
}
}
.beta-tag {
@@ -227,6 +204,10 @@
max-width: 24px;
max-height: 24px;
}
svg {
fill: #B8C7E0 !important;
}
}
.profile-text {
@@ -265,9 +246,26 @@
}
.toolbox-icon {
height: $newToolbarSize;
display: flex;
border-radius: 5px;
flex-direction: column;
font-size: 24px;
height: $newToolbarSize;
justify-content: center;
width: $newToolbarSize;
&:hover, &.toggled {
background: $newToolbarButtonHoverColor;
}
&.disabled {
cursor: initial !important;
background-color: #a4b8d1 !important;
svg {
fill: #fff !important;
}
}
}
}
}
@@ -297,10 +295,6 @@
background-color: $AOTToolbarButtonHoverColor;
}
.icon-hangup {
color: $hangupColor;
}
.toolbox-button {
color: $toolbarButtonColor;
cursor: pointer;
@@ -325,10 +319,6 @@
width: $newToolbarSize;
}
.icon-hangup {
font-size: $newToolbarHangupFontSize;
}
.disabled {
cursor: initial;
}

View File

@@ -28,6 +28,7 @@ $defaultColor: #F1F1F1;
$defaultSideBarFontColor: #44A5FF;
$defaultSemiDarkColor: #ACACAC;
$defaultDarkColor: #2b3d5c;
$defaultWarningColor: rgb(215, 121, 118);
/**
* Toolbar
@@ -163,9 +164,45 @@ $watermarkHeight: 74px;
*/
$welcomePageDescriptionColor: #fff;
$welcomePageFontFamily: inherit;
$welcomePageHeaderBackground: linear-gradient(-90deg, #1251AE 0%, #0074FF 50%, #1251AE 100%);
$welcomePageBackground: linear-gradient(-90deg, #1251AE 0%, #0074FF 50%, #1251AE 100%);
$welcomePageTitleColor: #fff;
$welcomePageHeaderBackground: none;
$welcomePageHeaderBackgroundSmall: none;
$welcomePageHeaderBackgroundPosition: none;
$welcomePageHeaderBackgroundRepeat: none;
$welcomePageHeaderBackgroundSize: none;
$welcomePageHeaderPaddingBottom: 0px;
$welcomePageHeaderTextMarginTop: 35px;
$welcomePageHeaderTextMarginBottom: 35px;
$welcomePageHeaderTextTitleMarginBottom: 16px;
$welcomePageHeaderTextDescriptionDisplay: inherit;
$welcomePageEnterRoomWidth: 680px;
$welcomePageEnterRoomPadding: 25px 30px;
$welcomePageEnterRoomBorderRadius: 0px;
$welcomePageEnterRoomInputContainerPadding: 0 8px 5px 0px;
$welcomePageEnterRoomInputContainerBorderWidth: 0px 0px 2px 0px;
$welcomePageEnterRoomInputContainerBorderStyle: solid;
$welcomePageEnterRoomInputContainerBorderImage: linear-gradient(to right, #dee1e6, #fff) 1;
$welcomePageEnterRoomTitleDisplay: inherit;
$welcomePageTabContainerDisplay: flex;
$welcomePageTabContentDisplay: inherit;
$welcomePageTabButtonsDisplay: flex;
$welcomePageTabDisplay: block;
$welcomePageButtonWidth: 51px;
$welcomePageButtonHeight: 35px;
$welcomePageButtonFontWeight: inherit;
$welcomePageButtonBorderRadius: 4px;
$welcomePageButtonLineHeight: 35px;
/**
* Deep-linking page variables.
*/

View File

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

View File

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

View File

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

View File

@@ -23,12 +23,6 @@ $flagsImagePath: "../images/";
@import "../node_modules/bc-css-flags/dist/css/bc-css-flags.scss";
/* Flags END */
/* Fonts BEGIN */
@import 'font';
/* Fonts END */
/* Modules BEGIN */
@import 'aui_reset';
@@ -57,6 +51,7 @@ $flagsImagePath: "../images/";
@import 'ringing/ringing';
@import 'welcome_page';
@import 'welcome_page_content';
@import 'welcome_page_settings_toolbar';
@import 'toolbars';
@import 'jquery.contextMenu';
@import 'keyboard-shortcuts';
@@ -86,5 +81,6 @@ $flagsImagePath: "../images/";
@import 'navigate_section_list';
@import 'third-party-branding/google';
@import 'third-party-branding/microsoft';
@import 'avatar';
/* Modules END */

7
debian/rules vendored
View File

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

View File

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

View File

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

78
doc/development.md Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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