Giter VIP home page Giter VIP logo

diegogarciar / twilio_voice Goto Github PK

View Code? Open in Web Editor NEW
31.0 4.0 71.0 1.46 MB

Flutter Twilio Voice Plugin

Home Page: https://twilio-voice-web.web.app/

License: MIT License

Java 0.03% Ruby 0.94% Swift 35.02% Objective-C 0.14% Dart 26.50% JavaScript 1.22% HTML 0.45% Kotlin 35.70%
apns callkit connection-service fcm flutter flutter-package flutter-plugin flutter-ui flutter-web javascript kotlin swift twilio twilio-voice voip

twilio_voice's Introduction

twilio_voice

Provides an interface to Twilio's Programmable Voice SDK to allow voice-over-IP (VoIP) calling into your Flutter applications. This plugin was taken from the original flutter_twilio_voice as it seems that plugin is no longer maintained, this one is. Project ownership & maintenance handed over by diegogarcia. For the foreseeable future, I'll be actively maintaining this project.

🐞Bug? Issue? Something odd?

Report it here.

🚀 Feature Requests?

Any and all Feature Requests or Pull Requests are gladly welcome!

Live Example/Samples:

Currently, only Web sample is provided. If demand arises for a Desktop or Mobile builds, I'll throw one up on the relevant store/app provider or make one available.

