feat: Add CallPage for initiating calls with contact details
All checks were successful
/ mirror (push) Successful in 4s
All checks were successful
/ mirror (push) Successful in 4s
This commit is contained in:
parent
ecf4ea16d8
commit
d3a0a4740d
217
dialer/lib/features/call/call_page.dart
Normal file
217
dialer/lib/features/call/call_page.dart
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:dialer/services/obfuscate_service.dart';
|
||||||
|
import 'package:dialer/widgets/username_color_generator.dart';
|
||||||
|
|
||||||
|
class CallPage extends StatefulWidget {
|
||||||
|
final String displayName;
|
||||||
|
final Uint8List? thumbnail;
|
||||||
|
|
||||||
|
const CallPage({super.key, required this.displayName, this.thumbnail});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_CallPageState createState() => _CallPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CallPageState extends State<CallPage> {
|
||||||
|
final ObfuscateService _obfuscateService = ObfuscateService();
|
||||||
|
bool isMuted = false;
|
||||||
|
bool isSpeakerOn = false;
|
||||||
|
bool isKeypadVisible = false;
|
||||||
|
bool icingProtocolOk = true;
|
||||||
|
|
||||||
|
void _toggleMute() {
|
||||||
|
setState(() {
|
||||||
|
isMuted = !isMuted;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _toggleSpeaker() {
|
||||||
|
setState(() {
|
||||||
|
isSpeakerOn = !isSpeakerOn;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _toggleKeypad() {
|
||||||
|
setState(() {
|
||||||
|
isKeypadVisible = !isKeypadVisible;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _toggleIcingProtocol() {
|
||||||
|
setState(() {
|
||||||
|
icingProtocolOk = !icingProtocolOk;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _hangUp() {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final double avatarRadius = isKeypadVisible ? 30.0 : 60.0;
|
||||||
|
final double nameFontSize = isKeypadVisible ? 20.0 : 32.0;
|
||||||
|
final double statusFontSize = isKeypadVisible ? 16.0 : 20.0;
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
body: Container(
|
||||||
|
color: Colors.black,
|
||||||
|
child: SafeArea(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
// Fixed size header area
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
ObfuscatedAvatar(
|
||||||
|
imageBytes: widget.thumbnail,
|
||||||
|
radius: avatarRadius,
|
||||||
|
backgroundColor: generateColorFromName(widget.displayName),
|
||||||
|
fallbackInitial: widget.displayName,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
_obfuscateService.obfuscateData(widget.displayName),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: nameFontSize,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
Text(
|
||||||
|
'Calling...',
|
||||||
|
style: TextStyle(fontSize: statusFontSize, color: Colors.white70),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
// Scrollable middle section
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
// Keypad if visible
|
||||||
|
if (isKeypadVisible)
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 40),
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black45,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: GridView.count(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
crossAxisCount: 3,
|
||||||
|
mainAxisSpacing: 10,
|
||||||
|
crossAxisSpacing: 10,
|
||||||
|
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 Center(
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 24, color: Colors.white),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Icing protocol status
|
||||||
|
GestureDetector(
|
||||||
|
onTap: _toggleIcingProtocol,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
icingProtocolOk ? Icons.lock : Icons.lock_open,
|
||||||
|
color: icingProtocolOk ? Colors.green : Colors.red,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'Icing protocol: ${icingProtocolOk ? "ok" : "ko"}',
|
||||||
|
style: TextStyle(
|
||||||
|
color: icingProtocolOk ? Colors.green : Colors.red,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Control buttons
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: _toggleMute,
|
||||||
|
icon: Icon(
|
||||||
|
isMuted ? Icons.mic_off : Icons.mic,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 32,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: _toggleKeypad,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.dialpad,
|
||||||
|
color: isKeypadVisible ? Colors.amber : Colors.white,
|
||||||
|
size: 32,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: _toggleSpeaker,
|
||||||
|
icon: Icon(
|
||||||
|
isSpeakerOn ? Icons.volume_up : Icons.volume_off,
|
||||||
|
color: isSpeakerOn ? Colors.amber : Colors.white,
|
||||||
|
size: 32,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Fixed size footer with hang up button
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: _hangUp,
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.call_end,
|
||||||
|
color: Colors.red,
|
||||||
|
size: 48,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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 '../../../features/call/call_page.dart';
|
||||||
|
|
||||||
class ContactModal extends StatefulWidget {
|
class ContactModal extends StatefulWidget {
|
||||||
final Contact contact;
|
final Contact contact;
|
||||||
@ -263,6 +264,18 @@ class _ContactModalState extends State<ContactModal> {
|
|||||||
_launchPhoneDialer(phoneNumber);
|
_launchPhoneDialer(phoneNumber);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onLongPress: () {
|
||||||
|
// Navigate to the beautiful calling page demo
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (_) => CallPage(
|
||||||
|
displayName: widget.contact.displayName,
|
||||||
|
thumbnail: widget.contact.thumbnail,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.message, color: Colors.blue),
|
leading: const Icon(Icons.message, color: Colors.blue),
|
||||||
|
Loading…
Reference in New Issue
Block a user