Flutter
Last updated
Last updated
Trong file AppDelegate.swift
:
```swift
import UIKit
import Flutter
import PushKit
import UserNotifications
import PortSIPVoIPSDK
@UIApplicationMain class AppDelegate: FlutterAppDelegate, PKPushRegistryDelegate {
private let channel = "name_channel"
private var portsipService: PortsipService! = PortsipService()
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
UserDefaults.standard.register(defaults: ["CallKit": true])
UserDefaults.standard.register(defaults: ["PushNotification": true])
UserDefaults.standard.register(defaults: ["ForceBackground": true])
fController = window?.rootViewController as? FlutterViewController
FlutterMethodChannel(name: channel, binaryMessenger: fController.binaryMessenger).setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
// Note: this method is invoked on the UI thread.
switch call.method {
case "registerPortsip":
let args = call.arguments as? [String: Any]
let username = args?["username"] as? String
let password = args?["password"] as? String
let domain = args?["domain"] as? String
let server = args?["sipServer"] as? String
if username != nil && password != nil && domain != nil {
let res = self?.registerPortSip(username: username!, password: password!, domain: domain!, sipServer: server!)
if res == 0 || res == -60021 || res == -60095 || res == -60098 {
result(Int32(res!))
} else {
result(FlutterError(code: "REGISTER FAILED", message: "Kết nối không thành công.", details: nil))
}
} else {
result(FlutterError(code: "NOT AUTHENTICATED", message: "Username và password không đúng.", details: nil))
}
case "unregisterPortsip":
if (self?.portsipService != nil) {
self?.updatePushStatusToSipServer(willPush: false)
self?.portsipService.unregisterPortsip()
}
case "callOut":
let args = call.arguments as? [String: Any]
let phoneNumber = args?["phoneNumber"] as? String
let videoCall = args?["videoCall"] as? Bool
if phoneNumber != nil {
let res = self?.portsipService.callOut(phoneNumber: phoneNumber!, videoCall: videoCall ?? false)
if res! {
result(nil)
} else {
result(FlutterError(code: "CALL FAILED", message: "Không thể thực hiện cuộc gọi", details: nil))
}
}
case "hangUp":
let res = self?.portsipService.hangUp()
if res! {
result(nil)
} else {
result(FlutterError(code: "HANGUP FAILED", message: "Không thể kết thúc cuộc gọi", details: nil))
}
case "answerCall":
let res = self?.portsipService.answerCall()
if res! {
result(nil)
} else {
result(FlutterError(code: "ANSWER_CALL FAILED", message: "Không thể trả lời cuộc gọi", details: nil))
}
case "rejectCall":
let res = self?.portsipService.rejectCall()
if res! {
result(nil)
} else {
result(FlutterError(code: "REJECT_CALL FAILED", message: "Không thể từ chối cuộc gọi", details: nil))
}
case "hold":
let res = self?.portsipService.hold()
if res! {
result(nil)
} else {
result(FlutterError(code: "HOLD_CALL FAILED", message: "Không thể giữ máy", details: nil))
}
case "unHold":
let res = self?.portsipService.unHold()
if res! {
result(nil)
} else {
result(FlutterError(code: "UNHOLD_CALL FAILED", message: "Không thể tiếp tục cuộc gọi", details: nil))
}
case "speakerOn":
self?.portsipService.speakerOn()
result(nil)
case "speakerOff":
self?.portsipService.speakerOff()
result(nil)
case "microphoneOn":
self?.portsipService.turnOnMicrophone()
result(nil)
case "microphoneOff":
self?.portsipService.turnOffMicrophone()
result(nil)
case "frontCamera":
self?.portsipService.switchToFrontCamera()
result(nil)
case "backCamera":
self?.portsipService.switchToBackCamera()
result(nil)
case "cameraOn":
self?.portsipService.turnOnCamera()
result(nil)
case "cameraOff":
self?.portsipService.turnOffCamera()
result(nil)
default:
result(FlutterMethodNotImplemented)
}
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func registerPortSip(username: String, password: String, domain: String, sipServer: String) -> Int32 {
PortsipService.fController = fController
if !CallManager.portsipRegistered {
CallManager.portsipExtension = username
CallManager.portsipPassword = password
CallManager.portsipDomain = domain
CallManager.portsipServer = sipServer
portsipService.registerPortsip()
} else {
portsipService.refreshRegistrationPortsip()
}
return 0
}
}
```
Tham khảo thêm các function tại đây: https://docs.etelecom.vn/tong-dai/sdk/ios
```kotlin
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import android.view.KeyEvent
import androidx.annotation.NonNull
import androidx.annotation.RequiresApi
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.portsip.PortSipErrorcode
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
@RequiresApi(Build.VERSION_CODES.O)
class MainActivity : FlutterActivity() {
companion object {
lateinit var shared: MainActivity
const val channel = "name_channel"
}
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
shared = this
PortsipService.engineF = flutterEngine
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, channel).setMethodCallHandler { call, result ->
// Note: this method is invoked on the main thread.
when (call.method) {
"registerPortsip" -> {
val username = call.argument<String>("username")
val password = call.argument<String>("password")
val domain = call.argument<String>("domain")
val server = call.argument<String>("sipServer")
if (username != null && password != null && domain != null && server != null) {
val res = registerPortsip(username, password, domain, server)
if (res == 0 || res == PortSipErrorcode.ECoreAlreadyRegistered ||
res == PortSipErrorcode.ECoreAllowOnlyOneUser ||
res == PortSipErrorcode.ECoreCreateTransportFailure
) {
result.success(res)
} else {
result.error("REGISTER FAILED", "Kết nối không thành công.", null)
}
} else {
result.error("NOT AUTHENTICATED", "Username và password không đúng.", null)
}
}
"unregisterPortsip" -> {
unregisterPortsip()
}
"callOut" -> {
val phoneNumber = call.argument<String>("phoneNumber")
val videoCall = call.argument<Boolean>("videoCall")
if (phoneNumber != null) {
val res = callOut(phoneNumber, videoCall ?: false)
if (res) {
result.success(null)
} else {
result.error("CALL FAILED", "Không thể thực hiện cuộc gọi", null)
}
}
}
"hangUp" -> {
val res = hangUp()
if (res) {
result.success(null)
} else {
result.error("HANGUP FAILED", "Không thể kết thúc cuộc gọi", null)
}
}
"answerCall" -> {
val res = answerCall()
if (res) {
result.success(null)
} else {
result.error("ANSWER_CALL FAILED", "Không thể trả lời cuộc gọi", null)
}
}
"rejectCall" -> {
val res = rejectCall()
if (res) {
result.success(null)
} else {
result.error("REJECT_CALL FAILED", "Không thể từ chối cuộc gọi", null)
}
}
"hold" -> {
val res = hold()
if (res) {
result.success(null)
} else {
result.error("HOLD FAILED", "Không thể giữ máy", null)
}
}
"unHold" -> {
val res = unHold()
if (res) {
result.success(null)
} else {
result.error("UN_HOLD FAILED", "Không thể tiếp tục giữ máy", null)
}
}
"speakerOn" -> {
PortsipService.shared?.speakerOn()
result.success(null)
}
"speakerOff" -> {
PortsipService.shared?.speakerOff()
result.success(null)
}
"microphoneOn" -> {
PortsipService.shared?.turnOnMicrophone()
result.success(null)
}
"microphoneOff" -> {
PortsipService.shared?.turnOffMicrophone()
result.success(null)
}
"frontCamera" -> {
PortsipService.shared?.switchToFrontCamera()
result.success(null)
}
"backCamera" -> {
PortsipService.shared?.switchToBackCamera()
result.success(null)
}
"cameraOn" -> {
PortsipService.shared?.turnOnCamera()
result.success(null)
}
"cameraOff" -> {
PortsipService.shared?.turnOffCamera()
result.success(null)
}
"finishDisposingCamera" -> {
val intent = Intent(this, VideoCallActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
else -> {
result.notImplemented()
}
}
}
}
private fun registerPortsip(username: String, password: String, domain: String, server: String): Int {
val sharedPref = getSharedPreferences("CallManager", MODE_PRIVATE) ?: return 0
with(sharedPref.edit()) {
putString(getString(R.string.call_manager_ps_extension), username)
putString(getString(R.string.call_manager_ps_password), password)
putString(getString(R.string.call_manager_ps_domain), domain)
putString(getString(R.string.call_manager_ps_server), server)
apply()
}
Intent(this, PortsipService::class.java).also { intent ->
startService(intent)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(
activity, Manifest.permission.BLUETOOTH_CONNECT)
) {
ActivityCompat.requestPermissions(
activity,
arrayOf(Manifest.permission.BLUETOOTH_CONNECT),
0
)
}
}
return 0
}
private fun unregisterPortsip() {
if (PortsipService.shared != null) {
PortsipService.shared!!.unregisterPortsip()
}
}
private fun callOut(phoneNumber: String, videoCall: Boolean = false): Boolean {
return PortsipService.shared?.callOut(phoneNumber, videoCall) ?: false
}
private fun hangUp(): Boolean {
return PortsipService.shared?.hangUp() ?: false
}
private fun answerCall(): Boolean {
return PortsipService.shared?.answerCall() ?: false
}
private fun rejectCall(): Boolean {
return PortsipService.shared?.rejectCall() ?: false
}
private fun hold(): Boolean {
return PortsipService.shared?.hold() ?: false
}
private fun unHold(): Boolean {
return PortsipService.shared?.unHold() ?: false
}
}
```
Tham khảo thêm các function tại đây: https://docs.etelecom.vn/tong-dai/sdk/android
const CHANNEL = 'name_channel';
const platform = const MethodChannel(CHANNEL);
static Future<void> portsipRegister() async {
try {
await platform.invokeMethod("registerPortsip", {
'username': extensionNumber,
'password': extensionPassword,
'domain': tenantDomain,
'sipServer': sipServer
});
} on PlatformException catch (e) {
throw e;
}
}
static Future<void> callOut(String phoneNumber, bool isVideoCall) async {
try {
await platform.invokeMethod("callOut", {
'phoneNumber': phoneNumber, 'videoCall': isVideoCall
});
} on PlatformException catch(e) {
throw e;
}
}
static Future<void> portsipHangUp() async {
try {
await platform.invokeMethod("hangUp");
} on PlatformException catch (e) {
throw e;
}
}
static Future<void> portsipAnswer() async {
try {
await platform.invokeMethod("answerCall");
} on PlatformException catch (e) {
throw e;
}
}
static Future<void> portsipReject() async {
try {
await platform.invokeMethod("rejectCall");
} on PlatformException catch (e) {
throw e;
}
}
static Future<void> portsipHold() async {
try {
await platform.invokeMethod("hold");
} on PlatformException catch (e) {
throw e;
}
}
static Future<void> portsipUnHold() async {
try {
await platform.invokeMethod("unHold");
} on PlatformException catch (e) {
throw e;
}
}
static Future<void> portsipSpeakerOn() async {
try {
await platform.invokeMethod("speakerOn");
} on PlatformException catch (e) {
throw e;
}
}
static Future<void> portsipSpeakerOff() async {
try {
await platform.invokeMethod("speakerOff");
} on PlatformException catch (e) {
throw e;
}
}
static Future<void> microphoneOn() async {
try {
await platform.invokeMethod("microphoneOn");
} on PlatformException catch (e) {
throw e;
}
}
static Future<void> microphoneOff() async {
try {
await platform.invokeMethod("microphoneOff");
} on PlatformException catch (e) {
throw e;
}
}
static Future<void> switchToFrontCamera() async {
try {
await platform.invokeMethod("frontCamera");
} on PlatformException catch (e) {
throw e;
}
}
static Future<void> switchToBackCamera() async {
try {
await platform.invokeMethod("backCamera");
} on PlatformException catch (e) {
throw e;
}
}
static Future<void> cameraOn() async {
try {
await platform.invokeMethod("cameraOn");
} on PlatformException catch (e) {
throw e;
}
}
static Future<void> cameraOff() async {
try {
await platform.invokeMethod("cameraOff");
} on PlatformException catch (e) {
throw e;
}
}
static void hookNativeEvents(BuildContext context) {
platform.setMethodCallHandler((call) async {
try {
switch (call.method) {
case 'onRegisterFailure':
String code = jsonDecode(call.arguments)["code"] ?? "";
if (code != callsReader.portsipRegisterFailureCode) {
//todo if onRegisterFailure
}
return;
case 'callIn':
String? caller = jsonDecode(call.arguments)["caller"];
String callingNumber = caller!.split('sip:')[1].split('@')[0];
bool? isVideoCall = jsonDecode(call.arguments)["isVideo"];
portsipCallIn(
phoneNumber: callingNumber,
isVideoCall: isVideoCall == null ? false : isVideoCall
);
return;
case 'callAnswered':
if (call.arguments != null && call.arguments.length > 0) {
String? caller = jsonDecode(call.arguments)["caller"];
}
return;
case 'callEnded':
//code here
return;
case 'sipMessageResponse':
if (call.arguments == null || call.arguments.length == 0) {
return;
}
String sipMessage = jsonDecode(call.arguments)['sipMessage'];
String _splitMessage = sipMessage.split('X-Session-Id:')[1];
if (_splitMessage.isEmpty) {
return;
}
String xSessionId = _splitMessage.substring(0, 18);
return;
case 'disposeCamera':
await callsReader.disposeCamera();
try {
await platform.invokeMethod("finishDisposingCamera");
} on PlatformException catch (e) {
}
return;
}
} catch (e) {
hookNativeEvents(context);
}
return;
});
}