Features

  • Receive and place calls from iOS devices, uses Callkit to receive calls.
  • Receive and place calls from Android devices, uses custom UI native call screen to receive calls (via a ConnectionService impl).
  • Receive and place calls from Web (FCM push notification integration not yet supported by Twilio Voice Web, see here for discussion)
  • Receive and place calls from MacOS devices, uses custom UI to receive calls (in future & macOS 13.0+, we'll be using CallKit).
  • Interpret TwiML parameters to populate UI, see below Interpreting Parameters

Feature addition schedule:

  • Audio device selection support (select input/output audio devices, on-hold)
  • Update plugin to Flutter federated packages (step 1 of 2 with Web support merge)
  • Desktop platform support (implementation as JS wrapper/native implementation, Windows/Linux to start development)

Android Limitations

As 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 haven't found a way to customize colors, if you find one, please submit a pull request.

Android provides a native UI by way of the ConnectionService. Twilio has made an attempt a ConnectionService implementation however it is fully realized in this package.

macOS Limitations

  1. CallKit support is found in macOS 13.0+ which there is no support for yet. In future, this will be taken into consideration for feature development.
  2. Twilio Voice does not offer a native SDK for macOS, so we're using the Twilio Voice Web SDK ( twilio-voice.js, v2.4.1-dev) to provide the functionality. This is a temporary solution until (or even if) Twilio Voice SDK for macOS is released.

This limits macOS to not support remote push notifications .voip and .apns as the web SDK does not support this. Instead, it uses a web socket connection to listen for incoming calls, arguably more efficient vs time but forces the app to be open at all times to receive incoming calls.

Setup

Please follow Twilio's quickstart setup for each platform, you don't need to write the native code but it will help you understand the basic functionality of setting up your server, registering your iOS app for VOIP, etc.

iOS Setup

To customize the icon displayed on a CallKit call, Open XCode and add a png icon named ' callkit_icon' to your assets.xassets folder

see [Notes] for more information

macOS Setup

Drop in addition.

see [Limitations] and [Notes] for more information.

Android Setup:

register in your AndroidManifest.xml the service in charge of displaying incoming call notifications:

<Application>
 .....
 <service
 android:name="com.twilio.twilio_voice.fcm.VoiceFirebaseMessagingService"
 android:stopWithTask="false">
<intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter> </service>

Phone Account

To register a Phone Account, request access to READ_PHONE_NUMBERS permission first:

TwilioVoice.instance.requestReadPhoneNumbersPermission();  // Gives Android permissions to read Phone Accounts

then, register the PhoneAccount with:

TwilioVoice.instance.registerPhoneAccount();

Enable calling account

To open the Call Account settings, use the following code:

TwilioVoice.instance.openPhoneAccountSettings();

Check if it's enabled with:

TwilioVoice.instance.isPhoneAccountEnabled();

Calling with ConnectionService

Placing a call with Telecom app via Connection Service requires a PhoneAccount to be registered. See Phone Account above for more information.

Finally, to grant access to place calls, run:

TwilioVoice.instance.requestCallPhonePermission();  // Gives Android permissions to place calls

See Customizing the Calling Account for more information.

Enabling the ConnectionService

To enable the ConnectionService and make/receive calls, run:

TwilioVoice.instance.requestReadPhoneStatePermission();  // Gives Android permissions to read Phone State

Highly recommended to review the notes for Android. See [Notes] for more information.

Customizing the Calling Account

To customize the label and shortDescription of the calling account, add the following in your res/values/strings.xml:

<string name="phone_account_name" translatable="false">Example App</string>
<string name="phone_account_desc" translatable="false">Example app voice calls calling account</string>

This can be found in alternatively the Phone App's settings, Other/Advanced Call Settings -> Calling Accounts -> (Example App) (then toggle the switch)

enter image description here

See example for more details

Known Issues

Bluetooth, Telecom App Crash
  • Upon accepting an inbound call, at times the Telecom app/ Bluetooth service will crash and restart. This is a known bug, caused by Class not found when unmarshalling: com.twilio.voice.CallInvite. This is due to the Telecom service not using the same Classloader as the main Flutter app. See here for source of error.
  • Callback action on post dialer screen may not work as expected - this is platform and manufacturer specific. PRs are welcome here.
  • Complete integration with showing missed calls. This is a work in progress.

Web Setup:

There are 4 important files for Twilio incoming/missed call notifications to work:

  • notifications.js is the main file, it handles the notifications and the service worker.
  • twilio-sw.js is the service worker content used to work with the default flutter_service_worker.js (this can be found in build/web/flutter_service_worker.js after calling flutter build web). This file's contents are to be copied into the flutter_service_worker.js file after you've built your application.

Also, the twilio javascript SDK itself, twilio.min.js is needed.

To ensure proper/as intended setup:

  1. Copy files example/web/notifications.js and example/web/twilio.min.js into your application's web folder.
  2. This step should be done AFTER you've built your application, every time the flutter_service_worker.js changes (this includes hot reloads on your local machine unfortunately)
    1. Copy the contents of example/web/twilio-sw.js into your build/web/flutter_service_worker.js file, at the end of the file. See service-worker for more information.

Note, these files can be changed to suite your needs - however the core functionality should remain the same: responding to notificationclick, notificationclose, message events and associated sub-functions.

Finally, add the following code to your index.html file, at the end of body tag:

    <body>
        <!--Start Twilio Voice impl-->
        <!--twilio native js library-->
        <script type="text/javascript" src="./twilio.min.js"></script>
        <!--End Twilio Voice impl-->

        <script>
            window.addEventListener('load', function(ev) {
              // Download main.dart.js
              ...          
    </body>

Web Considerations

If you need to debug the service worker, open up Chrome Devtools, go to Application tab, and select Service Workers from the left menu. There you can see the service workers and their status. To review service worker notificationclick, notificationclose, message, etc events - do this using Chrome Devtools (Sources tab, left panel below 'site code' the service workers are listed)

Service Worker

Unifying the service worker(s) is best done via post-compilation tools or a CI/CD pipeline (suggested).

A snippet of the suggested service worker integration is as follows:

#...
- run: cd ./example; flutter build web --release --target=lib/main.dart --output=build/web

- name: Update service worker
  run: cat ./example/web/twilio-sw.js >> ./example/build/web/flutter_service_worker.js
#...

A complete example could be found in the github workflows .github/workflows/flutter.yml file, see here.

Web Notifications

2 types of notifications are shown:

  • Incoming call notifications with 2 buttons: Answer and Reject,
  • Missed call notifications with 1 button: Call back.

Notifications are presented as alerts. These notifications may not always been shown, check:

  • if the browser supports notifications,
  • if the user has granted permissions to show notifications,
  • if the notifications display method / notifications is enabled by the system (e.g. macOS notifications are disabled, or Windows notifications are disabled, etc).
  • if there are already notifications shown (https://stackoverflow.com/a/36383155/4628115)
  • if system is in 'Do Not Disturb' or 'Focus' mode.

MacOS Setup:

The plugin is essentially a WKWebView wrapper. This makes macOS integration a drop-in solution.

However, you'll need to:

  1. add the following to your Info.plist file:

    <key>NSMicrophoneUsageDescription</key>
    <string>Allow microphone access to make calls</string>
  2. include Hardened Runtime entitlements (this is required for App Store distributed MacOS apps):

    <key>com.apple.security.audio-input</key>
    <true/>
    
    <!--Optionally for bluetooth support/permissions-->
    <key>com.apple.security.device.bluetooth</key>
    <true/>
  3. Lastly and most importantly, ensure the index.html and twilio.min.js is bundled inside of twilio_voice package (this shouldn't be a problem, but just in case). Found in twilio_voice.../.../Classes/Resources/*.

See this for more information on preparing for publishing your macOS app

Usage

The 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.

Register iOS capabilities

  • Add Audio and Voice over IP in background modes

TwilioVoice.instance

Setting the tokens

call TwilioVoice.instance.setTokens as soon as your app starts.

  • accessToken provided from your server, you can see an example cloud function here.
  • deviceToken is automatically handled on iOS, for android you need to pass a FCM token.

call 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.

Call Identifier

As incoming 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 actually 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.

Registering a client

TwilioVoice.instance.registerClient(String clientId, String clientName)

Unregistering a client

TwilioVoice.instance.unregisterClient(String clientId)

Default caller

You can also set a default caller, such as "unknown number" or "chat friend" in case a call comes in from an unregistered client.

TwilioVoice.instance.setDefaultCallerName(String callerName)

Call Events

use 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.

The events sent are the following

  • incoming // web, MacOS only
  • ringing
  • connected
  • reconnected
  • reconnecting
  • callEnded
  • unhold
  • hold
  • unmute
  • mute
  • speakerOn
  • speakerOff
  • log
  • declined (based on Twilio Error codes, or remote abort)
  • answer
  • missedCall
  • returningCall
  • permission (Android only)

Interpreting Parameters

As a convenience, the plugin will interpret the TwiML parameters and send them as a map in the CallInvite or provided via extraOptions when creating the call. This is useful for passing additional information to the call screen and are prefixed with __TWI.

  • __TWI_CALLER_ID - caller id
  • __TWI_CALLER_NAME - caller name
  • __TWI_CALLER_URL - caller image/thumbnail url (not implemented/supported at the moment)
  • __TWI_RECIPIENT_ID - recipient id
  • __TWI_RECIPIENT_NAME - recipient name
  • __TWI_RECIPIENT_URL - recipient image/thumbnail url (not implemented/supported at the moment)
  • __TWI_SUBJECT - subject/additional info

These parameters above are interpreted as follows.

Name resolution

Caller is usually referred to as call.from or callInvite.from. This can either be a number of a string (with the format client:clientName) or null.

The following rules are applied to determine the caller/recipient name, which is shown in the call screen and heads-up notification:

Incoming Calls:

__TWI_CALLER_NAME -> resolve(__TWI_CALLER_ID) -> (phone number) -> registered client (from) -> defaultCaller name -> "Unknown Caller"

Outgoing Calls:

__TWI_RECIPIENT_NAME -> resolve(__TWI_RECIPIENT_ID) -> (phone number) -> registered client (to) -> defaultCaller name -> "Unknown Caller"

Details explaination:

  • if the call is an CallInvite (incoming), the plugin will interpret the string as follows or if the call is outgoing, the twilio To parameter field is used to:
    • if the __TWI_CALLER_NAME (or __TWI_RECIPIENT_NAME) parameter is provided, the plugin will show the value of __TWI_CALLER_NAME (or __TWI_RECIPIENT_NAME) as is, else
    • if the __TWI_CALLER_ID (or __TWI_RECIPIENT_ID) parameter is provided, the plugin will search for a registered client with the same id and show the client name,
  • if the caller (from or to fields) is empty/not provided, the default caller name is shown e.g. "Unknown Caller", else
  • else if the caller (from or to fields) is a number, the plugin will show the number as is, else
  • else the plugin will search for a registered client with the callInvite.from (or call.to) value and show the client name, as a last resort
    • the default caller name is shown e.g. "Unknown Caller"

Please note: the same approach applies to both caller and recipient name resolution.

Subject

Using the provided __TWI_SUBJECT parameter, the plugin will show the subject as is, else (depending on the platform and manufacturer), the plugin will show:

  • the caller name as the subject, or
  • the app name as the subject, or
  • the default subject "Incoming Call"

showMissedCallNotifications

By 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.

Calls

Make a Call

from your own identifier to the id you want to call use extraOptions to pass additional variables to your server callback function.

 await TwilioVoice.instance.call.place(from:myId, to: clientId, extraOptions);

These translate to the your TwiML event function/service as:

javascript sample

exports.handler = function(context, event, callback) {
    const from = event.From;
    const to = event.To;
    // event contains extraOptions as a key/value map

    // your TwiML code...
}

See Setting up the Application for more information.

Please note: the hardcoded To, From may change in future.

Receiving Calls

iOS

Receives calls via CallKit integration. Make sure to review the iOS Setup section for more information.

Android

Receives calls via ConnectionService integration. Make sure to review the Android Setup section for more information.

Mute a Call

 TwilioVoice.instance.call.toggleMute(isMuted: true);

Toggle Speaker

 TwilioVoice.instance.call.toggleSpeaker(speakerIsOn: true);

Hang Up

 TwilioVoice.instance.call.hangUp();

Send Digits

 TwilioVoice.instance.call.sendDigits(String digits);

Permissions

Microphone

To receive and place calls you need Microphone permissions, register the microphone permission in your info.plist for iOS.

You can use TwilioVoice.instance.hasMicAccess and TwilioVoice.instance.requestMicAccess to check and request the permission. Permissions is also automatically requested when receiving a call.

Background calls (Android only on some devices)

Xiaomi devices, and maybe others, need a special 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 permission. Finally call TwilioVoice.instance.requestBackgroundPermissions which will take the user to the App Settings page to enable the permission.

Deprecated in 0.10.0, as it is no longer needed. Custom UI has been replaced with native UI.

ConnectionService & Native Phone Account (Android only)

Similar to CallKit on iOS, Android implements their own via a ConnectionService integration. To make use of this, you'll need to request CALL_PHONE permissions via:

TwilioVoice.instance.requestCallPhonePermission();  // Gives Android permissions to place outgoing calls
TwilioVoice.instance.requestReadPhoneStatePermission();  // Gives Android permissions to read Phone State including receiving calls
TwilioVoice.instance.requestReadPhoneNumbersPermission();  // Gives Android permissions to read Phone Accounts
TwilioVoice.instance.requestManageOwnCallsPermission();  // Gives Android permissions to manage calls, this isn't necessary to request as the permission is simply required in the Manifest, but added nontheless.

Following this, to register a Phone Account (required by all applications implementing a system-managed ConnectionService, run:

TwilioVoice.instance.registerPhoneAccount();  // Registers the Phone Account
TwilioVoice.instance.openPhoneAccountSettings();  // Opens the Phone Account settings

// After the account is enabled, you can check if it's enabled with:
TwilioVoice.instance.isPhoneAccountEnabled();  // Checks if the Phone Account is enabled

This last step can be considered the 'final check' to make/receive calls on Android.

Permissions not granted?

Finally, a consideration for not all (CALL_PHONE) permissions granted on an Android device. The following feature is available on Android only:

TwilioVoice.instance.rejectCallOnNoPermissions({Bool = false}); // Rejects incoming calls if permissions are not granted
TwilioVoice.instance.isRejectingCallOnNoPermissions(); // Checks if the plugin is rejecting calls if permissions are not granted

If the CALL_PHONE permissions group i.e. READ_PHONE_STATE, READ_PHONE_NUMBERS, CALL_PHONE aren't granted nor a Phone Account is registered and enabled, the plugin will either reject the incoming call (true) or not show the incoming call UI (false).

Note: If MANAGE_OWN_CALLS permission is not granted, outbound calls will not work.

See Android Setup and Android Notes for more information regarding configuring the ConnectionService and registering a Phone Account.

Localization

Because 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.


Twilio Setup/Quickstart Help

Twilio 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

There are 2 major components to get Twilio Setup.

  1. Cloud functions (facility generating access tokens and then handling call requests)
  2. Mobile app that receives/updates tokens and performs the actual calls (see above)

1) Cloud Functions

Cloud functions can be separated or grouped together. The main 2 components are:

  • generate access tokens
  • make-call endpoint to actually place the call

You 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 easy integration into Flutter and TwiML hosting the make-call function.

Cloud-Functions-Step-1: Create your TwiML app

This will allow you to actually place the call

Prerequisites

  • A Twilio Account. Don't have one? Sign up for free!

Setting up the Application

Grab this project from github, the sample TwiML app.

cp .env.example .env

Edit .env with the three configuration parameters we gathered from above.

See configure environment below for details

Next, we need to install our dependencies from npm:

npm install

To 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.

Then add the following code:

const AccessToken = require('twilio').jwt.AccessToken;
const VoiceGrant = AccessToken.VoiceGrant;
const VoiceResponse = require('twilio').twiml.VoiceResponse;

/**
 * Creates an endpoint that can be used in your TwiML App as the Voice Request Url.
 * <br><br>
 * In order to make an outgoing call using Twilio Voice SDK, you need to provide a
 * TwiML App SID in the Access Token. You can run your server, make it publicly
 * accessible and use `/makeCall` endpoint as the Voice Request Url in your TwiML App.
 * <br><br>
 *
 * @returns {Object} - The Response Object with TwiMl, used to respond to an outgoing call
 * @param context
 * @param event
 * @param callback
 */
exports.handler = function(context, event, callback) {
    // The recipient of the call, a phone number or a client

    console.log(event);
    const from = event.From;
    let to = event.to;
    if(isEmptyOrNull(to)) {
        to = event.To;
        if(isEmptyOrNull(to)) {
            console.error("Could not find someone to call");
            to = undefined;
        }
    }


    const voiceResponse = new VoiceResponse();

    if (!to) {
        voiceResponse.say("Welcome, you made your first call.");
    } else if (isNumber(to)) {
      const dial = voiceResponse.dial({callerId : callerNumber});
      dial.number(to);
  } else {
        console.log(`Calling [${from}] -> [${to}]`)

        const dial = voiceResponse.dial({callerId: to, timeout: 30, record: "record-from-answer-dual", trim: "trim-silence"});
        dial.client(to);
    }

    callback(null, voiceResponse);
}

const isEmptyOrNull = (s) => {
    return !s || s === '';
}

Setup Twilio CLI

Ensure you are logged into twilio-cli. First, install twilio-cli with

npm i twilio-cli -g

Afterwards, login to twilio using: (b sure to provide Twilio account SID and auth token for login):

twilio login

We need to generate an app, this will give us an App SID to use later in firebase functions, ( see this more info)

Create TwiML app

We need to create a TwiML app that will allow us to host a make-call function:

twilio api:core:applications:create \
--friendly-name=my-twiml-app \
--voice-method=POST \
--voice-url="https://my-quickstart-dev.twil.io/make-call"

This will present you with a application SID in the format APxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, we will use this later in firebase config and generating push credential keys.

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!

Configure environment

ensure you have a .env file in the root of your project in the same directory as package.json

next, edit the .env file in the format

ACCOUNT_SID=(insert account SID)
APP_SID=(insert App SID, found on TwiML app or the APxxxxx key above)

API_KEY and API_KEY_SECRET aren't necessary here since we won't be using them

Get Push Credential:

We will generate them a bit later

You will get a Push Credential SID in the format: CRxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, use this in PUSH_CREDENTIAL_SID

Deploying

Now lets deploy.

Please note: Check you have configured your environment first

Navigate to root directory, and deploy using

twilio serverless:deploy

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!

Cloud-Functions-Step-2: Setup Firebase & Configuration

Twilio's configurations are stored in .runtimeconfig.json which contains:

"auth_token": "", "account_sid": "", "app_sid": "", "phone": "", "api_key": "", "api_key_secret": "", "android_push_credential": "", "apple_push_credential_debug": "", "apple_push_credential_release": "" 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!

Push Credentials are created once (for iOS, Android) and used to generate access-tokens, a callback function for all Twilio apps to use for their communication.


Below are the 3 operations you need to run to generate push credentials that should be added into the .runtimeconfig.json above

Android

To generate Android push credentials, get the Cloud Messaging server key from Firebase FCM, and add it to the following:

twilio api:chat:v2:credentials:create \
--type=fcm \
--friendly-name="voice-push-credential-fcm" \
--secret=SERVER_KEY_VALUE

and then place into the field: android_push_credential above

This generated a push credential SID in the format CRxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx which must be used to generate access tokens for android devices.

see for more info: https://github.com/twilio/voice-quickstart-android#7-create-a-push-credential-using-your-fcm-server-key

iOS

Similar to Android, but more steps including using .p12 certificates. To get these certificates, login into Apple's developer site and go to the certificates page. You need to generate a VoIP Services certificate as shown below.

voip_services.png

Please note: there are 2 different modes: sandbox and production.

- SandBox Mode

Using sandbox VoIP certificate:

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.

$ openssl pkcs12 -in PATH_TO_YOUR_SANDBOX_P12 -nokeys -out sandbox_cert.pem -nodes
$ openssl pkcs12 -in PATH_TO_YOUR_SANDBOX_P12 -nocerts -out sandbox_key.pem -nodes
$ openssl rsa -in sandbox_key.pem -out sandbox_key.pem

Using sandbox certificates, generate credential:

twilio api:chat:v2:credentials:create \
--type=apn \
--sandbox \
--friendly-name="voice-push-credential (sandbox)" \
--certificate="$(cat PATH_TO_SANDBOX_CERT_PEM)" \
--private-key="$(cat PATH_TO_SANDBOX_KEY_PEM)"

then place it into the field apple_push_credential_debug

- Production Mode

Using production VoIP certificate:

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.

$ openssl pkcs12 -in PATH_TO_YOUR_P12 -nokeys -out prod_cert.pem -nodes
$ openssl pkcs12 -in PATH_TO_YOUR_P12 -nocerts -out prod_key.pem -nodes
$ openssl rsa -in prod_key.pem -out prod_key.pem

Using production certificates, generate credential:

twilio api:chat:v2:credentials:create \
--type=apn \
--friendly-name="voice-push-credential (production)" \
--certificate="$(cat PATH_TO_PROD_CERT_PEM)" \
--private-key="$(cat PATH_TO_PROD_KEY_PEM)"

then place it into the field apple_push_credential_release

see for more info: https://github.com/twilio/voice-quickstart-ios#6-create-a-push-credential-with-your-voip-service-certificate


Cloud-Functions-Step-3: Generate access tokens via cloud function

HTTP/GET api-voice-accessToken

To generate access-tokens, the following firebase function is used:

Please Note the default time is 1 hour token validity.

See for more info: https://github.com/twilio/voice-quickstart-android/blob/master/Docs/access-token.md

Firebase Cloud Function: access-token

const { AccessToken } = require('twilio').jwt;
const functions = require('firebase-functions');

const { VoiceGrant } = AccessToken;

/**
 * Creates an access token with VoiceGrant using your Twilio credentials.
 *
 * @param {Object} request - POST or GET request that provides the recipient of the call, a phone number or a client
 * @param {Object} response - The Response Object for the http request
 * @returns {string} - The Access Token string and expiry date in milliseconds
 */
exports.accessToken = functions.https.onCall((payload, context) => {
    // Check user authenticated
    if (typeof (context.auth) === 'undefined') {
        throw new functions.https.HttpsError('unauthenticated', 'The function must be called while authenticated');
    }
    let userId = context.auth.uid;

    console.log('creating access token for ', userId);

    //configuration using firebase environment variables
    const twilioConfig = functions.config().twilio;
    const accountSid = twilioConfig.account_sid;
    const apiKey = twilioConfig.api_key;
    const apiSecret = twilioConfig.api_key_secret;
    const outgoingApplicationSid = twilioConfig.app_sid;

    // Used specifically for creating Voice tokens, we need to use seperate push credentials for each platform.
    // iOS has different APNs environments, so we need to distinguish between sandbox & production as the one won't work in the other.
    let pushCredSid;
    if (payload.isIOS === true) {
        console.log('creating access token for iOS');
        pushCredSid = payload.production ? twilioConfig.apple_push_credential_release
            : (twilioConfig.apple_push_credential_debug || twilioConfig.apple_push_credential_release);
    } else if (payload.isAndroid === true) {
        console.log('creating access token for Android');
        pushCredSid = twilioConfig.android_push_credential;
    } else {
        throw new functions.https.HttpsError('unknown_platform', 'No platform specified');
    }

    // generate token valid for 24 hours - minimum is 3min, max is 24 hours, default is 1 hour
    const dateTime = new Date();
    dateTime.setDate(dateTime.getDate()+1);
    // Create an access token which we will sign and return to the client,
    // containing the grant we just created
    const voiceGrant = new VoiceGrant({
        outgoingApplicationSid,
        pushCredentialSid: pushCredSid,
    });

    // Create an access token which we will sign and return to the client,
    // containing the grant we just created
    const token = new AccessToken(accountSid, apiKey, apiSecret);
    token.addGrant(voiceGrant);

    // use firebase ID for identity
    token.identity = userId;
    console.log(`Token:${token.toJwt()}`);

    // return json object
    return {
        "token": token.toJwt(),
        "identity": userId,
        "expiry_date": dateTime.getTime()
    };
});

Add the function above to your Firebase Functions application, see this for more help on creating a firebase functions project

After you are done, deploy your .runtimeconfig.json, see this for more help.

Once done with everything above, deploy your firebase function with this:

firebase deploy --only functions
Done!

Calling should work naturally - just make sure to fetch the token from the endpoint and you can call

See example 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)

Future Work

  • Move package to federated plugin structure (see here for more info), see reduced overhead advantages covered as motivation (see here for more info))

Updating Twilio Voice JS SDK

twilio.js is no longer hosted via CDNs (see reference), instead it is hosted via npm / github. See instructions found here This is automatically added to your web/index.html file, as a <script/> tag during runtime. See here for more info.);

twilio_voice's People

Contributors

agent515 avatar cybex-dev avatar diegog-sf avatar diegogarciar avatar eopeter avatar gkpk avatar illia-romanenko avatar kpr7168 avatar mohsen-jalali avatar rajneeshsaini530 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

twilio_voice's Issues

Got Error MissingPluginException

Hi,

Trying to implement this on my app but I am getting error MissingPluginException. I spend days to find the problem but was not able to resolve it. Here is the error log

Device : Pixel 4 XL

====================================================================================================
I/flutter ( 2856): voip- service init
I/flutter ( 2856): voip-registtering with token 
I/flutter ( 2856): voip-calling voice-accessToken
I/flutter ( 2856): androidToken is XXXXXXXXXXXXXXXXX
E/flutter ( 2856): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: MissingPluginException(No implementation found for method isOnCall on channel twilio_voice/messages)
E/flutter ( 2856): #0      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:156:7)
E/flutter ( 2856): <asynchronous suspension>
E/flutter ( 2856): #1      _PeopleState.checkActiveCall (package:untitled/ui/people/peopleList.dart:147:22)
E/flutter ( 2856): <asynchronous suspension>
E/flutter ( 2856): 

======== Exception caught by services library ======================================================
The following MissingPluginException was thrown while activating platform stream on channel twilio_voice/events:
MissingPluginException(No implementation found for method listen on channel twilio_voice/events)

When the exception was thrown, this was the stack: 
#0      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:156:7)
<asynchronous suspension>
#1      EventChannel.receiveBroadcastStream.<anonymous closure> (package:flutter/src/services/platform_channel.dart:545:9)
<asynchronous suspension>
====================================================================================================
E/flutter ( 2856): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: MissingPluginException(No implementation found for method tokens on channel twilio_voice/messages)
E/flutter ( 2856): #0      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:156:7)
E/flutter ( 2856): <asynchronous suspension>
E/flutter ( 2856): 
E/flutter ( 2856): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: MissingPluginException(No implementation found for method hasMicPermission on channel twilio_voice/messages)
E/flutter ( 2856): #0      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:156:7)
E/flutter ( 2856): <asynchronous suspension>
E/flutter ( 2856): #1      _PeopleState._buildRow.<anonymous closure> (package:untitled/ui/people/peopleList.dart:308:30)
E/flutter ( 2856): <asynchronous suspension>
E/flutter ( 2856): 

Here is my AndroidMenifest.XML

<!--suppress AndroidDomInspection -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.untitled">
   <application
        android:label="untitled"
        android:icon="@mipmap/ic_launcher">

        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <!-- Displays an Android View that continues showing the launch screen
                 Drawable until Flutter paints its first frame, then this splash
                 screen fades out. A splash screen is useful to avoid any visual
                 gap between the end of Android's launch screen and the painting of
                 Flutter's first frame. -->
            <meta-data
              android:name="io.flutter.embedding.android.SplashScreenDrawable"
              android:resource="@drawable/launch_background"
              />

            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
                <action android:name="FLUTTER_NOTIFICATION_CLICK" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
       <service android:name="com.twilio.twilio_voice.fcm.VoiceFirebaseMessagingService" android:stopWithTask="false">
           <intent-filter>
               <action android:name="com.google.firebase.MESSAGING_EVENT" />
           </intent-filter>
       </service>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
</manifest>

ActiveCall.customParams not available/null when ringing, always returns "null" on Android/iOS for caller & called

Summary:

When calling another device, sending customer params isn't available on called or caller device


Details:

When making a call, I add custom parameters in my app (confirmed in sample app too) with the intention of these parameters being used on the called device. These parameters are never received on the called device. When accessing it on the caller device (the one who added it), the parameters aren't there either.

Sample usage:

Snippet 1

print("starting call to ${_controller.text}");
TwilioVoice.instance.call
    .place(to: _controller.text, from: userId, extraOptions: {
      "hello": "world"
});
pushToCallScreen();

code location

then upon call event on the "other" device, I access it with:

Snippet 2

callStateListener = TwilioVoice.instance.callEventsListener.listen((event) {
  print("voip-onCallStateChanged $event");

  if(TwilioVoice.instance.call.activeCall != null) {
    var customParams = TwilioVoice.instance.call.activeCall!.customParams;
    if(customParams != null) {
      print("call parameters ${customParams}");
    }
  }

code location

Result:
call parameters never has any output

Steps to reproduce:

  1. Setup app with valid tokens
  2. Add custom parameters when placing call (snippet 1)
  3. Add line above to print out custom parameters when any event is received on "other" device (snippet 2)
  4. Run app & call "other" device
  5. observe no instance of "call parameters" being displayed in console/output

Setup: & devices tested on (Android & iOS) in sandbox & production

C:\Users\CybeX\FlutterProjects\twilio_voice\example>flutter doctor -v
[√] Flutter (Channel stable, 2.5.3, on Microsoft Windows [Version 10.0.19043.1288], locale en-ZA)
    • Flutter version 2.5.3 at C:\Users\CybeX\.flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 18116933e7 (2 weeks ago), 2021-10-15 10:46:35 -0700
    • Engine revision d3ea636dc5
    • Dart version 2.14.4

[√] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    • Android SDK at C:\Users\CybeX\AppData\Local\Android\Sdk
    • Platform android-30, build-tools 30.0.3
    • ANDROID_HOME = C:\Users\CybeX\AppData\Local\Android\Sdk
    • ANDROID_SDK_ROOT = C:\Users\CybeX\AppData\Local\Android\Sdk
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)
    • All Android licenses accepted.

[√] Chrome - develop for the web
    • Chrome at C:\Program Files (x86)\Google\Chrome\Application\chrome.exe

[√] Android Studio (version 4.1)
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)

[√] IntelliJ IDEA Ultimate Edition (version 2020.1)
    • IntelliJ at C:\Users\CybeX\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\201.7223.91
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart

[√] IntelliJ IDEA Ultimate Edition (version 2021.1)
    • IntelliJ at C:\Users\CybeX\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\211.7142.45
    • Flutter plugin version 58.0.3
    • Dart plugin version 211.7665

[√] Connected device (6 available)
    • SM N975U1 (mobile) • RF8MC02F23L • android-arm64  • Android 11 (API 30)
    • SM A217F (mobile)  • RF8N51V8G3E • android-arm64  • Android 10 (API 29)
    • iPhone 6s (mobile)  • iOS (13.6.1)
    • iPhone 7 (mobile)  • iOS (14.4.1)
    • SM A217F (mobile)  • RF8N51V8G3E • android-arm64  • Android 10 (API 29)
    • Chrome (web)       • chrome      • web-javascript • Google Chrome 95.0.4638.54
    • Edge (web)         • edge        • web-javascript • Microsoft Edge 95.0.1020.30

• No issues found!

480 Temporarily Unavailable

I'm running the project on two different devices
and cannot place a call to my other device, it rings once and leaves the screen.

What need to do more settings.

  • I already created my TwiML Apps app
  • I configured firebase
  • I configured fcm
  • I created my keys api key
  • I'm generating the accessToken with my android credential

I went to check the call log and for me on twillio it shows

SIP Last Answer
480 temporarily unavailable
The destination number is not answering, or unable to answer. They may be out of service, roaming, or not accepting calls. Often seen when calling wireless phones that don't answer and don't have voicemail.

What more lack of settings ?

Android resource linking failed

I have been building with my emulator and i decided to build a release apk to test on my physical device but I'm getting this error even though i already put the splach_icon in drawable folder, see project tree below

debug sample

flutter doctor
flutter doctor

Execution failed for task ':twilio_voice:verifyReleaseResources'.

A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
1 exception was raised by workers:
com.android.builder.internal.aapt.v2.Aapt2Exception: Android resource linking failed
C:\src\flutter.pub-cache\hosted\pub.flutter-io.cn\twilio_voice-0.0.9\android\src\main\res\layout\activity_answer.xml:17: AAPT: error: resource drawable/splash_icon (aka com.twilio.twilio_voice:drawable/splash_icon) not found.

AndroidManifest.xml the service incomming call notifications

Referente ao README In AndroidManifest.xml the service incomming call notifications:

  <service
      android:name="com.twilio.twilio_voice.fcm.VoiceFirebaseMessagingService"
      android:stopWithTask="false">
      <intent-filter>
          <action android:name="com.google.firebase.MESSAGING_EVENT" />
      </intent-filter>
  </service>

I configured my app to FCM, I manage to trigger a notification for the app is working.
he returns this to me on the terminal but I received the notification:

E/FlutterFcmService(20920): The message was not a valid Twilio Voice SDK payload: {body=recive, title=teste, click_action=com.twilio.twilio_voice.fcm.VoiceFirebaseMessagingService}

I need to configure the android:name= or the app somewhere on twilio or the package does it all by itself?
Thank you for help.

Unable to call from android

I tried calling from android but got the following exception. It was working fine 2 weeks back. I don't know what went wrong. Is my project not building properly now?

E/MethodChannel#twilio_voice/messages: Failed to handle method call
    com.getkeepsafe.relinker.MissingLibraryException: libtwilio_voice_android_so.so
        at com.getkeepsafe.relinker.ApkLibraryInstaller.installLibrary(ApkLibraryInstaller.java:128)
        at com.getkeepsafe.relinker.ReLinkerInstance.loadLibraryInternal(ReLinkerInstance.java:180)
        at com.getkeepsafe.relinker.ReLinkerInstance.loadLibrary(ReLinkerInstance.java:136)
        at com.getkeepsafe.relinker.ReLinker.loadLibrary(ReLinker.java:70)
        at com.getkeepsafe.relinker.ReLinker.loadLibrary(ReLinker.java:51)
        at com.twilio.voice.Voice.loadLibrary(Voice.java:1233)
        at com.twilio.voice.MediaFactory.instance(MediaFactory.java:31)
        at com.twilio.voice.LocalAudioTrack.create(LocalAudioTrack.java:82)
        at com.twilio.voice.LocalAudioTrack.create(LocalAudioTrack.java:29)
        at com.twilio.voice.Voice.connect(Voice.java:573)
        at com.twilio.twilio_voice.TwilioVoicePlugin.onMethodCall(TwilioVoicePlugin.java:404)
        at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:233)
        at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:85)
        at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:818)
        at android.os.MessageQueue.nativePollOnce(Native Method)
        at android.os.MessageQueue.next(MessageQueue.java:335)
        at android.os.Looper.loop(Looper.java:183)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
