@cloudflare/realtimekit-react-native 1.2.0-staging.0 → 1.2.0-staging.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/android/src/main/AndroidManifest.xml +6 -1
  2. package/android/src/main/java/com/cloudflare/realtimekit/KeepAliveService.java +176 -0
  3. package/android/src/main/java/com/cloudflare/realtimekit/RTKHelperModule.java +27 -60
  4. package/lib/commonjs/LocalMediaHandler.js +114 -82
  5. package/lib/commonjs/LocalMediaHandler.js.map +1 -1
  6. package/lib/commonjs/LocalMediaUtils.js +3 -5
  7. package/lib/commonjs/LocalMediaUtils.js.map +1 -1
  8. package/lib/commonjs/ReactHooks.js +27 -9
  9. package/lib/commonjs/ReactHooks.js.map +1 -1
  10. package/lib/commonjs/index.js.map +1 -1
  11. package/lib/commonjs/utils/version.js +1 -1
  12. package/lib/commonjs/utils/version.js.map +1 -1
  13. package/lib/module/LocalMediaHandler.js +114 -82
  14. package/lib/module/LocalMediaHandler.js.map +1 -1
  15. package/lib/module/LocalMediaUtils.js +3 -5
  16. package/lib/module/LocalMediaUtils.js.map +1 -1
  17. package/lib/module/ReactHooks.js +27 -8
  18. package/lib/module/ReactHooks.js.map +1 -1
  19. package/lib/module/index.js.map +1 -1
  20. package/lib/module/utils/version.js +1 -1
  21. package/lib/module/utils/version.js.map +1 -1
  22. package/lib/typescript/LocalMediaHandler.d.ts +18 -4
  23. package/lib/typescript/ReactHooks.d.ts +18 -0
  24. package/lib/typescript/index.d.ts +1 -0
  25. package/lib/typescript/utils/version.d.ts +1 -1
  26. package/package.json +1 -1
  27. package/plugin/build/withRTK.js +50 -22
  28. package/plugin/src/withRTK.ts +58 -23
  29. package/android/src/main/java/com/cloudflare/realtimekit/ForegroundService.java +0 -46
  30. package/android/src/main/java/com/cloudflare/realtimekit/NotificationHelper.java +0 -126
