Compare commits
5 Commits
9ba714d25c
...
8f36157808
Author | SHA1 | Date | |
---|---|---|---|
8f36157808 | |||
bec5f5b5d2 | |||
6b3bac08e5 | |||
5c8373f575 | |||
7e1c48d38e |
@ -95,7 +95,6 @@ class MainActivity : FlutterActivity() {
|
||||
pendingIncomingCall = null
|
||||
}
|
||||
}
|
||||
checkAndRequestDefaultDialer()
|
||||
result.success(true)
|
||||
}
|
||||
"makeGsmCall" -> {
|
||||
@ -191,6 +190,33 @@ class MainActivity : FlutterActivity() {
|
||||
result.error("SPEAKER_FAILED", "No active call or failed to set speaker", null)
|
||||
}
|
||||
}
|
||||
"isDefaultDialer" -> {
|
||||
val isDefault = isDefaultDialer()
|
||||
Log.d(TAG, "isDefaultDialer called, returning: $isDefault")
|
||||
result.success(isDefault)
|
||||
}
|
||||
"requestDefaultDialer" -> {
|
||||
checkAndRequestDefaultDialer()
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -216,6 +242,13 @@ class MainActivity : FlutterActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun isDefaultDialer(): Boolean {
|
||||
val telecomManager = getSystemService(TELECOM_SERVICE) as TelecomManager
|
||||
val currentDefault = telecomManager.defaultDialerPackage
|
||||
Log.d(TAG, "Checking default dialer: current=$currentDefault, myPackage=$packageName")
|
||||
return currentDefault == packageName
|
||||
}
|
||||
|
||||
private fun checkAndRequestDefaultDialer() {
|
||||
val telecomManager = getSystemService(TELECOM_SERVICE) as TelecomManager
|
||||
val currentDefault = telecomManager.defaultDialerPackage
|
||||
|
@ -52,6 +52,22 @@ class MyInCallService : InCallService() {
|
||||
}
|
||||
} ?: 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() {
|
||||
@ -111,7 +127,7 @@ class MyInCallService : InCallService() {
|
||||
}
|
||||
call.registerCallback(callCallback)
|
||||
if (callAudioState != null) {
|
||||
val audioState = callAudioState
|
||||
val audioState = callAudioState
|
||||
channel?.invokeMethod("audioStateChanged", mapOf(
|
||||
"route" to audioState.route,
|
||||
"muted" to audioState.isMuted,
|
||||
|
@ -1,10 +1,10 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_contacts/flutter_contacts.dart';
|
||||
import 'package:dialer/services/call_service.dart';
|
||||
import 'package:dialer/services/obfuscate_service.dart';
|
||||
import 'package:dialer/widgets/username_color_generator.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class CallPage extends StatefulWidget {
|
||||
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(() {
|
||||
_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 {
|
||||
@ -195,7 +217,7 @@ class _CallPageState extends State<CallPage> {
|
||||
print('CallPage: Error hanging up: $e');
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text("Error hanging up: $e")),
|
||||
SnackBar(content: Text('Error hanging up: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -227,298 +249,316 @@ class _CallPageState extends State<CallPage> {
|
||||
print(
|
||||
'CallPage: Building UI, _callStatus: $_callStatus, route: ${ModalRoute.of(context)?.settings.name ?? "unknown"}');
|
||||
return PopScope(
|
||||
canPop:
|
||||
_callStatus == "Call Ended", // Allow navigation only if call ended
|
||||
child: Scaffold(
|
||||
body: Container(
|
||||
color: Colors.black,
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 35),
|
||||
ObfuscatedAvatar(
|
||||
imageBytes: widget.thumbnail,
|
||||
radius: avatarRadius,
|
||||
backgroundColor:
|
||||
generateColorFromName(widget.displayName),
|
||||
fallbackInitial: widget.displayName,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
icingProtocolOk ? Icons.lock : Icons.lock_open,
|
||||
canPop: _callStatus == "Call Ended",
|
||||
onPopInvoked: (didPop) {
|
||||
if (!didPop) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Cannot leave during an active call')),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Scaffold(
|
||||
body: Container(
|
||||
color: Colors.black,
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 35),
|
||||
ObfuscatedAvatar(
|
||||
imageBytes: widget.thumbnail,
|
||||
radius: avatarRadius,
|
||||
backgroundColor:
|
||||
generateColorFromName(widget.displayName),
|
||||
fallbackInitial: widget.displayName,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
icingProtocolOk ? Icons.lock : Icons.lock_open,
|
||||
color: icingProtocolOk ? Colors.green : Colors.red,
|
||||
size: 16,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'Icing protocol: ${icingProtocolOk ? "ok" : "ko"}',
|
||||
style: TextStyle(
|
||||
color:
|
||||
icingProtocolOk ? Colors.green : Colors.red,
|
||||
size: 16,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'Icing protocol: ${icingProtocolOk ? "ok" : "ko"}',
|
||||
style: TextStyle(
|
||||
color:
|
||||
icingProtocolOk ? Colors.green : Colors.red,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
_obfuscateService.obfuscateData(widget.displayName),
|
||||
style: TextStyle(
|
||||
fontSize: nameFontSize,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
widget.phoneNumber,
|
||||
style: TextStyle(
|
||||
fontSize: statusFontSize, color: Colors.white70),
|
||||
),
|
||||
Text(
|
||||
_callStatus,
|
||||
style: TextStyle(
|
||||
fontSize: statusFontSize, color: Colors.white70),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
if (isKeypadVisible) ...[
|
||||
const Spacer(flex: 2),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
_typedDigits,
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.right,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: _toggleKeypad,
|
||||
icon: const Icon(Icons.close,
|
||||
color: Colors.white),
|
||||
),
|
||||
],
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: MediaQuery.of(context).size.height * 0.35,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: GridView.count(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
crossAxisCount: 3,
|
||||
childAspectRatio: 1.3,
|
||||
mainAxisSpacing: 8,
|
||||
crossAxisSpacing: 8,
|
||||
children: List.generate(12, (index) {
|
||||
String label;
|
||||
if (index < 9) {
|
||||
label = '${index + 1}';
|
||||
} else if (index == 9) {
|
||||
label = '*';
|
||||
} else if (index == 10) {
|
||||
label = '0';
|
||||
} else {
|
||||
label = '#';
|
||||
}
|
||||
return GestureDetector(
|
||||
onTap: () => _addDigit(label),
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.transparent,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 32, color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
const Spacer(flex: 1),
|
||||
] else ...[
|
||||
const Spacer(),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 32.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: _toggleMute,
|
||||
icon: Icon(
|
||||
isMuted ? Icons.mic_off : Icons.mic,
|
||||
color: isMuted
|
||||
? Colors.amber
|
||||
: Colors.white,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
isMuted ? 'Unmute' : 'Mute',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: _toggleKeypad,
|
||||
icon: const Icon(
|
||||
Icons.dialpad,
|
||||
color: Colors.white,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'Keypad',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: _toggleSpeaker,
|
||||
icon: Icon(
|
||||
isSpeaker
|
||||
? Icons.volume_up
|
||||
: Icons.volume_off,
|
||||
color: isSpeaker
|
||||
? Colors.amber
|
||||
: Colors.white,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'Speaker',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
if (isNumberUnknown)
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: _addContact,
|
||||
icon: const Icon(
|
||||
Icons.person_add,
|
||||
color: Colors.white,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'Add Contact',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(
|
||||
Icons.sim_card,
|
||||
color: Colors.white,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'Change SIM',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Spacer(flex: 3),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: GestureDetector(
|
||||
onTap: _hangUp,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.red,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.call_end,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
_obfuscateService.obfuscateData(widget.displayName),
|
||||
style: TextStyle(
|
||||
fontSize: nameFontSize,
|
||||
color: Colors.white,
|
||||
size: 32,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
widget.phoneNumber,
|
||||
style: TextStyle(
|
||||
fontSize: statusFontSize,
|
||||
color: Colors.white70,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
_callStatus,
|
||||
style: TextStyle(
|
||||
fontSize: statusFontSize,
|
||||
color: Colors.white70,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
if (isKeypadVisible) ...[
|
||||
const Spacer(flex: 2),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
_typedDigits,
|
||||
maxLines: 1,
|
||||
textAlign: TextAlign.right,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: _toggleKeypad,
|
||||
icon: const Icon(
|
||||
Icons.close,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: MediaQuery.of(context).size.height * 0.4,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 20),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: GridView.count(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
crossAxisCount: 3,
|
||||
childAspectRatio: 1.5,
|
||||
mainAxisSpacing: 8,
|
||||
crossAxisSpacing: 8,
|
||||
children: List.generate(12, (index) {
|
||||
String label;
|
||||
if (index < 9) {
|
||||
label = '${index + 1}';
|
||||
} else if (index == 9) {
|
||||
label = '*';
|
||||
} else if (index == 10) {
|
||||
label = '0';
|
||||
} else {
|
||||
label = '#';
|
||||
}
|
||||
return GestureDetector(
|
||||
onTap: () => _addDigit(label),
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.transparent,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 32,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
const Spacer(flex: 1),
|
||||
] else ...[
|
||||
const Spacer(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: _toggleMute,
|
||||
icon: Icon(
|
||||
isMuted ? Icons.mic_off : Icons.mic,
|
||||
color: isMuted
|
||||
? Colors.amber
|
||||
: Colors.white,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
isMuted ? 'Unmute' : 'Mute',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: _toggleKeypad,
|
||||
icon: const Icon(
|
||||
Icons.dialpad,
|
||||
color: Colors.white,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'Keypad',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: _toggleSpeaker,
|
||||
icon: Icon(
|
||||
isSpeaker
|
||||
? Icons.volume_up
|
||||
: Icons.volume_off,
|
||||
color: isSpeaker
|
||||
? Colors.amber
|
||||
: Colors.white,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'Speaker',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
if (isNumberUnknown)
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: _addContact,
|
||||
icon: const Icon(
|
||||
Icons.person_add,
|
||||
color: Colors.white,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'Add Contact',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(
|
||||
Icons.sim_card,
|
||||
color: Colors.white,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'Change SIM',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Spacer(flex: 3),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: GestureDetector(
|
||||
onTap: _hangUp,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.red,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.call_end,
|
||||
color: Colors.white,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
102
dialer/lib/features/home/default_dialer_prompt.dart
Normal file
102
dialer/lib/features/home/default_dialer_prompt.dart
Normal file
@ -0,0 +1,102 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class DefaultDialerPromptScreen extends StatelessWidget {
|
||||
const DefaultDialerPromptScreen({super.key});
|
||||
|
||||
Future<void> _requestDefaultDialer(BuildContext context) async {
|
||||
const channel = MethodChannel('call_service');
|
||||
try {
|
||||
await channel.invokeMethod('requestDefaultDialer');
|
||||
// Navigate to home page after requesting default dialer
|
||||
Navigator.of(context).pushReplacementNamed('/home');
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Error requesting default dialer: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _exploreApp(BuildContext context) {
|
||||
// Navigate to home page without requesting default dialer
|
||||
Navigator.of(context).pushReplacementNamed('/home');
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Set as Default Dialer',
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Text(
|
||||
'To handle calls effectively, Icing needs to be your default dialer app. This allows Icing to manage incoming and outgoing calls seamlessly.\n\nWithout the permission, Icing will not be able to encrypt calls.',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white70,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: ElevatedButton(
|
||||
onPressed: () => _exploreApp(context),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey[800],
|
||||
foregroundColor: Colors.white,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 16, vertical: 12),
|
||||
),
|
||||
child: Text('Explore App first'),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: ElevatedButton(
|
||||
onPressed: () => _requestDefaultDialer(context),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 16, vertical: 12),
|
||||
),
|
||||
child: Text('Set as Default Dialer'),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import 'package:dialer/features/home/home_page.dart';
|
||||
import 'package:dialer/features/home/default_dialer_prompt.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:dialer/features/contacts/contact_state.dart';
|
||||
import 'package:dialer/services/call_service.dart';
|
||||
@ -51,6 +52,17 @@ Future<void> _requestPermissions() async {
|
||||
class Dialer extends StatelessWidget {
|
||||
const Dialer({super.key});
|
||||
|
||||
Future<bool> _isDefaultDialer() async {
|
||||
const channel = MethodChannel('call_service');
|
||||
try {
|
||||
final isDefault = await channel.invokeMethod<bool>('isDefaultDialer');
|
||||
return isDefault ?? false;
|
||||
} catch (e) {
|
||||
print('Error checking default dialer: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ContactState(
|
||||
@ -59,7 +71,24 @@ class Dialer extends StatelessWidget {
|
||||
theme: ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
),
|
||||
home: SafeArea(child: MyHomePage()),
|
||||
initialRoute: '/',
|
||||
routes: {
|
||||
'/': (context) => FutureBuilder<bool>(
|
||||
future: _isDefaultDialer(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return Scaffold(
|
||||
body: Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
if (snapshot.hasError || !snapshot.hasData || snapshot.data == false) {
|
||||
return DefaultDialerPromptScreen();
|
||||
}
|
||||
return SafeArea(child: MyHomePage());
|
||||
},
|
||||
),
|
||||
'/home': (context) => SafeArea(child: MyHomePage()),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user