2021-04-12 16:13:55.207 14779-14825/com.kasper E/flutter: [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: Unhandled error PlatformException(error, libtwilio_voice_android_so.so, null, com.getkeepsafe.relinker.MissingLibraryException: libtwilio_voice_android_so.so
        at com.getkeepsafe.relinker.ApkLibraryInstaller.installLibrary(ApkLibraryInstaller.java:128)
        at com.getkeepsafe.relinker.ReLinkerInstance.loadLibraryInternal(ReLinkerInstance.java:180)
        at com.getkeepsafe.relinker.ReLinkerInstance.loadLibrary(ReLinkerInstance.java:136)
        at com.getkeepsafe.relinker.ReLinker.loadLibrary(ReLinker.java:70)
        at com.getkeepsafe.relinker.ReLinker.loadLibrary(ReLinker.java:51)
        at com.twilio.voice.Voice.loadLibrary(Voice.java:1233)
        at com.twilio.voice.MediaFactory.instance(MediaFactory.java:31)
        at com.twilio.voice.LocalAudioTrack.create(LocalAudioTrack.java:82)
        at com.twilio.voice.LocalAudioTrack.create(LocalAudioTrack.java:29)
        at com.twilio.voice.Voice.connect(Voice.java:573)
        at com.twilio.twilio_voice.TwilioVoicePlugin.onMethodCall(TwilioVoicePlugin.java:404)
        at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:233)
        at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:85)
        at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:818)
        at android.os.MessageQueue.nativePollOnce(Native Method)
        at android.os.MessageQueue.next(MessageQueue.java:335)
        at android.os.Looper.loop(Looper.java:183)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

