From 1867c025fd19486b112740191af3a75316de14e8 Mon Sep 17 00:00:00 2001 From: Bartosz Date: Sat, 15 Feb 2025 14:35:47 +0000 Subject: [PATCH] add --- .../kotlin/com/example/dialer/MainActivity.kt | 80 ++++++++++++++++--- dialer/lib/main.dart | 33 +++++++- 2 files changed, 100 insertions(+), 13 deletions(-) diff --git a/dialer/android/app/src/main/kotlin/com/example/dialer/MainActivity.kt b/dialer/android/app/src/main/kotlin/com/example/dialer/MainActivity.kt index cc34665..c012aa8 100644 --- a/dialer/android/app/src/main/kotlin/com/example/dialer/MainActivity.kt +++ b/dialer/android/app/src/main/kotlin/com/example/dialer/MainActivity.kt @@ -1,4 +1,3 @@ -// File: android/app/src/main/kotlin/com/example/dialer/MainActivity.kt package com.example.dialer import android.app.role.RoleManager @@ -7,6 +6,7 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.content.pm.PackageManager import android.os.Build import android.os.Bundle import android.os.Handler @@ -14,6 +14,8 @@ import android.telecom.PhoneAccount import android.telecom.PhoneAccountHandle import android.telecom.TelecomManager import android.util.Log +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel @@ -25,6 +27,9 @@ class MainActivity : FlutterActivity() { // Request code for the default-dialer request. private val DEFAULT_DIALER_REQUEST_CODE = 1001 + // Request code for runtime permissions. + private val PERMISSION_REQUEST_CODE = 1002 + // BroadcastReceiver to catch incoming call events from our ConnectionService. private val incomingCallReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { @@ -49,6 +54,9 @@ class MainActivity : FlutterActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + // Request runtime permissions. + checkAndRequestPermissions() + // Create the intent filter for incoming calls. val filter = IntentFilter("com.example.dialer.INCOMING_CALL") // Register the receiver. (For Android 13+ we must specify not exported.) @@ -121,16 +129,30 @@ class MainActivity : FlutterActivity() { super.onActivityResult(requestCode, resultCode, data) if (requestCode == DEFAULT_DIALER_REQUEST_CODE) { Log.d("MainActivity", "onActivityResult received: resultCode=$resultCode") - // Delay a short time before checking the role. - Handler(mainLooper).postDelayed({ - if (isDefaultDialer()) { - Log.d("MainActivity", "Default dialer successfully set") - registerManagedPhoneAccount() - notifyDefaultDialerSet() - } else { - Log.d("MainActivity", "Default dialer not set") + when (resultCode) { + RESULT_OK -> { + Log.d("MainActivity", "User granted default dialer role") + Handler(mainLooper).postDelayed({ + if (isDefaultDialer()) { + Log.d("MainActivity", "Default dialer successfully set") + registerManagedPhoneAccount() + notifyDefaultDialerSet() + } else { + Log.d("MainActivity", "Default dialer not set") + // Notify Flutter that the default dialer role was not set + methodChannel.invokeMethod("onDefaultDialerNotSet", null) + } + }, 500) } - }, 500) + RESULT_CANCELED -> { + Log.d("MainActivity", "User denied default dialer role") + // Notify Flutter that the user denied the request + methodChannel.invokeMethod("onDefaultDialerDenied", null) + } + else -> { + Log.d("MainActivity", "Unknown resultCode: $resultCode") + } + } } } @@ -146,6 +168,7 @@ class MainActivity : FlutterActivity() { .setShortDescription("Icing Custom Dialer") .build() telecomManager.registerPhoneAccount(phoneAccount) + Log.d("MainActivity", "PhoneAccount registered") } // Notify Flutter that we are now the default dialer. @@ -157,4 +180,39 @@ class MainActivity : FlutterActivity() { private fun notifyIncomingCall(phoneNumber: String) { methodChannel.invokeMethod("onIncomingCall", phoneNumber) } -} + + // Check and request runtime permissions. + private fun checkAndRequestPermissions() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + val permissionsNeeded = mutableListOf() + val permissionsToRequest = mutableListOf() + + // Check if permissions are granted + if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) { + permissionsToRequest.add(android.Manifest.permission.READ_PHONE_STATE) + } + if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { + permissionsToRequest.add(android.Manifest.permission.CALL_PHONE) + } + if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ANSWER_PHONE_CALLS) != PackageManager.PERMISSION_GRANTED) { + permissionsToRequest.add(android.Manifest.permission.ANSWER_PHONE_CALLS) + } + + // Request permissions if needed + if (permissionsToRequest.isNotEmpty()) { + ActivityCompat.requestPermissions(this, permissionsToRequest.toTypedArray(), PERMISSION_REQUEST_CODE) + } + } + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (requestCode == PERMISSION_REQUEST_CODE) { + if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) { + Log.d("MainActivity", "All permissions granted") + } else { + Log.d("MainActivity", "Some permissions denied") + } + } + } +} \ No newline at end of file diff --git a/dialer/lib/main.dart b/dialer/lib/main.dart index e3d6aa8..f96e3f8 100644 --- a/dialer/lib/main.dart +++ b/dialer/lib/main.dart @@ -1,4 +1,3 @@ -// File: lib/main.dart import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -25,6 +24,8 @@ class _IcingDialerAppState extends State { platform.setMethodCallHandler(handleMethodCalls); } + + Future handleMethodCalls(MethodCall call) async { switch (call.method) { case 'onDefaultDialerSet': @@ -33,6 +34,10 @@ class _IcingDialerAppState extends State { isDefaultDialer = true; }); break; + case 'onDefaultDialerDenied': + // Show a message to the user when they deny the request. + _showDefaultDialerDeniedDialog(); + break; case 'onIncomingCall': final phoneNumber = call.arguments as String? ?? "Unknown"; _showIncomingCallDialog(phoneNumber); @@ -42,6 +47,26 @@ class _IcingDialerAppState extends State { } } + // Show a dialog when the user denies the default dialer request. + void _showDefaultDialerDeniedDialog() { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Permission Denied'), + content: const Text( + 'To use this app as your default dialer, please grant the permission when prompted.'), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('OK'), + ), + ], + ), + ); + } + // Show a dialog when an incoming call is intercepted. void _showIncomingCallDialog(String phoneNumber) { showDialog( @@ -76,6 +101,10 @@ class _IcingDialerAppState extends State { await platform.invokeMethod('requestDefaultDialer'); } catch (e) { print('Error requesting default dialer: $e'); + // Show an error message if the request fails. + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Error requesting default dialer: $e')), + ); } } @@ -135,4 +164,4 @@ class CallScreen extends StatelessWidget { ), ); } -} +} \ No newline at end of file