@@ -1 +1 @@
1
- {"version":3,"names":["useState","useEffect","useRef","useReducer","useContext","useCallback","NativeModules","Platform","RealtimeKitClient","RealtimeKitContext","BackgroundTimer","webRTCModuleEmitter","deviceChangeListener","broadcastEmitter","useRealtimeKitClient","clientParams","isRunning","client","setClient","resetOnLeave","initClient","options","current","undefined","cleanUpEvents","init","then","meeting","finally","onLeave","state","self","on","removeListener","connectedMeetings","onMeetingChanged","newClient","setTimeout","shouldUpdate","slice","newSlice","Array","isArray","length","useRTKSelector","selector","useRealtimeKitSelector","updates","Error","forceUpdate","c","currentState","listener","newStateSlice","addListener","RTKHelper","removeAllListeners","stop","OS","isForegroundServiceRunning","val","stopService","_err"],"sources":["ReactHooks.tsx"],"sourcesContent":["import {\n useState,\n useEffect,\n useRef,\n useReducer,\n useContext,\n useCallback,\n} from 'react';\nimport { NativeModules, Platform } from 'react-native';\nimport RealtimeKitClient, {\n RTKClientOptions,\n LeaveRoomState,\n} from '@cloudflare/realtimekit';\nimport { RealtimeKitContext } from './ReactContext';\nimport BackgroundTimer from './BackgroundHandler';\nimport { webRTCModuleEmitter } from './AudioSampleHandler';\nimport { deviceChangeListener } from './LocalMediaUtils';\nimport { broadcastEmitter } from './LocalMediaHandler';\n\ninterface RealtimeKitClientParams {\n resetOnLeave?: boolean;\n}\n\n/**\n * Hook which manages a RealtimeKitClient instance\n */\nexport const useRealtimeKitClient = (\n clientParams?: RealtimeKitClientParams\n): [\n RealtimeKitClient | undefined,\n (options: RTKClientOptions) => Promise<RealtimeKitClient | undefined>\n] => {\n const isRunning = useRef(false);\n const [client, setClient] = useState<RealtimeKitClient>();\n const resetOnLeave = clientParams?.resetOnLeave;\n\n const initClient = useCallback(\n async (\n options: RTKClientOptions\n ): Promise<RealtimeKitClient | undefined> => {\n if (isRunning.current) return undefined;\n isRunning.current = true;\n cleanUpEvents();\n return RealtimeKitClient.init(options)\n .then((meeting: RealtimeKitClient) => {\n setClient(meeting);\n return meeting;\n })\n .finally(() => {\n isRunning.current = false;\n });\n },\n []\n );\n\n useEffect(() => {\n if (!client || !resetOnLeave) return () => {};\n const onLeave = ({ state }: { state: LeaveRoomState }) => {\n if (state !== 'connected-meeting' && state !== 'disconnected') {\n setClient(undefined);\n }\n };\n\n client.self.on('roomLeft', onLeave);\n return () => {\n client.self.removeListener('roomLeft', onLeave);\n };\n }, [client, resetOnLeave]);\n\n useEffect(() => {\n if (!client || !client.connectedMeetings) return () => {};\n const onMeetingChanged = (newClient: RealtimeKitClient) => {\n // NOTE: do NOT clear this timeout in the effect cleanup. `setClient(\n // undefined)` re-runs this effect (client is a dependency) and its\n // cleanup fires *before* the timeout — clearing it would drop the new\n // client and leave the UI stuck on the loading screen.\n setClient(undefined);\n setTimeout(() => {\n setClient(newClient);\n }, 100);\n };\n client.connectedMeetings.on('meetingChanged', onMeetingChanged);\n return () => {\n client.connectedMeetings.removeListener(\n 'meetingChanged',\n onMeetingChanged\n );\n };\n }, [client]);\n\n return [client, initClient] as [\n RealtimeKitClient | undefined,\n (options: RTKClientOptions) => Promise<RealtimeKitClient | undefined>\n ];\n};\n\n/**\n * Utility function which determines whether to trigger update or not\n */\nconst shouldUpdate = <StateSlice,>(\n slice: StateSlice,\n newSlice: StateSlice\n): boolean => {\n if (slice !== newSlice) {\n return true;\n }\n if (Array.isArray(slice) && Array.isArray(newSlice)) {\n if (slice.length !== newSlice.length) {\n return true;\n }\n }\n\n return false;\n};\n\ntype StateSelector<T extends object, U> = (state: T) => U;\n\n/**\n * @deprecated Use `useRealtimeKitSelector` instead.\n * Hook which extracts data from the RealtimeKitClient object\n */\nexport const useRTKSelector = <StateSlice,>(\n selector: StateSelector<RealtimeKitClient, StateSlice>\n) => {\n return useRealtimeKitSelector(selector);\n};\n\n/**\n * Hook which extracts data from the RealtimeKitClient object\n */\nexport const useRealtimeKitSelector = <StateSlice,>(\n selector: StateSelector<RealtimeKitClient, StateSlice>\n) => {\n const { meeting, updates } = useContext(RealtimeKitContext);\n if (!meeting)\n throw new Error(\n 'useRealtimeKitSelector must be used within the RealtimeKitProvider'\n );\n const state = useRef(selector(meeting));\n const [, forceUpdate] = useReducer((c) => c + 1, 0) as [never, () => void];\n\n useEffect(() => {\n const currentState = meeting && selector(meeting);\n const listener = () => {\n const newStateSlice = meeting && selector(meeting);\n if (shouldUpdate(state.current, newStateSlice)) {\n state.current = newStateSlice;\n forceUpdate();\n }\n };\n if (\n currentState &&\n typeof currentState === 'object' &&\n 'addListener' in currentState\n ) {\n (currentState as any).addListener('*', forceUpdate);\n return () => {\n (currentState as any).removeListener('*', forceUpdate);\n };\n }\n updates?.addListener(listener);\n return () => {\n updates?.removeListener(listener);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [meeting, updates]);\n return state.current;\n};\n\nfunction cleanUpEvents() {\n const { RTKHelper } = NativeModules;\n\n webRTCModuleEmitter.removeAllListeners('audioSamples');\n deviceChangeListener.removeAllListeners('onAudioDeviceChanged');\n BackgroundTimer.stop();\n\n try {\n if (Platform.OS === 'android') {\n RTKHelper.isForegroundServiceRunning().then((val) => {\n if (val) {\n RTKHelper.stopService();\n }\n });\n } else {\n broadcastEmitter.removeAllListeners('iOS_BroadcastStarted');\n broadcastEmitter.removeAllListeners('iOS_BroadcastStopped');\n }\n } catch (_err) {}\n}\nexport * from './ReactContext';\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useState","useEffect","useRef","useReducer","useContext","useCallback","NativeModules","Platform","RealtimeKitClient","RealtimeKitContext","BackgroundTimer","webRTCModuleEmitter","deviceChangeListener","broadcastEmitter","LocalMediaHandler","useRealtimeKitClient","clientParams","isRunning","client","setClient","resetOnLeave","keepAliveService","undefined","configure","initClient","options","current","cleanUpEvents","init","then","meeting","finally","onLeave","state","OS","_NativeModules$RTKHel","_NativeModules$RTKHel2","RTKHelper","stopMeetingService","call","self","on","removeListener","connectedMeetings","onMeetingChanged","newClient","setTimeout","shouldUpdate","slice","newSlice","Array","isArray","length","useRTKSelector","selector","useRealtimeKitSelector","updates","Error","forceUpdate","c","currentState","listener","newStateSlice","addListener","removeAllListeners","stop","_NativeModules$RTKHel3","_NativeModules$RTKHel4","_err"],"sources":["ReactHooks.tsx"],"sourcesContent":["import {\n useState,\n useEffect,\n useRef,\n useReducer,\n useContext,\n useCallback,\n} from 'react';\nimport { NativeModules, Platform } from 'react-native';\nimport RealtimeKitClient, {\n RTKClientOptions,\n LeaveRoomState,\n} from '@cloudflare/realtimekit';\nimport { RealtimeKitContext } from './ReactContext';\nimport BackgroundTimer from './BackgroundHandler';\nimport { webRTCModuleEmitter } from './AudioSampleHandler';\nimport { deviceChangeListener } from './LocalMediaUtils';\nimport { broadcastEmitter } from './LocalMediaHandler';\nimport LocalMediaHandler, { MeetingServiceConfig } from './LocalMediaHandler';\n\n/**\n * Public configuration for the Android meeting foreground service.\n * Structurally identical to the relevant fields of MeetingServiceConfig so\n * no cast is required when passing it to LocalMediaHandler.configure().\n */\nexport type KeepAliveServiceConfig = Pick<\n MeetingServiceConfig,\n 'enabled' | 'title' | 'text'\n>;\n\ninterface RealtimeKitClientParams {\n resetOnLeave?: boolean;\n /**\n * Android only. Configures (or disables) the meeting foreground service.\n *\n * @example\n * // Disable:\n * useRealtimeKitClient({ keepAliveService: { enabled: false } })\n *\n * // Custom notification text:\n * useRealtimeKitClient({ keepAliveService: { enabled: true, title: 'Team call', text: 'Tap to return' } })\n */\n keepAliveService?: KeepAliveServiceConfig;\n}\n\n/**\n * Hook which manages a RealtimeKitClient instance\n */\nexport const useRealtimeKitClient = (\n clientParams?: RealtimeKitClientParams\n): [\n RealtimeKitClient | undefined,\n (options: RTKClientOptions) => Promise<RealtimeKitClient | undefined>\n] => {\n const isRunning = useRef(false);\n const [client, setClient] = useState<RealtimeKitClient>();\n const resetOnLeave = clientParams?.resetOnLeave;\n const keepAliveService = clientParams?.keepAliveService;\n\n // Configure synchronously during render — not in useEffect — so the\n // module-level config is guaranteed to be in place before initClient()\n // is called. useEffect is asynchronous w.r.t. render and would create a\n // race where initClient() fires before the config is applied.\n // LocalMediaHandler.configure() is idempotent for the same values.\n if (keepAliveService !== undefined) {\n LocalMediaHandler.configure(keepAliveService);\n }\n\n const initClient = useCallback(\n async (\n options: RTKClientOptions\n ): Promise<RealtimeKitClient | undefined> => {\n if (isRunning.current) return undefined;\n isRunning.current = true;\n cleanUpEvents();\n return RealtimeKitClient.init(options)\n .then((meeting: RealtimeKitClient) => {\n setClient(meeting);\n return meeting;\n })\n .finally(() => {\n isRunning.current = false;\n });\n },\n []\n );\n\n useEffect(() => {\n if (!client || !resetOnLeave) return () => {};\n const onLeave = ({ state }: { state: LeaveRoomState }) => {\n if (state !== 'connected-meeting' && state !== 'disconnected') {\n setClient(undefined);\n }\n if (\n state === 'ended' ||\n state === 'kicked' ||\n state === 'rejected' ||\n state === 'failed'\n ) {\n // Stop the KeepAlive foreground service when the meeting ends.\n if (Platform.OS === 'android') {\n NativeModules.RTKHelper?.stopMeetingService?.();\n }\n }\n };\n\n client.self.on('roomLeft', onLeave);\n return () => {\n client.self.removeListener('roomLeft', onLeave);\n };\n }, [client, resetOnLeave]);\n\n useEffect(() => {\n if (!client || !client.connectedMeetings) return () => {};\n const onMeetingChanged = (newClient: RealtimeKitClient) => {\n // NOTE: do NOT clear this timeout in the effect cleanup. `setClient(\n // undefined)` re-runs this effect (client is a dependency) and its\n // cleanup fires *before* the timeout — clearing it would drop the new\n // client and leave the UI stuck on the loading screen.\n setClient(undefined);\n setTimeout(() => {\n setClient(newClient);\n }, 100);\n };\n client.connectedMeetings.on('meetingChanged', onMeetingChanged);\n return () => {\n client.connectedMeetings.removeListener(\n 'meetingChanged',\n onMeetingChanged\n );\n };\n }, [client]);\n\n return [client, initClient] as [\n RealtimeKitClient | undefined,\n (options: RTKClientOptions) => Promise<RealtimeKitClient | undefined>\n ];\n};\n\n/**\n * Utility function which determines whether to trigger update or not\n */\nconst shouldUpdate = <StateSlice,>(\n slice: StateSlice,\n newSlice: StateSlice\n): boolean => {\n if (slice !== newSlice) {\n return true;\n }\n if (Array.isArray(slice) && Array.isArray(newSlice)) {\n if (slice.length !== newSlice.length) {\n return true;\n }\n }\n\n return false;\n};\n\ntype StateSelector<T extends object, U> = (state: T) => U;\n\n/**\n * @deprecated Use `useRealtimeKitSelector` instead.\n * Hook which extracts data from the RealtimeKitClient object\n */\nexport const useRTKSelector = <StateSlice,>(\n selector: StateSelector<RealtimeKitClient, StateSlice>\n) => {\n return useRealtimeKitSelector(selector);\n};\n\n/**\n * Hook which extracts data from the RealtimeKitClient object\n */\nexport const useRealtimeKitSelector = <StateSlice,>(\n selector: StateSelector<RealtimeKitClient, StateSlice>\n) => {\n const { meeting, updates } = useContext(RealtimeKitContext);\n if (!meeting)\n throw new Error(\n 'useRealtimeKitSelector must be used within the RealtimeKitProvider'\n );\n const state = useRef(selector(meeting));\n const [, forceUpdate] = useReducer((c) => c + 1, 0) as [never, () => void];\n\n useEffect(() => {\n const currentState = meeting && selector(meeting);\n const listener = () => {\n const newStateSlice = meeting && selector(meeting);\n if (shouldUpdate(state.current, newStateSlice)) {\n state.current = newStateSlice;\n forceUpdate();\n }\n };\n if (\n currentState &&\n typeof currentState === 'object' &&\n 'addListener' in currentState\n ) {\n (currentState as any).addListener('*', forceUpdate);\n return () => {\n (currentState as any).removeListener('*', forceUpdate);\n };\n }\n updates?.addListener(listener);\n return () => {\n updates?.removeListener(listener);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [meeting, updates]);\n return state.current;\n};\n\nfunction cleanUpEvents() {\n webRTCModuleEmitter.removeAllListeners('audioSamples');\n deviceChangeListener.removeAllListeners('onAudioDeviceChanged');\n BackgroundTimer.stop();\n\n try {\n if (Platform.OS === 'android') {\n NativeModules.RTKHelper?.stopMeetingService?.();\n } else {\n broadcastEmitter.removeAllListeners('iOS_BroadcastStarted');\n broadcastEmitter.removeAllListeners('iOS_BroadcastStopped');\n }\n } catch (_err) {}\n}\nexport * from './ReactContext';\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":["NativeModules","Platform","RealtimeKitMeeting","LocalMediaHandler","AudioSampleHandler","BackgroundTimer","registerGlobals","DeviceInfo","encode","decode","global","atob","btoa","RealtimeKitProvider","useRealtimeKitClient","useRTKSelector","useRealtimeKitSelector","useRealtimeKitMeeting","LINKING_ERROR","select","ios","default","Core","Proxy","get","Error","Object","defineProperty","navigator","configurable"],"sources":["index.tsx"],"sourcesContent":["import { NativeModules, Platform } from 'react-native';\nimport RealtimeKitMeeting from '@cloudflare/realtimekit';\nimport LocalMediaHandler from './LocalMediaHandler';\nimport AudioSampleHandler from './AudioSampleHandler';\nimport BackgroundTimer from './BackgroundHandler';\nimport { registerGlobals } from '@cloudflare/react-native-webrtc';\nimport './utils/crypto';\nimport 'text-encoding-polyfill';\nimport 'react-native-url-polyfill/auto';\nimport DeviceInfo from './DeviceInfo';\nimport { encode, decode } from 'base-64';\nglobal.atob = decode;\nglobal.btoa = encode;\n\nexport {\n RealtimeKitProvider,\n useRealtimeKitClient,\n useRTKSelector,\n useRealtimeKitSelector,\n useRealtimeKitMeeting,\n} from './ReactHooks';\n\nregisterGlobals();\n\nconst LINKING_ERROR =\n `The package '@cloudflare/realtimekit-react-native' doesn't seem to be linked. Make sure: \\n\\n` +\n Platform.select({ ios: \"- You have run 'pod install'\\n\", default: '' }) +\n '- You rebuilt the app after installing the package\\n' +\n '- You are not using Expo managed workflow\\n';\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst Core = NativeModules.Core\n ? NativeModules.Core\n : new Proxy(\n {},\n {\n get() {\n throw new Error(LINKING_ERROR);\n },\n }\n );\n\ndeclare global {\n // @ts-ignore\n var navigator: any;\n}\n\nObject.defineProperty(navigator as any, 'isReactNative', {\n get: function () {\n return true;\n },\n configurable: true,\n});\n\nObject.defineProperty(navigator as any, 'userAgent', {\n get: function () {\n return 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4753.0 Safari/537.36'; // customized user agent\n },\n configurable: true,\n});\n\nObject.defineProperty(navigator as any, 'RNLocalMediaHandlerImpl', {\n get: function () {\n return LocalMediaHandler;\n },\n});\n\nObject.defineProperty(navigator as any, 'RNAudioSampleHandlerImpl', {\n get: function () {\n return AudioSampleHandler;\n },\n});\n\nObject.defineProperty(navigator as any, 'RNBackgroundTimerImpl', {\n get: function () {\n return BackgroundTimer;\n },\n});\n\nObject.defineProperty(navigator as any, 'RNDeviceInfoImpl', {\n get: function () {\n return DeviceInfo;\n },\n});\n\nexport default RealtimeKitMeeting;\n"],"mappings":"AAAA,SAASA,aAAa,EAAEC,QAAQ,QAAQ,cAAc;AACtD,OAAOC,kBAAkB,MAAM,yBAAyB;AACxD,OAAOC,iBAAiB,MAAM,qBAAqB;AACnD,OAAOC,kBAAkB,MAAM,sBAAsB;AACrD,OAAOC,eAAe,MAAM,qBAAqB;AACjD,SAASC,eAAe,QAAQ,iCAAiC;AACjE,OAAO,gBAAgB;AACvB,OAAO,wBAAwB;AAC/B,OAAO,gCAAgC;AACvC,OAAOC,UAAU,MAAM,cAAc;AACrC,SAASC,MAAM,EAAEC,MAAM,QAAQ,SAAS;AACxCC,MAAM,CAACC,IAAI,GAAGF,MAAM;AACpBC,MAAM,CAACE,IAAI,GAAGJ,MAAM;AAEpB,SACEK,mBAAmB,EACnBC,oBAAoB,EACpBC,cAAc,EACdC,sBAAsB,EACtBC,qBAAqB,QAChB,cAAc;AAErBX,eAAe,CAAC,CAAC;AAEjB,MAAMY,aAAa,GACjB,+FAA+F,GAC/FjB,QAAQ,CAACkB,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,6CAA6C;;AAE/C;AACA,MAAMC,IAAI,GAAGtB,aAAa,CAACsB,IAAI,GAC3BtB,aAAa,CAACsB,IAAI,GAClB,IAAIC,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACP,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAOLQ,MAAM,CAACC,cAAc,CAACC,SAAS,EAAS,eAAe,EAAE;EACvDJ,GAAG,EAAE,SAAAA,CAAA,EAAY;IACf,OAAO,IAAI;EACb,CAAC;EACDK,YAAY,EAAE;AAChB,CAAC,CAAC;AAEFH,MAAM,CAACC,cAAc,CAACC,SAAS,EAAS,WAAW,EAAE;EACnDJ,GAAG,EAAE,SAAAA,CAAA,EAAY;IACf,OAAO,yHAAyH,CAAC,CAAC;EACpI,CAAC;EACDK,YAAY,EAAE;AAChB,CAAC,CAAC;AAEFH,MAAM,CAACC,cAAc,CAACC,SAAS,EAAS,yBAAyB,EAAE;EACjEJ,GAAG,EAAE,SAAAA,CAAA,EAAY;IACf,OAAOrB,iBAAiB;EAC1B;AACF,CAAC,CAAC;AAEFuB,MAAM,CAACC,cAAc,CAACC,SAAS,EAAS,0BAA0B,EAAE;EAClEJ,GAAG,EAAE,SAAAA,CAAA,EAAY;IACf,OAAOpB,kBAAkB;EAC3B;AACF,CAAC,CAAC;AAEFsB,MAAM,CAACC,cAAc,CAACC,SAAS,EAAS,uBAAuB,EAAE;EAC/DJ,GAAG,EAAE,SAAAA,CAAA,EAAY;IACf,OAAOnB,eAAe;EACxB;AACF,CAAC,CAAC;AAEFqB,MAAM,CAACC,cAAc,CAACC,SAAS,EAAS,kBAAkB,EAAE;EAC1DJ,GAAG,EAAE,SAAAA,CAAA,EAAY;IACf,OAAOjB,UAAU;EACnB;AACF,CAAC,CAAC;AAEF,eAAeL,kBAAkB","ignoreList":[]}
1
+ {"version":3,"names":["NativeModules","Platform","RealtimeKitMeeting","LocalMediaHandler","AudioSampleHandler","BackgroundTimer","registerGlobals","DeviceInfo","encode","decode","global","atob","btoa","RealtimeKitProvider","useRealtimeKitClient","useRTKSelector","useRealtimeKitSelector","useRealtimeKitMeeting","LINKING_ERROR","select","ios","default","Core","Proxy","get","Error","Object","defineProperty","navigator","configurable"],"sources":["index.tsx"],"sourcesContent":["import { NativeModules, Platform } from 'react-native';\nimport RealtimeKitMeeting from '@cloudflare/realtimekit';\nimport LocalMediaHandler from './LocalMediaHandler';\nimport AudioSampleHandler from './AudioSampleHandler';\nimport BackgroundTimer from './BackgroundHandler';\nimport { registerGlobals } from '@cloudflare/react-native-webrtc';\nimport './utils/crypto';\nimport 'text-encoding-polyfill';\nimport 'react-native-url-polyfill/auto';\nimport DeviceInfo from './DeviceInfo';\nimport { encode, decode } from 'base-64';\nexport type { KeepAliveServiceConfig } from './ReactHooks';\nglobal.atob = decode;\nglobal.btoa = encode;\n\nexport {\n RealtimeKitProvider,\n useRealtimeKitClient,\n useRTKSelector,\n useRealtimeKitSelector,\n useRealtimeKitMeeting,\n} from './ReactHooks';\n\nregisterGlobals();\n\nconst LINKING_ERROR =\n `The package '@cloudflare/realtimekit-react-native' doesn't seem to be linked. Make sure: \\n\\n` +\n Platform.select({ ios: \"- You have run 'pod install'\\n\", default: '' }) +\n '- You rebuilt the app after installing the package\\n' +\n '- You are not using Expo managed workflow\\n';\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst Core = NativeModules.Core\n ? NativeModules.Core\n : new Proxy(\n {},\n {\n get() {\n throw new Error(LINKING_ERROR);\n },\n }\n );\n\ndeclare global {\n // @ts-ignore\n var navigator: any;\n}\n\nObject.defineProperty(navigator as any, 'isReactNative', {\n get: function () {\n return true;\n },\n configurable: true,\n});\n\nObject.defineProperty(navigator as any, 'userAgent', {\n get: function () {\n return 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4753.0 Safari/537.36'; // customized user agent\n },\n configurable: true,\n});\n\nObject.defineProperty(navigator as any, 'RNLocalMediaHandlerImpl', {\n get: function () {\n return LocalMediaHandler;\n },\n});\n\nObject.defineProperty(navigator as any, 'RNAudioSampleHandlerImpl', {\n get: function () {\n return AudioSampleHandler;\n },\n});\n\nObject.defineProperty(navigator as any, 'RNBackgroundTimerImpl', {\n get: function () {\n return BackgroundTimer;\n },\n});\n\nObject.defineProperty(navigator as any, 'RNDeviceInfoImpl', {\n get: function () {\n return DeviceInfo;\n },\n});\n\nexport default RealtimeKitMeeting;\n"],"mappings":"AAAA,SAASA,aAAa,EAAEC,QAAQ,QAAQ,cAAc;AACtD,OAAOC,kBAAkB,MAAM,yBAAyB;AACxD,OAAOC,iBAAiB,MAAM,qBAAqB;AACnD,OAAOC,kBAAkB,MAAM,sBAAsB;AACrD,OAAOC,eAAe,MAAM,qBAAqB;AACjD,SAASC,eAAe,QAAQ,iCAAiC;AACjE,OAAO,gBAAgB;AACvB,OAAO,wBAAwB;AAC/B,OAAO,gCAAgC;AACvC,OAAOC,UAAU,MAAM,cAAc;AACrC,SAASC,MAAM,EAAEC,MAAM,QAAQ,SAAS;AAExCC,MAAM,CAACC,IAAI,GAAGF,MAAM;AACpBC,MAAM,CAACE,IAAI,GAAGJ,MAAM;AAEpB,SACEK,mBAAmB,EACnBC,oBAAoB,EACpBC,cAAc,EACdC,sBAAsB,EACtBC,qBAAqB,QAChB,cAAc;AAErBX,eAAe,CAAC,CAAC;AAEjB,MAAMY,aAAa,GACjB,+FAA+F,GAC/FjB,QAAQ,CAACkB,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,6CAA6C;;AAE/C;AACA,MAAMC,IAAI,GAAGtB,aAAa,CAACsB,IAAI,GAC3BtB,aAAa,CAACsB,IAAI,GAClB,IAAIC,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACP,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAOLQ,MAAM,CAACC,cAAc,CAACC,SAAS,EAAS,eAAe,EAAE;EACvDJ,GAAG,EAAE,SAAAA,CAAA,EAAY;IACf,OAAO,IAAI;EACb,CAAC;EACDK,YAAY,EAAE;AAChB,CAAC,CAAC;AAEFH,MAAM,CAACC,cAAc,CAACC,SAAS,EAAS,WAAW,EAAE;EACnDJ,GAAG,EAAE,SAAAA,CAAA,EAAY;IACf,OAAO,yHAAyH,CAAC,CAAC;EACpI,CAAC;EACDK,YAAY,EAAE;AAChB,CAAC,CAAC;AAEFH,MAAM,CAACC,cAAc,CAACC,SAAS,EAAS,yBAAyB,EAAE;EACjEJ,GAAG,EAAE,SAAAA,CAAA,EAAY;IACf,OAAOrB,iBAAiB;EAC1B;AACF,CAAC,CAAC;AAEFuB,MAAM,CAACC,cAAc,CAACC,SAAS,EAAS,0BAA0B,EAAE;EAClEJ,GAAG,EAAE,SAAAA,CAAA,EAAY;IACf,OAAOpB,kBAAkB;EAC3B;AACF,CAAC,CAAC;AAEFsB,MAAM,CAACC,cAAc,CAACC,SAAS,EAAS,uBAAuB,EAAE;EAC/DJ,GAAG,EAAE,SAAAA,CAAA,EAAY;IACf,OAAOnB,eAAe;EACxB;AACF,CAAC,CAAC;AAEFqB,MAAM,CAACC,cAAc,CAACC,SAAS,EAAS,kBAAkB,EAAE;EAC1DJ,GAAG,EAAE,SAAAA,CAAA,EAAY;IACf,OAAOjB,UAAU;EACnB;AACF,CAAC,CAAC;AAEF,eAAeL,kBAAkB","ignoreList":[]}
@@ -1,2 +1,2 @@
1
- export const SDK_VERSION = '1.2.0';
1
+ export const SDK_VERSION = '1.2.0-staging.1';
2
2
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["SDK_VERSION"],"sources":["version.ts"],"sourcesContent":["export const SDK_VERSION = '1.2.0';\n"],"mappings":"AAAA,OAAO,MAAMA,WAAW,GAAG,OAAO","ignoreList":[]}
1
+ {"version":3,"names":["SDK_VERSION"],"sources":["version.ts"],"sourcesContent":["export const SDK_VERSION = '1.2.0-staging.1';\n"],"mappings":"AAAA,OAAO,MAAMA,WAAW,GAAG,iBAAiB","ignoreList":[]}
@@ -3,8 +3,18 @@ import LocalMediaUtils from './LocalMediaUtils';
3
3
  import { MediaPermission } from './LocalMediaInterfaces';