Question: I can make calls but not receive them, what am I missing?

Hello I'm using the package and works like charm, but I'm not very sure if my configuration to receive calls is right, I subscribe to firebase and get my fcm token and with the TwilioVoice method .setTokens I subscribe my deviceToken and accessToken without problems. If I make the call everything works perfectly but I wasn't able to receive the calls. I suppose that it has to be with this method TwilioVoice.instance.registerClient(clientId, "app"), which clientId do I have to use to register the client. Sorry if the question is very obvious but I'm kind of new in the twilio platform and I don't know what is the real problem. Thanks in advance and great work with this package!

functions.js help

I know that inside the root of the project there is a file called
functions.js
which is responsible for generating the access token.
how do i make it work and consume the data from this js file?

How to change/override UI for receiving calls.

Hi,

Is there a way I can change/override UI for receiving calls and use "voip-customData" to be appear as Caller name on the incoming call and in the notifications for missed calls on android.

Thanks

Question: Getting custom parameters?

Hi there,

Really love the simplicity of integration. I manage to get the call on both platforms but cannot find a way to get custom parameters from Call or ActiveCall like we can do with native api. Any suggestion would be grateful.

Thanks 🙏🏻

Unable to open own screen after answring the call at reveicer side

`TwilioVoice.instance.callEventsListener.listen((event) {
switch (event) {
case CallEvent.answer:
try{
// TwilioVoice.instance.showBackgroundCallUI();
// ShowToast.show('Answer');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AudioCall(
token: "",
clientId: "",
isCall: false,
)));
}catch(e){
print("error --> $e");
}

      break;
    case CallEvent.ringing:
      {
        ShowToast.show('Ringing');
        break;
      }
    case CallEvent.connected:
      break;
    case CallEvent.callEnded:
      ShowToast.show('callEnded');
      Navigator.pop(context);
      break;
    case CallEvent.unhold:
    // TODO: Handle this case.
      break;
    case CallEvent.hold:
    // TODO: Handle this case.
      break;
    case CallEvent.unmute:
    // TODO: Handle this case.
      break;
    case CallEvent.mute:
    // TODO: Handle this case.
      break;
    case CallEvent.speakerOn:
    // TODO: Handle this case.
      break;
    case CallEvent.speakerOff:
    // TODO: Handle this case.
      break;
    case CallEvent.log:
    // TODO: Handle this case.
      break;
  }
});`

I can't answer a call when app is killed

Hi, I'm using the demo app. I make the configurations and work the app when is in first plane and second plane but when I close the app I receive the notification and try to answer nothing happen. The app doesn't start and I can't answer the call

Release apk crashes when user initiates call

I've been building on my emulator for a couple of days and i decided to build a release apk to test on my physical device but it crashes when i try to make a call but the strange thing is that it works fine on my emulator. I tried using the proguard.pro file that was provided in a previous issue that was raised but still app would crash when i navigate to screen to initiate call

below is the proguard.pro file i used

## Flutter wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.**  { *; }
-keep class io.flutter.util.**  { *; }
-keep class io.flutter.view.**  { *; }
-keep class io.flutter.**  { *; }
-keep class io.flutter.plugins.**  { *; }
-dontwarn io.flutter.embedding.**

# Twilio Programmable Voice
-keep class com.twilio.** { *; }
-keep class tvo.webrtc.** { *; }
-dontwarn tvo.webrtc.**
-keep class com.twilio.voice.** { *; }
-keepattributes InnerClasses

