diff --git a/dialer/lib/features/contacts/contact_page.dart b/dialer/lib/features/contacts/contact_page.dart index 9f09490..ad0b2bd 100644 --- a/dialer/lib/features/contacts/contact_page.dart +++ b/dialer/lib/features/contacts/contact_page.dart @@ -2,6 +2,39 @@ import 'package:dialer/features/contacts/contact_state.dart'; import 'package:dialer/features/contacts/widgets/alphabet_scroll_page.dart'; import 'package:flutter/material.dart'; import 'package:dialer/widgets/loading_indicator.dart'; +import 'package:flutter_contacts/flutter_contacts.dart'; + +const bool _privacyMode = bool.fromEnvironment('privacy-mode', defaultValue: false); + +List _maskContacts(List original) { + if (!_privacyMode) return original; + return original.map((c) { + final maskedName = _maskName(c.displayName); + final phones = c.phones.map((p) => Phone( + _maskPhone(p.number), + label: p.label, + )).toList(); + final masked = Contact() + ..displayName = maskedName + ..thumbnail = c.thumbnail + ..phones = phones + ..id = c.id; + return masked; + }).toList(); +} + +String _maskName(String name) { + final parts = name.split(' '); + return parts.map((part) { + if (part.length < 2) return part; + return '${part[0]}${'*' * (part.length - 1)}'; + }).join(' '); +} + +String _maskPhone(String phone) { + if (phone.length < 3) return phone; + return phone.substring(0, 2) + '*' * (phone.length - 2); +} class ContactPage extends StatefulWidget { const ContactPage({super.key}); @@ -19,7 +52,7 @@ class _ContactPageState extends State { ? const LoadingIndicatorWidget() : AlphabetScrollPage( scrollOffset: contactState.scrollOffset, - contacts: contactState.contacts, // Use all contacts here + contacts: _maskContacts(contactState.contacts), ), ); } diff --git a/dialer/lib/features/favorites/favorites_page.dart b/dialer/lib/features/favorites/favorites_page.dart index 47f74ec..6a13629 100644 --- a/dialer/lib/features/favorites/favorites_page.dart +++ b/dialer/lib/features/favorites/favorites_page.dart @@ -2,6 +2,39 @@ import 'package:dialer/features/contacts/contact_state.dart'; import 'package:dialer/features/contacts/widgets/alphabet_scroll_page.dart'; import 'package:flutter/material.dart'; import 'package:dialer/widgets/loading_indicator.dart'; +import 'package:flutter_contacts/flutter_contacts.dart'; + +const bool _privacyMode = bool.fromEnvironment('privacy-mode', defaultValue: false); + +List _maskContacts(List original) { + if (!_privacyMode) return original; + return original.map((c) { + final maskedName = _maskName(c.displayName); + final phones = c.phones.map((p) => Phone( + _maskPhone(p.number), + label: p.label, + )).toList(); + final masked = Contact() + ..displayName = maskedName + ..thumbnail = c.thumbnail + ..phones = phones + ..id = c.id; + return masked; + }).toList(); +} + +String _maskName(String name) { + final parts = name.split(' '); + return parts.map((part) { + if (part.length < 2) return part; + return '${part[0]}${'*' * (part.length - 1)}'; + }).join(' '); +} + +String _maskPhone(String phone) { + if (phone.length < 3) return phone; + return phone.substring(0, 2) + '*' * (phone.length - 2); +} class FavoritesPage extends StatefulWidget { const FavoritesPage({super.key}); @@ -24,8 +57,7 @@ class _FavoritesPageState extends State { ? const LoadingIndicatorWidget() : AlphabetScrollPage( scrollOffset: contactState.scrollOffset, - contacts: - contactState.favoriteContacts, // Use only favorites here + contacts: _maskContacts(contactState.favoriteContacts), ), ); } diff --git a/dialer/lib/features/history/history_page.dart b/dialer/lib/features/history/history_page.dart index 589a136..2025f21 100644 --- a/dialer/lib/features/history/history_page.dart +++ b/dialer/lib/features/history/history_page.dart @@ -22,6 +22,23 @@ class History { ); } +const bool _privacyMode = bool.fromEnvironment('privacy-mode', defaultValue: false); + +String _maskName(String name) { + if (!_privacyMode) return name; + final parts = name.split(' '); + return parts.map((part) { + if (part.length < 2) return part; + return '${part[0]}${'*' * (part.length - 1)}'; + }).join(' '); +} + +String _maskPhone(String phone) { + if (!_privacyMode) return phone; + if (phone.length < 3) return phone; + return phone.substring(0, 2) + '*' * (phone.length - 2); +} + class HistoryPage extends StatefulWidget { const HistoryPage({Key? key}) : super(key: key); @@ -198,6 +215,7 @@ class _HistoryPageState extends State with SingleTickerProviderStat } else if (item is History) { final history = item; final contact = history.contact; + final maskedName = _maskName(contact.displayName); final isExpanded = _expandedIndex == index; // Generate the avatar color @@ -213,14 +231,14 @@ class _HistoryPageState extends State with SingleTickerProviderStat : CircleAvatar( backgroundColor: avatarColor, child: Text( - contact.displayName.isNotEmpty - ? contact.displayName[0].toUpperCase() + maskedName.isNotEmpty + ? maskedName[0] : '?', style: TextStyle(color: darken(avatarColor, 0.4)), ), ), title: Text( - contact.displayName, + maskedName, style: const TextStyle(color: Colors.white), ), subtitle: Text( @@ -367,7 +385,7 @@ class CallDetailsPage extends StatelessWidget { const SizedBox(width: 16), Expanded( child: Text( - contact.displayName, + _maskName(contact.displayName), style: const TextStyle(color: Colors.white, fontSize: 24), ), ), @@ -399,7 +417,7 @@ class CallDetailsPage extends StatelessWidget { if (contact.phones.isNotEmpty) DetailRow( label: 'Number:', - value: contact.phones.first.number, + value: _maskPhone(contact.phones.first.number), ), ], ), diff --git a/dialer/lib/features/home/home_page.dart b/dialer/lib/features/home/home_page.dart index 23530a1..2e32621 100644 --- a/dialer/lib/features/home/home_page.dart +++ b/dialer/lib/features/home/home_page.dart @@ -7,6 +7,17 @@ import 'package:flutter_contacts/flutter_contacts.dart'; import 'package:dialer/features/settings/settings.dart'; import '../../services/contact_service.dart'; +const bool _privacyMode = bool.fromEnvironment('privacy-mode', defaultValue: false); + +String _maskName(String name) { + if (!_privacyMode) return name; + final parts = name.split(' '); + return parts.map((part) { + if (part.length < 2) return part; + return '${part[0]}${'*' * (part.length - 1)}'; + }).join(' '); +} + class _MyHomePageState extends State with SingleTickerProviderStateMixin { late TabController _tabController; @@ -128,14 +139,7 @@ class _MyHomePageState extends State suggestionsBuilder: (BuildContext context, SearchController controller) { return _contactSuggestions.map((contact) { - return ListTile( - key: ValueKey(contact.id), - title: Text(contact.displayName, - style: const TextStyle(color: Colors.white)), - onTap: () { - controller.closeView(contact.displayName); - }, - ); + return _buildSuggestionTile(contact, controller); }).toList(); }, ), @@ -225,6 +229,17 @@ class _MyHomePageState extends State ), ); } + + Widget _buildSuggestionTile(Contact contact, SearchController controller) { + final maskedName = _maskName(contact.displayName); + return ListTile( + key: ValueKey(contact.id), + title: Text(maskedName, style: const TextStyle(color: Colors.white)), + onTap: () { + controller.closeView(maskedName); + }, + ); + } } class MyHomePage extends StatefulWidget {