feat: request perm in flutter, wait for perm before trying to become main dialer
This commit is contained in:
parent
c886e29d75
commit
5529a6e038
@ -1,14 +1,18 @@
|
||||
package com.icing.dialer.activities
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
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
|
||||
@ -20,69 +24,14 @@ class MainActivity : FlutterActivity() {
|
||||
private val KEYSTORE_CHANNEL = "com.example.keystore"
|
||||
private val CALLLOG_CHANNEL = "com.example.calllog"
|
||||
private val CALL_CHANNEL = "call_service"
|
||||
private val REQUEST_CALL_PERMISSIONS = 1
|
||||
private val TAG = "MainActivity"
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Log.d(TAG, "onCreate started")
|
||||
registerPhoneAccount()
|
||||
checkAndRequestDefaultDialer()
|
||||
Log.d(TAG, "onCreate completed")
|
||||
Log.d(TAG, "Waiting for Flutter to signal permissions")
|
||||
}
|
||||
|
||||
// private fun checkPermissions(): Boolean {
|
||||
// val permissions =
|
||||
// arrayOf(
|
||||
// Manifest.permission.CALL_PHONE,
|
||||
// Manifest.permission.READ_PHONE_STATE,
|
||||
// Manifest.permission.MANAGE_OWN_CALLS,
|
||||
// Manifest.permission.READ_CONTACTS // Add this
|
||||
// )
|
||||
// return permissions
|
||||
// .all {
|
||||
// ContextCompat.checkSelfPermission(this, it) ==
|
||||
// PackageManager.PERMISSION_GRANTED
|
||||
// }
|
||||
// .also { Log.d(TAG, "Permissions check result: $it") }
|
||||
// }
|
||||
|
||||
// private fun requestPermissions() {
|
||||
// ActivityCompat.requestPermissions(
|
||||
// this,
|
||||
// arrayOf(
|
||||
// Manifest.permission.CALL_PHONE,
|
||||
// Manifest.permission.READ_PHONE_STATE,
|
||||
// Manifest.permission.MANAGE_OWN_CALLS,
|
||||
// Manifest.permission.READ_CONTACTS // Add this
|
||||
// ),
|
||||
// REQUEST_CALL_PERMISSIONS
|
||||
// )
|
||||
// Log.d(TAG, "Permission request dispatched")
|
||||
// }
|
||||
|
||||
// override fun onRequestPermissionsResult(
|
||||
// requestCode: Int,
|
||||
// permissions: Array<out String>,
|
||||
// grantResults: IntArray
|
||||
// ) {
|
||||
// super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
// Log.d(TAG, "onRequestPermissionsResult: $requestCode, ${grantResults.joinToString()}")
|
||||
// if (requestCode == REQUEST_CALL_PERMISSIONS &&
|
||||
// grantResults.all { it == PackageManager.PERMISSION_GRANTED }
|
||||
// ) {
|
||||
// Log.d(TAG, "All permissions granted")
|
||||
// registerPhoneAccount()
|
||||
// checkAndRequestDefaultDialer()
|
||||
// } else {
|
||||
// Log.e(
|
||||
// TAG,
|
||||
// "Required permissions not granted: ${permissions.joinToString()},
|
||||
// ${grantResults.joinToString()}"
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun registerPhoneAccount() {
|
||||
val telecomManager = getSystemService(TELECOM_SERVICE) as TelecomManager
|
||||
val phoneAccountHandle =
|
||||
@ -90,6 +39,7 @@ class MainActivity : FlutterActivity() {
|
||||
ComponentName(this, CallConnectionService::class.java),
|
||||
"IcingDialerAccount"
|
||||
)
|
||||
Log.d(TAG, "PhoneAccountHandle component: ${phoneAccountHandle.componentName}")
|
||||
val phoneAccount =
|
||||
PhoneAccount.builder(phoneAccountHandle, "Icing Dialer")
|
||||
.setCapabilities(
|
||||
@ -100,18 +50,46 @@ class MainActivity : FlutterActivity() {
|
||||
telecomManager.registerPhoneAccount(phoneAccount)
|
||||
CallService.setPhoneAccountHandle(phoneAccountHandle)
|
||||
Log.d(TAG, "PhoneAccount registered: ${phoneAccountHandle.id}")
|
||||
Log.d(
|
||||
TAG,
|
||||
"Registered PhoneAccounts: ${telecomManager.callCapablePhoneAccounts.joinToString()}"
|
||||
)
|
||||
|
||||
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)
|
||||
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<String>("phoneNumber")
|
||||
if (phoneNumber != null) {
|
||||
@ -173,25 +151,34 @@ class MainActivity : FlutterActivity() {
|
||||
TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME,
|
||||
packageName
|
||||
)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
try {
|
||||
startActivityForResult(intent, 1001) // Use startActivityForResult to track response
|
||||
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()
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Already the default dialer")
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchDefaultAppsSettings() {
|
||||
val settingsIntent = Intent(android.provider.Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
|
||||
startActivity(settingsIntent)
|
||||
Log.d(TAG, "Opened default apps settings as fallback")
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
Log.d(TAG, "onActivityResult: requestCode=$requestCode, resultCode=$resultCode")
|
||||
Log.d(TAG, "onActivityResult: requestCode=$requestCode, resultCode=$resultCode, data=$data")
|
||||
if (requestCode == 1001) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
Log.d(TAG, "User accepted default dialer change")
|
||||
} else {
|
||||
Log.d(TAG, "User rejected or canceled default dialer change")
|
||||
Log.d(TAG, "Default dialer prompt canceled (resultCode=$resultCode)")
|
||||
launchDefaultAppsSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,24 +5,38 @@ import android.telecom.ConnectionService
|
||||
import android.telecom.PhoneAccountHandle
|
||||
import android.telecom.TelecomManager
|
||||
import android.telecom.DisconnectCause
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
class CallConnectionService : ConnectionService() {
|
||||
companion object {
|
||||
var channel: MethodChannel? = null
|
||||
private const val TAG = "CallConnectionService"
|
||||
}
|
||||
|
||||
init {
|
||||
Log.d(TAG, "CallConnectionService initialized")
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Log.d(TAG, "Service created")
|
||||
}
|
||||
|
||||
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("CallConnectionService", "Connection state changed: $state")
|
||||
Log.d(TAG, "Connection state changed: $state")
|
||||
val stateStr = when (state) {
|
||||
STATE_DIALING -> "dialing"
|
||||
STATE_ACTIVE -> "active"
|
||||
@ -33,13 +47,14 @@ class CallConnectionService : ConnectionService() {
|
||||
}
|
||||
|
||||
override fun onDisconnect() {
|
||||
Log.d(TAG, "Connection disconnected")
|
||||
setDisconnected(DisconnectCause(DisconnectCause.LOCAL))
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
connection.setAddress(request.address, TelecomManager.PRESENTATION_ALLOWED)
|
||||
connection.setInitialized()
|
||||
connection.setDialing() // Start in dialing state
|
||||
connection.setDialing()
|
||||
return connection
|
||||
}
|
||||
|
||||
@ -47,12 +62,15 @@ class CallConnectionService : ConnectionService() {
|
||||
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()
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import 'package:dialer/features/home/home_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:dialer/features/contacts/contact_state.dart';
|
||||
import 'package:dialer/services/call_service.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'globals.dart' as globals;
|
||||
import 'package:dialer/services/cryptography/asymmetric_crypto_service.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
@ -39,6 +40,9 @@ Future<void> _requestPermissions() async {
|
||||
].request();
|
||||
if (statuses.values.every((status) => status.isGranted)) {
|
||||
print("All required permissions granted");
|
||||
// Signal MainActivity
|
||||
const channel = MethodChannel('call_service');
|
||||
await channel.invokeMethod('permissionsGranted');
|
||||
} else {
|
||||
print("Permissions denied: ${statuses.entries.where((e) => !e.value.isGranted).map((e) => e.key).join(', ')}");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user