#twilio
-keep class com.twilio.** { *; }
-keep class tvo.webrtc.** { *; }
-dontwarn tvo.webrtc.**
-keep class com.twilio.voice.** { *; }
-keepattributes InnerClasses

## Gson rules
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }

# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}

Ringing - From: null, To: null, Direction: CallDirection.outgoing

: Ringing - From: null, To: null, Direction: CallDirection.outgoing
I/flutter (11315): voip-onCallStateChanged CallEvent.ringing
I/flutter (11315): ringing
D/TwilioVoicePlugin(11315): Connect failure
E/TwilioVoicePlugin(11315): Call Error: 31603, Decline
I/flutter (11315): Call Error: 31603, Decline

backend

for helping any one have problem in backend function that used in twilio app
make call to this function
const VoiceResponse = require('twilio').twiml.VoiceResponse;

exports.makeCall = functions.https.onRequest(async(request, response) => {
try{
console.log('Responseto:' + request.body.To);
console.log('Responsefrom:' + request.body.From);
var to = null;var from=null;
if (request.method == 'POST') {
{
to = request.body.To;
from = request.body.From.replace('client:','');
}
} else {
to = request.query.to;
}
console.log('Responsetossss:' + to);
console.log('Responsefromsss:' + from);
const voiceResponse = new VoiceResponse();

    const dial = voiceResponse.dial({callerId : from});
    dial.client(to);
    console.log('Responseto:' + voiceResponse.toString());
    return response.send(voiceResponse.toString());

} catch (error) {
console.log(error);
return res.status(400).json({
message: "Error",
data: error,
});
}
});

check if the accessToken

Good morning,

I would like to know if the package offers any function when pressing the call button.
check if the accessToken is expired?

in the function below how do I pass my new changed token?
I tried to pass it as a parameter but it doesn't accept.

String newTokenChange = apinewToken;
 TwilioVoice.instance.setOnDeviceTokenChanged((newTokenChange) {
  print("voip-device token changed");
});

AUDIO_OUTPUT_FLAG_FAST denied by server;

When triggering the call function to my server:

TwilioVoice.instance.call.place(
      to: 'qrMJuS8DpYQGZyF6dxJ8cYc83X53',
      from: userId!,
);

It gives me this error on the console:
D/AudioTrack(23061): createTrack_l(121): AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount 0 -> 22368

I'm having trouble getting calls when the app is closed in the background.

Good afternoon.

I'm having trouble getting calls when the app is closed in the background.

Situation:
I close the entire app in the background (shut down), When I get a call from a device it shows the notification that they are calling me and shows the notification with the option 'Answer'.

When clicking 'Answer' instead of opening the app and showing the call screen, nothing happens.

video for exemple: https://drive.google.com/file/d/1IQ9_UO_u7V7AmgwOPA5E2x8SF6Zgs5aV/view?usp=sharing

[firebase_functions/not-found] NOT_FOUND

