social_live_one_on_one_call 1.0.2 copy "social_live_one_on_one_call: ^1.0.2" to clipboard
social_live_one_on_one_call: ^1.0.2 copied to clipboard

SDK for video/audio Calls

social_live_one_on_one_call (SDK) #

Version 1.0.2 #


Installation #

  • Add 'social_live_one_on_one_call' in pubspec.yaml

    or

    flutter pub add social_live_one_on_one_call
    
  • Import library
import 'package:social_live_one_on_one_call/social_live_one_on_one_call.dart';   

NOTE: 'social_live_one_on_one_call' required dart 3.4.0 and later, please upgrade your dart as needed.

*** IMPORTANT ***

  • On Android, add following permission (if not available) in 'android/app/src/main/AndroidManifest.xml'
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>
  • To maintain audio call when app go background, please start foreground service using package 'flutter_foreground_service'
void main() {
  runApp(MyApp());
  startForegroundService();
}

void startForegroundService() async {
  ForegroundService().start();
  debugPrint("Started service");
}
  • On iOS

    • open Info.plist on iOS Project, then add following description for microphone and camera feature (if not available)
        <string>Use camera for Video Call.</string>
        <key>NSMicrophoneUsageDescription</key>
        <string>Use microphone for Voice Call.</string>
        <key>UIApplicationSupportsIndirectInputEvents</key>
    
    • open Podfile of Pods project, then add following script to add camera and microphone configurations
    post_install do |installer|
        installer.pods_project.targets.each do |target|
            flutter_additional_ios_build_settings(target)
            target.build_configurations.each do |config|
                # You can remove unused permissions here
                # e.g. when you don't need camera permission,
                    just add 'PERMISSION_CAMERA=0'
    
                config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
                    '$(inherited)',
    
                    ## dart: PermissionGroup.camera
                    'PERMISSION_CAMERA=1',
    
                    ## dart: PermissionGroup.microphone
                    'PERMISSION_MICROPHONE=1',
                ]
            end
        end
    end
    
  • And to let call run in background, you need to set background mode to Voice over IP background_mode

Flow #

Step 1: Obtain Authentication token #

  • To use the SDK, you need an authentication token and an AppID.

    • To get AppID, please contact us.
    • To get Authentication Token, please read this documentation Authentication Flow.
    • The example simulate a simple flow to obtain authentication via phone number regsiter.
    final phoneToken = await NetworkUtils().createToken(number: myNumber, appId: appId);
    
  • After you got AppID and Authentication Token, the CallSDK is ready to use now.

Step 2: Start SDK #

  • Start the SDK with AppID and token from previous step.
void startSDK({
    required String appId,
    required String token,
    required Function(CallEvent eventName, Map<String,dynamic>? data) callEvent,
    Function(bool)? completion})
  • The callEvent will emit call-related event, for example when call 'connecting', 'connected', 'ended'. The 'CallEvent.audio' will emit when audio route change, for example when user plugin headphone or turn on/off speaker. Based on these events, you can update your Call UI appropriately.
enum CallEvent {
  connecting,
  connected,
  ended,
  audio,

  // For testing call flow only, remove on distribute this library
  notify,
  busy,
}

The 'CallEvent.notify' and 'CallEvent.busy' is just for demo.

  • 'CallEvent.notify' used for notify when someone call-in. You can follow our example to implement your own notify event. The 'call_id' is necessary for the call. You can also put in other information like 'caller_name'.
if (event == CallEvent.notify) {
    if (data != null) {
        final inputCallId = BaseEntity.stringValue(data, "call_id");
        final callerName = BaseEntity.stringValue(data, "caller_name");
        if (inputCallId != null) {
            handleReceiveCall(callId: inputCallId, callerNumber: callerName);
        }
    }
}
  • 'CallEvent.busy' used for response caller whether callee is busy or available.
if (name == "busy") {
    final inputCallId = BaseEntity.stringValue(info, "call_id");
    final status = BaseEntity.stringValue(info, "status");
    if (inputCallId == widget.callId && status == "busy") {
        _doEndCall();
    }
}

Step 3: Call Feature (Make, Cancel, Accept, Decline, End) #

