├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── README.md
├── client
├── .babelrc
├── .env.example
├── .gitignore
├── App.js
├── android
│ ├── app
│ │ ├── BUCK
│ │ ├── build.gradle
│ │ ├── build_defs.bzl
│ │ ├── debug.keystore
│ │ ├── google-services.json
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ ├── debug
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── call
│ │ │ │ └── trigger
│ │ │ │ └── ReactNativeFlipper.java
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── assets
│ │ │ └── fonts
│ │ │ │ ├── Roboto-Black.ttf
│ │ │ │ ├── Roboto-BlackItalic.ttf
│ │ │ │ ├── Roboto-Bold.ttf
│ │ │ │ ├── Roboto-BoldItalic.ttf
│ │ │ │ ├── Roboto-Italic.ttf
│ │ │ │ ├── Roboto-Light.ttf
│ │ │ │ ├── Roboto-LightItalic.ttf
│ │ │ │ ├── Roboto-Medium.ttf
│ │ │ │ ├── Roboto-MediumItalic.ttf
│ │ │ │ ├── Roboto-Regular.ttf
│ │ │ │ ├── Roboto-Thin.ttf
│ │ │ │ └── Roboto-ThinItalic.ttf
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── call
│ │ │ │ └── trigger
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── MainApplication.java
│ │ │ └── res
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── link-assets-manifest.json
│ └── settings.gradle
├── app.json
├── babel.config.js
├── firebase.json
├── index.js
├── ios
│ ├── BroadcastScreen
│ │ ├── Atomic.swift
│ │ ├── BroadcastScreen.entitlements
│ │ ├── DarwinNotificationCenter.swift
│ │ ├── Info.plist
│ │ ├── SampleHandler.swift
│ │ ├── SampleUploader.swift
│ │ └── SocketConnection.swift
│ ├── ExportOptions.plist
│ ├── Podfile
│ ├── Podfile.lock
│ ├── ReactNativeCallTrigger-Bridging-Header.h
│ ├── ReactNativeCallTrigger.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── ReactNativeCallTrigger.xcscheme
│ ├── ReactNativeCallTrigger.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── ReactNativeCallTrigger
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── Images.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ ├── 1024.png
│ │ │ │ ├── 114.png
│ │ │ │ ├── 120.png
│ │ │ │ ├── 180.png
│ │ │ │ ├── 29.png
│ │ │ │ ├── 40.png
│ │ │ │ ├── 57.png
│ │ │ │ ├── 58.png
│ │ │ │ ├── 60.png
│ │ │ │ ├── 80.png
│ │ │ │ ├── 87.png
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Info.plist
│ │ ├── LaunchScreen.storyboard
│ │ ├── ReactNativeCallTrigger.entitlements
│ │ └── main.m
│ ├── ReactNativeCallTriggerTests
│ │ ├── Info.plist
│ │ └── ReactNativeCallTriggerTests.m
│ ├── VideosdkRPK.m
│ ├── VideosdkRPK.swift
│ └── link-assets-manifest.json
├── metro.config.js
├── package.json
├── react-native.config.js
└── src
│ ├── api
│ └── api.js
│ ├── assets
│ ├── animation
│ │ └── joining_lottie.json
│ ├── fonts
│ │ └── Roboto
│ │ │ ├── Roboto-Black.ttf
│ │ │ ├── Roboto-BlackItalic.ttf
│ │ │ ├── Roboto-Bold.ttf
│ │ │ ├── Roboto-BoldItalic.ttf
│ │ │ ├── Roboto-Italic.ttf
│ │ │ ├── Roboto-Light.ttf
│ │ │ ├── Roboto-LightItalic.ttf
│ │ │ ├── Roboto-Medium.ttf
│ │ │ ├── Roboto-MediumItalic.ttf
│ │ │ ├── Roboto-Regular.ttf
│ │ │ ├── Roboto-Thin.ttf
│ │ │ └── Roboto-ThinItalic.ttf
│ └── icons
│ │ ├── CallEnd.js
│ │ ├── CameraSwitch.js
│ │ ├── Copy.js
│ │ ├── Leave.js
│ │ ├── MicOff.js
│ │ ├── MicOn.js
│ │ ├── VideoOff.js
│ │ ├── VideoOn.js
│ │ └── index.js
│ ├── components
│ ├── Avatar
│ │ └── index.js
│ ├── Button
│ │ └── index.js
│ ├── IconContainer
│ │ └── index.js
│ └── TextInputContainer
│ │ └── index.js
│ ├── navigators
│ └── screenNames.js
│ ├── scenes
│ ├── home
│ │ └── index.js
│ └── meeting
│ │ ├── Components
│ │ └── WaitingToJoinView.js
│ │ ├── MeetingContainer.js
│ │ ├── OneToOne
│ │ ├── LargeView
│ │ │ ├── LargeVideoRTCView.js
│ │ │ └── index.js
│ │ ├── LocalViewContainer.js
│ │ ├── MiniView
│ │ │ ├── MiniVideoRTCView.js
│ │ │ └── index.js
│ │ ├── ParticipantLimitViewer.js
│ │ └── index.js
│ │ └── index.js
│ ├── styles
│ ├── colors.js
│ ├── fonts.js
│ └── spacing.js
│ └── utils
│ └── incoming-video-call.js
├── iOS_SETUP.md
├── public
├── FIR_1.png
├── FIR_2.png
├── code.png
├── image-1.png
├── image-11.png
├── image-2.png
├── image-3.png
├── image-4.png
├── image-5.png
├── image-6.png
├── image-7.png
├── image-8.png
├── ot-1.png
├── ot-2.png
├── ot-3.png
├── ot-4.png
├── xcd-1.png
├── xcd-2.png
├── xcd-3.png
├── xiomi-1.png
└── xiomi-2.png
└── server
├── .firebaserc
├── .gitignore
├── firebase.json
├── functions
├── .gitignore
├── index.js
├── package.json
└── yarn.lock
└── n
├── 404.html
└── index.html
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Smartphone (please complete the following information):**
27 | - Device: [e.g. iPhone6]
28 | - OS: [e.g. iOS8.1]
29 | - Browser [e.g. stock browser, safari]
30 | - Version [e.g. 22]
31 |
32 | **Additional context**
33 | Add any other context about the problem here.
34 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React native Call Trigger with VideoSDK
2 |
3 | ## Demo App
4 |
5 | 📲 Download the Sample iOS app here: _COMING SOON_
6 |
7 | 📱 Download the Sample Android app here: https://appdistribution.firebase.dev/i/e977b56536d45796
8 |
9 |
10 | Before continuing, let's keep an eye on the third-party libraries used in this repository.
11 |
12 | 1. [React Native CallKeep](https://www.npmjs.com/package/react-native-callkeep)
13 | 2. [React Native VoIP Push Notification](https://www.npmjs.com/package/react-native-voip-push-notification)
14 | 3. [VideoSDK RN Android Overlay Permission](https://www.npmjs.com/package/videosdk-rn-android-overlay-permission)
15 | 4. [React Native Firebase - Messaging](https://rnfirebase.io/messaging/usage)
16 | 5. [React Native Firebase - Firestore](https://rnfirebase.io/firestore/usage)
17 |
18 | ⚠️ CallKit(iOS) and ConnectionService(Android) are only available on real devices, this app will not work on simulators.
19 |
20 | ## Client
21 |
22 | ### Step 1: Clone the sample project
23 |
24 | Clone the repository to your local environment.
25 |
26 | ```js
27 | https://github.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example.git
28 | ```
29 |
30 | ### Step 2: Go to client folder
31 |
32 | ```js
33 | cd client
34 | ```
35 |
36 | ### Step 3: Copy the .env.example file to .env file
37 |
38 | Open your favorite code editor and copy `.env.example` to `.env` file.
39 |
40 | ```js
41 | cp.env.example.env;
42 | ```
43 |
44 | ### Step 4: Modify .env file
45 |
46 | Generate temporary token from [Video SDK Account](https://app.videosdk.live/signup) and add it to `.env` file.
47 |
48 | ```js title=".env"
49 | REACT_APP_VIDEOSDK_TOKEN = "TEMPORARY-TOKEN";
50 | ```
51 |
52 | ## iOS Setup
53 |
54 | Please follow the guidance of [iOS setup](./iOS_SETUP.md)
55 |
56 | ### Step 1: Install packages and pods
57 |
58 | ```js
59 | npm install
60 | ```
61 |
62 | ```js
63 | cd ios && pod install
64 | ```
65 |
66 | ### Step 2: Run the application
67 |
68 | ```js
69 | npm run ios
70 | ```
71 |
72 | ## Android Setup
73 |
74 | ### Step 1: Setup Firebase
75 |
76 | #### FCM setup
77 |
78 | - Replace your firebase app `google-services.json` file at `/android/app/google-services.json`
79 |
80 | #### Firestore setup
81 |
82 | - Create web app in your firebase project and replace configuration at `client/database/firebaseDb.js`
83 | - Add `users` collection in firestore database.
84 |
85 | 
86 |
87 | ### Step 2: Install packages and run the project
88 |
89 | ```js
90 | npm install
91 | ```
92 |
93 | ```js
94 | npm run android
95 | ```
96 |
97 | ### Step 3: Allow calling and overlay permissions
98 |
99 | After successfully installing the app, app will ask `Display over other apps` and `Access phone accounts` permission.
100 |
101 | For an app to handle calls in a dead or background state, both permissions are required.
102 |
103 | ### 1. Display over other apps permission
104 |
105 | #### Xiomi device
106 |
107 |
108 |
109 |
110 |
111 | #### Other device
112 |
113 |
114 |
115 |
116 |
117 | ### 2. Access phone accounts permission
118 |
119 |
120 |
121 |
122 |
123 | Now, different device have different ways to allow call account permissions.
124 |
125 | #### Xiomi device
126 |
127 |
128 |
129 |
130 |
131 | #### Other device
132 |
133 |
134 |
135 |
136 |
137 | Click on `All calling accounts` and allow the app to receive call.
138 |
139 |
140 |
141 |
142 |
143 | _**NOTE : It is necesary to setup local server before run the project.**_
144 |
145 | ## Server Setup
146 |
147 | ### Step 1: Go to server folder
148 |
149 | ```js
150 | cd server
151 | ```
152 |
153 | ### Step 2: Setup Firebase Admin SDK for managing FCM
154 |
155 | Download private Key from firebase and repplace it with `server/serviceAccountKey.json`
156 | 
157 |
158 | ### Step 3: Install Package and start server
159 |
160 | ```js
161 | npm install
162 | ```
163 |
164 | ```js
165 | npm run start
166 | ```
167 |
168 | ### Step 4: Add local server url in client
169 |
170 | Add Local server ip address in `client/api/api.js` file.
171 |
172 | ```js title="api.js"
173 | const FCM_SERVER_URL = "http://192.168.1.10:9000";
174 | ```
175 |
176 | ## Issue
177 |
178 | You can generate the issue on [Github](https://github.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/issues) or ping us on [Discord](https://discord.gg/bsEukaNhrD)
179 |
180 | ## Other Information
181 |
182 | ### Tested on Devices
183 |
184 | - Samsung
185 | - Xiomi
186 | - Realme
187 | - Oppo
188 |
--------------------------------------------------------------------------------
/client/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | ["module:react-native-dotenv", {
4 | "moduleName": "@env",
5 | "path": ".env",
6 | "blocklist": null,
7 | "allowlist": null,
8 | "safe": false,
9 | "allowUndefined": true,
10 | "verbose": false
11 | }]
12 | ]
13 | }
--------------------------------------------------------------------------------
/client/.env.example:
--------------------------------------------------------------------------------
1 |
2 |
3 | #Provide your Static authentication token below
4 | REACT_APP_VIDEOSDK_TOKEN = ""
5 |
6 | #OR
7 |
8 | #Provide your authentication server base url
9 | REACT_APP_AUTH_URL = ""
10 |
11 | #Any one value from above is mandatory for testing purpose
12 | #For production deployment, we recommend using an Authentication server instead of static token
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 |
24 | # Android/IntelliJ
25 | #
26 | build/
27 | .idea
28 | .gradle
29 | local.properties
30 | *.iml
31 | google-services.json
32 |
33 | # node.js
34 | #
35 | node_modules/
36 | npm-debug.log
37 | yarn-error.log
38 |
39 | # BUCK
40 | buck-out/
41 | \.buckd/
42 | *.keystore
43 | !debug.keystore
44 |
45 | # Bundle artifact
46 | *.jsbundle
47 |
48 | # CocoaPods
49 | /ios/Pods/
50 | /ios/GoogleService-Info.plist
51 | .env
52 | yarn.lock
53 | android/local.properties
54 |
55 | database/firebaseDb.js
--------------------------------------------------------------------------------
/client/App.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import "react-native-gesture-handler";
3 | import { NavigationContainer } from "@react-navigation/native";
4 | import { createStackNavigator } from "@react-navigation/stack";
5 | import { SCREEN_NAMES } from "./src/navigators/screenNames";
6 | import Meeting from "./src/scenes/meeting";
7 | import { LogBox, Text, Alert } from "react-native";
8 | import Home from "./src/scenes/home";
9 | import OverlayPermissionModule from "videosdk-rn-android-overlay-permission";
10 | import RNCallKeep from "react-native-callkeep";
11 | LogBox.ignoreLogs(["Warning: ..."]);
12 | LogBox.ignoreAllLogs();
13 |
14 | const { Navigator, Screen } = createStackNavigator();
15 |
16 | const linking = {
17 | prefixes: ["videocalling://"],
18 | config: {
19 | screens: {
20 | meetingscreen: {
21 | path: `meetingscreen/:token/:meetingId`,
22 | },
23 | },
24 | },
25 | };
26 |
27 | export default function App() {
28 | useEffect(() => {
29 | const options = {
30 | ios: {
31 | appName: "VideoSDK",
32 | },
33 | android: {
34 | alertTitle: "Permissions required",
35 | alertDescription:
36 | "This application needs to access your phone accounts",
37 | cancelButton: "Cancel",
38 | okButton: "ok",
39 | imageName: "phone_account_icon",
40 | },
41 | };
42 | RNCallKeep.setup(options);
43 | RNCallKeep.setAvailable(true);
44 |
45 | if (Platform.OS === "android") {
46 | OverlayPermissionModule.requestOverlayPermission();
47 | }
48 | }, []);
49 |
50 | return (
51 | Loading...}>
52 |
59 |
64 |
69 |
70 |
71 | );
72 | }
73 |
--------------------------------------------------------------------------------
/client/android/app/BUCK:
--------------------------------------------------------------------------------
1 | # To learn about Buck see [Docs](https://buckbuild.com/).
2 | # To run your application with Buck:
3 | # - install Buck
4 | # - `npm start` - to start the packager
5 | # - `cd android`
6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
8 | # - `buck install -r android/app` - compile, install and run application
9 | #
10 |
11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
12 |
13 | lib_deps = []
14 |
15 | create_aar_targets(glob(["libs/*.aar"]))
16 |
17 | create_jar_targets(glob(["libs/*.jar"]))
18 |
19 | android_library(
20 | name = "all-libs",
21 | exported_deps = lib_deps,
22 | )
23 |
24 | android_library(
25 | name = "app-code",
26 | srcs = glob([
27 | "src/main/java/**/*.java",
28 | ]),
29 | deps = [
30 | ":all-libs",
31 | ":build_config",
32 | ":res",
33 | ],
34 | )
35 |
36 | android_build_config(
37 | name = "build_config",
38 | package = "com.call.trigger",
39 | )
40 |
41 | android_resource(
42 | name = "res",
43 | package = "com.call.trigger",
44 | res = "src/main/res",
45 | )
46 |
47 | android_binary(
48 | name = "app",
49 | keystore = "//android/keystores:debug",
50 | manifest = "src/main/AndroidManifest.xml",
51 | package_type = "debug",
52 | deps = [
53 | ":app-code",
54 | ],
55 | )
56 |
--------------------------------------------------------------------------------
/client/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 | apply plugin: "com.google.gms.google-services"
3 | apply plugin: "com.google.firebase.crashlytics"
4 | import com.android.build.OutputFile
5 |
6 | /**
7 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
8 | * and bundleReleaseJsAndAssets).
9 | * These basically call `react-native bundle` with the correct arguments during the Android build
10 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
11 | * bundle directly from the development server. Below you can see all the possible configurations
12 | * and their defaults. If you decide to add a configuration block, make sure to add it before the
13 | * `apply from: "../../node_modules/react-native/react.gradle"` line.
14 | *
15 | * project.ext.react = [
16 | * // the name of the generated asset file containing your JS bundle
17 | * bundleAssetName: "index.android.bundle",
18 | *
19 | * // the entry file for bundle generation. If none specified and
20 | * // "index.android.js" exists, it will be used. Otherwise "index.js" is
21 | * // default. Can be overridden with ENTRY_FILE environment variable.
22 | * entryFile: "index.android.js",
23 | *
24 | * // https://reactnative.dev/docs/performance#enable-the-ram-format
25 | * bundleCommand: "ram-bundle",
26 | *
27 | * // whether to bundle JS and assets in debug mode
28 | * bundleInDebug: false,
29 | *
30 | * // whether to bundle JS and assets in release mode
31 | * bundleInRelease: true,
32 | *
33 | * // whether to bundle JS and assets in another build variant (if configured).
34 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
35 | * // The configuration property can be in the following formats
36 | * // 'bundleIn${productFlavor}${buildType}'
37 | * // 'bundleIn${buildType}'
38 | * // bundleInFreeDebug: true,
39 | * // bundleInPaidRelease: true,
40 | * // bundleInBeta: true,
41 | *
42 | * // whether to disable dev mode in custom build variants (by default only disabled in release)
43 | * // for example: to disable dev mode in the staging build type (if configured)
44 | * devDisabledInStaging: true,
45 | * // The configuration property can be in the following formats
46 | * // 'devDisabledIn${productFlavor}${buildType}'
47 | * // 'devDisabledIn${buildType}'
48 | *
49 | * // the root of your project, i.e. where "package.json" lives
50 | * root: "../../",
51 | *
52 | * // where to put the JS bundle asset in debug mode
53 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
54 | *
55 | * // where to put the JS bundle asset in release mode
56 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release",
57 | *
58 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
59 | * // require('./image.png')), in debug mode
60 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
61 | *
62 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
63 | * // require('./image.png')), in release mode
64 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
65 | *
66 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means
67 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
68 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle
69 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
70 | * // for example, you might want to remove it from here.
71 | * inputExcludes: ["android/**", "ios/**"],
72 | *
73 | * // override which node gets called and with what additional arguments
74 | * nodeExecutableAndArgs: ["node"],
75 | *
76 | * // supply additional arguments to the packager
77 | * extraPackagerArgs: []
78 | * ]
79 | */
80 |
81 | project.ext.react = [
82 | enableHermes: false, // clean and rebuild if changing
83 | ]
84 |
85 | apply from: "../../node_modules/react-native/react.gradle"
86 |
87 | /**
88 | * Set this to true to create two separate APKs instead of one:
89 | * - An APK that only works on ARM devices
90 | * - An APK that only works on x86 devices
91 | * The advantage is the size of the APK is reduced by about 4MB.
92 | * Upload all the APKs to the Play Store and people will download
93 | * the correct one based on the CPU architecture of their device.
94 | */
95 | def enableSeparateBuildPerCPUArchitecture = false
96 |
97 | /**
98 | * Run Proguard to shrink the Java bytecode in release builds.
99 | */
100 | def enableProguardInReleaseBuilds = false
101 |
102 | /**
103 | * The preferred build flavor of JavaScriptCore.
104 | *
105 | * For example, to use the international variant, you can use:
106 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
107 | *
108 | * The international variant includes ICU i18n library and necessary data
109 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
110 | * give correct results when using with locales other than en-US. Note that
111 | * this variant is about 6MiB larger per architecture than default.
112 | */
113 | def jscFlavor = 'org.webkit:android-jsc:+'
114 |
115 | /**
116 | * Whether to enable the Hermes VM.
117 | *
118 | * This should be set on project.ext.react and mirrored here. If it is not set
119 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
120 | * and the benefits of using Hermes will therefore be sharply reduced.
121 | */
122 | def enableHermes = project.ext.react.get("enableHermes", false);
123 |
124 | android {
125 | ndkVersion rootProject.ext.ndkVersion
126 |
127 | compileSdkVersion rootProject.ext.compileSdkVersion
128 |
129 | compileOptions {
130 | sourceCompatibility JavaVersion.VERSION_1_8
131 | targetCompatibility JavaVersion.VERSION_1_8
132 | }
133 |
134 | defaultConfig {
135 | applicationId "com.call.trigger"
136 | minSdkVersion rootProject.ext.minSdkVersion
137 | targetSdkVersion rootProject.ext.targetSdkVersion
138 | versionCode 6
139 | versionName "2.0"
140 | }
141 | splits {
142 | abi {
143 | reset()
144 | enable enableSeparateBuildPerCPUArchitecture
145 | universalApk false // If true, also generate a universal APK
146 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
147 | }
148 | }
149 | signingConfigs {
150 | debug {
151 | storeFile file('debug.keystore')
152 | storePassword 'android'
153 | keyAlias 'androiddebugkey'
154 | keyPassword 'android'
155 | }
156 | release {
157 | storeFile file('videosdk_callkit.keystore')
158 | storePassword "videosdk"
159 | keyAlias "videosdk_callkit"
160 | keyPassword "videosdk"
161 | }
162 | }
163 | buildTypes {
164 | debug {
165 | signingConfig signingConfigs.debug
166 | }
167 | release {
168 | // Caution! In production, you need to generate your own keystore file.
169 | // see https://reactnative.dev/docs/signed-apk-android.
170 | signingConfig signingConfigs.debug
171 | minifyEnabled enableProguardInReleaseBuilds
172 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
173 | }
174 | }
175 |
176 | // applicationVariants are e.g. debug, release
177 | applicationVariants.all { variant ->
178 | variant.outputs.each { output ->
179 | // For each separate APK per architecture, set a unique version code as described here:
180 | // https://developer.android.com/studio/build/configure-apk-splits.html
181 | // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
182 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
183 | def abi = output.getFilter(OutputFile.ABI)
184 | if (abi != null) { // null for the universal-debug, universal-release variants
185 | output.versionCodeOverride =
186 | defaultConfig.versionCode * 1000 + versionCodes.get(abi)
187 | }
188 |
189 | }
190 | }
191 |
192 | lintOptions {
193 | checkReleaseBuilds false
194 | }
195 | }
196 |
197 | dependencies {
198 | implementation fileTree(dir: "libs", include: ["*.jar"])
199 | //noinspection GradleDynamicVersion
200 | implementation "com.facebook.react:react-native:+" // From node_modules
201 | implementation platform('com.google.firebase:firebase-bom:31.1.1')
202 | implementation 'com.google.firebase:firebase-analytics'
203 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
204 |
205 | implementation project(':rnfgservice')
206 | implementation project(':rnwebrtc')
207 | implementation project(':rnincallmanager')
208 | implementation project(':lottie-react-native')
209 |
210 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
211 | exclude group:'com.facebook.fbjni'
212 | }
213 |
214 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
215 | exclude group:'com.facebook.flipper'
216 | exclude group:'com.squareup.okhttp3', module:'okhttp'
217 | }
218 |
219 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
220 | exclude group:'com.facebook.flipper'
221 | }
222 |
223 | if (enableHermes) {
224 | def hermesPath = "../../node_modules/hermes-engine/android/";
225 | debugImplementation files(hermesPath + "hermes-debug.aar")
226 | releaseImplementation files(hermesPath + "hermes-release.aar")
227 | } else {
228 | implementation jscFlavor
229 | }
230 | }
231 |
232 | // Run this once to be able to run the application with BUCK
233 | // puts all compile dependencies into folder libs for BUCK to use
234 | task copyDownloadableDepsToLibs(type: Copy) {
235 | from configurations.implementation
236 | into 'libs'
237 | }
238 |
239 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
240 |
--------------------------------------------------------------------------------
/client/android/app/build_defs.bzl:
--------------------------------------------------------------------------------
1 | """Helper definitions to glob .aar and .jar targets"""
2 |
3 | def create_aar_targets(aarfiles):
4 | for aarfile in aarfiles:
5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
6 | lib_deps.append(":" + name)
7 | android_prebuilt_aar(
8 | name = name,
9 | aar = aarfile,
10 | )
11 |
12 | def create_jar_targets(jarfiles):
13 | for jarfile in jarfiles:
14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
15 | lib_deps.append(":" + name)
16 | prebuilt_jar(
17 | name = name,
18 | binary_jar = jarfile,
19 | )
20 |
--------------------------------------------------------------------------------
/client/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/debug.keystore
--------------------------------------------------------------------------------
/client/android/app/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "140434318989",
4 | "project_id": "react-native-callkit",
5 | "storage_bucket": "react-native-callkit.appspot.com"
6 | },
7 | "client": [
8 | {
9 | "client_info": {
10 | "mobilesdk_app_id": "XXXX",
11 | "android_client_info": {
12 | "package_name": "com.call.trigger"
13 | }
14 | },
15 | "oauth_client": [
16 | {
17 | "client_id": "XXXX.com",
18 | "client_type": 3
19 | }
20 | ],
21 | "api_key": [
22 | {
23 | "current_key": "XXXX"
24 | }
25 | ],
26 | "services": {
27 | "appinvite_service": {
28 | "other_platform_oauth_client": [
29 | {
30 | "client_id": "XXXX.googleusercontent.com",
31 | "client_type": 3
32 | }
33 | ]
34 | }
35 | }
36 | }
37 | ],
38 | "configuration_version": "1"
39 | }
--------------------------------------------------------------------------------
/client/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/client/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/client/android/app/src/debug/java/com/call/trigger/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.call.trigger;
8 |
9 | import android.content.Context;
10 | import com.facebook.flipper.android.AndroidFlipperClient;
11 | import com.facebook.flipper.android.utils.FlipperUtils;
12 | import com.facebook.flipper.core.FlipperClient;
13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping;
17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
22 | import com.facebook.react.ReactInstanceManager;
23 | import com.facebook.react.bridge.ReactContext;
24 | import com.facebook.react.modules.network.NetworkingModule;
25 | import okhttp3.OkHttpClient;
26 |
27 | public class ReactNativeFlipper {
28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
29 | if (FlipperUtils.shouldEnableFlipper(context)) {
30 | final FlipperClient client = AndroidFlipperClient.getInstance(context);
31 |
32 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
33 | client.addPlugin(new ReactFlipperPlugin());
34 | client.addPlugin(new DatabasesFlipperPlugin(context));
35 | client.addPlugin(new SharedPreferencesFlipperPlugin(context));
36 | client.addPlugin(CrashReporterPlugin.getInstance());
37 |
38 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
39 | NetworkingModule.setCustomClientBuilder(
40 | new NetworkingModule.CustomClientBuilder() {
41 | @Override
42 | public void apply(OkHttpClient.Builder builder) {
43 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
44 | }
45 | });
46 | client.addPlugin(networkFlipperPlugin);
47 | client.start();
48 |
49 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
50 | // Hence we run if after all native modules have been initialized
51 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
52 | if (reactContext == null) {
53 | reactInstanceManager.addReactInstanceEventListener(
54 | new ReactInstanceManager.ReactInstanceEventListener() {
55 | @Override
56 | public void onReactContextInitialized(ReactContext reactContext) {
57 | reactInstanceManager.removeReactInstanceEventListener(this);
58 | reactContext.runOnNativeModulesQueueThread(
59 | new Runnable() {
60 | @Override
61 | public void run() {
62 | client.addPlugin(new FrescoFlipperPlugin());
63 | }
64 | });
65 | }
66 | });
67 | } else {
68 | client.addPlugin(new FrescoFlipperPlugin());
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/client/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
36 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
59 |
63 |
67 |
68 |
69 |
70 |
71 |
72 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/client/android/app/src/main/assets/fonts/Roboto-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/assets/fonts/Roboto-Black.ttf
--------------------------------------------------------------------------------
/client/android/app/src/main/assets/fonts/Roboto-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/assets/fonts/Roboto-BlackItalic.ttf
--------------------------------------------------------------------------------
/client/android/app/src/main/assets/fonts/Roboto-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/assets/fonts/Roboto-Bold.ttf
--------------------------------------------------------------------------------
/client/android/app/src/main/assets/fonts/Roboto-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/assets/fonts/Roboto-BoldItalic.ttf
--------------------------------------------------------------------------------
/client/android/app/src/main/assets/fonts/Roboto-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/assets/fonts/Roboto-Italic.ttf
--------------------------------------------------------------------------------
/client/android/app/src/main/assets/fonts/Roboto-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/assets/fonts/Roboto-Light.ttf
--------------------------------------------------------------------------------
/client/android/app/src/main/assets/fonts/Roboto-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/assets/fonts/Roboto-LightItalic.ttf
--------------------------------------------------------------------------------
/client/android/app/src/main/assets/fonts/Roboto-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/assets/fonts/Roboto-Medium.ttf
--------------------------------------------------------------------------------
/client/android/app/src/main/assets/fonts/Roboto-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/assets/fonts/Roboto-MediumItalic.ttf
--------------------------------------------------------------------------------
/client/android/app/src/main/assets/fonts/Roboto-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/assets/fonts/Roboto-Regular.ttf
--------------------------------------------------------------------------------
/client/android/app/src/main/assets/fonts/Roboto-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/assets/fonts/Roboto-Thin.ttf
--------------------------------------------------------------------------------
/client/android/app/src/main/assets/fonts/Roboto-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/assets/fonts/Roboto-ThinItalic.ttf
--------------------------------------------------------------------------------
/client/android/app/src/main/java/com/call/trigger/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.call.trigger;
2 |
3 | import com.facebook.react.ReactActivity;
4 | import android.os.Bundle;
5 |
6 | public class MainActivity extends ReactActivity {
7 |
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(null);
11 | }
12 | /**
13 | * Returns the name of the main component registered from JavaScript. This is used to schedule
14 | * rendering of the component.
15 | */
16 | @Override
17 | protected String getMainComponentName() {
18 | return "ReactNativeCallTrigger";
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/client/android/app/src/main/java/com/call/trigger/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.call.trigger;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import com.facebook.react.PackageList;
6 | import com.facebook.react.ReactApplication;
7 | import com.facebook.react.ReactInstanceManager;
8 | import com.facebook.react.ReactNativeHost;
9 | import com.facebook.react.ReactPackage;
10 | import com.facebook.soloader.SoLoader;
11 | import java.lang.reflect.InvocationTargetException;
12 | import java.util.List;
13 | import com.airbnb.android.react.lottie.LottiePackage;
14 | import live.videosdk.rnfgservice.ForegroundServicePackage;
15 | import live.videosdk.rnincallmanager.InCallManagerPackage;
16 | import live.videosdk.rnwebrtc.WebRTCModulePackage;
17 |
18 |
19 | public class MainApplication extends Application implements ReactApplication {
20 |
21 | private final ReactNativeHost mReactNativeHost =
22 | new ReactNativeHost(this) {
23 | @Override
24 | public boolean getUseDeveloperSupport() {
25 | return BuildConfig.DEBUG;
26 | }
27 |
28 | @Override
29 | protected List getPackages() {
30 | @SuppressWarnings("UnnecessaryLocalVariable")
31 | List packages = new PackageList(this).getPackages();
32 | // Packages that cannot be autolinked yet can be added manually here, for example:
33 | // packages.add(new MyReactNativePackage());
34 | packages.add(new ForegroundServicePackage());
35 | packages.add(new InCallManagerPackage());
36 | packages.add(new WebRTCModulePackage());
37 | packages.add(new LottiePackage());
38 |
39 | return packages;
40 | }
41 |
42 | @Override
43 | protected String getJSMainModuleName() {
44 | return "index";
45 | }
46 | };
47 |
48 | @Override
49 | public ReactNativeHost getReactNativeHost() {
50 | return mReactNativeHost;
51 | }
52 |
53 | @Override
54 | public void onCreate() {
55 | super.onCreate();
56 | SoLoader.init(this, /* native exopackage */ false);
57 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
58 | }
59 |
60 | /**
61 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like
62 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
63 | *
64 | * @param context
65 | * @param reactInstanceManager
66 | */
67 | private static void initializeFlipper(
68 | Context context, ReactInstanceManager reactInstanceManager) {
69 | if (BuildConfig.DEBUG) {
70 | try {
71 | /*
72 | We use reflection here to pick up the class that initializes Flipper,
73 | since Flipper library is not available in release mode
74 | */
75 | Class> aClass = Class.forName("com.call.trigger.ReactNativeFlipper");
76 | aClass
77 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
78 | .invoke(null, context, reactInstanceManager);
79 | } catch (ClassNotFoundException e) {
80 | e.printStackTrace();
81 | } catch (NoSuchMethodException e) {
82 | e.printStackTrace();
83 | } catch (IllegalAccessException e) {
84 | e.printStackTrace();
85 | } catch (InvocationTargetException e) {
86 | e.printStackTrace();
87 | }
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/client/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/client/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/client/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/client/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/client/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/client/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/client/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/client/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/client/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/client/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/client/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 | - #FC0303
3 |
4 | - @color/red
5 |
6 |
--------------------------------------------------------------------------------
/client/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | VideoSDK Call Trigger App
3 |
4 |
--------------------------------------------------------------------------------
/client/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/client/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext {
5 | buildToolsVersion = "30.0.3"
6 | minSdkVersion = 23
7 | compileSdkVersion = 31
8 | targetSdkVersion = 31
9 | ndkVersion = "20.1.5948944"
10 | }
11 | repositories {
12 | google()
13 | jcenter()
14 | }
15 | dependencies {
16 | classpath("com.android.tools.build:gradle:7.2.1")
17 | classpath 'com.google.gms:google-services:4.3.10'
18 | classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1'
19 | // NOTE: Do not place your application dependencies here; they belong
20 | // in the individual module build.gradle files
21 | }
22 | }
23 |
24 | allprojects {
25 | repositories {
26 | mavenLocal()
27 | maven {
28 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
29 | url("$rootDir/../node_modules/react-native/android")
30 | }
31 | maven {
32 | // Android JSC is installed from npm
33 | url("$rootDir/../node_modules/jsc-android/dist")
34 | }
35 |
36 | google()
37 | jcenter()
38 | maven { url 'https://www.jitpack.io' }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/client/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 | # Automatically convert third-party libraries to use AndroidX
25 | android.enableJetifier=true
26 |
27 | # Version of flipper SDK to use with React Native
28 | FLIPPER_VERSION=0.105.0
29 | android.enableDexingArtifactTransform.desugaring=false
30 |
--------------------------------------------------------------------------------
/client/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/client/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/client/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/client/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/client/android/link-assets-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "migIndex": 1,
3 | "data": [
4 | {
5 | "path": "src/assets/fonts/Roboto/Roboto-Black.ttf",
6 | "sha1": "ee52f7cf8e54f3ee2afbc474a352d5c19514d9c1"
7 | },
8 | {
9 | "path": "src/assets/fonts/Roboto/Roboto-BlackItalic.ttf",
10 | "sha1": "94ba47a5510d3e21ae4db0da0425b855686de586"
11 | },
12 | {
13 | "path": "src/assets/fonts/Roboto/Roboto-Bold.ttf",
14 | "sha1": "3dd713113ff2d79b94d2df343e2e28fa8e7279cf"
15 | },
16 | {
17 | "path": "src/assets/fonts/Roboto/Roboto-BoldItalic.ttf",
18 | "sha1": "60b9860b7fc93d6b0b322f170634105bc6a8cc8d"
19 | },
20 | {
21 | "path": "src/assets/fonts/Roboto/Roboto-Italic.ttf",
22 | "sha1": "dc6756127707ab2d6e388a6023087351fa41999c"
23 | },
24 | {
25 | "path": "src/assets/fonts/Roboto/Roboto-Light.ttf",
26 | "sha1": "92cc3b6f9440193c12fd02ed690e434d685a9cc8"
27 | },
28 | {
29 | "path": "src/assets/fonts/Roboto/Roboto-LightItalic.ttf",
30 | "sha1": "a0440f60a96a59c4105c3eb639cb2573826f84fe"
31 | },
32 | {
33 | "path": "src/assets/fonts/Roboto/Roboto-Medium.ttf",
34 | "sha1": "f6783010d5def128c4a1539333324f75701d9bab"
35 | },
36 | {
37 | "path": "src/assets/fonts/Roboto/Roboto-MediumItalic.ttf",
38 | "sha1": "e4e31e55d279a9b12c32327a60a3a65c8350e5df"
39 | },
40 | {
41 | "path": "src/assets/fonts/Roboto/Roboto-Regular.ttf",
42 | "sha1": "096c9245b6a192d1403a82848e104a65f578a8ec"
43 | },
44 | {
45 | "path": "src/assets/fonts/Roboto/Roboto-Thin.ttf",
46 | "sha1": "711e666e7f52210ac487c7ed81a0ac68a2b52261"
47 | },
48 | {
49 | "path": "src/assets/fonts/Roboto/Roboto-ThinItalic.ttf",
50 | "sha1": "75ffde032d49005faa643338eda97c311ad7d316"
51 | }
52 | ]
53 | }
54 |
--------------------------------------------------------------------------------
/client/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'ReactNativeCallTrigger'
2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
3 |
4 | include ':rnwebrtc'
5 | project(':rnwebrtc').projectDir = new File(rootProject.projectDir, '../node_modules/@videosdk.live/react-native-webrtc/android')
6 |
7 | include ':rnincallmanager'
8 | project(':rnincallmanager').projectDir = new File(rootProject.projectDir, '../node_modules/@videosdk.live/react-native-incallmanager/android')
9 |
10 | include ':rnfgservice'
11 | project(':rnfgservice').projectDir = new File(rootProject.projectDir, '../node_modules/@videosdk.live/react-native-foreground-service/android')
12 |
13 | include ':lottie-react-native'
14 | project(':lottie-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/lottie-react-native/src/android')
15 |
16 | include ':app'
17 |
--------------------------------------------------------------------------------
/client/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ReactNativeCallTrigger",
3 | "displayName": "ReactNativeCallTrigger"
4 | }
--------------------------------------------------------------------------------
/client/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | };
4 |
--------------------------------------------------------------------------------
/client/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "react-native": {
3 | "crashlytics_debug_enabled": true,
4 | "crashlytics_disable_auto_disabler": true,
5 | "crashlytics_auto_collection_enabled": true,
6 | "crashlytics_is_error_generation_on_js_crash_enabled": true,
7 | "crashlytics_javascript_exception_handler_chaining_enabled": false
8 | }
9 | }
--------------------------------------------------------------------------------
/client/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @format
3 | */
4 |
5 | import { AppRegistry, StatusBar, Linking, Platform } from "react-native";
6 | import App from "./App";
7 | import { name as appName } from "./app.json";
8 | import { register } from "@videosdk.live/react-native-sdk";
9 | import colors from "./src/styles/colors";
10 | import messaging from "@react-native-firebase/messaging";
11 | import React from "react";
12 | import Incomingvideocall from "./src/utils/incoming-video-call";
13 | import { updateCallStatus } from "./src/api/api";
14 |
15 | Platform.OS == "android" && StatusBar.setBackgroundColor(colors.primary[900]);
16 |
17 | // Register the VideoSDK service
18 | register();
19 |
20 | const firebaseListener = async (remoteMessage) => {
21 | const { callerInfo, videoSDKInfo, type } = JSON.parse(
22 | remoteMessage.data.info
23 | );
24 |
25 | if (type === "CALL_INITIATED") {
26 | const incomingCallAnswer = ({ callUUID }) => {
27 | Incomingvideocall.backToForeground();
28 | updateCallStatus({
29 | callerInfo,
30 | type: "ACCEPTED",
31 | });
32 | Incomingvideocall.endIncomingcallAnswer(callUUID);
33 | Linking.openURL(
34 | `videocalling://meetingscreen/${videoSDKInfo.token}/${videoSDKInfo.meetingId}`
35 | ).catch((err) => {
36 | Toast.show(`Error`, err);
37 | });
38 | };
39 |
40 | const endIncomingCall = () => {
41 | Incomingvideocall.endIncomingcallAnswer();
42 | updateCallStatus({ callerInfo, type: "REJECTED" });
43 | };
44 |
45 | Incomingvideocall.configure(incomingCallAnswer, endIncomingCall);
46 | Incomingvideocall.displayIncomingCall(callerInfo.name);
47 | Incomingvideocall.backToForeground();
48 | }
49 | };
50 |
51 | // Register background handler
52 | messaging().setBackgroundMessageHandler(firebaseListener);
53 |
54 | function HeadlessCheck({ isHeadless }) {
55 | if (isHeadless) {
56 | // App has been launched in the background by iOS, ignore
57 | return null;
58 | }
59 |
60 | return ;
61 | }
62 |
63 | AppRegistry.registerComponent(appName, () => HeadlessCheck);
64 |
--------------------------------------------------------------------------------
/client/ios/BroadcastScreen/Atomic.swift:
--------------------------------------------------------------------------------
1 |
2 | import Foundation
3 |
4 | @propertyWrapper
5 | struct Atomic {
6 |
7 | private var value: Value
8 | private let lock = NSLock()
9 |
10 | init(wrappedValue value: Value) {
11 | self.value = value
12 | }
13 |
14 | var wrappedValue: Value {
15 | get { load() }
16 | set { store(newValue: newValue) }
17 | }
18 |
19 | func load() -> Value {
20 | lock.lock()
21 | defer { lock.unlock() }
22 | return value
23 | }
24 |
25 | mutating func store(newValue: Value) {
26 | lock.lock()
27 | defer { lock.unlock() }
28 | value = newValue
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/client/ios/BroadcastScreen/BroadcastScreen.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.application-groups
6 |
7 | group.com.example.broadcastScreen
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/client/ios/BroadcastScreen/DarwinNotificationCenter.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | enum DarwinNotification: String {
4 | case broadcastStarted = "iOS_BroadcastStarted"
5 | case broadcastStopped = "iOS_BroadcastStopped"
6 | }
7 |
8 | class DarwinNotificationCenter {
9 |
10 | static let shared = DarwinNotificationCenter()
11 |
12 | private let notificationCenter: CFNotificationCenter
13 |
14 | init() {
15 | notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
16 | }
17 |
18 | func postNotification(_ name: DarwinNotification) {
19 | CFNotificationCenterPostNotification(notificationCenter, CFNotificationName(rawValue: name.rawValue as CFString), nil, nil, true)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/client/ios/BroadcastScreen/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSExtension
6 |
7 | NSExtensionPointIdentifier
8 | com.apple.broadcast-services-upload
9 | NSExtensionPrincipalClass
10 | $(PRODUCT_MODULE_NAME).SampleHandler
11 | RPBroadcastProcessMode
12 | RPBroadcastProcessModeSampleBuffer
13 |
14 | CFBundleURLTypes
15 |
16 |
17 | CFBundleURLSchemes
18 |
19 | videocalling
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/client/ios/BroadcastScreen/SampleHandler.swift:
--------------------------------------------------------------------------------
1 | import ReplayKit
2 |
3 |
4 | private enum Constants {
5 | // the App Group ID value that the app and the broadcast extension targets are setup with. It differs for each app.
6 | static let appGroupIdentifier = "group.com.example.broadcastScreen"
7 | }
8 |
9 | class SampleHandler: RPBroadcastSampleHandler {
10 |
11 | private var clientConnection: SocketConnection?
12 | private var uploader: SampleUploader?
13 |
14 | private var frameCount: Int = 0
15 |
16 | var socketFilePath: String {
17 | let sharedContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupIdentifier)
18 | return sharedContainer?.appendingPathComponent("rtc_SSFD").path ?? ""
19 | }
20 |
21 | override init() {
22 | super.init()
23 | if let connection = SocketConnection(filePath: socketFilePath) {
24 | clientConnection = connection
25 | setupConnection()
26 |
27 | uploader = SampleUploader(connection: connection)
28 | }
29 | }
30 |
31 | override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {
32 | // User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional.
33 | frameCount = 0
34 |
35 | DarwinNotificationCenter.shared.postNotification(.broadcastStarted)
36 |
37 | openConnection()
38 | let notificationName = CFNotificationName("com.notification.start" as CFString)
39 | let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
40 | CFNotificationCenterPostNotification(notificationCenter, notificationName, nil, nil, true)
41 | }
42 |
43 | override func broadcastPaused() {
44 | // User has requested to pause the broadcast. Samples will stop being delivered.
45 | }
46 |
47 | override func broadcastResumed() {
48 | // User has requested to resume the broadcast. Samples delivery will resume.
49 | }
50 |
51 | override func broadcastFinished() {
52 | // User has requested to finish the broadcast.
53 | DarwinNotificationCenter.shared.postNotification(.broadcastStopped)
54 | clientConnection?.close()
55 |
56 | let notificationName = CFNotificationName("com.notification.stop" as CFString)
57 | let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
58 | CFNotificationCenterPostNotification(notificationCenter, notificationName, nil, nil, true)
59 | }
60 |
61 | override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {
62 | switch sampleBufferType {
63 | case RPSampleBufferType.video:
64 | // very simple mechanism for adjusting frame rate by using every third frame
65 | frameCount += 1
66 | if frameCount % 3 == 0 {
67 | uploader?.send(sample: sampleBuffer)
68 | }
69 | default:
70 | break
71 | }
72 | }
73 | }
74 |
75 | private extension SampleHandler {
76 |
77 | func setupConnection() {
78 | clientConnection?.didClose = { [weak self] error in
79 | print("client connection did close \(String(describing: error))")
80 |
81 | if let error = error {
82 | self?.finishBroadcastWithError(error)
83 | } else {
84 | // the displayed failure message is more user friendly when using NSError instead of Error
85 | let JMScreenSharingStopped = 10001
86 | let customError = NSError(domain: RPRecordingErrorDomain, code: JMScreenSharingStopped, userInfo: [NSLocalizedDescriptionKey: "Screen sharing stopped"])
87 | self?.finishBroadcastWithError(customError)
88 | }
89 | }
90 | }
91 |
92 | func openConnection() {
93 | let queue = DispatchQueue(label: "broadcast.connectTimer")
94 | let timer = DispatchSource.makeTimerSource(queue: queue)
95 | timer.schedule(deadline: .now(), repeating: .milliseconds(100), leeway: .milliseconds(500))
96 | timer.setEventHandler { [weak self] in
97 | guard self?.clientConnection?.open() == true else {
98 | return
99 | }
100 |
101 | timer.cancel()
102 | }
103 |
104 | timer.resume()
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/client/ios/BroadcastScreen/SampleUploader.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import ReplayKit
3 |
4 | private enum Constants {
5 | static let bufferMaxLength = 10240
6 | }
7 |
8 | class SampleUploader {
9 |
10 | private static var imageContext = CIContext(options: nil)
11 |
12 | @Atomic private var isReady = false
13 | private var connection: SocketConnection
14 |
15 | private var dataToSend: Data?
16 | private var byteIndex = 0
17 |
18 | private let serialQueue: DispatchQueue
19 |
20 | init(connection: SocketConnection) {
21 | self.connection = connection
22 | self.serialQueue = DispatchQueue(label: "org.videosdk.broadcast.sampleUploader")
23 |
24 | setupConnection()
25 | }
26 |
27 | @discardableResult func send(sample buffer: CMSampleBuffer) -> Bool {
28 | guard isReady else {
29 | return false
30 | }
31 |
32 | isReady = false
33 |
34 | dataToSend = prepare(sample: buffer)
35 | byteIndex = 0
36 |
37 | serialQueue.async { [weak self] in
38 | self?.sendDataChunk()
39 | }
40 |
41 | return true
42 | }
43 | }
44 |
45 | private extension SampleUploader {
46 |
47 | func setupConnection() {
48 | connection.didOpen = { [weak self] in
49 | self?.isReady = true
50 | }
51 | connection.streamHasSpaceAvailable = { [weak self] in
52 | self?.serialQueue.async {
53 | if let success = self?.sendDataChunk() {
54 | self?.isReady = !success
55 | }
56 | }
57 | }
58 | }
59 |
60 | @discardableResult func sendDataChunk() -> Bool {
61 | guard let dataToSend = dataToSend else {
62 | return false
63 | }
64 |
65 | var bytesLeft = dataToSend.count - byteIndex
66 | var length = bytesLeft > Constants.bufferMaxLength ? Constants.bufferMaxLength : bytesLeft
67 |
68 | length = dataToSend[byteIndex..<(byteIndex + length)].withUnsafeBytes {
69 | guard let ptr = $0.bindMemory(to: UInt8.self).baseAddress else {
70 | return 0
71 | }
72 |
73 | return connection.writeToStream(buffer: ptr, maxLength: length)
74 | }
75 |
76 | if length > 0 {
77 | byteIndex += length
78 | bytesLeft -= length
79 |
80 | if bytesLeft == 0 {
81 | self.dataToSend = nil
82 | byteIndex = 0
83 | }
84 | } else {
85 | print("writeBufferToStream failure")
86 | }
87 |
88 | return true
89 | }
90 |
91 | func prepare(sample buffer: CMSampleBuffer) -> Data? {
92 | guard let imageBuffer = CMSampleBufferGetImageBuffer(buffer) else {
93 | print("image buffer not available")
94 | return nil
95 | }
96 |
97 | CVPixelBufferLockBaseAddress(imageBuffer, .readOnly)
98 |
99 | let scaleFactor = 2.0
100 | let width = CVPixelBufferGetWidth(imageBuffer)/Int(scaleFactor)
101 | let height = CVPixelBufferGetHeight(imageBuffer)/Int(scaleFactor)
102 | let orientation = CMGetAttachment(buffer, key: RPVideoSampleOrientationKey as CFString, attachmentModeOut: nil)?.uintValue ?? 0
103 |
104 | let scaleTransform = CGAffineTransform(scaleX: CGFloat(1.0/scaleFactor), y: CGFloat(1.0/scaleFactor))
105 | let bufferData = self.jpegData(from: imageBuffer, scale: scaleTransform)
106 |
107 | CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly)
108 |
109 | guard let messageData = bufferData else {
110 | print("corrupted image buffer")
111 | return nil
112 | }
113 |
114 | let httpResponse = CFHTTPMessageCreateResponse(nil, 200, nil, kCFHTTPVersion1_1).takeRetainedValue()
115 | CFHTTPMessageSetHeaderFieldValue(httpResponse, "Content-Length" as CFString, String(messageData.count) as CFString)
116 | CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Width" as CFString, String(width) as CFString)
117 | CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Height" as CFString, String(height) as CFString)
118 | CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Orientation" as CFString, String(orientation) as CFString)
119 |
120 | CFHTTPMessageSetBody(httpResponse, messageData as CFData)
121 |
122 | let serializedMessage = CFHTTPMessageCopySerializedMessage(httpResponse)?.takeRetainedValue() as Data?
123 |
124 | return serializedMessage
125 | }
126 |
127 | func jpegData(from buffer: CVPixelBuffer, scale scaleTransform: CGAffineTransform) -> Data? {
128 | let image = CIImage(cvPixelBuffer: buffer).transformed(by: scaleTransform)
129 |
130 | guard let colorSpace = image.colorSpace else {
131 | return nil
132 | }
133 |
134 | let options: [CIImageRepresentationOption: Float] = [kCGImageDestinationLossyCompressionQuality as CIImageRepresentationOption: 1.0]
135 |
136 | return SampleUploader.imageContext.jpegRepresentation(of: image, colorSpace: colorSpace, options: options)
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/client/ios/BroadcastScreen/SocketConnection.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | class SocketConnection: NSObject {
4 | var didOpen: (() -> Void)?
5 | var didClose: ((Error?) -> Void)?
6 | var streamHasSpaceAvailable: (() -> Void)?
7 |
8 | private let filePath: String
9 | private var socketHandle: Int32 = -1
10 | private var address: sockaddr_un?
11 |
12 | private var inputStream: InputStream?
13 | private var outputStream: OutputStream?
14 |
15 | private var networkQueue: DispatchQueue?
16 | private var shouldKeepRunning = false
17 |
18 | init?(filePath path: String) {
19 | filePath = path
20 | socketHandle = Darwin.socket(AF_UNIX, SOCK_STREAM, 0)
21 |
22 | guard socketHandle != -1 else {
23 | print("failure: create socket")
24 | return nil
25 | }
26 | }
27 |
28 | func open() -> Bool {
29 | print("open socket connection")
30 |
31 | guard FileManager.default.fileExists(atPath: filePath) else {
32 | print("failure: socket file missing")
33 | return false
34 | }
35 |
36 | guard setupAddress() == true else {
37 | return false
38 | }
39 |
40 | guard connectSocket() == true else {
41 | return false
42 | }
43 |
44 | setupStreams()
45 |
46 | inputStream?.open()
47 | outputStream?.open()
48 |
49 | return true
50 | }
51 |
52 | func close() {
53 | unscheduleStreams()
54 |
55 | inputStream?.delegate = nil
56 | outputStream?.delegate = nil
57 |
58 | inputStream?.close()
59 | outputStream?.close()
60 |
61 | inputStream = nil
62 | outputStream = nil
63 | }
64 |
65 | func writeToStream(buffer: UnsafePointer, maxLength length: Int) -> Int {
66 | outputStream?.write(buffer, maxLength: length) ?? 0
67 | }
68 | }
69 |
70 | extension SocketConnection: StreamDelegate {
71 |
72 | func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
73 | switch eventCode {
74 | case .openCompleted:
75 | print("client stream open completed")
76 | if aStream == outputStream {
77 | didOpen?()
78 | }
79 | case .hasBytesAvailable:
80 | if aStream == inputStream {
81 | var buffer: UInt8 = 0
82 | let numberOfBytesRead = inputStream?.read(&buffer, maxLength: 1)
83 | if numberOfBytesRead == 0 && aStream.streamStatus == .atEnd {
84 | print("server socket closed")
85 | close()
86 | notifyDidClose(error: nil)
87 | }
88 | }
89 | case .hasSpaceAvailable:
90 | if aStream == outputStream {
91 | streamHasSpaceAvailable?()
92 | }
93 | case .errorOccurred:
94 | print("client stream error occured: \(String(describing: aStream.streamError))")
95 | close()
96 | notifyDidClose(error: aStream.streamError)
97 |
98 | default:
99 | break
100 | }
101 | }
102 | }
103 |
104 | private extension SocketConnection {
105 |
106 | func setupAddress() -> Bool {
107 | var addr = sockaddr_un()
108 | guard filePath.count < MemoryLayout.size(ofValue: addr.sun_path) else {
109 | print("failure: fd path is too long")
110 | return false
111 | }
112 |
113 | _ = withUnsafeMutablePointer(to: &addr.sun_path.0) { ptr in
114 | filePath.withCString {
115 | strncpy(ptr, $0, filePath.count)
116 | }
117 | }
118 |
119 | address = addr
120 | return true
121 | }
122 |
123 | func connectSocket() -> Bool {
124 | guard var addr = address else {
125 | return false
126 | }
127 |
128 | let status = withUnsafePointer(to: &addr) { ptr in
129 | ptr.withMemoryRebound(to: sockaddr.self, capacity: 1) {
130 | Darwin.connect(socketHandle, $0, socklen_t(MemoryLayout.size))
131 | }
132 | }
133 |
134 | guard status == noErr else {
135 | print("failure: \(status)")
136 | return false
137 | }
138 |
139 | return true
140 | }
141 |
142 | func setupStreams() {
143 | var readStream: Unmanaged?
144 | var writeStream: Unmanaged?
145 |
146 | CFStreamCreatePairWithSocket(kCFAllocatorDefault, socketHandle, &readStream, &writeStream)
147 |
148 | inputStream = readStream?.takeRetainedValue()
149 | inputStream?.delegate = self
150 | inputStream?.setProperty(kCFBooleanTrue, forKey: Stream.PropertyKey(kCFStreamPropertyShouldCloseNativeSocket as String))
151 |
152 | outputStream = writeStream?.takeRetainedValue()
153 | outputStream?.delegate = self
154 | outputStream?.setProperty(kCFBooleanTrue, forKey: Stream.PropertyKey(kCFStreamPropertyShouldCloseNativeSocket as String))
155 |
156 | scheduleStreams()
157 | }
158 |
159 | func scheduleStreams() {
160 | shouldKeepRunning = true
161 |
162 | networkQueue = DispatchQueue.global(qos: .userInitiated)
163 | networkQueue?.async { [weak self] in
164 | self?.inputStream?.schedule(in: .current, forMode: .common)
165 | self?.outputStream?.schedule(in: .current, forMode: .common)
166 | RunLoop.current.run()
167 |
168 | var isRunning = false
169 |
170 | repeat {
171 | isRunning = self?.shouldKeepRunning ?? false && RunLoop.current.run(mode: .default, before: .distantFuture)
172 | } while (isRunning)
173 | }
174 | }
175 |
176 | func unscheduleStreams() {
177 | networkQueue?.sync { [weak self] in
178 | self?.inputStream?.remove(from: .current, forMode: .common)
179 | self?.outputStream?.remove(from: .current, forMode: .common)
180 | }
181 |
182 | shouldKeepRunning = false
183 | }
184 |
185 | func notifyDidClose(error: Error?) {
186 | if didClose != nil {
187 | didClose?(error)
188 | }
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/client/ios/ExportOptions.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | method
6 | app-store
7 | teamID
8 | 8GZ776NSU2
9 | signingStyle
10 | manual
11 | provisioningProfiles
12 |
13 | org.reactjs.RNCodeSample
14 | React Native iOS Example
15 | org.reactjs.RNCodeSample.broadcastscreen
16 | React Native iOS BroadcastScreen
17 |
18 |
19 |
--------------------------------------------------------------------------------
/client/ios/Podfile:
--------------------------------------------------------------------------------
1 | require_relative '../node_modules/react-native/scripts/react_native_pods'
2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
3 |
4 | platform :ios, '13.0'
5 |
6 | target 'ReactNativeCallTrigger' do
7 | config = use_native_modules!
8 |
9 | use_react_native!(
10 | :path => config[:reactNativePath],
11 | # to enable hermes on iOS, change `false` to `true` and then install pods
12 | :hermes_enabled => false
13 | )
14 |
15 | pod 'react-native-webrtc', :path => '../node_modules/@videosdk.live/react-native-webrtc'
16 | pod 'Firebase', :modular_headers => true
17 | pod 'FirebaseCoreInternal', :modular_headers => true
18 | pod 'FirebaseCore', :modular_headers => true
19 | pod 'GoogleUtilities', :modular_headers => true
20 | pod 'RNCallKeep', :path => '../node_modules/react-native-callkeep'
21 |
22 | target 'ReactNativeCallTriggerTests' do
23 | inherit! :complete
24 | # Pods for testing
25 | end
26 |
27 | # Enables Flipper.
28 | #
29 | # Note that if you have use_frameworks! enabled, Flipper will not work and
30 | # you should disable the next line.
31 | # use_flipper!()
32 |
33 | post_install do |installer|
34 | react_native_post_install(installer)
35 | end
36 | end
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger-Bridging-Header.h:
--------------------------------------------------------------------------------
1 |
2 | #import "React/RCTEventEmitter.h"
3 |
4 |
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger.xcodeproj/xcshareddata/xcschemes/ReactNativeCallTrigger.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : UIResponder
5 |
6 | @property (nonatomic, strong) UIWindow *window;
7 |
8 | @end
9 |
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 | #import
5 | #import
6 | #import
7 | #import "RNCallKeep.h"
8 | #import /* <------ add this line */
9 | #import "RNVoipPushNotificationManager.h"
10 | #import
11 |
12 | @implementation AppDelegate
13 |
14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
15 | {
16 |
17 | [FIRApp configure];
18 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
19 |
20 | [RNCallKeep setup:@{
21 | @"appName": @"VideoSDK Call Trigger",
22 | @"maximumCallGroups": @3,
23 | @"maximumCallsPerCallGroup": @1,
24 | @"supportsVideo": @YES,
25 | }];
26 |
27 | [RNVoipPushNotificationManager voipRegistration];
28 |
29 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
30 | moduleName:@"ReactNativeCallTrigger"
31 | initialProperties:nil];
32 |
33 | if (@available(iOS 13.0, *)) {
34 | rootView.backgroundColor = [UIColor systemBackgroundColor];
35 | } else {
36 | rootView.backgroundColor = [UIColor whiteColor];
37 | }
38 |
39 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
40 | UIViewController *rootViewController = [UIViewController new];
41 | rootViewController.view = rootView;
42 | self.window.rootViewController = rootViewController;
43 | [self.window makeKeyAndVisible];
44 | return YES;
45 | }
46 |
47 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
48 | {
49 | #if DEBUG
50 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
51 | #else
52 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
53 | #endif
54 | }
55 |
56 | - (BOOL)application:(UIApplication *)application
57 | continueUserActivity:(NSUserActivity *)userActivity
58 | restorationHandler:(void(^)(NSArray * __nullable restorableObjects))restorationHandler
59 | {
60 | return [RNCallKeep application:application
61 | continueUserActivity:userActivity
62 | restorationHandler:restorationHandler];
63 | }
64 |
65 | - (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type {
66 | // Register VoIP push token (a property of PKPushCredentials) with server
67 | [RNVoipPushNotificationManager didUpdatePushCredentials:credentials forType:(NSString *)type];
68 | }
69 |
70 | - (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type
71 | {
72 | // --- The system calls this method when a previously provided push token is no longer valid for use. No action is necessary on your part to reregister the push type. Instead, use this method to notify your server not to send push notifications using the matching push token.
73 | }
74 |
75 | - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
76 |
77 |
78 | // --- NOTE: apple forced us to invoke callkit ASAP when we receive voip push
79 | // --- see: react-native-callkeep
80 |
81 | // --- Retrieve information from your voip push payload
82 | NSString *uuid = payload.dictionaryPayload[@"uuid"];
83 | NSString *callerName = [NSString stringWithFormat:@"%@ Calling from VideoSDK", payload.dictionaryPayload[@"callerName"]];
84 | NSString *handle = payload.dictionaryPayload[@"handle"];
85 |
86 | // --- this is optional, only required if you want to call `completion()` on the js side
87 | [RNVoipPushNotificationManager addCompletionHandler:uuid completionHandler:completion];
88 |
89 | // --- Process the received push
90 | [RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type];
91 | // NSDictionary *extra = [payload.dictionaryPayload valueForKeyPath:@"custom.path.to.data"];
92 |
93 | [RNCallKeep reportNewIncomingCall: uuid
94 | handle: handle
95 | handleType: @"generic"
96 | hasVideo: YES
97 | localizedCallerName: callerName
98 | supportsHolding: YES
99 | supportsDTMF: YES
100 | supportsGrouping: YES
101 | supportsUngrouping: YES
102 | fromPushKit: YES
103 | payload: nil
104 | withCompletionHandler: completion];
105 |
106 | // --- You don't need to call it if you stored `completion()` and will call it on the js side.
107 | completion();
108 | }
109 |
110 | - (BOOL)application:(UIApplication *)application
111 | openURL:(NSURL *)url
112 | options:(NSDictionary *)options
113 | {
114 | return [RCTLinkingManager application:application openURL:url options:options];
115 | }
116 |
117 | @end
118 |
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/1024.png
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/114.png
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/120.png
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/180.png
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/29.png
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/40.png
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/57.png
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/58.png
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/60.png
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/80.png
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/87.png
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "size": "60x60",
5 | "expected-size": "180",
6 | "filename": "180.png",
7 | "folder": "Images.xcassets/AppIcon.appiconset/",
8 | "idiom": "iphone",
9 | "scale": "3x"
10 | },
11 | {
12 | "size": "40x40",
13 | "expected-size": "80",
14 | "filename": "80.png",
15 | "folder": "Images.xcassets/AppIcon.appiconset/",
16 | "idiom": "iphone",
17 | "scale": "2x"
18 | },
19 | {
20 | "size": "40x40",
21 | "expected-size": "120",
22 | "filename": "120.png",
23 | "folder": "Images.xcassets/AppIcon.appiconset/",
24 | "idiom": "iphone",
25 | "scale": "3x"
26 | },
27 | {
28 | "size": "60x60",
29 | "expected-size": "120",
30 | "filename": "120.png",
31 | "folder": "Images.xcassets/AppIcon.appiconset/",
32 | "idiom": "iphone",
33 | "scale": "2x"
34 | },
35 | {
36 | "size": "57x57",
37 | "expected-size": "57",
38 | "filename": "57.png",
39 | "folder": "Images.xcassets/AppIcon.appiconset/",
40 | "idiom": "iphone",
41 | "scale": "1x"
42 | },
43 | {
44 | "size": "29x29",
45 | "expected-size": "58",
46 | "filename": "58.png",
47 | "folder": "Images.xcassets/AppIcon.appiconset/",
48 | "idiom": "iphone",
49 | "scale": "2x"
50 | },
51 | {
52 | "size": "29x29",
53 | "expected-size": "29",
54 | "filename": "29.png",
55 | "folder": "Images.xcassets/AppIcon.appiconset/",
56 | "idiom": "iphone",
57 | "scale": "1x"
58 | },
59 | {
60 | "size": "29x29",
61 | "expected-size": "87",
62 | "filename": "87.png",
63 | "folder": "Images.xcassets/AppIcon.appiconset/",
64 | "idiom": "iphone",
65 | "scale": "3x"
66 | },
67 | {
68 | "size": "57x57",
69 | "expected-size": "114",
70 | "filename": "114.png",
71 | "folder": "Images.xcassets/AppIcon.appiconset/",
72 | "idiom": "iphone",
73 | "scale": "2x"
74 | },
75 | {
76 | "size": "20x20",
77 | "expected-size": "40",
78 | "filename": "40.png",
79 | "folder": "Images.xcassets/AppIcon.appiconset/",
80 | "idiom": "iphone",
81 | "scale": "2x"
82 | },
83 | {
84 | "size": "20x20",
85 | "expected-size": "60",
86 | "filename": "60.png",
87 | "folder": "Images.xcassets/AppIcon.appiconset/",
88 | "idiom": "iphone",
89 | "scale": "3x"
90 | },
91 | {
92 | "size": "1024x1024",
93 | "filename": "1024.png",
94 | "expected-size": "1024",
95 | "idiom": "ios-marketing",
96 | "folder": "Images.xcassets/AppIcon.appiconset/",
97 | "scale": "1x"
98 | }
99 | ]
100 | }
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | React Native VideoSDK App
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.2
21 | CFBundleSignature
22 | ????
23 | CFBundleURLTypes
24 |
25 |
26 | CFBundleTypeRole
27 | Editor
28 | CFBundleURLName
29 | videocalling
30 | CFBundleURLSchemes
31 |
32 | videocalling
33 |
34 |
35 |
36 | CFBundleVersion
37 | 12
38 | LSRequiresIPhoneOS
39 |
40 | NSAppTransportSecurity
41 |
42 | NSExceptionDomains
43 |
44 | localhost
45 |
46 | NSExceptionAllowsInsecureHTTPLoads
47 |
48 |
49 |
50 |
51 | NSCameraUsageDescription
52 | Camera permission description
53 | NSLocationWhenInUseUsageDescription
54 |
55 | NSMicrophoneUsageDescription
56 | Microphone permission description
57 | RTCAppGroupIdentifier
58 | group.com.example.broadcastScreen
59 | RTCScreenSharingExtension
60 | org.reactjs.ReactNativeCallTrigger.broadcastscreen
61 | UIAppFonts
62 |
63 | Roboto-Black.ttf
64 | Roboto-BlackItalic.ttf
65 | Roboto-Bold.ttf
66 | Roboto-BoldItalic.ttf
67 | Roboto-Italic.ttf
68 | Roboto-Light.ttf
69 | Roboto-LightItalic.ttf
70 | Roboto-Medium.ttf
71 | Roboto-MediumItalic.ttf
72 | Roboto-Regular.ttf
73 | Roboto-Thin.ttf
74 | Roboto-ThinItalic.ttf
75 |
76 | UIBackgroundModes
77 |
78 | fetch
79 | processing
80 | remote-notification
81 | voip
82 |
83 | UILaunchStoryboardName
84 | LaunchScreen
85 | UIRequiredDeviceCapabilities
86 |
87 | armv7
88 |
89 | UISupportedInterfaceOrientations
90 |
91 | UIInterfaceOrientationPortrait
92 | UIInterfaceOrientationLandscapeLeft
93 | UIInterfaceOrientationLandscapeRight
94 |
95 | UIViewControllerBasedStatusBarAppearance
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/ReactNativeCallTrigger.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | aps-environment
6 | development
7 | com.apple.security.application-groups
8 |
9 | group.com.example.broadcastScreen
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTrigger/main.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char * argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTriggerTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/client/ios/ReactNativeCallTriggerTests/ReactNativeCallTriggerTests.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #import
5 | #import
6 |
7 | #define TIMEOUT_SECONDS 600
8 | #define TEXT_TO_LOOK_FOR @"Welcome to React"
9 |
10 | @interface ReactNativeCallTriggerTests : XCTestCase
11 |
12 | @end
13 |
14 | @implementation ReactNativeCallTriggerTests
15 |
16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
17 | {
18 | if (test(view)) {
19 | return YES;
20 | }
21 | for (UIView *subview in [view subviews]) {
22 | if ([self findSubviewInView:subview matching:test]) {
23 | return YES;
24 | }
25 | }
26 | return NO;
27 | }
28 |
29 | - (void)testRendersWelcomeScreen
30 | {
31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
33 | BOOL foundElement = NO;
34 |
35 | __block NSString *redboxError = nil;
36 | #ifdef DEBUG
37 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
38 | if (level >= RCTLogLevelError) {
39 | redboxError = message;
40 | }
41 | });
42 | #endif
43 |
44 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
45 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
46 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
47 |
48 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
49 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
50 | return YES;
51 | }
52 | return NO;
53 | }];
54 | }
55 |
56 | #ifdef DEBUG
57 | RCTSetLogFunction(RCTDefaultLogFunction);
58 | #endif
59 |
60 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
61 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
62 | }
63 |
64 |
65 | @end
66 |
--------------------------------------------------------------------------------
/client/ios/VideosdkRPK.m:
--------------------------------------------------------------------------------
1 |
2 | #import
3 | #import
4 | #import "React/RCTEventEmitter.h"
5 |
6 | @interface RCT_EXTERN_MODULE(VideosdkRPK, RCTEventEmitter)
7 | RCT_EXTERN_METHOD(startBroadcast)
8 |
9 | @end
10 |
--------------------------------------------------------------------------------
/client/ios/VideosdkRPK.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import ReplayKit
3 | import Photos
4 | import UIKit
5 |
6 | @objc(VideosdkRPK)
7 | class VideosdkRPK: RCTEventEmitter {
8 |
9 | private var status = "Empty"
10 |
11 |
12 | var start_notificaion_callback: CFNotificationCallback = { center, observer, name, object, info in
13 | NotificationCenter.default.post(name: Notification.Name("START_BROADCAST"), object: nil)
14 | }
15 |
16 |
17 | var stop_notificaion_callback: CFNotificationCallback = { center, observer, name, object, info in
18 | NotificationCenter.default.post(name: Notification.Name("STOP_BROADCAST"), object: nil)
19 | }
20 |
21 |
22 | override init() {
23 | super.init()
24 | NotificationCenter.default.addObserver(self, selector: #selector(self.startBroadcastCallback(notification:)), name: Notification.Name("START_BROADCAST"), object: nil)
25 |
26 |
27 | NotificationCenter.default.addObserver(self, selector: #selector(self.stopBroadcastCallback(notification:)), name: Notification.Name("STOP_BROADCAST"), object: nil)
28 |
29 | let notificationStartIdentifier = "com.notification.start" as CFString
30 | let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
31 |
32 | CFNotificationCenterAddObserver(notificationCenter,
33 | nil,
34 | start_notificaion_callback,
35 | notificationStartIdentifier,
36 | nil,
37 | CFNotificationSuspensionBehavior.deliverImmediately)
38 |
39 |
40 | let notificationStopIdentifier = "com.notification.stop" as CFString
41 |
42 | CFNotificationCenterAddObserver(notificationCenter,
43 | nil,
44 | stop_notificaion_callback,
45 | notificationStopIdentifier,
46 | nil,
47 | CFNotificationSuspensionBehavior.deliverImmediately)
48 |
49 |
50 | }
51 |
52 | @objc func startBroadcastCallback(notification: NSNotification){
53 | status = "START_BROADCAST"
54 | sendEvent(withName: "onScreenShare", body: status)
55 | status="STARTED_BROADCASTING"
56 | }
57 |
58 |
59 | @objc func stopBroadcastCallback(notification: NSNotification){
60 | status = "STOP_BROADCAST"
61 | sendEvent(withName: "onScreenShare", body: status)
62 | status = "Empty"
63 | }
64 |
65 |
66 | @objc
67 | func startBroadcast() {
68 | DispatchQueue.main.async { [self] in
69 | let pickerView = RPSystemBroadcastPickerView(
70 | frame: CGRect(x: 0, y: 0, width: 0, height: 0))
71 | var tap = pickerView.subviews.first as! UIButton
72 | pickerView.translatesAutoresizingMaskIntoConstraints = false
73 | pickerView.preferredExtension = "org.reactjs.native.example.RNCodeSample"
74 | tap.sendActions(for: .touchUpInside)
75 |
76 | }
77 | }
78 |
79 | override func supportedEvents() -> [String]! {
80 | return ["onScreenShare"]
81 | }
82 | override func constantsToExport() -> [AnyHashable : Any]! {
83 | return ["initialCount": status]
84 | }
85 | override static func requiresMainQueueSetup() -> Bool {
86 | return true
87 | }
88 |
89 | }
90 |
91 |
--------------------------------------------------------------------------------
/client/ios/link-assets-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "migIndex": 1,
3 | "data": [
4 | {
5 | "path": "src/assets/fonts/Roboto/Roboto-Black.ttf",
6 | "sha1": "ee52f7cf8e54f3ee2afbc474a352d5c19514d9c1"
7 | },
8 | {
9 | "path": "src/assets/fonts/Roboto/Roboto-BlackItalic.ttf",
10 | "sha1": "94ba47a5510d3e21ae4db0da0425b855686de586"
11 | },
12 | {
13 | "path": "src/assets/fonts/Roboto/Roboto-Bold.ttf",
14 | "sha1": "3dd713113ff2d79b94d2df343e2e28fa8e7279cf"
15 | },
16 | {
17 | "path": "src/assets/fonts/Roboto/Roboto-BoldItalic.ttf",
18 | "sha1": "60b9860b7fc93d6b0b322f170634105bc6a8cc8d"
19 | },
20 | {
21 | "path": "src/assets/fonts/Roboto/Roboto-Italic.ttf",
22 | "sha1": "dc6756127707ab2d6e388a6023087351fa41999c"
23 | },
24 | {
25 | "path": "src/assets/fonts/Roboto/Roboto-Light.ttf",
26 | "sha1": "92cc3b6f9440193c12fd02ed690e434d685a9cc8"
27 | },
28 | {
29 | "path": "src/assets/fonts/Roboto/Roboto-LightItalic.ttf",
30 | "sha1": "a0440f60a96a59c4105c3eb639cb2573826f84fe"
31 | },
32 | {
33 | "path": "src/assets/fonts/Roboto/Roboto-Medium.ttf",
34 | "sha1": "f6783010d5def128c4a1539333324f75701d9bab"
35 | },
36 | {
37 | "path": "src/assets/fonts/Roboto/Roboto-MediumItalic.ttf",
38 | "sha1": "e4e31e55d279a9b12c32327a60a3a65c8350e5df"
39 | },
40 | {
41 | "path": "src/assets/fonts/Roboto/Roboto-Regular.ttf",
42 | "sha1": "096c9245b6a192d1403a82848e104a65f578a8ec"
43 | },
44 | {
45 | "path": "src/assets/fonts/Roboto/Roboto-Thin.ttf",
46 | "sha1": "711e666e7f52210ac487c7ed81a0ac68a2b52261"
47 | },
48 | {
49 | "path": "src/assets/fonts/Roboto/Roboto-ThinItalic.ttf",
50 | "sha1": "75ffde032d49005faa643338eda97c311ad7d316"
51 | }
52 | ]
53 | }
54 |
--------------------------------------------------------------------------------
/client/metro.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Metro configuration for React Native
3 | * https://github.com/facebook/react-native
4 | *
5 | * @format
6 | */
7 |
8 | module.exports = {
9 | transformer: {
10 | getTransformOptions: async () => ({
11 | transform: {
12 | experimentalImportSupport: false,
13 | inlineRequires: true,
14 | },
15 | }),
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "videosdk-rtc-react-native-call-trigger-example",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "android": "react-native run-android",
7 | "ios": "react-native run-ios",
8 | "start": "react-native start",
9 | "test": "jest",
10 | "lint": "eslint .",
11 | "watch-need-help": "echo 256 | sudo tee -a /proc/sys/fs/inotify/max_user_instances && echo 32768 | sudo tee -a /proc/sys/fs/inotify/max_queued_events && echo 65536 | sudo tee -a /proc/sys/fs/inotify/max_user_watches && watchman shutdown-server",
12 | "android-linux": "react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res && react-native run-android"
13 | },
14 | "dependencies": {
15 | "@react-native-firebase/app": "^16.4.6",
16 | "@react-native-firebase/firestore": "^16.4.6",
17 | "@react-native-firebase/messaging": "^16.4.6",
18 | "@react-navigation/native": "^6.0.13",
19 | "@react-navigation/stack": "^6.3.2",
20 | "@videosdk.live/react-native-incallmanager": "^0.0.9",
21 | "@videosdk.live/react-native-sdk": "^0.0.40",
22 | "firebase": "^9.15.0",
23 | "lottie-ios": "3.4.0",
24 | "lottie-react-native": "^5.1.4",
25 | "moment": "^2.29.4",
26 | "react": "18.1.0",
27 | "react-native": "0.70.6",
28 | "react-native-callkeep": "git+https://github.com/react-native-webrtc/react-native-callkeep#4b1fa98a685f6502d151875138b7c81baf1ec680",
29 | "react-native-dotenv": "^3.1.1",
30 | "react-native-gesture-handler": "^2.7.0",
31 | "react-native-responsive-fontsize": "^0.5.1",
32 | "react-native-safe-area-context": "^4.4.1",
33 | "react-native-screens": "^3.18.0",
34 | "react-native-simple-toast": "^1.1.4",
35 | "react-native-svg": "^12.1.1",
36 | "react-native-uuid": "^2.0.1",
37 | "react-native-voip-push-notification": "^3.3.1",
38 | "videosdk-rn-android-overlay-permission": "1.0.6"
39 | },
40 | "devDependencies": {
41 | "@babel/core": "^7.14.6",
42 | "@babel/runtime": "^7.14.6",
43 | "@react-native-community/eslint-config": "^3.0.0",
44 | "babel-jest": "^27.0.6",
45 | "eslint": "^7.30.0",
46 | "jest": "^27.0.6",
47 | "metro-react-native-babel-preset": "^0.66.1",
48 | "react-test-renderer": "17.0.1"
49 | },
50 | "jest": {
51 | "preset": "react-native"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/client/react-native.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | assets: ["./src/assets/fonts/Roboto"],
3 | };
4 |
--------------------------------------------------------------------------------
/client/src/api/api.js:
--------------------------------------------------------------------------------
1 | const API_BASE_URL = "https://api.videosdk.live/v2";
2 | const VIDEOSDK_TOKEN = process.env.REACT_APP_VIDEOSDK_TOKEN;
3 |
4 | const FCM_SERVER_URL = "LOCAL_SERVER_URL:9000";
5 |
6 | export const getToken = () => {
7 | return VIDEOSDK_TOKEN;
8 | };
9 |
10 | export const createMeeting = async ({ token }) => {
11 | const url = `${API_BASE_URL}/rooms`;
12 | const options = {
13 | method: "POST",
14 | headers: { Authorization: token, "Content-Type": "application/json" },
15 | };
16 |
17 | const { roomId } = await fetch(url, options)
18 | .then((response) => response.json())
19 | .catch((error) => console.error("error", error));
20 |
21 | return roomId;
22 | };
23 |
24 | export const initiateCall = async ({
25 | callerInfo,
26 | calleeInfo,
27 | videoSDKInfo,
28 | }) => {
29 | await fetch(`${FCM_SERVER_URL}/initiate-call`, {
30 | method: "POST",
31 | headers: { "Content-Type": "application/json" },
32 | body: JSON.stringify({
33 | callerInfo,
34 | calleeInfo,
35 | videoSDKInfo,
36 | }),
37 | })
38 | .then((response) => {
39 | console.log(" RESP", response);
40 | })
41 | .catch((error) => console.error("error", error));
42 | };
43 |
44 | export const updateCallStatus = async ({ callerInfo, type }) => {
45 | await fetch(`${FCM_SERVER_URL}/update-call`, {
46 | method: "POST",
47 | headers: { "Content-Type": "application/json" },
48 | body: JSON.stringify({
49 | callerInfo,
50 | type,
51 | }),
52 | })
53 | .then((response) => {
54 | console.log("##RESP", response);
55 | })
56 | .catch((error) => console.error("error", error));
57 | };
58 |
--------------------------------------------------------------------------------
/client/src/assets/animation/joining_lottie.json:
--------------------------------------------------------------------------------
1 | {"v":"5.7.8","fr":25,"ip":0,"op":28,"w":1000,"h":414,"nm":"loading_1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"ball_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":12,"s":[754.951,204.902,0],"to":[0,-13.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":18,"s":[754.951,122.902,0],"to":[0,0,0],"ti":[0,-13.667,0]},{"t":24,"s":[754.951,204.902,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[127.049,127.049],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.333333333333,0.407843137255,0.996078431373,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":101,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"ball_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":6,"s":[497.451,204.902,0],"to":[0,-13.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":12,"s":[497.451,122.902,0],"to":[0,0,0],"ti":[0,-13.667,0]},{"t":18,"s":[497.451,204.902,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[127.049,127.049],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.333333333333,0.407843137255,0.996078431373,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":101,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"ball_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[239.951,204.902,0],"to":[0,-13.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":6,"s":[239.951,122.902,0],"to":[0,0,0],"ti":[0,-13.667,0]},{"t":12,"s":[239.951,204.902,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[127.049,127.049],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.333333333333,0.407843137255,0.996078431373,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":101,"st":0,"bm":0}],"markers":[]}
--------------------------------------------------------------------------------
/client/src/assets/fonts/Roboto/Roboto-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/src/assets/fonts/Roboto/Roboto-Black.ttf
--------------------------------------------------------------------------------
/client/src/assets/fonts/Roboto/Roboto-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/src/assets/fonts/Roboto/Roboto-BlackItalic.ttf
--------------------------------------------------------------------------------
/client/src/assets/fonts/Roboto/Roboto-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/src/assets/fonts/Roboto/Roboto-Bold.ttf
--------------------------------------------------------------------------------
/client/src/assets/fonts/Roboto/Roboto-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/src/assets/fonts/Roboto/Roboto-BoldItalic.ttf
--------------------------------------------------------------------------------
/client/src/assets/fonts/Roboto/Roboto-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/src/assets/fonts/Roboto/Roboto-Italic.ttf
--------------------------------------------------------------------------------
/client/src/assets/fonts/Roboto/Roboto-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/src/assets/fonts/Roboto/Roboto-Light.ttf
--------------------------------------------------------------------------------
/client/src/assets/fonts/Roboto/Roboto-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/src/assets/fonts/Roboto/Roboto-LightItalic.ttf
--------------------------------------------------------------------------------
/client/src/assets/fonts/Roboto/Roboto-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/src/assets/fonts/Roboto/Roboto-Medium.ttf
--------------------------------------------------------------------------------
/client/src/assets/fonts/Roboto/Roboto-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/src/assets/fonts/Roboto/Roboto-MediumItalic.ttf
--------------------------------------------------------------------------------
/client/src/assets/fonts/Roboto/Roboto-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/src/assets/fonts/Roboto/Roboto-Regular.ttf
--------------------------------------------------------------------------------
/client/src/assets/fonts/Roboto/Roboto-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/src/assets/fonts/Roboto/Roboto-Thin.ttf
--------------------------------------------------------------------------------
/client/src/assets/fonts/Roboto/Roboto-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/client/src/assets/fonts/Roboto/Roboto-ThinItalic.ttf
--------------------------------------------------------------------------------
/client/src/assets/icons/CallEnd.js:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import Svg, { Path } from "react-native-svg"
3 |
4 | function SvgComponent(props) {
5 | return (
6 |
12 | )
13 | }
14 |
15 | export default SvgComponent
16 |
--------------------------------------------------------------------------------
/client/src/assets/icons/CameraSwitch.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import Svg, { Defs, Path } from "react-native-svg";
3 |
4 | function CameraSwitch(props) {
5 | return (
6 |
19 | );
20 | }
21 |
22 | export default CameraSwitch;
23 |
--------------------------------------------------------------------------------
/client/src/assets/icons/Copy.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import Svg, { Path } from "react-native-svg";
3 |
4 | function Copy(props) {
5 | return (
6 |
13 | );
14 | }
15 |
16 | export default Copy;
17 |
--------------------------------------------------------------------------------
/client/src/assets/icons/Leave.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import Svg, { G, Path, Defs, ClipPath } from "react-native-svg";
3 |
4 | function Leave(props) {
5 | return (
6 |
24 | );
25 | }
26 |
27 | export default Leave;
28 |
--------------------------------------------------------------------------------
/client/src/assets/icons/MicOff.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Svg, {Path} from 'react-native-svg';
3 |
4 | function MicOff(props) {
5 | return (
6 |
13 | );
14 | }
15 |
16 | export default MicOff;
17 |
--------------------------------------------------------------------------------
/client/src/assets/icons/MicOn.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Svg, {Defs, ClipPath, Path, G} from 'react-native-svg';
3 |
4 | function MicOn(props) {
5 | return (
6 |
29 | );
30 | }
31 |
32 | export default MicOn;
33 |
--------------------------------------------------------------------------------
/client/src/assets/icons/VideoOff.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import Svg, { Defs, ClipPath, Path, G } from "react-native-svg";
3 |
4 | function VideoOff(props) {
5 | return (
6 |
21 | );
22 | }
23 |
24 | export default VideoOff;
25 |
--------------------------------------------------------------------------------
/client/src/assets/icons/VideoOn.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import Svg, { Path } from "react-native-svg";
3 |
4 | function VideoOn(props) {
5 | return (
6 |
12 | );
13 | }
14 |
15 | export default VideoOn;
16 |
--------------------------------------------------------------------------------
/client/src/assets/icons/index.js:
--------------------------------------------------------------------------------
1 | import MicOn from "./MicOn";
2 | import MicOff from "./MicOff";
3 |
4 | import VideoOn from "./VideoOn";
5 | import VideoOff from "./VideoOff";
6 |
7 | import CallEnd from "./CallEnd";
8 | import Leave from "./Leave";
9 | import CameraSwitch from "./CameraSwitch";
10 | import Copy from "./Copy";
11 |
12 | export { MicOff, MicOn, VideoOff, VideoOn, CallEnd, CameraSwitch, Copy, Leave };
13 |
--------------------------------------------------------------------------------
/client/src/components/Avatar/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-native/no-inline-styles */
2 | import React from "react";
3 | import { View, Text } from "react-native";
4 | import colors from "../../styles/colors";
5 | import { convertRFValue } from "../../styles/spacing";
6 |
7 | export default function Avatar({
8 | fullName,
9 | style,
10 | fontSize,
11 | containerBackgroundColor,
12 | }) {
13 | return (
14 |
24 |
32 |
38 | {fullName && fullName.charAt(0).toUpperCase()}
39 |
40 |
41 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/client/src/components/Button/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { TouchableOpacity, Text } from "react-native";
3 | import colors from "../../styles/colors";
4 | import { ROBOTO_FONTS } from "../../styles/fonts";
5 |
6 | const Button = ({
7 | text,
8 | backgroundColor,
9 | onPress,
10 | style = {},
11 | textStyle = {},
12 | }) => {
13 | return (
14 |
26 |
34 | {text}
35 |
36 |
37 | );
38 | };
39 | export default Button;
40 |
--------------------------------------------------------------------------------
/client/src/components/IconContainer/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { TouchableOpacity, View } from "react-native";
3 |
4 | const buttonStyle = {
5 | height: 50,
6 | aspectRatio: 1,
7 | justifyContent: "center",
8 | alignItems: "center",
9 | };
10 | const IconContainer = ({ backgroundColor, onPress, Icon, style }) => {
11 | return (
12 |
24 |
25 |
26 | );
27 | };
28 | export default IconContainer;
29 |
--------------------------------------------------------------------------------
/client/src/components/TextInputContainer/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { View, TextInput } from "react-native";
3 | import colors from "../../styles/colors";
4 | import { ROBOTO_FONTS } from "../../styles/fonts";
5 | const TextInputContainer = ({ placeholder, value, setValue, keyboardType }) => {
6 | return (
7 |
17 | {
33 | setValue(text);
34 | }}
35 | value={value}
36 | keyboardType={keyboardType}
37 | />
38 |
39 | );
40 | };
41 |
42 | export default TextInputContainer;
43 |
--------------------------------------------------------------------------------
/client/src/navigators/screenNames.js:
--------------------------------------------------------------------------------
1 | export const SCREEN_NAMES = {
2 | Home: "homescreen",
3 | Meeting: "meetingscreen",
4 | };
5 |
--------------------------------------------------------------------------------
/client/src/scenes/home/index.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState, useRef } from "react";
2 | import {
3 | Platform,
4 | KeyboardAvoidingView,
5 | TouchableWithoutFeedback,
6 | Keyboard,
7 | View,
8 | Text,
9 | Clipboard,
10 | Alert,
11 | Linking,
12 | } from "react-native";
13 | import { TouchableOpacity } from "react-native-gesture-handler";
14 | import { CallEnd, Copy } from "../../assets/icons";
15 | import TextInputContainer from "../../components/TextInputContainer";
16 | import colors from "../../styles/colors";
17 | import { ROBOTO_FONTS } from "../../styles/fonts";
18 | import firestore from "@react-native-firebase/firestore";
19 | import messaging from "@react-native-firebase/messaging";
20 | import Toast from "react-native-simple-toast";
21 | import {
22 | updateCallStatus,
23 | initiateCall,
24 | getToken,
25 | createMeeting,
26 | } from "../../api/api";
27 | import { SCREEN_NAMES } from "../../navigators/screenNames";
28 | import Incomingvideocall from "../../utils/incoming-video-call";
29 | import VoipPushNotification from "react-native-voip-push-notification";
30 |
31 | export default function Home({ navigation }) {
32 | const [number, setNumber] = useState("");
33 | const [firebaseUserConfig, setfirebaseUserConfig] = useState(null);
34 | const [isCalling, setisCalling] = useState(false);
35 | const [videosdkToken, setVideosdkToken] = useState(null);
36 | const [videosdkMeeting, setVideosdkMeeting] = useState(null);
37 | const [APN, setAPN] = useState(null);
38 |
39 | const videosdkTokenRef = useRef();
40 | const videosdkMeetingRef = useRef();
41 | const APNRef = useRef();
42 | videosdkTokenRef.current = videosdkToken;
43 | videosdkMeetingRef.current = videosdkMeeting;
44 | APNRef.current = APN;
45 |
46 | useEffect(() => {
47 | async function getFCMtoken() {
48 | const authStatus = await messaging().requestPermission();
49 | const enabled =
50 | authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
51 | authStatus === messaging.AuthorizationStatus.PROVISIONAL;
52 | Platform.OS === "ios" && VoipPushNotification.registerVoipToken();
53 |
54 | if (enabled) {
55 | const token = await messaging().getToken();
56 | const querySnapshot = await firestore()
57 | .collection("users")
58 | .where("token", "==", token)
59 | .get();
60 |
61 | const uids = querySnapshot.docs.map((doc) => {
62 | if (doc && doc?.data()?.callerId) {
63 | const { token, platform, APN, callerId } = doc?.data();
64 | setfirebaseUserConfig({
65 | callerId,
66 | token,
67 | platform,
68 | APN,
69 | });
70 | }
71 | return doc;
72 | });
73 |
74 | if (uids && uids.length == 0) {
75 | addUser({ token });
76 | } else {
77 | console.log("Token Found");
78 | }
79 | }
80 | }
81 |
82 | async function getTokenAndMeetingId() {
83 | const videoSDKtoken = getToken();
84 | const videoSDKMeetingId = await createMeeting({ token: videoSDKtoken });
85 | setVideosdkToken(videoSDKtoken);
86 | setVideosdkMeeting(videoSDKMeetingId);
87 | }
88 | getFCMtoken();
89 | getTokenAndMeetingId();
90 | }, []);
91 |
92 | useEffect(() => {
93 | const unsubscribe = messaging().onMessage((remoteMessage) => {
94 | const { callerInfo, videoSDKInfo, type } = JSON.parse(
95 | remoteMessage.data.info
96 | );
97 | switch (type) {
98 | case "CALL_INITIATED":
99 | const incomingCallAnswer = ({ callUUID }) => {
100 | updateCallStatus({
101 | callerInfo,
102 | type: "ACCEPTED",
103 | });
104 | Incomingvideocall.endIncomingcallAnswer(callUUID);
105 | setisCalling(false);
106 | Linking.openURL(
107 | `videocalling://meetingscreen/${videoSDKInfo.token}/${videoSDKInfo.meetingId}`
108 | ).catch((err) => {
109 | Toast.show(`Error`, err);
110 | });
111 | };
112 |
113 | const endIncomingCall = () => {
114 | Incomingvideocall.endIncomingcallAnswer();
115 | updateCallStatus({ callerInfo, type: "REJECTED" });
116 | };
117 |
118 | Incomingvideocall.configure(incomingCallAnswer, endIncomingCall);
119 | Incomingvideocall.displayIncomingCall(callerInfo.name);
120 |
121 | break;
122 | case "ACCEPTED":
123 | setisCalling(false);
124 | navigation.navigate(SCREEN_NAMES.Meeting, {
125 | name: "Person B",
126 | token: videosdkTokenRef.current,
127 | meetingId: videosdkMeetingRef.current,
128 | });
129 | break;
130 | case "REJECTED":
131 | Toast.show("Call Rejected");
132 | setisCalling(false);
133 | break;
134 | case "DISCONNECT":
135 | Platform.OS === "ios"
136 | ? Incomingvideocall.endAllCall()
137 | : Incomingvideocall.endIncomingcallAnswer();
138 | break;
139 | default:
140 | Toast.show("Call Could not placed");
141 | }
142 | });
143 |
144 | return () => {
145 | unsubscribe();
146 | };
147 | }, []);
148 |
149 | useEffect(() => {
150 | VoipPushNotification.addEventListener("register", (token) => {
151 | setAPN(token);
152 | });
153 |
154 | VoipPushNotification.addEventListener("notification", (notification) => {
155 | const { callerInfo, videoSDKInfo, type } = notification;
156 | if (type === "CALL_INITIATED") {
157 | const incomingCallAnswer = ({ callUUID }) => {
158 | updateCallStatus({
159 | callerInfo,
160 | type: "ACCEPTED",
161 | });
162 | navigation.navigate(SCREEN_NAMES.Meeting, {
163 | name: "Person B",
164 | token: videoSDKInfo.token,
165 | meetingId: videoSDKInfo.meetingId,
166 | });
167 | };
168 | const endIncomingCall = () => {
169 | Incomingvideocall.endAllCall();
170 | updateCallStatus({ callerInfo, type: "REJECTED" });
171 | };
172 | Incomingvideocall.configure(incomingCallAnswer, endIncomingCall);
173 | } else if (type === "DISCONNECT") {
174 | Incomingvideocall.endAllCall();
175 | }
176 | VoipPushNotification.onVoipNotificationCompleted(notification.uuid);
177 | });
178 |
179 | VoipPushNotification.addEventListener("didLoadWithEvents", (events) => {
180 | const { callerInfo, videoSDKInfo, type } =
181 | events.length > 1 && events[1].data;
182 | if (type === "CALL_INITIATED") {
183 | const incomingCallAnswer = ({ callUUID }) => {
184 | updateCallStatus({
185 | callerInfo,
186 | type: "ACCEPTED",
187 | });
188 | navigation.navigate(SCREEN_NAMES.Meeting, {
189 | name: "Person B",
190 | token: videoSDKInfo.token,
191 | meetingId: videoSDKInfo.meetingId,
192 | });
193 | };
194 |
195 | const endIncomingCall = () => {
196 | Incomingvideocall.endAllCall();
197 | updateCallStatus({ callerInfo, type: "REJECTED" });
198 | };
199 |
200 | Incomingvideocall.configure(incomingCallAnswer, endIncomingCall);
201 | }
202 | });
203 |
204 | return () => {
205 | VoipPushNotification.removeEventListener("didLoadWithEvents");
206 | VoipPushNotification.removeEventListener("register");
207 | VoipPushNotification.removeEventListener("notification");
208 | };
209 | }, []);
210 |
211 | const addUser = ({ token }) => {
212 | const platform = Platform.OS === "android" ? "ANDROID" : "iOS";
213 | const obj = {
214 | callerId: Math.floor(10000000 + Math.random() * 90000000).toString(),
215 | token,
216 | platform,
217 | };
218 | if (platform == "iOS") {
219 | obj.APN = APNRef.current;
220 | }
221 | firestore()
222 | .collection("users")
223 | .add(obj)
224 | .then(() => {
225 | setfirebaseUserConfig(obj);
226 | console.log("User added!");
227 | });
228 | };
229 |
230 | const getCallee = async (num) => {
231 | const querySnapshot = await firestore()
232 | .collection("users")
233 | .where("callerId", "==", num.toString())
234 | .get();
235 | return querySnapshot.docs.map((doc) => {
236 | return doc;
237 | });
238 | };
239 | return (
240 |
249 | {!isCalling ? (
250 |
251 | <>
252 |
261 |
268 | Your Caller ID
269 |
270 |
277 |
285 | {firebaseUserConfig
286 | ? firebaseUserConfig.callerId
287 | : "Loading.."}
288 |
289 | {
300 | Clipboard.setString(
301 | firebaseUserConfig && firebaseUserConfig.callerId
302 | );
303 | if (Platform.OS === "android") {
304 | Toast.show("Copied");
305 | Alert.alert(
306 | "Information",
307 | "This callerId will be unavailable, once you uninstall the App."
308 | );
309 | }
310 | }}
311 | >
312 |
313 |
314 |
315 |
316 |
317 |
326 |
333 | Enter call id of another user
334 |
335 |
341 | {
343 | if (number) {
344 | const data = await getCallee(number);
345 | if (data) {
346 | if (data.length === 0) {
347 | Toast.show("CallerId Does not Match");
348 | } else {
349 | Toast.show("CallerId Match!");
350 | const { token, platform, APN } = data[0]?.data();
351 | initiateCall({
352 | callerInfo: {
353 | name: "Person A",
354 | ...firebaseUserConfig,
355 | },
356 | calleeInfo: {
357 | token,
358 | platform,
359 | APN,
360 | },
361 | videoSDKInfo: {
362 | token: videosdkTokenRef.current,
363 | meetingId: videosdkMeetingRef.current,
364 | },
365 | });
366 | setisCalling(true);
367 | }
368 | }
369 | } else {
370 | Toast.show("Please provide CallerId");
371 | }
372 | }}
373 | style={{
374 | height: 50,
375 | backgroundColor: "#5568FE",
376 | justifyContent: "center",
377 | alignItems: "center",
378 | borderRadius: 12,
379 | marginTop: 16,
380 | }}
381 | >
382 |
388 | Call Now
389 |
390 |
391 |
392 | >
393 |
394 | ) : (
395 |
396 |
404 |
411 | Calling to...
412 |
413 |
414 |
423 | {number}
424 |
425 |
426 |
432 | {
434 | const data = await getCallee(number);
435 | if (data) {
436 | updateCallStatus({
437 | callerInfo: data[0]?.data(),
438 | type: "DISCONNECT",
439 | });
440 | setisCalling(false);
441 | }
442 | }}
443 | style={{
444 | backgroundColor: "#FF5D5D",
445 | borderRadius: 30,
446 | height: 60,
447 | aspectRatio: 1,
448 | justifyContent: "center",
449 | alignItems: "center",
450 | }}
451 | >
452 |
453 |
454 |
455 |
456 | )}
457 |
458 | );
459 | }
460 |
--------------------------------------------------------------------------------
/client/src/scenes/meeting/Components/WaitingToJoinView.js:
--------------------------------------------------------------------------------
1 | import { ROBOTO_FONTS } from "../../../styles/fonts";
2 | import { convertRFValue } from "../../../styles/spacing";
3 | import React from "react";
4 | import { Text, View } from "react-native";
5 | import colors from "../../../styles/colors";
6 | import Lottie from "lottie-react-native";
7 | import joining_animation from "../../../assets/animation/joining_lottie.json";
8 | export default function WaitingToJoinView() {
9 | return (
10 |
19 |
28 |
36 | Creating a room
37 |
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/client/src/scenes/meeting/MeetingContainer.js:
--------------------------------------------------------------------------------
1 | import {
2 | useMeeting,
3 | ReactNativeForegroundService,
4 | } from "@videosdk.live/react-native-sdk";
5 | import { useEffect, useState } from "react";
6 | import OneToOneMeetingViewer from "./OneToOne";
7 | import ParticipantLimitViewer from "./OneToOne/ParticipantLimitViewer";
8 | import WaitingToJoinView from "./Components/WaitingToJoinView";
9 | import React from "react";
10 |
11 | export default function MeetingContainer({ webcamEnabled }) {
12 | const [isJoined, setJoined] = useState(false);
13 | const [participantLimit, setParticipantLimit] = useState(false);
14 |
15 | const { join, changeWebcam, participants, leave } = useMeeting({
16 | onMeetingJoined: () => {
17 | setTimeout(() => {
18 | setJoined(true);
19 | }, 500);
20 | },
21 | onParticipantLeft: () => {
22 | if (participants.size < 2) {
23 | setParticipantLimit(false);
24 | }
25 | },
26 | });
27 |
28 | useEffect(() => {
29 | if (isJoined) {
30 | if (participants.size > 2) {
31 | setParticipantLimit(true);
32 | }
33 | }
34 | }, [isJoined]);
35 |
36 | useEffect(() => {
37 | setTimeout(() => {
38 | if (!isJoined) {
39 | join();
40 | if (webcamEnabled) changeWebcam();
41 | }
42 | }, 1000);
43 |
44 | return () => {
45 | leave();
46 | ReactNativeForegroundService.stopAll();
47 | };
48 | }, []);
49 |
50 | return isJoined ? (
51 | participantLimit ? (
52 |
53 | ) : (
54 |
55 | )
56 | ) : (
57 |
58 | );
59 | }
60 |
--------------------------------------------------------------------------------
/client/src/scenes/meeting/OneToOne/LargeView/LargeVideoRTCView.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { RTCView, MediaStream } from "@videosdk.live/react-native-sdk";
3 | import Avatar from "../../../../components/Avatar";
4 | import colors from "../../../../styles/colors";
5 |
6 | export default LargeVideoRTCView = ({
7 | stream,
8 | displayName,
9 | isOn,
10 | objectFit,
11 | isLocal = { isLocal },
12 | }) => {
13 | return isOn && stream ? (
14 |
20 | ) : (
21 |
32 | );
33 | };
34 |
--------------------------------------------------------------------------------
/client/src/scenes/meeting/OneToOne/LargeView/index.js:
--------------------------------------------------------------------------------
1 | import { useParticipant } from "@videosdk.live/react-native-sdk";
2 | import React, { useEffect } from "react";
3 | import { View } from "react-native";
4 | import colors from "../../../../styles/colors";
5 | import LargeVideoRTCView from "./LargeVideoRTCView";
6 |
7 | export default LargeViewContainer = ({ participantId }) => {
8 | const { webcamOn, webcamStream, displayName, setQuality, isLocal } =
9 | useParticipant(participantId, {});
10 |
11 | useEffect(() => {
12 | setQuality("high");
13 | }, []);
14 |
15 | return (
16 |
24 |
31 |
32 | );
33 | };
34 |
--------------------------------------------------------------------------------
/client/src/scenes/meeting/OneToOne/LocalViewContainer.js:
--------------------------------------------------------------------------------
1 | import { useParticipant } from "@videosdk.live/react-native-sdk";
2 | import React, { useEffect } from "react";
3 | import { View } from "react-native";
4 | import colors from "../../../styles/colors";
5 | import LargeVideoRTCView from "./LargeView/LargeVideoRTCView";
6 |
7 | export default function LocalViewContainer({ participantId }) {
8 | const { webcamOn, webcamStream, displayName, setQuality, isLocal } =
9 | useParticipant(participantId, {});
10 |
11 | useEffect(() => {
12 | setQuality("high");
13 | }, []);
14 |
15 | return (
16 |
24 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/client/src/scenes/meeting/OneToOne/MiniView/MiniVideoRTCView.js:
--------------------------------------------------------------------------------
1 | import { RTCView, MediaStream } from "@videosdk.live/react-native-sdk";
2 | import React from "react";
3 | import { View } from "react-native";
4 | import Avatar from "../../../../components/Avatar";
5 | import colors from "../../../../styles/colors";
6 |
7 | export default MiniVideoRTCView = ({ stream, isOn, displayName, isLocal }) => {
8 | return (
9 |
21 | {isOn && stream ? (
22 |
29 | ) : (
30 |
41 | )}
42 |
43 | );
44 | };
45 |
--------------------------------------------------------------------------------
/client/src/scenes/meeting/OneToOne/MiniView/index.js:
--------------------------------------------------------------------------------
1 | import { useParticipant } from "@videosdk.live/react-native-sdk";
2 | import React, { useEffect } from "react";
3 | import MiniVideoRTCView from "./MiniVideoRTCView";
4 |
5 | export default MiniViewContainer = ({ participantId }) => {
6 | const { webcamOn, webcamStream, displayName, setQuality, isLocal } =
7 | useParticipant(participantId, {});
8 |
9 | useEffect(() => {
10 | setQuality("high");
11 | }, []);
12 |
13 | return (
14 |
20 | );
21 | };
22 |
--------------------------------------------------------------------------------
/client/src/scenes/meeting/OneToOne/ParticipantLimitViewer.js:
--------------------------------------------------------------------------------
1 | import { ROBOTO_FONTS } from "../../../styles/fonts";
2 | import React from "react";
3 | import { convertRFValue } from "../../../styles/spacing";
4 | import { Text, View } from "react-native";
5 | import colors from "../../../styles/colors";
6 | import Button from "../../../components/Button";
7 | import { useMeeting } from "@videosdk.live/react-native-sdk";
8 |
9 | export default function ParticipantLimitViewer() {
10 | const { leave } = useMeeting({});
11 | return (
12 |
21 |
28 | OOPS !!
29 |
30 |
39 | Maximun 2 participants can join this meeting.
40 |
41 |
49 | Please try again later
50 |
51 |
52 |
68 | );
69 | }
70 |
--------------------------------------------------------------------------------
/client/src/scenes/meeting/OneToOne/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | View,
4 | Text,
5 | Clipboard,
6 | TouchableOpacity,
7 | ActivityIndicator,
8 | } from "react-native";
9 | import { useMeeting } from "@videosdk.live/react-native-sdk";
10 | import {
11 | CallEnd,
12 | CameraSwitch,
13 | Copy,
14 | MicOff,
15 | MicOn,
16 | VideoOff,
17 | VideoOn,
18 | } from "../../../assets/icons";
19 | import colors from "../../../styles/colors";
20 | import IconContainer from "../../../components/IconContainer";
21 | import LocalViewContainer from "./LocalViewContainer";
22 | import LargeView from "./LargeView";
23 | import MiniView from "./MiniView";
24 | import { ROBOTO_FONTS } from "../../../styles/fonts";
25 | import Toast from "react-native-simple-toast";
26 |
27 | export default function OneToOneMeetingViewer() {
28 | const {
29 | participants,
30 | localWebcamOn,
31 | localMicOn,
32 | leave,
33 | changeWebcam,
34 | toggleWebcam,
35 | toggleMic,
36 | meetingId,
37 | } = useMeeting({
38 | onError: (data) => {
39 | const { code, message } = data;
40 | Toast.show(`Error: ${code}: ${message}`);
41 | },
42 | });
43 |
44 | const participantIds = [...participants.keys()];
45 |
46 | const participantCount = participantIds ? participantIds.length : null;
47 |
48 | return (
49 | <>
50 |
57 |
63 |
64 |
71 | {meetingId ? meetingId : "xxx - xxx - xxx"}
72 |
73 |
74 | {
80 | Clipboard.setString(meetingId);
81 | Toast.show("Meeting Id copied Successfully");
82 | }}
83 | >
84 |
85 |
86 |
87 |
88 |
89 | {
91 | changeWebcam();
92 | }}
93 | >
94 |
95 |
96 |
97 |
98 | {/* Center */}
99 |
100 | {participantCount > 1 ? (
101 | <>
102 |
103 |
104 | >
105 | ) : participantCount === 1 ? (
106 |
107 | ) : (
108 |
111 |
112 |
113 | )}
114 |
115 | {/* Bottom */}
116 |
122 | {
125 | leave();
126 | }}
127 | Icon={() => {
128 | return ;
129 | }}
130 | />
131 | {
138 | toggleMic();
139 | }}
140 | Icon={() => {
141 | return localMicOn ? (
142 |
143 | ) : (
144 |
145 | );
146 | }}
147 | />
148 | {
155 | toggleWebcam();
156 | }}
157 | Icon={() => {
158 | return localWebcamOn ? (
159 |
160 | ) : (
161 |
162 | );
163 | }}
164 | />
165 |
166 | >
167 | );
168 | }
169 |
--------------------------------------------------------------------------------
/client/src/scenes/meeting/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Platform, SafeAreaView } from "react-native";
3 | import colors from "../../styles/colors";
4 | import {
5 | MeetingConsumer,
6 | MeetingProvider,
7 | } from "@videosdk.live/react-native-sdk";
8 | import MeetingContainer from "./MeetingContainer";
9 | import { SCREEN_NAMES } from "../../navigators/screenNames";
10 | import IncomingVideoCall from "../../utils/incoming-video-call";
11 |
12 | export default function ({ navigation, route }) {
13 | const token = route.params.token;
14 | const meetingId = route.params.meetingId;
15 | const micEnabled = route.params.micEnabled ? route.params.micEnabled : true;
16 | const webcamEnabled = route.params.webcamEnabled
17 | ? route.params.webcamEnabled
18 | : true;
19 | const name = route.params.name;
20 |
21 | return (
22 |
25 |
38 | {
41 | Platform.OS == "ios" && IncomingVideoCall.endAllCall();
42 | navigation.navigate(SCREEN_NAMES.Home);
43 | },
44 | }}
45 | >
46 | {() => {
47 | return ;
48 | }}
49 |
50 |
51 |
52 | );
53 | }
54 |
--------------------------------------------------------------------------------
/client/src/styles/colors.js:
--------------------------------------------------------------------------------
1 | const colors = {
2 | primary: {
3 | 100: "#FFFFFF",
4 | 200: "#EFEFEF",
5 | 300: "#DADADA",
6 | 400: "#818181",
7 | 500: "#6F767E",
8 | 600: "#404B53",
9 | 700: "#232830",
10 | 800: "#1A1C22",
11 | 900: "#050A0E",
12 | },
13 | black: "#000",
14 | purple: "#5568FE",
15 | };
16 |
17 | export default colors;
18 |
--------------------------------------------------------------------------------
/client/src/styles/fonts.js:
--------------------------------------------------------------------------------
1 | export const ROBOTO_FONTS = {
2 | RobotoBold: "Roboto-Bold",
3 | RobotoBoldItalic: "Roboto-BoldItalic",
4 | RobotoItalic: "Roboto-Italic",
5 | RobotoLight: "Roboto-Light",
6 | RobotoLightItalic: "Roboto-LightItalic",
7 | RobotoMediumItalic: "Roboto-MediumItalic",
8 | RobotoRegular: "Roboto-Regular",
9 | RobotoThin: "Roboto-Thin",
10 | RobotoThinItalic: "Roboto-ThinItalic",
11 | Roboto: "Roboto",
12 | RobotoMedium: "Roboto-Medium",
13 | RobotoBlack: "Roboto-Black",
14 | RobotoBlackItalic: "Roboto-BlackItalic",
15 | };
16 |
--------------------------------------------------------------------------------
/client/src/styles/spacing.js:
--------------------------------------------------------------------------------
1 | import { RFValue } from "react-native-responsive-fontsize";
2 |
3 | export const STANDERD_DESIGN_HEIGHT_WIDTH = { HEIGHT: 640, WIDTH: 360 };
4 |
5 | export const convertRFValue = (fontSize) =>
6 | RFValue(fontSize, STANDERD_DESIGN_HEIGHT_WIDTH.HEIGHT);
7 |
--------------------------------------------------------------------------------
/client/src/utils/incoming-video-call.js:
--------------------------------------------------------------------------------
1 | import { Platform } from "react-native";
2 | import RNCallKeep from "react-native-callkeep";
3 | import uuid from "react-native-uuid";
4 | import VoipPushNotification from "react-native-voip-push-notification";
5 |
6 | class IncomingCall {
7 | constructor() {
8 | this.currentCallId = null;
9 | }
10 |
11 | configure = (incomingcallAnswer, endIncomingCall) => {
12 | try {
13 | this.setupCallKeep();
14 | Platform.OS === "android" && RNCallKeep.setAvailable(true);
15 | RNCallKeep.addEventListener("answerCall", incomingcallAnswer);
16 | RNCallKeep.addEventListener("endCall", endIncomingCall);
17 | } catch (error) {
18 | console.error("initializeCallKeep error:", error?.message);
19 | }
20 | };
21 |
22 | setupCallKeep = () => {
23 | try {
24 | RNCallKeep.setup({
25 | ios: {
26 | appName: "VideoSDK",
27 | supportsVideo: false,
28 | maximumCallGroups: "1",
29 | maximumCallsPerCallGroup: "1",
30 | },
31 | android: {
32 | alertTitle: "Permissions required",
33 | alertDescription:
34 | "This application needs to access your phone accounts",
35 | cancelButton: "Cancel",
36 | okButton: "Ok",
37 | },
38 | });
39 | } catch (error) {
40 | console.error("initializeCallKeep error:", error?.message);
41 | }
42 | };
43 | // Use startCall to ask the system to start a call - Initiate an outgoing call from this point
44 | startCall = ({ handle, localizedCallerName }) => {
45 | // Your normal start call action
46 | RNCallKeep.startCall(this.getCurrentCallId(), handle, localizedCallerName);
47 | };
48 |
49 | reportEndCallWithUUID = (callUUID, reason) => {
50 | RNCallKeep.reportEndCallWithUUID(callUUID, reason);
51 | };
52 |
53 | endIncomingcallAnswer = () => {
54 | RNCallKeep.endCall(this.currentCallId);
55 | this.currentCallId = null;
56 | this.removeEvents();
57 | };
58 |
59 | removeEvents = () => {
60 | RNCallKeep.removeEventListener("answerCall");
61 | RNCallKeep.removeEventListener("endCall");
62 | };
63 |
64 | displayIncomingCall = (callerName) => {
65 | Platform.OS === "android" && RNCallKeep.setAvailable(false);
66 | RNCallKeep.displayIncomingCall(
67 | this.getCurrentCallId(),
68 | callerName,
69 | callerName,
70 | "number",
71 | true,
72 | null
73 | );
74 | };
75 |
76 | backToForeground = () => {
77 | RNCallKeep.backToForeground();
78 | };
79 |
80 | getCurrentCallId = () => {
81 | if (!this.currentCallId) {
82 | this.currentCallId = uuid.v4();
83 | }
84 | return this.currentCallId;
85 | };
86 |
87 | endAllCall = () => {
88 | RNCallKeep.endAllCalls();
89 | this.currentCallId = null;
90 | this.removeEvents();
91 | };
92 |
93 | setupEventListeners() {
94 | if (Platform.OS == "ios") {
95 | // --- NOTE: You still need to subscribe / handle the rest events as usuall.
96 | // --- This is just a helper whcih cache and propagate early fired events if and only if for
97 | // --- "the native events which DID fire BEFORE js bridge is initialed",
98 | // --- it does NOT mean this will have events each time when the app reopened.
99 | // ===== Step 1: subscribe `register` event =====
100 | // --- this.onVoipPushNotificationRegistered
101 | // ===== Step 4: register =====
102 | // --- it will be no-op if you have subscribed before (like in native side)
103 | // --- but will fire `register` event if we have latest cahced voip token ( it may be empty if no token at all )
104 | }
105 | }
106 | }
107 |
108 | export default Incomingvideocall = new IncomingCall();
109 |
--------------------------------------------------------------------------------
/iOS_SETUP.md:
--------------------------------------------------------------------------------
1 | # iOS Setup
2 |
3 | ## Client
4 |
5 | - Callkit enables you to display the system-calling UI for your app's VoIP services and manage communication between your app, the system, and other apps. [See for further details](https://developer.apple.com/documentation/callkit).
6 |
7 | - Pushkit sends particular kinds of notifications, including notifications of file provider changes, watchOS complication updates, and VoIP invitations. It helps VoIP apps a lot. Visit [Pushkit](https://developer.apple.com/documentation/pushkit) for additional details.
8 |
9 | ### Step 1: Setup Firebase app
10 |
11 | 1. Create Firebase iOS App within Firebase Project.
12 | 2. Download and add `GoogleService-info.plist` file to project
13 |
14 | 
15 |
16 | ### Step 2: Configure Pushkit
17 |
18 | You must upload an APNs Auth Key in order to implement push notifications. We need the following details about your app when sending push notifications via an APNs Auth Key:
19 |
20 | - Auth Key file
21 | - Team ID
22 | - Key ID
23 | - Your app’s bundle ID
24 |
25 | To create an APNs auth key, follow the steps below.
26 |
27 | Visit the Apple [Developer Member Center](https://developer.apple.com/account/)
28 |
29 | 
30 |
31 | Click on `Certificates, Identifiers & Profiles`. Go to Keys from the left side. Create a new Auth Key by clicking on the plus button in the top right side.
32 |
33 | 
34 |
35 | On the following page, add a Key Name, and select APNs.
36 |
37 | 
38 |
39 | Click on the Register button.
40 |
41 | 
42 |
43 | You can download your auth key file from this page and upload this file to Firebase dashboard without changing its name.
44 |
45 | 
46 |
47 | In your firebase project, go to `Settings` and select the `Cloud Messaging` tab. Scroll down to `iOS app configuration`and click upload under `APNs Authentication Key`
48 |
49 | 
50 |
51 | Enter Key ID and Team ID. Key ID is in the file name, `AuthKey_{Key ID}.p8` and is 10 characters. Your Team ID is in the Apple Member Center under the [membership tab](https://developer.apple.com/account/#/membership) or displayed always under your account name in the top right corner.
52 |
53 | 
54 |
55 | ### Note:
56 |
57 | Enable Push Notifications in Capabilities
58 |
59 | 
60 |
61 | 
62 |
63 | Enable selcted permission in Background Modes
64 |
65 | 
66 |
67 | ## Server
68 |
69 | You have to add `AuthKey_{Key ID}.p8` under server directory which we generated from Apple Dev and upload it to Firebase in client setup.
70 | This will helps us in VoIP push notification.
71 |
72 | 
73 |
74 | Update KeyId and teamId in APN provider located inside `/initiate-call` API.
75 |
76 | 
77 |
--------------------------------------------------------------------------------
/public/FIR_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/FIR_1.png
--------------------------------------------------------------------------------
/public/FIR_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/FIR_2.png
--------------------------------------------------------------------------------
/public/code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/code.png
--------------------------------------------------------------------------------
/public/image-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/image-1.png
--------------------------------------------------------------------------------
/public/image-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/image-11.png
--------------------------------------------------------------------------------
/public/image-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/image-2.png
--------------------------------------------------------------------------------
/public/image-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/image-3.png
--------------------------------------------------------------------------------
/public/image-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/image-4.png
--------------------------------------------------------------------------------
/public/image-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/image-5.png
--------------------------------------------------------------------------------
/public/image-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/image-6.png
--------------------------------------------------------------------------------
/public/image-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/image-7.png
--------------------------------------------------------------------------------
/public/image-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/image-8.png
--------------------------------------------------------------------------------
/public/ot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/ot-1.png
--------------------------------------------------------------------------------
/public/ot-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/ot-2.png
--------------------------------------------------------------------------------
/public/ot-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/ot-3.png
--------------------------------------------------------------------------------
/public/ot-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/ot-4.png
--------------------------------------------------------------------------------
/public/xcd-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/xcd-1.png
--------------------------------------------------------------------------------
/public/xcd-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/xcd-2.png
--------------------------------------------------------------------------------
/public/xcd-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/xcd-3.png
--------------------------------------------------------------------------------
/public/xiomi-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/xiomi-1.png
--------------------------------------------------------------------------------
/public/xiomi-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/videosdk-live/videosdk-rtc-react-native-call-trigger-example/186712534584d2c24a0c3fdb96c43177e311a976/public/xiomi-2.png
--------------------------------------------------------------------------------
/server/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "react-native-callkit"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/server/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | firebase-debug.log*
8 | firebase-debug.*.log*
9 |
10 | # Firebase cache
11 | .firebase/
12 |
13 | # Firebase config
14 |
15 | # Uncomment this if you'd like others to create their own Firebase project.
16 | # For a team working on the same Firebase project(s), it is recommended to leave
17 | # it commented so all members can deploy to the same project(s) in .firebaserc.
18 | # .firebaserc
19 |
20 | # Runtime data
21 | pids
22 | *.pid
23 | *.seed
24 | *.pid.lock
25 |
26 | # Directory for instrumented libs generated by jscoverage/JSCover
27 | lib-cov
28 |
29 | # Coverage directory used by tools like istanbul
30 | coverage
31 |
32 | # nyc test coverage
33 | .nyc_output
34 |
35 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
36 | .grunt
37 |
38 | # Bower dependency directory (https://bower.io/)
39 | bower_components
40 |
41 | # node-waf configuration
42 | .lock-wscript
43 |
44 | # Compiled binary addons (http://nodejs.org/api/addons.html)
45 | build/Release
46 |
47 | # Dependency directories
48 | node_modules/
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Optional REPL history
57 | .node_repl_history
58 |
59 | # Output of 'npm pack'
60 | *.tgz
61 |
62 | # Yarn Integrity file
63 | .yarn-integrity
64 |
65 | # dotenv environment variables file
66 | .env
67 |
--------------------------------------------------------------------------------
/server/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "functions": [
3 | {
4 | "source": "functions",
5 | "codebase": "default",
6 | "ignore": [
7 | "node_modules",
8 | ".git",
9 | "firebase-debug.log",
10 | "firebase-debug.*.log"
11 | ]
12 | }
13 | ],
14 | "hosting": {
15 | "public": "n",
16 | "ignore": [
17 | "firebase.json",
18 | "**/.*",
19 | "**/node_modules/**"
20 | ]
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/server/functions/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | serviceAccountKey.json
3 | AuthKey_93JKT8SSVF.p8
--------------------------------------------------------------------------------
/server/functions/index.js:
--------------------------------------------------------------------------------
1 | const functions = require("firebase-functions");
2 | const express = require("express");
3 | const cors = require("cors");
4 | const morgan = require("morgan");
5 | var fcm = require("fcm-notification");
6 | var FCM = new fcm("./serviceAccountKey.json");
7 | var Key = "./AuthKey_93JKT8SSVF.p8";
8 | const app = express();
9 | var apn = require("apn");
10 | const { v4: uuidv4 } = require("uuid");
11 |
12 | app.use(cors());
13 | app.use(express.json());
14 | app.use(express.urlencoded({ extended: true }));
15 | app.use(morgan("dev"));
16 |
17 | //
18 |
19 | app.get("/", (req, res) => {
20 | res.send("Hello World!");
21 | });
22 |
23 | app.post("/initiate-call", (req, res) => {
24 | const { calleeInfo, callerInfo, videoSDKInfo } = req.body;
25 |
26 | if (calleeInfo.platform === "iOS") {
27 | let deviceToken = calleeInfo.APN;
28 | var options = {
29 | token: {
30 | key: Key,
31 | keyId: "YOUR_KEY_ID",
32 | teamId: "YOUR_TEAM_ID",
33 | },
34 | production: true,
35 | };
36 |
37 | var apnProvider = new apn.Provider(options);
38 |
39 | var note = new apn.Notification();
40 |
41 | note.expiry = Math.floor(Date.now() / 1000) + 3600; // Expires 1 hour from now.
42 | note.badge = 1;
43 | note.sound = "ping.aiff";
44 | note.alert = "You have a new message";
45 | note.rawPayload = {
46 | callerName: callerInfo.name,
47 | aps: {
48 | "content-available": 1,
49 | },
50 | handle: callerInfo.name,
51 | callerInfo,
52 | videoSDKInfo,
53 | type: "CALL_INITIATED",
54 | uuid: uuidv4(),
55 | };
56 | note.pushType = "voip";
57 | note.topic = "org.reactjs.ReactNativeCallTrigger.voip";
58 | apnProvider.send(note, deviceToken).then((result) => {
59 | if (result.failed && result.failed.length > 0) {
60 | console.log("RESULT", result.failed[0].response);
61 | res.status(400).send(result.failed[0].response);
62 | } else {
63 | res.status(200).send(result);
64 | }
65 | });
66 | } else if (calleeInfo.platform === "ANDROID") {
67 | var FCMtoken = calleeInfo.token;
68 | const info = JSON.stringify({
69 | callerInfo,
70 | videoSDKInfo,
71 | type: "CALL_INITIATED",
72 | });
73 | var message = {
74 | data: {
75 | info,
76 | },
77 | android: {
78 | priority: "high",
79 | },
80 | token: FCMtoken,
81 | };
82 | FCM.send(message, function (err, response) {
83 | if (err) {
84 | res.status(200).send(response);
85 | } else {
86 | res.status(400).send(response);
87 | }
88 | });
89 | } else {
90 | res.status(400).send("Not supported platform");
91 | }
92 | });
93 |
94 | app.post("/update-call", (req, res) => {
95 | const { callerInfo, type } = req.body;
96 | const info = JSON.stringify({
97 | callerInfo,
98 | type,
99 | });
100 |
101 | var message = {
102 | data: {
103 | info,
104 | },
105 | apns: {
106 | headers: {
107 | "apns-priority": "10",
108 | },
109 | payload: {
110 | aps: {
111 | badge: 1,
112 | },
113 | },
114 | },
115 | token: callerInfo.token,
116 | };
117 |
118 | FCM.send(message, function (err, response) {
119 | if (err) {
120 | res.status(200).send(response);
121 | } else {
122 | res.status(400).send(response);
123 | }
124 | });
125 | });
126 |
127 | app.listen(9000, () => {
128 | console.log(`API server listening at http://localhost:9000`);
129 | });
130 |
131 | exports.app = functions.https.onRequest(app);
132 |
--------------------------------------------------------------------------------
/server/functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "functions",
3 | "description": "Cloud Functions for Firebase",
4 | "scripts": {
5 | "serve": "firebase emulators:start --only functions",
6 | "shell": "firebase functions:shell",
7 | "start": "npm run shell",
8 | "deploy": "firebase deploy --only functions",
9 | "logs": "firebase functions:log"
10 | },
11 | "engines": {
12 | "node": "16"
13 | },
14 | "main": "index.js",
15 | "dependencies": {
16 | "firebase-functions": "^3.18.0",
17 | "apn": "https://github.com/node-apn/node-apn.git",
18 | "cors": "^2.8.5",
19 | "express": "^4.17.1",
20 | "fcm-notification": "^2.0.0",
21 | "morgan": "^1.10.0",
22 | "node-fetch": "^2.6.1",
23 | "uuid": "^9.0.0"
24 | },
25 | "devDependencies": {
26 | "firebase-functions-test": "^0.2.0"
27 | },
28 | "private": true
29 | }
--------------------------------------------------------------------------------
/server/n/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Page Not Found
7 |
8 |
23 |
24 |
25 |
26 |
404
27 |
Page Not Found
28 |
The specified file was not found on this website. Please check the URL for mistakes and try again.
29 |
Why am I seeing this?
30 |
This page was generated by the Firebase Command-Line Interface. To modify it, edit the 404.html
file in your project's configured public
directory.
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/server/n/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Welcome to Firebase Hosting
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
24 |
25 |
26 |
40 |
41 |
42 |
43 |
Welcome
44 |
Firebase Hosting Setup Complete
45 |
You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!
46 |
Open Hosting Documentation
47 |
48 | Firebase SDK Loading…
49 |
50 |
88 |
89 |
90 |
--------------------------------------------------------------------------------