From 45f46434698a37695ef442bd8135a25c1e09da77 Mon Sep 17 00:00:00 2001 From: Titus-Andrei Moldovan Date: Thu, 21 Jan 2021 14:07:00 +0200 Subject: [PATCH] feat(mobile) adds ToggleScreenShare event and action. --- .../org/jitsi/meet/sdk/BroadcastAction.java | 3 +- .../org/jitsi/meet/sdk/BroadcastEvent.java | 6 +- .../jitsi/meet/sdk/BroadcastIntentHelper.java | 4 ++ .../org/jitsi/meet/sdk/BroadcastReceiver.java | 7 ++- .../org/jitsi/meet/sdk/ExternalAPIModule.java | 1 + .../org/jitsi/meet/sdk/JitsiMeetActivity.java | 10 ++-- ios/app/src/ViewController.m | 6 +- ios/sdk/src/ExternalAPI.h | 1 + ios/sdk/src/ExternalAPI.m | 10 +++- ios/sdk/src/JitsiMeetView.h | 2 + ios/sdk/src/JitsiMeetView.m | 5 ++ ios/sdk/src/JitsiMeetViewDelegate.h | 7 +++ react/features/app/reducers.native.js | 1 + .../mobile/external-api/actionTypes.js | 11 ++++ react/features/mobile/external-api/actions.js | 21 +++++++ .../mobile/external-api/middleware.js | 57 ++++++++++++++++++- react/features/mobile/external-api/reducer.js | 22 +++++++ 17 files changed, 159 insertions(+), 15 deletions(-) create mode 100644 react/features/mobile/external-api/actionTypes.js create mode 100644 react/features/mobile/external-api/actions.js create mode 100644 react/features/mobile/external-api/reducer.js diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastAction.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastAction.java index bc0b25d83..ad7a51f36 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastAction.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastAction.java @@ -61,7 +61,8 @@ public class BroadcastAction { enum Type { SET_AUDIO_MUTED("org.jitsi.meet.SET_AUDIO_MUTED"), HANG_UP("org.jitsi.meet.HANG_UP"), - SEND_ENDPOINT_TEXT_MESSAGE("org.jitsi.meet.SEND_ENDPOINT_TEXT_MESSAGE"); + SEND_ENDPOINT_TEXT_MESSAGE("org.jitsi.meet.SEND_ENDPOINT_TEXT_MESSAGE"), + TOGGLE_SCREEN_SHARE("org.jitsi.meet.TOGGLE_SCREEN_SHARE"); private final String action; diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastEvent.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastEvent.java index cab686303..14033efd5 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastEvent.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastEvent.java @@ -81,7 +81,8 @@ public class BroadcastEvent { AUDIO_MUTED_CHANGED("org.jitsi.meet.AUDIO_MUTED_CHANGED"), PARTICIPANT_JOINED("org.jitsi.meet.PARTICIPANT_JOINED"), PARTICIPANT_LEFT("org.jitsi.meet.PARTICIPANT_LEFT"), - ENDPOINT_TEXT_MESSAGE_RECEIVED("org.jitsi.meet.ENDPOINT_TEXT_MESSAGE_RECEIVED"); + ENDPOINT_TEXT_MESSAGE_RECEIVED("org.jitsi.meet.ENDPOINT_TEXT_MESSAGE_RECEIVED"), + SCREEN_SHARE_TOGGLED("org.jitsi.meet.SCREEN_SHARE_TOGGLED"); private static final String CONFERENCE_WILL_JOIN_NAME = "CONFERENCE_WILL_JOIN"; private static final String CONFERENCE_JOINED_NAME = "CONFERENCE_JOINED"; @@ -90,6 +91,7 @@ public class BroadcastEvent { private static final String PARTICIPANT_JOINED_NAME = "PARTICIPANT_JOINED"; private static final String PARTICIPANT_LEFT_NAME = "PARTICIPANT_LEFT"; private static final String ENDPOINT_TEXT_MESSAGE_RECEIVED_NAME = "ENDPOINT_TEXT_MESSAGE_RECEIVED"; + private static final String SCREEN_SHARE_TOGGLED_NAME= "SCREEN_SHARE_TOGGLED"; private final String action; @@ -126,6 +128,8 @@ public class BroadcastEvent { return PARTICIPANT_LEFT; case ENDPOINT_TEXT_MESSAGE_RECEIVED_NAME: return ENDPOINT_TEXT_MESSAGE_RECEIVED; + case SCREEN_SHARE_TOGGLED_NAME: + return SCREEN_SHARE_TOGGLED; } return null; diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastIntentHelper.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastIntentHelper.java index d09f37fec..b471edf24 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastIntentHelper.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastIntentHelper.java @@ -19,4 +19,8 @@ public class BroadcastIntentHelper { intent.putExtra("message", message); return intent; } + + public static Intent buildToggleScreenShareIntent() { + return new Intent(BroadcastAction.Type.TOGGLE_SCREEN_SHARE.getAction()); + } } diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastReceiver.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastReceiver.java index f61199c21..d5ac34ae6 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastReceiver.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastReceiver.java @@ -16,9 +16,10 @@ public class BroadcastReceiver extends android.content.BroadcastReceiver { LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context); IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(BroadcastAction.Type.SET_AUDIO_MUTED.getAction()); - intentFilter.addAction(BroadcastAction.Type.HANG_UP.getAction()); - intentFilter.addAction(BroadcastAction.Type.SEND_ENDPOINT_TEXT_MESSAGE.getAction()); + + for (BroadcastAction.Type type : BroadcastAction.Type.values()) { + intentFilter.addAction(type.getAction()); + } localBroadcastManager.registerReceiver(this, intentFilter); } diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/ExternalAPIModule.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/ExternalAPIModule.java index caa60e9bf..d818177af 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/ExternalAPIModule.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/ExternalAPIModule.java @@ -78,6 +78,7 @@ class ExternalAPIModule constants.put("SET_AUDIO_MUTED", BroadcastAction.Type.SET_AUDIO_MUTED.getAction()); constants.put("HANG_UP", BroadcastAction.Type.HANG_UP.getAction()); constants.put("SEND_ENDPOINT_TEXT_MESSAGE", BroadcastAction.Type.SEND_ENDPOINT_TEXT_MESSAGE.getAction()); + constants.put("TOGGLE_SCREEN_SHARE", BroadcastAction.Type.TOGGLE_SCREEN_SHARE.getAction()); return constants; } diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivity.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivity.java index 1b443aa4b..0a9578561 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivity.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivity.java @@ -266,12 +266,10 @@ public class JitsiMeetActivity extends FragmentActivity private void registerForBroadcastMessages() { IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(BroadcastEvent.Type.CONFERENCE_JOINED.getAction()); - intentFilter.addAction(BroadcastEvent.Type.CONFERENCE_WILL_JOIN.getAction()); - intentFilter.addAction(BroadcastEvent.Type.CONFERENCE_TERMINATED.getAction()); - intentFilter.addAction(BroadcastEvent.Type.PARTICIPANT_JOINED.getAction()); - intentFilter.addAction(BroadcastEvent.Type.PARTICIPANT_LEFT.getAction()); - intentFilter.addAction(BroadcastEvent.Type.ENDPOINT_TEXT_MESSAGE_RECEIVED.getAction()); + + for (BroadcastEvent.Type type : BroadcastEvent.Type.values()) { + intentFilter.addAction(type.getAction()); + } LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, intentFilter); } diff --git a/ios/app/src/ViewController.m b/ios/app/src/ViewController.m index 99205b48a..feb606363 100644 --- a/ios/app/src/ViewController.m +++ b/ios/app/src/ViewController.m @@ -115,10 +115,14 @@ NSLog(@"%@%@", @"Audio muted changed: ", data[@"muted"]); } -- (void)endpointTextMessageReceived:(NSDictionary *)data; { +- (void)endpointTextMessageReceived:(NSDictionary *)data { NSLog(@"%@%@", @"Endpoint text message received: ", data); } +- (void)screenShareToggled:(NSDictionary *)data { + NSLog(@"%@%@", @"Screen share toggled: ", data); +} + #pragma mark - Helpers - (void)terminate { diff --git a/ios/sdk/src/ExternalAPI.h b/ios/sdk/src/ExternalAPI.h index 66e5de9c0..af07e10b1 100644 --- a/ios/sdk/src/ExternalAPI.h +++ b/ios/sdk/src/ExternalAPI.h @@ -21,5 +21,6 @@ - (void)sendHangUp; - (void)sendSetAudioMuted: (BOOL)muted; - (void)sendEndpointTextMessage:(NSString*)to :(NSString*)message; +- (void)toggleScreenShare; @end diff --git a/ios/sdk/src/ExternalAPI.m b/ios/sdk/src/ExternalAPI.m index 266484cee..a8d8555a6 100644 --- a/ios/sdk/src/ExternalAPI.m +++ b/ios/sdk/src/ExternalAPI.m @@ -21,6 +21,7 @@ static NSString * const hangUpAction = @"org.jitsi.meet.HANG_UP"; static NSString * const setAudioMutedAction = @"org.jitsi.meet.SET_AUDIO_MUTED"; static NSString * const sendEndpointTextMessageAction = @"org.jitsi.meet.SEND_ENDPOINT_TEXT_MESSAGE"; +static NSString * const toggleScreenShareAction = @"org.jitsi.meet.TOGGLE_SCREEN_SHARE"; @implementation ExternalAPI @@ -30,7 +31,8 @@ RCT_EXPORT_MODULE(); return @{ @"HANG_UP": hangUpAction, @"SET_AUDIO_MUTED" : setAudioMutedAction, - @"SEND_ENDPOINT_TEXT_MESSAGE": sendEndpointTextMessageAction + @"SEND_ENDPOINT_TEXT_MESSAGE": sendEndpointTextMessageAction, + @"TOGGLE_SCREEN_SHARE": toggleScreenShareAction }; }; @@ -46,7 +48,7 @@ RCT_EXPORT_MODULE(); } - (NSArray *)supportedEvents { - return @[ hangUpAction, setAudioMutedAction, sendEndpointTextMessageAction ]; + return @[ hangUpAction, setAudioMutedAction, sendEndpointTextMessageAction, toggleScreenShareAction ]; } /** @@ -123,4 +125,8 @@ RCT_EXPORT_METHOD(sendEvent:(NSString *)name [self sendEventWithName:sendEndpointTextMessageAction body:data]; } +- (void)toggleScreenShare { + [self sendEventWithName:toggleScreenShareAction body:nil]; +} + @end diff --git a/ios/sdk/src/JitsiMeetView.h b/ios/sdk/src/JitsiMeetView.h index 93cb676bf..0aed728c0 100644 --- a/ios/sdk/src/JitsiMeetView.h +++ b/ios/sdk/src/JitsiMeetView.h @@ -43,4 +43,6 @@ - (void)sendEndpointTextMessage:(NSString*)to :(NSString*)message; +- (void)toggleScreenShare; + @end diff --git a/ios/sdk/src/JitsiMeetView.m b/ios/sdk/src/JitsiMeetView.m index 12970aef9..e84a259a0 100644 --- a/ios/sdk/src/JitsiMeetView.m +++ b/ios/sdk/src/JitsiMeetView.m @@ -130,6 +130,11 @@ static void initializeViewsMap() { [externalAPI sendEndpointTextMessage:to :message]; } +- (void)toggleScreenShare { + ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI]; + [externalAPI toggleScreenShare]; +} + #pragma mark Private methods /** diff --git a/ios/sdk/src/JitsiMeetViewDelegate.h b/ios/sdk/src/JitsiMeetViewDelegate.h index bed08e96b..83e219b36 100644 --- a/ios/sdk/src/JitsiMeetViewDelegate.h +++ b/ios/sdk/src/JitsiMeetViewDelegate.h @@ -83,4 +83,11 @@ */ - (void)endpointTextMessageReceived:(NSDictionary *)data; +/** + * Called when a participant toggled shared screen. + * + * The `data` dictionary contains a `participantId` key with the id of the participant and a 'sharing' key with boolean value. + */ +- (void)screenShareToggled:(NSDictionary *)data; + @end diff --git a/react/features/app/reducers.native.js b/react/features/app/reducers.native.js index 8adaebd5b..5989a7016 100644 --- a/react/features/app/reducers.native.js +++ b/react/features/app/reducers.native.js @@ -4,6 +4,7 @@ import '../authentication/reducer'; import '../mobile/audio-mode/reducer'; import '../mobile/background/reducer'; import '../mobile/call-integration/reducer'; +import '../mobile/external-api/reducer'; import '../mobile/full-screen/reducer'; import '../mobile/incoming-call/reducer'; import '../mobile/watchos/reducer'; diff --git a/react/features/mobile/external-api/actionTypes.js b/react/features/mobile/external-api/actionTypes.js new file mode 100644 index 000000000..3a4aed53c --- /dev/null +++ b/react/features/mobile/external-api/actionTypes.js @@ -0,0 +1,11 @@ +/** + * The type of the action which sets the list of known participant IDs which + * have an active screen share. + * + * @returns {{ + * type: SCREEN_SHARE_PARTICIPANTS_UPDATED, + * participantIds: Array + * }} + */ +export const SCREEN_SHARE_PARTICIPANTS_UPDATED + = 'SCREEN_SHARE_PARTICIPANTS_UPDATED'; diff --git a/react/features/mobile/external-api/actions.js b/react/features/mobile/external-api/actions.js new file mode 100644 index 000000000..0464ff8a5 --- /dev/null +++ b/react/features/mobile/external-api/actions.js @@ -0,0 +1,21 @@ +// @flow + +import { SCREEN_SHARE_PARTICIPANTS_UPDATED } from './actionTypes'; + +/** + * Creates a (redux) action which signals that the list of known participants + * with screen shares has changed. + * + * @param {string} participantIds - The participants which currently have active + * screen share streams. + * @returns {{ + * type: SCREEN_SHARE_PARTICIPANTS_UPDATED, + * participantId: string + * }} + */ +export function setParticipantsWithScreenShare(participantIds: Array) { + return { + type: SCREEN_SHARE_PARTICIPANTS_UPDATED, + participantIds + }; +} diff --git a/react/features/mobile/external-api/middleware.js b/react/features/mobile/external-api/middleware.js index 43315f144..8abb3ff8f 100644 --- a/react/features/mobile/external-api/middleware.js +++ b/react/features/mobile/external-api/middleware.js @@ -1,5 +1,6 @@ // @flow +import debounce from 'lodash/debounce'; import { NativeEventEmitter, NativeModules } from 'react-native'; import { ENDPOINT_TEXT_MESSAGE_NAME } from '../../../../modules/API/constants'; @@ -27,10 +28,12 @@ import { import { JitsiConferenceEvents } from '../../base/lib-jitsi-meet'; import { SET_AUDIO_MUTED } from '../../base/media/actionTypes'; import { PARTICIPANT_JOINED, PARTICIPANT_LEFT } from '../../base/participants'; -import { MiddlewareRegistry } from '../../base/redux'; +import { MiddlewareRegistry, StateListenerRegistry } from '../../base/redux'; +import { toggleScreensharing } from '../../base/tracks'; import { muteLocal } from '../../remote-video-menu/actions'; import { ENTER_PICTURE_IN_PICTURE } from '../picture-in-picture'; +import { setParticipantsWithScreenShare } from './actions'; import { sendEvent } from './functions'; import logger from './logger'; @@ -46,6 +49,12 @@ const CONFERENCE_TERMINATED = 'CONFERENCE_TERMINATED'; */ const ENDPOINT_TEXT_MESSAGE_RECEIVED = 'ENDPOINT_TEXT_MESSAGE_RECEIVED'; +/** + * Event which will be emitted on the native side to indicate a participant togggles + * the screen share. + */ +const SCREEN_SHARE_TOGGLED = 'SCREEN_SHARE_TOGGLED'; + const { ExternalAPI } = NativeModules; const eventEmitter = new NativeEventEmitter(ExternalAPI); @@ -171,6 +180,48 @@ MiddlewareRegistry.register(store => next => action => { return result; }); +/** + * Listen for changes to the known media tracks and look + * for updates to screen shares for emitting native events. + * The listener is debounced to avoid state thrashing that might occur, + * especially when switching in or out of p2p. + */ +StateListenerRegistry.register( + /* selector */ state => state['features/base/tracks'], + /* listener */ debounce((tracks, store) => { + const oldScreenShares = store.getState()['features/mobile/external-api'].screenShares || []; + const newScreenShares = tracks + .filter(track => track.mediaType === 'video' && track.videoType === 'desktop') + .map(track => track.participantId); + + oldScreenShares.forEach(participantId => { + if (!newScreenShares.includes(participantId)) { + sendEvent( + store, + SCREEN_SHARE_TOGGLED, + /* data */ { + participantId, + sharing: false + }); + } + }); + + newScreenShares.forEach(participantId => { + if (!oldScreenShares.includes(participantId)) { + sendEvent( + store, + SCREEN_SHARE_TOGGLED, + /* data */ { + participantId, + sharing: true + }); + } + }); + + store.dispatch(setParticipantsWithScreenShare(newScreenShares)); + + }, 100)); + /** * Registers for events sent from the native side via NativeEventEmitter. * @@ -199,6 +250,10 @@ function _registerForNativeEvents({ getState, dispatch }) { logger.warn('Cannot send endpointMessage', error); } }); + + eventEmitter.addListener(ExternalAPI.TOGGLE_SCREEN_SHARE, () => { + dispatch(toggleScreensharing()); + }); } /** diff --git a/react/features/mobile/external-api/reducer.js b/react/features/mobile/external-api/reducer.js new file mode 100644 index 000000000..f966dbd08 --- /dev/null +++ b/react/features/mobile/external-api/reducer.js @@ -0,0 +1,22 @@ +// @flow + +import { ReducerRegistry } from '../../base/redux'; + +import { SCREEN_SHARE_PARTICIPANTS_UPDATED } from './actionTypes'; + +const DEFAULT_STATE = { + screenShares: [] +}; + +ReducerRegistry.register('features/mobile/external-api', (state = DEFAULT_STATE, action) => { + switch (action.type) { + case SCREEN_SHARE_PARTICIPANTS_UPDATED: { + return { + ...state, + screenShares: action.participantIds + }; + } + } + + return state; +});