{"id":24992773,"url":"https://github.com/cybex-dev/twilio_voice_mimp","last_synced_at":"2025-03-29T13:22:01.835Z","repository":{"id":183923397,"uuid":"671011677","full_name":"cybex-dev/twilio_voice_mimp","owner":"cybex-dev","description":null,"archived":false,"fork":false,"pushed_at":"2023-07-26T10:59:00.000Z","size":744,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-04T13:57:36.648Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cybex-dev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-07-26T10:39:40.000Z","updated_at":"2023-07-26T10:41:54.000Z","dependencies_parsed_at":"2023-07-29T18:16:26.228Z","dependency_job_id":null,"html_url":"https://github.com/cybex-dev/twilio_voice_mimp","commit_stats":null,"previous_names":["cybex-dev/twilio_voice_mimp"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cybex-dev%2Ftwilio_voice_mimp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cybex-dev%2Ftwilio_voice_mimp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cybex-dev%2Ftwilio_voice_mimp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cybex-dev%2Ftwilio_voice_mimp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cybex-dev","download_url":"https://codeload.github.com/cybex-dev/twilio_voice_mimp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246187612,"owners_count":20737549,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-04T13:57:41.130Z","updated_at":"2025-03-29T13:22:01.806Z","avatar_url":"https://github.com/cybex-dev.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"### PLEASE NOTE\nThis is code provided by pub.dev for `twilio_voice_mimp` as the code isn't made publicly available as a github repository. \n\n# twilio_voice\n\nProvides an interface to Twilio's Programmable Voice SDK to allow voice-over-IP (VoIP) calling into your Flutter applications.\nThis plugin was taken from the original flutter_twilio_voice, as it seems that plugin is no longer maitained, this one is.\n\n## Features\n- Receive and place calls from iOS devices, uses callkit to receive calls.\n- Receive and place calls from Android devices, uses custom UI to receive calls.\n\n\n### Android Limitations\n\nAs iOS has CallKit, an Apple provided UI for answering calls, there is no default UI for android to receive calls, for this reason a default UI was made. To increase customization, the UI will use a splash_icon.png registered on your res/drawable folder. I havent found a way to customize colors, if you find one, please submit a pull request.\n\n### Setup\nPlease follow Twilio's quickstart setup for each platform, you dont need to write the native code but it will help you undestand the basic functionality of setting up your server, registering your iOS app for VOIP, etc.\n\n### iOS Setup\n\nTo customize the icon displayed on a CallKit call, Open XCode and add a png icon named 'callkit_icon' to your assets.xassets folder\n\n### Android Setup:\nregister in your `AndroidManifest.xml` the service in charge of displaying incomming call notifications:\n\n``` xml\n\u003cApplication\u003e\n  .....\n  \u003cservice\n      android:name=\"com.twilio.twilio_voice.fcm.VoiceFirebaseMessagingService\"\n      android:stopWithTask=\"false\"\u003e\n      \u003cintent-filter\u003e\n          \u003caction android:name=\"com.google.firebase.MESSAGING_EVENT\" /\u003e\n      \u003c/intent-filter\u003e\n  \u003c/service\u003e\n```\n\n\n### Usage\n\nThe plugin was separated into two classes, the `TwilioVoice.instance` and `TwilioVoice.instance.call`, the first one is in charge of general configuration and the second one is in charge of managing calls.\n\nRegister iOS capabilities \n- Add Audio and Voice over IP in background modes\n\n### TwilioVoice.instance\n\n\n#### Setting the tokens\n\ncall `TwilioVoice.instance.setTokens` as soon as your app starts.\n- `accessToken` provided from your server, you can see an example cloud function [here](https://github.com/diegogarciar/twilio_voice/blob/master/functions.js).\n- `deviceToken` is automatically handled on iOS, for android you need to pass a FCM token.\n\ncall `TwilioVoice.instance.unregister` to unregister from Twilio, if no access token is passed, it will use the token provided in `setTokens` at the same session.\n\n\n\n### Call Identifier\nAs incomming call UI is shown in background and the App can even be closed when receiving the calls, you can map call identifiers such as `firebaseAuth` userIds to real names, this operation must be done before actially receiving the call. So if you have a chat app, and know the members names, register them so when they call, the call UI can display their names and not their userIds.\n\n\n#### Registering a client\n```\nTwilioVoice.instance.registerClient(String clientId, String clientName)\n```\n\n#### Unegistering a client\n```\nTwilioVoice.instance.unregisterClient(String clientId)\n```\n\n#### Default caller\nYou can also set a dafault caller, such as \"unknown number\" or \"chat friend\" in case a call comes in from an unregistered client.\n\n```\nTwilioVoice.instance.setDefaultCallerName(String callerName)\n```\n\n### Call Events\nuse stream `TwilioVoice.instance.callEventsListener` to receive events from the TwilioSDK such as call events and logs, it is a broadcast so you can listen to it on different parts of your app. Some events might be missed when the app has not launched, please check out the example project to find the workarounds.\n\nThe events sent are the following\n- ringing\n- connected\n- callEnded\n- unhold\n- hold\n- unmute\n- mute\n- speakerOn\n- speakerOff\n- log\n- answer\n\n## showMissedCallNotifications\nBy default a local notification will be shown to the user after missing a call, clicking on the notification will call back the user. To remove this feature, set `showMissedCallNotifications` to `false`.\n\n\n\n### Calls\n\n\n#### Make a Call\n`from` your own identifier\n`to` the id you want to call\nuse `extraOptions` to pass additional variables to your server callback function.\n```\n await TwilioVoice.instance.call.place(from:myId, to: clientId, extraOptions)\n                   ;\n\n```\n\n#### Mute a Call\n\n```\n TwilioVoice.instance.call.toggleMute(isMuted: true);\n\n```\n\n#### Toggle Speaker\n\n```\n TwilioVoice.instance.call.toggleSpeaker(speakerIsOn: true);\n\n```\n\n#### Hang Up\n\n```\n TwilioVoice.instance.call.hangUp();\n\n```\n\n#### Send Digits\n\n```\n TwilioVoice.instance.call.sendDigits(String digits);\n\n```\n\n\n### Permissions\n\n#### Microphone\nTo receive and place calls you need Microphone permisisons, register the micropohone permission in your info.plist for iOS.\n\nYou can use `TwilioVoice.instance.hasMicAccess` and `TwilioVoice.instance.requestMicAccess` to check and request the permission. Permissions is also automatically requested when receiving a call.\n\n#### Background calls (Android only on some devices)\nXiami devices, and maybe others, need a spetial permission to receive background calls. use `TwilioVoice.instance.requiresBackgroundPermissions` to check if your device requires a special permission, if it does, show a rationale explaining the user why you need the permisison. Finally call \n`TwilioVoice.instance.requestBackgroundPermissions` which will take the user to the App Settings page to enable the permission.\n\n\n### Localization\nBecause some of the UI is in native code, you need to localize those strings natively in your project. You can find in the example project localization for spanish, PRs are welcome for other languages.\n\n---\n\n\n## Twilio Setup/Quickstart Help\n\nTwilio makes use of cloud functions to generate access tokens and sends them to your app. Further, Twilio makes use of their own apps called TwiML apps to handle calling functions, etc\n\nThere are 2 major components to get Twilio Setup.\n\n1. Cloud functions (facility generating **access tokens** and then **handling call requests**)\n2. Mobile app that receives/updates tokens and performs the actual calls (see above)\n\n---\n\n### 1) Cloud Funtions\n\nCloud functions can be seperated or grouped together. The main 2 components are:\n- generate access tokens\n- `make-call` endpoint to actually place the call\n\nYou can host both in firebase, in TwiML apps or a mixture. The setup below assumes a mixture, where Firebase Functions hosts the `access-token` for eashy integration into Flutter and TwiML hosting the `make-call` function.\n\n## Cloud-Functions-Step-1: Create your TwiML app\n\nThis will allow you to actually place the call\n\nPrerequisites\n---\n\n* A Twilio Account. Don't have one? [Sign up](https://www.twilio.com/try-twilio) for free!\n\n## Setting up the Application\n\nGrab [this](https://github.com/twilio/voice-quickstart-server-node) project from github, the sample TwiML app.\n\n```bash\ncp .env.example .env\n```\n\nEdit `.env` with the three configuration parameters we gathered from above.\n\n**See configure environment below for details**\n\nNext, we need to install our dependencies from npm:\n\n```bash\nnpm install\n```\n\nTo make things easier for you, go into the `src/` folder, rename the `server.js` file to `make-call`. This assumes each function will have its own file which for a new project isn't a bad idea.\n\nThen add the following code:\n\n```javascript\nconst AccessToken = require('twilio').jwt.AccessToken;\nconst VoiceGrant = AccessToken.VoiceGrant;\nconst VoiceResponse = require('twilio').twiml.VoiceResponse;\n\n/**\n * Creates an endpoint that can be used in your TwiML App as the Voice Request Url.\n * \u003cbr\u003e\u003cbr\u003e\n * In order to make an outgoing call using Twilio Voice SDK, you need to provide a\n * TwiML App SID in the Access Token. You can run your server, make it publicly\n * accessible and use `/makeCall` endpoint as the Voice Request Url in your TwiML App.\n * \u003cbr\u003e\u003cbr\u003e\n *\n * @returns {Object} - The Response Object with TwiMl, used to respond to an outgoing call\n * @param context\n * @param event\n * @param callback\n */\nexports.handler = function(context, event, callback) {\n    // The recipient of the call, a phone number or a client\n\n    console.log(event);\n    const from = event.From;\n    let to = event.to;\n    if(isEmptyOrNull(to)) {\n        to = event.To;\n        if(isEmptyOrNull(to)) {\n            console.error(\"Could not find someone to call\");\n            to = undefined;\n        }\n    }\n\n\n    const voiceResponse = new VoiceResponse();\n\n    if (!to) {\n        voiceResponse.say(\"Welcome, you made your first call.\");\n    } else if (isNumber(to)) {\n      const dial = voiceResponse.dial({callerId : callerNumber});\n      dial.number(to);\n  } else {\n        console.log(`Calling [${from}] -\u003e [${to}]`)\n\n        const dial = voiceResponse.dial({callerId: to, timeout: 30, record: \"record-from-answer-dual\", trim: \"trim-silence\"});\n        dial.client(to);\n    }\n\n    callback(null, voiceResponse);\n}\n\nconst isEmptyOrNull = (s) =\u003e {\n    return !s || s === '';\n}\n```\n\n### Setup Twilio CLI\n\nEnsure you are logged into `twilio-cli`. First, install `twilio-cli` with\n\n```javascript\nnpm i twilio-cli -g\n```\n\nAfterwards, login to twilio using: (b sure to provide Twilio account SID and auth token for login):\n\n```javascript\ntwilio login\n```\n\nWe need to generate an app, this will give us an App SID to use later in firebase functions, (see [this](https://github.com/twilio/voice-quickstart-ios#3-create-a-twiml-application-for-the-access-token) more info)\n\n### Create TwiML app\n\nWe need to create a TwiML app that will allow us to host a `make-call` function:\n\n```bash\ntwilio api:core:applications:create \\\n--friendly-name=my-twiml-app \\\n--voice-method=POST \\\n--voice-url=\"https://my-quickstart-dev.twil.io/make-call\"\n```\n\nThis will present you with a application SID in the format ```APxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx```, we will use this later in firebase config and generating push credential keys.\n\n**Very Important!** The URL given here `https://my-quickstart-dev.twil.io/make-call` won't work for you. Once you deployed your TwiML application (later), a URL is given to you (on first deploy) which you need to copy and paste as your **Request URL** call. If you don't do this, calling won't work!\n\n### Configure environment\n\nensure you have a `.env` file in the root of your project in the same directory as `package.json`\n\nnext, edit the `.env` file in the format\n\n```bash\nACCOUNT_SID=(insert account SID)\nAPP_SID=(insert App SID, found on TwiML app or the APxxxxx key above)\n```\n`API_KEY` and `API_KEY_SECRET` aren't necessary here since we won't be using them\n\n#### Get Push Credential:\n\n**We will generate them a bit later**\n\n- Android FCM: [Android instructions](https://github.com/twilio/voice-quickstart-android#7-create-a-push-credential-using-your-fcm-server-key)\n- Apple APNS: [Apple instructions](https://github.com/twilio/voice-quickstart-ios#6-create-a-push-credential-with-your-voip-service-certificate)\n\nYou will get a Push Credential SID in the format: `CRxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`, use this in `PUSH_CREDENTIAL_SID`\n\n### Deploying\n\nNow lets deploy.\n\n#### Please note:  Check you have configured your environment first\n\nNavigate to root directory, and deploy using\n\n```javascript\ntwilio serverless:deploy\n```\n\n**Very Important!**: once complete (if you haven't done so), make sure to add the `make-call` endpoint your Twilio app's `Request URL` in the main Twilio page. This URL will be shown as part of the deployment text. If this isn't done, calling won't work!\n\n\n### Cloud-Functions-Step-2: Setup Firebase \u0026 Configuration\n\nTwilio's configurations are stored in `.runtimeconfig.json` which contains:\n\n    \"auth_token\": \"\",\n    \"account_sid\": \"\",\n    \"app_sid\": \"\",\n    \"phone\": \"\",\n    \"api_key\": \"\",\n    \"api_key_secret\": \"\",\n    \"android_push_credential\": \"\",\n    \"apple_push_credential_debug\": \"\",\n    \"apple_push_credential_release\": \"\"\n\n_**Note:** this is used for local emulator testing, but you need to deploy these to your firebase function application once you are ready to go live. If you don't, this won't work!_\n\n**Push Credentials** are created once (for iOS, Android) and used to generate `access-token`s, a callback function for all Twilio apps to use for their communication.\n\n---\n\nBelow are the 3 operations you need to run to generate push credentials that should be added into the `.runtimeconfig.json` above\n\n##### Android\nTo generate Android push credentials, get the Cloud Messaging server key from Firebase FCM, and add it to the following:\n\n```\ntwilio api:chat:v2:credentials:create \\\n--type=fcm \\\n--friendly-name=\"voice-push-credential-fcm\" \\\n--secret=SERVER_KEY_VALUE\n```\n\nand then place into the field: `android_push_credential` above\n\nThis generated a push credential SID in the format `CRxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` which must be used to generate access tokens for android devices.\n\nsee for more info: https://github.com/twilio/voice-quickstart-android#7-create-a-push-credential-using-your-fcm-server-key\n\n##### iOS\n\nSimilar to Android, but more steps including using .p12 certificates. To get these certificates, login into [Apple's developer site](https://developer.apple.com/) and go to the [certificates page](https://developer.apple.com/account/resources/certificates/list). You need to generate a VoIP Services certificate as shown below.\n\n![voip_services.png](images/voip_services.png)\n\n**Please note:** there are 2 different modes: sandbox and production.\n\n**- SandBox Mode**\n\nUsing sandbox VoIP certificate:\n\n\u003e Export your VoIP Service Certificate as a .p12 file from Keychain Access and extract the certificate and private key from the .p12 file using the openssl command.\n\n```\n$ openssl pkcs12 -in PATH_TO_YOUR_SANDBOX_P12 -nokeys -out sandbox_cert.pem -nodes\n$ openssl pkcs12 -in PATH_TO_YOUR_SANDBOX_P12 -nocerts -out sandbox_key.pem -nodes\n$ openssl rsa -in sandbox_key.pem -out sandbox_key.pem\n```\n\nUsing sandbox certificates, generate credential:\n\n```\ntwilio api:chat:v2:credentials:create \\\n--type=apn \\\n--sandbox \\\n--friendly-name=\"voice-push-credential (sandbox)\" \\\n--certificate=\"$(cat PATH_TO_SANDBOX_CERT_PEM)\" \\\n--private-key=\"$(cat PATH_TO_SANDBOX_KEY_PEM)\"\n```\n\nthen place it into the field `apple_push_credential_debug`\n\n**- Production Mode**\n\nUsing production VoIP certificate:\n\n\u003e Export your VoIP Service Certificate as a .p12 file from Keychain Access and extract the certificate and private key from the .p12 file using the openssl command.\n\n```\n$ openssl pkcs12 -in PATH_TO_YOUR_P12 -nokeys -out prod_cert.pem -nodes\n$ openssl pkcs12 -in PATH_TO_YOUR_P12 -nocerts -out prod_key.pem -nodes\n$ openssl rsa -in prod_key.pem -out prod_key.pem\n```\n\nUsing production certificates, generate credential:\n\n```\ntwilio api:chat:v2:credentials:create \\\n--type=apn \\\n--friendly-name=\"voice-push-credential (production)\" \\\n--certificate=\"$(cat PATH_TO_PROD_CERT_PEM)\" \\\n--private-key=\"$(cat PATH_TO_PROD_KEY_PEM)\"\n```\n\nthen place it into the field `apple_push_credential_release`\n\nsee for more info: https://github.com/twilio/voice-quickstart-ios#6-create-a-push-credential-with-your-voip-service-certificate\n\n---\n\n## Cloud-Functions-Step-3: Generate access tokens via cloud function\n\n`HTTP/GET api-voice-accessToken`\n\nTo generate **access-tokens**, the following firebase function is used:\n\n_**Please Note** the default time is 1 hour token validity._ \n\nSee for more info: https://github.com/twilio/voice-quickstart-android/blob/master/Docs/access-token.md\n\n**Firebase Cloud Function: access-token**\n\n```javascript\nconst { AccessToken } = require('twilio').jwt;\nconst functions = require('firebase-functions');\n\nconst { VoiceGrant } = AccessToken;\n\n/**\n * Creates an access token with VoiceGrant using your Twilio credentials.\n *\n * @param {Object} request - POST or GET request that provides the recipient of the call, a phone number or a client\n * @param {Object} response - The Response Object for the http request\n * @returns {string} - The Access Token string and expiry date in milliseconds\n */\nexports.accessToken = functions.https.onCall((payload, context) =\u003e {\n    // Check user authenticated\n    if (typeof (context.auth) === 'undefined') {\n        throw new functions.https.HttpsError('unauthenticated', 'The function must be called while authenticated');\n    }\n    let userId = context.auth.uid;\n\n    console.log('creating access token for ', userId);\n\n    //configuration using firebase environment variables\n    const twilioConfig = functions.config().twilio;\n    const accountSid = twilioConfig.account_sid;\n    const apiKey = twilioConfig.api_key;\n    const apiSecret = twilioConfig.api_key_secret;\n    const outgoingApplicationSid = twilioConfig.app_sid;\n\n    // Used specifically for creating Voice tokens, we need to use seperate push credentials for each platform. \n    // iOS has different APNs environments, so we need to distinguish between sandbox \u0026 production as the one won't work in the other.\n    let pushCredSid;\n    if (payload.isIOS === true) {\n        console.log('creating access token for iOS');\n        pushCredSid = payload.production ? twilioConfig.apple_push_credential_release\n            : (twilioConfig.apple_push_credential_debug || twilioConfig.apple_push_credential_release);\n    } else if (payload.isAndroid === true) {\n        console.log('creating access token for Android');\n        pushCredSid = twilioConfig.android_push_credential;\n    } else {\n        throw new functions.https.HttpsError('unknown_platform', 'No platform specified');\n    }\n\n    // generate token valid for 24 hours - minimum is 3min, max is 24 hours, default is 1 hour\n    const dateTime = new Date();\n    dateTime.setDate(dateTime.getDate()+1);\n    // Create an access token which we will sign and return to the client,\n    // containing the grant we just created\n    const voiceGrant = new VoiceGrant({\n        outgoingApplicationSid,\n        pushCredentialSid: pushCredSid,\n    });\n\n    // Create an access token which we will sign and return to the client,\n    // containing the grant we just created\n    const token = new AccessToken(accountSid, apiKey, apiSecret);\n    token.addGrant(voiceGrant);\n\n    // use firebase ID for identity\n    token.identity = userId;\n    console.log(`Token:${token.toJwt()}`);\n\n    // return json object\n    return {\n        \"jwt_token\": token.toJwt(),\n        \"expiry_date\": dateTime.getTime()\n    };\n});\n```\n\nAdd the function above to your Firebase Functions application, see [this](https://firebase.google.com/docs/functions/get-started) for more help on creating a firebase functions project\n\nAfter you are done, deploy your `.runtimeconfig.json`, see [this](https://firebase.google.com/docs/functions/config-env) for more help.\n\nOnce done with everything above, deploy your firebase function with this: \n```bash\nfirebase deploy --only functions\n```\n\n##### Done! \n\nCalling should work naturally - just make sure to fetch the token from the endpoint and you can call\n\nSee [example](https://github.com/diegogarciar/twilio_voice/blob/master/example/lib/main.dart#L51) code, make sure to change the `voice-accessToken` to your function name, given to you by firebase when deploying (as part of the deploy text)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcybex-dev%2Ftwilio_voice_mimp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcybex-dev%2Ftwilio_voice_mimp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcybex-dev%2Ftwilio_voice_mimp/lists"}