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