feat: trigger call from app
All checks were successful
/ mirror (push) Successful in 4s

This commit is contained in:
Florian Griffon 2025-02-18 00:48:41 +01:00 committed by stcb
parent 21f470a464
commit ae56fc876f
6 changed files with 102 additions and 17 deletions

View File

@ -15,7 +15,7 @@
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:enableOnBackInvokedCallback="true"> android:enableOnBackInvokedCallback="true">
<activity <activity
android:name=".MainActivity" android:name=".activities.MainActivity"
android:exported="true" android:exported="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:taskAffinity="" android:taskAffinity=""

View File

@ -1,22 +1,46 @@
package com.icing.dialer package com.icing.dialer.activities
import android.database.Cursor import android.database.Cursor
import android.os.Bundle import android.os.Bundle
import android.provider.CallLog import android.provider.CallLog
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
import com.icing.dialer.KeystoreHelper import com.icing.dialer.KeystoreHelper
import com.icing.dialer.services.CallService
class MainActivity: FlutterActivity() { class MainActivity: FlutterActivity() {
// Existing channel for keystore operations. // Existing channel for keystore operations.
private val KEYSTORE_CHANNEL = "com.example.keystore" private val KEYSTORE_CHANNEL = "com.example.keystore"
// New channel for call log access. // New channel for call log access.
private val CALLLOG_CHANNEL = "com.example.calllog" private val CALLLOG_CHANNEL = "com.example.calllog"
private val CALL_CHANNEL = "call_service"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) { override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine) super.configureFlutterEngine(flutterEngine)
// Call service channel
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CALL_CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
"makeGsmCall" -> {
val phoneNumber = call.argument<String>("phoneNumber")
if (phoneNumber != null) {
CallService.makeGsmCall(this, phoneNumber)
result.success("Calling $phoneNumber")
} else {
result.error("INVALID_PHONE_NUMBER", "Phone number is required", null)
}
}
"hangUpCall" -> {
CallService.hangUpCall(this)
result.success("Call ended")
}
else -> result.notImplemented()
}
}
// Set up the keystore channel. // Set up the keystore channel.
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, KEYSTORE_CHANNEL) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, KEYSTORE_CHANNEL)
.setMethodCallHandler { call, result -> .setMethodCallHandler { call, result ->

View File

@ -0,0 +1,30 @@
package com.icing.dialer.services
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.telecom.TelecomManager
import android.os.Build
import android.util.Log
object CallService {
fun makeGsmCall(context: Context, phoneNumber: String) {
try {
val intent = Intent(Intent.ACTION_CALL)
intent.data = Uri.parse("tel:$phoneNumber")
context.startActivity(intent)
} catch (e: Exception) {
Log.e("CallService", "Error making GSM call: ${e.message}")
}
}
fun hangUpCall(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
telecomManager.endCall()
} else {
Log.e("CallService", "Hangup call is only supported on Android P or later.")
}
}
}

View File

@ -1,10 +1,9 @@
// lib/pages/composition_page.dart
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_contacts/flutter_contacts.dart'; import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../../services/contact_service.dart'; import '../../services/contact_service.dart';
import '../../services/obfuscate_service.dart'; // Import ObfuscateService import '../../services/obfuscate_service.dart'; // Import ObfuscateService
import '../../services/call_service.dart'; // Import the CallService
import '../contacts/widgets/add_contact_button.dart'; import '../contacts/widgets/add_contact_button.dart';
class CompositionPage extends StatefulWidget { class CompositionPage extends StatefulWidget {
@ -23,6 +22,9 @@ class _CompositionPageState extends State<CompositionPage> {
// Instantiate the ObfuscateService // Instantiate the ObfuscateService
final ObfuscateService _obfuscateService = ObfuscateService(); final ObfuscateService _obfuscateService = ObfuscateService();
// Instantiate the CallService
final CallService _callService = CallService();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -71,13 +73,15 @@ class _CompositionPageState extends State<CompositionPage> {
}); });
} }
// Function to call a contact's number // Function to call a contact's number using the CallService
void _launchPhoneDialer(String phoneNumber) async { void _makeCall(String phoneNumber) async {
final uri = Uri(scheme: 'tel', path: phoneNumber); try {
if (await canLaunchUrl(uri)) { await _callService.makeGsmCall(phoneNumber);
await launchUrl(uri); setState(() {
} else { dialedNumber = phoneNumber;
debugPrint('Could not launch $phoneNumber'); });
} catch (e) {
debugPrint("Error making call: $e");
} }
} }
@ -128,13 +132,13 @@ class _CompositionPageState extends State<CompositionPage> {
trailing: Row( trailing: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
// Call button // Call button (Now using CallService)
IconButton( IconButton(
icon: Icon(Icons.phone, icon: Icon(Icons.phone,
color: Colors.green[300], color: Colors.green[300],
size: 20), size: 20),
onPressed: () { onPressed: () {
_launchPhoneDialer(phoneNumber); _makeCall(phoneNumber); // Make a call using CallService
}, },
), ),
// Message button // Message button

View File

@ -5,6 +5,7 @@ import 'package:url_launcher/url_launcher.dart';
import 'package:dialer/widgets/username_color_generator.dart'; import 'package:dialer/widgets/username_color_generator.dart';
import '../../../services/block_service.dart'; import '../../../services/block_service.dart';
import '../../../services/contact_service.dart'; import '../../../services/contact_service.dart';
import '../../../services/call_service.dart'; // Import CallService
class ContactModal extends StatefulWidget { class ContactModal extends StatefulWidget {
final Contact contact; final Contact contact;
@ -28,6 +29,7 @@ class _ContactModalState extends State<ContactModal> {
late String phoneNumber; late String phoneNumber;
bool isBlocked = false; bool isBlocked = false;
final ObfuscateService _obfuscateService = ObfuscateService(); final ObfuscateService _obfuscateService = ObfuscateService();
final CallService _callService = CallService(); // Instantiate CallService
@override @override
void initState() { void initState() {
@ -258,9 +260,9 @@ class _ContactModalState extends State<ContactModal> {
_obfuscateService.obfuscateData(phoneNumber), _obfuscateService.obfuscateData(phoneNumber),
style: const TextStyle(color: Colors.white), style: const TextStyle(color: Colors.white),
), ),
onTap: () { onTap: () async {
if (widget.contact.phones.isNotEmpty) { if (widget.contact.phones.isNotEmpty) {
_launchPhoneDialer(phoneNumber); await _callService.makeGsmCall(phoneNumber);
} }
}, },
), ),
@ -329,12 +331,11 @@ class _ContactModalState extends State<ContactModal> {
icon: Icon( icon: Icon(
isBlocked ? Icons.block : Icons.block_flipped), isBlocked ? Icons.block : Icons.block_flipped),
label: Text(isBlocked ? 'Unblock' : 'Block'), label: Text(isBlocked ? 'Unblock' : 'Block'),
),
), ),
),
], ],
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
], ],
), ),

View File

@ -0,0 +1,26 @@
import 'package:flutter/services.dart';
// Service to manage call-related operations
class CallService {
static const MethodChannel _channel = MethodChannel('call_service');
// Function to make a GSM call
Future<void> makeGsmCall(String phoneNumber) async {
try {
await _channel.invokeMethod('makeGsmCall', {"phoneNumber": phoneNumber});
} catch (e) {
print("Error making call: $e");
rethrow;
}
}
// Function to hang up the current call
Future<void> hangUpCall() async {
try {
await _channel.invokeMethod('hangUpCall');
} catch (e) {
print("Error hanging up call: $e");
rethrow;
}
}
}