MAKE CALL

  • To make call, you need to get a CallID. Please read this documentation Call Flow
  • The example also simulate flow to get CallId via phone number, just enter your number and the number you want to call.
final newCallId = await NetworkUtils().createCall(appId: appId, callerNumber: registeredPhoneNumber, calleeNumber: calleeNumber);
  • After you get CallId, just call 'startCall'. You can put 'callerName' and 'calleeName' if needed.
void startCall({
    required String callId,
    bool videoCall = false,
    String? callerName,
    String? calleeName, 
    Function()? completion
})

CANCEL CALL

  • To Cancel the call on caller-side (before callee accept call), just call cancelCall
CallSDK().cancelCall();

ACCEPT CALL

  • On the Callee-side, you need to implement mechanism to notify Callee client about the callId, so that they can join the call and start communicate. The example use the CallEvent.notify to get notification and callId.
if (event == CallEvent.notify) {
    if (data != null) {
    final inputCallId = BaseEntity.stringValue(data, "call_id");
    final callerName = BaseEntity.stringValue(data, "caller_name");
    if (inputCallId != null) {
        handleReceiveCall(callId: inputCallId, callerNumber: callerName);
    }
}
  • When receive notify about the call. First, 'startCall' just like the caller do to connect with our server. When 'startCall' completed, you can now show incoming-call screen to the user.
void handleReceiveCall({required String callId}) async {
    CallSDK().startCall(callId: callId, callerName: callerNumber, calleeName: registeredPhoneNumber, completion: () {
      goToCallScreen(
        newRole: "callee",
        inputCallId: callId,
        callerNumber: callerNumber,
        calleeNumber: registeredPhoneNumber,
      );
    });
}

in_call_view

  • On the incoming-call screen, to accept call on Callee, just call acceptCall
void handleAcceptButton() async {
    await stopRingSound();
    if (widget.role == "callee") {
      CallSDK().acceptCall(completion: (result) {
        if (mounted) {
          if (result == true) {
            callAccepted = true;
            setState(() {

            });
          } else {
            NetworkUtils.showAlertDialog(
              context: context,
              alertMsg: "Cannot accept call, please allow microphone",
            );
          }
        }
      });
    }
}
  • Within seconds, both caller and callee will connect and the you can start talking. in_call_view

DECLINE CALL

  • To Decline the call on Callee-side, just call declineCall
CallSDK().declineCall();

END CALL

  • To stop the on-going call, just call terminateCall. Below code check the current call status and decide which action to do.
final callReady = CallSDK().isCallConnected();
if (callReady) {
    CallSDK().terminateCall();
} else {
    CallSDK().cancelCall();
}
_closeCallView();
  • You receive 'CallEvent.ended' in following cases:
    • Call was connected, and the other end the call.
    • The callee decline the call.
    • The caller cancel the call before you make any decision (Decline/Accept).

Step 4: Handle Microphone and Speaker #

  • The SDK provide enough function to handle microphone and speaker when in-call
AudioDevice? getCurrentAudioInput();
bool currentDeviceIsSpeaker();
bool currentDeviceIsBluetooth();
bool currentDeviceIsHeadphone();
Future<void> turnOnSpeaker();
Future<void> turnOffSpeaker();
bool haveBluetooth();
bool microMuted();
void setMicroMuted({required bool muted});
  • 'turnOnSpeaker' turn the loud speaker on device
  • 'turnOffSpeaker' turn off loud speaker and use either in-ear speaker or external device (headphone/bluetooth) if available.
void handleSpeakerButton() async {
    final isCurrentSpeaker = CallSDK().currentDeviceIsSpeaker();
    if (isCurrentSpeaker == true) {
      await CallSDK().turnOffSpeaker();
    } else {
      await CallSDK().turnOnSpeaker();
    }

    setState(() {

    });
}
  • 'microMuted' and 'setMicroMuted' to handle microphone when in-call
void handleMicrophoneButton() async {
    final isMuted = CallSDK().microMuted();
    CallSDK().setMicroMuted(muted: !isMuted);
    await Future.delayed(const Duration(milliseconds: 200));
    setState(() {

    });
}

Step 5: Stop SDK #

  • When not used anymore especially when logout, you can stop CallSDK
await CallSDK().stopSDK(completion: () {
}