diff --git a/dialer/android/app/src/main/AndroidManifest.xml b/dialer/android/app/src/main/AndroidManifest.xml index 9c369ce..95b4a91 100644 --- a/dialer/android/app/src/main/AndroidManifest.xml +++ b/dialer/android/app/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ - - + + @@ -12,11 +12,6 @@ - - - - - - + - - - - - android:name="io.flutter.embedding.android.NormalTheme" - android:resource="@style/NormalTheme" /> - - - - - - - - - - - - - - + + + + + + @@ -75,27 +56,36 @@ + + + + + + + + - - - - \ No newline at end of file diff --git a/dialer/android/app/src/main/kotlin/com/icing/dialer/activities/MainActivity.kt b/dialer/android/app/src/main/kotlin/com/icing/dialer/activities/MainActivity.kt index 5e66145..8354324 100644 --- a/dialer/android/app/src/main/kotlin/com/icing/dialer/activities/MainActivity.kt +++ b/dialer/android/app/src/main/kotlin/com/icing/dialer/activities/MainActivity.kt @@ -1,21 +1,21 @@ package com.icing.dialer.activities import android.Manifest -import android.content.ComponentName +import android.app.role.RoleManager +import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.database.Cursor import android.net.Uri +import android.os.Build import android.os.Bundle import android.provider.CallLog -import android.telecom.PhoneAccount -import android.telecom.PhoneAccountHandle import android.telecom.TelecomManager import android.util.Log import androidx.core.content.ContextCompat import com.icing.dialer.KeystoreHelper -import com.icing.dialer.services.CallConnectionService import com.icing.dialer.services.CallService +import com.icing.dialer.services.MyInCallService import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel @@ -25,6 +25,7 @@ class MainActivity : FlutterActivity() { private val CALLLOG_CHANNEL = "com.example.calllog" private val CALL_CHANNEL = "call_service" private val TAG = "MainActivity" + private val REQUEST_CODE_SET_DEFAULT_DIALER = 1001 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -32,132 +33,84 @@ class MainActivity : FlutterActivity() { Log.d(TAG, "Waiting for Flutter to signal permissions") } - private fun registerPhoneAccount() { - val telecomManager = getSystemService(TELECOM_SERVICE) as TelecomManager - val phoneAccountHandle = - PhoneAccountHandle( - ComponentName(this, CallConnectionService::class.java), - "IcingDialerAccount" - ) - Log.d(TAG, "PhoneAccountHandle component: ${phoneAccountHandle.componentName}") - val phoneAccount = - PhoneAccount.builder(phoneAccountHandle, "Icing Dialer") - .setCapabilities( - PhoneAccount.CAPABILITY_CALL_PROVIDER or - PhoneAccount.CAPABILITY_CONNECTION_MANAGER - ) - .build() - telecomManager.registerPhoneAccount(phoneAccount) - CallService.setPhoneAccountHandle(phoneAccountHandle) - Log.d(TAG, "PhoneAccount registered: ${phoneAccountHandle.id}") - - val registeredAccounts = telecomManager.callCapablePhoneAccounts - Log.d(TAG, "Registered PhoneAccounts: ${registeredAccounts.joinToString()}") - if (!registeredAccounts.contains(phoneAccountHandle)) { - Log.w(TAG, "PhoneAccount not found in callCapablePhoneAccounts") - if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == - PackageManager.PERMISSION_GRANTED - ) { - val uri = Uri.parse("tel:1234567890") - val extras = - Bundle().apply { - putParcelable( - TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, - phoneAccountHandle - ) - } - telecomManager.placeCall(uri, extras) - Log.d(TAG, "Triggered dummy call to bind CallConnectionService") - } else { - Log.w(TAG, "CALL_PHONE permission not granted, cannot test binding") - } - } else { - Log.d(TAG, "PhoneAccount successfully found in callCapablePhoneAccounts") - } - } - override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) Log.d(TAG, "Configuring Flutter engine") - CallConnectionService.channel = - MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CALL_CHANNEL) + + MyInCallService.channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CALL_CHANNEL) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CALL_CHANNEL) - .setMethodCallHandler { call, result -> - when (call.method) { - "permissionsGranted" -> { - Log.d(TAG, "Received permissionsGranted from Flutter") - registerPhoneAccount() - checkAndRequestDefaultDialer() - result.success(true) - } - "makeGsmCall" -> { - val phoneNumber = call.argument("phoneNumber") - if (phoneNumber != null) { - val success = CallService.makeGsmCall(this, phoneNumber) - if (success) { - result.success( - mapOf( - "status" to "calling", - "phoneNumber" to phoneNumber - ) - ) - } else { - result.error("CALL_FAILED", "Failed to initiate call", null) - } - } else { - result.error( - "INVALID_PHONE_NUMBER", - "Phone number is required", - null - ) - } - } - "hangUpCall" -> { - val success = CallService.hangUpCall(this) - if (success) { - result.success(mapOf("status" to "ended")) - } else { - result.error("HANGUP_FAILED", "Failed to end call", null) - } - } - else -> result.notImplemented() + .setMethodCallHandler { call, result -> + when (call.method) { + "permissionsGranted" -> { + Log.d(TAG, "Received permissionsGranted from Flutter") + checkAndRequestDefaultDialer() + result.success(true) } + "makeGsmCall" -> { + val phoneNumber = call.argument("phoneNumber") + if (phoneNumber != null) { + val success = CallService.makeGsmCall(this, phoneNumber) + if (success) { + result.success(mapOf("status" to "calling", "phoneNumber" to phoneNumber)) + } else { + result.error("CALL_FAILED", "Failed to initiate call", null) + } + } else { + result.error("INVALID_PHONE_NUMBER", "Phone number is required", null) + } + } + "hangUpCall" -> { + val success = CallService.hangUpCall(this) + if (success) { + result.success(mapOf("status" to "ended")) + } else { + result.error("HANGUP_FAILED", "Failed to end call", null) + } + } + else -> result.notImplemented() } + } MethodChannel(flutterEngine.dartExecutor.binaryMessenger, KEYSTORE_CHANNEL) - .setMethodCallHandler { call, result -> - KeystoreHelper(call, result).handleMethodCall() - } + .setMethodCallHandler { call, result -> KeystoreHelper(call, result).handleMethodCall() } MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CALLLOG_CHANNEL) - .setMethodCallHandler { call, result -> - if (call.method == "getCallLogs") { - val callLogs = getCallLogs() - result.success(callLogs) - } else { - result.notImplemented() - } + .setMethodCallHandler { call, result -> + if (call.method == "getCallLogs") { + val callLogs = getCallLogs() + result.success(callLogs) + } else { + result.notImplemented() } + } } private fun checkAndRequestDefaultDialer() { - val tm = getSystemService(TELECOM_SERVICE) as TelecomManager - val currentDefault = tm.defaultDialerPackage + val telecomManager = getSystemService(TELECOM_SERVICE) as TelecomManager + val currentDefault = telecomManager.defaultDialerPackage Log.d(TAG, "Current default dialer: $currentDefault, My package: $packageName") + if (currentDefault != packageName) { - val intent = - Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER) - .putExtra( - TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, - packageName - ) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - try { - startActivityForResult(intent, 1001) - Log.d(TAG, "Default dialer prompt launched with requestCode 1001") - } catch (e: Exception) { - Log.e(TAG, "Failed to launch default dialer prompt: ${e.message}", e) - launchDefaultAppsSettings() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val roleManager = getSystemService(Context.ROLE_SERVICE) as RoleManager + if (roleManager.isRoleAvailable(RoleManager.ROLE_DIALER) && !roleManager.isRoleHeld(RoleManager.ROLE_DIALER)) { + val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER) + startActivityForResult(intent, REQUEST_CODE_SET_DEFAULT_DIALER) + Log.d(TAG, "Launched RoleManager intent for default dialer on API 29+") + } else { + Log.d(TAG, "RoleManager: Available=${roleManager.isRoleAvailable(RoleManager.ROLE_DIALER)}, Held=${roleManager.isRoleHeld(RoleManager.ROLE_DIALER)}") + } + } else { + val intent = Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER) + .putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, packageName) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + try { + startActivityForResult(intent, REQUEST_CODE_SET_DEFAULT_DIALER) + Log.d(TAG, "Launched TelecomManager intent for default dialer") + } catch (e: Exception) { + Log.e(TAG, "Failed to launch default dialer prompt: ${e.message}", e) + launchDefaultAppsSettings() + } } } else { Log.d(TAG, "Already the default dialer") @@ -173,11 +126,11 @@ class MainActivity : FlutterActivity() { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) Log.d(TAG, "onActivityResult: requestCode=$requestCode, resultCode=$resultCode, data=$data") - if (requestCode == 1001) { + if (requestCode == REQUEST_CODE_SET_DEFAULT_DIALER) { if (resultCode == RESULT_OK) { Log.d(TAG, "User accepted default dialer change") } else { - Log.d(TAG, "Default dialer prompt canceled (resultCode=$resultCode)") + Log.w(TAG, "Default dialer prompt canceled or failed (resultCode=$resultCode)") launchDefaultAppsSettings() } } @@ -185,14 +138,13 @@ class MainActivity : FlutterActivity() { private fun getCallLogs(): List> { val logsList = mutableListOf>() - val cursor: Cursor? = - contentResolver.query( - CallLog.Calls.CONTENT_URI, - null, - null, - null, - CallLog.Calls.DATE + " DESC" - ) + val cursor: Cursor? = contentResolver.query( + CallLog.Calls.CONTENT_URI, + null, + null, + null, + CallLog.Calls.DATE + " DESC" + ) cursor?.use { while (it.moveToNext()) { val number = it.getString(it.getColumnIndexOrThrow(CallLog.Calls.NUMBER)) @@ -200,13 +152,12 @@ class MainActivity : FlutterActivity() { val date = it.getLong(it.getColumnIndexOrThrow(CallLog.Calls.DATE)) val duration = it.getLong(it.getColumnIndexOrThrow(CallLog.Calls.DURATION)) - val map = - mutableMapOf( - "number" to number, - "type" to type, - "date" to date, - "duration" to duration - ) + val map = mutableMapOf( + "number" to number, + "type" to type, + "date" to date, + "duration" to duration + ) logsList.add(map) } } diff --git a/dialer/android/app/src/main/kotlin/com/icing/dialer/services/CallConnectionService.kt b/dialer/android/app/src/main/kotlin/com/icing/dialer/services/CallConnectionService.kt index 53d73b2..c39d608 100644 --- a/dialer/android/app/src/main/kotlin/com/icing/dialer/services/CallConnectionService.kt +++ b/dialer/android/app/src/main/kotlin/com/icing/dialer/services/CallConnectionService.kt @@ -1,82 +1,82 @@ -package com.icing.dialer.services +// package com.icing.dialer.services -import android.telecom.Connection -import android.telecom.ConnectionService -import android.telecom.PhoneAccountHandle -import android.telecom.TelecomManager -import android.telecom.DisconnectCause -import android.util.Log -import io.flutter.plugin.common.MethodChannel +// import android.telecom.Connection +// import android.telecom.ConnectionService +// import android.telecom.PhoneAccountHandle +// import android.telecom.TelecomManager +// import android.telecom.DisconnectCause +// import android.util.Log +// import io.flutter.plugin.common.MethodChannel -class CallConnectionService : ConnectionService() { - companion object { - var channel: MethodChannel? = null - private const val TAG = "CallConnectionService" - } +// class CallConnectionService : ConnectionService() { +// companion object { +// var channel: MethodChannel? = null +// private const val TAG = "CallConnectionService" +// } - init { - Log.d(TAG, "CallConnectionService initialized") - } +// init { +// Log.d(TAG, "CallConnectionService initialized") +// } - override fun onCreate() { - super.onCreate() - Log.d(TAG, "Service created") - } +// override fun onCreate() { +// super.onCreate() +// Log.d(TAG, "Service created") +// } - override fun onDestroy() { - super.onDestroy() - Log.d(TAG, "Service destroyed") - } +// override fun onDestroy() { +// super.onDestroy() +// Log.d(TAG, "Service destroyed") +// } - override fun onCreateOutgoingConnection( - connectionManagerPhoneAccount: PhoneAccountHandle?, - request: android.telecom.ConnectionRequest - ): Connection { - Log.d(TAG, "Creating outgoing connection for ${request.address}, account: $connectionManagerPhoneAccount") - val connection = object : Connection() { - override fun onStateChanged(state: Int) { - super.onStateChanged(state) - Log.d(TAG, "Connection state changed: $state") - val stateStr = when (state) { - STATE_DIALING -> "dialing" - STATE_ACTIVE -> "active" - STATE_DISCONNECTED -> "disconnected" - else -> "unknown" - } - channel?.invokeMethod("callStateChanged", mapOf("state" to stateStr, "phoneNumber" to request.address.toString())) - } +// override fun onCreateOutgoingConnection( +// connectionManagerPhoneAccount: PhoneAccountHandle?, +// request: android.telecom.ConnectionRequest +// ): Connection { +// Log.d(TAG, "Creating outgoing connection for ${request.address}, account: $connectionManagerPhoneAccount") +// val connection = object : Connection() { +// override fun onStateChanged(state: Int) { +// super.onStateChanged(state) +// Log.d(TAG, "Connection state changed: $state") +// val stateStr = when (state) { +// STATE_DIALING -> "dialing" +// STATE_ACTIVE -> "active" +// STATE_DISCONNECTED -> "disconnected" +// else -> "unknown" +// } +// channel?.invokeMethod("callStateChanged", mapOf("state" to stateStr, "phoneNumber" to request.address.toString())) +// } - override fun onDisconnect() { - Log.d(TAG, "Connection disconnected") - setDisconnected(DisconnectCause(DisconnectCause.LOCAL)) - destroy() - } - } - connection.setAddress(request.address, TelecomManager.PRESENTATION_ALLOWED) - connection.setInitialized() - connection.setDialing() - return connection - } +// override fun onDisconnect() { +// Log.d(TAG, "Connection disconnected") +// setDisconnected(DisconnectCause(DisconnectCause.LOCAL)) +// destroy() +// } +// } +// connection.setAddress(request.address, TelecomManager.PRESENTATION_ALLOWED) +// connection.setInitialized() +// connection.setDialing() +// return connection +// } - override fun onCreateIncomingConnection( - connectionManagerPhoneAccount: PhoneAccountHandle?, - request: android.telecom.ConnectionRequest - ): Connection { - Log.d(TAG, "Creating incoming connection for ${request.address}, account: $connectionManagerPhoneAccount") - val connection = object : Connection() { - override fun onAnswer() { - Log.d(TAG, "Connection answered") - setActive() - } +// override fun onCreateIncomingConnection( +// connectionManagerPhoneAccount: PhoneAccountHandle?, +// request: android.telecom.ConnectionRequest +// ): Connection { +// Log.d(TAG, "Creating incoming connection for ${request.address}, account: $connectionManagerPhoneAccount") +// val connection = object : Connection() { +// override fun onAnswer() { +// Log.d(TAG, "Connection answered") +// setActive() +// } - override fun onDisconnect() { - Log.d(TAG, "Connection disconnected") - setDisconnected(DisconnectCause(DisconnectCause.LOCAL)) - destroy() - } - } - connection.setAddress(request.address, TelecomManager.PRESENTATION_ALLOWED) - connection.setRinging() - return connection - } -} \ No newline at end of file +// override fun onDisconnect() { +// Log.d(TAG, "Connection disconnected") +// setDisconnected(DisconnectCause(DisconnectCause.LOCAL)) +// destroy() +// } +// } +// connection.setAddress(request.address, TelecomManager.PRESENTATION_ALLOWED) +// connection.setRinging() +// return connection +// } +// } \ No newline at end of file diff --git a/dialer/android/app/src/main/kotlin/com/icing/dialer/services/CallService.kt b/dialer/android/app/src/main/kotlin/com/icing/dialer/services/CallService.kt index 8dbf4c1..7958799 100644 --- a/dialer/android/app/src/main/kotlin/com/icing/dialer/services/CallService.kt +++ b/dialer/android/app/src/main/kotlin/com/icing/dialer/services/CallService.kt @@ -1,24 +1,17 @@ package com.icing.dialer.services -import android.Manifest import android.content.Context -import android.content.Intent import android.net.Uri import android.os.Build import android.os.Bundle import android.telecom.TelecomManager -import android.telecom.PhoneAccountHandle -import android.telephony.TelephonyManager import android.util.Log import androidx.core.content.ContextCompat import android.content.pm.PackageManager +import android.Manifest object CallService { - private var phoneAccountHandle: PhoneAccountHandle? = null - - fun setPhoneAccountHandle(handle: PhoneAccountHandle) { - phoneAccountHandle = handle - } + private val TAG = "CallService" fun makeGsmCall(context: Context, phoneNumber: String): Boolean { return try { @@ -26,37 +19,36 @@ object CallService { val uri = Uri.parse("tel:$phoneNumber") if (ContextCompat.checkSelfPermission(context, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - val extras = Bundle() - extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle) - telecomManager.placeCall(uri, extras) - true - } else { - Log.e("CallService", "GSM call not supported below Android M") - false - } + telecomManager.placeCall(uri, Bundle()) + Log.d(TAG, "Initiated call to $phoneNumber") + true } else { - Log.e("CallService", "CALL_PHONE permission not granted") + Log.e(TAG, "CALL_PHONE permission not granted") false } } catch (e: Exception) { - Log.e("CallService", "Error making GSM call: ${e.message}") + Log.e(TAG, "Error making GSM call: ${e.message}", e) false } } fun hangUpCall(context: Context): Boolean { return try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + if (MyInCallService.currentCall != null) { + MyInCallService.currentCall?.disconnect() + Log.d(TAG, "Disconnected active call via MyInCallService") + true + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager telecomManager.endCall() + Log.d(TAG, "Ended call via TelecomManager (no active call in MyInCallService)") true } else { - Log.e("CallService", "Hangup call only supported on Android P or later") + Log.e(TAG, "No active call and hangup not supported below Android P") false } } catch (e: Exception) { - Log.e("CallService", "Error hanging up call: ${e.message}") + Log.e(TAG, "Error hanging up call: ${e.message}", e) false } } diff --git a/dialer/android/app/src/main/kotlin/com/icing/dialer/services/MyInCallService.kt b/dialer/android/app/src/main/kotlin/com/icing/dialer/services/MyInCallService.kt new file mode 100644 index 0000000..c4b67c7 --- /dev/null +++ b/dialer/android/app/src/main/kotlin/com/icing/dialer/services/MyInCallService.kt @@ -0,0 +1,60 @@ +package com.icing.dialer.services + +import android.telecom.Call +import android.telecom.InCallService +import io.flutter.plugin.common.MethodChannel + +class MyInCallService : InCallService() { + companion object { + var channel: MethodChannel? = null + var currentCall: Call? = null + } + + private val callCallback = object : Call.Callback() { + override fun onStateChanged(call: Call, state: Int) { + super.onStateChanged(call, state) + val stateStr = when (state) { + Call.STATE_DIALING -> "dialing" + Call.STATE_ACTIVE -> "active" + Call.STATE_DISCONNECTED -> "disconnected" + Call.STATE_DISCONNECTING -> "disconnecting" + else -> "unknown" + } + channel?.invokeMethod("callStateChanged", mapOf( + "callId" to call.details.handle.toString(), + "state" to stateStr + )) + if (state == Call.STATE_DISCONNECTED || state == Call.STATE_DISCONNECTING) { + channel?.invokeMethod("callEnded", mapOf("callId" to call.details.handle.toString())) + currentCall = null + } + } + } + + override fun onCallAdded(call: Call) { + super.onCallAdded(call) + currentCall = call + val stateStr = when (call.state) { + Call.STATE_DIALING -> "dialing" + Call.STATE_ACTIVE -> "active" + else -> "unknown" + } + channel?.invokeMethod("callAdded", mapOf( + "callId" to call.details.handle.toString(), + "state" to stateStr + )) + call.registerCallback(callCallback) + } + + override fun onCallRemoved(call: Call) { + super.onCallRemoved(call) + call.unregisterCallback(callCallback) + channel?.invokeMethod("callRemoved", mapOf("callId" to call.details.handle.toString())) + currentCall = null + } + + override fun onCallAudioStateChanged(state: android.telecom.CallAudioState) { + super.onCallAudioStateChanged(state) + channel?.invokeMethod("audioStateChanged", mapOf("route" to state.route)) + } +} \ No newline at end of file diff --git a/dialer/lib/main.dart b/dialer/lib/main.dart index 80751b2..0c8068f 100644 --- a/dialer/lib/main.dart +++ b/dialer/lib/main.dart @@ -37,6 +37,7 @@ Future _requestPermissions() async { Map statuses = await [ Permission.phone, Permission.contacts, + Permission.microphone, ].request(); if (statuses.values.every((status) => status.isGranted)) { print("All required permissions granted"); diff --git a/dialer/lib/services/call_service.dart b/dialer/lib/services/call_service.dart index 21fef7c..6b6ed48 100644 --- a/dialer/lib/services/call_service.dart +++ b/dialer/lib/services/call_service.dart @@ -1,37 +1,66 @@ -import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import '../features/call/call_page.dart'; class CallService { static const MethodChannel _channel = MethodChannel('call_service'); static String? currentPhoneNumber; + // Add a GlobalKey for Navigator + static final GlobalKey navigatorKey = GlobalKey(); + CallService() { _channel.setMethodCallHandler((call) async { - if (call.method == "callStateChanged") { - final state = call.arguments["state"] as String; - final phoneNumber = call.arguments["phoneNumber"] as String; - if (state == "dialing" || state == "active") { - Navigator.push( - navigatorKey.currentContext!, - MaterialPageRoute( - builder: (context) => CallPage( - displayName: phoneNumber, // Replace with contact lookup if available - phoneNumber: phoneNumber, - thumbnail: null, + final context = navigatorKey.currentContext; + if (context == null) return; + + switch (call.method) { + case "callAdded": + final phoneNumber = call.arguments["callId"] as String; // tel:1234567890 + final state = call.arguments["state"] as String; + currentPhoneNumber = phoneNumber.replaceFirst('tel:', ''); // Extract number + if (state == "dialing" || state == "active") { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => CallPage( + displayName: currentPhoneNumber!, // Replace with contact lookup if available + phoneNumber: currentPhoneNumber!, + thumbnail: null, + ), ), - ), - ); - } else if (state == "disconnected") { - Navigator.pop(navigatorKey.currentContext!); - } + ); + } + break; + case "callStateChanged": + final state = call.arguments["state"] as String; + if (state == "disconnected" || state == "disconnecting") { + Navigator.pop(context); + } else if (state == "active") { + // Ensure CallPage is shown if not already + if (Navigator.canPop(context) && ModalRoute.of(context)?.settings.name != '/call') { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => CallPage( + displayName: currentPhoneNumber!, + phoneNumber: currentPhoneNumber!, + thumbnail: null, + ), + ), + ); + } + } + break; + case "callEnded": + case "callRemoved": + Navigator.pop(context); + currentPhoneNumber = null; + break; } }); } - // Add a GlobalKey for Navigator - static final GlobalKey navigatorKey = GlobalKey(); - Future makeGsmCall( BuildContext context, { required String phoneNumber, @@ -42,15 +71,17 @@ class CallService { currentPhoneNumber = phoneNumber; final result = await _channel.invokeMethod('makeGsmCall', {"phoneNumber": phoneNumber}); if (result["status"] == "calling") { - // CallPage will be shown via CallConnectionService callback - } else if (result["status"] == "pending_default_dialer") { - print("Waiting for user to set app as default dialer"); + // CallPage will be shown via MyInCallService callback + } else { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("Please set this app as your default dialer to proceed")), + SnackBar(content: Text("Failed to initiate call")), ); } } catch (e) { print("Error making call: $e"); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Error making call: $e")), + ); rethrow; } } @@ -59,11 +90,14 @@ class CallService { try { final result = await _channel.invokeMethod('hangUpCall'); if (result["status"] == "ended") { - Navigator.pop(context); + // Navigator.pop will be handled by MyInCallService callback } } catch (e) { print("Error hanging up call: $e"); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Error hanging up call: $e")), + ); rethrow; } } -} +} \ No newline at end of file