Skip to content

Commit

Permalink
Adds screen share support on iOS sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
rcorrie committed Jul 18, 2024
1 parent 1376f59 commit 0d5e20d
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 1 deletion.
21 changes: 21 additions & 0 deletions ios/sdk/src/screen-share/ScreenShareEventEmitter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* 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.
*/

#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface ScreenShareEventEmitter : RCTEventEmitter <RCTBridgeModule>
@end
99 changes: 99 additions & 0 deletions ios/sdk/src/screen-share/ScreenShareEventEmitter.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright @ 2019-present 8x8, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#import "ScreenShareEventEmitter.h"
#import <React/RCTLog.h>
#import <React/RCTBridge.h>

NSNotificationName const kBroadcastStartedNotification = @"iOS_BroadcastStarted";
NSNotificationName const kBroadcastStoppedNotification = @"iOS_BroadcastStopped";

static NSString * const toggleScreenShareAction = @"org.jitsi.meet.TOGGLE_SCREEN_SHARE";

@implementation ScreenShareEventEmitter {
CFNotificationCenterRef _notificationCenter;
bool hasListeners;
}

RCT_EXPORT_MODULE();

- (NSDictionary *)constantsToExport {
return @{
@"TOGGLE_SCREEN_SHARE": toggleScreenShareAction,
};
};

- (instancetype)init {
self = [super init];
if (self) {
_notificationCenter = CFNotificationCenterGetDarwinNotifyCenter();
[self setupObserver];
}
return self;
}

- (void)dealloc {
[self clearObserver];
}

// MARK: Private Methods

- (void)setupObserver {
CFNotificationCenterAddObserver(_notificationCenter, (__bridge const void *)(self), broadcastStartedNotificationCallback, (__bridge CFStringRef)kBroadcastStartedNotification, NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
CFNotificationCenterAddObserver(_notificationCenter, (__bridge const void *)(self), broadcastStoppedNotificationCallback, (__bridge CFStringRef)kBroadcastStoppedNotification, NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
hasListeners = YES;
}

- (void)clearObserver {
CFNotificationCenterRemoveObserver(_notificationCenter, (__bridge const void *)(self), (__bridge CFStringRef)kBroadcastStartedNotification, NULL);
CFNotificationCenterRemoveObserver(_notificationCenter, (__bridge const void *)(self), (__bridge CFStringRef)kBroadcastStoppedNotification, NULL);
hasListeners = NO;
}

void broadcastStartedNotificationCallback(CFNotificationCenterRef center,
void *observer,
CFStringRef name,
const void *object,
CFDictionaryRef userInfo) {
ScreenShareEventEmitter *self = (__bridge ScreenShareEventEmitter *)observer;
[self handleBroadcastStarted];
}

void broadcastStoppedNotificationCallback(CFNotificationCenterRef center,
void *observer,
CFStringRef name,
const void *object,
CFDictionaryRef userInfo) {
ScreenShareEventEmitter *self = (__bridge ScreenShareEventEmitter *)observer;
[self handleBroadcastStopped];
}

- (void)handleBroadcastStarted {
RCTLogInfo(@"Broadcast started");
[self sendEventWithName:toggleScreenShareAction body:@{@"enabled": @TRUE}];
}

- (void)handleBroadcastStopped {
RCTLogInfo(@"Broadcast stopped");
[self sendEventWithName:toggleScreenShareAction body:@{@"enabled": @FALSE}];
}

// Override the supportedEvents method
- (NSArray<NSString *> *)supportedEvents {
return @[toggleScreenShareAction];
}

@end
4 changes: 4 additions & 0 deletions react-native-sdk/prepare_sdk.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ copyFolderRecursiveSync(
`${iosSrcPath}/dropbox`,
iosDestPath
);
copyFolderRecursiveSync(
`${iosSrcPath}/screen-share`,
iosDestPath
);
fs.copyFileSync(
`${iosSrcPath}/AppInfo.m`,
`${iosDestPath}/AppInfo.m`
Expand Down
11 changes: 11 additions & 0 deletions react/features/mobile/react-native-sdk/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,14 @@ export function isExternalAPIAvailable() {

return ExternalAPI !== null;
}

/**
* Determines if the ScreenShareEventEmitter native module is available.
*
* @returns {boolean} If yes {@code true} otherwise {@code false}.
*/
export function isScreenShareAPIAvailable() {
const { ScreenShareEventEmitter } = NativeModules;

return ScreenShareEventEmitter !== null;
}
48 changes: 47 additions & 1 deletion react/features/mobile/react-native-sdk/middleware.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { NativeEventEmitter, NativeModules } from 'react-native';

import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app/actionTypes';
import { getAppProp } from '../../base/app/functions';
import {
CONFERENCE_BLURRED,
Expand All @@ -10,14 +13,26 @@ import {
import { SET_AUDIO_MUTED, SET_VIDEO_MUTED } from '../../base/media/actionTypes';
import { PARTICIPANT_JOINED, PARTICIPANT_LEFT } from '../../base/participants/actionTypes';
import MiddlewareRegistry from '../../base/redux/MiddlewareRegistry';
import { toggleScreensharing } from '../../base/tracks/actions.native';
import { READY_TO_CLOSE } from '../external-api/actionTypes';
import { participantToParticipantInfo } from '../external-api/functions';
import { ENTER_PICTURE_IN_PICTURE } from '../picture-in-picture/actionTypes';

import { isExternalAPIAvailable } from './functions';
import { isExternalAPIAvailable, isScreenShareAPIAvailable } from './functions';

const externalAPIEnabled = isExternalAPIAvailable();
const screenShareApi = isScreenShareAPIAvailable();

let screenShareEventEmitter;

// Get the native module
const { ScreenShareEventEmitter } = NativeModules;

// Create an event emitter

if (screenShareApi) {
screenShareEventEmitter = new NativeEventEmitter(ScreenShareEventEmitter);
}

/**
* Check if native modules are being used or not.
Expand All @@ -29,6 +44,12 @@ const externalAPIEnabled = isExternalAPIAvailable();
const rnSdkHandlers = getAppProp(store, 'rnSdkHandlers');

switch (type) {
case APP_WILL_MOUNT:
_registerForNativeEvents(store);
break;
case APP_WILL_UNMOUNT:
_unregisterForNativeEvents();
break;
case SET_AUDIO_MUTED:
rnSdkHandlers?.onAudioMutedChanged?.(action.muted);
break;
Expand Down Expand Up @@ -84,3 +105,28 @@ const externalAPIEnabled = isExternalAPIAvailable();

return result;
});

/**
* Registers for events sent from the native side via NativeEventEmitter.
*
* @param {Store} store - The redux store.
* @private
* @returns {void}
*/
function _registerForNativeEvents(store) {
const { dispatch } = store;

screenShareEventEmitter.addListener(ScreenShareEventEmitter.TOGGLE_SCREEN_SHARE, ({ enabled }) => {
dispatch(toggleScreensharing(enabled));
});
}

/**
* Unregister for events sent from the native side via NativeEventEmitter.
*
* @private
* @returns {void}
*/
function _unregisterForNativeEvents() {
screenShareEventEmitter.removeAllListeners(ScreenShareEventEmitter.TOGGLE_SCREEN_SHARE);
}

0 comments on commit 0d5e20d

Please sign in to comment.