Compare commits

...

379 Commits

Author SHA1 Message Date
Aaron van Meerten
4ed9d5893b util updates 2019-09-09 08:42:00 -05:00
Aaron van Meerten
07b7f03aa7 Merge pull request #4308 from jitsi/prosody-token-allow-asap-server-override
allows override of asap key server in token utility
2019-06-06 14:26:25 -06:00
Aaron van Meerten
7ce44f85ca changed to using a setter for the asapKeyServer 2019-06-06 15:22:38 -05:00
Aaron van Meerten
41e0d782ce allows override of asap key server in token utility 2019-06-06 14:41:46 -05:00
Aaron van Meerten
2a8fafdd36 Merge pull request #4303 from jitsi/start-silent
Adds a config param startSilent to disable audio output.
2019-06-05 12:29:57 -06:00
damencho
faee1c139e Adds a config param startSilent to disable audio output. 2019-06-05 18:01:18 +01:00
virtuacoplenny
eb644987ce fix(settings): do not use non-set localStorage values (#4299)
If a value is not set in localStorage then null is
returned. null should not be converted to an empty
string (via _.escape) because that will then be
stored in localStorage as the user set preference
and will keep overriding any other values set
in localStorage for the displayname.
2019-06-04 17:42:48 -07:00
damencho
de60a70daf Commit from translate.jitsi.org by user damencho.: 555 of 625 strings translated (8 fuzzy). 2019-06-03 11:19:10 +00:00
damencho
2904dfa794 Commit from translate.jitsi.org by user damencho.: 584 of 625 strings translated (2 fuzzy). 2019-06-03 11:19:01 +00:00
damencho
d51cf7c581 Commit from translate.jitsi.org by user damencho.: 625 of 625 strings translated (0 fuzzy). 2019-06-03 10:58:25 +00:00
Hristo Terezov
f25e6c6a5d chore(package.json): Update lib-jitsi-meet version 2019-06-01 02:28:04 -07:00
Hristo Terezov
5fb9422513 feat(API): Add show feedback parameter to hangup 2019-06-01 02:28:04 -07:00
Hristo Terezov
d01cfc8466 fix(conference): API left event. 2019-06-01 02:28:04 -07:00
Saúl Ibarra Corretgé
fa3888991f rn: avoid logging initial props in release builds
They may contain sensitive information.
2019-05-31 11:49:36 +02:00
virtuacoplenny
ded355a807 fix(settings): use moderator check helper (#4292) 2019-05-30 14:10:40 -07:00
Leonard Kim
b655c8d54a fix(large-video): clear remote video stream on track removal
VideoLayout schedules a large video update by passing in
the video stream on the small video instance. When a stream
is removed, the UI is removed from the small video instance
but a reference to the stream is left. So when VideoLayout
schedules the large video update after a stream removal,
the old stream from the small video instance is re-used,
even though it has been removed.

This change also brings balance with RemoteVideo method
"addRemoteStreamElement" which sets the stream on the
small video instance, so now "removeRemoteStreamElement
unsets it.
2019-05-30 09:46:35 -07:00
Leonard Kim
42a6e6faaf ref(large-video): remove redundant call to update on stream removal 2019-05-30 09:46:35 -07:00
Leonard Kim
c7954c284d ref(large-video): move layout update on stream removal to layout middleware 2019-05-30 09:46:35 -07:00
virtuacoplenny
251da1861a feat(api): notify api of mic and camera errors (#4289)
- Use actions to notify the rest of the app that
  a mic or camera error has occurred
- Use middleware to respond to those notifications
  of errors by showing in-app notifications and
  notifying the external api
2019-05-29 14:17:07 -07:00
Hristo Terezov
9712804040 fix(Amplitude): user id 2019-05-29 09:53:31 -07:00
Hristo Terezov
fecbef0aff fix(AmplitudeModule): class name 2019-05-29 17:22:50 +02:00
Saúl Ibarra Corretgé
d65b71b584 rn: add ability to set the conference subject 2019-05-29 14:48:02 +02:00
Saúl Ibarra Corretgé
579d291bca config: add ability to pass the subject as a URL parameter 2019-05-29 14:48:02 +02:00
Saúl Ibarra Corretgé
871026f4ba conference: clear the pending subject after it has been set 2019-05-29 14:48:02 +02:00
Saúl Ibarra Corretgé
9a8a070c62 rn: show conference subject if set 2019-05-29 14:48:02 +02:00
Leonard Kim
7cf4c7bd78 Revert "feat(screenshare): enable auto-pin of latest and last screenshare"
This reverts commit f42d0411b1.

The UX provided by this feature flag in its current state is not
desired. Also, I noticed filmstrip sometimes failing to properly
update small video display mode on pin/unpin. The feature is
being left in for consumers of jitsi-meet to enable as needed.
2019-05-28 15:28:50 -07:00
Hristo Terezov
72a1def571 feat(config): whitelist config.analytics 2019-05-24 13:42:12 -07:00
damencho
0dad99c3b7 Enables local video flip menu by default. 2019-05-24 23:09:24 +03:00
Hristo Terezov
840c0190c4 fix(deep-linking): Don't rely on custom scheme 2019-05-24 12:51:14 -07:00
Leonard Kim
e0fdeea69b fix(large-video): do not stretch dominant speaker avatar 2019-05-24 12:00:35 -07:00
Leonard Kim
e3612929f8 fix(avatar): dynamically size avatar in dynamically sizable filmstrip 2019-05-24 12:00:35 -07:00
Hristo Terezov
70921bb6ef feat(analytics): local tracks duration event. 2019-05-24 10:09:25 -07:00
Saúl Ibarra Corretgé
371ca4eef1 ios: don't require bitcode for Debug builds 2019-05-24 14:11:08 +02:00
Bettenbuk Zoltan
54fdb7066f feat: scrollable bottom sheet 2019-05-24 14:06:17 +02:00
Bettenbuk Zoltan
85bcb0c757 fix: auth dialog button labels 2019-05-24 11:35:56 +02:00
Bettenbuk Zoltan
d387cbe5bd fix: iOS 10 bottom sheet style 2019-05-24 11:28:28 +02:00
paweldomas
1bc28e4904 watchos: display a message if the recent list is empty 2019-05-24 09:41:31 +02:00
Saúl Ibarra Corretgé
cb3419ba2a android: enter PiP mode when pressing back button
When in a conference, try to enter PiP when pressing the back button. If this is
not possible (because it's unsupported, not enabled, etc.) fall back to the
previous behavior of simply hanging up.
2019-05-23 16:00:12 +02:00
Saúl Ibarra Corretgé
a2f8e156da app: avoid loading config when going back to the welcome page 2019-05-23 15:16:31 +02:00
Saúl Ibarra Corretgé
a4cf79c161 rn: fix losing audio if call is hangup too quickly
This PR changes the logic for connecting / disconnecting conferences. Instead of
doing it in mount / unmount events from the Conference component, it moves the
logic to the appNavigatee action.

This fixes a regression introduced in 774c5ecd when trying to make sure the
conference terminated event is always sent.

By moving the logic to appNavigate we no longer depend on side-effects for
connecting / disconnecting, and the code should be more maintainable moving
forward.

An improvement to this is the concept of sessions, which, while not tackled
here, was taken into consideration.
2019-05-23 15:16:31 +02:00
Saúl Ibarra Corretgé
9352517705 ios: always log delegate method calls 2019-05-23 15:16:31 +02:00
Saúl Ibarra Corretgé
47d5163c52 rn: don't tag builds by default
People run these in their own checkout and will run into problems because
tagging will fail.
2019-05-23 12:07:04 +02:00
Bettenbuk Zoltan
def22b01bb fix: set explicit color for search field to avoid theme override 2019-05-23 12:06:50 +02:00
Saúl Ibarra Corretgé
9445cf99fd Revert "ios: remove no longer needed code"
This reverts commit 603d161788.
2019-05-22 18:10:35 +02:00
paweldomas
96b226de24 watchos: change the icons
Inverts the icons to follow more what's in the phone app instead of
CallKit.
2019-05-22 17:25:35 +02:00
Bettenbuk Zoltan
5101f69e4e feat: don’t render moderator icon if everyone is moderator 2019-05-22 14:08:52 +02:00
François Benaiteau
61b66e0edf doc: fix incorrect code examples for universal / deep linking 2019-05-22 14:08:37 +02:00
Bettenbuk Zoltan
700051f809 fix: device selection colour scheme support 2019-05-22 12:29:28 +02:00
Дамян Минков
d16e10baec room-lock: adds ability to allow only digits for room locking 2019-05-22 09:43:17 +02:00
ibauersachs
11e5c14f83 Commit from translate.jitsi.org by user ibauersachs.: 317 of 625 strings translated (31 fuzzy). 2019-05-21 17:02:23 +00:00
ibauersachs
ded58d77d1 Commit from translate.jitsi.org by user ibauersachs.: 319 of 613 strings translated (26 fuzzy). 2019-05-21 16:58:20 +00:00
ibauersachs
8642c372c4 Commit from translate.jitsi.org by user ibauersachs.: 323 of 583 strings translated (4 fuzzy). 2019-05-21 16:56:52 +00:00
ibauersachs
edbf591059 Commit from translate.jitsi.org by user ibauersachs.: 430 of 583 strings translated (18 fuzzy). 2019-05-21 16:55:09 +00:00
jitsi-pootle
ded4291d6a New files added from translate.jitsi.org based on templates 2019-05-21 14:09:04 +00:00
Дамян Минков
a14fead0f3 Groups devices notifications by type audio/video. (#4238)
* Groups devices notifications by type audio/video.

* Fixes passing correct device array.
2019-05-20 21:35:42 +01:00
Saúl Ibarra Corretgé
466e1e3eb8 android: fix publishing new async storage package
The naming didn't match, so adjust it. @ cannot be used for maven artifact
names.
2019-05-20 17:33:36 +02:00
Saúl Ibarra Corretgé
8a90f0dab1 android: include SDK version in Maven repo commit message 2019-05-20 17:33:17 +02:00
Leonard Kim
d7e0aa3f61 fix(api): enable the external api before the first redux update
For the external api to fire update events out of the iframe, it
must first be initialized within the jitsi app. Any invocations
by the app to send updates events before initialization will
cause the api to swallow the events. The chosen fix is to
initialize the api earlier so the first update of app's redux
store fires update events that the api will also fire out of
the iframe.

This change will affect current behavior in that right now
the update event of the initial set of the avatar url is
blocked, but the change will make that event fire out of the
iframe.
2019-05-20 11:56:08 +02:00
Leonard Kim
37b343a797 feat(api): add ability to toggle tile view 2019-05-20 02:53:16 -07:00
Leonard Kim
149485905c fix(api): store passed in devices as user selected
Currently devices set through the api are stored
as ids, and not user selected. This can cause
other existing user selected devices to take
precedence over the devices passed into the api.
2019-05-17 10:47:31 +01:00
Aaron van Meerten
7f1df5629e Merge pull request #4229 from jitsi/poltergeist-prefix-support
updates bosh to support optional prefix
2019-05-16 15:40:28 -06:00
Leonard Kim
f42d0411b1 feat(screenshare): enable auto-pin of latest and last screenshare 2019-05-16 14:19:34 -07:00
Aaron van Meerten
8d1d573266 updates bosh to support optional prefix
use optional prefix in poltergeist room lookup
2019-05-16 14:23:36 -05:00
Leonard Kim
d86b60ea72 fix(chat): maintain bottom scroll on input resize 2019-05-15 08:06:35 -07:00
Leonard Kim
dfe5fbb702 ref(chat): change initial input size to 1 line 2019-05-15 08:06:35 -07:00
Leonard Kim
09f881c0f5 ref(chat): bring in package for text area auto-resizing 2019-05-15 08:06:35 -07:00
Leonard Kim
f1546008f9 ref(chat): removed unused getChatInputRef callback for input 2019-05-15 08:06:35 -07:00
Leonard Kim
d8df7fde84 ref(chat): clean up public blur/focus methods on input
Method blur is not called. Method blur is called
internally only.
2019-05-15 08:06:35 -07:00
Saúl Ibarra Corretgé
1c809eb428 ios: strip bitcode when releasing the SDK 2019-05-15 14:07:25 +02:00
Saúl Ibarra Corretgé
e94edcd4ae ios: automagically download a bitcode WebRTC build if needed 2019-05-15 09:54:17 +02:00
paweldomas
b48651396f fix(travis): upload through ssh proxy 2019-05-14 19:37:37 -05:00
Leonard Kim
e2044074ad Revert "fix(welcome-page): remove watermark container to avoid z-index wars"
This reverts commit 890151fa72.
2019-05-14 12:42:54 -07:00
Saúl Ibarra Corretgé
f060ac9db1 ios: notify RTCAudioSession about CallKit AVAudioSession activation 2019-05-14 21:09:39 +02:00
Leonard Kim
5a53d7f32a fix(chat): re-fix letting long messages wrap 2019-05-14 09:20:25 -07:00
Leonard Kim
4eec13da1c ref(chat): de-parameterize AbstractMessageContainer 2019-05-14 09:20:25 -07:00
Leonard Kim
cb8282dfe5 ref(chat): remove unused method 2019-05-14 09:20:25 -07:00
Leonard Kim
5cd0b1a9be fix(chat): fix auto-scrolling to bottom
Empower the parent.
2019-05-14 09:20:25 -07:00
Leonard Kim
504fadaf71 ref(chat): on web, move timestamp to chat message 2019-05-14 09:20:25 -07:00
Leonard Kim
7187e540a8 ref(chat): on native, show messages as grouped by sender 2019-05-14 09:20:25 -07:00
Leonard Kim
34dffbfc5e ref(chat): on native, group messages by sender (no styling) 2019-05-14 09:20:25 -07:00
Leonard Kim
a9637f93c3 ref(chat): create AbstractMessageContainer
So mobile and web can share logic for grouping chat
messages by sender.
2019-05-14 09:20:25 -07:00
Leonard Kim
0e8b0a9c5c ref(chat): create web MessageContainer component 2019-05-14 09:20:25 -07:00
Saúl Ibarra Corretgé
e66b596a0d ios: add ability to override SDK version when releasing 2019-05-14 17:00:02 +02:00
Saúl Ibarra Corretgé
6f320f463d rn: don't use annotated tags when building the SDKs 2019-05-14 17:00:02 +02:00
Saúl Ibarra Corretgé
02955ab57c deps: react-native@0.59.8
https://github.com/react-native-community/releases/blob/master/CHANGELOG.md#v0598
2019-05-14 10:22:50 +02:00
Leonard Kim
a9d76a2577 fix(large-video): vertically align center screenshare
Stop using special case logic for aligning screenshare videos.
It may be possible to have positioning all done using CSS but that
seems to be a more significant refactoring.
2019-05-10 08:09:56 -07:00
Bettenbuk Zoltan
dcf31baf3a doc: update google auth doc 2019-05-10 13:22:05 +02:00
Bettenbuk Zoltan
1e346f10ab rn: fix streaming key input color 2019-05-10 13:22:05 +02:00
Leonard Kim
a114d55fac fix(chat): ensure really long words can trigger wrapping 2019-05-09 08:25:47 -07:00
Leonard Kim
afde717ca4 ref(chat): use message type as classname 2019-05-09 07:06:27 -07:00
Leonard Kim
fb5a45f714 feat(chat): on web, group messages by sender 2019-05-09 07:06:27 -07:00
Дамян Минков
f5ac18da18 Add option to allow guest(moderators) to add a room password 2019-05-09 13:30:38 +02:00
Saúl Ibarra Corretgé
103ae363f6 ios: fix CallKit crash in development mode
It's possible a CallKit event arrives when the React Bridge has been torn down
and there is an assert that checks this. In order to avoid a crash, just skip
the event.
2019-05-09 13:22:58 +02:00
damencho
9bde673397 Updates copy info with parltcipant name info if available. 2019-05-09 10:33:55 +01:00
paweldomas
ff6b27eafa fix(travis): add watch dev prov profile 2019-05-08 19:01:28 -05:00
paweldomas
8cb19ccbf6 fix(travis): download WebRTC bitcode 2019-05-08 19:01:28 -05:00
damencho
198eba3682 Does not play sound notifications on the recording side. 2019-05-08 10:27:05 -07:00
Leonard Kim
a49f62238b ref(chat): clean up extra dom 2019-05-08 08:57:00 -07:00
Leonard Kim
a8233bdb84 ref(chat): move some colors to css variables 2019-05-08 08:57:00 -07:00
Leonard Kim
ec2826e0fc ref(chat): make wider 2019-05-08 08:57:00 -07:00
Leonard Kim
3d9606f6da ref(chat): use somewhat transparent background 2019-05-08 08:57:00 -07:00
Leonard Kim
01458eeff9 ref(chat): add a header for holding the close button 2019-05-08 08:57:00 -07:00
Leonard Kim
0318568a30 ref(chat): add light top border on input for visual separation 2019-05-08 08:57:00 -07:00
Leonard Kim
4d04141f24 ref(chat): change input placeholder 2019-05-08 08:57:00 -07:00
Leonard Kim
afbc622fb9 ref(chat): differentiate local and remote messages with background color 2019-05-08 08:57:00 -07:00
Leonard Kim
fbc7f865ec ref(chat): change chat bubble borders based on sender
For remote chat messages, all corners should be rounded
except the top left. For local messages all corners
should be rounded except the top right.
2019-05-08 08:57:00 -07:00
Leonard Kim
2a4bac7a27 ref(chat): remove chat bubble arrow 2019-05-08 08:57:00 -07:00
Saúl Ibarra Corretgé
b45a5da6e2 rn: use new AsyncStorage package
It was extracted from the RN core to a community maintained package.
2019-05-08 16:03:09 +02:00
Saúl Ibarra Corretgé
2fad9f9ba8 ios: update Podfile.lock 2019-05-08 12:12:54 +01:00
Saúl Ibarra Corretgé
8b0e5b9d15 android: set system navbar color to match the header 2019-05-08 12:44:17 +02:00
paweldomas
0889ffdf27 android fix: do not enter PiP mode when the permissions alert is shown
Entering PiP mode while the permissions dialog is display will not only
fail, but also mess up the Activity lifecycle on some OS versions.
We may end up with two activity/fragment instances and a situation where
the onStop callback was not called yet on the instance #1 while
the onResume has been already called on instance #2.
2019-05-08 10:15:14 +02:00
Bettenbuk Zoltan
86d0d4fc22 rn: add DialInSummary 2019-05-07 18:02:14 +02:00
Bettenbuk Zoltan
7e9df74e60 rn: add generic alert dialog 2019-05-07 18:02:14 +02:00
Bettenbuk Zoltan
3eca67e1ad rn: add HeaderWithNavigation component 2019-05-07 18:02:14 +02:00
Дамян Минков
c040b3a7dd Fall back to using label for preferred devices (#4171)
* Skips setting undefined device id to sink in audio preview.

* Fallbacks to use labels for user selected devices.

* Fixes comment.
2019-05-07 09:53:01 +01:00
Leonard Kim
3f4a71c26d fix(welcome-page): remove watermark container to avoid z-index wars
By making the container 100% height and position relative, that
would cause it to overlap any static-positioned  elements below it.
The 100% makes it so that any watermarks intended for the bottom
of the page show up on the bottom of the page. However, it's not
needed because watermark stylings already try to position the
watermarks at the bottom.
2019-05-04 11:03:48 -07:00
Leonard Kim
e7db8d6812 fix(chat): save chat error messages into redux
The proper field name is "messageType",
not "type." Also using "type" would
override the actionType.
2019-05-03 14:06:36 -07:00
Saúl Ibarra Corretgé
7d2ac0244d deps: react-native-webrtc@4064c6f2db4f8b961daaaa8dafc6a896d7cfbc43
New M69 build with Metal crash fixes.
2019-05-03 19:29:30 +02:00
Saúl Ibarra Corretgé
c0efea5168 ios: enable bitcode
Time has come. We need to enable bitcode. It's optional for iOS targets, but
mandatory for the entire project if there is a watchOS target. Since we have a
watchOS target, it's time to enable it.
2019-05-03 19:29:30 +02:00
Saúl Ibarra Corretgé
5ed53dcef5 ios: update Fastlane for watchOS app 2019-05-03 19:29:30 +02:00
Saúl Ibarra Corretgé
746159a1ac ios: set compilation mode to "wholemodule" for release builds 2019-05-03 19:29:30 +02:00
Saúl Ibarra Corretgé
43a8fd2a53 ios: set iOS deployment target correctly everywhere 2019-05-03 19:29:30 +02:00
Saúl Ibarra Corretgé
a26bb2c1a6 watchos: add watchOS app
Co-authored-by: Pawel Domas <pawel.domas@jitsi.org>
2019-05-03 19:29:30 +02:00
paweldomas
e4af5ddbe9 feat(base/connection): throw error and add isInviteURLReady 2019-05-03 19:29:30 +02:00
Дамян Минков
768cff48a4 Notify for new device (#4165)
* Fix detecting preferred audio output.

Fixes detecting when a new output device is found and we have stored user preference of using that device.

* Does not store which is the currently open device on save.

Does not save the currently opened device when saving settings dialog, this will be done once we successfully replace the tracks to use the new devices.

* Saves opened audio device after successfully changing it.

If we do it earlier _updateAudioDeviceId is using localAudio and can store wrong value.

* Adds notification for new non preferred devices.

A notification is shown which gives an option to the user to select and use the newly plugged devices.
Adding custom button and handler for the action to the notifications.

* Changes logic to search and handle all newly added devices from array.

* Moves some utility methods to features/base/devices.
2019-05-03 18:25:33 +01:00
damencho
384f0d4317 Removes hardcoded defaulting to US number from the code. 2019-05-03 16:55:22 +01:00
damencho
0ec4e6a805 Makes numbers clickable on the more numbers page when opened on mobile. 2019-05-03 16:55:22 +01:00
Saúl Ibarra Corretgé
5cc01b074e ios: update Podfile.lock for RN update 2019-05-03 10:27:17 +02:00
damencho
deaf5ba612 Always uses the id for the device extrackted from the track.
When updating the currently used devices always uses the id that is used by the local track instance.
2019-05-02 11:55:46 +01:00
damencho
740c1eb84f Adds new persistent state for devices user selection.
The state about currently opened devices is filtered and not stored, where we only store when user selects a device preferences.
Also allow changing input devices for Firefox when we are not in a conference.
2019-05-02 11:55:46 +01:00
Saúl Ibarra Corretgé
2d45709a6a android: add the ability to make a "libre" build
A libre build will exclude the following:

- Analytics modules
- Google Play services GMS
- Crashlytics
- Firebase
2019-05-02 09:26:20 +02:00
Saúl Ibarra Corretgé
6bbc2927ab analytics: don't initialize handlers if they are not properly configured 2019-05-02 09:26:20 +02:00
Saúl Ibarra Corretgé
08891b17b6 android: expose JitsiMeetActivity.leave() 2019-05-02 09:26:20 +02:00
Saúl Ibarra Corretgé
aab3428347 android: make JitsiMeetActivity.join public 2019-05-02 09:26:20 +02:00
Saúl Ibarra Corretgé
bf7b1c5cfc rn: add support for alpha.jitsi.net 2019-05-01 23:23:24 +02:00
Saúl Ibarra Corretgé
7b347baab6 deps: react-native@0.59.5 2019-05-01 19:16:08 +02:00
Saúl Ibarra Corretgé
f9b3d470e9 cc: fix showing CC button if config option is undefined 2019-04-30 14:50:56 +02:00
Saúl Ibarra Corretgé
7b78fa45f4 invite: don't consider "add people" enabled if there is no search URL
This avoids showing the + button when there is no service configured, ie with
the default Jitsi Meet install.
2019-04-30 14:50:56 +02:00
Saúl Ibarra Corretgé
34dcbd991e rn: wait for animation before hiding SlidingView 2019-04-30 12:45:53 +02:00
Saúl Ibarra Corretgé
70dc22c107 rn: refactor BottomSheet
Avoid using a Modal since those create trouble with the view hierarchy.
2019-04-30 12:45:53 +02:00
damencho
89719520e2 Disposes the tracks in component was unmounted while creating those.
The issue is if you quickly click Devices and then another tab, we may leave open tracks (video light stays on even when you are video muted).
2019-04-29 14:38:17 +00:00
Дамян Минков
ce7bdb35ac Merge pull request #4152 from jitsi/prosody-0.10-update
Updates config if prosody 0.10 is used.
2019-04-29 14:36:55 +00:00
Дамян Минков
bf8c716477 Merge pull request #4150 from jitsi/invitation-update
Update copy invite text.
2019-04-29 14:36:46 +00:00
Дамян Минков
93e8d755d3 Merge pull request #4148 from zbettenbuk/recording-web-css-fix
Safeguard Container style when used cross-platform
2019-04-29 14:36:35 +00:00
damencho
c09eee0985 Disables chat when we are in recorder mode. 2019-04-29 14:36:22 +00:00
damencho
4f6a0d7d3a Updates config if prosody 0.10 is used. 2019-04-29 15:24:55 +01:00
damencho
dd5233d31b Update copy invite text. 2019-04-29 15:06:36 +01:00
Bettenbuk Zoltan
31638133b7 Safeguard Container style when used cross-platform 2019-04-29 13:37:30 +02:00
Bettenbuk Zoltan
b886f8d72d rn: specify chat text field font color 2019-04-26 21:33:11 +02:00
Дамян Минков
a6555c5d24 Singleton follow me (#4144)
* Prints errors in case of wrong initialization.

Not printing can masks some errors in the code.

* Allow only one Follow Me moderator in a meeting.

* Sends Follow Me state with all presences of the moderator.

This fixes an issue where the moderator sends the Follow Me state and then for example mute or unmute video (this will produce a presence without Follow Me state) and the new comers will not reflect current Follow Me state till a change of it comes.

* Changes fixing comments.

* Changes fixing comments.
2019-04-26 18:11:53 +00:00
Hristo Terezov
98c8fb09c4 feat(package.json): Update lib-jitsi-meet. 2019-04-26 07:54:38 -07:00
Leonard Kim
b76b261cab fix(invite): show telephone icon 2019-04-26 07:46:41 -07:00
Bettenbuk Zoltan
0b6c51f666 rn: replace 3rd party chat library with custom implementation 2019-04-26 15:17:36 +02:00
Sylvia van Os
1cb9bbc7a4 Fix wrong filename change 2019-04-26 09:06:43 +00:00
Sylvia van Os
fee9bdb98c Fix NAT documentation 2019-04-26 09:06:43 +00:00
virtuacoplenny
fb82cf4517 fix(modal): bump dep to 8.0.1 to remove scrollbars (#4139) 2019-04-25 11:58:11 -07:00
Saúl Ibarra Corretgé
85388b8d23 ios: handle some corner cases with Firebase Dynamic Links
- handle some weird bug
(https://github.com/firebase/firebase-ios-sdk/issues/233)
- use a common function to extract the URL off a dynamic link
2019-04-25 18:18:09 +02:00
Saúl Ibarra Corretgé
33f133ac25 ios: simplify code for handling CallKit listeners
Replace the Swift array with an Objective-C one, since it's going to store
Objective-C objects and not Swift objects (or Swift objects which inherit from
NSObject, which is equivalent).

This avoids the need for JMCallKitEventListenerWrapper entirely, since an
NSArray can store NSObjectProtocol objects, unlike a Swift array, which prompted
the creation of the wrapper in the first place.
2019-04-25 18:17:55 +02:00
Saúl Ibarra Corretgé
774c5ecd18 rn: ensure the conference terminated event is always sent
Dear reader, I'm not proud at all of what you are about to read, but sometimes
life just gives you lemons, so enjoy some lemonade!

Joining a conference implies first creating the XMPP connection and then joining
the MUC. It's very possible the XMPP connection was made but there was no chance
for the conference to be created.

This patch fixes this case by artificially genrating a conference terminated
event in such case. In order to have all the necessary knowledge for this event
to be sent the connection now keeps track of the conference that runs it.

In addition, there is an even more obscure corner case: it's not impossible to
try to disconnect when there is not even a connection. This was fixed by
creating a fake disconnect event. Alas the location URL is lost at this point,
but it's better than nothing I guess.
2019-04-25 14:04:26 +02:00
Saúl Ibarra Corretgé
ccc5e19e3c rn: fix filling LoadConfigOverlay's background 2019-04-24 17:41:28 +02:00
Saúl Ibarra Corretgé
32a81b0be5 rn: add our benevolent employer to the list of known domains 2019-04-24 17:36:41 +02:00
Saúl Ibarra Corretgé
59db39d4d9 rn: cleanup old code
This legacy code was added about a year ago to ease the migration between
releases:
631f51d627

I consider this not to be needed anymore.
2019-04-24 17:36:41 +02:00
damencho
2219298501 Updates web styling for file recording service sharing option. 2019-04-24 14:54:33 +00:00
Bettenbuk Zoltan
f92d530b0a Fix double file recording sharing switch 2019-04-24 10:35:43 +00:00
Bettenbuk Zoltan
e98c169c2f [RN] Fix iOS keyboard bug on invite search dialog 2019-04-24 11:48:09 +02:00
Saúl Ibarra Corretgé
6bf962817b ios: add a CallKit icon
The SDK will now search for an asset called "CallKitIcon" on the main bundle,
and fallback to a built-in asset it it's not there, allowing SDK users to
customize it by just adding asset with that name.
2019-04-24 10:03:46 +02:00
virtuacoplenny
4c286b8580 chore(deps): bump lib to get fixed rtc.destroy call (#4122) 2019-04-23 15:38:32 -07:00
Дамян Минков
37639a5614 Merge pull request #4121 from jitsi/remove-edge-support
Removes Edge support.
2019-04-23 20:32:17 +00:00
Bettenbuk Zoltan
b7198ba4b3 Add recording file sharing switch 2019-04-23 20:32:10 +00:00
damencho
2180d33e3d Adds alias for external_api.js in all default web config. 2019-04-23 20:31:53 +00:00
damencho
43a0ae578e Removes Edge support. 2019-04-23 17:55:07 +01:00
Bettenbuk Zoltan
154200460d Add some other paths to proxy bypass 2019-04-23 18:00:27 +02:00
Saúl Ibarra Corretgé
9a92dc578c ios: fix resetting CallKit's CXProvider
When CallKit is enabled / disabled, a new CXProvider must be created in order to
not confuse CallKit (it misbehaves otherwise).
2019-04-23 09:55:01 +02:00
paweldomas
eb38300c0d chore: update LJM to 1bfc96a2876d6527bfa27cf46d4f1e9f6e65edbc
Updates LJM in order to stop printing few obsolete error messages.
2019-04-18 12:24:23 -05:00
paweldomas
37b1ccbe61 ios: update react-native-werbtc in order to fix leaking streams 2019-04-18 12:24:23 -05:00
Bettenbuk Zoltan
725f9b1961 [RN] Rename lock room button 2019-04-18 14:18:08 +01:00
Leonard Kim
b172639237 fix(follow-me): remove duplicate default state 2019-04-17 15:58:55 -05:00
virtuacoplenny
c7013f5c4b ref(follow-me): hook into redux (#3991)
Use subscribers to detect state change and emit those
out to other participants. Use middleware to register
the command listener.
2019-04-17 08:05:32 -07:00
Saúl Ibarra Corretgé
aefa836406 Revert "misc: don't show a warning on Safari with VP8"
This reverts commit 9625be1db3.
2019-04-17 15:15:48 +02:00
virtuacoplenny
f6c410610a fix(toolbar): let click through gradient (#4107)
Otherwise it can eat clicks on elements it is above,
like YouTube and Etherpad controls.
2019-04-17 06:15:08 -07:00
Saúl Ibarra Corretgé
603d161788 ios: remove no longer needed code
Ever since we switched to handling track events instead of mute actions this has
been dead code. It was also added in the wrong place, since it's responsibility
of the JS code to solve the ping-pong problem.
2019-04-17 13:24:42 +02:00
Saúl Ibarra Corretgé
c1f8a35156 doc: add links to the sample SDK applications repo 2019-04-17 09:37:28 +02:00
virtuacoplenny
866dc4dbc6 ref(subtitles): remove logic around dialing transcriber (#4011) 2019-04-16 12:27:17 -07:00
damencho
69a12395d2 Removes debug log and adds safety check whether config exists. 2019-04-16 21:16:38 +02:00
virtuacoplenny
17627291e8 ref(css): use var for desktop drag area margin (#4104) 2019-04-16 11:45:25 -07:00
Hristo Terezov
30669c7699 feat(lib-jitsi-meet): Update version. 2019-04-16 17:05:02 +01:00
Hristo Terezov
4abc2db24a fix(device-selection): Default device change. 2019-04-16 17:05:02 +01:00
Bettenbuk Zoltan
2b4ace75ae [RN] Add connection indicator 2019-04-16 17:33:23 +02:00
Saúl Ibarra Corretgé
3217ef2bb4 ios: make sure symbols are uploaded to TestFlight
While that option should default to true, let's be explicit about it.
2019-04-16 15:43:15 +02:00
Bettenbuk Zoltan
e5caca9cfd [RN] Add display name label to tile view 2019-04-15 18:58:15 +02:00
Bettenbuk Zoltan
42c85c22a9 [RN] rearrange display-name files for later refactor 2019-04-15 18:58:15 +02:00
Leonard Kim
fd0eef4c84 fix(chat): change margins to avoid different background color 2019-04-12 22:39:09 +01:00
Leonard Kim
ab022c62f5 fix(filmstrip): bring down height to avoid electron drag area 2019-04-12 22:39:09 +01:00
Leonard Kim
f9c0c3e2f6 fix(chat): bring down elements to avoid electron drag area 2019-04-12 22:39:09 +01:00
paweldomas
70ec7c5b3d ios: bump travis osx_image to 10.2 2019-04-12 16:16:57 -05:00
virtuacoplenny
ca38bd53fe fix(chat): use css for arrow (#4097) 2019-04-12 13:59:03 -07:00
Leonard Kim
b3cae9a962 fix(chat): allow smiley opener to expand in width
The chat icons are different on windows and mac, with
windows icons being bigger. By settings a specific
width on the smiley container, windows would see
part of the smiley cut off.
2019-04-12 18:29:04 +01:00
Saúl Ibarra Corretgé
b768b88491 android: add ability to override SDK version when releasing 2019-04-12 15:39:29 +02:00
Saúl Ibarra Corretgé
9f339c452b android: raise SDK version 2019-04-12 15:39:29 +02:00
Leonard Kim
c34f9cf233 fix(screenshare): properly gate autopin behavior behind flag check 2019-04-11 09:09:48 -07:00
virtuacoplenny
76642b7c4b feat(screenshare): add auto pin of latest screen share (#4076) 2019-04-11 08:53:34 -07:00
Saúl Ibarra Corretgé
b78989f5f2 android: improve SDK release script
- don't hardcode defaults in gradle files
- allow for uploading also to HTTP URLs
- support HTTP authentication when publishing
2019-04-11 17:43:33 +02:00
virtuacoplenny
088b5d95c2 chore(deps): update lib for listener leak fix (#4084) 2019-04-11 08:00:05 -07:00
virtuacoplenny
c6e5adbe0e fix(large-video): respect update in progress when queuing update (#4078)
When a fade in/out animation is in progress, another large
video update can be queued but can try to force itself onto
large video. For example a pin can be in progress and while
the fade in/out animation plays, local video can change its
video type during the animation and forcing an update of
large video. This results in local video getting forcible
updated onto large video while the pinned video is left on
small video only.
2019-04-10 08:16:02 -07:00
Bettenbuk Zoltan
8bb56be317 [RN] Add conference connecting overlay 2019-04-10 15:47:36 +02:00
Bettenbuk Zoltan
84b917d708 Reorg overlay feature files 2019-04-10 15:47:36 +02:00
Bettenbuk Zoltan
18d908ce84 Fix flow errors from base/connection 2019-04-10 15:47:36 +02:00
Bettenbuk Zoltan
3987655f2a Refactor config loading 2019-04-10 15:47:36 +02:00
Saúl Ibarra Corretgé
ee3b8af4cf ios: remove PiP sample application
It now lives here: https://github.com/jitsi/jitsi-meet-sdk-samples
2019-04-10 15:10:47 +02:00
Saúl Ibarra Corretgé
9625be1db3 misc: don't show a warning on Safari with VP8 2019-04-10 14:15:02 +02:00
Saúl Ibarra Corretgé
21c0745504 android,ios: now working on version 19.2 2019-04-10 14:14:04 +02:00
Saúl Ibarra Corretgé
26c9ee5f9c deps: lib-jitsi-meet@9659b4e413b12c637810fb2ab52354f1357a177e 2019-04-10 12:32:57 +02:00
Saúl Ibarra Corretgé
9b7af64e11 ios: raise SDK version 2019-04-09 16:08:45 +02:00
Saúl Ibarra Corretgé
f26c5570df ios: update sample PiP app
- Xcode 10.2 + Swift 5
- Latest SDK API
- Fix warnings
2019-04-09 16:08:45 +02:00
Saúl Ibarra Corretgé
d37a0eee3a ios: fix compilation warnings 2019-04-09 16:08:45 +02:00
Saúl Ibarra Corretgé
024fc73e63 ios: update to Xcode 5 and Swift 5 2019-04-09 16:08:45 +02:00
Saúl Ibarra Corretgé
bdaabf6d3d odeps: react-native@0.59.4
Fixes a crash on some old Android devices.
2019-04-09 16:08:45 +02:00
virtuacoplenny
7a677ead93 ref(device-selection): set audio output sink id after receiving ref (#4066)
The Audio.js setRef callback does not behave like react ref callback
in that the former will not have fired before componentDidMount
but the later will have. So for audio output preview, trying to set
sink id on mount will no-op because it does not have a ref yet to
Audio.js, possibly leading to audio output previews playing on
the default speaker device. This generally has not been a user
visible problem due to coincidence; other re-renders necessary
by the parent of audio output preview will have triggered
componentDidUpdates on the audio out preview, which would then
set the sink id on the Audio.js ref it should have received
by then.
2019-04-08 10:38:06 -07:00
virtuacoplenny
e7812c7d84 fix(device-selection): search for device by label and kind (#4064)
Searching for a device (id) by label alone can result in
false results when devices share labels, such as a mic
and speaker having the same label. To prevent such,
specify the device kind to be found instead of iterating
over all device kinds.
2019-04-08 10:03:45 -07:00
damencho
ea54713f9a Supports prosody 0.11 when configuring.
Doing few changes needed for general config and for tokens.
2019-04-05 17:18:17 +02:00
virtuacoplenny
5a99697ae2 fix(toolbox): fix typo in action type for hide timeout (#4069) 2019-04-04 10:53:14 -07:00
virtuacoplenny
ec09085a50 fix(device-selection): set audio output device on initial configuration
When the iFrame api is used to set a preferred audio output using
options passed into the JitsiMeetExternalAPI constructor, no logic
fires to actually change the audio output destination.
2019-04-04 08:10:01 -07:00
virtuacoplenny
b731459ea4 fix(device-selection): use device kind when getting current devices (#4059)
Devices of different kinds can have the same id, such as speaker
and mic both being default. Using id only can then lead to
incorrectly setting device descriptions in the current devices
object.
2019-04-03 08:06:41 -07:00
Saúl Ibarra Corretgé
f73d3a4063 ios: add SDK release script 2019-04-03 16:15:11 +02:00
Saúl Ibarra Corretgé
4c3cf8c14a android: add an improved SDK release script
It releases the SDK and all dependencies (including React Native) to the
specified Maven repo.
2019-04-03 16:15:11 +02:00
Saúl Ibarra Corretgé
1f371ab055 android: simplify qualifying dependencies when publishing
Use an ever increasing number so no manual updates are necessary.
2019-04-03 16:15:11 +02:00
Saúl Ibarra Corretgé
dfeb26597b android: make Maven repo used for publising configurable 2019-04-03 16:15:11 +02:00
Saúl Ibarra Corretgé
384f7d7890 ios: update Podfile.lock 2019-04-02 16:18:09 +02:00
Bettenbuk Zoltan
4d9dcf5d43 [RN] Add InfoDialogButton 2019-04-02 16:18:09 +02:00
Saúl Ibarra Corretgé
e217e10af5 android: update custom version for native dependencies 2019-04-02 14:17:45 +02:00
Saúl Ibarra Corretgé
7feec7c11d deps: react-native-sound€0.10.12 2019-04-02 14:17:45 +02:00
Saúl Ibarra Corretgé
36eb27e233 rn: add build information to SettingsView 2019-04-02 12:40:35 +02:00
Bettenbuk Zoltan
b791fc32fd [RN] Make BaseIndicator render simpler 2019-04-01 21:03:36 +02:00
Bettenbuk Zoltan
b1a70240fc Clear raise hand status on confidence leave 2019-04-01 21:03:36 +02:00
Bettenbuk Zoltan
50d7c1521f Remove legacy web raise hand code 2019-04-01 21:03:36 +02:00
Bettenbuk Zoltan
5d9762b429 Extract notification timeout to a constant 2019-04-01 21:03:36 +02:00
Bettenbuk Zoltan
4e78502c9e Generalize indicators 2019-04-01 21:03:36 +02:00
Bettenbuk Zoltan
6ff733dae0 Platform generic notification for raised hand 2019-04-01 21:03:36 +02:00
Bettenbuk Zoltan
2dc59b9ea0 [RN] Add button to toggle raised hand 2019-04-01 21:03:36 +02:00
Bettenbuk Zoltan
e65918564b [RN] Add UI for raised hand feature 2019-04-01 21:03:36 +02:00
Bettenbuk Zoltan
ce9744b9c3 Move participant event handler to a platform generic location 2019-04-01 21:03:36 +02:00
damencho
b413457a4f Commit from translate.jitsi.org by user damencho.: 262 of 588 strings translated (0 fuzzy). 2019-04-01 12:41:41 +00:00
damencho
75daedf9ab Commit from translate.jitsi.org by user damencho.: 406 of 583 strings translated (30 fuzzy). 2019-04-01 12:40:08 +00:00
damencho
45eeea447a Commit from translate.jitsi.org by user damencho.: 583 of 583 strings translated (0 fuzzy). 2019-04-01 12:40:00 +00:00
damencho
16fcc55ad1 Commit from translate.jitsi.org by user damencho.: 583 of 583 strings translated (0 fuzzy). 2019-04-01 12:39:52 +00:00
damencho
a70009e486 Commit from translate.jitsi.org by user damencho.: 583 of 583 strings translated (0 fuzzy). 2019-04-01 12:39:41 +00:00
damencho
06502e5aac Commit from translate.jitsi.org by user damencho.: 423 of 583 strings translated (24 fuzzy). 2019-04-01 12:39:33 +00:00
damencho
6316447d4b Commit from translate.jitsi.org by user damencho.: 504 of 583 strings translated (0 fuzzy). 2019-04-01 12:39:06 +00:00
jitsi-pootle
df5fa71b92 New files added from translate.jitsi.org based on templates 2019-04-01 12:39:06 +00:00
Bettenbuk Zoltan
10e951c17c Reorg notifications feature files 2019-03-29 18:52:44 +01:00
Hristo Terezov
f12317dc59 docs(api.md): Comply with our coding style. 2019-03-29 16:39:21 +00:00
Hristo Terezov
829e5597d5 fix(iframe-api-devices): Misc small issues. 2019-03-29 15:42:02 +00:00
Hristo Terezov
f2e0704b93 fix(filmstrip-only): DeviceSelectionPopup 2019-03-29 15:42:02 +00:00
Hristo Terezov
a7aaf31c79 feat(iframe-api): Add deviceListChanged event. 2019-03-29 15:42:02 +00:00
Hristo Terezov
4967488e56 ref(iframe-api-devices): Use labels instead of IDs 2019-03-29 15:42:02 +00:00
Hristo Terezov
ed1d3d3df5 fix(api-devices): Initial device function calls 2019-03-29 15:42:02 +00:00
Hristo Terezov
427f49367b feat(iframe-api): Device handling. 2019-03-29 15:42:02 +00:00
Saúl Ibarra Corretgé
659e420005 ios: make sure Fastlane can update the provisioning profile
https://docs.fastlane.tools/codesigning/xcode-project/#xcode-9-and-up
2019-03-29 14:50:59 +01:00
Saúl Ibarra Corretgé
9a46606f0d misc: s/Atlassian/8x8/ 2019-03-29 13:11:36 +01:00
Leonard Kim
3b911b0362 chore(deps): bump lib-jitsi-meet for permissions api fixes 2019-03-28 08:47:42 -05:00
Philip-Choi
7ae8ae5791 doc: update README 2019-03-28 09:46:03 +01:00
paweldomas
a386740103 fix(ConnectionService): history and audio focus on Samsung devices
On some Samsung devices the call done with the ConnectionService end up
in the native call history which we don't want. That's fixable by
marking the Connection as "external" just before the call is
disconnected.

Another issue specific to Samsung devices about the audio focus not
always being release when that call ends. That's fixable by marking
the call as holding just before disconnecting it.
2019-03-27 13:39:52 -05:00
Saúl Ibarra Corretgé
451949f49d android: enable PiP support by default in JitsiMeetActivity 2019-03-27 17:39:09 +01:00
Saúl Ibarra Corretgé
61ed459971 android: add JitsiMeetActivity.launch helper methods
They greatly simplify starting a JitsiMeetActivity by encapsulating the creation
of the Intent adn extras placement.

In order to make this possible JitsiMeetConferenceOptions now implements
Parcelable so it can be serialized and passed around when creating an Intent.
2019-03-27 17:39:09 +01:00
Saúl Ibarra Corretgé
c30a4a0aa6 android: make Amplitude and Dropbox modules package private 2019-03-27 17:39:09 +01:00
paweldomas
e839684ae9 fix(base/conference): tracks not added to the conference
If tracks are created while the conference is in the 'joining' state
they will never be added.
2019-03-27 10:06:59 +01:00
Saúl Ibarra Corretgé
15c5a2339b android: fix getting permission request results
Now that we have both a Fragment and an Activity there are lifecycle methods
that overlap. If a Fragment requests permission by calling requestPermissions
then the result handler will be called on itself. React Native's permissions
module, however, calls ActivityCompat.requestPermissions on the Activity, thus
we need to handle the results at the Activity level and not at the Fragment
level.
2019-03-26 11:56:14 -05:00
virtuacoplenny
d7e112aaf0 fix(display-name): do not default name to placeholder name (#4027)
* ref(display-name): do not pass in display name

The component gets the state itself from redux.

* fix(display-name): do not default name to placeholder name

The web display name component supports inline editing of
the name. Problems can occur when the displayed name
differs from the actual saved name, because participants
without a display name, including the local user, have
a different, default display name displayed. So when
editing starts, the input field is populated with the
default name. To workaround such while supporting fetching
the display name using mapStateToProps, pass in both the
name which should be shown and the name value saved in
settings.

* ref(display-name): rename methods
2019-03-26 09:34:02 -07:00
Saúl Ibarra Corretgé
24339b2461 ios: update Podfile.lock 2019-03-26 13:47:57 +01:00
Bettenbuk Zoltan
5b02c575f7 eslint-config-jitsi#1.0.0 2019-03-26 13:35:02 +01:00
Mayur Shah
1f8904a95b deps: react-native@0.59.2 2019-03-26 10:53:50 +01:00
Saúl Ibarra Corretgé
50268a08a0 ios: ensure the git tree is clean when / after building 2019-03-25 19:08:12 +01:00
Saúl Ibarra Corretgé
985385f364 ios: update fastlane Fastfile 2019-03-25 19:08:12 +01:00
Saúl Ibarra Corretgé
f662edd135 ios: don't bundle a dummy GoogleService-Info.plist file
It complicates automated builds.
2019-03-25 19:08:12 +01:00
Saúl Ibarra Corretgé
7ebcf69937 android: add fastlane integration 2019-03-25 19:08:12 +01:00
Saúl Ibarra Corretgé
40364ae269 misc: update gitignore 2019-03-25 19:08:12 +01:00
Saúl Ibarra Corretgé
030af37668 android: generate versionCode automatically
It's a number whichb must be ever increasing with each build submitted to the
store.

Automate its value by using the number of seconds since 1st of January 2019.
That should be enough for ~680 years.
2019-03-25 19:08:12 +01:00
Saúl Ibarra Corretgé
e29bc4bbb6 deps: update react-native-callstats to version 3.58.2 2019-03-25 14:56:52 +01:00
Bettenbuk Zoltan
13212a5980 [RN] Replace chat modal with SlidingView 2019-03-25 14:52:43 +01:00
Bettenbuk Zoltan
2a5adfc601 Remove some Flow annotations 2019-03-25 13:53:08 +01:00
paweldomas
19e8e8710a fix(Android/ConnectionService): do not display the address
Turns out that on Samsung phones the calls placed with
the ConnectionService appear in the calls log as weird long numbers.
The system mangles the address we give it ("sip:meet.jit.si/something")
into this weird long number and the call to request.getAddress() returns
that. Turn off the presentation as neither this number nor our address
makes sense. This way the call appears as from "Unknown" caller in call
history which is still not perfect, but better than the random number.

Note that other phones will preserve the originally passed address value
(tested on One Plus 5).
2019-03-25 09:24:33 +01:00
paweldomas
3b24124d57 fix(Android/ConnectionService): mic not working
Turns out the microphone will not work on some devices when starting in
"audio only", because the audio mode is not set to the MODE_IN_COMMUNICATION,
but to the MODE_IN_CALL. Calling setAudioModeIsVoip(true) makes
the system adjust to MODE_IN_COMMUNICATION and the mic works fine.
2019-03-25 09:24:33 +01:00
damencho
6894de00cc Fixes a typo of getting default number. 2019-03-23 09:59:25 +00:00
paweldomas
6d0b6bee85 ref(AudioModeModule): check 1 method to enable ConnectionService 2019-03-22 09:17:14 +01:00
virtuacoplenny
ac02a17943 feat(notifications): provide a way to turn off sticky notifications (#4010) 2019-03-21 14:06:33 -07:00
paweldomas
043d4db314 fix(NotificationsContainer.native): flow error 2019-03-21 17:09:56 +01:00
Saúl Ibarra Corretgé
6a919916d3 ios: pin all pod dependencies 2019-03-21 16:57:58 +01:00
Saúl Ibarra Corretgé
1de4897a6b ios: xcode project change shenanigans
Signing wouldn't work, disabling it and enabling it again created these changes.
Oh well!
2019-03-21 16:29:18 +01:00
paweldomas
0175690a2b ref(mobile): display only the topmost notification
Only one notification will be displayed at a time on mobile.
2019-03-21 15:47:14 +01:00
paweldomas
c7979a3944 feat(mobile): add 1 liner notifications
Adds 1 liner notifications to mobile. Only the title is displayed. In
case the title is missing there's a fallback to the description.
2019-03-21 15:47:14 +01:00
paweldomas
15fd27543a ref(Conference): extract AbstractConference 2019-03-21 15:47:14 +01:00
paweldomas
64f8a8d700 fix(AbstractNotificationsContainer): broken timeouts chain
If user dismisses the not topmost notification the timeout will be
cleared and a new one will not be set, because the top notification
remained the same (see the if at line 90).
2019-03-21 15:47:14 +01:00
paweldomas
95f684da2f fix(AbstractNotificationsContainer): dismiss timeout not always set
The docs of 'componentDidUpdate' say that it's not called for the
initial render. If the component is added to the DOM with 1 notification
already, then the update will not happen and timeout will never be set
which will effectively break the timeouts chain.
2019-03-21 15:47:14 +01:00
paweldomas
f3f8dc2072 ref: move participant joined notification to the middleware 2019-03-21 15:47:14 +01:00
Saúl Ibarra Corretgé
08efc46f21 android: fix crash in debug mode 2019-03-21 14:47:55 +01:00
Saúl Ibarra Corretgé
e32336b96f android: run the React packager when running from AS
When running the app from Android Studio the React packager is not automatically
started. In vanilla RN projects this is done by the "react-native run-android"
command, but often times it is desired to run from Android Studio.

This fixes that by starting the packager from Gradle.
2019-03-21 14:47:55 +01:00
Saúl Ibarra Corretgé
c91880859b android: fix gradle warning 2019-03-21 14:47:55 +01:00
Saúl Ibarra Corretgé
cff78d7a83 android: disable delta updates
It's enabled by default, but marked as experimental (uh?!). It creates trouble
as sometimes the packager goes bananas. Disable them until further notice, our
bundle is not that large anyway.
2019-03-21 14:47:55 +01:00
Saúl Ibarra Corretgé
49a0c03ff0 ios: fix deprecation warning
NSURLConnection sendSynchronousRequest is deprecated since iOS 9. Replace the
method by whjat's currently on RN master, which implements a modern alternative.
2019-03-21 14:47:55 +01:00
Saúl Ibarra Corretgé
2c592f61c3 android: enable 64bit builds 2019-03-21 14:47:55 +01:00
Saúl Ibarra Corretgé
c025c7e132 flow: tame the beast
🔥🔥🔥
2019-03-21 14:47:55 +01:00
Saúl Ibarra Corretgé
278d3a163b flow: update type definitions 2019-03-21 14:47:55 +01:00
Saúl Ibarra Corretgé
0e92e73789 chore: use strings as action types
Using anything non-serializable for action types is discouraged:
https://redux.js.org/faq/actions#actions

In fact, this is the Flow definition for dispatching actions:

declare export type DispatchAPI<A> = (action: A) => A;
declare export type Dispatch<A: { type: $Subtype<string> }> = DispatchAPI<A>;

Note how the `type` field is defined as a subtype of string, which Symbol isn’t.
2019-03-21 14:47:55 +01:00
Saúl Ibarra Corretgé
75527e01dd deps: update lib-jitsi-meet 2019-03-21 14:47:55 +01:00
Saúl Ibarra Corretgé
b53a034aaf deps: update React Native to version 0.59
This new version comes with an updated JSC runtime, so we no longer need to
depend on the updated version ourselves.
2019-03-21 14:47:55 +01:00
paweldomas
4d1c0cf219 fix eslint error 2019-03-19 21:59:45 +01:00
virtuacoplenny
a667c9bff2 ref: js-utils for random and room name generator (#3975) 2019-03-19 10:35:36 -07:00
Damien Fetis
e652117571 Remove the boolean conversion of authLogin string to display the string value instead of 'true'. (#3995) 2019-03-19 08:55:14 -07:00
Hristo Terezov
a6719896a2 fix(recording): Respect the selected recording service. 2019-03-15 20:12:25 +00:00
damencho
f5a7e0bccb Adds provider name to fix welcomepage text.
* Removes unused ADD_PEOPLE_APP_NAME
* Moves deep-link header background and logo size as variables.
* Fixes more numbers page space in the header.
* Fixes left padding on deep-linking mobile page.
2019-03-15 11:24:18 +00:00
damencho
f94db0da2c Adjust some paths to respect base. 2019-03-14 11:22:37 +00:00
paweldomas
460593a93e chrome: bump LJM to 74f48e168eec4c05fd8600812cc00e6e34e9ab90
Required for the Spot's TURN changes to work
2019-03-13 14:25:19 -05:00
paweldomas
19a27e75bd feat(spot): pass JitsiConnection to ProxyConnectionService to get TURN 2019-03-13 14:25:19 -05:00
Hristo Terezov
cb8e9eed5e feat(subject): UI 2019-03-12 23:03:58 +00:00
Saúl Ibarra Corretgé
2715e81f1d rn: add more SDK documentation 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
f941f15def ios: remove Jitsi Meet specific defaults
The app should always provide them.
2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
7712c6913c android: make ReactInstanceManagerHolder.emitEvent return void 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
45b6a8b5d5 android: throw if a unsupported type makes it to the props Bundle 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
26ca0e6630 android: throw if the overlsay permission is not granted in Debug mode 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
7978f9f5f4 misc: update .gitignore 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
d39290f9fa rn: refactor conference events
Consolidate all failure cases into a single one: CONFERENCE_TERMINATED. If the
conference ended gracefully no error indicator will be present, otherwise there
will be.
2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
f696a6dbe2 ios: update SDK documentation 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
bf3bcd65d6 android: add JitsiMeetActivity
It renders a single JitsiMeetFragment which holds the JitsiMeetView view.
2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
a7018970ca android: update SDK documentation 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
20edb7c279 android: make onExternalAPIEvent protected 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
59b00d022b android: don't proxy enterPictureInPicture in JitsiMeetFragment 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
53722fd2e6 android: remove ReactContextUtils
In practice, we are never going to be in a position where we don't have a
ReactContext but we do have some React Native code running. So let's not expect
the impossible.
2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
54bab793e5 android: remove JitsiMeetViewAdapter
It was never used and typicallt the Activity / Fragment holding the
JitsiMeetView object will be the listener.

In addition, once we refactor the events they will be reduced into far fewer.
2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
975ff9c83d rn: support passing serverURL and room to URL object
That's what the SDK passes now, if the room URL is not absolute.
2019-03-12 16:55:28 +01:00
paweldomas
5b3e8a9b5e android: introduce JitsiMeetConferenceOptions
Co-authored-by: Saúl Ibarra Corretgé <saghul@jitsi.org>
2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
aedcfba263 ios: introduce JitsiMeetConferenceOptions 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
b97cb3509a rn: document externalAPIScope prop 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
7d8ea85ea0 rn: start removing defaultURL 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
e4c3e15791 rn: simplified code
There is no need for AbstractApp to require some getWindowLocation function.
It's only used in one place and we even polyfill it on mobile.

Thus replace it's usage with more specific functions.
2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
56135bd085 android: add initial implementation of join / leave 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
549b495d16 ios: add initial implementation of join / leave 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
f3abca6462 ios: add ability to control deep / universal linking
Since the SDK may be embedded with other apps, we need to recognize our custom
URL scheme and universal links in order to tell the user if we will process the
request or not.

Make them configurable with sane defaults.
2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
405905be82 rn: raise SDK version 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
468b02b812 ios: adjust to latest Swift syntax 2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
3fa5aed950 rn: drop deep / universal links handling from JS
It's now all handled in the SDK an we'll get the new URLs via props.
2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
90803c8ff6 android: SDK v2 pass one
Add JitsiMeetFragment and refactor the app to use it.
2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
dbc88b972e ios: SDK v2 pass one
- cleanup
- API simplification (single loadURL method)
- JitsiMeet singleton for linking helpers and globals
- Linking moved to app
2019-03-12 16:55:28 +01:00
Saúl Ibarra Corretgé
1c47720a08 misc: ignore build artifacts 2019-03-12 16:55:28 +01:00
Hristo Terezov
59fc3642a6 feat(amplitude): add mobile implementation 2019-03-12 16:48:08 +01:00
Saúl Ibarra Corretgé
19b4b92150 deps: update lib-jitsi-meet 2019-03-12 16:48:08 +01:00
Bettenbuk Zoltan
8400d01d75 [RN] Add color scheme support to dialog buttons 2019-03-12 12:36:15 +01:00
Bettenbuk Zoltan
d04068344a [RN] Make header button same size as header label 2019-03-12 12:36:15 +01:00
Bettenbuk Zoltan
55a971c0fd [RN] Add color scheme support to header 2019-03-12 12:36:15 +01:00
Bettenbuk Zoltan
20c1b1cfae [RN] Wrap PagedList navigator with SafeAreaView 2019-03-12 12:36:15 +01:00
Bettenbuk Zoltan
ecb44b6ab4 [RN] Make the header more compact 2019-03-12 12:36:15 +01:00
Jose Angel Gonzalez
039805eba3 fix(android-sdk): Recover audio device if the OS changes it 2019-03-12 09:55:51 +01:00
virtuacoplenny
22277ad799 feat(api): add notification for when filmstrip gets toggled (#3974) 2019-03-11 11:17:28 -07:00
Hristo Terezov
2af1e8da95 fix(remote-video-menu): Icon position 2019-03-11 17:36:58 +00:00
Дамян Минков
12d0aef686 Updates recording dialog. (#3953)
* Updates recording dialog.

* Update config.js doc.

* Adds comment and make a check more intuitive.

* Changes of using enum for recording types.
2019-03-11 09:17:21 -07:00
Hristo Terezov
f439ad2999 feat(popover): Make the popover menus customizable. 2019-03-11 16:04:20 +00:00
Leonard Kim
81d4f694b7 fix(chat): prevent empty messages 2019-03-09 12:09:29 +01:00
Saúl Ibarra Corretgé
c737d46d90 android: update gradle plugin version 2019-03-08 17:24:49 +01:00
Saúl Ibarra Corretgé
3f2a559d64 rn: now working on version 19.1 2019-03-08 14:04:03 +01:00
Saúl Ibarra Corretgé
bdb3099073 android: fix running on Android < M
The android.telecom.CallAudioState class was only added in API level 23 (Android
M), so make sure we don't import it in lower versioned devices.
2019-03-08 11:37:25 +01:00
damencho
b3a05db286 Update dropbox redirect uri to always use the main domain static page. 2019-03-07 15:39:16 +00:00
virtuacoplenny
08f2edf350 feat(screenshare): emit source type when starting screenshare (#3959)
* feat(screenshare): emit source type when starting screenshare

* squash: update doc
2019-03-06 21:46:17 -08:00
Bettenbuk Zoltan
98c7430b6f [RN] Replace red screen with dialog 2019-03-07 01:09:03 +01:00
Bettenbuk Zoltan
ebdcbe122a Change dialog button property keys 2019-03-07 01:09:03 +01:00
virtuacoplenny
31c1034be7 deps(chore): bump lib-jitsi-meet to e398584 (#3958)
Bring over two fixes for spot. One is for
identifying the screenshare type when using
a camera for screenshare or when using a proxy
stream. Also bring in a fix to avoid a js error
in chrome ios.
2019-03-06 15:39:01 -08:00
Hristo Terezov
a9d82a79ea fix(toolbar): Move buttons to overflow menu when the space isn't enough 2019-03-06 17:51:31 +00:00
Bettenbuk Zoltan
27e1f5a1bc [RN] Avoid adding undefined in the invite 2019-03-06 16:52:22 +01:00
virtuacoplenny
dbedee5e22 chore(deps): update to lib 320919e (#3951) 2019-03-05 17:02:22 -08:00
Дамян Минков
636c63397b Adds integrations doc. (#3929)
* Adds integrations doc.

Google, Microsoft and Dropbox for now.

* Updates doc.
2019-02-28 09:09:09 +00:00
damencho
67e7994e36 Removes unused config. 2019-02-27 16:45:26 +00:00
damencho
40f03fedc2 Replaces emoji flags with flag from a png.
Seems like windows does not have emojis for flags.
2019-02-27 16:45:26 +00:00
Hristo Terezov
55149670da fix(dropbox-auth): In Electron. 2019-02-27 16:12:54 +00:00
Gabriel-Tiberiu Imre-Lucaci
5739e1deaa feat(external_api): notify when api is disposed 2019-02-27 14:39:04 +00:00
Bettenbuk Zoltan
b6e2701991 [RN] Add invite screen 2019-02-27 13:26:21 +01:00
Bettenbuk Zoltan
38b1be1291 [RN] Extract AvatarListItem 2019-02-27 13:26:21 +01:00
damencho
555f8b3a99 Fixes toll free position. 2019-02-26 17:08:56 +00:00
Дамян Минков
ea4d49f2a0 Adds new format of phoneList service and re-design dial in numbers page. (#3903)
* Adds new format of phoneList service and re-design dial in numbers page.

Adds flags and country names (with translations) for the numbers if using the new format.

* Fixes tests and fixes get default number.

* Updates swagger with new format.

* Moves html back yo table.

Fixes displaying on mobile and also the tel: URI generation. The tel: URI is tested on Android and iOS and seems to work (Android was not interpreting 'p', but both seems to like ',').

* Fixes a wrong return statement.

* Small fixes.
2019-02-26 13:32:46 +00:00
fossterer
d7eea8abbc Added links related to NAT config in a new FAQ doc 2019-02-26 09:53:48 +00:00
paweldomas
1b8ef9a05a chore: update LJM to 5a9fc76739bcf0bed50676c7be160f688f3a19b5 2019-02-25 14:16:05 -06:00
Emil Ivov
ac7311cb52 Merge pull request #3920 from jitsi/emcho-patch-3
Update api.md
2019-02-23 17:04:13 +00:00
784 changed files with 25489 additions and 13217 deletions

View File

@@ -33,7 +33,6 @@
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/
node_modules/react-native/flow-github/
[options]
emoji=true
@@ -83,4 +82,4 @@ module.file_ext=.jsx
module.file_ext=.json
[version]
^0.78.0
^0.92.0

11
.gitignore vendored
View File

@@ -70,8 +70,10 @@ buck-out/
*/fastlane/Preview.html
*/fastlane/screenshots
# Bundle artifact
# Build artifacts
*.jsbundle
*.framework
android/app/release
# precommit-hook
.jshintignore
@@ -80,3 +82,10 @@ buck-out/
# VSCode files
android/.project
android/.settings/org.eclipse.buildship.core.prefs
# Secrets
android/app/dropbox.key
android/app/google-services.json
ios/app/dropbox.key
ios/app/GoogleService-Info.plist

View File

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

View File

@@ -13,7 +13,7 @@ Found a bug and know how to fix it? Great! Please read on.
## Contributor License Agreement
While the Jitsi projects are released under the
[Apache License 2.0](https://github.com/jitsi/jitsi-meet/blob/master/LICENSE), the copyright
holder and principal creator is [Atlassian](https://www.atlassian.com/). To
holder and principal creator is [8x8](https://www.8x8.com/). To
ensure that we can continue making these projects available under an Open Source license,
we need you to sign our Apache-based contributor
license agreement as either a [corporation](https://jitsi.org/ccla) or an

View File

@@ -45,8 +45,6 @@ deploy-appbundle:
$(OUTPUT_DIR)/analytics-ga.js \
$(BUILD_DIR)/analytics-ga.min.js \
$(BUILD_DIR)/analytics-ga.min.map \
$(BUILD_DIR)/analytics-amplitude.min.js \
$(BUILD_DIR)/analytics-amplitude.min.map \
$(DEPLOY_DIR)
deploy-lib-jitsi-meet:

View File

@@ -1,16 +1,16 @@
# Jitsi Meet - Secure, Simple and Scalable Video Conferences
Jitsi Meet is an open-source (Apache) WebRTC JavaScript application that uses [Jitsi Videobridge](https://jitsi.org/videobridge) to provide high quality, [secure](#security) and scalable video conferences. You can see Jitsi Meet in action [here at the session #482 of the VoIP Users Conference](http://youtu.be/7vFUVClsNh0).
Jitsi Meet is an open-source (Apache) WebRTC JavaScript application that uses [Jitsi Videobridge](https://jitsi.org/videobridge) to provide high quality, [secure](#security) and scalable video conferences. Jitsi Meet in action can be seen at [here at the session #482 of the VoIP Users Conference](http://youtu.be/7vFUVClsNh0).
The Jitsi Meet client runs in your browser, without the need for installing anything on your computer. You can also try it out yourself at https://meet.jit.si .
The Jitsi Meet client runs in your browser, without installing anything on your computer. You can try it out at https://meet.jit.si .
Jitsi Meet allows for very efficient collaboration. It allows users to stream their desktop or only some windows. It also supports shared document editing with Etherpad.
Jitsi Meet allows very efficient collaboration. Users can stream their desktop or only some windows. It also supports shared document editing with Etherpad.
## Installation
On the client side, no installation is necessary. You just point your browser to the URL of your deployment. This section is about installing the Jitsi Meet suite on your server and hosting your own conferencing service.
On the client side, no installation is necessary. You just point your browser to the URL of your deployment. This section is about installing a Jitsi Meet suite on your server and hosting your own conferencing service.
Installing Jitsi Meet is quite a simple experience. For Debian-based systems, we recommend following the [quick-install](https://github.com/jitsi/jitsi-meet/blob/master/doc/quick-install.md) document, which uses the package system. You can also see a demonstration of the process in [this tutorial video](https://jitsi.org/tutorial).
Installing Jitsi Meet is a simple experience. For Debian-based system, following the [quick-install](https://github.com/jitsi/jitsi-meet/blob/master/doc/quick-install.md) document, which uses the package system. You can also see a demonstration of the process in [this tutorial video](https://jitsi.org/tutorial).
For other systems, or if you wish to install all components manually, see the [detailed manual installation instructions](https://github.com/jitsi/jitsi-meet/blob/master/doc/manual-install.md).
@@ -33,7 +33,7 @@ You can get our mobile versions from here:
## Building the sources
Node.js >= 8 and npm >= 6 are required.
Node.js >= 10 and npm >= 6 are required.
On Debian/Ubuntu systems, the required packages can be installed with:
```
@@ -59,7 +59,7 @@ To work with local copy you must change the path to:
"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' will not do it.
To make the project you must force it to take the sources as 'npm update':
```
npm install lib-jitsi-meet --force && make
```
@@ -84,8 +84,8 @@ cd ../jitsi-meet
npm link lib-jitsi-meet
```
So now 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, no longer the case with version 6.x.
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
@@ -100,7 +100,7 @@ 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:
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
@@ -115,18 +115,16 @@ see our [guidelines for contributing](CONTRIBUTING.md).
## Embedding in external applications
Jitsi Meet provides a very flexible way of embedding it in external applications by using the [Jitsi Meet API](doc/api.md).
Jitsi Meet provides a very flexible way of embedding in external applications by using the [Jitsi Meet API](doc/api.md).
## Security
WebRTC today does not provide a way of conducting multiparty conversations with
end-to-end encryption. As a matter of fact, unless you consistently vocally
compare DTLS fingerprints with your peers, the same goes for one-to-one calls.
As a result when using a Jitsi Meet instance, your stream is encrypted on the
network but decrypted on the machine that hosts the bridge.
WebRTC does not 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.
The Jitsi Meet architecture allows you to deploy your own version, including
all server components, and in that case your security guarantees will be roughly
equivalent to these of a direct one-to-one WebRTC call. This is what's unique to
all server components. In that case, your security guarantees will be roughly
equivalent to a direct one-to-one WebRTC call. This is the uniqueness of
Jitsi Meet in terms of security.
The [meet.jit.si](https://meet.jit.si) service is maintained by the Jitsi team
@@ -138,4 +136,4 @@ Instructions on how to build it can be found [here](doc/mobile.md).
## Acknowledgements
Jitsi Meet started out as a sample conferencing application using Jitsi Videobridge. It was originally developed by then ESTOS' developer Philipp Hancke who then contributed it to the community where development continues with joint forces!
Jitsi Meet started out as a sample conferencing application using Jitsi Videobridge. It was originally developed by ESTOS' developer Philipp Hancke who then contributed it to the community where development continues with joint forces!

View File

@@ -1,8 +1,15 @@
# Jitsi Meet SDK for Android
## Sample applications using the SDK
If you want to see how easy integrating the Jitsi Meet SDK into a native application is, take a look at the
[sample applications repository](https://github.com/jitsi/jitsi-meet-sdk-samples).
## Build your own, or use a pre-build SDK artifacts/binaries
Jitsi conveniently provides a pre-build SDK artifacts/binaries in its Maven repository. When you do not require any modification to the SDK itself, it's suggested to use the pre-build SDK. This avoids the complexity of building and installing your own SDK artifacts/binaries.
Jitsi conveniently provides a pre-build SDK artifacts/binaries in its Maven repository. When you do not require any
modification to the SDK itself or any of its dependencies, it's suggested to use the pre-build SDK. This avoids the
complexity of building and installing your own SDK artifacts/binaries.
### Use pre-build SDK artifacts/binaries
@@ -29,23 +36,10 @@ Dependency definitions belong in the individual module `build.gradle` files:
```gradle
dependencies {
// (other dependencies)
implementation ('org.jitsi.react:jitsi-meet-sdk:+') { transitive = true }
implementation ('org.jitsi.react:jitsi-meet-sdk:2.+') { transitive = true }
}
```
Also, enable 32bit mode for react-native, since react-native only supports 32bit apps. (If you have a 64bit device, it will not run unless this setting it set)
```gradle
android {
...
defaultConfig {
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
...
```
### Build and use your own SDK artifacts/binaries
<details>
@@ -57,53 +51,20 @@ A note on dependencies: Apart from the SDK, Jitsi also publishes a binary Maven
If you want to use a SDK that is built from source, you will likely benefit from composing a local Maven repository that contains these dependencies. The text below describes how you create a repository that includes both the SDK as well as these dependencies. For illustration purposes, we'll define the location of this local Maven repository as `/tmp/repo`
In source code form, the Android SDK dependencies are locked/pinned by package.json and package-lock.json of the Jitsi Meet project. To obtain the data, execute NPM in the parent directory:
In source code form, the Android SDK dependencies are locked/pinned by package.json and package-lock.json of the Jitsi Meet project. To obtain the data, execute NPM in the jitsi-meet project directory:
$ (cd ..; npm install)
npm install
This will pull in the dependencies in either binary format, or in source code format, somewhere under /node_modules/
At the time of writing, there are two packages pulled in in binary format.
Third-party React Native _modules_, which Jitsi Meet SDK for Android depends on, are download by NPM in source code
or binary form. These need to be assembled into Maven artifacts, and then published to your local Maven repository.
A script is provided to facilitate this. From the root of the jitsi-meet project repository, run:
To copy React Native to your local Maven repository, you can simply copy part of the directory structure that was pulled in by NPM:
./android/scripts/release-sdk.sh /tmp/repo
$ cp -r ../node_modules/react-native/android/com /tmp/repo/
In the same way, copy the JavaScriptCore dependency:
$ cp -r ../node_modules/jsc-android/dist/org /tmp/repo/
Alternatively, you can use the scripts located in the android/scripts directory to publish these dependencies to your Maven repo.
Third-party React Native _modules_, which Jitsi Meet SDK for Android depends on, are download by NPM in source code form. These need to be assembled into Maven artifacts, and then published to your local Maven repository. The SDK project facilitates this.
To prepare, Configure the Maven repositories in which you are going to publish the SDK artifacts/binaries. In `android/sdk/build.gradle` as well as in `android/build.gradle` modify the lines that contain:
"file:${rootProject.projectDir}/../../jitsi-maven-repository/releases"
Change this value (which represents the Maven repository location used internally by the Jitsi Developers) to the location of the repository that you'd like to use:
"file:/tmp/repo"
Make sure to do this in both files! Each file should require one line to be changed.
To prevent artifacts from previous builds affecting you're outcome, it's good to start with cleaning your work directories:
$ ./gradlew clean
To create the release assembly for any _specific_ third-party React Native module that you need, you can execture the following commands, replace the module name in the examples below.
$ ./gradlew :react-native-webrtc:assembleRelease
$ ./gradlew :react-native-webrtc:publish
You build and publish the SDK itself in the same way:
$ ./gradlew :sdk:assembleRelease
$ ./gradlew :sdk:publish
Alternatively, you can assemble and publish _all_ subprojects, which include the react-native modules, but also the SDK itself, with a single command:
$ ./gradlew clean assembleRelease publish
This will build and publish the SDK, and all of its dependencies to the specified Maven repository (`/tmp/repo`) in
this example.
You're now ready to use the artifacts. In _your_ project, add the Maven repository that you used above (`/tmp/repo`) into your top-level `build.gradle` file:
@@ -121,7 +82,8 @@ Then, define the dependency `org.jitsi.react:jitsi-meet-sdk` into the `build.gra
implementation ('org.jitsi.react:jitsi-meet-sdk:+') { transitive = true }
Note that there should not be a need to explicitly add the other dependencies, as they will be pulled in as transitive dependencies of `jitsi-meet-sdk`.
Note that there should not be a need to explicitly add the other dependencies, as they will be pulled in as transitive
dependencies of `jitsi-meet-sdk`.
</details>
@@ -167,14 +129,14 @@ View is strongly recommended.
package org.jitsi.example;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v4.app.FragmentActivity;
import org.jitsi.meet.sdk.JitsiMeetView;
import org.jitsi.meet.sdk.ReactActivityLifecycleCallbacks;
// Example
//
public class MainActivity extends AppCompatActivity {
public class MainActivity extends FragmentActivity implements JitsiMeetActivityInterface {
private JitsiMeetView view;
@Override
@@ -182,13 +144,13 @@ public class MainActivity extends AppCompatActivity {
int requestCode,
int resultCode,
Intent data) {
ReactActivityLifecycleCallbacks.onActivityResult(
JitsiMeetActivityDelegate.onActivityResult(
this, requestCode, resultCode, data);
}
@Override
public void onBackPressed() {
ReactActivityLifecycleCallbacks.onBackPressed();
JitsiMeetActivityDelegate.onBackPressed();
}
@Override
@@ -196,7 +158,10 @@ public class MainActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
view = new JitsiMeetView(this);
view.loadURL(null);
JitsiMeetConferenceOptions options = new JitsiMeetConferenceOptions.Builder()
.setRoom("https://meet.jit.si/test123")
.build();
view.join(options);
setContentView(view);
}
@@ -208,12 +173,12 @@ public class MainActivity extends AppCompatActivity {
view.dispose();
view = null;
ReactActivityLifecycleCallbacks.onHostDestroy(this);
JitsiMeetActivityDelegate.onHostDestroy(this);
}
@Override
public void onNewIntent(Intent intent) {
ReactActivityLifecycleCallbacks.onNewIntent(intent);
JitsiMeetActivityDelegate.onNewIntent(intent);
}
@Override
@@ -221,21 +186,21 @@ public class MainActivity extends AppCompatActivity {
final int requestCode,
final String[] permissions,
final int[] grantResults) {
ReactActivityLifecycleCallbacks.onRequestPermissionsResult(requestCode, permissions, grantResults);
JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
protected void onResume() {
super.onResume();
ReactActivityLifecycleCallbacks.onHostResume(this);
JitsiMeetActivityDelegate.onHostResume(this);
}
@Override
protected void onStop() {
super.onStop();
ReactActivityLifecycleCallbacks.onHostPause(this);
JitsiMeetActivityDelegate.onHostPause(this);
}
}
```
@@ -262,103 +227,62 @@ implementation("com.github.bumptech.glide:annotations:${glideVersion}") {
### JitsiMeetActivity
This class encapsulates a high level API in the form of an Android `Activity`
which displays a single `JitsiMeetView`.
This class encapsulates a high level API in the form of an Android `FragmentActivity`
which displays a single `JitsiMeetView`. You can pass a URL as a `ACTION_VIEW`
on the Intent when starting it and it will join the conference, and will be
automatically terminated (finish() will be called on the activity) when the
conference ends or fails.
### JitsiMeetView
The `JitsiMeetView` class is the core of Jitsi Meet SDK. It's designed to
display a Jitsi Meet conference (or a welcome page).
#### join(options)
Joins the conference specified by the given `JitsiMeetConferenceOptions`.
#### leave()
Leaves the currently active conference. If the welcome page is enabled it will
go back to it, otherwise a black window will be shown.
#### dispose()
Releases all resources associated with this view. This method MUST be called
when the Activity holding this view is going to be destroyed, usually in the
`onDestroy()` method.
#### getDefaultURL()
Returns the default base URL used to join a conference when a partial URL (e.g.
a room name only) is specified to `loadURLString`/`loadURLObject`. If not set or
if set to `null`, the default built in JavaScript is used: https://meet.jit.si.
#### getListener()
Returns the `JitsiMeetViewListener` instance attached to the view.
#### isPictureInPictureEnabled()
Returns `true` if Picture-in-Picture is enabled; `false`, otherwise. If not
explicitly set (by a preceding `setPictureInPictureEnabled` call), defaults to
`true` if the platform supports Picture-in-Picture natively; `false`, otherwise.
#### isWelcomePageEnabled()
Returns true if the Welcome page is enabled; otherwise, false. If false, a black
empty view will be rendered when not in a conference. Defaults to false.
#### loadURL(URL)
Loads a specific URL which may identify a conference to join. If the specified
URL is null and the Welcome page is enabled, the Welcome page is displayed
instead.
#### loadURLString(String)
Loads a specific URL which may identify a conference to join. If the specified
URL is null and the Welcome page is enabled, the Welcome page is displayed
instead.
#### loadURLObject(Bundle)
Loads a specific URL which may identify a conference to join. The URL is
specified in the form of a Bundle of properties which (1) internally are
sufficient to construct a URL (string) while (2) abstracting the specifics of
constructing the URL away from API clients/consumers. If the specified URL is
null and the Welcome page is enabled, the Welcome page is displayed instead.
Example:
```java
Bundle config = new Bundle();
config.putBoolean("startWithAudioMuted", true);
config.putBoolean("startWithVideoMuted", false);
Bundle urlObject = new Bundle();
urlObject.putBundle("config", config);
urlObject.putString("url", "https://meet.jit.si/Test123");
view.loadURLObject(urlObject);
```
#### setDefaultURL(URL)
Sets the default URL. See `getDefaultURL` for more information.
NOTE: Must be called before (if at all) `loadURL`/`loadURLString` for it to take
effect.
#### setListener(listener)
Sets the given listener (class implementing the `JitsiMeetViewListener`
interface) on the view.
#### setPictureInPictureEnabled(boolean)
### JitsiMeetConferenceOptions
Sets whether Picture-in-Picture is enabled. If not set, Jitsi Meet SDK
automatically enables/disables Picture-in-Picture based on native platform
support.
This object encapsulates all the options that can be tweaked when joining
a conference.
NOTE: Must be called (if at all) before `loadURL`/`loadURLString` for it to take
effect.
Example:
#### setWelcomePageEnabled(boolean)
```java
JitsiMeetConferenceOptions options = new JitsiMeetConferenceOptions.Builder()
.setServerURL(new URL("https://meet.jit.si"))
.setRoom("test123")
.setAudioMuted(false)
.setVideoMuted(false)
.setAudioOnly(false)
.setWelcomePageEnabled(false)
.build();
```
Sets whether the Welcome page is enabled. See `isWelcomePageEnabled` for more
information.
See the `JitsiMeetConferenceOptions` implementation for all available options.
NOTE: Must be called (if at all) before `loadURL`/`loadURLString` for it to take
effect.
### ReactActivityLifecycleCallbacks
### JitsiMeetActivityDelegate
This class handles the interaction between `JitsiMeetView` and its enclosing
`Activity`. Generally this shouldn't be consumed by users, because they'd be
@@ -414,29 +338,20 @@ This is a static method.
`JitsiMeetViewListener` provides an interface apps can implement to listen to
the state of the Jitsi Meet conference displayed in a `JitsiMeetView`.
`JitsiMeetViewAdapter`, a default implementation of the
`JitsiMeetViewListener` interface is also provided. Apps may extend the class
instead of implementing the interface in order to minimize boilerplate.
##### onConferenceFailed
Called when a joining a conference was unsuccessful or when there was an error
while in a conference.
The `data` `Map` contains an "error" key describing the error and a "url" key
with the conference URL.
#### onConferenceJoined
Called when a conference was joined.
The `data` `Map` contains a "url" key with the conference URL.
#### onConferenceLeft
#### onConferenceTerminated
Called when a conference was left.
Called when a conference was terminated either by user choice or due to a
failure.
The `data` `Map` contains a "url" key with the conference URL.
The `data` `Map` contains an "error" key with the error and a "url" key
with the conference URL. If the conference finished gracefully no `error`
key will be present.
#### onConferenceWillJoin
@@ -444,20 +359,6 @@ Called before a conference is joined.
The `data` `Map` contains a "url" key with the conference URL.
#### onConferenceWillLeave
Called before a conference is left.
The `data` `Map` contains a "url" key with the conference URL.
#### onLoadConfigError
Called when loading the main configuration file from the Jitsi Meet deployment
fails.
The `data` `Map` contains an "error" key with the error and a "url" key with the
conference URL which necessitated the loading of the configuration file.
## ProGuard rules
When using the SDK on a project some proguard rules have to be added in order
@@ -470,10 +371,6 @@ rules file: https://github.com/jitsi/jitsi-meet/blob/master/android/app/proguard
Picture-in-Picture style scenario, in a rectangle too small to accommodate its
"full" UI.
Jitsi Meet SDK automatically enables (unless explicitly disabled by a
`setPictureInPictureEnabled(false)` call) Android's native Picture-in-Picture
mode iff the platform is supported i.e. Android >= Oreo.
## Dropbox integration
To setup the Dropbox integration, follow these steps:

View File

@@ -1,6 +1,7 @@
apply plugin: 'com.android.application'
boolean googleServicesEnabled = project.file('google-services.json').exists()
boolean googleServicesEnabled \
= project.file('google-services.json').exists() && !rootProject.ext.libreBuild
// Crashlytics integration is done as part of Firebase now, so it gets
// automagically activated with google-services.json
@@ -8,6 +9,11 @@ if (googleServicesEnabled) {
apply plugin: 'io.fabric'
}
// Use the number of seconds/10 since Jan 1 2019 as the versionCode.
// This lets us upload a new build at most every 10 seconds for the
// next ~680 years.
// https://stackoverflow.com/a/38643838
def vcode = (int)(((new Date().getTime()/1000) - 1546297200) / 10)
android {
compileSdkVersion rootProject.ext.compileSdkVersion
@@ -15,24 +21,14 @@ android {
defaultConfig {
applicationId 'org.jitsi.meet'
versionCode Integer.parseInt(project.buildNumber)
versionCode vcode
versionName project.appVersion
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
ndk {
abiFilters 'armeabi-v7a', 'x86'
}
packagingOptions {
// The project react-native does not provide 64-bit binaries at the
// time of this writing. Unfortunately, packaging any 64-bit
// binaries into the .apk will crash the app at runtime on 64-bit
// platforms.
exclude '/lib/mips64/**'
exclude '/lib/arm64-v8a/**'
exclude '/lib/x86_64/**'
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
}
@@ -41,11 +37,24 @@ android {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules-debug.pro'
buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${googleServicesEnabled}"
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules-release.pro'
buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${googleServicesEnabled}"
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
}
}
sourceSets {
main {
java {
if (rootProject.ext.libreBuild) {
srcDir "src"
exclude "**/GoogleServicesHelper.java"
}
}
}
}
@@ -63,7 +72,17 @@ 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 'com.google.android.gms:play-services-auth:16.0.1'
if (!rootProject.ext.libreBuild) {
implementation 'com.google.android.gms:play-services-auth:16.0.1'
// Firebase
// - Crashlytics
// - Dynamic Links
implementation 'com.google.firebase:firebase-core:16.0.6'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8'
implementation 'com.google.firebase:firebase-dynamic-links:16.1.5'
}
implementation project(':sdk')
@@ -78,13 +97,6 @@ dependencies {
exclude group: "com.android.support", module: "annotations"
}
annotationProcessor "com.github.bumptech.glide:compiler:${rootProject.ext.glideVersion}"
// Firebase
// - Crashlytics
// - Dynamic Links
implementation 'com.google.firebase:firebase-core:16.0.6'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8'
implementation 'com.google.firebase:firebase-dynamic-links:16.1.5'
}
gradle.projectsEvaluated {
@@ -114,21 +126,53 @@ gradle.projectsEvaluated {
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
output.processManifest.doLast {
def f = new File(manifestOutputDirectory, 'AndroidManifest.xml')
if (!f.isFile()) {
f = new File(new File(manifestOutputDirectory, output.dirName), 'AndroidManifest.xml')
}
if (f.exists()) {
def charset = 'UTF-8'
def s = f.getText(charset)
s = s.replace('</application>', "${dropboxActivity}</application>")
f.write(s, charset)
}
output.getProcessManifestProvider().get().doLast {
def outputDir = manifestOutputDirectory.get().asFile
def manifestPath = new File(outputDir, 'AndroidManifest.xml')
def charset = 'UTF-8'
def text
text = manifestPath.getText(charset)
text = text.replace('</application>', "${dropboxActivity}</application>")
manifestPath.write(text, charset)
}
}
}
}
// Run React packager
android.applicationVariants.all { variant ->
def targetName = variant.name.capitalize()
def currentRunPackagerTask = tasks.create(
name: "run${targetName}ReactPackager",
type: Exec) {
group = "react"
description = "Run the React packager."
doFirst {
println "Starting the React packager..."
def androidRoot = file("${projectDir}/../")
// Set up the call to the script
workingDir androidRoot
// Run the packager
commandLine("scripts/run-packager.sh")
}
// Set up dev mode
def devEnabled = !targetName.toLowerCase().contains("release")
// Only enable for dev builds
enabled devEnabled
}
def packageTask = variant.packageApplicationProvider.get()
packageTask.dependsOn(currentRunPackagerTask)
}
}
if (googleServicesEnabled) {

View File

@@ -80,6 +80,7 @@
# Jisti Meet SDK
-keep class org.jitsi.meet.** { *; }
-keep class org.jitsi.meet.sdk.** { *; }
# We added the following when we switched minifyEnabled on. Probably because we

View File

@@ -25,6 +25,7 @@
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:host="alpha.jitsi.net" android:scheme="https" />
<data android:host="beta.meet.jit.si" android:scheme="https" />
<data android:host="meet.jit.si" android:scheme="https" />
</intent-filter>

View File

@@ -0,0 +1,40 @@
package org.jitsi.meet;
import android.net.Uri;
import android.util.Log;
import com.crashlytics.android.Crashlytics;
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
import io.fabric.sdk.android.Fabric;
import org.jitsi.meet.sdk.JitsiMeetActivity;
/**
* Helper class to initialize Google related services and functionality.
* This functionality is compiled conditionally and called via reflection, that's why it was
* extracted here.
*
* "Libre builds" (builds with the LIBRE_BUILD flag set) will not include this file.
*/
final class GoogleServicesHelper {
public static void initialize(JitsiMeetActivity activity) {
if (BuildConfig.GOOGLE_SERVICES_ENABLED) {
Log.d(activity.getClass().getSimpleName(), "Initializing Google Services");
Fabric.with(activity, new Crashlytics());
FirebaseDynamicLinks.getInstance().getDynamicLink(activity.getIntent())
.addOnSuccessListener(activity, pendingDynamicLinkData -> {
Uri dynamicLink = null;
if (pendingDynamicLinkData != null) {
dynamicLink = pendingDynamicLinkData.getLink();
}
if (dynamicLink != null) {
activity.join(dynamicLink.toString());
}
});
}
}
}

View File

@@ -17,126 +17,134 @@
package org.jitsi.meet;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.KeyEvent;
import org.jitsi.meet.sdk.JitsiMeet;
import org.jitsi.meet.sdk.JitsiMeetActivity;
import org.jitsi.meet.sdk.JitsiMeetView;
import org.jitsi.meet.sdk.JitsiMeetViewListener;
import com.crashlytics.android.Crashlytics;
import com.facebook.react.bridge.UiThreadUtil;
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
import io.fabric.sdk.android.Fabric;
import org.jitsi.meet.sdk.JitsiMeetConferenceOptions;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
/**
* The one and only {@link Activity} that the Jitsi Meet app needs. The
* The one and only Activity that the Jitsi Meet app needs. The
* {@code Activity} is launched in {@code singleTask} mode, so it will be
* created upon application initialization and there will be a single instance
* of it. Further attempts at launching the application once it was already
* launched will result in {@link Activity#onNewIntent(Intent)} being called.
*
* This {@code Activity} extends {@link JitsiMeetActivity} to keep the React
* Native CLI working, since the latter always tries to launch an
* {@code Activity} named {@code MainActivity} when doing
* {@code react-native run-android}.
* launched will result in {@link MainActivity#onNewIntent(Intent)} being called.
*/
public class MainActivity extends JitsiMeetActivity {
/**
* The request code identifying requests for the permission to draw on top
* of other apps. The value must be 16-bit and is arbitrarily chosen here.
*/
private static final int OVERLAY_PERMISSION_REQUEST_CODE
= (int) (Math.random() * Short.MAX_VALUE);
// JitsiMeetActivity overrides
//
@Override
protected JitsiMeetView initializeView() {
JitsiMeetView view = super.initializeView();
// XXX In order to increase (1) awareness of API breakages and (2) API
// coverage, utilize JitsiMeetViewListener in the Debug configuration of
// the app.
if (BuildConfig.DEBUG && view != null) {
view.setListener(new JitsiMeetViewListener() {
private void on(String name, Map<String, Object> data) {
UiThreadUtil.assertOnUiThread();
// Log with the tag "ReactNative" in order to have the log
// visible in react-native log-android as well.
Log.d(
"ReactNative",
JitsiMeetViewListener.class.getSimpleName() + " "
+ name + " "
+ data);
}
@Override
public void onConferenceFailed(Map<String, Object> data) {
on("CONFERENCE_FAILED", data);
}
@Override
public void onConferenceJoined(Map<String, Object> data) {
on("CONFERENCE_JOINED", data);
}
@Override
public void onConferenceLeft(Map<String, Object> data) {
on("CONFERENCE_LEFT", data);
}
@Override
public void onConferenceWillJoin(Map<String, Object> data) {
on("CONFERENCE_WILL_JOIN", data);
}
@Override
public void onConferenceWillLeave(Map<String, Object> data) {
on("CONFERENCE_WILL_LEAVE", data);
}
@Override
public void onLoadConfigError(Map<String, Object> data) {
on("LOAD_CONFIG_ERROR", data);
}
});
}
return view;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// As this is the Jitsi Meet app (i.e. not the Jitsi Meet SDK), we do
// want to enable some options.
// The welcome page defaults to disabled in the SDK at the time of this
// writing but it is clearer to be explicit about what we want anyway.
setWelcomePageEnabled(true);
super.onCreate(savedInstanceState);
protected boolean extraInitialize() {
Log.d(this.getClass().getSimpleName(), "LIBRE_BUILD="+BuildConfig.LIBRE_BUILD);
// Setup Crashlytics and Firebase Dynamic Links
if (BuildConfig.GOOGLE_SERVICES_ENABLED) {
Fabric.with(this, new Crashlytics());
// Here we are using reflection since it may have been disabled at compile time.
try {
Class<?> cls = Class.forName("org.jitsi.meet.GoogleServicesHelper");
Method m = cls.getMethod("initialize", JitsiMeetActivity.class);
m.invoke(null, this);
} catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
}
FirebaseDynamicLinks.getInstance().getDynamicLink(getIntent())
.addOnSuccessListener(this, pendingDynamicLinkData -> {
Uri dynamicLink = null;
// In Debug builds React needs permission to write over other apps in
// order to display the warning and error overlays.
if (BuildConfig.DEBUG) {
if (canRequestOverlayPermission() && !Settings.canDrawOverlays(this)) {
Intent intent
= new Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
if (pendingDynamicLinkData != null) {
dynamicLink = pendingDynamicLinkData.getLink();
}
startActivityForResult(intent, OVERLAY_PERMISSION_REQUEST_CODE);
if (dynamicLink != null) {
try {
loadURL(new URL(dynamicLink.toString()));
} catch (MalformedURLException e) {
Log.d("ReactNative", "Malformed dynamic link", e);
}
}
});
return true;
}
}
return false;
}
@Override
protected void initialize() {
// Set default options
JitsiMeetConferenceOptions defaultOptions
= new JitsiMeetConferenceOptions.Builder()
.setWelcomePageEnabled(true)
.setServerURL(buildURL("https://meet.jit.si"))
.build();
JitsiMeet.setDefaultConferenceOptions(defaultOptions);
super.initialize();
}
@Override
public void onConferenceTerminated(Map<String, Object> data) {
Log.d(TAG, "Conference terminated: " + data);
}
// Activity lifecycle method overrides
//
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == OVERLAY_PERMISSION_REQUEST_CODE
&& canRequestOverlayPermission()) {
if (Settings.canDrawOverlays(this)) {
initialize();
return;
}
throw new RuntimeException("Overlay permission is required when running in Debug mode.");
}
super.onActivityResult(requestCode, resultCode, data);
}
// ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (BuildConfig.DEBUG && keyCode == KeyEvent.KEYCODE_MENU) {
JitsiMeet.showDevOptions();
return true;
}
return super.onKeyUp(keyCode, event);
}
// Helper methods
//
private @Nullable URL buildURL(String urlStr) {
try {
return new URL(urlStr);
} catch (MalformedURLException e) {
return null;
}
}
private boolean canRequestOverlayPermission() {
return
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.M;
}
}

View File

@@ -2,5 +2,6 @@
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="android:navigationBarColor">#1081B2</item>
</style>
</resources>

View File

@@ -12,7 +12,7 @@ buildscript {
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.google.gms:google-services:4.2.0'
classpath 'io.fabric.tools:gradle:1.27.0'
@@ -25,7 +25,6 @@ allprojects {
repositories {
google()
jcenter()
maven { url "$rootDir/../node_modules/jsc-android/dist" }
// React Native (JS, Obj-C sources, Android binaries) is installed from
// npm.
maven { url "$rootDir/../node_modules/react-native/android" }
@@ -42,12 +41,6 @@ allprojects {
def version = new JsonSlurper().parseText(file.text).version
details.useVersion version
}
if (details.requested.group == 'org.webkit'
&& details.requested.name == 'android-jsc') {
def file = new File("$rootDir/../node_modules/jsc-android/package.json")
def version = new JsonSlurper().parseText(file.text).version
details.useVersion "r${version.tokenize('.')[0]}"
}
}
}
}
@@ -62,45 +55,33 @@ allprojects {
publishing {
publications {}
repositories {
maven { url "file:${rootProject.projectDir}/../../jitsi-maven-repository/releases" }
maven {
url rootProject.ext.mavenRepo
if (!rootProject.ext.mavenRepo.startsWith("file")) {
credentials {
username rootProject.ext.mavenUser
password rootProject.ext.mavenPassword
}
}
}
}
}
}
// Use the number of seconds/10 since Jan 1 2019 as the version qualifier number.
// This will last for the next ~680 years.
// https://stackoverflow.com/a/38643838
def versionQualifierNumber = (int)(((new Date().getTime()/1000) - 1546297200) / 10)
afterEvaluate { project ->
if (project.name.startsWith('react-native-')) {
def npmManifest = project.file('../package.json')
def json = new JsonSlurper().parseText(npmManifest.text)
// React Native modules have an npm peer dependency on react-native,
// they do not have an npm dependency on it. Further below though we
// choose a react-native version (range) when we represent them as
// Maven artifacts. Effectively, we are forking the projects by not
// complying with the full range of their npm peer dependency and,
// consequently, we should qualify their version.
def versionQualifier = '-jitsi-1'
if ('react-native-background-timer' == project.name)
versionQualifier = '-jitsi-3' // 2.0.0 + react-native 0.57
else if ('react-native-calendar-events' == project.name)
versionQualifier = '-jitsi-2' // 1.6.4 + react-native 0.57
else if ('react-native-fast-image' == project.name)
versionQualifier = '-jitsi-2' // 5.1.1 + react-native 0.57
else if ('react-native-google-signin' == project.name)
versionQualifier = '-jitsi-2' // 1.0.2 + react-native 0.57
else if ('react-native-immersive' == project.name)
versionQualifier = '-jitsi-5' // 2.0.0 + react-native 0.57
else if ('react-native-keep-awake' == project.name)
versionQualifier = '-jitsi-4' // 4.0.0 + react-native 0.57
else if ('react-native-linear-gradient' == project.name)
versionQualifier = '-jitsi-1' // 2.4.0 + react-native 0.57
else if ('react-native-sound' == project.name)
versionQualifier = '-jitsi-2' // 0.10.9 + react-native 0.57
else if ('react-native-vector-icons' == project.name)
versionQualifier = '-jitsi-3' // 6.0.2 + react-native 0.57
else if ('react-native-webrtc' == project.name)
versionQualifier = '-jitsi-9' // 6322a9b5a38ce590cfaea4041072ea87c8dbf558 + react-native 0.57
// Release every dependency the SDK has with a -jitsi-XXX qualified version. This allows
// us to pin the dependencies and make sure they are always updated, no matter what.
project.version = "${json.version}${versionQualifier}"
project.version = "${json.version}-jitsi-${versionQualifierNumber}"
project.android {
compileSdkVersion rootProject.ext.compileSdkVersion
@@ -178,9 +159,17 @@ ext {
// of ours.
moduleGroupId = 'com.facebook.react'
// Maven repo where artifacts will be published
mavenRepo = System.env.MVN_REPO ?: ""
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 ?: "true").toBoolean()
}
// If Android SDK is not installed, accept its license so that it

2
android/fastlane/Appfile Normal file
View File

@@ -0,0 +1,2 @@
json_key_file("")
package_name("org.jitsi.meet")

34
android/fastlane/Fastfile Normal file
View File

@@ -0,0 +1,34 @@
ENV["FASTLANE_SKIP_UPDATE_CHECK"] = "1"
opt_out_usage
default_platform(:android)
platform :android do
desc "Deploy a new version to Goolge Play (Closed Beta)"
lane :deploy do
# Cleanup
gradle(task: "clean")
# Build and sign the app
gradle(
task: "assemble",
build_type: "Release",
print_command: false,
properties: {
"android.injected.signing.store.file" => ENV["JITSI_KEYSTORE"],
"android.injected.signing.store.password" => ENV["JITSI_KEYSTORE_PASSWORD"],
"android.injected.signing.key.alias" => ENV["JITSI_KEY_ALIAS"],
"android.injected.signing.key.password" => ENV["JITSI_KEY_PASSWORD"],
}
)
# Upload built artifact to the Closed Beta track
upload_to_play_store(
track: "Closed Beta",
json_key: ENV["JITSI_JSON_KEY_FILE"],
skip_upload_metadata: true,
skip_upload_images: true,
skip_upload_screenshots: true
)
end
end

View File

@@ -0,0 +1,29 @@
fastlane documentation
================
# Installation
Make sure you have the latest version of the Xcode command line tools installed:
```
xcode-select --install
```
Install _fastlane_ using
```
[sudo] gem install fastlane -NV
```
or alternatively using `brew cask install fastlane`
# Available Actions
## Android
### android deploy
```
fastlane android deploy
```
Deploy a new version to Goolge Play (Closed Beta)
----
This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).

View File

@@ -17,6 +17,5 @@
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
buildNumber=1
appVersion=19.0.0
sdkVersion=1.21.0
appVersion=19.2.0
sdkVersion=2.1.0

View File

@@ -1,6 +1,6 @@
#Wed Dec 19 12:02:47 CET 2018
#Fri Mar 08 13:36:51 CET 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip

View File

@@ -1,17 +0,0 @@
#!/bin/bash
CWD=$(dirname $0)
MVN_REPO=$(realpath $1)
JSC_VERSION="r"$(jq -r '.dependencies."jsc-android"' ${CWD}/../../package.json | cut -d . -f 1)
pushd ${CWD}/../../node_modules/jsc-android/dist/org/webkit/android-jsc/${JSC_VERSION}
mvn \
deploy:deploy-file \
-Durl=file://${MVN_REPO} \
-Dfile=android-jsc-${JSC_VERSION}.aar \
-Dpackaging=aar \
-DgeneratePom=false \
-DpomFile=android-jsc-${JSC_VERSION}.pom
popd

View File

@@ -1,19 +0,0 @@
#!/bin/bash
CWD=$(dirname $0)
MVN_REPO=$(realpath $1)
RN_VERSION=$(jq -r '.dependencies."react-native"' ${CWD}/../../package.json)
pushd ${CWD}/../../node_modules/react-native/android/com/facebook/react/react-native/${RN_VERSION}
mvn \
deploy:deploy-file \
-Durl=file://${MVN_REPO} \
-Dfile=react-native-${RN_VERSION}.aar \
-Dpackaging=aar \
-Dsources=react-native-${RN_VERSION}-sources.jar \
-Djavadoc=react-native-${RN_VERSION}-javadoc.jar \
-DgeneratePom=false \
-DpomFile=react-native-${RN_VERSION}.pom
popd

80
android/scripts/release-sdk.sh Executable file
View File

@@ -0,0 +1,80 @@
#!/bin/bash
set -e -u
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
DEFAULT_MVN_REPO="${THIS_DIR}/../../../jitsi-maven-repository/releases"
THE_MVN_REPO=${MVN_REPO:-${1:-$DEFAULT_MVN_REPO}}
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)
DO_GIT_TAG=${GIT_TAG:-0}
if [[ $THE_MVN_REPO == http* ]]; then
MVN_HTTP=1
else
MVN_REPO_PATH=$(realpath $THE_MVN_REPO)
THE_MVN_REPO="file:${MVN_REPO_PATH}"
fi
export MVN_REPO=$THE_MVN_REPO
echo "Releasing Jitsi Meet SDK ${SDK_VERSION}"
echo "Using ${MVN_REPO} as the Maven repo"
if [[ $MVN_HTTP == 1 ]]; then
# Push React Native
echo "Pushing React Native ${RN_VERSION} to the Maven repo"
pushd ${THIS_DIR}/../../node_modules/react-native/android/com/facebook/react/react-native/${RN_VERSION}
mvn \
deploy:deploy-file \
-Durl=${MVN_REPO} \
-DrepositoryId=${MVN_REPO_ID} \
-Dfile=react-native-${RN_VERSION}.aar \
-Dpackaging=aar \
-DgeneratePom=false \
-DpomFile=react-native-${RN_VERSION}.pom || true
popd
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
if [[ ! -d ${MVN_REPO}/com/facebook/react/react-native/${RN_VERSION} ]]; then
echo "Pushing React Native ${RN_VERSION} to the Maven repo"
pushd ${THIS_DIR}/../../node_modules/react-native/android/com/facebook/react/react-native/${RN_VERSION}
mvn \
deploy:deploy-file \
-Durl=${MVN_REPO} \
-Dfile=react-native-${RN_VERSION}.aar \
-Dpackaging=aar \
-DgeneratePom=false \
-DpomFile=react-native-${RN_VERSION}.pom
popd
fi
fi
# Now build and publish the Jitsi Meet SDK and its dependencies
echo "Building and publishing the Jitsi Meet SDK"
pushd ${THIS_DIR}/../
./gradlew clean assembleRelease publish
popd
if [[ $DO_GIT_TAG == 1 ]]; then
# The artifacts are now on the Maven repo, commit them
pushd ${MVN_REPO_PATH}
git add -A .
git commit -m "Jitsi Meet SDK + dependencies: ${SDK_VERSION}"
popd
# Tag the release
git tag android-sdk-${SDK_VERSION}
fi
# Done!
echo "Finished! Don't forget to push the tag and the Maven repo artifacts."

24
android/scripts/run-packager.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
# This script is executed bt Gradle to start the React packager for Debug
# targets.
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
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"
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
CMD="${THIS_DIR}/../../node_modules/react-native/scripts/launchPackager.command"
if [[ `uname` == "Darwin" ]]; then
open -g "${CMD}" || echo "Can't start packager automatically"
else
xdg-open "${CMD}" || echo "Can't start packager automatically"
fi
fi

View File

@@ -10,10 +10,25 @@ android {
}
buildTypes {
debug {}
debug {
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
}
}
sourceSets {
main {
java {
if (rootProject.ext.libreBuild) {
srcDir "src"
exclude "**/AmplitudeModule.java"
}
exclude "test/"
}
}
}
}
@@ -24,25 +39,31 @@ dependencies {
implementation "com.android.support:support-v4:${rootProject.ext.supportLibVersion}"
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation 'org.webkit:android-jsc:+'
implementation 'com.dropbox.core:dropbox-core-sdk:3.0.8'
api 'com.facebook.react:react-native:+'
implementation 'com.dropbox.core:dropbox-core-sdk:3.0.8'
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'
}
}
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-google-signin")) {
exclude group: 'com.google.android.gms'
exclude group: 'com.android.support'
}
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-webrtc')
implementation project(':react-native-webview')
testImplementation 'junit:junit:4.12'
}
@@ -107,13 +128,17 @@ android.libraryVariants.all { def variant ->
currentBundleTask.ext.generatedResFolders = files(resourcesDir).builtBy(currentBundleTask)
currentBundleTask.ext.generatedAssetsFolders = files(jsBundleDir).builtBy(currentBundleTask)
variant.registerGeneratedResFolders(currentBundleTask.generatedResFolders)
variant.mergeResources.dependsOn(currentBundleTask)
def assetsDir = variant.mergeAssets.outputDir
def mergeAssetsTask = variant.mergeAssetsProvider.get()
def mergeResourcesTask = variant.mergeResourcesProvider.get()
mergeAssetsTask.dependsOn(currentBundleTask)
mergeResourcesTask.dependsOn(currentBundleTask)
mergeAssetsTask.doLast {
def assetsDir = mergeAssetsTask.outputDir
variant.mergeAssets.doLast {
// Bundle fonts
//
copy {
@@ -139,19 +164,19 @@ android.libraryVariants.all { def variant ->
//
if (currentBundleTask.enabled) {
copy {
from(jsBundleDir)
from(jsBundleFile)
into(assetsDir)
}
}
}
variant.mergeResources.doLast {
mergeResourcesTask.doLast {
// Copy React resources
//
if (currentBundleTask.enabled) {
copy {
from(resourcesDir)
into(variant.mergeResources.outputDir)
into(mergeResourcesTask.outputDir)
}
}
}
@@ -163,7 +188,7 @@ publishing {
aarArchive(MavenPublication) {
groupId 'org.jitsi.react'
artifactId 'jitsi-meet-sdk'
version project.sdkVersion
version System.env.OVERRIDE_SDK_VERSION ?: project.sdkVersion
artifact("${project.buildDir}/outputs/aar/${project.name}-release.aar") {
extension "aar"
@@ -182,8 +207,7 @@ publishing {
def groupId = it.moduleGroup
def artifactId = it.moduleName
if (artifactId.startsWith('react-native-')
&& groupId.equals('jitsi-meet')) {
if (artifactId.startsWith('react-native-') && groupId.equals('jitsi-meet')) {
groupId = rootProject.ext.moduleGroupId
}
@@ -197,6 +221,14 @@ publishing {
}
repositories {
maven { url "file:${rootProject.projectDir}/../../jitsi-maven-repository/releases" }
maven {
url rootProject.ext.mavenRepo
if (!rootProject.ext.mavenRepo.startsWith("file")) {
credentials {
username rootProject.ext.mavenUser
password rootProject.ext.mavenPassword
}
}
}
}
}

View File

@@ -1,37 +1,49 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jitsi.meet.sdk">
<!-- XXX ACCESS_NETWORK_STATE is required by WebRTC. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- XXX ACCESS_NETWORK_STATE is required by WebRTC. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="false" />
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="false" />
<application
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true">
<activity
android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<service android:name="org.jitsi.meet.sdk.ConnectionService"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>
</application>
</manifest>
<application
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true">
<activity
android:name=".JitsiMeetActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize"
android:launchMode="singleTask"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:windowSoftInputMode="adjustResize">
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<service
android:name=".ConnectionService"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>
</application>
</manifest>

View File

@@ -0,0 +1,101 @@
/*
* 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.bridge.ReadableMap;
import com.amplitude.api.Amplitude;
import com.facebook.react.module.annotations.ReactModule;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Implements the react-native module for the Amplitude integration.
*/
@ReactModule(name = AmplitudeModule.NAME)
class AmplitudeModule
extends ReactContextBaseJavaModule {
public static final String NAME = "Amplitude";
public AmplitudeModule(ReactApplicationContext reactContext) {
super(reactContext);
}
/**
* Initializes the Amplitude SDK.
*
* @param instanceName The name of the Amplitude instance. Should
* be used only for multi-project logging.
* @param apiKey The API_KEY of the Amplitude project.
*/
@ReactMethod
public void init(String instanceName, String apiKey) {
Amplitude.getInstance(instanceName).initialize(getCurrentActivity(), apiKey);
}
/**
* Sets the user ID for an Amplitude instance.
*
* @param instanceName The name of the Amplitude instance.
* @param userId The new value for the user ID.
*/
@ReactMethod
public void setUserId(String instanceName, String userId) {
Amplitude.getInstance(instanceName).setUserId(userId);
}
/**
* Sets the user properties for an Amplitude instance.
*
* @param instanceName The name of the Amplitude instance.
* @param userProps JSON string with user properties to be set.
*/
@ReactMethod
public void setUserProperties(String instanceName, ReadableMap userProps) {
if (userProps != null) {
Amplitude.getInstance(instanceName).setUserProperties(
new JSONObject(userProps.toHashMap()));
}
}
/**
* Log an analytics event.
*
* @param instanceName The name of the Amplitude instance.
* @param eventType The event type.
* @param eventPropsString JSON string with the event properties.
*/
@ReactMethod
public void logEvent(String instanceName, String eventType, String eventPropsString) {
try {
JSONObject eventProps = new JSONObject(eventPropsString);
Amplitude.getInstance(instanceName).logEvent(eventType, eventProps);
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public String getName() {
return NAME;
}
}

View File

@@ -15,17 +15,21 @@ 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;
@ReactModule(name = AndroidSettingsModule.NAME)
class AndroidSettingsModule
extends ReactContextBaseJavaModule {
public static final String NAME = "AndroidSettings";
public AndroidSettingsModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "AndroidSettings";
return NAME;
}
@ReactMethod

View File

@@ -23,13 +23,17 @@ import android.content.pm.PackageManager;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.module.annotations.ReactModule;
import java.util.HashMap;
import java.util.Map;
@ReactModule(name = AppInfoModule.NAME)
class AppInfoModule
extends ReactContextBaseJavaModule {
public static final String NAME = "AppInfo";
public AppInfoModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@@ -60,6 +64,9 @@ class AppInfoModule
Map<String, Object> constants = new HashMap<>();
constants.put(
"buildNumber",
packageInfo == null ? "" : String.valueOf(packageInfo.versionCode));
constants.put(
"name",
applicationInfo == null
@@ -68,12 +75,13 @@ class AppInfoModule
constants.put(
"version",
packageInfo == null ? "" : packageInfo.versionName);
constants.put("LIBRE_BUILD", BuildConfig.LIBRE_BUILD);
return constants;
}
@Override
public String getName() {
return "AppInfo";
return NAME;
}
}

View File

@@ -26,7 +26,6 @@ import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.telecom.CallAudioState;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
@@ -36,6 +35,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.module.annotations.ReactModule;
import java.util.HashMap;
import java.util.HashSet;
@@ -59,10 +59,12 @@ import java.util.concurrent.Executors;
* Before a call has started and after it has ended the
* {@code AudioModeModule.DEFAULT} mode should be used.
*/
class AudioModeModule
extends ReactContextBaseJavaModule
@ReactModule(name = AudioModeModule.NAME)
class AudioModeModule extends ReactContextBaseJavaModule
implements AudioManager.OnAudioFocusChangeListener {
public static final String NAME = "AudioMode";
/**
* Constants representing the audio mode.
* - DEFAULT: Used before and after every call. It represents the default
@@ -91,42 +93,36 @@ class AudioModeModule
*/
private static final int TYPE_USB_HEADSET = 22;
/**
* The name of {@code AudioModeModule} to be used in the React Native
* bridge.
*/
private static final String MODULE_NAME = "AudioMode";
/**
* The {@code Log} tag {@code AudioModeModule} is to log messages with.
*/
static final String TAG = MODULE_NAME;
static final String TAG = NAME;
/**
* Converts any of the "DEVICE_" constants into the corresponding
* {@link CallAudioState} "ROUTE_" number.
* {@link android.telecom.CallAudioState} "ROUTE_" number.
*
* @param audioDevice one of the "DEVICE_" constants.
* @return a route number {@link CallAudioState#ROUTE_EARPIECE} if no match
* is found.
* @return a route number {@link android.telecom.CallAudioState#ROUTE_EARPIECE} if
* no match is found.
*/
@RequiresApi(api = Build.VERSION_CODES.M)
private static int audioDeviceToRouteInt(String audioDevice) {
if (audioDevice == null) {
return CallAudioState.ROUTE_EARPIECE;
return android.telecom.CallAudioState.ROUTE_EARPIECE;
}
switch (audioDevice) {
case DEVICE_BLUETOOTH:
return CallAudioState.ROUTE_BLUETOOTH;
return android.telecom.CallAudioState.ROUTE_BLUETOOTH;
case DEVICE_EARPIECE:
return CallAudioState.ROUTE_EARPIECE;
return android.telecom.CallAudioState.ROUTE_EARPIECE;
case DEVICE_HEADPHONES:
return CallAudioState.ROUTE_WIRED_HEADSET;
return android.telecom.CallAudioState.ROUTE_WIRED_HEADSET;
case DEVICE_SPEAKER:
return CallAudioState.ROUTE_SPEAKER;
return android.telecom.CallAudioState.ROUTE_SPEAKER;
default:
Log.e(TAG, "Unsupported device name: " + audioDevice);
return CallAudioState.ROUTE_EARPIECE;
return android.telecom.CallAudioState.ROUTE_EARPIECE;
}
}
@@ -134,25 +130,26 @@ class AudioModeModule
* Populates given route mask into the "DEVICE_" list.
*
* @param supportedRouteMask an integer coming from
* {@link CallAudioState#getSupportedRouteMask()}.
* {@link android.telecom.CallAudioState#getSupportedRouteMask()}.
* @return a list of device names.
*/
@RequiresApi(api = Build.VERSION_CODES.M)
private static Set<String> routesToDeviceNames(int supportedRouteMask) {
Set<String> devices = new HashSet<>();
if ((supportedRouteMask & CallAudioState.ROUTE_EARPIECE)
== CallAudioState.ROUTE_EARPIECE) {
if ((supportedRouteMask & android.telecom.CallAudioState.ROUTE_EARPIECE)
== android.telecom.CallAudioState.ROUTE_EARPIECE) {
devices.add(DEVICE_EARPIECE);
}
if ((supportedRouteMask & CallAudioState.ROUTE_BLUETOOTH)
== CallAudioState.ROUTE_BLUETOOTH) {
if ((supportedRouteMask & android.telecom.CallAudioState.ROUTE_BLUETOOTH)
== android.telecom.CallAudioState.ROUTE_BLUETOOTH) {
devices.add(DEVICE_BLUETOOTH);
}
if ((supportedRouteMask & CallAudioState.ROUTE_SPEAKER)
== CallAudioState.ROUTE_SPEAKER) {
if ((supportedRouteMask & android.telecom.CallAudioState.ROUTE_SPEAKER)
== android.telecom.CallAudioState.ROUTE_SPEAKER) {
devices.add(DEVICE_SPEAKER);
}
if ((supportedRouteMask & CallAudioState.ROUTE_WIRED_HEADSET)
== CallAudioState.ROUTE_WIRED_HEADSET) {
if ((supportedRouteMask & android.telecom.CallAudioState.ROUTE_WIRED_HEADSET)
== android.telecom.CallAudioState.ROUTE_WIRED_HEADSET) {
devices.add(DEVICE_HEADPHONES);
}
return devices;
@@ -161,7 +158,7 @@ class AudioModeModule
/**
* Whether or not the ConnectionService is used for selecting audio devices.
*/
private static boolean useConnectionService() {
static boolean useConnectionService() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
@@ -272,7 +269,7 @@ class AudioModeModule
/**
* Used on API >= 26 to store the most recently reported audio devices.
* Makes it easier to compare for a change, because the devices are stored
* as a mask in the {@link CallAudioState}. The mask is populated into
* as a mask in the {@link android.telecom.CallAudioState}. The mask is populated into
* the {@link #availableDevices} on each update.
*/
@RequiresApi(api = Build.VERSION_CODES.O)
@@ -298,9 +295,9 @@ class AudioModeModule
= (AudioManager)
reactContext.getSystemService(Context.AUDIO_SERVICE);
// Starting Oreo the ConnectionImpl from ConnectionService us used to
// Starting Oreo the ConnectionImpl from ConnectionService is used to
// detect the available devices.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
if (!useConnectionService()) {
// Setup runtime device change detection.
setupAudioRouteChangeDetection();
@@ -374,7 +371,7 @@ class AudioModeModule
*/
@Override
public String getName() {
return MODULE_NAME;
return NAME;
}
/**
@@ -433,7 +430,9 @@ class AudioModeModule
}
@RequiresApi(api = Build.VERSION_CODES.O)
void onCallAudioStateChange(final CallAudioState callAudioState) {
void onCallAudioStateChange(Object callAudioState_) {
final android.telecom.CallAudioState callAudioState
= (android.telecom.CallAudioState)callAudioState_;
runInAudioThread(new Runnable() {
@Override
public void run() {
@@ -456,6 +455,10 @@ class AudioModeModule
// Reset user selection
userSelectedDevice = null;
// If the OS changes the Audio Route or Devices we could have lost
// the selected audio device
selectedDevice = null;
if (mode != -1) {
updateAudioRoute(mode);
}

View File

@@ -170,7 +170,7 @@ public abstract class BaseReactView<ListenerT>
* @param data - The details of the event associated with/specific to the
* specified {@code name}.
*/
public abstract void onExternalAPIEvent(String name, ReadableMap data);
protected abstract void onExternalAPIEvent(String name, ReadableMap data);
protected void onExternalAPIEvent(
Map<String, Method> listenerMethods,

View File

@@ -81,6 +81,13 @@ public class ConnectionService extends android.telecom.ConnectionService {
return new ArrayList<>(connections.values());
}
/**
* @return {@code true} if running a Samsung device.
*/
static boolean isSamsungDevice() {
return android.os.Build.MANUFACTURER.toLowerCase().contains("samsung");
}
/**
* Registers a start call promise.
*
@@ -129,6 +136,14 @@ public class ConnectionService extends android.telecom.ConnectionService {
ConnectionImpl connection = connections.get(callUUID);
if (connection != null) {
if (isSamsungDevice()) {
// Required to release the audio focus correctly.
connection.setOnHold();
// Prevents from including in the native phone calls history
connection.setConnectionProperties(
Connection.PROPERTY_SELF_MANAGED
| Connection.PROPERTY_IS_EXTERNAL_CALL);
}
// Note that the connection is not removed from the list here, but
// in ConnectionImpl's state changed callback. It's a safer
// approach, because in case the app would crash on the JavaScript
@@ -189,8 +204,11 @@ public class ConnectionService extends android.telecom.ConnectionService {
connection.setConnectionProperties(Connection.PROPERTY_SELF_MANAGED);
connection.setAddress(
request.getAddress(),
TelecomManager.PRESENTATION_ALLOWED);
TelecomManager.PRESENTATION_UNKNOWN);
connection.setExtras(request.getExtras());
connection.setAudioModeIsVoip(true);
// NOTE there's a time gap between the placeCall and this callback when
// things could get out of sync, but they are put back in sync once
// the startCall Promise is resolved below. That's because on
@@ -332,8 +350,7 @@ public class ConnectionService extends android.telecom.ConnectionService {
Log.d(TAG, "onDisconnect " + getCallUUID());
WritableNativeMap data = new WritableNativeMap();
data.putString("callUUID", getCallUUID());
ReactContextUtils.emitEvent(
null,
ReactInstanceManagerHolder.emitEvent(
"org.jitsi.meet:features/connection_service#disconnect",
data);
// The JavaScript side will not go back to the native with
@@ -353,8 +370,7 @@ public class ConnectionService extends android.telecom.ConnectionService {
Log.d(TAG, "onAbort " + getCallUUID());
WritableNativeMap data = new WritableNativeMap();
data.putString("callUUID", getCallUUID());
ReactContextUtils.emitEvent(
null,
ReactInstanceManagerHolder.emitEvent(
"org.jitsi.meet:features/connection_service#abort",
data);
// The JavaScript side will not go back to the native with
@@ -428,7 +444,7 @@ public class ConnectionService extends android.telecom.ConnectionService {
@Override
public String toString() {
return String.format(
"ConnectionImpl[adress=%s, uuid=%s]@%d",
"ConnectionImpl[address=%s, uuid=%s]@%d",
getAddress(), getCallUUID(), hashCode());
}
}

View File

@@ -22,7 +22,7 @@ import android.app.Activity;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
/**
* Defines the default behavior of {@code JitsiMeetActivity} and
* Defines the default behavior of {@code JitsiMeetFragment} and
* {@code JitsiMeetView} upon invoking the back button if no
* {@code JitsiMeetView} handles the invocation. For example, a
* {@code JitsiMeetView} may (1) handle the invocation of the back button

View File

@@ -1,4 +1,4 @@
package org.jitsi.meet.sdk.dropbox;
package org.jitsi.meet.sdk;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -20,6 +20,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.dropbox.core.android.Auth;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.module.annotations.ReactModule;
import java.util.HashMap;
import java.util.Map;
@@ -27,9 +28,13 @@ import java.util.Map;
/**
* Implements the react-native module for the dropbox integration.
*/
public class Dropbox
@ReactModule(name = DropboxModule.NAME)
class DropboxModule
extends ReactContextBaseJavaModule
implements LifecycleEventListener {
public static final String NAME = "Dropbox";
private String appKey;
private String clientId;
@@ -38,7 +43,7 @@ public class Dropbox
private Promise promise;
public Dropbox(ReactApplicationContext reactContext) {
public DropboxModule(ReactApplicationContext reactContext) {
super(reactContext);
String pkg = reactContext.getApplicationContext().getPackageName();
@@ -131,7 +136,7 @@ public class Dropbox
@Override
public String getName() {
return "Dropbox";
return NAME;
}
/**

View File

@@ -22,14 +22,18 @@ 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;
/**
* Module implementing an API for sending events from JavaScript to native code.
*/
@ReactModule(name = ExternalAPIModule.NAME)
class ExternalAPIModule
extends ReactContextBaseJavaModule {
private static final String TAG = ExternalAPIModule.class.getSimpleName();
public static final String NAME = "ExternalAPI";
private static final String TAG = NAME;
/**
* Initializes a new module instance. There shall be a single instance of
@@ -49,7 +53,7 @@ class ExternalAPIModule
*/
@Override
public String getName() {
return "ExternalAPI";
return NAME;
}
/**

View File

@@ -0,0 +1,63 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import android.os.Bundle;
import com.facebook.react.ReactInstanceManager;
public class JitsiMeet {
/**
* Default {@link JitsiMeetConferenceOptions} which will be used for all conferences. When
* joining a conference these options will be merged with the ones passed to
* {@link JitsiMeetView} join().
*/
private static JitsiMeetConferenceOptions defaultConferenceOptions;
public static JitsiMeetConferenceOptions getDefaultConferenceOptions() {
return defaultConferenceOptions;
}
public static void setDefaultConferenceOptions(JitsiMeetConferenceOptions options) {
defaultConferenceOptions = options;
}
/**
* Helper to get the default conference options as a {@link Bundle}.
*
* @return a {@link Bundle} with the default conference options.
*/
static Bundle getDefaultProps() {
if (defaultConferenceOptions != null) {
return defaultConferenceOptions.asProps();
}
return new Bundle();
}
/**
* Used in development mode. It displays the React Native development menu.
*/
public static void showDevOptions() {
ReactInstanceManager reactInstanceManager
= ReactInstanceManagerHolder.getReactInstanceManager();
if (reactInstanceManager != null) {
reactInstanceManager.showDevOptionsDialog();
}
}
}

View File

@@ -1,6 +1,5 @@
/*
* Copyright @ 2019-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,326 +16,184 @@
package org.jitsi.meet.sdk;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.KeyEvent;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.modules.core.PermissionListener;
import java.net.URL;
import java.util.Map;
/**
* Base Activity for applications integrating Jitsi Meet at a higher level. It
* contains all the required wiring between the {@code JitsiMeetView} and
* the Activity lifecycle methods already implemented.
*
* In this activity we use a single {@code JitsiMeetView} instance. This
* instance gives us access to a view which displays the welcome page and the
* conference itself. All lifetime methods associated with this Activity are
* hooked to the React Native subsystem via proxy calls through the
* {@code JitsiMeetView} static methods.
* A base activity for SDK users to embed. It uses {@link JitsiMeetFragment} to do the heavy
* lifting and wires the remaining Activity lifecycle methods so it works out of the box.
*/
public class JitsiMeetActivity
extends AppCompatActivity implements JitsiMeetActivityInterface {
public class JitsiMeetActivity extends FragmentActivity
implements JitsiMeetActivityInterface, JitsiMeetViewListener {
/**
* The request code identifying requests for the permission to draw on top
* of other apps. The value must be 16-bit and is arbitrarily chosen here.
*/
private static final int OVERLAY_PERMISSION_REQUEST_CODE
= (int) (Math.random() * Short.MAX_VALUE);
protected static final String TAG = JitsiMeetActivity.class.getSimpleName();
/**
* A color scheme object to override the default color is the SDK.
*/
private Bundle colorScheme;
public static final String ACTION_JITSI_MEET_CONFERENCE = "org.jitsi.meet.CONFERENCE";
public static final String JITSI_MEET_CONFERENCE_OPTIONS = "JitsiMeetConferenceOptions";
/**
* The default base {@code URL} used to join a conference when a partial URL
* (e.g. a room name only) is specified. The value is used only while
* {@link #view} equals {@code null}.
*/
private URL defaultURL;
// Helpers for starting the activity
//
/**
* Instance of the {@link JitsiMeetView} which this activity will display.
*/
private JitsiMeetView view;
/**
* Whether Picture-in-Picture is enabled. The value is used only while
* {@link #view} equals {@code null}.
*/
private Boolean pictureInPictureEnabled;
/**
* Whether the Welcome page is enabled. The value is used only while
* {@link #view} equals {@code null}.
*/
private boolean welcomePageEnabled;
private boolean canRequestOverlayPermission() {
return
BuildConfig.DEBUG
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& getApplicationInfo().targetSdkVersion
>= Build.VERSION_CODES.M;
public static void launch(Context context, JitsiMeetConferenceOptions options) {
Intent intent = new Intent(context, JitsiMeetActivity.class);
intent.setAction(ACTION_JITSI_MEET_CONFERENCE);
intent.putExtra(JITSI_MEET_CONFERENCE_OPTIONS, options);
context.startActivity(intent);
}
/**
*
* @see JitsiMeetView#getDefaultURL()
*/
public URL getDefaultURL() {
return view == null ? defaultURL : view.getDefaultURL();
public static void launch(Context context, String url) {
JitsiMeetConferenceOptions options
= new JitsiMeetConferenceOptions.Builder().setRoom(url).build();
launch(context, options);
}
/**
* Initializes the {@link #view} of this {@code JitsiMeetActivity} with a
* new {@link JitsiMeetView} instance.
*/
private void initializeContentView() {
JitsiMeetView view = initializeView();
if (view != null) {
// XXX Allow extenders who override initializeView() to configure
// the view before the first loadURL(). Probably works around a
// problem related to ReactRootView#setAppProperties().
view.loadURL(null);
this.view = view;
setContentView(this.view);
}
}
/**
* Initializes a new {@link JitsiMeetView} instance.
*
* @return a new {@code JitsiMeetView} instance.
*/
protected JitsiMeetView initializeView() {
JitsiMeetView view = new JitsiMeetView(this);
// XXX Before calling JitsiMeetView#loadURL, make sure to call whatever
// is documented to need such an order in order to take effect:
view.setColorScheme(colorScheme);
view.setDefaultURL(defaultURL);
if (pictureInPictureEnabled != null) {
view.setPictureInPictureEnabled(
pictureInPictureEnabled.booleanValue());
}
view.setWelcomePageEnabled(welcomePageEnabled);
return view;
}
/**
*
* @see JitsiMeetView#isPictureInPictureEnabled()
*/
public boolean isPictureInPictureEnabled() {
return
view == null
? pictureInPictureEnabled
: view.isPictureInPictureEnabled();
}
/**
*
* @see JitsiMeetView#isWelcomePageEnabled()
*/
public boolean isWelcomePageEnabled() {
return view == null ? welcomePageEnabled : view.isWelcomePageEnabled();
}
/**
* Loads the given URL and displays the conference. If the specified URL is
* null, the welcome page is displayed instead.
*
* @param url The conference URL.
*/
public void loadURL(@Nullable URL url) {
view.loadURL(url);
}
@Override
protected void onActivityResult(
int requestCode,
int resultCode,
Intent data) {
if (requestCode == OVERLAY_PERMISSION_REQUEST_CODE
&& canRequestOverlayPermission()) {
if (Settings.canDrawOverlays(this)) {
initializeContentView();
}
return;
}
ReactActivityLifecycleCallbacks.onActivityResult(
this, requestCode, resultCode, data);
}
@Override
public void onBackPressed() {
ReactActivityLifecycleCallbacks.onBackPressed();
}
// Overrides
//
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// In Debug builds React needs permission to write over other apps in
// order to display the warning and error overlays.
if (canRequestOverlayPermission() && !Settings.canDrawOverlays(this)) {
Intent intent
= new Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
setContentView(R.layout.activity_jitsi_meet);
startActivityForResult(intent, OVERLAY_PERMISSION_REQUEST_CODE);
return;
if (!extraInitialize()) {
initialize();
}
initializeContentView();
}
@Override
protected void onDestroy() {
super.onDestroy();
public void finish() {
leave();
if (view != null) {
view.dispose();
view = null;
}
ReactActivityLifecycleCallbacks.onHostDestroy(this);
super.finish();
}
// ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
ReactInstanceManager reactInstanceManager;
// Helper methods
//
if (!super.onKeyUp(keyCode, event)
&& BuildConfig.DEBUG
&& (reactInstanceManager
= ReactInstanceManagerHolder.getReactInstanceManager())
!= null
&& keyCode == KeyEvent.KEYCODE_MENU) {
reactInstanceManager.showDevOptionsDialog();
return true;
protected JitsiMeetView getJitsiView() {
JitsiMeetFragment fragment
= (JitsiMeetFragment) getSupportFragmentManager().findFragmentById(R.id.jitsiFragment);
return fragment.getJitsiView();
}
public void join(@Nullable String url) {
JitsiMeetConferenceOptions options
= new JitsiMeetConferenceOptions.Builder()
.setRoom(url)
.build();
join(options);
}
public void join(JitsiMeetConferenceOptions options) {
getJitsiView().join(options);
}
public void leave() {
getJitsiView().leave();
}
private @Nullable JitsiMeetConferenceOptions getConferenceOptions(Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_VIEW.equals(action)) {
Uri uri = intent.getData();
if (uri != null) {
return new JitsiMeetConferenceOptions.Builder().setRoom(uri.toString()).build();
}
} else if (ACTION_JITSI_MEET_CONFERENCE.equals(action)) {
return intent.getParcelableExtra(JITSI_MEET_CONFERENCE_OPTIONS);
}
return null;
}
/**
* Helper function called during activity initialization. If {@code true} is returned, the
* initialization is delayed and the {@link JitsiMeetActivity#initialize()} method is not
* called. In this case, it's up to the subclass to call the initialize method when ready.
*
* This is mainly required so we do some extra initialization in the Jitsi Meet app.
*
* @return {@code true} if the initialization will be delayed, {@code false} otherwise.
*/
protected boolean extraInitialize() {
return false;
}
protected void initialize() {
// Listen for conference events.
getJitsiView().setListener(this);
// Join the room specified by the URL the app was launched with.
// Joining without the room option displays the welcome page.
join(getConferenceOptions(getIntent()));
}
// Activity lifecycle methods
//
@Override
public void onBackPressed() {
JitsiMeetActivityDelegate.onBackPressed();
}
@Override
public void onNewIntent(Intent intent) {
// XXX At least twice we received bug reports about malfunctioning
// loadURL in the Jitsi Meet SDK while the Jitsi Meet app seemed to
// functioning as expected in our testing. But that was to be expected
// because the app does not exercise loadURL. In order to increase the
// test coverage of loadURL, channel deep linking through loadURL.
Uri uri;
JitsiMeetConferenceOptions options;
if (Intent.ACTION_VIEW.equals(intent.getAction())
&& (uri = intent.getData()) != null
&& JitsiMeetView.loadURLStringInViews(uri.toString())) {
if ((options = getConferenceOptions(intent)) != null) {
join(options);
return;
}
ReactActivityLifecycleCallbacks.onNewIntent(intent);
}
// https://developer.android.com/reference/android/support/v4/app/ActivityCompat.OnRequestPermissionsResultCallback
@Override
public void onRequestPermissionsResult(
final int requestCode,
final String[] permissions,
final int[] grantResults) {
ReactActivityLifecycleCallbacks.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
protected void onResume() {
super.onResume();
ReactActivityLifecycleCallbacks.onHostResume(this);
}
@Override
public void onStop() {
super.onStop();
ReactActivityLifecycleCallbacks.onHostPause(this);
JitsiMeetActivityDelegate.onNewIntent(intent);
}
@Override
protected void onUserLeaveHint() {
if (view != null) {
view.enterPictureInPicture();
}
getJitsiView().enterPictureInPicture();
}
/**
* Implementation of the {@code PermissionAwareActivity} interface.
*/
// JitsiMeetActivityInterface
//
@Override
public void requestPermissions(String[] permissions, int requestCode, PermissionListener listener) {
ReactActivityLifecycleCallbacks.requestPermissions(this, permissions, requestCode, listener);
JitsiMeetActivityDelegate.requestPermissions(this, permissions, requestCode, listener);
}
/**
* @see JitsiMeetView#setColorScheme(Bundle)
*/
public void setColorScheme(Bundle colorScheme) {
if (view == null) {
this.colorScheme = colorScheme;
} else {
view.setColorScheme(colorScheme);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
/**
*
* @see JitsiMeetView#setDefaultURL(URL)
*/
public void setDefaultURL(URL defaultURL) {
if (view == null) {
this.defaultURL = defaultURL;
} else {
view.setDefaultURL(defaultURL);
}
// JitsiMeetViewListener
//
@Override
public void onConferenceJoined(Map<String, Object> data) {
Log.d(TAG, "Conference joined: " + data);
}
/**
*
* @see JitsiMeetView#setPictureInPictureEnabled(boolean)
*/
public void setPictureInPictureEnabled(boolean pictureInPictureEnabled) {
if (view == null) {
this.pictureInPictureEnabled
= Boolean.valueOf(pictureInPictureEnabled);
} else {
view.setPictureInPictureEnabled(pictureInPictureEnabled);
}
@Override
public void onConferenceTerminated(Map<String, Object> data) {
Log.d(TAG, "Conference terminated: " + data);
finish();
}
/**
*
* @see JitsiMeetView#setWelcomePageEnabled(boolean)
*/
public void setWelcomePageEnabled(boolean welcomePageEnabled) {
if (view == null) {
this.welcomePageEnabled = welcomePageEnabled;
} else {
view.setWelcomePageEnabled(welcomePageEnabled);
}
@Override
public void onConferenceWillJoin(Map<String, Object> data) {
Log.d(TAG, "Conference will join: " + data);
}
}

View File

@@ -32,7 +32,22 @@ import com.facebook.react.modules.core.PermissionListener;
* {@link Activity} lifecycle methods in order for the React side to be aware of
* it.
*/
public class ReactActivityLifecycleCallbacks {
public class JitsiMeetActivityDelegate {
/**
* Needed for making sure this class working with the "PermissionsAndroid"
* React Native module.
*/
private static PermissionListener permissionListener;
private static Callback permissionsCallback;
/**
* Tells whether or not the permissions request is currently in progress.
*
* @return {@code true} if the permssions are being requested or {@code false} otherwise.
*/
static boolean arePermissionsBeingRequested() {
return permissionListener != null;
}
/**
* {@link Activity} lifecycle method which should be called from
@@ -57,13 +72,6 @@ public class ReactActivityLifecycleCallbacks {
}
}
/**
* Needed for making sure this class working with the "PermissionsAndroid"
* React Native module.
*/
private static PermissionListener permissionListener;
private static Callback permissionsCallback;
/**
* {@link Activity} lifecycle method which should be called from
* {@link Activity#onBackPressed} so we can do the required internal

View File

@@ -6,7 +6,7 @@ import com.facebook.react.modules.core.PermissionAwareActivity;
/**
* This interface serves as the umbrella interface that applications not using
* {@code JitsiMeetActivity} must implement in order to ensure full
* {@code JitsiMeetFragment} must implement in order to ensure full
* functionality.
*/
public interface JitsiMeetActivityInterface

View File

@@ -0,0 +1,318 @@
/*
* 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.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import java.net.URL;
/**
* This class represents the options when joining a Jitsi Meet conference. The user can create an
* instance by using {@link JitsiMeetConferenceOptions.Builder} and setting the desired options
* there.
*
* The resulting {@link JitsiMeetConferenceOptions} object is immutable and represents how the
* conference will be joined.
*/
public class JitsiMeetConferenceOptions implements Parcelable {
/**
* Server where the conference should take place.
*/
private URL serverURL;
/**
* Room name.
*/
private String room;
/**
* Conference subject.
*/
private String subject;
/**
* JWT token used for authentication.
*/
private String token;
/**
* Color scheme override, see: https://github.com/jitsi/jitsi-meet/blob/dbedee5e22e5dcf9c92db96ef5bb3c9982fc526d/react/features/base/color-scheme/defaultScheme.js
*/
private Bundle colorScheme;
/**
* Set to {@code true} to join the conference with audio / video muted or to start in audio
* only mode respectively.
*/
private Boolean audioMuted;
private Boolean audioOnly;
private Boolean videoMuted;
/**
* Set to {@code true} to enable the welcome page. Typically SDK users won't need this enabled
* since the host application decides which meeting to join.
*/
private Boolean welcomePageEnabled;
/**
* Class used to build the immutable {@link JitsiMeetConferenceOptions} object.
*/
public static class Builder {
private URL serverURL;
private String room;
private String subject;
private String token;
private Bundle colorScheme;
private Boolean audioMuted;
private Boolean audioOnly;
private Boolean videoMuted;
private Boolean welcomePageEnabled;
public Builder() {
}
/**\
* Sets the server URL.
* @param url - {@link URL} of the server where the conference should take place.
* @return - The {@link Builder} object itself so the method calls can be chained.
*/
public Builder setServerURL(URL url) {
this.serverURL = url;
return this;
}
/**
* Sets the room where the conference will take place.
* @param room - Name of the room.
* @return - The {@link Builder} object itself so the method calls can be chained.
*/
public Builder setRoom(String room) {
this.room = room;
return this;
}
/**
* Sets the conference subject.
* @param subject - Subject for the conference.
* @return - The {@link Builder} object itself so the method calls can be chained.
*/
public Builder setSubject(String subject) {
this.subject = subject;
return this;
}
/**
* Sets the JWT token to be used for authentication when joining a conference.
* @param token - The JWT token to be used for authentication.
* @return - The {@link Builder} object itself so the method calls can be chained.
*/
public Builder setToken(String token) {
this.token = token;
return this;
}
/**
* Sets the color scheme override so the app is themed. See:
* https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/color-scheme/defaultScheme.js
* for the structure.
* @param colorScheme - A color scheme to be applied to the app.
* @return - The {@link Builder} object itself so the method calls can be chained.
*/
public Builder setColorScheme(Bundle colorScheme) {
this.colorScheme = colorScheme;
return this;
}
/**
* Indicates the conference will be joined with the microphone muted.
* @param muted - Muted indication.
* @return - The {@link Builder} object itself so the method calls can be chained.
*/
public Builder setAudioMuted(boolean muted) {
this.audioMuted = muted;
return this;
}
/**
* Indicates the conference will be joined in audio-only mode. In this mode no video is
* sent or received.
* @param audioOnly - Audio-mode indicator.
* @return - The {@link Builder} object itself so the method calls can be chained.
*/
public Builder setAudioOnly(boolean audioOnly) {
this.audioOnly = audioOnly;
return this;
}
/**
* Indicates the conference will be joined with the camera muted.
* @param videoMuted - Muted indication.
* @return - The {@link Builder} object itself so the method calls can be chained.
*/
public Builder setVideoMuted(boolean videoMuted) {
this.videoMuted = videoMuted;
return this;
}
/**
* Sets the welcome page enabled / disabled. The welcome page lists recent meetings and
* calendar appointments and it's meant to be used by standalone applications. Defaults to
* false.
* @param enabled - Whether the welcome page should be enabled or not.
* @return - The {@link Builder} object itself so the method calls can be chained.
*/
public Builder setWelcomePageEnabled(boolean enabled) {
this.welcomePageEnabled = enabled;
return this;
}
/**
* Builds the immutable {@link JitsiMeetConferenceOptions} object with the configuration
* that this {@link Builder} instance specified.
* @return - The built {@link JitsiMeetConferenceOptions} object.
*/
public JitsiMeetConferenceOptions build() {
JitsiMeetConferenceOptions options = new JitsiMeetConferenceOptions();
options.serverURL = this.serverURL;
options.room = this.room;
options.subject = this.subject;
options.token = this.token;
options.colorScheme = this.colorScheme;
options.audioMuted = this.audioMuted;
options.audioOnly = this.audioOnly;
options.videoMuted = this.videoMuted;
options.welcomePageEnabled = this.welcomePageEnabled;
return options;
}
}
private JitsiMeetConferenceOptions() {
}
private JitsiMeetConferenceOptions(Parcel in) {
room = in.readString();
subject = in.readString();
token = in.readString();
colorScheme = in.readBundle();
byte tmpAudioMuted = in.readByte();
audioMuted = tmpAudioMuted == 0 ? null : tmpAudioMuted == 1;
byte tmpAudioOnly = in.readByte();
audioOnly = tmpAudioOnly == 0 ? null : tmpAudioOnly == 1;
byte tmpVideoMuted = in.readByte();
videoMuted = tmpVideoMuted == 0 ? null : tmpVideoMuted == 1;
byte tmpWelcomePageEnabled = in.readByte();
welcomePageEnabled = tmpWelcomePageEnabled == 0 ? null : tmpWelcomePageEnabled == 1;
}
Bundle asProps() {
Bundle props = new Bundle();
if (colorScheme != null) {
props.putBundle("colorScheme", colorScheme);
}
if (welcomePageEnabled != null) {
props.putBoolean("welcomePageEnabled", welcomePageEnabled);
}
// TODO: get rid of this.
props.putBoolean("pictureInPictureEnabled", true);
Bundle config = new Bundle();
if (audioMuted != null) {
config.putBoolean("startWithAudioMuted", audioMuted);
}
if (audioOnly != null) {
config.putBoolean("startAudioOnly", audioOnly);
}
if (videoMuted != null) {
config.putBoolean("startWithVideoMuted", videoMuted);
}
if (subject != null) {
config.putString("subject", subject);
}
Bundle urlProps = new Bundle();
// The room is fully qualified
if (room != null && room.contains("://")) {
urlProps.putString("url", room);
} else {
if (serverURL != null) {
urlProps.putString("serverURL", serverURL.toString());
}
if (room != null) {
urlProps.putString("room", room);
}
}
if (token != null) {
urlProps.putString("jwt", token);
}
urlProps.putBundle("config", config);
props.putBundle("url", urlProps);
return props;
}
// Parcelable interface
//
public static final Creator<JitsiMeetConferenceOptions> CREATOR = new Creator<JitsiMeetConferenceOptions>() {
@Override
public JitsiMeetConferenceOptions createFromParcel(Parcel in) {
return new JitsiMeetConferenceOptions(in);
}
@Override
public JitsiMeetConferenceOptions[] newArray(int size) {
return new JitsiMeetConferenceOptions[size];
}
};
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(room);
dest.writeString(subject);
dest.writeString(token);
dest.writeBundle(colorScheme);
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));
dest.writeByte((byte) (welcomePageEnabled == null ? 0 : welcomePageEnabled ? 1 : 2));
}
@Override
public int describeContents() {
return 0;
}
}

View File

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

View File

@@ -1,5 +1,6 @@
/*
* Copyright @ 2017-present Atlassian Pty Ltd
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,11 +26,10 @@ import android.util.Log;
import com.facebook.react.bridge.ReadableMap;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Map;
public class JitsiMeetView
extends BaseReactView<JitsiMeetViewListener> {
public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener> {
/**
* The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
@@ -44,50 +44,6 @@ public class JitsiMeetView
*/
private static final String TAG = JitsiMeetView.class.getSimpleName();
/**
* Loads a specific URL {@code String} in all existing
* {@code JitsiMeetView}s.
*
* @param urlString he URL {@code String} to load in all existing
* {@code JitsiMeetView}s.
* @return If the specified {@code urlString} was submitted for loading in
* at least one {@code JitsiMeetView}, then {@code true}; otherwise,
* {@code false}.
*/
public static boolean loadURLStringInViews(String urlString) {
boolean loaded = false;
synchronized (views) {
for (BaseReactView view : views) {
if (view instanceof JitsiMeetView) {
((JitsiMeetView)view).loadURLString(urlString);
loaded = true;
}
}
}
return loaded;
}
/**
* A color scheme object to override the default color is the SDK.
*/
private Bundle colorScheme;
/**
* The default base {@code URL} used to join a conference when a partial URL
* (e.g. a room name only) is specified to {@link #loadURLString(String)} or
* {@link #loadURLObject(Bundle)}.
*/
private URL defaultURL;
/**
* Whether Picture-in-Picture is enabled. If {@code null}, defaults to
* {@code true} iff the Android platform supports Picture-in-Picture
* natively.
*/
private Boolean pictureInPictureEnabled;
/**
* The URL of the current conference.
*/
@@ -96,9 +52,51 @@ public class JitsiMeetView
private volatile String url;
/**
* Whether the Welcome page is enabled.
* Helper method to recursively merge 2 {@link Bundle} objects representing React Native props.
*
* @param a - The first {@link Bundle}.
* @param b - The second {@link Bundle}.
* @return The merged {@link Bundle} object.
*/
private boolean welcomePageEnabled;
private static Bundle mergeProps(@Nullable Bundle a, @Nullable Bundle b) {
Bundle result = new Bundle();
if (a == null) {
if (b != null) {
result.putAll(b);
}
return result;
}
if (b == null) {
result.putAll(a);
return result;
}
// Start by putting all of a in the result.
result.putAll(a);
// Iterate over each key in b and override if appropriate.
for (String key : b.keySet()) {
Object bValue = b.get(key);
Object aValue = a.get(key);
String valueType = bValue.getClass().getSimpleName();
if (valueType.contentEquals("Boolean")) {
result.putBoolean(key, (Boolean)bValue);
} else if (valueType.contentEquals("String")) {
result.putString(key, (String)bValue);
} else if (valueType.contentEquals("Bundle")) {
result.putBundle(key, mergeProps((Bundle)aValue, (Bundle)bValue));
} else {
throw new RuntimeException("Unsupported type: " + valueType);
}
}
return result;
}
public JitsiMeetView(@NonNull Context context) {
super(context);
@@ -120,165 +118,60 @@ public class JitsiMeetView
* page.
*/
public void enterPictureInPicture() {
if (isPictureInPictureEnabled() && getURL() != null) {
PictureInPictureModule pipModule
= ReactInstanceManagerHolder.getNativeModule(
PictureInPictureModule pipModule
= ReactInstanceManagerHolder.getNativeModule(
PictureInPictureModule.class);
if (pipModule != null) {
try {
pipModule.enterPictureInPicture();
} catch (RuntimeException re) {
Log.e(TAG, "onUserLeaveHint: failed to enter PiP mode", re);
}
if (pipModule != null
&& PictureInPictureModule.isPictureInPictureSupported()
&& !JitsiMeetActivityDelegate.arePermissionsBeingRequested()
&& this.url != null) {
try {
pipModule.enterPictureInPicture();
} catch (RuntimeException re) {
Log.e(TAG, "failed to enter PiP mode", re);
}
}
}
/**
* Gets the color scheme used in the SDK.
*
* @return The color scheme map.
* Joins the conference specified by the given {@link JitsiMeetConferenceOptions}. If there is
* already an active conference, it will be left and the new one will be joined.
* @param options - Description of what conference must be joined and what options will be used
* when doing so.
*/
public Bundle getColorScheme() {
return colorScheme;
public void join(@Nullable JitsiMeetConferenceOptions options) {
setProps(options != null ? options.asProps() : new Bundle());
}
/**
* Gets the default base {@code URL} used to join a conference when a
* partial URL (e.g. a room name only) is specified to
* {@link #loadURLString(String)} or {@link #loadURLObject(Bundle)}. If not
* set or if set to {@code null}, the default built in JavaScript is used:
* https://meet.jit.si
*
* @return The default base {@code URL} or {@code null}.
* Leaves the currently active conference.
*/
public URL getDefaultURL() {
return defaultURL;
public void leave() {
setProps(new Bundle());
}
/**
* Gets the URL of the current conference.
*
* XXX The method is meant for internal purposes only at the time of this
* writing because there is no equivalent API on iOS.
*
* @return the URL {@code String} of the current conference if any;
* otherwise, {@code null}.
* Helper method to set the React Native props.
* @param newProps - New props to be set on the React Native view.
*/
String getURL() {
return url;
}
private void setProps(@NonNull Bundle newProps) {
// Merge the default options with the newly provided ones.
Bundle props = mergeProps(JitsiMeet.getDefaultProps(), newProps);
/**
* Gets whether Picture-in-Picture is enabled. Picture-in-Picture is
* natively supported on Android API >= 26 (Oreo), so it should not be
* enabled on older platform versions.
*
* @return If Picture-in-Picture is enabled, {@code true}; {@code false},
* otherwise.
*/
public boolean isPictureInPictureEnabled() {
return
PictureInPictureModule.isPictureInPictureSupported()
&& (pictureInPictureEnabled == null
|| pictureInPictureEnabled);
}
/**
* Gets whether the Welcome page is enabled. If {@code true}, the Welcome
* page is rendered when this {@code JitsiMeetView} is not at a URL
* identifying a Jitsi Meet conference/room.
*
* @return {@code true} if the Welcome page is enabled; otherwise,
* {@code false}.
*/
public boolean isWelcomePageEnabled() {
return welcomePageEnabled;
}
/**
* Loads a specific {@link URL} which may identify a conference to join. If
* the specified {@code URL} is {@code null} and the Welcome page is
* enabled, the Welcome page is displayed instead.
*
* @param url The {@code URL} to load which may identify a conference to
* join.
*/
public void loadURL(@Nullable URL url) {
loadURLString(url == null ? null : url.toString());
}
/**
* Loads a specific URL which may identify a conference to join. The URL is
* specified in the form of a {@link Bundle} of properties which (1)
* internally are sufficient to construct a URL {@code String} while (2)
* abstracting the specifics of constructing the URL away from API
* clients/consumers. If the specified URL is {@code null} and the Welcome
* page is enabled, the Welcome page is displayed instead.
*
* @param urlObject The URL to load which may identify a conference to join.
*/
public void loadURLObject(@Nullable Bundle urlObject) {
Bundle props = new Bundle();
// color scheme
if (colorScheme != null) {
props.putBundle("colorScheme", colorScheme);
}
// defaultURL
if (defaultURL != null) {
props.putString("defaultURL", defaultURL.toString());
}
// pictureInPictureEnabled
props.putBoolean(
"pictureInPictureEnabled",
isPictureInPictureEnabled());
// url
if (urlObject != null) {
props.putBundle("url", urlObject);
}
// welcomePageEnabled
props.putBoolean("welcomePageEnabled", welcomePageEnabled);
// XXX The method loadURLObject: is supposed to be imperative i.e.
// XXX The setProps() method is supposed to be imperative i.e.
// a second invocation with one and the same URL is expected to join
// the respective conference again if the first invocation was followed
// by leaving the conference. However, React and, respectively,
// appProperties/initialProperties are declarative expressions i.e. one
// and the same URL will not trigger an automatic re-render in the
// JavaScript source code. The workaround implemented bellow introduces
// imperativeness in React Component props by defining a unique value
// per loadURLObject: invocation.
// "imperativeness" in React Component props by defining a unique value
// per setProps() invocation.
props.putLong("timestamp", System.currentTimeMillis());
createReactRootView("App", props);
}
/**
* Loads a specific URL {@link String} which may identify a conference to
* join. If the specified URL {@code String} is {@code null} and the Welcome
* page is enabled, the Welcome page is displayed instead.
*
* @param urlString The URL {@code String} to load which may identify a
* conference to join.
*/
public void loadURLString(@Nullable String urlString) {
Bundle urlObject;
if (urlString == null) {
urlObject = null;
} else {
urlObject = new Bundle();
urlObject.putString("url", urlString);
}
loadURLObject(urlObject);
}
/**
* The internal processing for the URL of the current conference set on the
* associated {@link JitsiMeetView}.
@@ -288,18 +181,16 @@ public class JitsiMeetView
* by/associated with the specified {@code eventName}.
*/
private void maybeSetViewURL(String eventName, ReadableMap eventData) {
String url = eventData.getString("url");
switch(eventName) {
case "CONFERENCE_WILL_JOIN":
setURL(eventData.getString("url"));
this.url = url;
break;
case "CONFERENCE_FAILED":
case "CONFERENCE_WILL_LEAVE":
case "LOAD_CONFIG_ERROR":
String url = eventData.getString("url");
if (url != null && url.equals(getURL())) {
setURL(null);
case "CONFERENCE_TERMINATED":
if (url != null && url.equals(this.url)) {
this.url = null;
}
break;
}
@@ -313,7 +204,7 @@ public class JitsiMeetView
* by/associated with the specified {@code name}.
*/
@Override
public void onExternalAPIEvent(String name, ReadableMap data) {
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
@@ -323,62 +214,4 @@ public class JitsiMeetView
onExternalAPIEvent(LISTENER_METHODS, name, data);
}
/**
* Sets the color scheme to override the default colors of the SDK.
*
* @param colorScheme The color scheme map.
*/
public void setColorScheme(Bundle colorScheme) {
this.colorScheme = colorScheme;
}
/**
* Sets the default base {@code URL} used to join a conference when a
* partial URL (e.g. a room name only) is specified to
* {@link #loadURLString(String)} or {@link #loadURLObject(Bundle)}. Must be
* called before {@link #loadURL(URL)} for it to take effect.
*
* @param defaultURL The {@code URL} to be set as the default base URL.
* @see #getDefaultURL()
*/
public void setDefaultURL(URL defaultURL) {
this.defaultURL = defaultURL;
}
/**
* Sets whether Picture-in-Picture is enabled. Because Picture-in-Picture is
* natively supported only since certain platform versions, specifying
* {@code true} will have no effect on unsupported platform versions.
*
* @param pictureInPictureEnabled To enable Picture-in-Picture,
* {@code true}; otherwise, {@code false}.
*/
public void setPictureInPictureEnabled(boolean pictureInPictureEnabled) {
this.pictureInPictureEnabled = pictureInPictureEnabled;
}
/**
* Sets the URL of the current conference.
*
* XXX The method is meant for internal purposes only. It does not
* {@code loadURL}, it merely remembers the specified URL.
*
* @param url the URL {@code String} which to be set as the URL of the
* current conference.
*/
void setURL(String url) {
this.url = url;
}
/**
* Sets whether the Welcome page is enabled. Must be called before
* {@link #loadURL(URL)} for it to take effect.
*
* @param welcomePageEnabled {@code true} to enable the Welcome page;
* otherwise, {@code false}.
*/
public void setWelcomePageEnabled(boolean welcomePageEnabled) {
this.welcomePageEnabled = welcomePageEnabled;
}
}

View File

@@ -1,51 +0,0 @@
/*
* Copyright @ 2017-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import java.util.Map;
/**
* Implements {@link JitsiMeetViewListener} so apps don't have to add stubs for
* all methods in the interface if they are only interested in some.
*/
public abstract class JitsiMeetViewAdapter
implements JitsiMeetViewListener {
@Override
public void onConferenceFailed(Map<String, Object> data) {
}
@Override
public void onConferenceJoined(Map<String, Object> data) {
}
@Override
public void onConferenceLeft(Map<String, Object> data) {
}
@Override
public void onConferenceWillJoin(Map<String, Object> data) {
}
@Override
public void onConferenceWillLeave(Map<String, Object> data) {
}
@Override
public void onLoadConfigError(Map<String, Object> data) {
}
}

View File

@@ -22,15 +22,6 @@ import java.util.Map;
* Interface for listening to events coming from Jitsi Meet.
*/
public interface JitsiMeetViewListener {
/**
* Called when joining a conference fails or an ongoing conference is
* interrupted due to a failure.
*
* @param data Map with an "error" key describing the problem, and a "url"
* key with the conference URL.
*/
void onConferenceFailed(Map<String, Object> data);
/**
* Called when a conference was joined.
*
@@ -39,11 +30,16 @@ public interface JitsiMeetViewListener {
void onConferenceJoined(Map<String, Object> data);
/**
* Called when the conference was left, typically after hanging up.
* Called when the active conference ends, be it because of user choice or
* because of a failure.
*
* @param data Map with a "url" key with the conference URL.
* @param data Map with an "error" key with the error and a "url" key with
* the conference URL. If the conference finished gracefully no `error`
* key will be present. The possible values for "error" are described here:
* https://github.com/jitsi/lib-jitsi-meet/blob/master/JitsiConnectionErrors.js
* https://github.com/jitsi/lib-jitsi-meet/blob/master/JitsiConferenceErrors.js
*/
void onConferenceLeft(Map<String, Object> data);
void onConferenceTerminated(Map<String, Object> data);
/**
* Called before the conference is joined.
@@ -51,21 +47,4 @@ public interface JitsiMeetViewListener {
* @param data Map with a "url" key with the conference URL.
*/
void onConferenceWillJoin(Map<String, Object> data);
/**
* Called before the conference is left.
*
* @param data Map with a "url" key with the conference URL.
*/
void onConferenceWillLeave(Map<String, Object> data);
/**
* Called when loading the main configuration file from the Jitsi Meet
* deployment fails.
*
* @param data Map with an "error" key with the error and a "url" key with
* the conference URL which necessitated the loading of the configuration
* file.
*/
void onLoadConfigError(Map<String, Object> data);
}

View File

@@ -27,11 +27,15 @@ 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;
@ReactModule(name = PictureInPictureModule.NAME)
class PictureInPictureModule
extends ReactContextBaseJavaModule {
private final static String TAG = "PictureInPicture";
public static final String NAME = "PictureInPicture";
private static final String TAG = NAME;
static boolean isPictureInPictureSupported() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
@@ -101,6 +105,6 @@ class PictureInPictureModule
@Override
public String getName() {
return TAG;
return NAME;
}
}

View File

@@ -24,6 +24,7 @@ import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.module.annotations.ReactModule;
/**
* Module implementing a simple API to enable a proximity sensor-controlled
@@ -31,14 +32,11 @@ import com.facebook.react.bridge.UiThreadUtil;
* object it will dim the screen and disable touch controls. The functionality
* is used with the conference audio-only mode.
*/
@ReactModule(name = ProximityModule.NAME)
class ProximityModule
extends ReactContextBaseJavaModule {
/**
* The name of {@code ProximityModule} to be used in the React Native
* bridge.
*/
private static final String MODULE_NAME = "Proximity";
public static final String NAME = "Proximity";
/**
* This type of wake lock (the one activated by the proximity sensor) has
@@ -74,7 +72,7 @@ class ProximityModule
wakeLock
= powerManager.newWakeLock(
PROXIMITY_SCREEN_OFF_WAKE_LOCK,
MODULE_NAME);
"jitsi:"+NAME);
} catch (Throwable ignored) {
wakeLock = null;
}
@@ -89,7 +87,7 @@ class ProximityModule
*/
@Override
public String getName() {
return MODULE_NAME;
return NAME;
}
/**

View File

@@ -18,6 +18,7 @@ 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;
/**
* The react-native side of Jitsi Meet's {@link ConnectionService}. Exposes
@@ -26,10 +27,13 @@ import com.facebook.react.bridge.ReadableMap;
* @author Pawel Domas
*/
@RequiresApi(api = Build.VERSION_CODES.O)
@ReactModule(name = RNConnectionService.NAME)
class RNConnectionService
extends ReactContextBaseJavaModule {
private final static String TAG = ConnectionService.TAG;
public static final String NAME = "ConnectionService";
private static final String TAG = ConnectionService.TAG;
/**
* Sets the audio route on all existing {@link android.telecom.Connection}s
@@ -146,7 +150,7 @@ class RNConnectionService
@Override
public String getName() {
return "ConnectionService";
return NAME;
}
/**

View File

@@ -1,43 +0,0 @@
/*
* Copyright @ 2017-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet.sdk;
import android.support.annotation.Nullable;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.core.DeviceEventManagerModule;
public class ReactContextUtils {
public static boolean emitEvent(
ReactContext reactContext,
String eventName,
@Nullable Object data) {
if (reactContext == null) {
// XXX If no ReactContext is specified, emit through the
// ReactContext of ReactInstanceManager. ReactInstanceManager
// cooperates with ReactContextUtils i.e. ReactInstanceManager will
// not invoke ReactContextUtils without a ReactContext.
return ReactInstanceManagerHolder.emitEvent(eventName, data);
}
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, data);
return true;
}
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright @ 2017-present Atlassian Pty Ltd
* Copyright @ 2019-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,11 +21,15 @@ import android.app.Application;
import android.support.annotation.Nullable;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
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.modules.core.DeviceEventManagerModule;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -48,19 +53,26 @@ class ReactInstanceManagerHolder {
new AndroidSettingsModule(reactContext),
new AppInfoModule(reactContext),
new AudioModeModule(reactContext),
new DropboxModule(reactContext),
new ExternalAPIModule(reactContext),
new LocaleDetector(reactContext),
new PictureInPictureModule(reactContext),
new ProximityModule(reactContext),
new WiFiStatsModule(reactContext),
new org.jitsi.meet.sdk.dropbox.Dropbox(reactContext),
new org.jitsi.meet.sdk.net.NAT64AddrInfoModule(reactContext)));
if (android.os.Build.VERSION.SDK_INT
>= android.os.Build.VERSION_CODES.O) {
if (AudioModeModule.useConnectionService()) {
nativeModules.add(new RNConnectionService(reactContext));
}
try {
Class<?> amplitudeModuleClass = Class.forName("org.jitsi.meet.sdk.AmplitudeModule");
Constructor constructor = amplitudeModuleClass.getConstructor(ReactApplicationContext.class);
nativeModules.add((NativeModule)constructor.newInstance(reactContext));
} catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
}
return nativeModules;
}
@@ -70,7 +82,7 @@ class ReactInstanceManagerHolder {
* @param eventName {@code String} containing the event name.
* @param data {@code Object} optional ancillary data for the event.
*/
static boolean emitEvent(
static void emitEvent(
String eventName,
@Nullable Object data) {
ReactInstanceManager reactInstanceManager
@@ -80,15 +92,12 @@ class ReactInstanceManagerHolder {
ReactContext reactContext
= reactInstanceManager.getCurrentReactContext();
return
reactContext != null
&& ReactContextUtils.emitEvent(
reactContext,
eventName,
data);
if (reactContext != null) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, data);
}
}
return false;
}
/**
@@ -128,33 +137,50 @@ class ReactInstanceManagerHolder {
return;
}
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.ocetnik.timer.BackgroundTimerPackage(),
new com.oney.WebRTCModule.WebRTCModulePackage(),
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
new com.reactnativecommunity.webview.RNCWebViewPackage(),
new com.rnimmersive.RNImmersivePackage(),
new com.zmxv.RNSound.RNSoundPackage(),
new ReactPackageAdapter() {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return ReactInstanceManagerHolder.createNativeModules(reactContext);
}
}));
try {
Class<?> googlePackageClass = Class.forName("co.apptailor.googlesignin.RNGoogleSigninPackage");
Constructor constructor = googlePackageClass.getConstructor();
packages.add((ReactPackage)constructor.newInstance());
} catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
}
reactInstanceManager
= ReactInstanceManager.builder()
.setApplication(application)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index.android")
.addPackage(new co.apptailor.googlesignin.RNGoogleSigninPackage())
.addPackage(new com.BV.LinearGradient.LinearGradientPackage())
.addPackage(new com.calendarevents.CalendarEventsPackage())
.addPackage(new com.corbt.keepawake.KCKeepAwakePackage())
.addPackage(new com.dylanvann.fastimage.FastImageViewPackage())
.addPackage(new com.facebook.react.shell.MainReactPackage())
.addPackage(new com.oblador.vectoricons.VectorIconsPackage())
.addPackage(new com.ocetnik.timer.BackgroundTimerPackage())
.addPackage(new com.oney.WebRTCModule.WebRTCModulePackage())
.addPackage(new com.rnimmersive.RNImmersivePackage())
.addPackage(new com.zmxv.RNSound.RNSoundPackage())
.addPackage(new ReactPackageAdapter() {
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
return
ReactInstanceManagerHolder.createNativeModules(
reactContext);
}
})
.addPackages(packages)
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
// Disable delta updates on Android, they have caused trouble.
DevInternalSettings devSettings
= (DevInternalSettings)reactInstanceManager.getDevSupportManager().getDevSettings();
if (devSettings != null) {
devSettings.setBundleDeltasEnabled(false);
}
}
}

View File

@@ -25,6 +25,7 @@ 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.json.JSONArray;
import org.json.JSONObject;
@@ -43,19 +44,16 @@ import java.util.concurrent.Executors;
* Gathers rssi, signal in percentage, timestamp and the addresses of the wifi
* device.
*/
@ReactModule(name = WiFiStatsModule.NAME)
class WiFiStatsModule
extends ReactContextBaseJavaModule {
/**
* The name of {@code WiFiStatsModule} to be used in the React Native
* bridge.
*/
private static final String MODULE_NAME = "WiFiStats";
public static final String NAME = "WiFiStats";
/**
* The {@code Log} tag {@code WiFiStatsModule} is to log messages with.
*/
static final String TAG = MODULE_NAME;
static final String TAG = NAME;
/**
* The scale used for the signal value. A level of the signal, given in the
@@ -87,7 +85,7 @@ class WiFiStatsModule
*/
@Override
public String getName() {
return MODULE_NAME;
return NAME;
}
/**

View File

@@ -50,7 +50,7 @@ public class IncomingCallView
* by/associated with the specified {@code name}.
*/
@Override
public void onExternalAPIEvent(String name, ReadableMap data) {
protected void onExternalAPIEvent(String name, ReadableMap data) {
onExternalAPIEvent(LISTENER_METHODS, name, data);
}

View File

@@ -21,6 +21,7 @@ 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 java.net.UnknownHostException;
@@ -32,9 +33,12 @@ import java.net.UnknownHostException;
* [1]: https://tools.ietf.org/html/rfc6146
* [2]: https://tools.ietf.org/html/rfc6052
*/
@ReactModule(name = NAT64AddrInfoModule.NAME)
public class NAT64AddrInfoModule
extends ReactContextBaseJavaModule {
public final static String NAME = "NAT64AddrInfo";
/**
* The host for which the module wil try to resolve both IPv4 and IPv6
* addresses in order to figure out the NAT64 prefix.
@@ -46,15 +50,10 @@ public class NAT64AddrInfoModule
*/
private final static long INFO_LIFETIME = 60 * 1000;
/**
* The name of this module.
*/
private final static String MODULE_NAME = "NAT64AddrInfo";
/**
* The {@code Log} tag {@code NAT64AddrInfoModule} is to log messages with.
*/
private final static String TAG = MODULE_NAME;
private final static String TAG = NAME;
/**
* The {@link NAT64AddrInfo} instance which holds NAT64 prefix/suffix.
@@ -119,6 +118,6 @@ public class NAT64AddrInfoModule
@Override
public String getName() {
return MODULE_NAME;
return NAME;
}
}

View File

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

View File

@@ -3,6 +3,10 @@ rootProject.name = 'jitsi-meet'
include ':app', ':sdk'
include ':react-native-background-timer'
project(':react-native-background-timer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-background-timer/android')
include ':react-native-calendar-events'
project(':react-native-calendar-events').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-calendar-events/android')
include ':react-native-community-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-google-signin'
@@ -19,5 +23,5 @@ 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-webrtc'
project(':react-native-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android')
include ':react-native-calendar-events'
project(':react-native-calendar-events').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-calendar-events/android')
include ':react-native-webview'
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')

View File

@@ -38,6 +38,7 @@ import {
conferenceFailed,
conferenceJoined,
conferenceLeft,
conferenceSubjectChanged,
conferenceWillJoin,
conferenceWillLeave,
dataChannelOpened,
@@ -45,11 +46,13 @@ import {
onStartMutedPolicyChanged,
p2pStatusChanged,
sendLocalParticipant,
setDesktopSharingEnabled,
setSubject
setDesktopSharingEnabled
} from './react/features/base/conference';
import {
checkAndNotifyForNewDevice,
getAvailableDevices,
notifyCameraError,
notifyMicError,
setAudioOutputDeviceId,
updateDeviceList
} from './react/features/base/devices';
@@ -437,8 +440,8 @@ class ConferenceConnector {
hasRead: true,
error: code,
message: msg,
timestamp: Date.now(),
type: 'error'
messageType: 'error',
timestamp: Date.now()
}));
}
break;
@@ -485,10 +488,13 @@ class ConferenceConnector {
* call in hangup() to resolve when all operations are finished.
*/
function disconnect() {
connection.disconnect();
APP.API.notifyConferenceLeft(APP.conference.roomName);
const onDisconnected = () => {
APP.API.notifyConferenceLeft(APP.conference.roomName);
return Promise.resolve();
return Promise.resolve();
};
return connection.disconnect().then(onDisconnected, onDisconnected);
}
/**
@@ -693,13 +699,14 @@ export default {
// If both requests for 'audio' + 'video' and 'audio'
// only failed, we assume that there are some problems
// with user's microphone and show corresponding dialog.
APP.UI.showMicErrorNotification(audioOnlyError);
APP.UI.showCameraErrorNotification(videoOnlyError);
APP.store.dispatch(notifyMicError(audioOnlyError));
APP.store.dispatch(notifyCameraError(videoOnlyError));
} else {
// If request for 'audio' + 'video' failed, but request
// for 'audio' only was OK, we assume that we had
// problems with camera and show corresponding dialog.
APP.UI.showCameraErrorNotification(audioAndVideoError);
APP.store.dispatch(
notifyCameraError(audioAndVideoError));
}
}
@@ -717,13 +724,21 @@ export default {
this.roomName = options.roomName;
return (
this.createInitialLocalTracksAndConnect(
// Initialize the device list first. This way, when creating tracks
// based on preferred devices, loose label matching can be done in
// cases where the exact ID match is no longer available, such as
// when the camera device has switched USB ports.
this._initDeviceList()
.catch(error => logger.warn(
'initial device list initialization failed', error))
.then(() => this.createInitialLocalTracksAndConnect(
options.roomName, {
startAudioOnly: config.startAudioOnly,
startScreenSharing: config.startScreenSharing,
startWithAudioMuted: config.startWithAudioMuted,
startWithVideoMuted: config.startWithVideoMuted
})
}))
.then(([ tracks, con ]) => {
tracks.forEach(track => {
if ((track.isAudioTrack() && this.isLocalAudioMuted())
@@ -768,12 +783,22 @@ export default {
this.setVideoMuteStatus(true);
}
this._initDeviceList();
// Initialize device list a second time to ensure device labels
// get populated in case of an initial gUM acceptance; otherwise
// they may remain as empty strings.
this._initDeviceList(true);
if (config.iAmRecorder) {
this.recorder = new Recorder();
}
if (config.startSilent) {
APP.store.dispatch(showNotification({
descriptionKey: 'notify.startSilentDescription',
titleKey: 'notify.startSilentTitle'
}));
}
// XXX The API will take care of disconnecting from the XMPP
// server (and, thus, leaving the room) on unload.
return new Promise((resolve, reject) => {
@@ -827,7 +852,7 @@ export default {
if (!this.localAudio && !mute) {
const maybeShowErrorDialog = error => {
showUI && APP.UI.showMicErrorNotification(error);
showUI && APP.store.dispatch(notifyMicError(error));
};
createLocalTracksF({ devices: [ 'audio' ] }, false)
@@ -890,7 +915,7 @@ export default {
if (!this.localVideo && !mute) {
const maybeShowErrorDialog = error => {
showUI && APP.UI.showCameraErrorNotification(error);
showUI && APP.store.dispatch(notifyCameraError(error));
};
// Try to create local video if there wasn't any.
@@ -1326,7 +1351,14 @@ export default {
this.isSharingScreen = newStream && newStream.videoType === 'desktop';
if (wasSharingScreen !== this.isSharingScreen) {
APP.API.notifyScreenSharingStatusChanged(this.isSharingScreen);
const details = {};
if (this.isSharingScreen) {
details.sourceType = newStream.sourceType;
}
APP.API.notifyScreenSharingStatusChanged(
this.isSharingScreen, details);
}
},
@@ -1827,7 +1859,7 @@ export default {
APP.UI.showToolbar(6000);
});
room.on(JitsiConferenceEvents.SUBJECT_CHANGED,
subject => APP.API.notifySubjectChanged(subject));
subject => APP.store.dispatch(conferenceSubjectChanged(subject)));
room.on(
JitsiConferenceEvents.LAST_N_ENDPOINTS_CHANGED,
@@ -1912,21 +1944,6 @@ export default {
JitsiConferenceEvents.PARTICIPANT_PROPERTY_CHANGED,
(participant, name, oldValue, newValue) => {
switch (name) {
case 'features_screen-sharing': {
APP.store.dispatch(participantUpdated({
conference: room,
id: participant.getId(),
features: { 'screen-sharing': true }
}));
break;
}
case 'raisedHand':
APP.store.dispatch(participantUpdated({
conference: room,
id: participant.getId(),
raisedHand: newValue === 'true'
}));
break;
case 'remoteControlSessionStatus':
APP.UI.setRemoteControlActiveStatus(
participant.getId(),
@@ -2102,12 +2119,10 @@ export default {
})
.then(() => {
logger.log('switched local video device');
APP.store.dispatch(updateSettings({
cameraDeviceId
}));
this._updateVideoDeviceId();
})
.catch(err => {
APP.UI.showCameraErrorNotification(err);
APP.store.dispatch(notifyCameraError(err));
});
}
);
@@ -2133,15 +2148,14 @@ export default {
return stream;
})
.then(stream => {
this.useAudioStream(stream);
.then(stream => this.useAudioStream(stream))
.then(() => {
logger.log('switched local audio device');
APP.store.dispatch(updateSettings({
micDeviceId
}));
this._updateAudioDeviceId();
})
.catch(err => {
APP.UI.showMicErrorNotification(err);
APP.store.dispatch(notifyMicError(err));
});
}
);
@@ -2287,20 +2301,23 @@ export default {
},
/**
* Inits list of current devices and event listener for device change.
* Updates the list of current devices.
* @param {boolean} setDeviceListChangeHandler - Whether to add the deviceList change handlers.
* @private
* @returns {Promise}
*/
_initDeviceList() {
_initDeviceList(setDeviceListChangeHandler = false) {
const { mediaDevices } = JitsiMeetJS;
if (mediaDevices.isDeviceListAvailable()
&& mediaDevices.isDeviceChangeAvailable()) {
this.deviceChangeListener = devices =>
window.setTimeout(() => this._onDeviceListChanged(devices), 0);
mediaDevices.addEventListener(
JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
this.deviceChangeListener);
if (setDeviceListChangeHandler) {
this.deviceChangeListener = devices =>
window.setTimeout(() => this._onDeviceListChanged(devices), 0);
mediaDevices.addEventListener(
JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
this.deviceChangeListener);
}
const { dispatch } = APP.store;
@@ -2309,18 +2326,9 @@ export default {
// Ugly way to synchronize real device IDs with local
// storage and settings menu. This is a workaround until
// getConstraints() method will be implemented in browsers.
if (this.localAudio) {
dispatch(updateSettings({
micDeviceId: this.localAudio.getDeviceId()
}));
}
this._updateAudioDeviceId();
if (this.localVideo
&& this.localVideo.videoType === 'camera') {
dispatch(updateSettings({
cameraDeviceId: this.localVideo.getDeviceId()
}));
}
this._updateVideoDeviceId();
APP.UI.onAvailableDevicesChanged(devices);
});
@@ -2329,6 +2337,33 @@ export default {
return Promise.resolve();
},
/**
* Updates the settings for the currently used video device, extracting
* the device id from the used track.
* @private
*/
_updateVideoDeviceId() {
if (this.localVideo
&& this.localVideo.videoType === 'camera') {
APP.store.dispatch(updateSettings({
cameraDeviceId: this.localVideo.getDeviceId()
}));
}
},
/**
* Updates the settings for the currently used audio device, extracting
* the device id from the used track.
* @private
*/
_updateAudioDeviceId() {
if (this.localAudio) {
APP.store.dispatch(updateSettings({
micDeviceId: this.localAudio.getDeviceId()
}));
}
},
/**
* Event listener for JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED to
* handle change of available media devices.
@@ -2337,6 +2372,8 @@ export default {
* @returns {Promise}
*/
_onDeviceListChanged(devices) {
const oldDevices = APP.store.getState()['features/base/devices'].availableDevices;
APP.store.dispatch(updateDeviceList(devices));
const newDevices
@@ -2348,6 +2385,10 @@ export default {
const promises = [];
const audioWasMuted = this.isLocalAudioMuted();
const videoWasMuted = this.isLocalVideoMuted();
const requestedInput = {
audio: Boolean(newDevices.audioinput),
video: Boolean(newDevices.videoinput)
};
if (typeof newDevices.audiooutput !== 'undefined') {
const { dispatch } = APP.store;
@@ -2359,6 +2400,46 @@ export default {
promises.push(setAudioOutputPromise);
}
// Handles the use case when the default device is changed (we are always stopping the streams because it's
// simpler):
// If the default device is changed we need to first stop the local streams and then call GUM. Otherwise GUM
// will return a stream using the old default device.
if (requestedInput.audio && this.localAudio) {
this.localAudio.stopStream();
}
if (requestedInput.video && this.localVideo) {
this.localVideo.stopStream();
}
// Let's handle unknown/non-preferred devices
const newAvailDevices
= APP.store.getState()['features/base/devices'].availableDevices;
let newAudioDevices = [];
let oldAudioDevices = [];
if (typeof newDevices.audiooutput === 'undefined') {
newAudioDevices = newAvailDevices.audioOutput;
oldAudioDevices = oldDevices.audioOutput;
}
if (!requestedInput.audio) {
newAudioDevices = newAudioDevices.concat(newAvailDevices.audioInput);
oldAudioDevices = oldAudioDevices.concat(oldDevices.audioInput);
}
// check for audio
if (newAudioDevices.length > 0) {
APP.store.dispatch(
checkAndNotifyForNewDevice(newAudioDevices, oldAudioDevices));
}
// check for video
if (!requestedInput.video) {
APP.store.dispatch(
checkAndNotifyForNewDevice(newAvailDevices.videoInput, oldDevices.videoInput));
}
promises.push(
mediaDeviceHelper.createLocalTracksAfterDeviceListChanged(
createLocalTracksF,
@@ -2377,8 +2458,25 @@ export default {
});
return Promise.all(muteSyncPromises)
.then(() => Promise.all(
this._setLocalAudioVideoStreams(tracks)));
.then(() =>
Promise.all(Object.keys(requestedInput).map(mediaType => {
if (requestedInput[mediaType]) {
const useStream
= mediaType === 'audio'
? this.useAudioStream.bind(this)
: this.useVideoStream.bind(this);
// Use the new stream or null if we failed to obtain it.
return useStream(tracks.find(track => track.getType() === mediaType) || null)
.then(() => {
mediaType === 'audio'
? this._updateAudioDeviceId()
: this._updateVideoDeviceId();
});
}
return Promise.resolve();
})));
})
.then(() => {
// Log and sync known mute state.
@@ -2410,7 +2508,7 @@ export default {
*/
updateAudioIconEnabled() {
const audioMediaDevices
= APP.store.getState()['features/base/devices'].audioInput;
= APP.store.getState()['features/base/devices'].availableDevices.audioInput;
const audioDeviceCount
= audioMediaDevices ? audioMediaDevices.length : 0;
@@ -2433,7 +2531,7 @@ export default {
*/
updateVideoIconEnabled() {
const videoMediaDevices
= APP.store.getState()['features/base/devices'].videoInput;
= APP.store.getState()['features/base/devices'].availableDevices.videoInput;
const videoDeviceCount
= videoMediaDevices ? videoMediaDevices.length : 0;
@@ -2515,8 +2613,7 @@ export default {
leaveRoomAndDisconnect() {
APP.store.dispatch(conferenceWillLeave(room));
return room.leave()
.then(disconnect, disconnect);
return room.leave().then(disconnect, disconnect);
},
/**
@@ -2692,6 +2789,13 @@ export default {
onProxyConnectionEvent(event) {
if (!this._proxyConnection) {
this._proxyConnection = new JitsiMeetJS.ProxyConnectionService({
/**
* Pass the {@code JitsiConnection} instance which will be used
* to fetch TURN credentials.
*/
jitsiConnection: APP.connection,
/**
* The proxy connection feature is currently tailored towards
* taking a proxied video stream and showing it as a local
@@ -2760,16 +2864,6 @@ export default {
APP.API.notifyAudioMutedStatusChanged(muted);
},
/**
* Changes the subject of the conference.
* Note: available only for moderator.
*
* @param subject {string} the new subject for the conference.
*/
setSubject(subject) {
APP.store.dispatch(setSubject(subject));
},
/**
* Dispatches the passed in feedback for submission. The submitted score
* should be a number inclusively between 1 through 5, or -1 for no score.

View File

@@ -90,6 +90,10 @@ var config = {
// applied locally. FIXME: having these 2 options is confusing.
// startWithAudioMuted: false,
// Enabling it (with #params) will disable local audio output of remote
// participants and to enable it back a reload is needed.
// startSilent: false
// Video
// Sets the preferred resolution (height) for local video. Defaults to 720.
@@ -174,7 +178,21 @@ var config = {
// Enable the dropbox integration.
// dropbox: {
// appKey: '<APP_KEY>' // Specify your app key here.
// // A URL to redirect the user to, after authenticating
// // by default uses:
// // 'https://jitsi-meet.example.com/static/oauth.html'
// redirectURI:
// 'https://jitsi-meet.example.com/subfolder/static/oauth.html'
// },
// When integrations like dropbox are enabled only that will be shown,
// by enabling fileRecordingsServiceEnabled, we show both the integrations
// and the generic recording service (its configuration and storage type
// depends on jibri configuration)
// fileRecordingsServiceEnabled: false,
// Whether to show the possibility to share file recording with other people
// (e.g. meeting participants), based on the actual implementation
// on the backend.
// fileRecordingsServiceSharingEnabled: false,
// Whether to enable live streaming or not.
// liveStreamingEnabled: false,
@@ -252,6 +270,13 @@ var config = {
// Whether or not some features are checked based on token.
// enableFeaturesBasedOnToken: false,
// Enable lock room for all moderators, even when userRolesBasedOnToken is enabled and participants are guests.
// lockRoomGuestEnabled: false,
// When enabled the password used for locking a room is restricted to up to the number of digits specified
// roomPasswordNumberOfDigits: 10,
// default: roomPasswordNumberOfDigits: false,
// Message to show the users. Example: 'The service will be down for
// maintenance at 01:00 AM GMT,
// noticeMessage: '',
@@ -393,6 +418,10 @@ var config = {
// use only.
// _desktopSharingSourceDevice: 'sample-id-or-label'
// 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
// List of undocumented settings used in jitsi-meet
/**
_immediateReloadThreshold
@@ -407,12 +436,10 @@ var config = {
dialOutCodesUrl
disableRemoteControl
displayJids
enableLocalVideoFlip
etherpad_base
externalConnectUrl
firefox_fake_device
googleApiApplicationClientID
googleApiIOSClientID
iAmRecorder
iAmSipGateway
microsoftApiApplicationClientID

View File

@@ -40,3 +40,11 @@
.videocontainer .tOoji {
background: none;
}
/**
* Override @atlaskit/InlineDialog styling for the overflowmenu so it displays
* with the correct height.
*/
.toolbox-button-wth-dialog .eYJELv {
max-height: initial;
}

View File

@@ -1,6 +1,9 @@
#sideToolbarContainer {
background-color: $newToolbarBackgroundColor;
box-sizing: border-box;
color: #FFF;
display: flex;
flex-direction: column;
/**
* Make the sidebar flush with the top of the toolbar. Take the size of
* the toolbar and subtract from 100%.
@@ -21,20 +24,6 @@
&.slideInExt {
left: 0;
}
.sideToolbarContainer__inner {
box-sizing: border-box;
color: #FFF;
display: flex;
flex-direction: column;
height: 100%;
width: $sidebarWidth;
}
}
#chat_container * {
-webkit-user-select: text;
user-select: text;
}
#chatconversation {
@@ -42,9 +31,8 @@
flex: 1;
font-size: 10pt;
line-height: 20px;
margin-top: 15px;
overflow: auto;
padding: 5px;
padding: 16px;
text-align: left;
width: $sidebarWidth;
word-wrap: break-word;
@@ -92,26 +80,41 @@
}
}
.chat-close {
background: gray;
border: 3px solid rgba(255, 255, 255, 0.1);
border-radius: 100%;
color: white;
cursor:pointer;
height: 10px;
line-height: 10px;
padding: 4px;
position: absolute;
right: 5px;
text-align: center;
top: 5px;
width: 10px;
.chat-header {
background-color: $chatHeaderBackgroundColor;
height: 70px;
position: relative;
width: 100%;
z-index: 1;
.chat-close {
align-items: center;
bottom: 8px;
color: white;
cursor: pointer;
display: flex;
font-size: 18px;
height: 40px;
justify-content: center;
line-height: 15px;
padding: 4px;
position: absolute;
right: 5px;
width: 40px;
&:hover {
color: rgba(255, 255, 255, 0.8);
}
}
}
#chat-input {
background-color: $newToolbarBackgroundColor;
border-top: 1px solid $chatInputSeparatorColor;
display: flex;
* {
background-color: transparent;
}
}
.remoteuser {
@@ -120,20 +123,16 @@
.usrmsg-form {
flex: 1;
margin-left: 5px;
}
#usermsg {
background-color: $newToolbarBackgroundColor;
border: 0px none;
border-radius:0;
box-shadow: none;
color: white;
font-size: 10pt;
font-size: 15px;
line-height: 30px;
padding: 5px 5px 5px 0px;
max-height:150px;
min-height:35px;
padding: 5px;
overflow-y: auto;
resize: none;
width: 100%;
@@ -146,61 +145,47 @@
}
#nickname {
position: absolute;
text-align: center;
color: #9d9d9d;
font-size: 18px;
top: 100px;
margin-top: 30px;
left: 5px;
right: 5px;
width: 95%;
}
#chat_container .display-name {
float: left;
padding-left: 5px;
font-weight: bold;
white-space: nowrap;
text-overflow: ellipsis;
width: 95%;
overflow: hidden;
}
.sideToolbarContainer {
* {
-webkit-user-select: text;
user-select: text;
}
#chat_container .timestamp {
float: right;
padding-right: 5px;
font-size: 11px;
}
.usermessage {
padding-top: 20px;
padding-left: 5px;
}
.chatArrow {
height: 15px;
left: -10px;
position: absolute;
.display-name {
font-size: 13px;
font-weight: bold;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
.chatmessage {
background-color: $newToolbarBackgroundColor;
width: 93%;
margin-left: 9px;
margin-right: auto;
border-radius: 5px;
border-top-left-radius: 0px;
margin-top: 3px;
background-color: $chatRemoteMessageBackgroundColor;
border-radius: 0px 6px 6px 6px;
box-sizing: border-box;
color: white;
margin-top: 3px;
max-width: 100%;
padding-bottom: 3px;
position: relative;
&.localuser .display-name {
color: #4C9AFF
&.localuser {
background-color: $chatLocalMessageBackgroundColor;
border-radius: 6px 0px 6px 6px;
}
&.error {
.chatArrow,
border-radius: 0px;
.timestamp,
.display-name {
display: none;
@@ -219,8 +204,6 @@
#smileys {
font-size: 20pt;
display: inline-block;
height: 26px;
margin: auto;
cursor: pointer;
}
@@ -231,33 +214,36 @@
}
#smileysarea {
background-color: $newToolbarBackgroundColor;
border: 0px none;
display: flex;
height: 70px;
max-height: 150px;
min-height: 35px;
min-width: 31px;
padding: 0px;
overflow: hidden;
width: 17%;
}
.smiley-input {
display: flex;
position: relative;
}
.smileys-panel {
bottom: 100%;
box-sizing: border-box;
height: 0;
height: auto;
max-height: 0;
overflow: hidden;
position: absolute;
transition: height 0.3s;
width: $sidebarWidth;
/**
* CSS transitions do not apply for auto dimensions. So to produce the css
* accordion effect for showing and hiding the smiley-panel, while allowing
* for variable panel, height, use a very large max-height and animate off
* of that.
*/
transition: max-height 0.3s;
&.show-smileys {
height: 146px;
max-height: 500%;
}
#smileysContainer {
@@ -291,3 +277,49 @@
#usermsg::-webkit-scrollbar-track-piece {
background: #3a3a3a;
}
.chat-message-group {
display: flex;
flex-direction: column;
&.local {
align-items: flex-end;
.chatmessage {
background-color: $chatLocalMessageBackgroundColor;
border-radius: 6px 0px 6px 6px;
}
.display-name {
display: none;
}
.timestamp {
text-align: right;
}
}
&.error {
.chatmessage {
border-radius: 0px;
color: red;
}
.display-name {
display: none;
}
}
.chatmessage-wrapper {
max-width: 100%;
}
.chatmessage {
background-color: $chatRemoteMessageBackgroundColor;
border-radius: 0px 6px 6px 6px;
display: inline-block;
margin-top: 3px;
color: white;
padding: 8px;
}
}

View File

@@ -25,6 +25,27 @@
-moz-osx-font-smoothing: grayscale;
}
.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";
}

View File

@@ -41,3 +41,10 @@
top: -25px;
width: 100%;
}
.popover {
background-color: $popoverBg;
border-radius: 3px;
margin: -16px -24px;
padding: 16px 24px;
}

View File

@@ -5,16 +5,13 @@
.popupmenu {
min-width: 75px;
text-align: left;
padding: 0;
padding: 0px;
width: 150px;
white-space: nowrap;
&__item {
list-style-type: none;
height: 35px;
&:hover {
background-color: rgba(9, 30, 66, 0.04);
}
}
// Link Appearance
@@ -28,6 +25,12 @@
width: 100%;
cursor: pointer;
padding: 0 5px;
color: $popupMenuColor;
&:hover {
background-color: $popupMenuHoverBackground;
color: $popupMenuHoverColor;
}
&.disabled {
pointer-events: none;
@@ -62,6 +65,18 @@
top: 50%;
transform: translate(0, -50%);
width: 100%;
&::-webkit-slider-runnable-track {
background-color: $popupSliderColor;
}
&::-moz-range-track {
background-color: $popupSliderColor;
}
&::-ms-fill-lower {
background-color: $popupSliderColor;
}
}
}
}
@@ -91,7 +106,7 @@
* InlineDialogs.
*/
ul.popupmenu {
margin: -15px;
margin: -16px -24px;
}
span.remotevideomenu:hover ul.popupmenu, ul.popupmenu:hover {

View File

@@ -11,42 +11,45 @@
flex: 0;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding-top: 32px;
.recording-title {
display: inline-flex;
align-items: center;
font-size: 16px;
font-weight: bold;
margin-left: 16px;
}
}
.recording-header-line {
border-top: 1px solid #5e6d7a;
}
.recording-switch-disabled {
opacity: 0.5;
}
.recording-icon-container {
display: inline-flex;
align-items: center;
}
.recording-icon {
width: 32px;
height: 32px;
object-fit: contain;
}
.recording-switch {
margin-left: auto;
}
.authorization-panel {
display: flex;
flex-direction: column;
margin-bottom: 10px;
margin: 0 40px 10px 40px;
padding-bottom: 10px;
.dropbox-sign-in {
align-items: center;
border: 1px solid #4285f4;
background-color: white;
border-radius: 2px;
cursor: pointer;
display: inline-flex;
padding: 10px;
font-size: 18px;
font-weight: 600;
margin: 10px 0px;
color: #4285f4;
.dropbox-logo {
background-color: white;
border-radius: 2px;
display: inline-block;
padding-right: 5px;
height: 18px;
}
}
.logged-in-panel {
padding: 10px;
}

22
css/_subject.scss Normal file
View File

@@ -0,0 +1,22 @@
.subject {
top: -120px;
transition: top .3s ease-in;
height: 95px;
width: 100%;
pointer-events: none;
position: absolute;
padding: 25px 140px 0 140px;
text-align: center;
font-size: 17px;
color: #fff;
z-index: $toolbarBackgroundZ;
overflow: hidden;
text-overflow: ellipsis;
box-sizing: border-box;
white-space: nowrap;
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
&.visible {
top: 0px;
}
}

View File

@@ -48,6 +48,7 @@
height: 160px;
width: 100%;
bottom: -160px;
pointer-events: none;
position: absolute;
z-index: $toolbarBackgroundZ;
}
@@ -190,6 +191,14 @@
cursor: initial;
color: #3b475c;
}
i.toggled {
background: inherit;
}
i.toggled:hover {
background: inherit;
}
}
.beta-tag {

View File

@@ -63,7 +63,7 @@ $audioLevelShadow: rgba(9, 36, 77, 0.9);
$videoStateIndicatorColor: $defaultColor;
$videoStateIndicatorBackground: $toolbarBackground;
$videoStateIndicatorSize: 40px;
$remoteVideoMenuIconLeft: initial;
$remoteVideoMenuIconMargin: initial;
/**
* Feedback Modal
@@ -83,13 +83,22 @@ $modalMockAKInputBorder: 1px solid #f4f5f7;
$modalTextColor: #333;
/**
* Chat
*/
$chatHeaderBackgroundColor: rgba(42, 58, 75, 0.9);
$chatInputSeparatorColor: #A4B8D1;
$chatLocalMessageBackgroundColor: rgba(26, 108, 180, 1);
$chatRemoteMessageBackgroundColor: rgba(240, 243, 247, 0.15);
$sidebarWidth: 375px;
/**
* Misc.
*/
$borderRadius: 4px;
$defaultWatermarkLink: '../images/watermark.png';
$sidebarWidth: 220px;
$popoverMenuPadding: 13px;
$happySoftwareBackground: transparent;
$desktopAppDragBarHeight: 25px;
/**
* Z-indexes. TODO: Replace this by a function.
@@ -156,3 +165,9 @@ $welcomePageDescriptionColor: #fff;
$welcomePageFontFamily: inherit;
$welcomePageHeaderBackground: linear-gradient(-90deg, #1251AE 0%, #0074FF 50%, #1251AE 100%);
$welcomePageTitleColor: #fff;
/**
* Deep-linking page variables.
*/
$deepLinkingMobileLogoHeight: 40px;
$deepLinkingMobileHeaderBackground: #f1f2f5;

View File

@@ -409,10 +409,10 @@
height: 13px;
color: #FFF;
font-size: 10pt;
margin-right: $remoteVideoMenuIconMargin;
>i{
cursor: hand;
margin-left: $remoteVideoMenuIconLeft;
}
}
.remote-video-menu-trigger {
@@ -514,6 +514,7 @@
}
#dominantSpeakerAvatar {
background-color: #000000;
object-fit: cover;
}
.dynamic-shadow {
@@ -525,12 +526,21 @@
transition: box-shadow 0.3s ease;
}
.userAvatar {
.avatar-container {
@include maxSize(60px);
@include absoluteAligning();
border-radius: 50%;
display: flex;
justify-content: center;
height: 50%;
overflow: hidden;
width: auto;
.userAvatar {
height: 100%;
object-fit: cover;
width: 100%;
}
}
#videoNotAvailableScreen {

View File

@@ -8,13 +8,13 @@
.header {
width: 100%;
height: 70px;
background-color: #f1f2f5;
background-color: $deepLinkingMobileHeaderBackground;
text-align: center;
.logo {
margin-top: 15px;
margin-left: auto;
margin-right: auto;
height: 40px;
height: $deepLinkingMobileLogoHeight;
}
}
@@ -28,7 +28,7 @@
max-width: 40em;
padding: 35px 0 40px 0;
text-align: center;
width: 75%;
width: 90%;
a:active {
text-decoration: none;
@@ -46,7 +46,7 @@
&__text,
.deep-linking-dial-in {
font-size: 1.2em;
font-size: 1em;
line-height: em(29px, 21px);
margin-bottom: 0.65em;
@@ -59,6 +59,31 @@
font-size: em(21, 18);
}
}
table {
font-size: 1em;
}
.dial-in-conference-id {
margin: 10px 0 10px 0;
}
.dial-in-conference-description {
font-size: 0.8em;
}
.toll-free-list {
min-width: 80px;
}
.numbers-list {
min-width: 150px;
}
li.toll-free:empty:before {
content: '.';
visibility: hidden;
}
}
&__href {
@@ -108,6 +133,7 @@
.dial-in-numbers-list {
color: $unsupportedBrowserTextColor;
padding-left: 20px;
}
.dial-in-numbers-body {

View File

@@ -6,7 +6,7 @@
/**
* Let the avatar grow with the tile.
*/
.userAvatar {
.avatar-container {
max-height: initial;
max-width: initial;
}

View File

@@ -25,7 +25,7 @@
display: flex;
flex-direction: column-reverse;
height: 100%;
padding: 10px 5px;
padding: ($desktopAppDragBarHeight - 5px) 5px 10px;
/**
* fixed positioning is necessary for remote menus and tooltips to pop
* out of the scrolling filmstrip. AtlasKit dialogs and tooltips use

View File

@@ -74,6 +74,7 @@
.remote-video-menu-trigger {
margin-bottom: 7px;
margin-left: $remoteVideoMenuIconMargin;
}
}

View File

@@ -18,6 +18,11 @@
/* Animations END */
/* Flags BEGIN */
$flagsImagePath: "../images/";
@import "../node_modules/bc-css-flags/dist/css/bc-css-flags.scss";
/* Flags END */
/* Fonts BEGIN */
@import 'font';
@@ -44,6 +49,7 @@
@import 'modals/local-recording/local-recording';
@import 'videolayout_default';
@import 'notice';
@import 'subject';
@import 'popup_menu';
@import 'recording';
@import 'login_menu';

View File

@@ -66,7 +66,7 @@
}
.info-dialog-dial-in {
white-space: nowrap;
word-break: break-all;
.conference-id,
.phone-number {
@@ -77,6 +77,7 @@
.info-dialog-icon {
color: #6453C0;
font-size: 16px;
min-width: 30px;
}
.info-dialog-url-text,
@@ -124,29 +125,85 @@
}
}
.dial-in-numbers-list {
margin-top: 20px;
font-size: 12px;
line-height: 24px;
border-collapse: separate;
border-spacing: 0 5px;
thead {
text-align: left;
}
td,
th {
border-bottom: 1px solid #d1dbe8;
}
.flag-cell {
vertical-align: top;
width: 30px;
}
.flag {
display: block;
margin: 5px 5px 0px 5px;
}
.country {
font-weight: bold;
vertical-align: top;
padding: 0 20px 0 0;
}
ul {
padding: 0px 0px 0px 0px;
}
.numbers-list {
list-style: none;
padding: 0 20px 0 0;
}
.toll-free-list {
font-weight: bold;
list-style: none;
vertical-align: top;
}
li.toll-free:empty:before {
content: '.';
visibility: hidden;
}
}
.dial-in-page {
align-items: center;
box-sizing: border-box;
display: flex;
flex-direction: column;
font-size: 24px;
font-size: 12px;
max-height: 100%;
overflow: auto;
padding: 25px;
padding: 15pt;
position: absolute;
transform: translateY(-50%);
top: 50%;
width: 100%;
.dial-in-numbers-list {
font-size: 24px;
margin-top: 20px;
}
.dial-in-conference-id {
text-align: center;
min-width: 200px;
width: 30%;
margin-top: 40px;
}
.dial-in-conference-name,
.dial-in-conference-pin {
font-size: 18px;
}
.dial-in-conference-description {
margin: 12px;
}
}

View File

@@ -80,9 +80,12 @@ $errorColor: #c61600;
$feedbackCancelFontColor: #333;
// Popover colors
$popoverBg: #000;
$popoverFontColor: #ffffff;
$popupMenuSelectedItemBackground: rgba(256, 256, 256, .2);
$popoverBg: initial;
$popoverFontColor: #ffffff !important;
$popupMenuColor: #ffffff !important;
$popupMenuHoverColor: #ffffff !important;
$popupMenuHoverBackground: rgba(255, 255, 255, 0.1);
$popupSliderColor: #0376da;
// Toolbar
$secondaryToolbarBg: rgba(0, 0, 0, 0.5);

4
debian/control vendored
View File

@@ -36,7 +36,7 @@ Description: Configuration for web serving of Jitsi Meet
Package: jitsi-meet-prosody
Architecture: all
Depends: openssl, prosody | prosody-trunk
Depends: openssl, prosody | prosody-trunk | prosody-0.11
Description: Prosody configuration for Jitsi Meet
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
Videobridge to provide high quality, scalable video conferences.
@@ -50,6 +50,6 @@ Description: Prosody configuration for Jitsi Meet
Package: jitsi-meet-tokens
Architecture: all
Depends: ${misc:Depends}, prosody-trunk (>= 1nightly607), libssl-dev, luarocks, jitsi-meet-prosody
Depends: ${misc:Depends}, prosody-trunk (>= 1nightly747) | prosody-0.11 | prosody (>= 0.11.2), libssl-dev, luarocks, jitsi-meet-prosody
Description: Prosody token authentication plugin for Jitsi Meet

View File

@@ -125,6 +125,33 @@ case "$1" in
ln -sf /var/lib/prosody/$JVB_HOSTNAME.crt /etc/prosody/certs/$JVB_HOSTNAME.crt
fi
PR11_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'prosody-0.11' 2>/dev/null | awk '{print $3}' || true)"
PR10_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'prosody-0.10' 2>/dev/null | awk '{print $3}' || true)"
PR_VER_INSTALLED=$(dpkg-query -f='${Version}\n' --show prosody 2>/dev/null || true)
if [ "$PR11_INSTALL_CHECK" = "installed" ] \
|| [ "$PR11_INSTALL_CHECK" = "unpacked" ] \
|| dpkg --compare-versions "$PR_VER_INSTALLED" gt "0.11" ; then
if [ -f $PROSODY_HOST_CONFIG ]; then
sed -i 's/storage = \"null\"/storage = \"memory\"/g' $PROSODY_HOST_CONFIG
# trigger a restart
PROSODY_CONFIG_PRESENT="false"
fi
fi
if [ "$PR10_INSTALL_CHECK" = "installed" ] \
|| [ "$PR10_INSTALL_CHECK" = "unpacked" ] \
|| dpkg --compare-versions "$PR_VER_INSTALLED" gt "0.10" ; then
# if the version is 0.10.X (>0.10 and <0.11)
if [ -f $PROSODY_HOST_CONFIG ] \
&& dpkg --compare-versions "$PR_VER_INSTALLED" lt "0.11" ; then
sed -i 's/storage = \"null\"/storage = \"none\"/g' $PROSODY_HOST_CONFIG
# trigger a restart
PROSODY_CONFIG_PRESENT="false"
fi
fi
if [ ! -f /var/lib/prosody/$JICOFO_AUTH_DOMAIN.crt ]; then
# prosodyctl takes care for the permissions
# echo for using all default values

View File

@@ -69,14 +69,17 @@ case "$1" in
echo "Failed to install basexx - try installing it manually"
fi
PR11_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'prosody-0.11' 2>/dev/null | awk '{print $3}' || true)"
PR_VER_INSTALLED=$(dpkg-query -f='${Version}\n' --show prosody 2>/dev/null || true)
if [ "$PR11_INSTALL_CHECK" = "installed" ] \
|| [ "$PR11_INSTALL_CHECK" = "unpacked" ] \
|| dpkg --compare-versions "$PR_VER_INSTALLED" gt "0.11" ; then
sed -i 's/module:hook/module:hook_global/g' /usr/share/jitsi-meet/prosody-plugins/mod_auth_token.lua
fi
if [ -x "/etc/init.d/prosody" ]; then
invoke-rc.d prosody restart
fi
echo "This package requires BOSH Prosody module to be patched !"
echo "Use the following command, after this package has been installed and"
echo "after every prosody-trunk upgrade:"
echo "sudo patch -N /usr/lib/prosody/modules/mod_bosh.lua /usr/share/jitsi-meet/prosody-plugins/mod_bosh.lua.patch"
fi
else
echo "Prosody config not found at $PROSODY_HOST_CONFIG - unable to auto-configure token authentication"

View File

@@ -132,6 +132,7 @@ case "$1" in
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./config.js=/etc/jitsi/meet/$JVB_HOSTNAME-config.js" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./interface_config.js=/usr/share/jitsi-meet/interface_config.js" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./logging_config.js=/usr/share/jitsi-meet/logging_config.js" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./external_api.js=/usr/share/jitsi-meet/libs/external_api.min.js" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.RewriteHandler.regex=^/([a-zA-Z0-9]+)$" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.RewriteHandler.replacement=/" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.SSIResourceHandler.paths=/" >> $JVB_CONFIG

12
debian/rules vendored
View File

@@ -3,12 +3,22 @@
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
LANGUAGES := $(shell node -p "Object.keys(require('./lang/languages.json')).join(' ')")
COUNTRIES_DIR := node_modules/i18n-iso-countries/langs
%:
dh $@
# we skip making Makefile exists for updating browserify modules when developing
override_dh_auto_build:
override_dh_install:
override_dh_install: $(LANGUAGES)
dh_installdirs
dh_install -X/config.js -X/package.json
$(LANGUAGES):
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; \
fi;

View File

@@ -7,7 +7,7 @@ You can use the Jitsi Meet API to embed Jitsi Meet in to your application. You a
To embed Jitsi Meet in your application you need to add the Jitsi Meet API library:
```javascript
<script src="https://meet.jit.si/external_api.js"></script>
<script src='https://meet.jit.si/external_api.js'></script>
```
## API
@@ -16,7 +16,7 @@ To embed Jitsi Meet in your application you need to add the Jitsi Meet API libra
The next step for embedding Jitsi Meet is to create the Jitsi Meet API object.
Its constructor gets a number of options:
* **domain**: domain used to build the conference URL, "meet.jit.si" for
* **domain**: domain used to build the conference URL, 'meet.jit.si' for
example.
* **options**: object with properties - the optional arguments:
* **roomName**: (optional) name of the room to join.
@@ -29,46 +29,160 @@ Its constructor gets a number of options:
* **jwt**: (optional) [JWT](https://jwt.io/) token.
* **onload**: (optional) handler for the iframe onload event.
* **invitees**: (optional) Array of objects containing information about new participants that will be invited in the call.
* **devices**: (optional) A map containing information about the initial devices that will be used in the call.
Example:
```javascript
var domain = "meet.jit.si";
var options = {
roomName: "JitsiMeetAPIExample",
const domain = 'meet.jit.si';
const options = {
roomName: 'JitsiMeetAPIExample',
width: 700,
height: 700,
parentNode: document.querySelector('#meet')
}
var api = new JitsiMeetExternalAPI(domain, options);
};
const api = new JitsiMeetExternalAPI(domain, options);
```
You can set the initial media devices for the call:
```javascript
const domain = 'meet.jit.si';
const options = {
...
devices: {
audioInput: '<deviceLabel>',
audioOutput: '<deviceLabel>',
videoInput: '<deviceLabel>'
},
...
};
const api = new JitsiMeetExternalAPI(domain, options);
```
You can overwrite options set in [config.js] and [interface_config.js].
For example, to enable the filmstrip-only interface mode, you can use:
```javascript
var options = {
interfaceConfigOverwrite: {filmStripOnly: true}
const options = {
...
interfaceConfigOverwrite: { filmStripOnly: true },
...
};
var api = new JitsiMeetExternalAPI(domain, options);
const api = new JitsiMeetExternalAPI(domain, options);
```
You can also pass a jwt token to Jitsi Meet:
```javascript
var options = {
jwt: "<jwt_token>",
noSsl: false
const options = {
...
jwt: '<jwt_token>',
noSsl: false,
...
};
var api = new JitsiMeetExternalAPI(domain, options);
const api = new JitsiMeetExternalAPI(domain, options);
```
### Controlling the embedded Jitsi Meet Conference
Device management `JitsiMeetExternalAPI` methods:
* **getAvailableDevices** - Retrieve a list of available devices.
```javascript
api.getAvailableDevices().then(devices => {
// devices = {
// audioInput: [{
// deviceId: 'ID'
// groupId: 'grpID'
// kind: 'audioinput'
// label: 'label'
// },....],
// audioOutput: [{
// deviceId: 'ID'
// groupId: 'grpID'
// kind: 'audioOutput'
// label: 'label'
// },....],
// videoInput: [{
// deviceId: 'ID'
// groupId: 'grpID'
// kind: 'videoInput'
// label: 'label'
// },....]
// }
...
});
```
* **getCurrentDevices** - Retrieve a list with the devices that are currently selected.
```javascript
api.getCurrentDevices().then(devices => {
// devices = {
// audioInput: {
// deviceId: 'ID'
// groupId: 'grpID'
// kind: 'videoInput'
// label: 'label'
// },
// audioOutput: {
// deviceId: 'ID'
// groupId: 'grpID'
// kind: 'videoInput'
// label: 'label'
// },
// videoInput: {
// deviceId: 'ID'
// groupId: 'grpID'
// kind: 'videoInput'
// label: 'label'
// }
// }
...
});
```
* **isDeviceChangeAvailable** - Resolves with true if the device change is available and with false if not.
```javascript
// The accepted deviceType values are - 'output', 'input' or undefined.
api.isDeviceChangeAvailable(deviceType).then(isDeviceChangeAvailable => {
...
});
```
* **isDeviceListAvailable** - Resolves with true if the device list is available and with false if not.
```javascript
api.isDeviceListAvailable().then(isDeviceListAvailable => {
...
});
```
* **isMultipleAudioInputSupported** - Resolves with true if multiple audio input is supported and with false if not.
```javascript
api.isMultipleAudioInputSupported().then(isMultipleAudioInputSupported => {
...
});
```
* **setAudioInputDevice** - Sets the audio input device to the one with the label or id that is passed.
```javascript
api.setAudioInputDevice(deviceLabel, deviceId);
```
* **setAudioOutputDevice** - Sets the audio output device to the one with the label or id that is passed.
```javascript
api.setAudioOutputDevice(deviceLabel, deviceId);
```
* **setVideoInputDevice** - Sets the video input device to the one with the label or id that is passed.
```javascript
api.setVideoInputDevice(deviceLabel, deviceId);
```
You can control the embedded Jitsi Meet conference using the `JitsiMeetExternalAPI` object by using `executeCommand`:
```javascript
api.executeCommand(command, ...arguments)
api.executeCommand(command, ...arguments);
```
The `command` parameter is String object with the name of the command. The following commands are currently supported:
@@ -85,90 +199,128 @@ api.executeCommand('subject', 'New Conference Subject');
* **toggleAudio** - Mutes / unmutes the audio for the local participant. No arguments are required.
```javascript
api.executeCommand('toggleAudio')
api.executeCommand('toggleAudio');
```
* **toggleVideo** - Mutes / unmutes the video for the local participant. No arguments are required.
```javascript
api.executeCommand('toggleVideo')
api.executeCommand('toggleVideo');
```
* **toggleFilmStrip** - Hides / shows the filmstrip. No arguments are required.
```javascript
api.executeCommand('toggleFilmStrip')
api.executeCommand('toggleFilmStrip');
```
* **toggleChat** - Hides / shows the chat. No arguments are required.
```javascript
api.executeCommand('toggleChat')
api.executeCommand('toggleChat');
```
* **toggleShareScreen** - Starts / stops screen sharing. No arguments are required.
```javascript
api.executeCommand('toggleShareScreen')
api.executeCommand('toggleShareScreen');
```
* **toggleTileView** - Enter / exit tile view layout mode. No arguments are required.
```javascript
api.executeCommand('toggleTileView');
```
* **hangup** - Hangups the call. No arguments are required.
```javascript
api.executeCommand('hangup')
api.executeCommand('hangup');
```
* **email** - Changes the local email address. This command requires one argument - the new email address to be set.
```javascript
api.executeCommand('email', 'example@example.com')
api.executeCommand('email', 'example@example.com');
```
* **avatarUrl** - Changes the local avatar URL. This command requires one argument - the new avatar URL to be set.
```javascript
api.executeCommand('avatarUrl', 'https://avatars0.githubusercontent.com/u/3671647')
api.executeCommand('avatarUrl', 'https://avatars0.githubusercontent.com/u/3671647');
```
You can also execute multiple commands using the `executeCommands` method:
```javascript
api.executeCommands(commands)
api.executeCommands(commands);
```
The `commands` parameter is an object with the names of the commands as keys and the arguments for the commands as values:
```javascript
api.executeCommands({displayName: ['nickname'], toggleAudio: []});
api.executeCommands({
displayName: [ 'nickname' ],
toggleAudio: []
});
```
You can add event listeners to the embedded Jitsi Meet using the `addEventListener` method.
**NOTE: This method still exists but it is deprecated. JitsiMeetExternalAPI class extends [EventEmitter]. Use [EventEmitter] methods (`addListener` or `on`).**
```javascript
api.addEventListener(event, listener)
api.addEventListener(event, listener);
```
The `event` parameter is a String object with the name of the event.
The `listener` parameter is a Function object with one argument that will be notified when the event occurs with data related to the event.
The following events are currently supported:
* **cameraError** - event notifications about Jitsi-Meet having failed to access the camera. The listener will receive an object with the following structure:
```javascript
{
type: string, // A constant representing the overall type of the error.
message: string // Additional information about the error.
}
```
* **avatarChanged** - event notifications about avatar
changes. The listener will receive an object with the following structure:
```javascript
{
"id": id, // the id of the participant that changed his avatar.
"avatarURL": avatarURL // the new avatar URL.
id: string, // the id of the participant that changed his avatar.
avatarURL: string // the new avatar URL.
}
```
* **audioAvailabilityChanged** - event notifications about audio availability status changes. The listener will receive an object with the following structure:
```javascript
{
"available": available // new available status - boolean
available: boolean // new available status - boolean
}
```
* **audioMuteStatusChanged** - event notifications about audio mute status changes. The listener will receive an object with the following structure:
```javascript
{
"muted": muted // new muted status - boolean
muted: boolean // new muted status - boolean
}
```
* **micError** - event notifications about Jitsi-Meet having failed to access the mic. The listener will receive an object with the following structure:
```javascript
{
type: string, // A constant representing the overall type of the error.
message: string // Additional information about the error.
}
```
* **screenSharingStatusChanged** - receives event notifications about turning on/off the local user screen sharing. The listener will receive object with the following structure:
```javascript
{
"on": on //whether screen sharing is on
on: boolean, //whether screen sharing is on
details: {
// From where the screen sharing is capturing, if known. Values which are
// passed include 'window', 'screen', 'proxy', 'device'. The value undefined
// will be passed if the source type is unknown or screen share is off.
sourceType: string|undefined
}
}
```
* **tileViewChanged** - event notifications about tile view layout mode being entered or exited. The listener will receive object with the following structure:
```javascript
{
enabled: boolean, // whether tile view is not displayed or not
}
```
@@ -176,9 +328,9 @@ changes. The listener will receive an object with the following structure:
messages. The listener will receive an object with the following structure:
```javascript
{
"from": from, // The id of the user that sent the message
"nick": nick, // the nickname of the user that sent the message
"message": txt // the text of the message
from: string, // The id of the user that sent the message
nick: string, // the nickname of the user that sent the message
message: string // the text of the message
}
```
@@ -186,7 +338,7 @@ messages. The listener will receive an object with the following structure:
messages. The listener will receive an object with the following structure:
```javascript
{
"message": txt // the text of the message
message: string // the text of the message
}
```
@@ -194,73 +346,87 @@ messages. The listener will receive an object with the following structure:
changes. The listener will receive an object with the following structure:
```javascript
{
"id": id, // the id of the participant that changed his display name
"displayname": displayName // the new display name
id: string, // the id of the participant that changed his display name
displayname: string // the new display name
}
```
* **deviceListChanged** - event notifications about device list changes. The listener will receive an object with the following structure:
```javascript
{
devices: Object // the new list of available devices.
}
```
NOTE: The devices object has the same format as the getAvailableDevices result format.
* **emailChange** - event notifications about email
changes. The listener will receive an object with the following structure:
```javascript
{
"id": id, // the id of the participant that changed his email
"email": email // the new email
id: string, // the id of the participant that changed his email
email: string // the new email
}
```
* **filmstripDisplayChanged** - event notifications about the visibility of the filmstrip being updated.
```javascript
{
visible: boolean // Whether or not the filmstrip is displayed or hidden.
}
```
* **participantJoined** - event notifications about new participants who join the room. The listener will receive an object with the following structure:
```javascript
{
"id": id, // the id of the participant
"displayName": displayName // the display name of the participant
id: string, // the id of the participant
displayName: string // the display name of the participant
}
```
* **participantLeft** - event notifications about participants that leave the room. The listener will receive an object with the following structure:
```javascript
{
"id": id // the id of the participant
id: string // the id of the participant
}
```
* **videoConferenceJoined** - event notifications fired when the local user has joined the video conference. The listener will receive an object with the following structure:
```javascript
{
"roomName": room, // the room name of the conference
"id": id, // the id of the local participant
"displayName": displayName, // the display name of the local participant
"avatarURL": avatarURL // the avatar URL of the local participant
roomName: string, // the room name of the conference
id: string, // the id of the local participant
displayName: string, // the display name of the local participant
avatarURL: string // the avatar URL of the local participant
}
```
* **videoConferenceLeft** - event notifications fired when the local user has left the video conference. The listener will receive an object with the following structure:
```javascript
{
"roomName": room // the room name of the conference
roomName: string // the room name of the conference
}
```
* **videoAvailabilityChanged** - event notifications about video availability status changes. The listener will receive an object with the following structure:
```javascript
{
"available": available // new available status - boolean
available: boolean // new available status - boolean
}
```
* **videoMuteStatusChanged** - event notifications about video mute status changes. The listener will receive an object with the following structure:
```javascript
{
"muted": muted // new muted status - boolean
muted: boolean // new muted status - boolean
}
```
* **readyToClose** - event notification fired when Jitsi Meet is ready to be closed (hangup operations are completed).
* **subjectChange** - event notifications about subject of conference changes.
* **subjectChange** - event notifications about subject of conference changes.
The listener will receive an object with the following structure:
```javascript
{
"subject": subject // the new subject
subject: string // the new subject
}
```
@@ -282,79 +448,80 @@ function outgoingMessageListener(object)
api.addEventListeners({
incomingMessage: incomingMessageListener,
outgoingMessage: outgoingMessageListener})
outgoingMessage: outgoingMessageListener
});
```
If you want to remove a listener you can use `removeEventListener` method with argument the name of the event.
**NOTE: This method still exists but it is deprecated. JitsiMeetExternalAPI class extends [EventEmitter]. Use [EventEmitter] methods( `removeListener`).**
```javascript
api.removeEventListener("incomingMessage");
api.removeEventListener('incomingMessage');
```
If you want to remove more than one event you can use `removeEventListeners` method with an Array with the names of the events as an argument.
**NOTE: This method still exists but it is deprecated. JitsiMeetExternalAPI class extends [EventEmitter]. Use [EventEmitter] methods.**
```javascript
api.removeEventListeners(["incomingMessage", "outgoingMessageListener"]);
api.removeEventListeners([ 'incomingMessage', 'outgoingMessageListener' ]);
```
You can get the number of participants in the conference with the following API function:
```javascript
var numberOfParticipants = api.getNumberOfParticipants();
const numberOfParticipants = api.getNumberOfParticipants();
```
You can get the avatar URL of a participant in the conference with the following API function:
```javascript
var avatarURL = api.getAvatarURL(participantId);
const avatarURL = api.getAvatarURL(participantId);
```
You can get the display name of a participant in the conference with the following API function:
```javascript
var displayName = api.getDisplayName(participantId);
const displayName = api.getDisplayName(participantId);
```
You can get the email of a participant in the conference with the following API function:
```javascript
var email = api.getEmail(participantId);
const email = api.getEmail(participantId);
```
You can get the iframe HTML element where Jitsi Meet is loaded with the following API function:
```javascript
var iframe = api.getIFrame();
const iframe = api.getIFrame();
```
You can check whether the audio is muted with the following API function:
```javascript
api.isAudioMuted().then(function(muted) {
api.isAudioMuted().then(muted => {
...
});
```
You can check whether the video is muted with the following API function:
```javascript
api.isVideoMuted().then(function(muted) {
api.isVideoMuted().then(muted => {
...
});
```
You can check whether the audio is available with the following API function:
```javascript
api.isAudioAvailable().then(function(available) {
api.isAudioAvailable().then(available => {
...
});
```
You can check whether the video is available with the following API function:
```javascript
api.isVideoAvailable().then(function(available) {
api.isVideoAvailable().then(available => {
...
});
```
You can invite new participants to the call with the following API function:
```javascript
api.invite([{...}, {...}, {...}]).then(function() {
api.invite([ {...}, {...}, {...} ]).then(() => {
// success
}).catch(function() {
}).catch(() => {
// failure
});
```
@@ -362,7 +529,7 @@ api.invite([{...}, {...}, {...}]).then(function() {
You can remove the embedded Jitsi Meet Conference with the following API function:
```javascript
api.dispose()
api.dispose();
```
NOTE: It's a good practice to remove the conference before the page is unloaded.

View File

@@ -71,7 +71,7 @@ paths:
$ref: "#/definitions/ConferenceMapperDetails"
405:
description: "Invalid input"
/phoneNumberList:
get:
tags:
@@ -96,7 +96,7 @@ securityDefinitions:
name: "Authorization"
in: "header"
definitions:
ConferenceMapperRequest:
description: "Request to create or find a conference mapping"
type: "object"
@@ -114,7 +114,7 @@ definitions:
domain:
type: "string"
description: "Domain part of the conference. Used if 'conference' is not provided. Defaults to domain of the API endpoint. Used to generate a 'conference' value (search by conference)"
ConferenceMapperDetails:
description: "Conference mapping between conference JID and numeric ID"
type: "object"
@@ -126,22 +126,26 @@ definitions:
type: "string"
format: "JID"
description: "Full JID for the conference OR boolean false if no conference was found (search by ID)"
PhoneNumberList:
type: "object"
properties:
numbersEnabled:
type: "boolean"
description: "Control flag for Jitsi Meet user interface. Must be set to true for Jitsi Meet to display phone-in UI elements"
numbers:
description: "List of dial in numbers for the conference."
type: "array"
items:
type: "object"
description: "Keys are Country Names, each value is an array of phone numbers"
additionalProperties:
type: "array"
items:
properties:
countryCode:
type: "string"
format: "phone"
description: "ISO 3166-1 country code. Alpha-2 supported."
default:
type: "boolean"
description: "Whether this number is the default one to show. Optional."
formattedNumber:
type: "string"
description: "The formatted telephone number to show."
tollFree:
type: "boolean"
description: "Whether the number is toll free number."
externalDocs:
description: "Find out more about the Jitsi Cloud API"
url: "https://jitsi.org/CloudAPI"
url: "https://jitsi.org/CloudAPI"

View File

@@ -26,6 +26,10 @@ server {
alias /etc/jitsi/meet/jitsi-meet.example.com-config.js;
}
location /external_api.js {
alias /usr/share/jitsi-meet/libs/external_api.min.js;
}
location ~ ^/([a-zA-Z0-9=\?]+)$ {
rewrite ^/(.*)$ / break;
}
@@ -34,11 +38,6 @@ server {
ssi on;
}
# Backward compatibility
location ~ /external_api.* {
root /usr/share/jitsi-meet/libs;
}
# BOSH
location /http-bind {
proxy_pass http://localhost:5280/http-bind;

View File

@@ -36,6 +36,11 @@
Require all granted
</Location>
Alias "/external_api.js" "/usr/share/jitsi-meet/libs/external_api.min.js"
<Location /external_api.js>
Require all granted
</Location>
ProxyPreserveHost on
ProxyPass /http-bind http://localhost:5280/http-bind/
ProxyPassReverse /http-bind http://localhost:5280/http-bind/

7
doc/faq.md Normal file
View File

@@ -0,0 +1,7 @@
**1. How to tell if my server instance is behind NAT?**
A. In general, if the tool ifconfig (or ipconfig) shows the assigned IP address to be some local address (10.x.x.x or 192.x.x.x) but you know that its public IP address is different from that, the server is most probably behind NAT
**2. Clients could communicate well in room created at meet.jit.si . The same clients still could connect to my self-hosted instance but can neither hear nor see one another. What's wrong?**
A. Most probably, the server is behind NAT. See this [resolved question](https://community.jitsi.org/t/cannot-see-video-or-hear-audio-on-self-hosted-instance/). You need to follow the steps detailed [here](https://github.com/jitsi/ice4j/blob/master/doc/quick-install.md#Advanced-configuration)

38
doc/integrations.md Normal file
View File

@@ -0,0 +1,38 @@
Document describing enabling various jitsi-meet integrations.
## Creating the Google API client for Google Calendar and Youtube integration
1. Log into a Google admin account.
1. Go to Google cloud platform dashboard. https://console.cloud.google.com/apis/dashboard
1. In the Select a Project dropdown, click New Project.
1. Give the project a name.
1. Proceed to the Credentials settings of the new project.
1. In the Credentials tab of the Credentials settings, click Create Credentials and select the type OAuth client ID.
1. Proceed with creating a Web application and add the domains (origins) on which the application will be hosted. Local development environments (http://localhost:8000 for example) can be added here.
1. While still in the Google cloud platform dashboard, click the Library settings for the calendar project.
1. Search for the Google Calendar API (used for calendar accessing), click its result, and enable it.
1. Do the same for YouTube Data API v3
## Creating the Microsoft app for Microsoft Outlook integration
1. Go to https://apps.dev.microsoft.com/
1. Proceed through the "Add an app" flow. Once created, a page with several Graph Permissions fields should display.
1. Under "Platforms" add "Web"
1. Add a redirect URL for the Microsoft auth flow to visit once a user has confirmed authentication. Target domain if available is just 'yourdomain.com' (the deployment address) and the redirect URL is `https://yourdomain.com/static/msredirect.html`.
1. Add Microsoft Graph delegated permissions, if this option is available: Calendars.Read, Calendars.ReadWrite, Calendars.Read.Shared, Calendars.ReadWrite.Shared.
1. Check `Allow Implicit Flow` (and `Restrict token issuing to this app` if available).
1. Save the changes.
## Creating the Dropbox app for Dropbox recording integration
1. You need a Dropbox account (If you don't already have one, you can sign up for a free account [here](https://www.dropbox.com/register).)
1. Create new App as described in [Getting Started Guide](https://www.dropbox.com/developers/reference/getting-started?_tk=guides_lp&_ad=guides2&_camp=get_started#app%20console) in App Console section.
1. Choose
1. 'Dropbox API - For apps that need to access files in Dropbox.'
1. 'App folder Access to a single folder created specifically for your app.'
1. Fill in the name of your app
1. You need only, the newly created App key, goes in config.js in
```
dropbox: {
appKey: '__dropbox_app_key__'
}
```
1. Add your Redirect URIs in the form `https://yourdeployment.com//static/oauth.html`
1. Fill in Branding

View File

@@ -236,19 +236,12 @@ invoke-rc.d nginx restart
```
## Running behind NAT
Jitsi-Videobridge can run behind a NAT, provided that all required ports are routed (forwarded) to the machine that it runs on. By default these ports are (TCP/443 or TCP/4443 and UDP 10000-20000).
Jitsi-Videobridge can run behind a NAT, provided that all required ports are routed (forwarded) to the machine that it runs on. By default these ports are (TCP/443 or TCP/4443 and UDP 10000).
The following extra lines need to be added the file `~/.sip-communicator/sip-communicator.properties` (in the home directory of the user running the videobridge):
```
org.jitsi.videobridge.NAT_HARVESTER_LOCAL_ADDRESS=<Local.IP.Address>
org.jitsi.videobridge.NAT_HARVESTER_PUBLIC_ADDRESS=<Public.IP.Address>
```
So the file should look like this at the end:
```
org.jitsi.impl.neomedia.transform.srtp.SRTPCryptoContext.checkReplay=false
org.jitsi.videobridge.NAT_HARVESTER_LOCAL_ADDRESS=<Local.IP.Address>
org.jitsi.videobridge.NAT_HARVESTER_PUBLIC_ADDRESS=<Public.IP.Address>
org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=<Local.IP.Address>
org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=<Public.IP.Address>
```
# Hold your first conference

View File

@@ -5,7 +5,7 @@ signed Android build for that, that can be a debug self-signed build too, just
retrieve the signing hash. The key hash of an already signed ap can be obtained
as follows (on macOS): ```keytool -list -printcert -jarfile the-app.apk```
- Place the generated ```google-services.json``` file in ```android/app```
for Android and the ```GoogleService-Info.plist``` into ```ios/app/src``` for
for Android and the ```GoogleService-Info.plist``` into ```ios/app``` for
iOS (you can stop at that step, no need for the driver and the code changes they
suggest in the wizard).
- You may want to exclude these files in YOUR GIT config (do not exclude them in

View File

@@ -1,13 +1,14 @@
# Jitsi Meet apps for Android and iOS
Jitsi Meet can also be built as a standalone app for Android or iOS. It uses the
Jitsi Meet can be built as a standalone app for Android or iOS. It uses the
[React Native] framework.
**If you want to rebuild the SDK yourself look in [Android README] or [iOS README].**
First make sure the [React Native dependencies] are installed.
**NOTE**: This document assumes the app is being built on a macOS system.
**NOTE**: Node 6.X and npm 3.X are recommended for building.
**NOTE**: Node 10.X and npm 6.X are recommended for building.
## iOS
@@ -21,8 +22,6 @@ First make sure the [React Native dependencies] are installed.
npm install -g ios-deploy
```
You may need to add ```--unsafe-perm=true``` if you are running on [Mac OS 10.11 or greater](https://github.com/phonegap/ios-deploy#os-x-1011-el-capitan-or-greater).
- Install main dependencies:
```bash
@@ -68,9 +67,7 @@ First make sure the [React Native dependencies] are installed.
3. Other remarks
It's likely you'll need to change the bundle ID for deploying to a device
because the default bundle ID points to the application signed by Atlassian.
It's likely you'll need to change the bundle ID for deploying to a device.
This can be changed in the "General" tab. Under "Identity" set
"Bundle Identifier" to a different value, and adjust the "Team" in the
"Signing" section to match your own.
@@ -102,6 +99,8 @@ code is being interpreted by Chrome's V8 engine, instead of JSCore which React
Native uses. It's important to keep this in mind due to potential differences in
supported JavaScript features.
[Android README]: https://github.com/jitsi/jitsi-meet/blob/master/android/README.md
[iOS README]: https://github.com/jitsi/jitsi-meet/blob/master/ios/README.md
[Android Studio]: https://developer.android.com/studio/index.html
[debugging]: https://facebook.github.io/react-native/docs/debugging.html
[React Native]: https://facebook.github.io/react-native/

View File

@@ -55,7 +55,7 @@ Simply run the following in your shell
```
#### Advanced configuration
If installation is on a machine behind NAT further configuration of jitsi-videobridge is needed in order for it to be accessible.
If installation is on a machine [behind NAT](https://github.com/jitsi/jitsi-meet/blob/master/doc/faq.md) further configuration of jitsi-videobridge is needed in order for it to be accessible.
Provided that all required ports are routed (forwarded) to the machine that it runs on. By default these ports are (TCP/443 or TCP/4443 and UDP 10000).
The following extra lines need to be added the file `/etc/jitsi/videobridge/sip-communicator.properties`:
```

View File

@@ -19,10 +19,6 @@ edit /etc/jitsi/jicofo/sip-communicator.properties (or similar), set the appropr
peopleSearchQueryTypes: ['conferenceRooms'],
peopleSearchUrl: 'https://api.yourdomain.com/testpath/searchpeople',
```
- interface_config.js:
```
ADD_PEOPLE_APP_NAME: 'Jitsi'
```
The combination of the above settings and providing a jwt token will enable a button under invite option which will show the dialog 'Add people'.

View File

@@ -1,5 +1,5 @@
// flow-typed signature: d71d314ca25fc6c20610a3ba80af9df0
// flow-typed version: 9698a46399/jquery_v3.x.x/flow_>=v0.28.x
// flow-typed signature: f26fda66e3a551aef37d3b0f53058e6a
// flow-typed version: 44ad941b7a/jquery_v3.x.x/flow_>=v0.28.x
/* eslint-disable max-len, no-unused-vars, flowtype/no-weak-types */
@@ -364,6 +364,13 @@ declare class JQueryGenericPromise<T> {
failFilter?: (...reasons: any[]) => any,
progressFilter?: (...progression: any[]) => any
): JQueryPromise<void>;
/**
* Add handlers to be called when the Deferred object is rejected.
*
* @param failFilter An function that is called when the Deferred is rejected.
*/
catch(failFilter: (...reasons: any[]) => any): JQueryPromise<T>;
}
/**
@@ -822,7 +829,7 @@ declare class JQueryStatic {
*/
get(
url: string,
data?: Object | string,
data?: {} | string,
success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any,
dataType?: string
): JQueryXHR;
@@ -845,7 +852,7 @@ declare class JQueryStatic {
*/
getJSON(
url: string,
data?: Object | string,
data?: {} | string,
success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any
): JQueryXHR;
/**
@@ -886,7 +893,7 @@ declare class JQueryStatic {
*/
post(
url: string,
data?: Object | string,
data?: {} | string,
success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any,
dataType?: string
): JQueryXHR;
@@ -967,7 +974,7 @@ declare class JQueryStatic {
* @param html A string defining a single, standalone, HTML element (e.g. <div/> or <div></div>).
* @param attributes An object of attributes, events, and methods to call on the newly-created element.
*/
(html: string, attributes: Object): JQuery;
(html: string, attributes: {}): JQuery;
/**
* Relinquish jQuery's control of the $ variable.
@@ -1425,7 +1432,7 @@ declare class JQuery {
*/
load(
url: string,
data?: string | Object,
data?: string | {},
complete?: (
responseText: string,
textStatus: string,
@@ -1482,7 +1489,7 @@ declare class JQuery {
*
* @param attributes An object of attribute-value pairs to set.
*/
attr(attributes: Object): JQuery;
attr(attributes: {}): JQuery;
/**
* Get the value of an attribute for the first element in the set of matched elements.
*
@@ -1532,7 +1539,7 @@ declare class JQuery {
*
* @param properties An object of property-value pairs to set.
*/
prop(properties: Object): JQuery;
prop(properties: {}): JQuery;
/**
* Set one or more properties for the set of matched elements.
*
@@ -1598,7 +1605,7 @@ declare class JQuery {
/**
* Get the current value of the first element in the set of matched elements.
*/
val(_: void): any;
val(_: void): string | string[] | number;
/**
* Set the value of each element in the set of matched elements.
*
@@ -1634,7 +1641,7 @@ declare class JQuery {
*
* @param properties An object of property-value pairs to set.
*/
css(properties: Object): JQuery;
css(properties: {}): JQuery;
/**
* Get the value of style properties for the first element in the set of matched elements.
*
@@ -1847,7 +1854,7 @@ declare class JQuery {
* @param complete A function to call once the animation is complete.
*/
animate(
properties: Object,
properties: {},
duration?: string | number,
complete?: Function
): JQuery;
@@ -1860,7 +1867,7 @@ declare class JQuery {
* @param complete A function to call once the animation is complete.
*/
animate(
properties: Object,
properties: {},
duration?: string | number,
easing?: string,
complete?: Function
@@ -1871,7 +1878,7 @@ declare class JQuery {
* @param properties An object of CSS properties and values that the animation will move toward.
* @param options A map of additional options to pass to the method.
*/
animate(properties: Object, options: JQueryAnimationOptions): JQuery;
animate(properties: {}, options: JQueryAnimationOptions): JQuery;
/**
* Set a timer to delay execution of subsequent items in the queue.
@@ -2920,14 +2927,14 @@ declare class JQuery {
* @param eventType A string containing a JavaScript event type, such as click or submit.
* @param extraParameters Additional parameters to pass along to the event handler.
*/
trigger(eventType: string, extraParameters?: any[] | Object): JQuery;
trigger(eventType: string, extraParameters?: any[] | {}): JQuery;
/**
* Execute all handlers and behaviors attached to the matched elements for the given event type.
*
* @param event A jQuery.Event object.
* @param extraParameters Additional parameters to pass along to the event handler.
*/
trigger(event: JQueryEventObject, extraParameters?: any[] | Object): JQuery;
trigger(event: JQueryEventObject, extraParameters?: any[] | {}): JQuery;
/**
* Execute all handlers attached to an element for an event.

File diff suppressed because it is too large Load Diff

View File

@@ -1,329 +0,0 @@
// flow-typed signature: c30aa20539f52183d4d30dd36d8ab9c2
// flow-typed version: 886cf7c002/moment_v2.3.x/flow_>=v0.63.x
type moment$MomentOptions = {
y?: number | string,
year?: number | string,
years?: number | string,
M?: number | string,
month?: number | string,
months?: number | string,
d?: number | string,
day?: number | string,
days?: number | string,
date?: number | string,
h?: number | string,
hour?: number | string,
hours?: number | string,
m?: number | string,
minute?: number | string,
minutes?: number | string,
s?: number | string,
second?: number | string,
seconds?: number | string,
ms?: number | string,
millisecond?: number | string,
milliseconds?: number | string
};
type moment$MomentObject = {
years: number,
months: number,
date: number,
hours: number,
minutes: number,
seconds: number,
milliseconds: number
};
type moment$MomentCreationData = {
input: string,
format: string,
locale: Object,
isUTC: boolean,
strict: boolean
};
type moment$CalendarFormat = string | ((moment: moment$Moment) => string);
type moment$CalendarFormats = {
sameDay?: moment$CalendarFormat,
nextDay?: moment$CalendarFormat,
nextWeek?: moment$CalendarFormat,
lastDay?: moment$CalendarFormat,
lastWeek?: moment$CalendarFormat,
sameElse?: moment$CalendarFormat
};
declare class moment$LocaleData {
months(moment: moment$Moment): string,
monthsShort(moment: moment$Moment): string,
monthsParse(month: string): number,
weekdays(moment: moment$Moment): string,
weekdaysShort(moment: moment$Moment): string,
weekdaysMin(moment: moment$Moment): string,
weekdaysParse(weekDay: string): number,
longDateFormat(dateFormat: string): string,
isPM(date: string): boolean,
meridiem(hours: number, minutes: number, isLower: boolean): string,
calendar(
key:
| "sameDay"
| "nextDay"
| "lastDay"
| "nextWeek"
| "prevWeek"
| "sameElse",
moment: moment$Moment
): string,
relativeTime(
number: number,
withoutSuffix: boolean,
key: "s" | "m" | "mm" | "h" | "hh" | "d" | "dd" | "M" | "MM" | "y" | "yy",
isFuture: boolean
): string,
pastFuture(diff: any, relTime: string): string,
ordinal(number: number): string,
preparse(str: string): any,
postformat(str: string): any,
week(moment: moment$Moment): string,
invalidDate(): string,
firstDayOfWeek(): number,
firstDayOfYear(): number
}
declare class moment$MomentDuration {
humanize(suffix?: boolean): string,
milliseconds(): number,
asMilliseconds(): number,
seconds(): number,
asSeconds(): number,
minutes(): number,
asMinutes(): number,
hours(): number,
asHours(): number,
days(): number,
asDays(): number,
months(): number,
asMonths(): number,
years(): number,
asYears(): number,
add(value: number | moment$MomentDuration | Object, unit?: string): this,
subtract(value: number | moment$MomentDuration | Object, unit?: string): this,
as(unit: string): number,
get(unit: string): number,
toJSON(): string,
toISOString(): string,
isValid(): boolean
}
declare class moment$Moment {
static ISO_8601: string,
static (
string?: string,
format?: string | Array<string>,
strict?: boolean
): moment$Moment,
static (
string?: string,
format?: string | Array<string>,
locale?: string,
strict?: boolean
): moment$Moment,
static (
initDate: ?Object | number | Date | Array<number> | moment$Moment | string
): moment$Moment,
static unix(seconds: number): moment$Moment,
static utc(): moment$Moment,
static utc(number: number | Array<number>): moment$Moment,
static utc(
str: string,
str2?: string | Array<string>,
str3?: string
): moment$Moment,
static utc(moment: moment$Moment): moment$Moment,
static utc(date: Date): moment$Moment,
static parseZone(): moment$Moment,
static parseZone(rawDate: string): moment$Moment,
static parseZone(
rawDate: string,
format: string | Array<string>
): moment$Moment,
static parseZone(
rawDate: string,
format: string,
strict: boolean
): moment$Moment,
static parseZone(
rawDate: string,
format: string,
locale: string,
strict: boolean
): moment$Moment,
isValid(): boolean,
invalidAt(): 0 | 1 | 2 | 3 | 4 | 5 | 6,
creationData(): moment$MomentCreationData,
millisecond(number: number): this,
milliseconds(number: number): this,
millisecond(): number,
milliseconds(): number,
second(number: number): this,
seconds(number: number): this,
second(): number,
seconds(): number,
minute(number: number): this,
minutes(number: number): this,
minute(): number,
minutes(): number,
hour(number: number): this,
hours(number: number): this,
hour(): number,
hours(): number,
date(number: number): this,
dates(number: number): this,
date(): number,
dates(): number,
day(day: number | string): this,
days(day: number | string): this,
day(): number,
days(): number,
weekday(number: number): this,
weekday(): number,
isoWeekday(number: number): this,
isoWeekday(): number,
dayOfYear(number: number): this,
dayOfYear(): number,
week(number: number): this,
weeks(number: number): this,
week(): number,
weeks(): number,
isoWeek(number: number): this,
isoWeeks(number: number): this,
isoWeek(): number,
isoWeeks(): number,
month(number: number): this,
months(number: number): this,
month(): number,
months(): number,
quarter(number: number): this,
quarter(): number,
year(number: number): this,
years(number: number): this,
year(): number,
years(): number,
weekYear(number: number): this,
weekYear(): number,
isoWeekYear(number: number): this,
isoWeekYear(): number,
weeksInYear(): number,
isoWeeksInYear(): number,
get(string: string): number,
set(unit: string, value: number): this,
set(options: { [unit: string]: number }): this,
static max(...dates: Array<moment$Moment>): moment$Moment,
static max(dates: Array<moment$Moment>): moment$Moment,
static min(...dates: Array<moment$Moment>): moment$Moment,
static min(dates: Array<moment$Moment>): moment$Moment,
add(
value: number | moment$MomentDuration | moment$Moment | Object,
unit?: string
): this,
subtract(
value: number | moment$MomentDuration | moment$Moment | string | Object,
unit?: string
): this,
startOf(unit: string): this,
endOf(unit: string): this,
local(): this,
utc(): this,
utcOffset(
offset: number | string,
keepLocalTime?: boolean,
keepMinutes?: boolean
): this,
utcOffset(): number,
format(format?: string): string,
fromNow(removeSuffix?: boolean): string,
from(
value: moment$Moment | string | number | Date | Array<number>,
removePrefix?: boolean
): string,
toNow(removePrefix?: boolean): string,
to(
value: moment$Moment | string | number | Date | Array<number>,
removePrefix?: boolean
): string,
calendar(refTime?: any, formats?: moment$CalendarFormats): string,
diff(
date: moment$Moment | string | number | Date | Array<number>,
format?: string,
floating?: boolean
): number,
valueOf(): number,
unix(): number,
daysInMonth(): number,
toDate(): Date,
toArray(): Array<number>,
toJSON(): string,
toISOString(
keepOffset?: boolean
): string,
toObject(): moment$MomentObject,
isBefore(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isSame(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isAfter(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isSameOrBefore(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isSameOrAfter(
date?: moment$Moment | string | number | Date | Array<number>,
units?: ?string
): boolean,
isBetween(
fromDate: moment$Moment | string | number | Date | Array<number>,
toDate?: ?moment$Moment | string | number | Date | Array<number>,
granularity?: ?string,
inclusion?: ?string
): boolean,
isDST(): boolean,
isDSTShifted(): boolean,
isLeapYear(): boolean,
clone(): moment$Moment,
static isMoment(obj: any): boolean,
static isDate(obj: any): boolean,
static locale(locale: string, localeData?: Object): string,
static updateLocale(locale: string, localeData?: ?Object): void,
static locale(locales: Array<string>): string,
locale(locale: string, customization?: Object | null): moment$Moment,
locale(): string,
static months(): Array<string>,
static monthsShort(): Array<string>,
static weekdays(): Array<string>,
static weekdaysShort(): Array<string>,
static weekdaysMin(): Array<string>,
static months(): string,
static monthsShort(): string,
static weekdays(): string,
static weekdaysShort(): string,
static weekdaysMin(): string,
static localeData(key?: string): moment$LocaleData,
static duration(
value: number | Object | string,
unit?: string
): moment$MomentDuration,
static isDuration(obj: any): boolean,
static normalizeUnits(unit: string): string,
static invalid(object: any): moment$Moment
}
declare module "moment" {
declare module.exports: Class<moment$Moment>;
}

View File

@@ -1,192 +1,276 @@
// flow-typed signature: d4e793bc07ef1dc9906a244b12960f7b
// flow-typed version: cf33ff8762/react-redux_v5.x.x/flow_>=v0.63.0
// flow-typed signature: f06f00c3ad0cfedb90c0c6de04b219f3
// flow-typed version: 3a6d556e4b/react-redux_v5.x.x/flow_>=v0.89.x
import type { Dispatch, Store } from "redux";
/**
The order of type arguments for connect() is as follows:
declare module "react-redux" {
import type { ComponentType, ElementConfig } from 'react';
connect<Props, OwnProps, StateProps, DispatchProps, State, Dispatch>(…)
declare export class Provider<S, A> extends React$Component<{
store: Store<S, A>,
children?: any
}> {}
In Flow v0.89 only the first two are mandatory to specify. Other 4 can be repaced with the new awesome type placeholder:
declare export function createProvider(
storeKey?: string,
subKey?: string
): Provider<*, *>;
connect<Props, OwnProps, _, _, _, _>(…)
/*
But beware, in case of weird type errors somewhere in random places
just type everything and get to a green field and only then try to
remove the definitions you see bogus.
Decrypting the abbreviations:
WC = Component being wrapped
S = State
A = Action
D = Dispatch
OP = OwnProps
SP = StateProps
DP = DispatchProps
MP = Merge props
MDP = Map dispatch to props object
RSP = Returned state props
RDP = Returned dispatch props
RMP = Returned merge props
CP = Props for returned component
Com = React Component
*/
ST = Static properties of Com
EFO = Extra factory options (used only in connectAdvanced)
*/
declare type MapStateToProps<S: Object, SP: Object, RSP: Object> = (state: S, props: SP) => RSP;
declare module "react-redux" {
// ------------------------------------------------------------
// Typings for connect()
// ------------------------------------------------------------
declare type MapDispatchToProps<A, OP: Object, RDP: Object> = (dispatch: Dispatch<A>, ownProps: OP) => RDP;
declare type MergeProps<SP: Object, DP: Object, MP: Object, RMP: Object> = (
stateProps: SP,
dispatchProps: DP,
ownProps: MP
) => RMP;
declare type ConnectOptions<S: Object, OP: Object, RSP: Object, RMP: Object> = {|
declare export type Options<S, OP, SP, MP> = {|
pure?: boolean,
withRef?: boolean,
areStatesEqual?: (next: S, prev: S) => boolean,
areOwnPropsEqual?: (next: OP, prev: OP) => boolean,
areStatePropsEqual?: (next: RSP, prev: RSP) => boolean,
areMergedPropsEqual?: (next: RMP, prev: RMP) => boolean,
storeKey?: string
areStatePropsEqual?: (next: SP, prev: SP) => boolean,
areMergedPropsEqual?: (next: MP, prev: MP) => boolean,
storeKey?: string,
|};
declare type OmitDispatch<Component> = $Diff<Component, {dispatch: Dispatch<*>}>;
declare type MapStateToProps<-S, -OP, +SP> =
| ((state: S, ownProps: OP) => SP)
// If you want to use the factory function but get a strange error
// like "function is not an object" then just type the factory function
// like this:
// const factory: (State, OwnProps) => (State, OwnProps) => StateProps
// and provide the StateProps type to the SP type parameter.
| ((state: S, ownProps: OP) => (state: S, ownProps: OP) => SP);
declare export function connect<
Com: ComponentType<*>,
declare type Bind<D> = <A, R>((...A) => R) => (...A) => $Call<D, R>;
declare type MapDispatchToPropsFn<D, -OP, +DP> =
| ((dispatch: D, ownProps: OP) => DP)
// If you want to use the factory function but get a strange error
// like "function is not an object" then just type the factory function
// like this:
// const factory: (Dispatch, OwnProps) => (Dispatch, OwnProps) => DispatchProps
// and provide the DispatchProps type to the DP type parameter.
| ((dispatch: D, ownProps: OP) => (dispatch: D, ownProps: OP) => DP);
declare class ConnectedComponent<OP, +WC> extends React$Component<OP> {
static +WrappedComponent: WC;
getWrappedInstance(): React$ElementRef<WC>;
}
// The connection of the Wrapped Component and the Connected Component
// happens here in `MP: P`. It means that type wise MP belongs to P,
// so to say MP >= P.
declare type Connector<P, OP, MP: P> = <WC: React$ComponentType<P>>(
WC,
) => Class<ConnectedComponent<OP, WC>> & WC;
// No `mergeProps` argument
// Got error like inexact OwnProps is incompatible with exact object type?
// Just make the OP parameter for `connect()` an exact object.
declare type MergeOP<OP, D> = {| ...$Exact<OP>, dispatch: D |};
declare type MergeOPSP<OP, SP, D> = {| ...$Exact<OP>, ...SP, dispatch: D |};
declare type MergeOPDP<OP, DP> = {| ...$Exact<OP>, ...DP |};
declare type MergeOPSPDP<OP, SP, DP> = {| ...$Exact<OP>, ...SP, ...DP |};
declare export function connect<-P, -OP, -SP, -DP, -S, -D>(
mapStateToProps?: null | void,
mapDispatchToProps?: null | void,
mergeProps?: null | void,
options?: ?Options<S, OP, {||}, MergeOP<OP, D>>,
): Connector<P, OP, MergeOP<OP, D>>;
declare export function connect<-P, -OP, -SP, -DP, -S, -D>(
// If you get error here try adding return type to your mapStateToProps function
mapStateToProps: MapStateToProps<S, OP, SP>,
mapDispatchToProps?: null | void,
mergeProps?: null | void,
options?: ?Options<S, OP, SP, MergeOPSP<OP, SP, D>>,
): Connector<P, OP, MergeOPSP<OP, SP, D>>;
// In this case DP is an object of functions which has been bound to dispatch
// by the given mapDispatchToProps function.
declare export function connect<-P, -OP, -SP, -DP, S, D>(
mapStateToProps: null | void,
mapDispatchToProps: MapDispatchToPropsFn<D, OP, DP>,
mergeProps?: null | void,
options?: ?Options<S, OP, {||}, MergeOPDP<OP, DP>>,
): Connector<P, OP, MergeOPDP<OP, DP>>;
// In this case DP is an object of action creators not yet bound to dispatch,
// this difference is not important in the vanila redux,
// but in case of usage with redux-thunk, the return type may differ.
declare export function connect<-P, -OP, -SP, -DP, S, D>(
mapStateToProps: null | void,
mapDispatchToProps: DP,
mergeProps?: null | void,
options?: ?Options<S, OP, {||}, MergeOPDP<OP, DP>>,
): Connector<P, OP, MergeOPDP<OP, $ObjMap<DP, Bind<D>>>>;
declare export function connect<-P, -OP, -SP, -DP, S, D>(
// If you get error here try adding return type to your mapStateToProps function
mapStateToProps: MapStateToProps<S, OP, SP>,
mapDispatchToProps: MapDispatchToPropsFn<D, OP, DP>,
mergeProps?: null | void,
options?: ?Options<S, OP, SP, {| ...OP, ...SP, ...DP |}>,
): Connector<P, OP, {| ...OP, ...SP, ...DP |}>;
declare export function connect<-P, -OP, -SP, -DP, S, D>(
// If you get error here try adding return type to your mapStateToProps function
mapStateToProps: MapStateToProps<S, OP, SP>,
mapDispatchToProps: DP,
mergeProps?: null | void,
options?: ?Options<S, OP, SP, MergeOPSPDP<OP, SP, DP>>,
): Connector<P, OP, MergeOPSPDP<OP, SP, $ObjMap<DP, Bind<D>>>>;
// With `mergeProps` argument
declare type MergeProps<+P, -OP, -SP, -DP> = (
stateProps: SP,
dispatchProps: DP,
ownProps: OP,
) => P;
declare export function connect<-P, -OP, -SP: {||}, -DP: {||}, S, D>(
mapStateToProps: null | void,
mapDispatchToProps: null | void,
// If you get error here try adding return type to you mapStateToProps function
mergeProps: MergeProps<P, OP, {||}, {| dispatch: D |}>,
options?: ?Options<S, OP, {||}, P>,
): Connector<P, OP, P>;
declare export function connect<-P, -OP, -SP, -DP: {||}, S, D>(
mapStateToProps: MapStateToProps<S, OP, SP>,
mapDispatchToProps: null | void,
// If you get error here try adding return type to you mapStateToProps function
mergeProps: MergeProps<P, OP, SP, {| dispatch: D |}>,
options?: ?Options<S, OP, SP, P>,
): Connector<P, OP, P>;
// In this case DP is an object of functions which has been bound to dispatch
// by the given mapDispatchToProps function.
declare export function connect<-P, -OP, -SP: {||}, -DP, S, D>(
mapStateToProps: null | void,
mapDispatchToProps: MapDispatchToPropsFn<D, OP, DP>,
mergeProps: MergeProps<P, OP, {||}, DP>,
options?: ?Options<S, OP, {||}, P>,
): Connector<P, OP, P>;
// In this case DP is an object of action creators not yet bound to dispatch,
// this difference is not important in the vanila redux,
// but in case of usage with redux-thunk, the return type may differ.
declare export function connect<-P, -OP, -SP: {||}, -DP, S, D>(
mapStateToProps: null | void,
mapDispatchToProps: DP,
mergeProps: MergeProps<P, OP, {||}, $ObjMap<DP, Bind<D>>>,
options?: ?Options<S, OP, {||}, P>,
): Connector<P, OP, P>;
// In this case DP is an object of functions which has been bound to dispatch
// by the given mapDispatchToProps function.
declare export function connect<-P, -OP, -SP, -DP, S, D>(
mapStateToProps: MapStateToProps<S, OP, SP>,
mapDispatchToProps: MapDispatchToPropsFn<D, OP, DP>,
mergeProps: MergeProps<P, OP, SP, DP>,
options?: ?Options<S, OP, SP, P>,
): Connector<P, OP, P>;
// In this case DP is an object of action creators not yet bound to dispatch,
// this difference is not important in the vanila redux,
// but in case of usage with redux-thunk, the return type may differ.
declare export function connect<-P, -OP, -SP, -DP, S, D>(
mapStateToProps: MapStateToProps<S, OP, SP>,
mapDispatchToProps: DP,
mergeProps: MergeProps<P, OP, SP, $ObjMap<DP, Bind<D>>>,
options?: ?Options<S, OP, SP, P>,
): Connector<P, OP, P>;
// ------------------------------------------------------------
// Typings for Provider
// ------------------------------------------------------------
declare export class Provider<Store> extends React$Component<{
store: Store,
children?: React$Node,
}> {}
declare export function createProvider(
storeKey?: string,
subKey?: string,
): Class<Provider<*>>;
// ------------------------------------------------------------
// Typings for connectAdvanced()
// ------------------------------------------------------------
declare type ConnectAdvancedOptions = {
getDisplayName?: (name: string) => string,
methodName?: string,
renderCountProp?: string,
shouldHandleStateChanges?: boolean,
storeKey?: string,
withRef?: boolean,
};
declare type SelectorFactoryOptions<Com> = {
getDisplayName: (name: string) => string,
methodName: string,
renderCountProp: ?string,
shouldHandleStateChanges: boolean,
storeKey: string,
withRef: boolean,
displayName: string,
wrappedComponentName: string,
WrappedComponent: Com,
};
declare type MapStateToPropsEx<S: Object, SP: Object, RSP: Object> = (
state: S,
props: SP,
) => RSP;
declare type SelectorFactory<
Com: React$ComponentType<*>,
Dispatch,
S: Object,
DP: Object,
RSP: Object,
CP: $Diff<OmitDispatch<ElementConfig<Com>>, RSP>
>(
mapStateToProps: MapStateToProps<S, DP, RSP>,
mapDispatchToProps?: null
): (component: Com) => ComponentType<CP & DP>;
declare export function connect<Com: ComponentType<*>>(
mapStateToProps?: null,
mapDispatchToProps?: null
): (component: Com) => ComponentType<OmitDispatch<ElementConfig<Com>>>;
declare export function connect<
Com: ComponentType<*>,
A,
S: Object,
DP: Object,
SP: Object,
RSP: Object,
RDP: Object,
CP: $Diff<$Diff<ElementConfig<Com>, RSP>, RDP>
>(
mapStateToProps: MapStateToProps<S, SP, RSP>,
mapDispatchToProps: MapDispatchToProps<A, DP, RDP>
): (component: Com) => ComponentType<CP & SP & DP>;
declare export function connect<
Com: ComponentType<*>,
A,
OP: Object,
DP: Object,
PR: Object,
CP: $Diff<ElementConfig<Com>, DP>
>(
mapStateToProps?: null,
mapDispatchToProps: MapDispatchToProps<A, OP, DP>
): (Com) => ComponentType<CP & OP>;
EFO: Object,
CP: Object,
> = (
dispatch: Dispatch,
factoryOptions: SelectorFactoryOptions<Com> & EFO,
) => MapStateToPropsEx<S, OP, CP>;
declare export function connect<
Com: ComponentType<*>,
MDP: Object
>(
mapStateToProps?: null,
mapDispatchToProps: MDP
): (component: Com) => ComponentType<$Diff<ElementConfig<Com>, MDP>>;
declare export function connect<
Com: ComponentType<*>,
declare export function connectAdvanced<
Com: React$ComponentType<*>,
D,
S: Object,
SP: Object,
RSP: Object,
MDP: Object,
CP: $Diff<ElementConfig<Com>, RSP>
>(
mapStateToProps: MapStateToProps<S, SP, RSP>,
mapDispatchToPRops: MDP
): (component: Com) => ComponentType<$Diff<CP, MDP> & SP>;
declare export function connect<
Com: ComponentType<*>,
A,
S: Object,
DP: Object,
SP: Object,
RSP: Object,
RDP: Object,
MP: Object,
RMP: Object,
CP: $Diff<ElementConfig<Com>, RMP>
>(
mapStateToProps: MapStateToProps<S, SP, RSP>,
mapDispatchToProps: ?MapDispatchToProps<A, DP, RDP>,
mergeProps: MergeProps<RSP, RDP, MP, RMP>
): (component: Com) => ComponentType<CP & SP & DP & MP>;
declare export function connect<
Com: ComponentType<*>,
A,
S: Object,
DP: Object,
SP: Object,
RSP: Object,
RDP: Object,
MDP: Object,
MP: Object,
RMP: Object,
CP: $Diff<ElementConfig<Com>, RMP>
>(
mapStateToProps: MapStateToProps<S, SP, RSP>,
mapDispatchToProps: MDP,
mergeProps: MergeProps<RSP, RDP, MP, RMP>
): (component: Com) => ComponentType<CP & SP & DP & MP>;
declare export function connect<Com: ComponentType<*>,
A,
S: Object,
DP: Object,
SP: Object,
RSP: Object,
RDP: Object,
MP: Object,
RMP: Object
>(
mapStateToProps: ?MapStateToProps<S, SP, RSP>,
mapDispatchToProps: ?MapDispatchToProps<A, DP, RDP>,
mergeProps: ?MergeProps<RSP, RDP, MP, RMP>,
options: ConnectOptions<S, SP & DP & MP, RSP, RMP>
): (component: Com) => ComponentType<$Diff<ElementConfig<Com>, RMP> & SP & DP & MP>;
declare export function connect<Com: ComponentType<*>,
A,
S: Object,
DP: Object,
SP: Object,
RSP: Object,
RDP: Object,
MDP: Object,
MP: Object,
RMP: Object
>(
mapStateToProps: ?MapStateToProps<S, SP, RSP>,
mapDispatchToProps: ?MapDispatchToProps<A, DP, RDP>,
mergeProps: MDP,
options: ConnectOptions<S, SP & DP & MP, RSP, RMP>
): (component: Com) => ComponentType<$Diff<ElementConfig<Com>, RMP> & SP & DP & MP>;
OP: Object,
CP: Object,
EFO: Object,
ST: { [_: $Keys<Com>]: any },
>(
selectorFactory: SelectorFactory<Com, D, S, OP, EFO, CP>,
connectAdvancedOptions: ?(ConnectAdvancedOptions & EFO),
): (component: Com) => React$ComponentType<OP> & $Shape<ST>;
declare export default {
Provider: typeof Provider,
createProvider: typeof createProvider,
connect: typeof connect,
connectAdvanced: typeof connectAdvanced,
};
}

View File

@@ -1,3 +1,6 @@
// flow-typed signature: df80bdd535bfed9cf3223e077f3b4543
// flow-typed version: c4c8963c9c/redux_v4.x.x/flow_>=v0.55.x
declare module 'redux' {
/*

Binary file not shown.

View File

@@ -8,6 +8,7 @@
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" d="" />
<glyph unicode="&#xe0b7;" glyph-name="chat-unread" d="M768 682v86h-512v-86h512zM598 426v86h-342v-86h342zM256 640v-86h512v86h-512zM854 938c46 0 84-38 84-84v-512c0-46-38-86-84-86h-598l-170-170v768c0 46 38 84 84 84h684z" />
<glyph unicode="&#xe0cd;" glyph-name="phone" d="M282 564c62-120 162-220 282-282l94 94c12 12 30 16 44 10 48-16 100-24 152-24 24 0 42-18 42-42v-150c0-24-18-42-42-42-400 0-726 326-726 726 0 24 18 42 42 42h150c24 0 42-18 42-42 0-54 8-104 24-152 4-14 2-32-10-44z" />
<glyph unicode="&#xe145;" glyph-name="invite" d="M810 470h-256v-256h-84v256h-256v84h256v256h84v-256h256v-84z" />
<glyph unicode="&#xe146;" glyph-name="add" d="M810 470h-256v-256h-84v256h-256v84h256v256h84v-256h256v-84z" />
<glyph unicode="&#xe1aa;" glyph-name="bluetooth" d="M550 328l-80 82v-162zM470 776v-162l80 82zM670 696l-184-184 184-184-244-242h-42v324l-196-196-60 60 238 238-238 238 60 60 196-196v324h42zM834 738c40-64 62-142 62-222 0-84-24-160-66-226l-50 50c26 52 42 110 42 172s-16 120-42 172zM608 512l98 98c12-30 20-64 20-98s-8-70-20-100z" />
@@ -21,13 +22,19 @@
<glyph unicode="&#xe616;" glyph-name="event_note" d="M598 426v-84h-300v84h300zM810 214v468h-596v-468h596zM810 896c46 0 86-40 86-86v-596c0-46-40-86-86-86h-596c-48 0-86 40-86 86v596c0 46 38 86 86 86h42v86h86v-86h340v86h86v-86h42zM726 598v-86h-428v86h428z" />
<glyph unicode="&#xe61d;" glyph-name="phone-talk" d="M640 512c0 70-58 128-128 128v86c118 0 214-96 214-214h-86zM810 512c0 166-132 298-298 298v86c212 0 384-172 384-384h-86zM854 362c24 0 42-18 42-42v-150c0-24-18-42-42-42-400 0-726 326-726 726 0 24 18 42 42 42h150c24 0 42-18 42-42 0-54 8-104 24-152 4-14 2-32-10-44l-94-94c62-122 162-220 282-282l94 94c12 12 30 14 44 10 48-16 98-24 152-24z" />
<glyph unicode="&#xe80b;" glyph-name="public" d="M764 282c56 60 90 142 90 230 0 142-88 266-214 316v-18c0-46-40-84-86-84h-84v-86c0-24-20-42-44-42h-84v-86h256c24 0 42-18 42-42v-128h42c38 0 70-26 82-60zM470 174v82c-46 0-86 40-86 86v42l-204 204c-6-24-10-50-10-76 0-174 132-318 300-338zM512 938c236 0 426-190 426-426s-190-426-426-426-426 190-426 426 190 426 426 426z" />
<glyph unicode="&#xe836;" glyph-name="radio_button_unchecked" d="M512 170c188 0 342 154 342 342s-154 342-342 342-342-154-342-342 154-342 342-342zM512 938c236 0 426-190 426-426s-190-426-426-426-426 190-426 426 190 426 426 426z" />
<glyph unicode="&#xe837;" glyph-name="radio_button_checked" d="M512 170c188 0 342 154 342 342s-154 342-342 342-342-154-342-342 154-342 342-342zM512 938c236 0 426-190 426-426s-190-426-426-426-426 190-426 426 190 426 426 426zM512 726c118 0 214-96 214-214s-96-214-214-214-214 96-214 214 96 214 214 214z" />
<glyph unicode="&#xe89e;" glyph-name="open_in_new" d="M598 896h298v-298h-86v152l-418-418-60 60 418 418h-152v86zM810 214v298h86v-298c0-46-40-86-86-86h-596c-48 0-86 40-86 86v596c0 46 38 86 86 86h298v-86h-298v-596h596z" />
<glyph unicode="&#xe8b3;" glyph-name="restore" d="M512 682h64v-180l150-90-32-52-182 110v212zM554 896c212 0 384-172 384-384s-172-384-384-384c-106 0-200 42-270 112l60 62c54-54 128-88 210-88 166 0 300 132 300 298s-134 298-300 298-298-132-298-298h128l-172-172-4 6-166 166h128c0 212 172 384 384 384z" />
<glyph unicode="&#xe8b6;" glyph-name="search" d="M406 426c106 0 192 86 192 192s-86 192-192 192-192-86-192-192 86-192 192-192zM662 426l212-212-64-64-212 212v34l-12 12c-48-42-112-66-180-66-154 0-278 122-278 276s124 278 278 278 276-124 276-278c0-68-24-132-66-180l12-12h34z" />
<glyph unicode="&#xe900;" glyph-name="AUD" d="M512 0c-282.77 0-512 229.23-512 512s229.23 512 512 512c282.77 0 512-229.23 512-512s-229.23-512-512-512zM308.25 387.3h57.225l-87.675 252.525h-62.125l-87.675-252.525h53.025l19.425 60.2h88.725l19.075-60.2zM461.9 639.825h-52.85v-165.375c0-56 41.125-93.625 105.7-93.625 64.75 0 105.875 37.625 105.875 93.625v165.375h-52.85v-159.95c0-31.85-19.075-52.15-53.025-52.15-33.775 0-52.85 20.3-52.85 52.15v159.95zM682.225 640v-252.7h99.4c75.6 0 118.475 46.025 118.475 128.1 0 79.1-43.4 124.6-118.475 124.6h-99.4zM735.075 594.85v-162.4h38.15c46.725 0 72.975 28.7 72.975 82.075 0 51.1-27.125 80.325-72.975 80.325h-38.15zM243.5 587.325l-31.675-99.050h66.15l-31.325 99.050h-3.15z" />
<glyph unicode="&#xe901;" glyph-name="signal_cellular_0" d="M938 938v-852h-852zM854 732l-562-562h562v562z" />
<glyph unicode="&#xe902;" glyph-name="signal_cellular_1" d="M86 86l852 852v-256h-170v-596h-682zM854 86v84h84v-84h-84zM854 256v342h84v-342h-84z" />
<glyph unicode="&#xe903;" glyph-name="mic-camera-combined" d="M756.704 628.138l267.296 202.213v-635.075l-267.296 202.213v-191.923c0-12.085-11.296-21.863-25.216-21.863h-706.272c-13.92 0-25.216 9.777-25.216 21.863v612.25c0 12.085 11.296 21.863 25.216 21.863h706.272c13.92 0 25.216-9.777 25.216-21.863v-189.679zM371.338 376.228c47.817 0 86.529 40.232 86.529 89.811v184.835c0 49.651-38.713 89.883-86.529 89.883-47.788 0-86.515-40.232-86.515-89.883v-184.835c0-49.579 38.756-89.811 86.515-89.811v0zM356.754 314.070v-32.78h33.718v33.412c73.858 9.606 131.235 73.73 131.235 151.351v88.232h-30.636v-88.232c0-67.57-53.696-122.534-119.734-122.534-66.024 0-119.691 54.964-119.691 122.534v88.232h-30.636v-88.232c0-79.215 59.674-144.502 135.744-151.969v-0.014z" />
<glyph unicode="&#xe904;" glyph-name="kick" d="M512 810l284-426h-568zM214 298h596v-84h-596v84z" />
<glyph unicode="&#xe905;" glyph-name="hangup" d="M512 640c-68 0-134-10-196-30v-132c0-16-10-34-24-40-42-20-80-46-114-78-8-8-18-12-30-12s-22 4-30 12l-106 106c-8 8-12 18-12 30s4 22 12 30c130 124 306 200 500 200s370-76 500-200c8-8 12-18 12-30s-4-22-12-30l-106-106c-8-8-18-12-30-12s-22 4-30 12c-34 32-72 58-114 78-14 6-24 20-24 38v132c-62 20-128 32-196 32z" />
<glyph unicode="&#xe906;" glyph-name="chat" d="M854 342v512h-684v-598l86 86h598zM854 938c46 0 84-38 84-84v-512c0-46-38-86-84-86h-598l-170-170v768c0 46 38 84 84 84h684z" />
<glyph unicode="&#xe907;" glyph-name="signal_cellular_2" d="M86 86l852 852v-852h-852z" />
<glyph unicode="&#xe908;" glyph-name="share-doc" d="M554 640h236l-236 234v-234zM682 426v86h-340v-86h340zM682 256v86h-340v-86h340zM598 938l256-256v-512c0-46-40-84-86-84h-512c-46 0-86 38-86 84l2 684c0 46 38 84 84 84h342z" />
<glyph unicode="&#xe909;" glyph-name="ninja" d="M330.667 469.333c-0.427 14.933 6.4 29.44 17.92 39.253 32-6.827 61.867-20.053 88.747-39.253 0-29.013-23.893-52.907-53.333-52.907s-52.907 23.467-53.333 52.907zM586.667 469.333c26.88 18.773 56.747 32 88.747 38.827 11.52-9.813 18.347-24.32 17.92-38.827 0-29.867-23.893-53.76-53.333-53.76s-53.333 23.893-53.333 53.76v0zM512 640c-118.187 1.707-234.667-27.733-338.347-85.333l-2.987-42.667c0-52.48 12.373-104.107 35.84-151.040 101.12 15.36 203.093 23.040 305.493 23.040s204.373-7.68 305.493-23.040c23.467 46.933 35.84 98.56 35.84 151.040l-2.987 42.667c-103.68 57.6-220.16 87.040-338.347 85.333zM512 938.667c235.641 0 426.667-191.025 426.667-426.667s-191.025-426.667-426.667-426.667c-235.641 0-426.667 191.025-426.667 426.667s191.025 426.667 426.667 426.667z" />
<glyph unicode="&#xe90b;" glyph-name="full-screen" d="M598 810h212v-212h-84v128h-128v84zM726 298v128h84v-212h-212v84h128zM214 598v212h212v-84h-128v-128h-84zM298 426v-128h128v-84h-212v212h84z" />

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="258.559px" height="396.871px" viewBox="0 0 258.559 396.871" enable-background="new 0 0 258.559 396.871"
xml:space="preserve">
<g id="u6PRpE_1_">
<g>
<path fill="#3A3A3A" d="M341.829,396.871c0,0-16.524-193.936-258.445-396.871c86.17,0,258.445,0,258.445,0V396.871z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 668 B

BIN
images/dropboxLogo_square.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

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