In function :

 registerUser() async {
    print("voip-registtering with token ");
    print("voip-calling voice-accessToken");
    final function =
        FirebaseFunctions.instance.httpsCallable("voice-accessToken");
    print(function);

    final data = {
      "platform": Platform.isIOS ? "iOS" : "Android",
    };

    final result = await function.call(data);

Exception :

Exception has occurred.
FirebaseFunctionsException ([firebase_functions/not-found] NOT_FOUND

My pubspec.yaml:
cloud_functions: ^3.0.2
https://i.ibb.co/9GkkK0d/Captura-de-Tela-2021-10-14-a-s-20-18-30.png

Android resource linking failed

I have put splash_icon to drawable but errror still happens :(

  • What went wrong:
    Execution failed for task ':twilio_voice:verifyReleaseResources'.

A failure occurred while executing com.android.build.gradle.tasks.VerifyLibraryResourcesTask$Action
Android resource linking failed
ERROR:/Users/apple/.pub-cache/hosted/pub.dartlang.org/twilio_voice-0.0.9/android/src/main/res/layout/activity_answer.xml:31: AAPT: error: resource drawable/splash_icon (aka com.twilio.twilio_voice:drawable/splash_icon) not found.

 ERROR:/Users/apple/.pub-cache/hosted/pub.dartlang.org/twilio_voice-0.0.9/android/src/main/res/layout/activity_background_call.xml:32: AAPT: error: resource drawable/splash_icon (aka com.twilio.twilio_voice:drawable/splash_icon) not found.

Not able to receive own notification which are sending from own server

I am not able to receive notification from the server if i register
<service android:name="com.twilio.twilio_voice.fcm.VoiceFirebaseMessagingService" android:stopWithTask="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service>

this service in the project AndroidManifest.xnl file or if i don't register this service in AndroidManifest.xml then i am not getting call notification. How i solve this case please provide some solution.

Sample app automatically declines call [31603] after few seconds with no call notification on callee (regardless of setup)

Summary:

Calling another device using sample app (most recent, #e3a45f2) results in call declined 31603 - Twilio Error


Details:

Using the example app (most recent commit, 7 days ago as of writing), when attempting to call a different device the call declines automatically after a few seconds.

First device with a Twilio generated token (using twilio-cli) and unique identity calls another device with a Twilio token and unique identity. The calling device goes to the calling screen and awaits a response from the called device. The caller device will report call declined with error 31603 indicating the callee declined the call.

The problem is the callee never receives a notification to decline.

(see logs, exact steps to reproduce, exact twilio setup, and system setup including devices below)


Steps to reproduce:

  1. Clone repo
  2. Insert own google-services.json (replace all package names in Android/ to that of own package - 5 replacements)
  3. (remove/comment server function for getting access-token, lines 50-60), replace with generated token and send with accessToken to setTokens()
  4. build & run app with one token on 1 device, run app with another token on a different device
  5. call 1 device from another
  6. calling device goes to calling screen (blue screen with buttons), after a few seconds ~5, log output shows call declined.

Twilio setup: (steps from here)

  1. run npm install -g twilio-cli
  2. run twilio login
  3. create sample app using
twilio api:core:applications:create \
    --friendly-name=my-twiml-app \
    --voice-method=POST \
    --voice-url="https://my-quickstart-dev.twil.io/make-call"
  1. generate tokens using twilio token:voice --identity=alice --voice-app-sid=APxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (change identity for each) - use these tokens in Steps to Reproduce

Log Outputs

Caller:

Launching lib\main.dart on SM N975U1 in debug mode...
Running Gradle task 'assembleDebug'...
√  Built build\app\outputs\flutter-apk\app-debug.apk.
Installing build\app\outputs\flutter-apk\app.apk...
W/FlutterActivityAndFragmentDelegate(22008): A splash screen was provided to Flutter, but this is deprecated. See flutter.dev/go/android-splash-migration for migration steps.
Debug service listening on ws://127.0.0.1:50164/C2Rr95Zzwzs=/ws
Syncing files to device SM N975U1...
D/TwilioVoicePlugin(22008): Is on call invoked
I/TwilioVoicePlugin(22008): Setting event sink
I/flutter (22008): registering user OD0YVvv5s3MJ9huCY1oE01UxokM2
I/flutter (22008): voip- service init
I/flutter (22008): voip-registtering with token 
I/flutter (22008): checkActiveCall false
I/flutter (22008): androidToken is cFcVwD44TnqrNziPVdpWJY:APA91bEGlGX1i_UdOxKwTu9gvDipJGH8hVQMSD2Kpuk2yMkVqPtsIbI4Fq4jzpb9Vccu6rK13hK7Llz1YkByAp_ti7FQnxfrb_Og9jtnuIStT1i1gaP5svFON4rcv2IYeJ2oGjW7cv6V
D/TwilioVoicePlugin(22008): Setting up tokens
I/TwilioVoicePlugin(22008): Registering with FCM
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
D/TwilioVoicePlugin(22008): Successfully registered FCM cFcVwD44TnqrNziPVdpWJY:APA91bEGlGX1i_UdOxKwTu9gvDipJGH8hVQMSD2Kpuk2yMkVqPtsIbI4Fq4jzpb9Vccu6rK13hK7Llz1YkByAp_ti7FQnxfrb_Og9jtnuIStT1i1gaP5svFON4rcv2IYeJ2oGjW7cv6V
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/ViewRootImpl@65dc161[MainActivity](22008): ViewPostIme pointer 0
I/ViewRootImpl@65dc161[MainActivity](22008): ViewPostIme pointer 1
D/InputMethodManager(22008): SSI - flag : 0 Pid : 22008 view : io.awesome.app
D/InputMethodManager(22008): view is not EditText
D/InputMethodManager(22008): prepareNavigationBarInfo() DecorView@bff4be5[MainActivity]
D/InputMethodManager(22008): getNavigationBarColor() -16711423
V/InputMethodManager(22008): Starting input: tba=io.awesome.app ic=io.flutter.plugin.editing.InputConnectionAdaptor@cef6754 mNaviBarColor -16711423 mIsGetNaviBarColorSuccess true , NavVisible : true , NavTrans : false
D/InputMethodManager(22008): startInputInner - Id : 0
I/InputMethodManager(22008): startInputInner - mService.startInputOrWindowGainedFocus
D/InputTransport(22008): Input channel constructed: 'ClientS', fd=125
D/InputTransport(22008): Input channel destroyed: 'ClientS', fd=109
D/InputMethodManager(22008): prepareNavigationBarInfo() DecorView@bff4be5[MainActivity]
D/InputMethodManager(22008): getNavigationBarColor() -16711423
D/InputMethodManager(22008): prepareNavigationBarInfo() DecorView@bff4be5[MainActivity]
D/InputMethodManager(22008): getNavigationBarColor() -16711423
V/InputMethodManager(22008): Starting input: tba=io.awesome.app ic=io.flutter.plugin.editing.InputConnectionAdaptor@15dfff2 mNaviBarColor -16711423 mIsGetNaviBarColorSuccess true , NavVisible : true , NavTrans : false
D/InputMethodManager(22008): startInputInner - Id : 0
I/InputMethodManager(22008): startInputInner - mService.startInputOrWindowGainedFocus
D/InputTransport(22008): Input channel constructed: 'ClientS', fd=124
D/InputTransport(22008): Input channel destroyed: 'ClientS', fd=125
W/IInputConnectionWrapper(22008): getTextBeforeCursor on inactive InputConnection
W/IInputConnectionWrapper(22008): getTextAfterCursor on inactive InputConnection
W/IInputConnectionWrapper(22008): getCursorCapsMode on inactive InputConnection
W/IInputConnectionWrapper(22008): getExtractedText on inactive InputConnection
W/IInputConnectionWrapper(22008): requestCursorAnchorInfo on inactive InputConnection
W/IInputConnectionWrapper(22008): getExtractedText on inactive InputConnection
W/IInputConnectionWrapper(22008): getExtractedText on inactive InputConnection
D/InputConnectionAdaptor(22008): The input method toggled cursor monitoring on
D/InsetsSourceConsumer(22008): setRequestedVisible: visible=true, type=13, host=io.awesome.app/io.awesome.app.MainActivity, from=android.view.InsetsSourceConsumer.show:229 android.view.InsetsController.showDirectly:1437 android.view.InsetsController.controlAnimationUnchecked:1110 android.view.InsetsController.applyAnimation:1417 android.view.InsetsController.show:962 android.view.ViewRootImpl$ViewRootHandler.handleMessage:6146 android.os.Handler.dispatchMessage:106 android.os.Looper.loop:246 android.app.ActivityThread.main:8595 java.lang.reflect.Method.invoke:-2 
I/ViewRootImpl@65dc161[MainActivity](22008): MSG_RESIZED_REPORT: frame=(0,0,1080,2280) ci=(0,85,0,981) vi=(0,85,0,981) or=1
I/SurfaceControl(22008): nativeRelease nativeObject s[-5476376615171385792]
I/SurfaceControl(22008): nativeRelease nativeObject e[-5476376615171385792]
I/SurfaceControl(22008): assignNativeObject: nativeObject = 0 Surface(name=null)/@0xbd4129d / android.view.SurfaceControl.readFromParcel:1117 android.view.IWindowSession$Stub$Proxy.relayout:1820 android.view.ViewRootImpl.relayoutWindow:9005 android.view.ViewRootImpl.performTraversals:3360 android.view.ViewRootImpl.doTraversal:2618 android.view.ViewRootImpl$TraversalRunnable.run:9971 android.view.Choreographer$CallbackRecord.run:1010 android.view.Choreographer.doCallbacks:809 android.view.Choreographer.doFrame:744 android.view.Choreographer$FrameDisplayEventReceiver.run:995 
I/SurfaceControl(22008): nativeRelease nativeObject s[-5476376615171176704]
I/SurfaceControl(22008): nativeRelease nativeObject e[-5476376615171176704]
I/SurfaceControl(22008): nativeRelease nativeObject s[-5476376615171430672]
I/SurfaceControl(22008): nativeRelease nativeObject e[-5476376615171430672]
I/SurfaceControl(22008): nativeRelease nativeObject s[-5476376615171436304]
I/SurfaceControl(22008): nativeRelease nativeObject e[-5476376615171436304]
I/ViewRootImpl@65dc161[MainActivity](22008): Relayout returned: old=(0,0,1080,2280) new=(0,0,1080,2280) req=(1080,2280)0 dur=7 res=0x1 s={true -5476376610876762032} ch=false fn=35
I/SurfaceControl(22008): nativeRelease nativeObject s[-5476376615171183744]
I/SurfaceControl(22008): nativeRelease nativeObject e[-5476376615171183744]
D/InputConnectionAdaptor(22008): The input method toggled text monitoring on
D/InputMethodManager(22008): SSI - flag : 0 Pid : 22008 view : io.awesome.app
D/InputMethodManager(22008): view is not EditText
D/InputMethodManager(22008): prepareNavigationBarInfo() DecorView@bff4be5[MainActivity]
D/InputMethodManager(22008): getNavigationBarColor() -16711423
D/InputMethodManager(22008): SSI - flag : 0 Pid : 22008 view : io.awesome.app
D/InputMethodManager(22008): view is not EditText
D/InputMethodManager(22008): prepareNavigationBarInfo() DecorView@bff4be5[MainActivity]
D/InputMethodManager(22008): getNavigationBarColor() -16711423
D/InputMethodManager(22008): SSI - flag : 0 Pid : 22008 view : io.awesome.app
D/InputMethodManager(22008): view is not EditText
D/InputMethodManager(22008): prepareNavigationBarInfo() DecorView@bff4be5[MainActivity]
D/InputMethodManager(22008): getNavigationBarColor() -16711423
D/InputMethodManager(22008): SSI - flag : 0 Pid : 22008 view : io.awesome.app
D/InputMethodManager(22008): view is not EditText
D/InputMethodManager(22008): prepareNavigationBarInfo() DecorView@bff4be5[MainActivity]
D/InputMethodManager(22008): getNavigationBarColor() -16711423
I/ViewRootImpl@65dc161[MainActivity](22008): ViewPostIme pointer 0
I/ViewRootImpl@65dc161[MainActivity](22008): ViewPostIme pointer 1
I/flutter (22008): checkPermissionForMicrophone
I/flutter (22008): voip-onCallStateChanged CallEvent.log
I/flutter (22008): starting call to john
D/TwilioVoicePlugin(22008): Making new call
D/TwilioVoicePlugin(22008): calling to john
I/flutter (22008): Making new call
I/flutter (22008): voip-onCallStateChanged CallEvent.log
I/tvo.webrtc.Logging(22008): WebRtcAudioManagerExternal: Sample rate is set to 48000 Hz
I/tvo.webrtc.Logging(22008): WebRtcAudioManagerExternal: Sample rate is set to 48000 Hz
I/tvo.webrtc.Logging(22008): JavaAudioDeviceModule: createAudioDeviceModule
I/tvo.webrtc.Logging(22008): JavaAudioDeviceModule: HW NS will be used.
I/tvo.webrtc.Logging(22008): JavaAudioDeviceModule: HW AEC will be used.
I/tvo.webrtc.Logging(22008): WebRtcAudioEffectsExternal: ctor@[name=main, id=2]
I/tvo.webrtc.Logging(22008): WebRtcAudioRecordExternal: ctor@[name=main, id=2]
I/tvo.webrtc.Logging(22008): WebRtcAudioTrackExternal: ctor@[name=main, id=2]
I/tvo.webrtc.Logging(22008): WebRtcAudioRecordExternal: enableBuiltInAEC(true)
I/tvo.webrtc.Logging(22008): WebRtcAudioEffectsExternal: setAEC(true)
I/tvo.webrtc.Logging(22008): WebRtcAudioRecordExternal: enableBuiltInNS(true)
I/tvo.webrtc.Logging(22008): WebRtcAudioEffectsExternal: setNS(true)
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
D/InputMethodManager(22008): HSIFW - flag : 0
D/InsetsSourceConsumer(22008): setRequestedVisible: visible=false, type=13, host=io.awesome.app/io.awesome.app.MainActivity, from=android.view.InsetsSourceConsumer.hide:236 android.view.ImeInsetsSourceConsumer.hide:101 android.view.InsetsController.hideDirectly:1430 android.view.InsetsController.controlAnimationUnchecked:1112 android.view.InsetsController.applyAnimation:1417 android.view.InsetsController.hide:984 android.view.ViewRootImpl$ViewRootHandler.handleMessage:6150 android.os.Handler.dispatchMessage:106 android.os.Looper.loop:246 android.app.ActivityThread.main:8595 
D/InputConnectionAdaptor(22008): The input method toggled cursor monitoring off
D/InputConnectionAdaptor(22008): The input method toggled text monitoring off
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/SurfaceControl(22008): nativeRelease nativeObject s[-5476376615171049104]
I/SurfaceControl(22008): nativeRelease nativeObject e[-5476376615171049104]
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
D/TwilioVoicePlugin(22008): onRinging
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/flutter (22008): Ringing - From: null, To: null, Direction: CallDirection.outgoing
I/flutter (22008): voip-onCallStateChanged CallEvent.ringing
I/flutter (22008): Ringing - From: null, To: null, Direction: CallDirection.outgoing
I/flutter (22008): voip-onCallStateChanged CallEvent.ringing
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(22008): (HTTPLog)-Static: isSBSettingEnabled false
D/TwilioVoicePlugin(22008): Connect failure
E/TwilioVoicePlugin(22008): Call Error: 31603, Decline
I/flutter (22008): Call Error: 31603, Decline
I/flutter (22008): voip-onCallStateChanged CallEvent.log
I/flutter (22008): Call Error: 31603, Decline
I/flutter (22008): voip-onCallStateChanged CallEvent.log

Log output of called device:

Launching lib\main.dart on SM A217F in debug mode...
Running Gradle task 'assembleDebug'...
√  Built build\app\outputs\flutter-apk\app-debug.apk.
W/FlutterActivityAndFragmentDelegate(24996): A splash screen was provided to Flutter, but this is deprecated. See flutter.dev/go/android-splash-migration for migration steps.
Debug service listening on ws://127.0.0.1:50028/Y0MAsGyC4OI=/ws
Syncing files to device SM A217F...
D/TwilioVoicePlugin(24996): Is on call invoked
I/TwilioVoicePlugin(24996): Setting event sink
I/flutter (24996): registering user 3Spxch0wLhfR7o9kaGPGd8sEWTx2
I/flutter (24996): voip- service init
I/flutter (24996): voip-registtering with token 
I/flutter (24996): checkActiveCall false
I/flutter (24996): androidToken is eZM27oZGS-eMl9WtSH4cVO:APA91bF2ZROdfR0LVyDhd0pop4IcRealjjmBux6nyhhZ5xZGsTnx_32PuBZXds8i3E8ZvdaaZxSJoTufJA_ogt5-DOHu4ORtPtH98PzQjcGhrWbu_HfVbZ1nMBoeFGSAzZTD0b8uZAEy
D/TwilioVoicePlugin(24996): Setting up tokens
I/TwilioVoicePlugin(24996): Registering with FCM
D/NetworkSecurityConfig(24996): No Network Security Config specified, using platform default
I/System.out(24996): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(24996): (HTTPLog)-Static: isSBSettingEnabled false
D/TwilioVoicePlugin(24996): Successfully registered FCM eZM27oZGS-eMl9WtSH4cVO:APA91bF2ZROdfR0LVyDhd0pop4IcRealjjmBux6nyhhZ5xZGsTnx_32PuBZXds8i3E8ZvdaaZxSJoTufJA_ogt5-DOHu4ORtPtH98PzQjcGhrWbu_HfVbZ1nMBoeFGSAzZTD0b8uZAEy
I/System.out(24996): (HTTPLog)-Static: isSBSettingEnabled false
I/System.out(24996): (HTTPLog)-Static: isSBSettingEnabled false

Setup: & devices tested on (Android only)

C:\Users\CybeX\FlutterProjects\twilio_voice\example>flutter doctor -v
[√] Flutter (Channel stable, 2.5.3, on Microsoft Windows [Version 10.0.19043.1288], locale en-ZA)
    • Flutter version 2.5.3 at C:\Users\CybeX\.flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 18116933e7 (2 weeks ago), 2021-10-15 10:46:35 -0700
    • Engine revision d3ea636dc5
    • Dart version 2.14.4

[√] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    • Android SDK at C:\Users\CybeX\AppData\Local\Android\Sdk
    • Platform android-30, build-tools 30.0.3
    • ANDROID_HOME = C:\Users\CybeX\AppData\Local\Android\Sdk
    • ANDROID_SDK_ROOT = C:\Users\CybeX\AppData\Local\Android\Sdk
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)
    • All Android licenses accepted.

[√] Chrome - develop for the web
    • Chrome at C:\Program Files (x86)\Google\Chrome\Application\chrome.exe

[√] Android Studio (version 4.1)
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)

[√] IntelliJ IDEA Ultimate Edition (version 2020.1)
    • IntelliJ at C:\Users\CybeX\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\201.7223.91
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart

[√] IntelliJ IDEA Ultimate Edition (version 2021.1)
    • IntelliJ at C:\Users\CybeX\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\211.7142.45
    • Flutter plugin version 58.0.3
    • Dart plugin version 211.7665

[√] Connected device (4 available)
    • SM N975U1 (mobile) • RF8MC02F23L • android-arm64  • Android 11 (API 30)
    • SM A217F (mobile)  • RF8N51V8G3E • android-arm64  • Android 10 (API 29)
    • Chrome (web)       • chrome      • web-javascript • Google Chrome 95.0.4638.54
    • Edge (web)         • edge        • web-javascript • Microsoft Edge 95.0.1020.30

• No issues found!

how does receiving a call to a number that appears with the answering layout works

TwilioVoice .instance.call. local (FROM: _controller.text, to : userId);

To work INTERNALLY the connection and be able to see on the other device playing in the background with a layout of the app itself, what is needed? The other device must have the app downloaded. o From: expect user id authenticated by firebase ?how does this work? thanks.

Registering a client just hangs and fails to register a client such as firebase Id -> [insert name]

Summary:

Trying to call registerClient(String, String) (source) hangs forever and does not register a client resulting in the caller remaining "Unknown Caller", this is reflected in Twilio logs.


Details:

In an attempt to make use of registering a client preventing an "unknown caller" from showing, I attempt to register a user using the example application provided.

Calling the registerClient(String, String) doesn't ever complete, it simply hangs:

TwilioVoice.instance.registerClient("oJXmE0xlwiVoRICPAFdEQq2E4qk2", "Test User").then((value) {
  print("Test User registered");
}).catchError((error) {
  print("Failed to register Test User");
  print(error);
});

this can also be observed by using async/await which will hang forever and never return continuing execution.

To observe log output, I use:

Timer(Duration(seconds: 5), () async{
  await TwilioVoice.instance.registerClient("oJXmE0xlwiVoRICPAFdEQq2E4qk2", "Test User");
});

Twilio make-call function's log output:

Execution started...

// payload
{
   "ApplicationSid":"APxxx...",
   "ApiVersion":"2010-04-01",
   "Called":"",
   "Caller":"client:oJXmE0xlwiVoRICPAFdEQq2E4qk2",
   "CallStatus":"ringing",
   "CallSid":"CA....",
   "From":"client:oJXmE0xlwiVoRICPAFdEQq2E4qk2",
   "To":"4STs40rd4teTLyLSiDtqqkNwJhu1",
   "hello":"world"
   "Direction":"inbound",
   "AccountSid":"AC.xxx...."
}

// calling [event.From] -> [event.to or event.To]
Calling [client:oJXmE0xlwiVoRICPAFdEQq2E4qk2] -> [4STs40rd4teTLyLSiDtqqkNwJhu1]

// end
Execution ended in 50.38ms using 102MB

Steps to reproduce:

  1. Clone sample app
  2. setup app with firebase, generate Twilio tokens to use for testing
  3. manually assign 2 devices' firebase IDs with unique names e.g. registerClient(firebaseId, "Unique User Name")
  4. run app on 2 seperate physical devices, each having unique tokens & names
  5. call 1 device from another, observe no name/caller name change.

Setup: & devices tested on (Android & iOS) in sandbox & (Android) production

C:\Users\CybeX\FlutterProjects\twilio_voice\example>flutter doctor -v
[√] Flutter (Channel stable, 2.5.3, on Microsoft Windows [Version 10.0.19043.1288], locale en-ZA)
    • Flutter version 2.5.3 at C:\Users\CybeX\.flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 18116933e7 (2 weeks ago), 2021-10-15 10:46:35 -0700
    • Engine revision d3ea636dc5
    • Dart version 2.14.4

[√] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    • Android SDK at C:\Users\CybeX\AppData\Local\Android\Sdk
    • Platform android-30, build-tools 30.0.3
    • ANDROID_HOME = C:\Users\CybeX\AppData\Local\Android\Sdk
    • ANDROID_SDK_ROOT = C:\Users\CybeX\AppData\Local\Android\Sdk
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)
    • All Android licenses accepted.

[√] Chrome - develop for the web
    • Chrome at C:\Program Files (x86)\Google\Chrome\Application\chrome.exe

[√] Android Studio (version 4.1)
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)

