@q9labsai/chalk-react-native 4.1.17 → 5.0.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.
- package/README.md +134 -29
- package/android/build.gradle +35 -0
- package/android/compatibility/ModuleApiCompatibility.java +215 -0
- package/android/src/main/AndroidManifest.xml +2 -1
- package/android/src/main/java/ai/q9labs/chalk/reactnative/ChalkSpaceForegroundService.java +105 -31
- package/android/src/main/kotlin/ai/q9labs/chalk/reactnative/ChalkAndroidConnectionServicePackage.kt +2 -0
- package/android/src/main/kotlin/ai/q9labs/chalk/reactnative/ChalkAudioRouteModule.kt +137 -0
- package/android/src/main/kotlin/ai/q9labs/chalk/reactnative/ChalkCallServiceModule.kt +32 -0
- package/android/src/main/kotlin/ai/q9labs/chalk/reactnative/CommunicationDeviceListener.kt +23 -0
- package/android/src/test/kotlin/ai/q9labs/chalk/compatibility/UnsafeSignatures.kt +29 -0
- package/dist/android/call-service.d.ts +4 -0
- package/dist/android/call-service.d.ts.map +1 -0
- package/dist/avatar/FaceAvatar.d.ts +19 -0
- package/dist/avatar/FaceAvatar.d.ts.map +1 -0
- package/dist/avatar/tile-avatar-size.d.ts +3 -0
- package/dist/avatar/tile-avatar-size.d.ts.map +1 -0
- package/dist/chunk-GYVZSOGQ.js +30 -0
- package/dist/chunk-IHE6EA3T.js +25 -0
- package/dist/chunk-OIBT2FNM.js +1479 -0
- package/dist/client.d.ts +1 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +185 -2
- package/dist/components/EmbeddedWhiteboard.d.ts.map +1 -1
- package/dist/components/native-media-visibility.d.ts +3 -2
- package/dist/components/native-media-visibility.d.ts.map +1 -1
- package/dist/components/useAndroidCallService.d.ts +8 -0
- package/dist/components/useAndroidCallService.d.ts.map +1 -0
- package/dist/dev.d.ts +10 -0
- package/dist/dev.d.ts.map +1 -0
- package/dist/dev.js +10 -0
- package/dist/dock/Dock.d.ts +23 -0
- package/dist/dock/Dock.d.ts.map +1 -0
- package/dist/dock/LeaveMenu.d.ts +12 -0
- package/dist/dock/LeaveMenu.d.ts.map +1 -0
- package/dist/dock/MoreSheet.d.ts +17 -0
- package/dist/dock/MoreSheet.d.ts.map +1 -0
- package/dist/dock/ReactionBurst.d.ts +8 -0
- package/dist/dock/ReactionBurst.d.ts.map +1 -0
- package/dist/dock/RoutePicker.d.ts +16 -0
- package/dist/dock/RoutePicker.d.ts.map +1 -0
- package/dist/dock/audio-route.d.ts +25 -0
- package/dist/dock/audio-route.d.ts.map +1 -0
- package/dist/error-YOPTEH5N.wav +0 -0
- package/dist/frame/CallFrame.d.ts +17 -0
- package/dist/frame/CallFrame.d.ts.map +1 -0
- package/dist/frame/Chalk.d.ts +40 -0
- package/dist/frame/Chalk.d.ts.map +1 -0
- package/dist/frame/back-handler.d.ts +6 -0
- package/dist/frame/back-handler.d.ts.map +1 -0
- package/dist/frame/dev-scenario.d.ts +28 -0
- package/dist/frame/dev-scenario.d.ts.map +1 -0
- package/dist/frame/device-layout.d.ts +16 -0
- package/dist/frame/device-layout.d.ts.map +1 -0
- package/dist/frame/measurement.d.ts +28 -0
- package/dist/frame/measurement.d.ts.map +1 -0
- package/dist/frame/portal.d.ts +24 -0
- package/dist/frame/portal.d.ts.map +1 -0
- package/dist/frame/preferences.d.ts +21 -0
- package/dist/frame/preferences.d.ts.map +1 -0
- package/dist/frame/regions.d.ts +25 -0
- package/dist/frame/regions.d.ts.map +1 -0
- package/dist/hand-DFWVXPH2.wav +0 -0
- package/dist/haptics/haptics.d.ts +12 -0
- package/dist/haptics/haptics.d.ts.map +1 -0
- package/dist/header/CallDetailsSheet.d.ts +18 -0
- package/dist/header/CallDetailsSheet.d.ts.map +1 -0
- package/dist/header/Header.d.ts +17 -0
- package/dist/header/Header.d.ts.map +1 -0
- package/dist/header/QrCode.d.ts +6 -0
- package/dist/header/QrCode.d.ts.map +1 -0
- package/dist/icons/Icon.d.ts +12 -0
- package/dist/icons/Icon.d.ts.map +1 -0
- package/dist/icons/icon-element.d.ts +24 -0
- package/dist/icons/icon-element.d.ts.map +1 -0
- package/dist/icons/icon-paths.d.ts +530 -0
- package/dist/icons/icon-paths.d.ts.map +1 -0
- package/dist/index.d.ts +6 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2956 -38
- package/dist/join-Z6RL5AHE.wav +0 -0
- package/dist/knock-4VBQSHK4.wav +0 -0
- package/dist/leave-MQLZOWQD.wav +0 -0
- package/dist/limbo/Limbo.d.ts +25 -0
- package/dist/limbo/Limbo.d.ts.map +1 -0
- package/dist/limbo/Veil.d.ts +10 -0
- package/dist/limbo/Veil.d.ts.map +1 -0
- package/dist/media/native-capture-error.d.ts +7 -0
- package/dist/media/native-capture-error.d.ts.map +1 -0
- package/dist/message-6Y6WJGTB.wav +0 -0
- package/dist/motion/MovingFrame.d.ts +36 -0
- package/dist/motion/MovingFrame.d.ts.map +1 -0
- package/dist/motion/motion.d.ts +82 -0
- package/dist/motion/motion.d.ts.map +1 -0
- package/dist/motion/presence.d.ts +6 -0
- package/dist/motion/presence.d.ts.map +1 -0
- package/dist/motion/press.d.ts +15 -0
- package/dist/motion/press.d.ts.map +1 -0
- package/dist/mute-H5SJFXVI.wav +0 -0
- package/dist/primitives/BottomSheet.d.ts +36 -0
- package/dist/primitives/BottomSheet.d.ts.map +1 -0
- package/dist/primitives/ChalkText.d.ts +13 -0
- package/dist/primitives/ChalkText.d.ts.map +1 -0
- package/dist/primitives/CopyButton.d.ts +11 -0
- package/dist/primitives/CopyButton.d.ts.map +1 -0
- package/dist/primitives/IconButton.d.ts +26 -0
- package/dist/primitives/IconButton.d.ts.map +1 -0
- package/dist/primitives/Menu.d.ts +39 -0
- package/dist/primitives/Menu.d.ts.map +1 -0
- package/dist/primitives/NameTag.d.ts +19 -0
- package/dist/primitives/NameTag.d.ts.map +1 -0
- package/dist/primitives/PillButton.d.ts +19 -0
- package/dist/primitives/PillButton.d.ts.map +1 -0
- package/dist/primitives/SegmentedControl.d.ts +17 -0
- package/dist/primitives/SegmentedControl.d.ts.map +1 -0
- package/dist/primitives/SheetGroup.d.ts +24 -0
- package/dist/primitives/SheetGroup.d.ts.map +1 -0
- package/dist/primitives/Switch.d.ts +9 -0
- package/dist/primitives/Switch.d.ts.map +1 -0
- package/dist/primitives/TileShell.d.ts +18 -0
- package/dist/primitives/TileShell.d.ts.map +1 -0
- package/dist/primitives/announce.d.ts +3 -0
- package/dist/primitives/announce.d.ts.map +1 -0
- package/dist/primitives/menu-placement.d.ts +32 -0
- package/dist/primitives/menu-placement.d.ts.map +1 -0
- package/dist/primitives/toast/Toast.d.ts +8 -0
- package/dist/primitives/toast/Toast.d.ts.map +1 -0
- package/dist/primitives/toast/ToastStack.d.ts +18 -0
- package/dist/primitives/toast/ToastStack.d.ts.map +1 -0
- package/dist/primitives/toast/toast-store.d.ts +39 -0
- package/dist/primitives/toast/toast-store.d.ts.map +1 -0
- package/dist/recording-Q4GB77Y4.wav +0 -0
- package/dist/sheets/people-chat/ChatNotices.d.ts +14 -0
- package/dist/sheets/people-chat/ChatNotices.d.ts.map +1 -0
- package/dist/sheets/people-chat/ChatTab.d.ts +6 -0
- package/dist/sheets/people-chat/ChatTab.d.ts.map +1 -0
- package/dist/sheets/people-chat/Composer.d.ts +7 -0
- package/dist/sheets/people-chat/Composer.d.ts.map +1 -0
- package/dist/sheets/people-chat/PeopleChatSheet.d.ts +17 -0
- package/dist/sheets/people-chat/PeopleChatSheet.d.ts.map +1 -0
- package/dist/sheets/people-chat/PeopleTab.d.ts +3 -0
- package/dist/sheets/people-chat/PeopleTab.d.ts.map +1 -0
- package/dist/sheets/people-chat/PhotoViewer.d.ts +19 -0
- package/dist/sheets/people-chat/PhotoViewer.d.ts.map +1 -0
- package/dist/sheets/people-chat/TextField.d.ts +4 -0
- package/dist/sheets/people-chat/TextField.d.ts.map +1 -0
- package/dist/sheets/people-chat/attachments.d.ts +33 -0
- package/dist/sheets/people-chat/attachments.d.ts.map +1 -0
- package/dist/sheets/people-chat/chat-model.d.ts +64 -0
- package/dist/sheets/people-chat/chat-model.d.ts.map +1 -0
- package/dist/sheets/people-chat/people-model.d.ts +43 -0
- package/dist/sheets/people-chat/people-model.d.ts.map +1 -0
- package/dist/sounds/sounds.d.ts +33 -0
- package/dist/sounds/sounds.d.ts.map +1 -0
- package/dist/space-client/create-native-space-client.d.ts +0 -4
- package/dist/space-client/create-native-space-client.d.ts.map +1 -1
- package/dist/space-client/random-uuid.d.ts +7 -0
- package/dist/space-client/random-uuid.d.ts.map +1 -0
- package/dist/stage/ContentTile.d.ts +18 -0
- package/dist/stage/ContentTile.d.ts.map +1 -0
- package/dist/stage/ParticipantTile.d.ts +18 -0
- package/dist/stage/ParticipantTile.d.ts.map +1 -0
- package/dist/stage/Stage.d.ts +14 -0
- package/dist/stage/Stage.d.ts.map +1 -0
- package/dist/stage/TileSheet.d.ts +12 -0
- package/dist/stage/TileSheet.d.ts.map +1 -0
- package/dist/stage/camera.d.ts +7 -0
- package/dist/stage/camera.d.ts.map +1 -0
- package/dist/stage/layout.d.ts +57 -0
- package/dist/stage/layout.d.ts.map +1 -0
- package/dist/stage/space-selector.d.ts +10 -0
- package/dist/stage/space-selector.d.ts.map +1 -0
- package/dist/stage/stage-view.d.ts +23 -0
- package/dist/stage/stage-view.d.ts.map +1 -0
- package/dist/status/CallOverlays.d.ts +17 -0
- package/dist/status/CallOverlays.d.ts.map +1 -0
- package/dist/status/ChalkArrival.d.ts +24 -0
- package/dist/status/ChalkArrival.d.ts.map +1 -0
- package/dist/status/StatusPage.d.ts +14 -0
- package/dist/status/StatusPage.d.ts.map +1 -0
- package/dist/status/call-screen.d.ts +20 -0
- package/dist/status/call-screen.d.ts.map +1 -0
- package/dist/test-VD2KAM4D.wav +0 -0
- package/dist/theme/theme.d.ts +15 -0
- package/dist/theme/theme.d.ts.map +1 -0
- package/dist/theme/tokens.d.ts +248 -0
- package/dist/theme/tokens.d.ts.map +1 -0
- package/dist/toasts/CallEventToasts.d.ts +21 -0
- package/dist/toasts/CallEventToasts.d.ts.map +1 -0
- package/dist/toasts/notices.d.ts +34 -0
- package/dist/toasts/notices.d.ts.map +1 -0
- package/dist/ui/ChalkUI.d.ts +12 -0
- package/dist/ui/ChalkUI.d.ts.map +1 -0
- package/dist/ui.d.ts +25 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +77 -0
- package/dist/unmute-2XR2LICU.wav +0 -0
- package/embedded/chalk-whiteboard/asset-manifest.json +2 -2
- package/embedded/chalk-whiteboard/renderer.js +510 -510
- package/ios/ChalkAudioRouteModule.swift +93 -0
- package/ios/ChalkAudioRouteModuleBridge.m +9 -0
- package/package.json +46 -76
- package/src/android/call-service.ts +19 -0
- package/src/avatar/FaceAvatar.tsx +145 -0
- package/src/avatar/tile-avatar-size.ts +4 -0
- package/src/client.ts +1 -5
- package/src/components/EmbeddedWhiteboard.tsx +14 -71
- package/src/components/native-media-visibility.ts +1 -2
- package/src/components/useAndroidCallService.ts +41 -0
- package/src/dev.ts +9 -0
- package/src/dock/Dock.tsx +95 -0
- package/src/dock/LeaveMenu.tsx +21 -0
- package/src/dock/MoreSheet.tsx +233 -0
- package/src/dock/ReactionBurst.tsx +55 -0
- package/src/dock/RoutePicker.tsx +42 -0
- package/src/dock/audio-route.ts +69 -0
- package/src/frame/CallFrame.tsx +209 -0
- package/src/frame/Chalk.tsx +83 -0
- package/src/frame/back-handler.ts +19 -0
- package/src/frame/dev-scenario.ts +48 -0
- package/src/frame/device-layout.tsx +31 -0
- package/src/frame/measurement.ts +44 -0
- package/src/frame/portal.tsx +107 -0
- package/src/frame/preferences.tsx +48 -0
- package/src/frame/regions.test.ts +38 -0
- package/src/frame/regions.ts +60 -0
- package/src/haptics/haptics.ts +19 -0
- package/src/header/CallDetailsSheet.tsx +139 -0
- package/src/header/Header.tsx +107 -0
- package/src/header/QrCode.tsx +34 -0
- package/src/icons/Icon.tsx +35 -0
- package/src/icons/icon-element.ts +11 -0
- package/src/icons/icon-paths.ts +199 -0
- package/src/index.ts +6 -9
- package/src/limbo/Limbo.tsx +159 -0
- package/src/limbo/Veil.tsx +36 -0
- package/src/media/native-capture-error.test.ts +19 -0
- package/src/media/native-capture-error.ts +13 -0
- package/src/motion/MovingFrame.tsx +101 -0
- package/src/motion/motion.ts +33 -0
- package/src/motion/presence.ts +20 -0
- package/src/motion/press.ts +35 -0
- package/src/primitives/BottomSheet.tsx +211 -0
- package/src/primitives/ChalkText.tsx +23 -0
- package/src/primitives/CopyButton.tsx +78 -0
- package/src/primitives/IconButton.tsx +90 -0
- package/src/primitives/Menu.tsx +212 -0
- package/src/primitives/NameTag.tsx +68 -0
- package/src/primitives/PillButton.tsx +56 -0
- package/src/primitives/SegmentedControl.tsx +55 -0
- package/src/primitives/SheetGroup.tsx +72 -0
- package/src/primitives/Switch.tsx +39 -0
- package/src/primitives/TileShell.tsx +53 -0
- package/src/primitives/announce.ts +7 -0
- package/src/primitives/menu-placement.test.ts +50 -0
- package/src/primitives/menu-placement.ts +43 -0
- package/src/primitives/toast/Toast.tsx +50 -0
- package/src/primitives/toast/ToastStack.tsx +89 -0
- package/src/primitives/toast/toast-store.ts +86 -0
- package/src/sheets/people-chat/ChatNotices.tsx +69 -0
- package/src/sheets/people-chat/ChatTab.tsx +186 -0
- package/src/sheets/people-chat/Composer.tsx +182 -0
- package/src/sheets/people-chat/PeopleChatSheet.tsx +100 -0
- package/src/sheets/people-chat/PeopleTab.tsx +326 -0
- package/src/sheets/people-chat/PhotoViewer.tsx +132 -0
- package/src/sheets/people-chat/TextField.tsx +14 -0
- package/src/sheets/people-chat/attachments.tsx +183 -0
- package/src/sheets/people-chat/chat-model.ts +157 -0
- package/src/sheets/people-chat/people-chat-model.test.ts +98 -0
- package/src/sheets/people-chat/people-model.ts +68 -0
- package/src/sounds/assets/error.wav +0 -0
- package/src/sounds/assets/hand.wav +0 -0
- package/src/sounds/assets/join.wav +0 -0
- package/src/sounds/assets/knock.wav +0 -0
- package/src/sounds/assets/leave.wav +0 -0
- package/src/sounds/assets/message.wav +0 -0
- package/src/sounds/assets/mute.wav +0 -0
- package/src/sounds/assets/recording.wav +0 -0
- package/src/sounds/assets/test.wav +0 -0
- package/src/sounds/assets/unmute.wav +0 -0
- package/src/sounds/assets.d.ts +5 -0
- package/src/sounds/sounds.ts +50 -0
- package/src/space-client/create-native-space-client.ts +20 -181
- package/src/space-client/random-uuid.ts +17 -0
- package/src/space-client/whiteboard-watchdog.test.ts +23 -0
- package/src/stage/ContentTile.tsx +100 -0
- package/src/stage/ParticipantTile.tsx +140 -0
- package/src/stage/Stage.tsx +152 -0
- package/src/stage/TileSheet.tsx +116 -0
- package/src/stage/camera.ts +20 -0
- package/src/stage/layout.test.ts +77 -0
- package/src/stage/layout.ts +226 -0
- package/src/stage/space-selector.ts +31 -0
- package/src/stage/stage-view.tsx +68 -0
- package/src/status/CallOverlays.tsx +47 -0
- package/src/status/ChalkArrival.tsx +53 -0
- package/src/status/StatusPage.tsx +123 -0
- package/src/status/call-screen.test.ts +42 -0
- package/src/status/call-screen.ts +29 -0
- package/src/theme/theme.tsx +28 -0
- package/src/theme/tokens.ts +247 -0
- package/src/toasts/CallEventToasts.tsx +192 -0
- package/src/toasts/notices.test.ts +92 -0
- package/src/toasts/notices.ts +62 -0
- package/src/ui/ChalkUI.tsx +49 -0
- package/src/ui.ts +24 -0
- package/dist/android.d.ts +0 -2
- package/dist/android.d.ts.map +0 -1
- package/dist/android.js +0 -62
- package/dist/chunk-2OS7SSXE.js +0 -552
- package/dist/chunk-5DQ4KBRH.js +0 -690
- package/dist/chunk-ITLUHTVS.js +0 -5271
- package/dist/chunk-JB4LNWVP.js +0 -247
- package/dist/components/Chalk.d.ts +0 -79
- package/dist/components/Chalk.d.ts.map +0 -1
- package/dist/components/ChatMessageList.d.ts +0 -11
- package/dist/components/ChatMessageList.d.ts.map +0 -1
- package/dist/components/Entrance.d.ts +0 -22
- package/dist/components/Entrance.d.ts.map +0 -1
- package/dist/components/EntranceView.android.d.ts +0 -4
- package/dist/components/EntranceView.android.d.ts.map +0 -1
- package/dist/components/EntranceView.d.ts +0 -18
- package/dist/components/EntranceView.d.ts.map +0 -1
- package/dist/components/EntranceView.ios-pad.d.ts +0 -3
- package/dist/components/EntranceView.ios-pad.d.ts.map +0 -1
- package/dist/components/EntranceView.ios-phone.d.ts +0 -3
- package/dist/components/EntranceView.ios-phone.d.ts.map +0 -1
- package/dist/components/EntranceView.macos.d.ts +0 -4
- package/dist/components/EntranceView.macos.d.ts.map +0 -1
- package/dist/components/FaceAvatar.d.ts +0 -16
- package/dist/components/FaceAvatar.d.ts.map +0 -1
- package/dist/components/GradientSurface.d.ts +0 -14
- package/dist/components/GradientSurface.d.ts.map +0 -1
- package/dist/components/MediaView.d.ts +0 -14
- package/dist/components/MediaView.d.ts.map +0 -1
- package/dist/components/ReactionPicker.d.ts +0 -10
- package/dist/components/ReactionPicker.d.ts.map +0 -1
- package/dist/components/SpaceView.android.d.ts +0 -2
- package/dist/components/SpaceView.android.d.ts.map +0 -1
- package/dist/components/SpaceView.d.ts +0 -33
- package/dist/components/SpaceView.d.ts.map +0 -1
- package/dist/components/SpaceView.ios-pad.d.ts +0 -2
- package/dist/components/SpaceView.ios-pad.d.ts.map +0 -1
- package/dist/components/SpaceView.ios-phone.d.ts +0 -2
- package/dist/components/SpaceView.ios-phone.d.ts.map +0 -1
- package/dist/components/SpaceView.macos.d.ts +0 -2
- package/dist/components/SpaceView.macos.d.ts.map +0 -1
- package/dist/components/SpaceView.shared.d.ts +0 -3
- package/dist/components/SpaceView.shared.d.ts.map +0 -1
- package/dist/components/episode-diagnostic-render-observer.d.ts +0 -2
- package/dist/components/episode-diagnostic-render-observer.d.ts.map +0 -1
- package/dist/components/logo/Logo.d.ts +0 -11
- package/dist/components/logo/Logo.d.ts.map +0 -1
- package/dist/components/native-animation-controller.d.ts +0 -7
- package/dist/components/native-animation-controller.d.ts.map +0 -1
- package/dist/components/native-chat.d.ts +0 -8
- package/dist/components/native-chat.d.ts.map +0 -1
- package/dist/components/native-entrance/entrance-controller-store.d.ts +0 -34
- package/dist/components/native-entrance/entrance-controller-store.d.ts.map +0 -1
- package/dist/components/native-entrance/useEntranceController.d.ts +0 -19
- package/dist/components/native-entrance/useEntranceController.d.ts.map +0 -1
- package/dist/components/native-joining-loading-animation.d.ts +0 -9
- package/dist/components/native-joining-loading-animation.d.ts.map +0 -1
- package/dist/components/native-space-view/AppearanceSettings.d.ts +0 -2
- package/dist/components/native-space-view/AppearanceSettings.d.ts.map +0 -1
- package/dist/components/native-space-view/FeedbackSheet.d.ts +0 -12
- package/dist/components/native-space-view/FeedbackSheet.d.ts.map +0 -1
- package/dist/components/native-space-view/ParticipantActionSheet.d.ts +0 -9
- package/dist/components/native-space-view/ParticipantActionSheet.d.ts.map +0 -1
- package/dist/components/native-space-view/SettingsSheet.d.ts +0 -7
- package/dist/components/native-space-view/SettingsSheet.d.ts.map +0 -1
- package/dist/components/native-space-view/SpaceActionMenu.d.ts +0 -10
- package/dist/components/native-space-view/SpaceActionMenu.d.ts.map +0 -1
- package/dist/components/native-space-view/SpaceBottomDock.android.d.ts +0 -4
- package/dist/components/native-space-view/SpaceBottomDock.android.d.ts.map +0 -1
- package/dist/components/native-space-view/SpaceChatAttachment.d.ts +0 -8
- package/dist/components/native-space-view/SpaceChatAttachment.d.ts.map +0 -1
- package/dist/components/native-space-view/SpaceChatSheet.d.ts +0 -5
- package/dist/components/native-space-view/SpaceChatSheet.d.ts.map +0 -1
- package/dist/components/native-space-view/SpaceGrid.android.d.ts +0 -8
- package/dist/components/native-space-view/SpaceGrid.android.d.ts.map +0 -1
- package/dist/components/native-space-view/SpaceMoreSheet.d.ts +0 -7
- package/dist/components/native-space-view/SpaceMoreSheet.d.ts.map +0 -1
- package/dist/components/native-space-view/SpacePanelSheet.d.ts +0 -5
- package/dist/components/native-space-view/SpacePanelSheet.d.ts.map +0 -1
- package/dist/components/native-space-view/SpacePeopleSheet.d.ts +0 -5
- package/dist/components/native-space-view/SpacePeopleSheet.d.ts.map +0 -1
- package/dist/components/native-space-view/SpaceStage.android.d.ts +0 -29
- package/dist/components/native-space-view/SpaceStage.android.d.ts.map +0 -1
- package/dist/components/native-space-view/SpaceSurfacePrimitives.d.ts +0 -30
- package/dist/components/native-space-view/SpaceSurfacePrimitives.d.ts.map +0 -1
- package/dist/components/native-space-view/SpaceTopBar.android.d.ts +0 -11
- package/dist/components/native-space-view/SpaceTopBar.android.d.ts.map +0 -1
- package/dist/components/native-space-view/SpaceWhiteboardSurface.d.ts +0 -15
- package/dist/components/native-space-view/SpaceWhiteboardSurface.d.ts.map +0 -1
- package/dist/components/native-space-view/diagnostics.d.ts +0 -48
- package/dist/components/native-space-view/diagnostics.d.ts.map +0 -1
- package/dist/components/native-space-view/format-space-duration.d.ts +0 -2
- package/dist/components/native-space-view/format-space-duration.d.ts.map +0 -1
- package/dist/components/native-space-view/native-space-grid-keys.d.ts +0 -3
- package/dist/components/native-space-view/native-space-grid-keys.d.ts.map +0 -1
- package/dist/components/native-space-view/native-space-page-store.d.ts +0 -8
- package/dist/components/native-space-view/native-space-page-store.d.ts.map +0 -1
- package/dist/components/native-space-view/space-chat-attachments.d.ts +0 -15
- package/dist/components/native-space-view/space-chat-attachments.d.ts.map +0 -1
- package/dist/components/native-space-view/space-panel-layout.d.ts +0 -10
- package/dist/components/native-space-view/space-panel-layout.d.ts.map +0 -1
- package/dist/components/native-space-view/space-progressive-surface-helpers.d.ts +0 -16
- package/dist/components/native-space-view/space-progressive-surface-helpers.d.ts.map +0 -1
- package/dist/components/native-space-view/space-progressive-surface-types.d.ts +0 -3
- package/dist/components/native-space-view/space-progressive-surface-types.d.ts.map +0 -1
- package/dist/components/native-space-view/types.d.ts +0 -24
- package/dist/components/native-space-view/types.d.ts.map +0 -1
- package/dist/components/native-space-view/useSpaceViewController.contract.d.ts +0 -41
- package/dist/components/native-space-view/useSpaceViewController.contract.d.ts.map +0 -1
- package/dist/components/native-space-view/useSpaceViewController.d.ts +0 -156
- package/dist/components/native-space-view/useSpaceViewController.d.ts.map +0 -1
- package/dist/components/native-space-view/useSpaceViewDerived.d.ts +0 -12
- package/dist/components/native-space-view/useSpaceViewDerived.d.ts.map +0 -1
- package/dist/components/native-space-view/useSpaceViewPanels.d.ts +0 -70
- package/dist/components/native-space-view/useSpaceViewPanels.d.ts.map +0 -1
- package/dist/feedback/native-capture.d.ts +0 -9
- package/dist/feedback/native-capture.d.ts.map +0 -1
- package/dist/feedback/native-evidence.d.ts +0 -3
- package/dist/feedback/native-evidence.d.ts.map +0 -1
- package/dist/hooks/entrance-preview-store.d.ts +0 -16
- package/dist/hooks/entrance-preview-store.d.ts.map +0 -1
- package/dist/hooks/useEntrancePreview.d.ts +0 -7
- package/dist/hooks/useEntrancePreview.d.ts.map +0 -1
- package/dist/hooks/useLayout.d.ts +0 -16
- package/dist/hooks/useLayout.d.ts.map +0 -1
- package/dist/hooks/usePanels.d.ts +0 -12
- package/dist/hooks/usePanels.d.ts.map +0 -1
- package/dist/ios-pad.d.ts +0 -2
- package/dist/ios-pad.d.ts.map +0 -1
- package/dist/ios-pad.js +0 -62
- package/dist/ios-phone.d.ts +0 -2
- package/dist/ios-phone.d.ts.map +0 -1
- package/dist/ios-phone.js +0 -62
- package/dist/ios.d.ts +0 -2
- package/dist/ios.d.ts.map +0 -1
- package/dist/ios.js +0 -62
- package/dist/macos.d.ts +0 -2
- package/dist/macos.d.ts.map +0 -1
- package/dist/macos.js +0 -62
- package/dist/space-client/cloudflare-rtk-native.d.ts +0 -3
- package/dist/space-client/cloudflare-rtk-native.d.ts.map +0 -1
- package/dist/telemetry.d.ts +0 -63
- package/dist/telemetry.d.ts.map +0 -1
- package/dist/ui/appearance.d.ts +0 -246
- package/dist/ui/appearance.d.ts.map +0 -1
- package/dist/ui/appearance.js +0 -14
- package/dist/ui/native-appearance-context.d.ts +0 -15
- package/dist/ui/native-appearance-context.d.ts.map +0 -1
- package/dist/ui/native-theme.d.ts +0 -17
- package/dist/ui/native-theme.d.ts.map +0 -1
- package/dist/ui/native-types.d.ts +0 -38
- package/dist/ui/native-types.d.ts.map +0 -1
- package/dist/ui/participant-avatar.d.ts +0 -21
- package/dist/ui/participant-avatar.d.ts.map +0 -1
- package/dist/ui/theme-tokens.d.ts +0 -25
- package/dist/ui/theme-tokens.d.ts.map +0 -1
- package/dist/ui/theme.d.ts +0 -404
- package/dist/ui/theme.d.ts.map +0 -1
- package/dist/ui/theme.js +0 -8
- package/dist/utils/native-space-layout.d.ts +0 -42
- package/dist/utils/native-space-layout.d.ts.map +0 -1
- package/src/android.ts +0 -1
- package/src/components/Chalk.tsx +0 -363
- package/src/components/ChatMessageList.tsx +0 -209
- package/src/components/Entrance.tsx +0 -42
- package/src/components/EntranceView.android.tsx +0 -274
- package/src/components/EntranceView.ios-pad.tsx +0 -326
- package/src/components/EntranceView.ios-phone.tsx +0 -271
- package/src/components/EntranceView.macos.tsx +0 -278
- package/src/components/EntranceView.tsx +0 -38
- package/src/components/FaceAvatar.tsx +0 -120
- package/src/components/GradientSurface.tsx +0 -23
- package/src/components/MediaView.tsx +0 -76
- package/src/components/ReactionPicker.tsx +0 -98
- package/src/components/SpaceView.android.tsx +0 -1
- package/src/components/SpaceView.ios-pad.tsx +0 -1
- package/src/components/SpaceView.ios-phone.tsx +0 -1
- package/src/components/SpaceView.macos.tsx +0 -1
- package/src/components/SpaceView.shared.tsx +0 -98
- package/src/components/SpaceView.tsx +0 -54
- package/src/components/episode-diagnostic-render-observer.ts +0 -12
- package/src/components/logo/Logo.tsx +0 -309
- package/src/components/native-animation-controller.ts +0 -36
- package/src/components/native-chat.ts +0 -23
- package/src/components/native-entrance/entrance-controller-store.ts +0 -112
- package/src/components/native-entrance/useEntranceController.ts +0 -60
- package/src/components/native-joining-loading-animation.ts +0 -26
- package/src/components/native-space-view/AppearanceSettings.tsx +0 -114
- package/src/components/native-space-view/FeedbackSheet.tsx +0 -296
- package/src/components/native-space-view/ParticipantActionSheet.tsx +0 -99
- package/src/components/native-space-view/SettingsSheet.tsx +0 -164
- package/src/components/native-space-view/SpaceActionMenu.tsx +0 -109
- package/src/components/native-space-view/SpaceBottomDock.android.tsx +0 -115
- package/src/components/native-space-view/SpaceChatAttachment.tsx +0 -105
- package/src/components/native-space-view/SpaceChatSheet.tsx +0 -184
- package/src/components/native-space-view/SpaceGrid.android.tsx +0 -323
- package/src/components/native-space-view/SpaceMoreSheet.tsx +0 -190
- package/src/components/native-space-view/SpacePanelSheet.tsx +0 -45
- package/src/components/native-space-view/SpacePeopleSheet.tsx +0 -116
- package/src/components/native-space-view/SpaceStage.android.tsx +0 -372
- package/src/components/native-space-view/SpaceSurfacePrimitives.tsx +0 -75
- package/src/components/native-space-view/SpaceTopBar.android.tsx +0 -113
- package/src/components/native-space-view/SpaceWhiteboardSurface.tsx +0 -69
- package/src/components/native-space-view/diagnostics.ts +0 -84
- package/src/components/native-space-view/format-space-duration.ts +0 -3
- package/src/components/native-space-view/native-space-grid-keys.ts +0 -6
- package/src/components/native-space-view/native-space-page-store.ts +0 -41
- package/src/components/native-space-view/space-chat-attachments.test.ts +0 -26
- package/src/components/native-space-view/space-chat-attachments.ts +0 -37
- package/src/components/native-space-view/space-panel-layout.ts +0 -10
- package/src/components/native-space-view/space-progressive-surface-helpers.ts +0 -33
- package/src/components/native-space-view/space-progressive-surface-types.ts +0 -3
- package/src/components/native-space-view/types.ts +0 -25
- package/src/components/native-space-view/useSpaceViewController.contract.ts +0 -34
- package/src/components/native-space-view/useSpaceViewController.ts +0 -416
- package/src/components/native-space-view/useSpaceViewDerived.ts +0 -29
- package/src/components/native-space-view/useSpaceViewPanels.ts +0 -170
- package/src/feedback/native-capture.ts +0 -62
- package/src/feedback/native-evidence.ts +0 -37
- package/src/hooks/entrance-preview-store.ts +0 -99
- package/src/hooks/useEntrancePreview.ts +0 -26
- package/src/hooks/useLayout.ts +0 -37
- package/src/hooks/usePanels.ts +0 -30
- package/src/ios-pad.ts +0 -1
- package/src/ios-phone.ts +0 -1
- package/src/ios.ts +0 -1
- package/src/macos.ts +0 -1
- package/src/space-client/cloudflare-rtk-native.ts +0 -104
- package/src/telemetry.ts +0 -234
- package/src/types/hugeicons-direct.d.ts +0 -4
- package/src/types/react-native-safe-area-context.d.ts +0 -8
- package/src/ui/appearance.ts +0 -808
- package/src/ui/native-appearance-context.tsx +0 -74
- package/src/ui/native-theme.tsx +0 -54
- package/src/ui/native-types.ts +0 -40
- package/src/ui/participant-avatar.ts +0 -47
- package/src/ui/theme-tokens.ts +0 -41
- package/src/ui/theme.ts +0 -214
- package/src/utils/native-space-layout.ts +0 -135
package/README.md
CHANGED
|
@@ -1,52 +1,157 @@
|
|
|
1
1
|
# @q9labsai/chalk-react-native
|
|
2
2
|
|
|
3
|
-
React Native bindings
|
|
3
|
+
React Native bindings, native platform adapters and the drop-in call view for Chalk.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
Install the SDK with every native module it needs as a peer, at the versions your Expo SDK pins:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npx expo install @q9labsai/chalk-client @q9labsai/chalk-react-native @cloudflare/react-native-webrtc react-native-webview react-native-reanimated react-native-worklets react-native-gesture-handler react-native-safe-area-context react-native-svg expo-audio expo-haptics expo-clipboard expo-document-picker expo-file-system
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The app must also embed the Figtree font (Regular, Medium, SemiBold and Bold) with the `expo-font` config plugin.
|
|
14
|
+
|
|
15
|
+
The root package exports:
|
|
16
|
+
|
|
17
|
+
- Call view: `Chalk`, `ChalkProps`, `Permission`, `ColorScheme`, and `Palette`.
|
|
18
|
+
- Before the call: `ChalkArrival`, `ChalkArrivalProps`, and `ArrivalState`.
|
|
19
|
+
- Context: `ChalkProvider`, `useSpaceClient`, and `ChalkProviderProps`.
|
|
20
|
+
- Snapshot hooks: `useCan`, `useChat`, `useConnection`, `useMedia`, `useParticipants`, `useReactions`, `useRecording`, `useSelf`, and `useWhiteboard`.
|
|
21
|
+
- Invite helpers: `extractSpaceInviteFromLink`, `getClipboardInviteSuggestion`, `parseSpaceInviteLink`, `buildChalkInviteLink`, and `SpaceInviteLink`.
|
|
22
|
+
|
|
23
|
+
Use `ChalkProvider` instead of `Chalk` to build your own call UI. It receives a `SpaceClient`. Components inside the provider can read snapshots with the hooks and access client actions through `useSpaceClient`. The application owns joining, leaving, disposing the client, and rendering its UI.
|
|
24
|
+
|
|
25
|
+
## Call view
|
|
26
|
+
|
|
27
|
+
`Chalk` draws the whole call: header, stage, dock, sheets, menus and toasts. It fills the screen it's given, joins muted with the camera off, and keeps every control inside the safe area in portrait and landscape. It covers the call while it joins or reconnects, and shows a page when the call is over.
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import type { SpaceClient } from "@q9labsai/chalk-client";
|
|
31
|
+
import { Chalk, type ColorScheme } from "@q9labsai/chalk-react-native";
|
|
32
|
+
import { useState } from "react";
|
|
33
|
+
|
|
34
|
+
export function DesignReview({ client, onLeave, onRejoin }: { readonly client: SpaceClient; readonly onLeave: () => void; readonly onRejoin: () => void }) {
|
|
35
|
+
const [colorScheme, setColorScheme] = useState<ColorScheme>("dark");
|
|
36
|
+
const [soundsEnabled, setSoundsEnabled] = useState(true);
|
|
37
|
+
return <Chalk callName="Design review" client={client} colorScheme={colorScheme} inviteLink="https://chalk.example/design-review" onColorSchemeChange={setColorScheme} onLeave={onLeave} onRejoin={onRejoin} onSoundsEnabledChange={setSoundsEnabled} soundsEnabled={soundsEnabled} />;
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`Chalk` lays out the call itself, so the stage needs no props. It switches between Spotlight and Grid, moves the active speaker into the spotlight, and sizes screen shares to their shape. iPad uses the phone layout in 5.0.0.
|
|
42
|
+
|
|
43
|
+
| Prop | Type | What it does |
|
|
44
|
+
| ----------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
45
|
+
| `client` | `SpaceClient` | The call's client. The app creates it with `createNativeSpaceClient` and disposes it. `Chalk` joins it. |
|
|
46
|
+
| `displayName` | `string` | The name others see. Without it, the server's stored name is used. |
|
|
47
|
+
| `callName` | `string` | The call's name in the header, such as "Q9 team". Tapping it opens the call details. |
|
|
48
|
+
| `inviteLink` | `string` | The call's link. More offers to copy and share it, and the call details show it with copy, share and a QR code. Without it, they leave those rows out. |
|
|
49
|
+
| `colorScheme` | `"dark" \| "light"` | The call's look. Dark by default. |
|
|
50
|
+
| `onColorSchemeChange` | `(colorScheme: ColorScheme) => void` | Called when the person changes the look in the call details. |
|
|
51
|
+
| `soundsEnabled` | `boolean` | Call sounds. On by default. |
|
|
52
|
+
| `onSoundsEnabledChange` | `(enabled: boolean) => void` | Called when the person turns call sounds on or off. |
|
|
53
|
+
| `onLeave` | `() => void` | Called when the person is done with the call: from Home on the page that shows after they leave, the call ends, it fails, or they were removed. |
|
|
54
|
+
| `onRejoin` | `() => void` | Called when the person chooses Rejoin or Try again on a page that shows after they leave, the call ends, or it fails. Give the arrival back and render `Chalk` again with a new client, since the old one can't join. |
|
|
55
|
+
| `onOpenSettings` | `(permission: Permission) => void` | Called just before Open Settings takes the person to the system settings to allow the microphone or camera. |
|
|
56
|
+
| `resumedAfterSettings` | `Permission` | Set when the app opens the call again after Open Settings. The call says "Rejoining" and then that it can use the device now. |
|
|
57
|
+
|
|
58
|
+
`Permission` is `"microphone" | "camera"`.
|
|
59
|
+
|
|
60
|
+
Leaving no longer calls `onLeave` straight away. The "You left" page shows first, with Rejoin and Home, and only Home calls `onLeave`. This is a breaking change from 4.x.
|
|
61
|
+
|
|
62
|
+
Rejoin and Try again call `onRejoin`, not `onLeave`. Give the arrival back there, then render `Chalk` again with a new client. The client that left or ended can't join the call a second time.
|
|
63
|
+
|
|
64
|
+
The person can switch the look and call sounds from the call details. The example keeps both in state. To keep the person's choice, store both through the change callbacks and pass them back on the next launch. Load them before you render `Chalk`, so the first frame already has the right theme.
|
|
65
|
+
|
|
66
|
+
### After a permission change
|
|
67
|
+
|
|
68
|
+
When the microphone or camera isn't allowed, the call shows a notice with Open Settings. iOS restarts the app when a permission changes, so the call is gone when the person comes back. Store the call in `onOpenSettings`, and on the next launch open it again with `resumedAfterSettings`:
|
|
13
69
|
|
|
14
70
|
```tsx
|
|
15
|
-
import {
|
|
71
|
+
import type { SpaceClient } from "@q9labsai/chalk-client";
|
|
72
|
+
import { Chalk, type Permission } from "@q9labsai/chalk-react-native";
|
|
16
73
|
|
|
17
|
-
|
|
18
|
-
|
|
74
|
+
type ResumableCallProps = {
|
|
75
|
+
readonly client: SpaceClient;
|
|
76
|
+
readonly resumed?: Permission;
|
|
77
|
+
readonly saveResume: (permission: Permission) => void;
|
|
78
|
+
readonly onLeave: () => void;
|
|
79
|
+
readonly onRejoin: () => void;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export function ResumableCall({ client, resumed, saveResume, onLeave, onRejoin }: ResumableCallProps) {
|
|
83
|
+
return <Chalk callName="Design review" client={client} onLeave={onLeave} onOpenSettings={saveResume} onRejoin={onRejoin} resumedAfterSettings={resumed} />;
|
|
19
84
|
}
|
|
20
85
|
```
|
|
21
86
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
87
|
+
Clear the stored call once you read it, so a later launch doesn't rejoin.
|
|
88
|
+
|
|
89
|
+
## Before the call lets you in
|
|
90
|
+
|
|
91
|
+
`ChalkArrival` draws the screens before `Chalk` joins, for a call that asks the host first. Render it in place of `Chalk` while the arrival is pending (`state="knocking"`), after the host denies it (`"refused"`), or when the call is full (`"full"`). Render `Chalk` once the person is let in.
|
|
26
92
|
|
|
27
|
-
|
|
93
|
+
```tsx
|
|
94
|
+
import { type ArrivalState, ChalkArrival } from "@q9labsai/chalk-react-native";
|
|
28
95
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
96
|
+
export function Arrival({ state, onCancel, onRetry }: { readonly state: ArrivalState; readonly onCancel: () => void; readonly onRetry: () => void }) {
|
|
97
|
+
return <ChalkArrival callName="Design review" onCancel={onCancel} onRetry={onRetry} state={state} />;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
33
100
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
101
|
+
| Prop | Type | What it does |
|
|
102
|
+
| ------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
103
|
+
| `state` | `"knocking" \| "refused" \| "full"` | Which screen to show: "Asking to join" with Cancel, "The host didn't let you in" with Ask again, or "This call is full" with Try again and Home. |
|
|
104
|
+
| `callName` | `string` | The call's name, such as "Q9 team". |
|
|
105
|
+
| `colorScheme` | `"dark" \| "light"` | Dark by default, like `Chalk`. |
|
|
106
|
+
| `onCancel` | `() => void` | Stops waiting: Cancel while knocking, Home when the call is full, and Android back. |
|
|
107
|
+
| `onRetry` | `() => void` | Knocks again after a refusal, or tries again once a full call has room. |
|
|
108
|
+
| `recording` | `boolean` | Shows recording consent while knocking on a recorded call. |
|
|
37
109
|
|
|
38
|
-
|
|
39
|
-
|
|
110
|
+
## Your own screens
|
|
111
|
+
|
|
112
|
+
`@q9labsai/chalk-react-native/ui` gives an app's own screens, such as its Home or name step, the same look as the call. Wrap them in `ChalkUI`, which provides the theme, safe areas, sheets, menus and toasts, and build them from the call's primitives.
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
import { ChalkText, ChalkUI, PillButton } from "@q9labsai/chalk-react-native/ui";
|
|
116
|
+
|
|
117
|
+
export function Home({ onStart }: { readonly onStart: () => void }) {
|
|
118
|
+
return (
|
|
119
|
+
<ChalkUI colorScheme="dark">
|
|
120
|
+
<ChalkText>Start a call or join with a link.</ChalkText>
|
|
121
|
+
<PillButton label="Start a call" onPress={onStart} />
|
|
122
|
+
</ChalkUI>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The entry exports:
|
|
128
|
+
|
|
129
|
+
- Frame: `ChalkUI` and `ChalkUIProps`.
|
|
130
|
+
- Theme: `useTheme`, `Theme`, `fixed`, `fontFamilies`, `radii`, `type`, `ColorScheme`, `Palette`, and `useDeviceLayout`.
|
|
131
|
+
- Icons: `Icon` and `IconName`.
|
|
132
|
+
- Primitives: `ChalkText`, `IconButton`, `PillButton`, `BottomSheet`, `SheetGroup`, `SheetRow`, `Menu`, `MenuItem`, `Switch`, `SegmentedControl`, `CopyButton`, `FaceAvatar`, `useToasts`, `announce`, `usePressAnimation`, and `Limbo`.
|
|
40
133
|
|
|
41
134
|
## Native client adapter
|
|
42
135
|
|
|
43
|
-
|
|
44
|
-
from `@q9labsai/chalk-react-native/client`. It exposes only
|
|
45
|
-
`createNativeSpaceClient` and `NativeSpaceClientOptions`; it does not add a
|
|
46
|
-
second root binding shape.
|
|
136
|
+
`@q9labsai/chalk-react-native/client` exports `createNativeSpaceClient` and `NativeSpaceClientOptions`. The native client connects Chalk to React Native media and application lifecycle APIs.
|
|
47
137
|
|
|
48
138
|
```tsx
|
|
49
139
|
import { createNativeSpaceClient } from "@q9labsai/chalk-react-native/client";
|
|
50
140
|
|
|
51
141
|
const client = createNativeSpaceClient({ space: "design-review", getAccess });
|
|
52
142
|
```
|
|
143
|
+
|
|
144
|
+
The `runtime`, `clipboard`, `diagnostics`, and `invites` subpaths expose native runtime information, clipboard access, diagnostics state, and invite parsing respectively.
|
|
145
|
+
|
|
146
|
+
## Development entry
|
|
147
|
+
|
|
148
|
+
`@q9labsai/chalk-react-native/dev` is for development only. `setMeasurementRegistry` reports each call surface's frame and declared style by testID, and `setDevScenario` opens a sheet or menu and draws fake video in tiles. Require it behind `__DEV__`, so Metro leaves it out of release bundles:
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
if (__DEV__) {
|
|
152
|
+
const { setDevScenario } = require("@q9labsai/chalk-react-native/dev");
|
|
153
|
+
setDevScenario({ sheet: "more" });
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Don't import it at the top of a file. A static import puts it in every bundle.
|
package/android/build.gradle
CHANGED
|
@@ -41,3 +41,38 @@ dependencies {
|
|
|
41
41
|
implementation("com.facebook.react:react-android")
|
|
42
42
|
implementation("androidx.core:core:1.16.0")
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
def compatibilityClasses = layout.buildDirectory.dir("compatibility/classes")
|
|
46
|
+
def compileCompatibility = tasks.register("compileModuleApiCompatibility", JavaCompile) {
|
|
47
|
+
source fileTree("compatibility") { include "*.java" }
|
|
48
|
+
classpath = files()
|
|
49
|
+
destinationDirectory = compatibilityClasses
|
|
50
|
+
sourceCompatibility = "17"
|
|
51
|
+
targetCompatibility = "17"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check the bytecode RN actually reflects, including inferred/private/synthetic getters.
|
|
55
|
+
tasks.register("checkModuleApiCompatibility", JavaExec) {
|
|
56
|
+
dependsOn compileCompatibility, "compileDebugKotlin", "compileDebugJavaWithJavac",
|
|
57
|
+
"compileDebugUnitTestKotlin"
|
|
58
|
+
classpath = files(compatibilityClasses)
|
|
59
|
+
mainClass = "ModuleApiCompatibility"
|
|
60
|
+
doFirst {
|
|
61
|
+
def manifest = layout.buildDirectory.file("compatibility/classpath.txt").get().asFile
|
|
62
|
+
def paths = [android.bootClasspath.first(),
|
|
63
|
+
tasks.named("compileDebugUnitTestKotlin").get().destinationDirectory.get().asFile,
|
|
64
|
+
tasks.named("compileDebugKotlin").get().destinationDirectory.get().asFile,
|
|
65
|
+
tasks.named("compileDebugJavaWithJavac").get().destinationDirectory.get().asFile]
|
|
66
|
+
paths.addAll(configurations.debugCompileClasspath.files)
|
|
67
|
+
manifest.text = paths.collect { it.absolutePath }.join("\n")
|
|
68
|
+
def platforms = [android.defaultConfig.minSdkVersion.apiLevel, 30].unique().collect {
|
|
69
|
+
new File(android.sdkDirectory, "platforms/android-${it}/android.jar")
|
|
70
|
+
}
|
|
71
|
+
platforms.each {
|
|
72
|
+
if (!it.isFile()) throw new GradleException("Install compatibility platform: sdkmanager 'platforms;${it.parentFile.name}'")
|
|
73
|
+
}
|
|
74
|
+
args = [manifest.absolutePath, layout.buildDirectory.dir("compatibility/dependencies").get().asFile.absolutePath] + platforms*.absolutePath
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
tasks.named("check").configure { dependsOn "checkModuleApiCompatibility" }
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import java.io.*;
|
|
2
|
+
import java.net.*;
|
|
3
|
+
import java.nio.file.*;
|
|
4
|
+
import java.util.*;
|
|
5
|
+
import java.util.jar.JarFile;
|
|
6
|
+
import java.util.regex.Pattern;
|
|
7
|
+
import java.util.zip.ZipFile;
|
|
8
|
+
|
|
9
|
+
/** Runs without Android initialization: RN reflects private and synthetic members too. */
|
|
10
|
+
public final class ModuleApiCompatibility {
|
|
11
|
+
private static final Set<String> ROOTS = Set.of(
|
|
12
|
+
"com/facebook/react/bridge/NativeModule", "com/facebook/react/uimanager/ViewManager");
|
|
13
|
+
private static final Pattern TYPE = Pattern.compile("L([^;]+);");
|
|
14
|
+
|
|
15
|
+
record Member(String name, String descriptor) {}
|
|
16
|
+
record ClassInfo(String name, List<String> parents, List<Member> members) {}
|
|
17
|
+
|
|
18
|
+
private static ClassInfo readClass(InputStream stream) throws IOException {
|
|
19
|
+
try (var in = new DataInputStream(stream)) {
|
|
20
|
+
if (in.readInt() != 0xcafebabe) throw new IOException("Not a class file");
|
|
21
|
+
in.skipNBytes(4);
|
|
22
|
+
Object[] pool = new Object[in.readUnsignedShort()];
|
|
23
|
+
for (int i = 1; i < pool.length; i++) {
|
|
24
|
+
switch (in.readUnsignedByte()) {
|
|
25
|
+
case 1 -> pool[i] = in.readUTF();
|
|
26
|
+
case 7 -> pool[i] = in.readUnsignedShort();
|
|
27
|
+
case 3, 4, 9, 10, 11, 12, 17, 18 -> in.skipNBytes(4);
|
|
28
|
+
case 5, 6 -> { in.skipNBytes(8); i++; }
|
|
29
|
+
case 8, 16, 19, 20 -> in.skipNBytes(2);
|
|
30
|
+
case 15 -> in.skipNBytes(3);
|
|
31
|
+
default -> throw new IOException("Unknown constant pool entry");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
in.readUnsignedShort();
|
|
35
|
+
String name = (String) pool[(int) pool[in.readUnsignedShort()]];
|
|
36
|
+
var parents = new ArrayList<String>();
|
|
37
|
+
int superclass = in.readUnsignedShort();
|
|
38
|
+
if (superclass != 0) parents.add((String) pool[(int) pool[superclass]]);
|
|
39
|
+
int interfaces = in.readUnsignedShort();
|
|
40
|
+
for (int i = 0; i < interfaces; i++) {
|
|
41
|
+
parents.add((String) pool[(int) pool[in.readUnsignedShort()]]);
|
|
42
|
+
}
|
|
43
|
+
var members = new ArrayList<Member>();
|
|
44
|
+
// Both field_info and method_info have the same layout.
|
|
45
|
+
for (int kind = 0; kind < 2; kind++) {
|
|
46
|
+
int count = in.readUnsignedShort();
|
|
47
|
+
for (int i = 0; i < count; i++) {
|
|
48
|
+
in.readUnsignedShort();
|
|
49
|
+
members.add(new Member((String) pool[in.readUnsignedShort()],
|
|
50
|
+
(String) pool[in.readUnsignedShort()]));
|
|
51
|
+
int attributes = in.readUnsignedShort();
|
|
52
|
+
for (int j = 0; j < attributes; j++) {
|
|
53
|
+
in.readUnsignedShort();
|
|
54
|
+
in.skipNBytes(Integer.toUnsignedLong(in.readInt()));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return new ClassInfo(name, parents, members);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private static boolean reflected(String name, ClassLoader classes, Map<String, Boolean> cache)
|
|
63
|
+
throws IOException {
|
|
64
|
+
if (ROOTS.contains(name)) return true;
|
|
65
|
+
if (cache.containsKey(name)) return cache.get(name);
|
|
66
|
+
var stream = classes.getResourceAsStream(name + ".class");
|
|
67
|
+
if (stream == null) throw new IOException("Missing hierarchy class: " + name);
|
|
68
|
+
var info = readClass(stream);
|
|
69
|
+
for (String parent : info.parents()) {
|
|
70
|
+
if (reflected(parent, classes, cache)) {
|
|
71
|
+
cache.put(name, true);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
cache.put(name, false);
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private static Set<String> platformTypes(Path jar) throws IOException {
|
|
80
|
+
try (var zip = new JarFile(jar.toFile())) {
|
|
81
|
+
var types = new HashSet<String>();
|
|
82
|
+
zip.stream().filter(e -> e.getName().endsWith(".class"))
|
|
83
|
+
.forEach(e -> types.add(e.getName().replaceFirst("\\.class$", "")));
|
|
84
|
+
return types;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private static List<String> check(ClassInfo info, ClassLoader runtime,
|
|
89
|
+
Set<String> compileTypes, Set<String> supportedTypes) throws ClassNotFoundException {
|
|
90
|
+
var errors = new ArrayList<String>();
|
|
91
|
+
for (var member : info.members()) {
|
|
92
|
+
var types = TYPE.matcher(member.descriptor());
|
|
93
|
+
while (types.find()) {
|
|
94
|
+
String type = types.group(1);
|
|
95
|
+
// Also covers Java platform types that the host JDK could otherwise mask.
|
|
96
|
+
if (compileTypes.contains(type) && !supportedTypes.contains(type)) {
|
|
97
|
+
errors.add(member.name() + member.descriptor() + " missing " + type);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
Class<?> module = Class.forName(info.name().replace('/', '.'), false, runtime);
|
|
103
|
+
try { module.getDeclaredMethods(); }
|
|
104
|
+
catch (LinkageError e) { errors.add("getDeclaredMethods: " + e); }
|
|
105
|
+
try { module.getDeclaredFields(); }
|
|
106
|
+
catch (LinkageError e) { errors.add("getDeclaredFields: " + e); }
|
|
107
|
+
} catch (LinkageError e) {
|
|
108
|
+
errors.add("class loading: " + e);
|
|
109
|
+
}
|
|
110
|
+
return errors;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private static List<ClassInfo> classes(Path directory) throws IOException {
|
|
114
|
+
try (var files = Files.walk(directory)) {
|
|
115
|
+
var result = new ArrayList<ClassInfo>();
|
|
116
|
+
for (Path file : files.filter(p -> p.toString().endsWith(".class")).sorted().toList()) {
|
|
117
|
+
result.add(readClass(Files.newInputStream(file)));
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private static URLClassLoader loader(List<Path> paths) throws MalformedURLException {
|
|
124
|
+
var urls = new ArrayList<URL>();
|
|
125
|
+
for (Path path : paths) urls.add(path.toUri().toURL());
|
|
126
|
+
return new URLClassLoader(urls.toArray(URL[]::new), ClassLoader.getPlatformClassLoader());
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public static void main(String[] args) throws Exception {
|
|
130
|
+
// Gradle writes paths one per line, avoiding command-line length limits.
|
|
131
|
+
List<Path> paths = Files.readAllLines(Path.of(args[0])).stream().map(Path::of).toList();
|
|
132
|
+
Path compilePlatform = paths.get(0);
|
|
133
|
+
Path fixtureDirectory = paths.get(1);
|
|
134
|
+
List<Path> moduleDirectories = paths.subList(2, 4);
|
|
135
|
+
var dependencies = new ArrayList<Path>();
|
|
136
|
+
Path extracted = Path.of(args[1]);
|
|
137
|
+
Files.createDirectories(extracted);
|
|
138
|
+
for (Path path : paths.subList(4, paths.size())) {
|
|
139
|
+
if (!path.toString().endsWith(".aar")) { dependencies.add(path); continue; }
|
|
140
|
+
try (var aar = new ZipFile(path.toFile())) {
|
|
141
|
+
for (var entry : aar.stream().filter(e -> e.getName().endsWith(".jar")).toList()) {
|
|
142
|
+
Path jar = extracted.resolve(dependencies.size() + ".jar");
|
|
143
|
+
try (var stream = aar.getInputStream(entry)) {
|
|
144
|
+
Files.copy(stream, jar, StandardCopyOption.REPLACE_EXISTING);
|
|
145
|
+
}
|
|
146
|
+
dependencies.add(jar);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
var ownClasses = new ArrayList<ClassInfo>();
|
|
151
|
+
for (Path directory : moduleDirectories) {
|
|
152
|
+
if (Files.isDirectory(directory)) ownClasses.addAll(classes(directory));
|
|
153
|
+
}
|
|
154
|
+
if (ownClasses.isEmpty()) throw new AssertionError("No compiled SDK classes");
|
|
155
|
+
var classpath = new ArrayList<Path>(moduleDirectories);
|
|
156
|
+
classpath.add(fixtureDirectory);
|
|
157
|
+
classpath.addAll(dependencies);
|
|
158
|
+
var hierarchyPath = new ArrayList<Path>(classpath);
|
|
159
|
+
hierarchyPath.add(compilePlatform);
|
|
160
|
+
Set<String> compileTypes = platformTypes(compilePlatform);
|
|
161
|
+
try (var hierarchy = loader(hierarchyPath)) {
|
|
162
|
+
var cache = new HashMap<String, Boolean>();
|
|
163
|
+
var modules = new ArrayList<ClassInfo>();
|
|
164
|
+
for (var info : ownClasses) {
|
|
165
|
+
if (reflected(info.name(), hierarchy, cache)) modules.add(info);
|
|
166
|
+
}
|
|
167
|
+
if (modules.isEmpty()) throw new AssertionError("No React Native modules/view managers");
|
|
168
|
+
var fixtures = classes(fixtureDirectory);
|
|
169
|
+
var expected = Map.of(
|
|
170
|
+
"OriginalListenerModule", "AudioManager$OnCommunicationDeviceChangedListener",
|
|
171
|
+
"FactoryInferredModule", "AudioManager$OnCommunicationDeviceChangedListener",
|
|
172
|
+
"ModeListenerViewManager", "AudioManager$OnModeChangedListener");
|
|
173
|
+
int failures = 0;
|
|
174
|
+
for (int index = 2; index < args.length; index++) {
|
|
175
|
+
Path platform = Path.of(args[index]);
|
|
176
|
+
Set<String> supportedTypes = platformTypes(platform);
|
|
177
|
+
var runtimePath = new ArrayList<Path>();
|
|
178
|
+
runtimePath.add(platform);
|
|
179
|
+
runtimePath.addAll(classpath);
|
|
180
|
+
try (var runtime = loader(runtimePath)) {
|
|
181
|
+
for (var module : modules) {
|
|
182
|
+
var errors = check(module, runtime, compileTypes, supportedTypes);
|
|
183
|
+
for (String error : errors) System.err.println(platform + ": " + module.name() + ": " + error);
|
|
184
|
+
if (!errors.isEmpty()) failures++;
|
|
185
|
+
}
|
|
186
|
+
int rejected = 0;
|
|
187
|
+
for (var fixture : fixtures) {
|
|
188
|
+
String simpleName = fixture.name().substring(fixture.name().lastIndexOf('/') + 1);
|
|
189
|
+
if (!expected.containsKey(simpleName)) continue;
|
|
190
|
+
if (!reflected(fixture.name(), hierarchy, cache)) {
|
|
191
|
+
throw new AssertionError("Fixture is not a module/view manager: " + simpleName);
|
|
192
|
+
}
|
|
193
|
+
var errors = check(fixture, runtime, compileTypes, supportedTypes);
|
|
194
|
+
String missing = expected.get(simpleName);
|
|
195
|
+
if (supportedTypes.contains("android/media/" + missing)) {
|
|
196
|
+
if (!errors.isEmpty()) throw new AssertionError("Supported fixture failed: " + simpleName + ": " + errors);
|
|
197
|
+
rejected++;
|
|
198
|
+
System.out.println("EXPECTED PASS " + platform.getParent().getFileName() + " " + simpleName);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (errors.stream().noneMatch(e -> e.contains("missing android/media/" + missing))
|
|
202
|
+
|| errors.stream().noneMatch(e -> e.contains("getDeclaredMethods: java.lang.NoClassDefFoundError"))) {
|
|
203
|
+
throw new AssertionError("Unsafe fixture escaped: " + simpleName + ": " + errors);
|
|
204
|
+
}
|
|
205
|
+
rejected++;
|
|
206
|
+
System.out.println("EXPECTED FAIL " + platform.getParent().getFileName() + " " + simpleName + ": " + errors);
|
|
207
|
+
}
|
|
208
|
+
if (rejected != expected.size()) throw new AssertionError("Missing negative fixtures");
|
|
209
|
+
System.out.println("Checked " + modules.size() + " SDK modules/view managers against " + platform);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (args.length < 3 || failures != 0) throw new AssertionError("Incompatible SDK classes: " + failures);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
android:name=".ChalkSpaceForegroundService"
|
|
20
20
|
android:enabled="true"
|
|
21
21
|
android:exported="false"
|
|
22
|
-
android:foregroundServiceType="camera|microphone"
|
|
22
|
+
android:foregroundServiceType="camera|microphone"
|
|
23
|
+
android:stopWithTask="true" />
|
|
23
24
|
</application>
|
|
24
25
|
</manifest>
|
|
@@ -1,74 +1,140 @@
|
|
|
1
1
|
package ai.q9labs.chalk.reactnative;
|
|
2
2
|
|
|
3
|
+
import android.Manifest;
|
|
3
4
|
import android.app.Notification;
|
|
4
5
|
import android.app.NotificationChannel;
|
|
5
6
|
import android.app.NotificationManager;
|
|
7
|
+
import android.app.PendingIntent;
|
|
6
8
|
import android.app.Service;
|
|
9
|
+
import android.content.ComponentName;
|
|
7
10
|
import android.content.Context;
|
|
8
11
|
import android.content.Intent;
|
|
12
|
+
import android.content.pm.PackageManager;
|
|
9
13
|
import android.content.pm.ServiceInfo;
|
|
10
14
|
import android.os.Build;
|
|
11
15
|
import android.os.IBinder;
|
|
16
|
+
import android.util.Log;
|
|
12
17
|
|
|
13
18
|
import androidx.core.app.NotificationCompat;
|
|
19
|
+
import androidx.core.content.ContextCompat;
|
|
14
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Keeps a call's microphone and camera working in the background. Android 11 and later cut off a
|
|
23
|
+
* background app's audio and video input unless a foreground service of that type runs. Android 14
|
|
24
|
+
* throws when a type's permission is missing, so the service declares only the types granted.
|
|
25
|
+
*/
|
|
15
26
|
public class ChalkSpaceForegroundService extends Service {
|
|
16
|
-
static final String
|
|
17
|
-
static final String
|
|
18
|
-
static final String EXTRA_SPACE_NAME = "spaceName";
|
|
19
|
-
static final String EXTRA_PARTICIPANT_NAME = "participantName";
|
|
20
|
-
static final String EXTRA_CAMERA_OFF = "cameraOff";
|
|
21
|
-
private static final String CHANNEL_ID = "chalk_space_background";
|
|
27
|
+
private static final String TAG = "ChalkCallService";
|
|
28
|
+
private static final String CHANNEL_ID = "chalk_call_background";
|
|
22
29
|
private static final int NOTIFICATION_ID = 19487;
|
|
23
30
|
|
|
31
|
+
// Main thread only, like the service's own callbacks. Android crashes the app when a service started
|
|
32
|
+
// with startForegroundService() stops before it calls startForeground(), so nothing outside the service
|
|
33
|
+
// stops it: a stop asks the live service to stop through stopSelfResult(), which does nothing while a
|
|
34
|
+
// newer start is still on its way, and every delivered start promotes first, then stops if the call is over.
|
|
35
|
+
private static boolean wanted;
|
|
36
|
+
private static ChalkSpaceForegroundService live;
|
|
37
|
+
private int lastStartId = NO_START;
|
|
38
|
+
private static final int NO_START = -1;
|
|
39
|
+
|
|
40
|
+
/** Asks for the service on the main thread. Returns false when nothing is granted or Android refuses the start. */
|
|
41
|
+
static boolean requestStart(Context context) {
|
|
42
|
+
if (grantedForegroundTypes(context) == 0) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
wanted = true;
|
|
46
|
+
Intent intent = new Intent(context, ChalkSpaceForegroundService.class);
|
|
47
|
+
try {
|
|
48
|
+
ComponentName started = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? context.startForegroundService(intent) : context.startService(intent);
|
|
49
|
+
return started != null;
|
|
50
|
+
} catch (RuntimeException error) {
|
|
51
|
+
Log.w(TAG, "Couldn't start the call's foreground service", error);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Stops the service on the main thread once its latest start has promoted it. */
|
|
57
|
+
static void requestStop() {
|
|
58
|
+
wanted = false;
|
|
59
|
+
if (live != null && live.lastStartId != NO_START) {
|
|
60
|
+
live.stopSelfResult(live.lastStartId);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The foreground types the person has granted: microphone and camera, or 0 when neither is granted. */
|
|
65
|
+
static int grantedForegroundTypes(Context context) {
|
|
66
|
+
int types = 0;
|
|
67
|
+
if (isGranted(context, Manifest.permission.RECORD_AUDIO)) {
|
|
68
|
+
types |= ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
|
|
69
|
+
}
|
|
70
|
+
if (isGranted(context, Manifest.permission.CAMERA)) {
|
|
71
|
+
types |= ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
|
|
72
|
+
}
|
|
73
|
+
return types;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private static boolean isGranted(Context context, String permission) {
|
|
77
|
+
return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED;
|
|
78
|
+
}
|
|
79
|
+
|
|
24
80
|
@Override
|
|
25
81
|
public IBinder onBind(Intent intent) {
|
|
26
82
|
return null;
|
|
27
83
|
}
|
|
28
84
|
|
|
85
|
+
@Override
|
|
86
|
+
public void onCreate() {
|
|
87
|
+
super.onCreate();
|
|
88
|
+
live = this;
|
|
89
|
+
}
|
|
90
|
+
|
|
29
91
|
@Override
|
|
30
92
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
93
|
+
lastStartId = startId;
|
|
94
|
+
int foregroundTypes = grantedForegroundTypes(this);
|
|
95
|
+
if (foregroundTypes == 0) {
|
|
96
|
+
stopSelfResult(startId);
|
|
35
97
|
return START_NOT_STICKY;
|
|
36
98
|
}
|
|
37
99
|
|
|
38
100
|
ensureNotificationChannel();
|
|
39
|
-
|
|
40
|
-
String spaceName = intent != null ? intent.getStringExtra(EXTRA_SPACE_NAME) : null;
|
|
41
|
-
String participantName = intent != null ? intent.getStringExtra(EXTRA_PARTICIPANT_NAME) : null;
|
|
42
|
-
boolean cameraOff = intent != null && intent.getBooleanExtra(EXTRA_CAMERA_OFF, false);
|
|
43
|
-
Notification notification = buildNotification(spaceName, participantName, cameraOff);
|
|
101
|
+
Notification notification = buildNotification();
|
|
44
102
|
|
|
45
103
|
try {
|
|
46
104
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
47
|
-
|
|
48
|
-
if (!cameraOff) {
|
|
49
|
-
foregroundType |= ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
|
|
50
|
-
}
|
|
51
|
-
startForeground(NOTIFICATION_ID, notification, foregroundType);
|
|
105
|
+
startForeground(NOTIFICATION_ID, notification, foregroundTypes);
|
|
52
106
|
} else {
|
|
53
107
|
startForeground(NOTIFICATION_ID, notification);
|
|
54
108
|
}
|
|
55
109
|
} catch (RuntimeException error) {
|
|
56
|
-
|
|
110
|
+
Log.w(TAG, "Couldn't start the call's foreground service", error);
|
|
111
|
+
stopSelfResult(startId);
|
|
112
|
+
return START_NOT_STICKY;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!wanted) {
|
|
116
|
+
stopSelfResult(startId);
|
|
57
117
|
}
|
|
58
118
|
|
|
59
|
-
|
|
119
|
+
// Not sticky: a call can't resume after the process is killed, so a restart would leave a notification with no call behind it.
|
|
120
|
+
return START_NOT_STICKY;
|
|
60
121
|
}
|
|
61
122
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
123
|
+
@Override
|
|
124
|
+
public void onDestroy() {
|
|
125
|
+
if (live == this) {
|
|
126
|
+
live = null;
|
|
127
|
+
}
|
|
128
|
+
stopForeground(STOP_FOREGROUND_REMOVE);
|
|
129
|
+
super.onDestroy();
|
|
130
|
+
}
|
|
66
131
|
|
|
132
|
+
private Notification buildNotification() {
|
|
67
133
|
return new NotificationCompat.Builder(this, CHANNEL_ID)
|
|
68
134
|
.setCategory(NotificationCompat.CATEGORY_CALL)
|
|
69
|
-
.setContentTitle(
|
|
70
|
-
.setContentText(
|
|
71
|
-
.
|
|
135
|
+
.setContentTitle("Chalk call in progress")
|
|
136
|
+
.setContentText("Tap to return to the call")
|
|
137
|
+
.setContentIntent(openAppIntent())
|
|
72
138
|
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
|
73
139
|
.setOngoing(true)
|
|
74
140
|
.setOnlyAlertOnce(true)
|
|
@@ -77,6 +143,14 @@ public class ChalkSpaceForegroundService extends Service {
|
|
|
77
143
|
.build();
|
|
78
144
|
}
|
|
79
145
|
|
|
146
|
+
private PendingIntent openAppIntent() {
|
|
147
|
+
Intent launch = getPackageManager().getLaunchIntentForPackage(getPackageName());
|
|
148
|
+
if (launch == null) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
return PendingIntent.getActivity(this, 0, launch, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
|
152
|
+
}
|
|
153
|
+
|
|
80
154
|
private void ensureNotificationChannel() {
|
|
81
155
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
|
82
156
|
return;
|
|
@@ -87,8 +161,8 @@ public class ChalkSpaceForegroundService extends Service {
|
|
|
87
161
|
return;
|
|
88
162
|
}
|
|
89
163
|
|
|
90
|
-
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "Chalk
|
|
91
|
-
channel.setDescription("Keeps
|
|
164
|
+
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "Chalk call", NotificationManager.IMPORTANCE_LOW);
|
|
165
|
+
channel.setDescription("Keeps the microphone and camera on while Chalk is in the background");
|
|
92
166
|
manager.createNotificationChannel(channel);
|
|
93
167
|
}
|
|
94
168
|
}
|