4
4
  import type { MediaStreamTrack } from '@cloudflare/react-native-webrtc';
5
5
  import type { MediaDeviceInfo } from './LocalMediaInterfaces';
6
- import { NativeEventEmitter } from 'react-native';
6
+ import { AppStateStatus, NativeEventEmitter } from 'react-native';
7
7
  export declare const broadcastEmitter: NativeEventEmitter;
8
+ export interface MeetingServiceConfig {
9
+ /** Whether to start the Android meeting foreground service. Default: true. */
10
+ enabled: boolean;
11
+ /** Notification title. Default: "In a call". */
12
+ title?: string;
13
+ /** Notification body text. Default: "Tap to return to your meeting". */
14
+ text?: string;
15
+ channelId?: string;
16
+ channelName?: string;
17
+ }
8
18
  export declare enum MediaEvents {
9
19
  AUDIO_TRACK_CHANGE = 0,
10
20
  VIDEO_TRACK_CHANGE = 1,
@@ -38,8 +48,7 @@ export default class LocalMediaHandler extends EventEmitter {
38
48
  audio?: MediaPermission;
39
49
  video?: MediaPermission;
40
50
  };
41
- _handleAppStateChange: (nextAppState: any) => void;
42
- private configureForeground;
51
+ _handleAppStateChange: (nextAppState: AppStateStatus) => void;
43
52
  constructor(localMediaUtils: LocalMediaUtils);
44
53
  private conditionallyRestartAudio;
45
54
  private conditionallyRestartVideo;
@@ -85,7 +94,6 @@ export default class LocalMediaHandler extends EventEmitter {
85
94
  setDevice(route: MediaDeviceInfo): Promise<void>;
86
95
  private oniOSBroadcastStart;
87
96
  private oniOSBroadcastStop;
88
- toggleScreenShare(): Promise<void>;
89
97
  enableScreenShare(): Promise<void>;
90
98
  disableScreenShare(): Promise<void>;
91
99
  getAllDevices(): MediaDeviceInfo[];
@@ -110,7 +118,13 @@ export default class LocalMediaHandler extends EventEmitter {
110
118
  removed: MediaDeviceInfo[];
111
119
  selectedDevice?: string;
112
120
  }, forceDeviceChange: boolean): Promise<void>;
121
+ /**
122
+ * Override the meeting foreground service config before the first meeting
123
+ * session. Called by useRealtimeKitClient when keepAliveService is set.
124
+ */
125
+ static configure(config: Partial<MeetingServiceConfig>): void;
113
126
  static init(_: any): Promise<LocalMediaHandler>;
127
+ private startMeetingService;
114
128
  emit(event: keyof typeof MediaEvents, ...args: any[]): boolean;
115
129
  on(event: keyof typeof MediaEvents, listener: (...args: any[]) => void): this;
116
130
  destruct(): void;
@@ -1,6 +1,24 @@
1
1
  import RealtimeKitClient, { RTKClientOptions } from '@cloudflare/realtimekit';
2
+ import { MeetingServiceConfig } from './LocalMediaHandler';
3
+ /**
4
+ * Public configuration for the Android meeting foreground service.
5
+ * Structurally identical to the relevant fields of MeetingServiceConfig so
6
+ * no cast is required when passing it to LocalMediaHandler.configure().
7
+ */
8
+ export type KeepAliveServiceConfig = Pick<MeetingServiceConfig, 'enabled' | 'title' | 'text'>;
2
9
  interface RealtimeKitClientParams {
3
10
  resetOnLeave?: boolean;
11
+ /**
12
+ * Android only. Configures (or disables) the meeting foreground service.
13
+ *
14
+ * @example
15
+ * // Disable:
16
+ * useRealtimeKitClient({ keepAliveService: { enabled: false } })
17
+ *
18
+ * // Custom notification text:
19
+ * useRealtimeKitClient({ keepAliveService: { enabled: true, title: 'Team call', text: 'Tap to return' } })
20
+ */
21
+ keepAliveService?: KeepAliveServiceConfig;
4
22
  }
5
23
  /**
6
24
  * Hook which manages a RealtimeKitClient instance
@@ -2,6 +2,7 @@ import RealtimeKitMeeting from '@cloudflare/realtimekit';
2
2
  import './utils/crypto';
3
3
  import 'text-encoding-polyfill';
4
4
  import 'react-native-url-polyfill/auto';
5
+ export type { KeepAliveServiceConfig } from './ReactHooks';
5
6
  export { RealtimeKitProvider, useRealtimeKitClient, useRTKSelector, useRealtimeKitSelector, useRealtimeKitMeeting, } from './ReactHooks';
6
7
  declare global {
7
8
  var navigator: any;
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "1.2.0";
1
+ export declare const SDK_VERSION = "1.2.0-staging.1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudflare/realtimekit-react-native",
3
- "version": "1.2.0-staging.0",
3
+ "version": "1.2.0-staging.1",
4
4
  "description": "Cloudflare RealtimeKit SDK for react native",
5
5
  "main": "lib/commonjs/index",
6
6
  "author": "Cloudflare",
@@ -38,23 +38,13 @@ const withIosPermissions = (c, { microphonePermission, cameraPermission, library
38
38
  });
39
39
  };
40
40
  /**
41
- * Adds the following to the `AndroidManifest.xml`:
42
- * <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
43
- <uses-permission android:name="android.permission.BLUETOOTH"/>
44
- <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
45
- <uses-permission android:name="android.permission.CAMERA"/>
46
- <uses-permission android:name="android.permission.INTERNET"/>
47
- <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
48
- <uses-permission android:name="android.permission.RECORD_AUDIO"/>
49
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
50
- <uses-permission android:name="android.permission.WAKE_LOCK"/>
51
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
52
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
53
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
54
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
55
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
56
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
57
- <uses-permission android:name="android.permission.MICROPHONE" />
41
+ * Adds the following permissions to `AndroidManifest.xml`:
42
+ * ACCESS_NETWORK_STATE, BLUETOOTH, CAMERA, INTERNET,
43
+ * MODIFY_AUDIO_SETTINGS, RECORD_AUDIO, SYSTEM_ALERT_WINDOW, WAKE_LOCK,
44
+ * POST_NOTIFICATIONS,
45
+ * FOREGROUND_SERVICE, FOREGROUND_SERVICE_CAMERA, FOREGROUND_SERVICE_MICROPHONE,
46
+ * FOREGROUND_SERVICE_MEDIA_PLAYBACK,
47
+ * WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE, MICROPHONE
58
48
  */
59
49
  const withAndroidPermissions = (config) => {
60
50
  return config_plugins_1.AndroidConfig.Permissions.withPermissions(config, [
@@ -66,8 +56,11 @@ const withAndroidPermissions = (config) => {
66
56
  'android.permission.RECORD_AUDIO',
67
57
  'android.permission.SYSTEM_ALERT_WINDOW',
68
58
  'android.permission.WAKE_LOCK',
59
+ 'android.permission.POST_NOTIFICATIONS',
69
60
  'android.permission.FOREGROUND_SERVICE',
70
- 'android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION',
61
+ 'android.permission.FOREGROUND_SERVICE_CAMERA',
62
+ 'android.permission.FOREGROUND_SERVICE_MICROPHONE',
63
+ 'android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK',
71
64
  'android.permission.WRITE_EXTERNAL_STORAGE',
72
65
  'android.permission.READ_EXTERNAL_STORAGE',
73
66
  'android.permission.MICROPHONE',
@@ -78,17 +71,27 @@ const withAndroidScreenshare = (config) => {
78
71
  const manifest = _config.modResults;
79
72
  const app = manifest.manifest.application?.[0];
80
73
  if (app) {
81
- // Avoid duplicate entries
82
- const exists = (app.service || []).some((service) => service.$['android:name'] ===
83
- 'com.cloudflare.realtimekit.ForegroundService');
74
+ const newServiceName = 'com.cloudflare.realtimekit.WebRTCModule.MediaProjectionService';
75
+ // Remove the old ForegroundService entry if it exists (migration from previous versions).
76
+ const oldServiceName = 'com.cloudflare.realtimekit.ForegroundService';
77
+ if (app.service) {
78
+ app.service = app.service.filter((service) => service.$['android:name'] !== oldServiceName);
79
+ }
80
+ // Add the new MediaProjectionService entry if not already present.
81
+ // Note: react-native-webrtc's own AndroidManifest.xml also declares this
82
+ // service, so Expo managed apps that merge library manifests may not need
83
+ // this entry. It is added here explicitly for safety with bare workflow
84
+ // apps that do not perform automatic manifest merging.
85
+ const exists = (app.service || []).some((service) => service.$['android:name'] === newServiceName);
84
86
  if (!exists) {
85
87
  app.service = [
86
88
  ...(app.service || []),
87
89
  {
88
90
  $: {
89
91
  'android:enabled': 'true',
92
+ 'android:exported': 'false',
90
93
  'android:foregroundServiceType': 'mediaProjection',
91
- 'android:name': 'com.cloudflare.realtimekit.ForegroundService',
94
+ 'android:name': newServiceName,
92
95
  },
93
96
  },
94
97
  ];
@@ -114,6 +117,30 @@ const withAndroidBlobProviderAuthority = (config) => {
114
117
  return _config;
115
118
  });
116
119
  };
120
+ const withAndroidMeetingService = (config) => {
121
+ return (0, config_plugins_1.withAndroidManifest)(config, (_config) => {
122
+ const manifest = _config.modResults;
123
+ const app = manifest.manifest.application?.[0];
124
+ if (!app)
125
+ return _config;
126
+ const serviceName = 'com.cloudflare.realtimekit.KeepAliveService';
127
+ const exists = (app.service || []).some((service) => service.$['android:name'] === serviceName);
128
+ if (!exists) {
129
+ app.service = [
130
+ ...(app.service || []),
131
+ {
132
+ $: {
133
+ 'android:name': serviceName,
134
+ 'android:enabled': 'true',
135
+ 'android:exported': 'false',
136
+ 'android:foregroundServiceType': 'camera|microphone|mediaPlayback',
137
+ },
138
+ },
139
+ ];
140
+ }
141
+ return _config;
142
+ });
143
+ };
117
144
  const withVoice = (config, props = {}) => {
118
145
  const _props = props ? props : {};
119
146
  config = withIosPermissions(config, _props);
@@ -121,6 +148,7 @@ const withVoice = (config, props = {}) => {
121
148
  config = withAndroidPermissions(config);
122
149
  }
123
150
  config = withAndroidScreenshare(config);
151
+ config = withAndroidMeetingService(config);
124
152
  config = withAndroidBlobProviderAuthority(config);
125
153
  return config;
126
154
  };
@@ -81,23 +81,13 @@ const withIosPermissions: ConfigPlugin<Props> = (
81
81
  };
82
82
 
83
83
  /**
84
- * Adds the following to the `AndroidManifest.xml`:
85
- * <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
86
- <uses-permission android:name="android.permission.BLUETOOTH"/>
87
- <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
88
- <uses-permission android:name="android.permission.CAMERA"/>
89
- <uses-permission android:name="android.permission.INTERNET"/>
90
- <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
91
- <uses-permission android:name="android.permission.RECORD_AUDIO"/>
92
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
93
- <uses-permission android:name="android.permission.WAKE_LOCK"/>
94
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
95
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
96
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
97
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
98
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
99
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
100
- <uses-permission android:name="android.permission.MICROPHONE" />
84
+ * Adds the following permissions to `AndroidManifest.xml`:
85
+ * ACCESS_NETWORK_STATE, BLUETOOTH, CAMERA, INTERNET,
86
+ * MODIFY_AUDIO_SETTINGS, RECORD_AUDIO, SYSTEM_ALERT_WINDOW, WAKE_LOCK,
87
+ * POST_NOTIFICATIONS,
88
+ * FOREGROUND_SERVICE, FOREGROUND_SERVICE_CAMERA, FOREGROUND_SERVICE_MICROPHONE,
89
+ * FOREGROUND_SERVICE_MEDIA_PLAYBACK,
90
+ * WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE, MICROPHONE
101
91
  */
102
92
  const withAndroidPermissions: ConfigPlugin = (config) => {
103
93
  return AndroidConfig.Permissions.withPermissions(config, [
@@ -109,8 +99,11 @@ const withAndroidPermissions: ConfigPlugin = (config) => {
109
99
  'android.permission.RECORD_AUDIO',
110
100
  'android.permission.SYSTEM_ALERT_WINDOW',
111
101
  'android.permission.WAKE_LOCK',
102
+ 'android.permission.POST_NOTIFICATIONS',
112
103
  'android.permission.FOREGROUND_SERVICE',
113
- 'android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION',
104
+ 'android.permission.FOREGROUND_SERVICE_CAMERA',
105
+ 'android.permission.FOREGROUND_SERVICE_MICROPHONE',
106
+ 'android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK',
114
107
  'android.permission.WRITE_EXTERNAL_STORAGE',
115
108
  'android.permission.READ_EXTERNAL_STORAGE',
116
109
  'android.permission.MICROPHONE',
@@ -123,11 +116,24 @@ const withAndroidScreenshare: ConfigPlugin = (config) => {
123
116
  const app = manifest.manifest.application?.[0];
124
117
 
125
118
  if (app) {
126
- // Avoid duplicate entries
119
+ const newServiceName =
120
+ 'com.cloudflare.realtimekit.WebRTCModule.MediaProjectionService';
121
+
122
+ // Remove the old ForegroundService entry if it exists (migration from previous versions).
123
+ const oldServiceName = 'com.cloudflare.realtimekit.ForegroundService';
124
+ if (app.service) {
125
+ app.service = app.service.filter(
126
+ (service) => service.$['android:name'] !== oldServiceName
127
+ );
128
+ }
129
+
130
+ // Add the new MediaProjectionService entry if not already present.
131
+ // Note: react-native-webrtc's own AndroidManifest.xml also declares this
132
+ // service, so Expo managed apps that merge library manifests may not need
133
+ // this entry. It is added here explicitly for safety with bare workflow
134
+ // apps that do not perform automatic manifest merging.
127
135
  const exists = (app.service || []).some(
128
- (service) =>
129
- service.$['android:name'] ===
130
- 'com.cloudflare.realtimekit.ForegroundService'
136
+ (service) => service.$['android:name'] === newServiceName
131
137
  );
132
138
  if (!exists) {
133
139
  app.service = [
@@ -135,8 +141,9 @@ const withAndroidScreenshare: ConfigPlugin = (config) => {
135
141
  {
136
142
  $: {
137
143
  'android:enabled': 'true',
144
+ 'android:exported': 'false',
138
145
  'android:foregroundServiceType': 'mediaProjection',
139
- 'android:name': 'com.cloudflare.realtimekit.ForegroundService',
146
+ 'android:name': newServiceName,
140
147
  },
141
148
  },
142
149
  ];
@@ -167,6 +174,33 @@ const withAndroidBlobProviderAuthority: ConfigPlugin = (config) => {
167
174
  });
168
175
  };
169
176
 
177
+ const withAndroidMeetingService: ConfigPlugin = (config) => {
178
+ return withAndroidManifest(config, (_config) => {
179
+ const manifest = _config.modResults as AndroidManifest;
180
+ const app = manifest.manifest.application?.[0];
181
+ if (!app) return _config;
182
+
183
+ const serviceName = 'com.cloudflare.realtimekit.KeepAliveService';
184
+ const exists = (app.service || []).some(
185
+ (service) => service.$['android:name'] === serviceName
186
+ );
187
+ if (!exists) {
188
+ app.service = [
189
+ ...(app.service || []),
190
+ {
191
+ $: {
192
+ 'android:name': serviceName,
193
+ 'android:enabled': 'true',
194
+ 'android:exported': 'false',
195
+ 'android:foregroundServiceType': 'camera|microphone|mediaPlayback',
196
+ },
197
+ },
198
+ ];
199
+ }
200
+ return _config;
201
+ });
202
+ };
203
+
170
204
  const withVoice: ConfigPlugin<Props | void> = (config, props = {}) => {
171
205
  const _props = props ? props : {};
172
206
  config = withIosPermissions(config, _props);
@@ -174,6 +208,7 @@ const withVoice: ConfigPlugin<Props | void> = (config, props = {}) => {
174
208
  config = withAndroidPermissions(config);
175
209
  }
176
210
  config = withAndroidScreenshare(config);
211
+ config = withAndroidMeetingService(config);
177
212
  config = withAndroidBlobProviderAuthority(config);
178
213
  return config;
179
214
  };
@@ -1,46 +0,0 @@
1
- package com.cloudflare.realtimekit;
2
-
3
- import android.app.Notification;
4
- import android.app.Service;
5
- import android.content.Intent;
6
- import android.os.Build;
7
- import android.os.IBinder;
8
- import android.content.pm.ServiceInfo;
9
- import android.app.Activity;
10
- import android.widget.Toast;
11
- import com.cloudflare.realtimekit.RTKHolder;
12
-
13
- public class ForegroundService extends Service {
14
- @Override
15
- public IBinder onBind(Intent intent) {
16
- return null;
17
- }
18
-
19
- public int onStartCommand(Intent intent, int flags, int startId) {
20
- String action = intent.getAction();
21
- if (action != null) {
22
- if (action.equals(Constants.ACTION_FOREGROUND_SERVICE_START)) {
23
- if(RTKHolder.currActivity!=null) {
24
- NotificationHelper notificationHelper = NotificationHelper.getInstance(RTKHolder.currActivity);
25
- Notification notification = notificationHelper.buildNotification(RTKHolder.currActivity);
26
- if (notification != null) {
27
- try {
28
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
29
- startForeground(notificationHelper.NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
30
- } else {
31
- startForeground(notificationHelper.NOTIFICATION_ID, notification);
32
- }
33
- } catch (RuntimeException e) {
34
- RTKLogger.w("ForegroundService", "Failed to start ForegroundService due to: " + e.getMessage());
35
- }
36
- }
37
- }
38
-
39
- } else if (action.equals(Constants.ACTION_FOREGROUND_SERVICE_STOP)) {
40
- stopSelf();
41
- }
42
- }
43
- return START_NOT_STICKY;
44
-
45
- }
46
- }
@@ -1,126 +0,0 @@
1
- package com.cloudflare.realtimekit;
2
-
3
- import android.app.Notification;
4
- import android.app.NotificationChannel;
5
- import android.app.NotificationManager;
6
- import android.app.PendingIntent;
7
- import android.content.Context;
8
- import android.content.Intent;
9
- import android.content.pm.ApplicationInfo;
10
- import android.content.pm.PackageManager;
11
- import android.graphics.Bitmap;
12
- import android.graphics.drawable.BitmapDrawable;
13
- import android.graphics.drawable.Drawable;
14
- import android.os.Build;
15
- import android.util.Log;
16
- import androidx.core.app.NotificationCompat;
17
-
18
- import com.facebook.react.bridge.Promise;
19
-
20
- import java.util.Random;
21
-
22
-
23
- class NotificationHelper {
24
- public static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
25
- private static final String CHANNEL_ID = "RealtimeKitNotificationChannel";
26
- private static final String CHANNEL_NAME = "Screen Sharing";
27
- private static final String CHANNEL_DESC = "Screen Sharing";
28
-
29
- private static NotificationHelper instance = null;
30
- private NotificationManager mNotificationManager;
31
-
32
- public static synchronized NotificationHelper getInstance(Context context) {
33
- if (instance == null) {
34
- instance = new NotificationHelper(context);
35
- }
36
- return instance;
37
- }
38
-
39
- private NotificationHelper(Context context) {
40
- mNotificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
41
-
42
- }
43
-
44
- void createNotificationChannel(Promise promise) {
45
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
46
- int channelImportance = NotificationManager.IMPORTANCE_DEFAULT;
47
- boolean enableVibration = false;
48
- NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, channelImportance);
49
- channel.setDescription(CHANNEL_DESC);
50
- channel.enableVibration(enableVibration);
51
- mNotificationManager.createNotificationChannel(channel);
52
- promise.resolve(null);
53
- } else {
54
- promise.reject(Constants.ERROR_ANDROID_VERSION, "RTKForegroundService: Notification channel can be created on Android O+");
55
- }
56
- }
57
-
58
- Notification buildNotification(Context context) {
59
- if (context == null) {
60
- return null;
61
- }
62
- Intent notificationIntent = new Intent(context, context.getClass());
63
- PendingIntent pendingIntent;
64
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
65
- pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_MUTABLE);
66
- }
67
- else {
68
- pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
69
- }
70
-
71
- NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID);
72
- final PackageManager packageManager = context.getPackageManager();
73
- int appIconResId = android.R.mipmap.sym_def_app_icon;
74
- Bitmap largeIcon = null;
75
- try {
76
- final ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
77
- appIconResId=applicationInfo.icon;
78
- Drawable drawable=applicationInfo.loadIcon(packageManager);
79
- largeIcon = ((BitmapDrawable)drawable).getBitmap();
80
- } catch (Exception e) {
81
- //pass
82
- }
83
-
84
- builder
85
- .setCategory(NotificationCompat.CATEGORY_CALL)
86
- .setContentTitle("Screen Share")
87
- .setContentText("You are sharing your screen")
88
- .setPriority(NotificationCompat.PRIORITY_LOW)
89
- .setContentIntent(pendingIntent)
90
- .setOngoing(false)
91
- .setUsesChronometer(false)
92
- .setAutoCancel(true)
93
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
94
- .setOnlyAlertOnce(true)
95
- .setSmallIcon(appIconResId)
96
- .setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE);
97
- if(largeIcon != null){
98
- builder.setLargeIcon(largeIcon);
99
- }
100
-
101
- return builder.build();
102
- }
103
-
104
- private Class getMainActivityClass(Context context) {
105
- String packageName = context.getPackageName();
106
- Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
107
- if (launchIntent == null || launchIntent.getComponent() == null) {
108
- // Log.e("NotificationHelper", "Failed to get launch intent or component");
109
- return null;
110
- }
111
- try {
112
- return Class.forName(launchIntent.getComponent().getClassName());
113
- } catch (ClassNotFoundException e) {
114
- // Log.e("NotificationHelper", "Failed to get main activity class");
115
- return null;
116
- }
117
- }
118
-
119
- private int getResourceIdForResourceName(Context context, String resourceName) {
120
- int resourceId = context.getResources().getIdentifier(resourceName, "drawable", context.getPackageName());
121
- if (resourceId == 0) {
122
- resourceId = context.getResources().getIdentifier(resourceName, "mipmap", context.getPackageName());
123
- }
124
- return resourceId;
125
- }
126
- }