[√] IntelliJ IDEA Ultimate Edition (version 2020.1)
    • IntelliJ at C:\Users\CybeX\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\201.7223.91
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart

[√] IntelliJ IDEA Ultimate Edition (version 2021.1)
    • IntelliJ at C:\Users\CybeX\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\211.7142.45
    • Flutter plugin version 58.0.3
    • Dart plugin version 211.7665

[√] Connected device (6 available)
    • SM N975U1 (mobile) • RF8MC02F23L • android-arm64  • Android 11 (API 30)
    • SM A217F (mobile)  • RF8N51V8G3E • android-arm64  • Android 10 (API 29)
    • iPhone 6s (mobile)  • iOS (13.6.1)
    • iPhone 7 (mobile)  • iOS (14.4.1)
    • SM A217F (mobile)  • RF8N51V8G3E • android-arm64  • Android 10 (API 29)
    • Chrome (web)       • chrome      • web-javascript • Google Chrome 95.0.4638.54
    • Edge (web)         • edge        • web-javascript • Microsoft Edge 95.0.1020.30

• No issues found!

Connection failed. Not found

D/TwilioVoicePlugin( 6779): Connect failure
E/TwilioVoicePlugin( 6779): Call Error: 31404, Not Found

I am trying to call a physical number, and I am using the correct access token but don't know why I am facing this error.

Can't deploy firebase functions

i used the example firebase function you posted but i keep getting the error below.
Unhandled error ReferenceError: helpers is not defined
at /workspace/index.js:7:20
at fixedLen (/workspace/node_modules/firebase-functions/lib/providers/https.js:66:41)
at /workspace/node_modules/firebase-functions/lib/common/providers/https.js:322:32
at processTicksAndRejections (internal/process/task_queues.js:95:5)

i don't really know much about cloud functions but it seems i'm missing and import statement. please help

Question: How to make it use iOS CallKit

I can make Twilio calls, but on iOS it is not using CallKit.
I notice in the example, a Flutter screen is pushed during calls, but looking at the
iOS code (for the plugin) it seems to be integrated with CallKit.
Do I have to set any parameters in the code, or add anything to the iOS project (I've already set 'Audio, Airplay, and Picture in Picture' and 'Voice over IP' in the Info.plist).

pushCredentialSid

I was checking the repository's function.js file to compare how you generate the accessToken for an identifier and I noticed that you're passing the pushCredentialSid method something I wasn't doing on my server.

According to your function below you check the device if it is android : android_push_sid else ios_push_sid

how was a Sid generated for android and ios?
because following the twillio documentation:
https://www.twilio.com/docs/conversations/android/push-notifications

They pass it like pushCredentialSid is the console's Push Crendecial configured just right, so it's just ONE pushCredentialSid, ​​not one for android and one for ios.
Thank you for me help.

 if (payload.platform === 'iOS') {
    console.log('creating access token for iOS');
    pushCredSid = payload.production ? twilioConfig.apple_push_sid_release
      : (twilioConfig.apple_push_sid_debug || twilioConfig.apple_push_sid_release);
  } else {
    console.log('creating access token for Android');
    pushCredSid = twilioConfig.android_push_sid;
  }

I couldn't make the call to another device

Guy checks if I'm making the correct flow with the package because I couldn't make the call to another device:

I passed the accessToken I generated from the userId user and the FCM token from firebase.

TwilioVoice.instance.setTokens(
        accessToken: tokenDeAcesso.acessToken!, //generated the token passing the userId as identity
        deviceToken: tokenFCM,
      );

With that I register the user and the other specifications in the readme.

TwilioVoice.instance.registerClient(user.uid, user.email!);

My doubt is:

When I call the function below, I'm passing in the TO the userId that I want to call because it was with the userId that I generated the access token in my server, so it's okay for me to call the identifier that I generated the token in my case the userId ?

Is this how I need to make the call for other devices?
for me it opens the CallScreen() and in that it taps once and closes the screen and does not generate logs for me on the twilio console

TwilioVoice.instance.call.place(
     to: '1HEeyqFBszXnfXO5n03Aj2Ti9Dw2',
      from: userId!,
);

Unknow Caller in screen background

Good Morning.

Well my doubt is.

I'm using the function passed in readme
TwilioVoice.instance.registerClient(user.uid, 'Victor');

to register the client and even so when I receive the call from the other device it doesn't show the name I registered in this function, which would be the client's name to show his name on the screen in the background.
result: Unknow Caller

I'm registering as follows.
is the registerClient function really responsible for this action?

} else if (!registered) {
        registered = true;
        this.userId = user.uid;
        TwilioVoice.instance.registerClient(user.uid, 'celular');
        registerUser();
      }

stack Overflow

He will only show me who is calling me only when I accept the call, I accept, he shows the ID.
the correct shouldn't show before?

stack Overflow

I can not recive calls

during a call I receive a message in the logs: broadcast received for message.
Condition of CallEvent.answer is not performed:

