monorepo/dialer/lib/features/call/call_page.dart
AlexisDanlos 97d6a3200b
All checks were successful
/ mirror (push) Successful in 4s
/ build (push) Successful in 8m27s
/ build-stealth (push) Successful in 8m36s
feat: implement dynamic width for typed digits display in CallPage
2025-03-16 23:39:30 +01:00

390 lines
16 KiB
Dart

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;
String _typedDigits = ""; // New state variable for pressed digits
bool isOnHold = false; // New state variable for hold status
void _addDigit(String digit) {
setState(() {
_typedDigits += digit;
});
}
void _toggleMute() {
setState(() {
isMuted = !isMuted;
});
}
void _toggleSpeaker() {
setState(() {
isSpeakerOn = !isSpeakerOn;
});
}
void _toggleKeypad() {
setState(() {
isKeypadVisible = !isKeypadVisible;
});
}
void _toggleIcingProtocol() {
setState(() {
icingProtocolOk = !icingProtocolOk;
});
}
void _toggleHold() {
setState(() {
isOnHold = !isOnHold;
});
}
void _hangUp() {
Navigator.pop(context);
}
@override
Widget build(BuildContext context) {
final double avatarRadius = isKeypadVisible ? 45.0 : 45.0; // Smaller avatar
final double nameFontSize = isKeypadVisible ? 24.0 : 24.0; // Smaller font
final double statusFontSize = isKeypadVisible ? 16.0 : 16.0; // Smaller status
// Calculate max visible chars based on screen width
double screenWidth = MediaQuery.of(context).size.width;
// Estimate ~14 pixels per character for the digit display (fontSize 24)
// Subtract some padding (40) for the margins
int estimatedMaxChars = ((screenWidth - 80) ~/ 14).clamp(10, 100) - 1;
// Helper function to format the displayed digits with dynamic width
String _formatDisplayDigits() {
if (_typedDigits.length <= estimatedMaxChars) {
return _typedDigits;
} else {
// Show ellipsis at the beginning and the most recent digits
return '...' + _typedDigits.substring(_typedDigits.length - estimatedMaxChars + 3);
}
}
return Scaffold(
body: Container(
color: Colors.black,
child: SafeArea(
child: Column(
children: [
// Top section - make it more compact
Container(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
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,
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(
'Calling...',
style: TextStyle(fontSize: statusFontSize, color: Colors.white70),
),
],
),
),
// Middle section - make it flexible and scrollable if needed
Expanded(
child: Column(
children: [
if (isKeypadVisible) ...
[
// Updated typed digits display with dynamic width
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
_formatDisplayDigits(),
maxLines: 1,
textAlign: TextAlign.right,
overflow: TextOverflow.visible,
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),
),
],
),
),
// Modified keypad section
Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
for (var i = 0; i < 4; i++)
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (var j = 1; j <= 3; j++)
Expanded(
child: AspectRatio(
aspectRatio: 1.5,
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Material(
color: Colors.transparent,
child: InkWell(
customBorder: const CircleBorder(),
onTap: () {
String label;
if (i < 3) {
label = '${i * 3 + j}';
} else {
if (j == 1) label = '*';
else if (j == 2) label = '0';
else label = '#';
}
_addDigit(label);
},
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.grey[900]?.withOpacity(0.5),
),
child: Center(
child: Text(
i < 3 ? '${i * 3 + j}'
: (j == 1 ? '*' : (j == 2 ? '0' : '#')),
style: const TextStyle(
fontSize: 32,
color: Colors.white,
),
),
),
),
),
),
),
),
),
],
),
],
),
),
] else ...
[
const Spacer(),
const Spacer(),
// Control buttons
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Main control buttons
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// Mute
Column(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: _toggleMute,
icon: Icon(
isMuted ? Icons.mic_off : Icons.mic,
color: Colors.white,
size: 32,
),
),
Text(
isMuted ? 'Unmute' : 'Mute',
style: const TextStyle(color: Colors.white, fontSize: 14),
),
],
),
// Keypad
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),
),
],
),
// Speaker
Column(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: _toggleSpeaker,
icon: Icon(
isSpeakerOn ? Icons.volume_up : Icons.volume_off,
color: isSpeakerOn ? Colors.amber : Colors.white,
size: 32,
),
),
const Text(
'Speaker',
style: TextStyle(color: Colors.white, fontSize: 14),
),
],
),
// New "mettre en attente" / Hold button
],
),
const SizedBox(height: 20),
// Additional buttons
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// Add Contact
Column(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () {
// ...existing code...
},
icon: const Icon(Icons.person_add, color: Colors.white, size: 32),
),
const Text('Add Contact',
style: TextStyle(color: Colors.white, fontSize: 14)),
],
),
// New "Hold" button inserted between Add Contact and Change SIM
Column(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: _toggleHold,
icon: Icon(
isOnHold ? Icons.play_arrow : Icons.pause,
color: Colors.white,
size: 32,
),
),
Text(
isOnHold ? 'Resume' : 'Hold',
style: const TextStyle(color: Colors.white, fontSize: 14),
),
],
),
// Change SIM
Column(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () {
// ...existing code...
},
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),
],
],
),
),
// Bottom section - hang up button
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,
),
),
),
),
],
),
),
),
);
}
}