feat: perms & UI methodchannel
All checks were successful
/ mirror (push) Successful in 5s
/ build-stealth (push) Successful in 8m24s
/ build (push) Successful in 8m24s

This commit is contained in:
Florian Griffon 2025-03-04 18:44:55 +01:00
parent 24dc5a9bbe
commit c886e29d75
3 changed files with 63 additions and 17 deletions

View File

@ -5,23 +5,31 @@ 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
object CallService {
private var phoneAccountHandle: PhoneAccountHandle? = null
fun setPhoneAccountHandle(handle: PhoneAccountHandle) {
phoneAccountHandle = handle
}
fun makeGsmCall(context: Context, phoneNumber: String): Boolean {
return try {
val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
val uri = Uri.parse("tel:$phoneNumber")
// Check CALL_PHONE permission
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
telecomManager.placeCall(uri, null)
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")

View File

@ -1,8 +1,10 @@
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 'globals.dart' as globals;
import 'package:dialer/services/cryptography/asymmetric_crypto_service.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
void main() async {
@ -13,19 +15,35 @@ void main() async {
final AsymmetricCryptoService cryptoService = AsymmetricCryptoService();
await cryptoService.initializeDefaultKeyPair();
// Request permissions before running the app
await _requestPermissions();
CallService(); // Initialize CallService
runApp(
MultiProvider(
providers: [
Provider<AsymmetricCryptoService>(
create: (_) => cryptoService,
),
// Add other providers here
],
child: Dialer(),
),
);
}
Future<void> _requestPermissions() async {
Map<Permission, PermissionStatus> statuses = await [
Permission.phone,
Permission.contacts,
].request();
if (statuses.values.every((status) => status.isGranted)) {
print("All required permissions granted");
} else {
print("Permissions denied: ${statuses.entries.where((e) => !e.value.isGranted).map((e) => e.key).join(', ')}");
}
}
class Dialer extends StatelessWidget {
const Dialer({super.key});
@ -33,11 +51,12 @@ class Dialer extends StatelessWidget {
Widget build(BuildContext context) {
return ContactState(
child: MaterialApp(
navigatorKey: CallService.navigatorKey,
theme: ThemeData(
brightness: Brightness.dark
brightness: Brightness.dark,
),
home: SafeArea(child: MyHomePage()),
)
),
);
}
}
}

View File

@ -4,26 +4,45 @@ import '../features/call/call_page.dart';
class CallService {
static const MethodChannel _channel = MethodChannel('call_service');
static String? currentPhoneNumber;
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,
),
),
);
} else if (state == "disconnected") {
Navigator.pop(navigatorKey.currentContext!);
}
}
});
}
// Add a GlobalKey for Navigator
static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
Future<void> makeGsmCall(
BuildContext context, {
required String phoneNumber,
String? displayName,
Uint8List? thumbnail, // Added optional thumbnail
Uint8List? thumbnail,
}) async {
try {
currentPhoneNumber = phoneNumber;
final result = await _channel.invokeMethod('makeGsmCall', {"phoneNumber": phoneNumber});
if (result["status"] == "calling") {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CallPage(
displayName: displayName ?? phoneNumber, // Fallback to phoneNumber if no name
phoneNumber: phoneNumber,
thumbnail: thumbnail, // Pass the thumbnail
),
),
);
// CallPage will be shown via CallConnectionService callback
} else if (result["status"] == "pending_default_dialer") {
print("Waiting for user to set app as default dialer");
ScaffoldMessenger.of(context).showSnackBar(