void waitForCall() {
    checkActiveCall();
    TwilioVoice.instance.callEventsListener
      ..listen((event) {
        log("voip-onCallStateChanged $event");

        switch (event) {
          case CallEvent.answer:
            log("answer");
            if (Platform.isIOS && state == null ||
                state == AppLifecycleState.resumed) {
              pushToAnswerScreen();
              hasPushedToCall = true;
            }
            break;
          case CallEvent.ringing:
            log("ringing");
            final activeCall = TwilioVoice.instance.call.activeCall;
            if (activeCall != null) {
              final customData = activeCall.customParams;
              if (customData != null) {
                log("voip-customData $customData");
              }
            }
            break;
          case CallEvent.connected:
            log("connected");
            if (Platform.isAndroid &&
                TwilioVoice.instance.call.activeCall.callDirection ==
                    CallDirection.incoming) {
              if (state != AppLifecycleState.resumed) {
                TwilioVoice.instance.showBackgroundCallUI();
              } else if (state == null || state == AppLifecycleState.resumed) {
                pushToCallScreen();
                hasPushedToCall = true;
              }
            }
            break;
          case CallEvent.callEnded:
            log("callEnded");
            hasPushedToCall = false;
            break;
          case CallEvent.returningCall:
            log("returningCall");
            pushToCallScreen();
            hasPushedToCall = true;
            break;
          default:
            break;
        }
      });
  }

Also I noticed: Class referenced in the manifest, com.twilio.twilio_voice.fcm.VoiceFirebaseMessagingService, was not found in the project or the libraries.

Unable to receive notifications on Android

Expected Behaviour

Receive / Handle calls when being called directly from a client. Call screen should be brought up when incoming call detected.

Actual Behaviour

Nothing happens. Based from logs, I can get the FCM message but its not being handled by the sample flutter project.

Misc

I've started playing around with the library, specifically on Android, and I'm able to make outbound calls just fine but when I try to do inbound calls, for a lack of a better word, it gets ignored by the app. Already have the proper FCM related setup finished; google-services.json placed under android/app, all the app_id changed to the correct one.

Ringing and Answer event not working at caller side

I found that the connected and callEnded events are working fine at caller side but ringing and answer are not working ..

void registerEvents() { /// callStateListener = TwilioVoice.instance.callEventsListener.listen((event) { print("Event ======================>>>>>>>>>>>>${event.toString()}"); switch (event) { case CallEvent.callEnded: Navigator.pop(context); ShowToast.show('callEnded'); break; case CallEvent.ringing: CallData().setStatus("Ringing..."); break; case CallEvent.answer: CallData().setStatus("Connected"); break; case CallEvent.connected: break; case CallEvent.unhold: case CallEvent.hold: case CallEvent.unmute: case CallEvent.mute: case CallEvent.speakerOn: case CallEvent.speakerOff: break; case CallEvent.log: break; } }); }

AUDIO_OUTPUT_FLAG_FAST denied, rates do not match 44100 Hz, require 48000 Hz

Screenshot from 2021-11-25 19-42-13

WebRtcAudioRecordExternal: verifyAudioConfig: PASS
I/tvo.webrtc.Logging( 3801): WebRtcAudioRecordExternal: stopRecording
I/tvo.webrtc.Logging( 3801): WebRtcAudioRecordExternal: stopThread
I/tvo.webrtc.Logging( 3801): WebRtcAudioRecordExternal: doAudioRecordStateCallback: STOP
I/tvo.webrtc.Logging( 3801): WebRtcAudioEffectsExternal: release
I/tvo.webrtc.Logging( 3801): WebRtcAudioRecordExternal: releaseAudioResources
I/tvo.webrtc.Logging( 3801): VolumeLogger: stop@[name=MediaFactoryImp - 4068, id=4703]
I/tvo.webrtc.Logging( 3801): WebRtcAudioTrackExternal: stopPlayout
I/tvo.webrtc.Logging( 3801): WebRtcAudioTrackExternal: underrun count: 0
I/tvo.webrtc.Logging( 3801): WebRtcAudioTrackExternal: stopThread
I/tvo.webrtc.Logging( 3801): WebRtcAudioTrackExternal: Stopping the AudioTrackThread...
I/tvo.webrtc.Logging( 3801): WebRtcAudioTrackExternal: AudioTrackThread has now been stopped.
I/tvo.webrtc.Logging( 3801): WebRtcAudioTrackExternal: Calling AudioTrack.stop...
D/AudioTrack( 3801): stop() called with 44640 frames delivered
I/tvo.webrtc.Logging( 3801): WebRtcAudioTrackExternal: AudioTrack.stop is done.
I/tvo.webrtc.Logging( 3801): WebRtcAudioTrackExternal: doAudioTrackStateCallback: 1
I/tvo.webrtc.Logging( 3801): WebRtcAudioTrackExternal: releaseAudioResources
D/TwilioVoicePlugin( 3801): Disconnected
I/flutter ( 3801): voip-onCallStateChanged CallEvent.callEnded
W/AudioTrack( 3801): AUDIO_OUTPUT_FLAG_FAST denied, rates do not match 44100 Hz, require 48000 Hz
I/flutter ( 3801): voip-onCallStateChanged CallEvent.callEnded
I/flutter ( 3801): call Ended

Change the custom UI when receiving an incoming call in android

When receiving an incoming call, it is pushing to a custom UI written in the package, I want to show a UI which I have designed, when pushing to the designed UI for answering the call the custom UI is dominating, is there a way to disable the custom UI for answering the call?

Questions: how do I call a URL from my server

I would like to understand, how does it make the request to my URL (Server), which expects this data to trigger a call?

Example my server URL is this:

https://twillio-voice-prova.000webhostapp.com/voice-server-twilio-php/voiceCall.php

In the function below, how do I make the request to my server URL?

TwilioVoice.instance.call.place( to: _controller.text, from: '7766576578657');

Thank you !!!

FirebaseFunctions.instance.httpsCallable

What would this function be and what does it expect in voice-accessToken?
Is it the token generated by my server, or the firebase user's token what would it be?

 final function =
        FirebaseFunctions.instance.httpsCallable("voice-accessToken");

Closed app does not receive incoming calls after Twilio Token expires

Summary:

App that hasn't been open for e.g. 2 days (with expired token) will not receive call intent/notification nor generate new token and process notification.


Details/Questions/Solutions:

This issue has a few StackOverflow questions asked but non have workable solutions or any good documentation to follow (atleast what I found).

Some answers suggest using the stopListening() function, looking through the API docs for [Twilio's Android API](https://twilio.github.io/twilio-voice-android/docs/latest/), I don't find any method/function named or similar to stopListening(). From context, I assume this refers to the token is expiring` function but nothing related to a token expiring while app is closed.

Thus, after the token expires and app is closed for more than a day, no calls will be received - this is a problem!


Log output:

When call is received on device with expired token that is (force closed), the only console output with own package name $ adb logcat | grep com.myawesome.app:

11-03 11:16:31.307 29039 29039 W GCM     : broadcast intent callback: result=CANCELLED forIntent { act=com.google.android.c2dm.intent.RECEIVE flg=0x10000000 pkg=com.myawesome.app (has extras) }

output for $ adb logcat | grep com.twilio.twilio_voice:
nothing

Steps to reproduce:

  1. Setup App
  2. Setup TwiML make-call endpoint
  3. Acquire valid tokens (valid for e.g. 5min) and confirm push notifications work.
  4. Install on 2 devices
  5. Device 1, close app completely (i.e. force kill), on Device 2 leave app open
  6. after e.g. 6min (assuming 5min expiry), make call on Device 2 (your app should handle expired tokens, acquires new tokens and place call)
  7. Device 1 will receive broadcast intent only but will not ring or perform any action.

Setup & devices tested on: (Android & iOS) in sandbox & (iOS) production

C:\Users\CybeX\FlutterProjects\twilio_voice\example>flutter doctor -v
[√] Flutter (Channel stable, 2.5.3, on Microsoft Windows [Version 10.0.19043.1288], locale en-ZA)
    • Flutter version 2.5.3 at C:\Users\CybeX\.flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 18116933e7 (2 weeks ago), 2021-10-15 10:46:35 -0700
    • Engine revision d3ea636dc5
    • Dart version 2.14.4

[√] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    • Android SDK at C:\Users\CybeX\AppData\Local\Android\Sdk
    • Platform android-30, build-tools 30.0.3
    • ANDROID_HOME = C:\Users\CybeX\AppData\Local\Android\Sdk
    • ANDROID_SDK_ROOT = C:\Users\CybeX\AppData\Local\Android\Sdk
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)
    • All Android licenses accepted.

[√] Chrome - develop for the web
    • Chrome at C:\Program Files (x86)\Google\Chrome\Application\chrome.exe

[√] Android Studio (version 4.1)
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)

[√] IntelliJ IDEA Ultimate Edition (version 2020.1)
    • IntelliJ at C:\Users\CybeX\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\201.7223.91
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart

[√] IntelliJ IDEA Ultimate Edition (version 2021.1)
    • IntelliJ at C:\Users\CybeX\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\211.7142.45
    • Flutter plugin version 58.0.3
    • Dart plugin version 211.7665

[√] Connected device (6 available)
    • SM N975U1 (mobile) • RF8MC02F23L • android-arm64  • Android 11 (API 30)
    • SM A217F (mobile)  • RF8N51V8G3E • android-arm64  • Android 10 (API 29)
    • iPhone 6s (mobile)  • iOS (13.6.1)
    • iPhone 7 (mobile)  • iOS (14.4.1)
    • SM A217F (mobile)  • RF8N51V8G3E • android-arm64  • Android 10 (API 29)
    • Chrome (web)       • chrome      • web-javascript • Google Chrome 95.0.4638.54
    • Edge (web)         • edge        • web-javascript • Microsoft Edge 95.0.1020.30

• No issues found!

twilio account

HI, I have doubts regarding where twillio's accessToken and AccountID is configured in the project's main.dart?

Samsung Device Pending intent issue

When we send CallInvite class in pending intent then Samsung device which have Android version 10 or 11 it receives null in AnswerJavaActivity.java

error in create release version

  • What went wrong:
    Execution failed for task ':twilio_voice:verifyReleaseResources'.

A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
1 exception was raised by workers:
com.android.builder.internal.aapt.v2.Aapt2Exception: Android resource linking failed
/Users/yasmeenmahmoud/flutterDev/flutter/.pub-cache/hosted/pub.dartlang.org/twilio_voice-0.0.9/android/src/main/res/layout/activity_answer.xml:17: AAPT: error: resource drawable/splash_icon (aka com.twilio.twilio_voice:drawable/splash_icon) not found.

 /Users/yasmeenmahmoud/flutterDev/flutter/.pub-cache/hosted/pub.dartlang.org/twilio_voice-0.0.9/android/src/main/res/layout/activity_background_call.xml:18: AAPT: error: resource drawable/splash_icon (aka com.twilio.twilio_voice:drawable/splash_icon) not found.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.