feat: DTMF dialpad #55
@ -199,6 +199,24 @@ class MainActivity : FlutterActivity() {
|
|||||||
checkAndRequestDefaultDialer()
|
checkAndRequestDefaultDialer()
|
||||||
result.success(true)
|
result.success(true)
|
||||||
}
|
}
|
||||||
|
"sendDtmfTone" -> {
|
||||||
|
val digit = call.argument<String>("digit")
|
||||||
|
if (digit != null) {
|
||||||
|
val success = MyInCallService.sendDtmfTone(digit)
|
||||||
|
result.success(success)
|
||||||
|
} else {
|
||||||
|
result.error("INVALID_ARGUMENT", "Digit is null", null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"isDefaultDialer" -> {
|
||||||
|
val isDefault = isDefaultDialer()
|
||||||
|
Log.d(TAG, "isDefaultDialer called, returning: $isDefault")
|
||||||
|
result.success(isDefault)
|
||||||
|
}
|
||||||
|
"requestDefaultDialer" -> {
|
||||||
|
checkAndRequestDefaultDialer()
|
||||||
|
result.success(true)
|
||||||
|
}
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,22 @@ class MyInCallService : InCallService() {
|
|||||||
}
|
}
|
||||||
} ?: false
|
} ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sendDtmfTone(digit: String): Boolean {
|
||||||
|
return instance?.let { service ->
|
||||||
|
try {
|
||||||
|
currentCall?.let { call ->
|
||||||
|
call.playDtmfTone(digit[0])
|
||||||
|
call.stopDtmfTone()
|
||||||
|
Log.d(TAG, "Sent DTMF tone: $digit")
|
||||||
|
true
|
||||||
|
} ?: false
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to send DTMF tone: $e")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} ?: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val callCallback = object : Call.Callback() {
|
private val callCallback = object : Call.Callback() {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
|
||||||
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:dialer/services/call_service.dart';
|
import 'package:dialer/services/call_service.dart';
|
||||||
import 'package:dialer/services/obfuscate_service.dart';
|
import 'package:dialer/services/obfuscate_service.dart';
|
||||||
import 'package:dialer/widgets/username_color_generator.dart';
|
import 'package:dialer/widgets/username_color_generator.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
class CallPage extends StatefulWidget {
|
class CallPage extends StatefulWidget {
|
||||||
final String displayName;
|
final String displayName;
|
||||||
@ -124,10 +124,32 @@ class _CallPageState extends State<CallPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _addDigit(String digit) {
|
void _addDigit(String digit) async {
|
||||||
|
print('CallPage: Tapped digit: $digit');
|
||||||
setState(() {
|
setState(() {
|
||||||
_typedDigits += digit;
|
_typedDigits += digit;
|
||||||
});
|
});
|
||||||
|
// Send DTMF tone
|
||||||
|
const channel = MethodChannel('call_service');
|
||||||
|
try {
|
||||||
|
final success =
|
||||||
|
await channel.invokeMethod<bool>('sendDtmfTone', {'digit': digit});
|
||||||
|
if (success != true) {
|
||||||
|
print('CallPage: Failed to send DTMF tone for $digit');
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Failed to send DTMF tone')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('CallPage: Error sending DTMF tone: $e');
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Error sending DTMF tone: $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _toggleMute() async {
|
void _toggleMute() async {
|
||||||
@ -195,7 +217,7 @@ class _CallPageState extends State<CallPage> {
|
|||||||
print('CallPage: Error hanging up: $e');
|
print('CallPage: Error hanging up: $e');
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text("Error hanging up: $e")),
|
SnackBar(content: Text('Error hanging up: $e')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,8 +249,14 @@ class _CallPageState extends State<CallPage> {
|
|||||||
print(
|
print(
|
||||||
'CallPage: Building UI, _callStatus: $_callStatus, route: ${ModalRoute.of(context)?.settings.name ?? "unknown"}');
|
'CallPage: Building UI, _callStatus: $_callStatus, route: ${ModalRoute.of(context)?.settings.name ?? "unknown"}');
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop:
|
canPop: _callStatus == "Call Ended",
|
||||||
_callStatus == "Call Ended", // Allow navigation only if call ended
|
onPopInvoked: (didPop) {
|
||||||
|
if (!didPop) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Cannot leave during an active call')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
body: Container(
|
body: Container(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
@ -254,8 +282,7 @@ class _CallPageState extends State<CallPage> {
|
|||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
icingProtocolOk ? Icons.lock : Icons.lock_open,
|
icingProtocolOk ? Icons.lock : Icons.lock_open,
|
||||||
color:
|
color: icingProtocolOk ? Colors.green : Colors.red,
|
||||||
icingProtocolOk ? Colors.green : Colors.red,
|
|
||||||
size: 16,
|
size: 16,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
@ -282,12 +309,16 @@ class _CallPageState extends State<CallPage> {
|
|||||||
Text(
|
Text(
|
||||||
widget.phoneNumber,
|
widget.phoneNumber,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: statusFontSize, color: Colors.white70),
|
fontSize: statusFontSize,
|
||||||
|
color: Colors.white70,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
_callStatus,
|
_callStatus,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: statusFontSize, color: Colors.white70),
|
fontSize: statusFontSize,
|
||||||
|
color: Colors.white70,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -298,8 +329,7 @@ class _CallPageState extends State<CallPage> {
|
|||||||
if (isKeypadVisible) ...[
|
if (isKeypadVisible) ...[
|
||||||
const Spacer(flex: 2),
|
const Spacer(flex: 2),
|
||||||
Padding(
|
Padding(
|
||||||
padding:
|
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
const EdgeInsets.symmetric(horizontal: 20.0),
|
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
@ -319,20 +349,23 @@ class _CallPageState extends State<CallPage> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
onPressed: _toggleKeypad,
|
onPressed: _toggleKeypad,
|
||||||
icon: const Icon(Icons.close,
|
icon: const Icon(
|
||||||
color: Colors.white),
|
Icons.close,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
height: MediaQuery.of(context).size.height * 0.35,
|
height: MediaQuery.of(context).size.height * 0.4,
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 20),
|
margin: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
child: GridView.count(
|
child: GridView.count(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
crossAxisCount: 3,
|
crossAxisCount: 3,
|
||||||
childAspectRatio: 1.3,
|
childAspectRatio: 1.5,
|
||||||
mainAxisSpacing: 8,
|
mainAxisSpacing: 8,
|
||||||
crossAxisSpacing: 8,
|
crossAxisSpacing: 8,
|
||||||
children: List.generate(12, (index) {
|
children: List.generate(12, (index) {
|
||||||
@ -357,7 +390,9 @@ class _CallPageState extends State<CallPage> {
|
|||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 32, color: Colors.white),
|
fontSize: 32,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -369,8 +404,7 @@ class _CallPageState extends State<CallPage> {
|
|||||||
] else ...[
|
] else ...[
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Padding(
|
Padding(
|
||||||
padding:
|
padding: const EdgeInsets.symmetric(horizontal: 32.0),
|
||||||
const EdgeInsets.symmetric(horizontal: 32.0),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -395,7 +429,8 @@ class _CallPageState extends State<CallPage> {
|
|||||||
isMuted ? 'Unmute' : 'Mute',
|
isMuted ? 'Unmute' : 'Mute',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 14),
|
fontSize: 14,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -414,7 +449,8 @@ class _CallPageState extends State<CallPage> {
|
|||||||
'Keypad',
|
'Keypad',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 14),
|
fontSize: 14,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -437,7 +473,8 @@ class _CallPageState extends State<CallPage> {
|
|||||||
'Speaker',
|
'Speaker',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 14),
|
fontSize: 14,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -464,7 +501,8 @@ class _CallPageState extends State<CallPage> {
|
|||||||
'Add Contact',
|
'Add Contact',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 14),
|
fontSize: 14,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -483,7 +521,8 @@ class _CallPageState extends State<CallPage> {
|
|||||||
'Change SIM',
|
'Change SIM',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 14),
|
fontSize: 14,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -519,6 +558,7 @@ class _CallPageState extends State<CallPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
));
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user