Compare commits
No commits in common. "819ec2949dc10aec4c1710e13abd00ef6332e1ad" and "475f4320477e93cff6f762d126bfb6c6fca6daf9" have entirely different histories.
819ec2949d
...
475f432047
@ -11,12 +11,12 @@ android {
|
|||||||
ndkVersion = flutter.ndkVersion
|
ndkVersion = flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = JavaVersion.VERSION_17
|
jvmTarget = JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
|
@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
|
||||||
|
@ -18,8 +18,8 @@ pluginManagement {
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||||
id "com.android.application" version "8.3.2" apply false
|
id "com.android.application" version "8.1.0" apply false
|
||||||
id "org.jetbrains.kotlin.android" version "2.0.20" apply false
|
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
include ":app"
|
include ":app"
|
||||||
|
@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_contacts/flutter_contacts.dart';
|
import 'package:flutter_contacts/flutter_contacts.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import '../../services/contact_service.dart';
|
import '../../services/contact_service.dart';
|
||||||
import '../../services/obfuscate_service.dart'; // Import ObfuscateService
|
|
||||||
import '../contacts/widgets/add_contact_button.dart';
|
import '../contacts/widgets/add_contact_button.dart';
|
||||||
|
|
||||||
class CompositionPage extends StatefulWidget {
|
class CompositionPage extends StatefulWidget {
|
||||||
@ -20,9 +19,6 @@ class _CompositionPageState extends State<CompositionPage> {
|
|||||||
List<Contact> _filteredContacts = [];
|
List<Contact> _filteredContacts = [];
|
||||||
final ContactService _contactService = ContactService();
|
final ContactService _contactService = ContactService();
|
||||||
|
|
||||||
// Instantiate the ObfuscateService
|
|
||||||
final ObfuscateService _obfuscateService = ObfuscateService();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -118,12 +114,16 @@ class _CompositionPageState extends State<CompositionPage> {
|
|||||||
: 'No phone number';
|
: 'No phone number';
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
_obfuscateService.obfuscateData(contact.displayName),
|
'${contact.displayName.isNotEmpty ? contact.displayName[0] : ''}...${contact.displayName.isNotEmpty ? contact.displayName[contact.displayName.length - 1] : ''}',
|
||||||
style: const TextStyle(color: Colors.white),
|
// contact.displayName
|
||||||
|
style:
|
||||||
|
const TextStyle(color: Colors.white),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
_obfuscateService.obfuscateData(phoneNumber),
|
'${phoneNumber.isNotEmpty ? phoneNumber[0] : ''}...${phoneNumber.isNotEmpty ? phoneNumber[phoneNumber.length - 1] : ''}',
|
||||||
style: const TextStyle(color: Colors.grey),
|
// phoneNumber,
|
||||||
|
style:
|
||||||
|
const TextStyle(color: Colors.grey),
|
||||||
),
|
),
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
// alphabet_scrollpage.dart
|
|
||||||
import 'dart:ui';
|
|
||||||
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/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_contacts/flutter_contacts.dart';
|
import 'package:flutter_contacts/flutter_contacts.dart';
|
||||||
import '../contact_state.dart';
|
import '../contact_state.dart';
|
||||||
|
import '../../../widgets/color_darkener.dart';
|
||||||
import 'add_contact_button.dart';
|
import 'add_contact_button.dart';
|
||||||
import 'contact_modal.dart';
|
import 'contact_modal.dart';
|
||||||
import 'share_own_qr.dart';
|
import 'share_own_qr.dart';
|
||||||
@ -26,8 +24,6 @@ class AlphabetScrollPage extends StatefulWidget {
|
|||||||
class _AlphabetScrollPageState extends State<AlphabetScrollPage> {
|
class _AlphabetScrollPageState extends State<AlphabetScrollPage> {
|
||||||
late ScrollController _scrollController;
|
late ScrollController _scrollController;
|
||||||
|
|
||||||
final ObfuscateService _obfuscateService = ObfuscateService();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -128,7 +124,7 @@ class _AlphabetScrollPageState extends State<AlphabetScrollPage> {
|
|||||||
vertical: 8.0, horizontal: 16.0),
|
vertical: 8.0, horizontal: 16.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
letter,
|
letter,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
@ -138,25 +134,31 @@ class _AlphabetScrollPageState extends State<AlphabetScrollPage> {
|
|||||||
// Contact Entries
|
// Contact Entries
|
||||||
...contactsForLetter.map((contact) {
|
...contactsForLetter.map((contact) {
|
||||||
String phoneNumber = contact.phones.isNotEmpty
|
String phoneNumber = contact.phones.isNotEmpty
|
||||||
? _obfuscateService.obfuscateData(contact.phones.first.number)
|
? contact.phones.first.number
|
||||||
: 'No phone number';
|
: 'No phone number';
|
||||||
Color avatarColor =
|
Color avatarColor =
|
||||||
generateColorFromName(contact.displayName);
|
generateColorFromName(contact.displayName);
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: ObfuscatedAvatar(
|
leading: (contact.thumbnail != null &&
|
||||||
imageBytes: contact.thumbnail,
|
contact.thumbnail!.isNotEmpty)
|
||||||
radius: 25,
|
? CircleAvatar(
|
||||||
|
backgroundImage:
|
||||||
|
MemoryImage(contact.thumbnail!),
|
||||||
|
)
|
||||||
|
: CircleAvatar(
|
||||||
backgroundColor: avatarColor,
|
backgroundColor: avatarColor,
|
||||||
fallbackInitial: contact.displayName,
|
child: Text(
|
||||||
|
contact.displayName.isNotEmpty
|
||||||
|
? contact.displayName[0].toUpperCase()
|
||||||
|
: '?',
|
||||||
|
style: TextStyle(
|
||||||
|
color: darken(avatarColor, 0.4)),
|
||||||
),
|
),
|
||||||
title: Text(
|
|
||||||
_obfuscateService.obfuscateData(contact.displayName),
|
|
||||||
style: const TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
subtitle: Text(
|
|
||||||
phoneNumber,
|
|
||||||
style: const TextStyle(color: Colors.white70),
|
|
||||||
),
|
),
|
||||||
|
title: Text(contact.displayName,
|
||||||
|
style: TextStyle(color: Colors.white)),
|
||||||
|
subtitle: Text(phoneNumber,
|
||||||
|
style: TextStyle(color: Colors.white70)),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:dialer/services/obfuscate_service.dart';
|
|
||||||
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:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
@ -28,7 +25,6 @@ class ContactModal extends StatefulWidget {
|
|||||||
class _ContactModalState extends State<ContactModal> {
|
class _ContactModalState extends State<ContactModal> {
|
||||||
late String phoneNumber;
|
late String phoneNumber;
|
||||||
bool isBlocked = false;
|
bool isBlocked = false;
|
||||||
final ObfuscateService _obfuscateService = ObfuscateService();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -103,8 +99,7 @@ class _ContactModalState extends State<ContactModal> {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: const Text('Delete Contact'),
|
title: const Text('Delete Contact'),
|
||||||
content: Text(
|
content: Text('Are you sure you want to delete ${widget.contact.displayName}?'),
|
||||||
'Are you sure you want to delete ${_obfuscateService.obfuscateData(widget.contact.displayName)}?'),
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(false),
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
@ -125,7 +120,7 @@ class _ContactModalState extends State<ContactModal> {
|
|||||||
|
|
||||||
// Show success message
|
// Show success message
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('${_obfuscateService.obfuscateData(widget.contact.displayName)} deleted')),
|
SnackBar(content: Text('${widget.contact.displayName} deleted')),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Close the modal
|
// Close the modal
|
||||||
@ -142,7 +137,7 @@ class _ContactModalState extends State<ContactModal> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
String email = widget.contact.emails.isNotEmpty
|
String email = widget.contact.emails.isNotEmpty
|
||||||
? _obfuscateService.obfuscateData(widget.contact.emails.first.address)
|
? widget.contact.emails.first.address
|
||||||
: 'No email';
|
: 'No email';
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
@ -189,7 +184,6 @@ class _ContactModalState extends State<ContactModal> {
|
|||||||
if (choice == 'delete') {
|
if (choice == 'delete') {
|
||||||
_deleteContact();
|
_deleteContact();
|
||||||
}
|
}
|
||||||
// Handle other choices if needed
|
|
||||||
},
|
},
|
||||||
itemBuilder: (BuildContext context) {
|
itemBuilder: (BuildContext context) {
|
||||||
return [
|
return [
|
||||||
@ -226,32 +220,40 @@ class _ContactModalState extends State<ContactModal> {
|
|||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
ObfuscatedAvatar(
|
CircleAvatar(
|
||||||
imageBytes: widget.contact.thumbnail,
|
|
||||||
radius: 50,
|
radius: 50,
|
||||||
|
backgroundImage: (widget.contact.thumbnail != null &&
|
||||||
|
widget.contact.thumbnail!.isNotEmpty)
|
||||||
|
? MemoryImage(widget.contact.thumbnail!)
|
||||||
|
: null,
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
generateColorFromName(widget.contact.displayName),
|
generateColorFromName(widget.contact.displayName),
|
||||||
fallbackInitial: widget.contact.displayName,
|
child: (widget.contact.thumbnail == null ||
|
||||||
|
widget.contact.thumbnail!.isEmpty)
|
||||||
|
? Text(
|
||||||
|
widget.contact.displayName.isNotEmpty
|
||||||
|
? widget.contact.displayName[0]
|
||||||
|
.toUpperCase()
|
||||||
|
: '?',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 40, color: Colors.white),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Text(
|
Text(
|
||||||
_obfuscateService.obfuscateData(widget.contact.displayName),
|
widget.contact.displayName,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 24,
|
fontSize: 24, fontWeight: FontWeight.bold),
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Divider(color: Colors.grey),
|
const Divider(),
|
||||||
// Contact Actions
|
// Contact Actions
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.phone, color: Colors.green),
|
leading: const Icon(Icons.phone, color: Colors.green),
|
||||||
title: Text(
|
title: Text(phoneNumber),
|
||||||
_obfuscateService.obfuscateData(phoneNumber),
|
|
||||||
style: const TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (widget.contact.phones.isNotEmpty) {
|
if (widget.contact.phones.isNotEmpty) {
|
||||||
_launchPhoneDialer(phoneNumber);
|
_launchPhoneDialer(phoneNumber);
|
||||||
@ -260,10 +262,7 @@ class _ContactModalState extends State<ContactModal> {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.message, color: Colors.blue),
|
leading: const Icon(Icons.message, color: Colors.blue),
|
||||||
title: Text(
|
title: Text(phoneNumber),
|
||||||
_obfuscateService.obfuscateData(phoneNumber),
|
|
||||||
style: const TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (widget.contact.phones.isNotEmpty) {
|
if (widget.contact.phones.isNotEmpty) {
|
||||||
_launchSms(phoneNumber);
|
_launchSms(phoneNumber);
|
||||||
@ -272,17 +271,14 @@ class _ContactModalState extends State<ContactModal> {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.email, color: Colors.orange),
|
leading: const Icon(Icons.email, color: Colors.orange),
|
||||||
title: Text(
|
title: Text(email),
|
||||||
email,
|
|
||||||
style: const TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (widget.contact.emails.isNotEmpty) {
|
if (widget.contact.emails.isNotEmpty) {
|
||||||
_launchEmail(email);
|
_launchEmail(email);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Divider(color: Colors.grey),
|
const Divider(),
|
||||||
// Favorite, Edit, and Block/Unblock Buttons
|
// Favorite, Edit, and Block/Unblock Buttons
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
@ -290,7 +286,8 @@ class _ContactModalState extends State<ContactModal> {
|
|||||||
children: [
|
children: [
|
||||||
// Favorite button
|
// Favorite button
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double
|
||||||
|
.infinity, // This makes the button take full width
|
||||||
child: ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
@ -299,25 +296,28 @@ class _ContactModalState extends State<ContactModal> {
|
|||||||
icon: Icon(widget.isFavorite
|
icon: Icon(widget.isFavorite
|
||||||
? Icons.star
|
? Icons.star
|
||||||
: Icons.star_border),
|
: Icons.star_border),
|
||||||
label: Text(widget.isFavorite
|
label: Text(
|
||||||
? 'Unfavorite'
|
widget.isFavorite ? 'Unfavorite' : 'Favorite'),
|
||||||
: 'Favorite'),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10), // Space between buttons
|
||||||
|
|
||||||
// Edit button
|
// Edit button
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double
|
||||||
|
.infinity, // This makes the button take full width
|
||||||
child: ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
onPressed: () => widget.onEdit(),
|
onPressed: () => widget.onEdit(),
|
||||||
icon: const Icon(Icons.edit),
|
icon: const Icon(Icons.edit),
|
||||||
label: const Text('Edit Contact'),
|
label: const Text('Edit Contact'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10), // Space between buttons
|
||||||
|
|
||||||
// Block/Unblock button
|
// Block/Unblock button
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double
|
||||||
|
.infinity, // This makes the button take full width
|
||||||
child: ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
onPressed: _toggleBlockState,
|
onPressed: _toggleBlockState,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import 'package:dialer/services/obfuscate_service.dart';
|
|
||||||
import 'package:dialer/widgets/color_darkener.dart';
|
|
||||||
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:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:dialer/features/contacts/contact_state.dart';
|
import 'package:dialer/features/contacts/contact_state.dart';
|
||||||
import 'package:dialer/widgets/username_color_generator.dart';
|
import 'package:dialer/widgets/username_color_generator.dart';
|
||||||
|
import 'package:dialer/widgets/color_darkener.dart';
|
||||||
|
|
||||||
class History {
|
class History {
|
||||||
final Contact contact;
|
final Contact contact;
|
||||||
@ -35,8 +34,6 @@ class _HistoryPageState extends State<HistoryPage> with SingleTickerProviderStat
|
|||||||
bool loading = true;
|
bool loading = true;
|
||||||
int? _expandedIndex;
|
int? _expandedIndex;
|
||||||
|
|
||||||
final ObfuscateService _obfuscateService = ObfuscateService();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
@ -209,21 +206,21 @@ class _HistoryPageState extends State<HistoryPage> with SingleTickerProviderStat
|
|||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: ObfuscatedAvatar(
|
leading: (contact.thumbnail != null && contact.thumbnail!.isNotEmpty)
|
||||||
imageBytes: contact.thumbnail,
|
? CircleAvatar(
|
||||||
radius: 25,
|
backgroundImage: MemoryImage(contact.thumbnail!),
|
||||||
|
)
|
||||||
|
: CircleAvatar(
|
||||||
backgroundColor: avatarColor,
|
backgroundColor: avatarColor,
|
||||||
fallbackInitial: contact.displayName,
|
child: Text(
|
||||||
|
contact.displayName.isNotEmpty
|
||||||
|
? contact.displayName[0].toUpperCase()
|
||||||
|
: '?',
|
||||||
|
style: TextStyle(color: darken(avatarColor, 0.4)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
// child: Text(
|
|
||||||
// contact.displayName.isNotEmpty
|
|
||||||
// ? contact.displayName[0].toUpperCase()
|
|
||||||
// : '?',
|
|
||||||
// style: TextStyle(color: darken(avatarColor, 0.4)),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
title: Text(
|
title: Text(
|
||||||
_obfuscateService.obfuscateData(contact.displayName),
|
contact.displayName,
|
||||||
style: const TextStyle(color: Colors.white),
|
style: const TextStyle(color: Colors.white),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
@ -332,15 +329,12 @@ class _HistoryPageState extends State<HistoryPage> with SingleTickerProviderStat
|
|||||||
|
|
||||||
class CallDetailsPage extends StatelessWidget {
|
class CallDetailsPage extends StatelessWidget {
|
||||||
final History history;
|
final History history;
|
||||||
final ObfuscateService _obfuscateService = ObfuscateService();
|
|
||||||
|
|
||||||
CallDetailsPage({super.key, required this.history});
|
const CallDetailsPage({Key? key, required this.history}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final contact = history.contact;
|
final contact = history.contact;
|
||||||
final contactBg = generateColorFromName(contact.displayName);
|
|
||||||
final contactLetter = darken(contactBg);
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
@ -356,20 +350,18 @@ class CallDetailsPage extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
(contact.thumbnail != null && contact.thumbnail!.isNotEmpty)
|
(contact.thumbnail != null && contact.thumbnail!.isNotEmpty)
|
||||||
? ObfuscatedAvatar(
|
? CircleAvatar(
|
||||||
imageBytes: contact.thumbnail,
|
backgroundImage: MemoryImage(contact.thumbnail!),
|
||||||
radius: 30,
|
radius: 30,
|
||||||
backgroundColor: contactBg,
|
|
||||||
fallbackInitial: contact.displayName,
|
|
||||||
)
|
)
|
||||||
: CircleAvatar(
|
: CircleAvatar(
|
||||||
backgroundColor: generateColorFromName(contact.displayName),
|
backgroundColor: Colors.grey[700],
|
||||||
radius: 30,
|
radius: 30,
|
||||||
child: Text(
|
child: Text(
|
||||||
contact.displayName.isNotEmpty
|
contact.displayName.isNotEmpty
|
||||||
? contact.displayName[0].toUpperCase()
|
? contact.displayName[0].toUpperCase()
|
||||||
: '?',
|
: '?',
|
||||||
style: TextStyle(color: contactLetter),
|
style: const TextStyle(color: Colors.white),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
@ -407,7 +399,7 @@ class CallDetailsPage extends StatelessWidget {
|
|||||||
if (contact.phones.isNotEmpty)
|
if (contact.phones.isNotEmpty)
|
||||||
DetailRow(
|
DetailRow(
|
||||||
label: 'Number:',
|
label: 'Number:',
|
||||||
value: _obfuscateService.obfuscateData(contact.phones.first.number),
|
value: contact.phones.first.number,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import 'package:dialer/services/obfuscate_service.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:dialer/features/contacts/contact_page.dart';
|
import 'package:dialer/features/contacts/contact_page.dart';
|
||||||
import 'package:dialer/features/favorites/favorites_page.dart';
|
import 'package:dialer/features/favorites/favorites_page.dart';
|
||||||
@ -14,7 +13,7 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
List<Contact> _allContacts = [];
|
List<Contact> _allContacts = [];
|
||||||
List<Contact> _contactSuggestions = [];
|
List<Contact> _contactSuggestions = [];
|
||||||
final ContactService _contactService = ContactService();
|
final ContactService _contactService = ContactService();
|
||||||
final ObfuscateService _obfuscateService = ObfuscateService();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -131,7 +130,7 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
return _contactSuggestions.map((contact) {
|
return _contactSuggestions.map((contact) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
key: ValueKey(contact.id),
|
key: ValueKey(contact.id),
|
||||||
title: Text(_obfuscateService.obfuscateData(contact.displayName),
|
title: Text(contact.displayName,
|
||||||
style: const TextStyle(color: Colors.white)),
|
style: const TextStyle(color: Colors.white)),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
controller.closeView(contact.displayName);
|
controller.closeView(contact.displayName);
|
||||||
|
@ -1 +0,0 @@
|
|||||||
bool isStealthMode = false;
|
|
@ -1,16 +1,13 @@
|
|||||||
import 'package:dialer/features/home/home_page.dart';
|
import 'package:dialer/features/home/home_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:dialer/features/contacts/contact_state.dart';
|
import 'package:dialer/features/contacts/contact_state.dart';
|
||||||
import 'globals.dart' as globals;
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
const stealthFlag = String.fromEnvironment('STEALTH', defaultValue: 'false');
|
runApp(const MyApp());
|
||||||
globals.isStealthMode = stealthFlag.toLowerCase() == 'true';
|
|
||||||
runApp(const Dialer());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Dialer extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const Dialer({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -1,91 +0,0 @@
|
|||||||
// lib/services/obfuscate_service.dart
|
|
||||||
import 'package:dialer/widgets/color_darkener.dart';
|
|
||||||
|
|
||||||
import '../../globals.dart' as globals;
|
|
||||||
import 'dart:ui';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class ObfuscateService {
|
|
||||||
// Private constructor
|
|
||||||
ObfuscateService._privateConstructor();
|
|
||||||
|
|
||||||
// Singleton instance
|
|
||||||
static final ObfuscateService _instance = ObfuscateService._privateConstructor();
|
|
||||||
|
|
||||||
// Factory constructor to return the same instance
|
|
||||||
factory ObfuscateService() {
|
|
||||||
return _instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public method to obfuscate data
|
|
||||||
String obfuscateData(String data) {
|
|
||||||
if (globals.isStealthMode) {
|
|
||||||
return _obfuscateData(data);
|
|
||||||
} else {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private helper method for obfuscation logic
|
|
||||||
String _obfuscateData(String data) {
|
|
||||||
if (data.isNotEmpty) {
|
|
||||||
// Ensure the string has at least two characters to obfuscate
|
|
||||||
if (data.length == 1) {
|
|
||||||
return '${data[0]}';
|
|
||||||
} else {
|
|
||||||
return '${data[0]}...${data[data.length - 1]}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ObfuscatedAvatar extends StatelessWidget {
|
|
||||||
final Uint8List? imageBytes;
|
|
||||||
final double radius;
|
|
||||||
final Color backgroundColor;
|
|
||||||
final String? fallbackInitial;
|
|
||||||
|
|
||||||
const ObfuscatedAvatar({
|
|
||||||
Key? key,
|
|
||||||
required this.imageBytes,
|
|
||||||
this.radius = 25,
|
|
||||||
this.backgroundColor = Colors.grey,
|
|
||||||
this.fallbackInitial,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (imageBytes != null && imageBytes!.isNotEmpty) {
|
|
||||||
return ClipOval(
|
|
||||||
child: ImageFiltered(
|
|
||||||
imageFilter: globals.isStealthMode
|
|
||||||
? ImageFilter.blur(sigmaX: 10, sigmaY: 10)
|
|
||||||
: ImageFilter.blur(sigmaX: 0, sigmaY: 0),
|
|
||||||
child: Image.memory(
|
|
||||||
imageBytes!,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
width: radius * 2,
|
|
||||||
height: radius * 2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return CircleAvatar(
|
|
||||||
radius: radius,
|
|
||||||
backgroundColor: backgroundColor,
|
|
||||||
child: Text(
|
|
||||||
fallbackInitial != null && fallbackInitial!.isNotEmpty
|
|
||||||
? fallbackInitial![0].toUpperCase()
|
|
||||||
: '?',
|
|
||||||
style: TextStyle(
|
|
||||||
color: darken(backgroundColor),
|
|
||||||
fontSize: radius,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -40,6 +40,7 @@ dependencies:
|
|||||||
permission_handler: ^11.3.1 # For handling permissions
|
permission_handler: ^11.3.1 # For handling permissions
|
||||||
cached_network_image: ^3.2.3 # For caching contact images
|
cached_network_image: ^3.2.3 # For caching contact images
|
||||||
qr_flutter: ^4.1.0
|
qr_flutter: ^4.1.0
|
||||||
|
android_intent_plus: ^5.2.0
|
||||||
camera: ^0.10.0+2
|
camera: ^0.10.0+2
|
||||||
mobile_scanner: ^6.0.2
|
mobile_scanner: ^6.0.2
|
||||||
pretty_qr_code: ^3.3.0
|
pretty_qr_code: ^3.3.0
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
echo "Running Icing Dialer in STEALTH mode..."
|
|
||||||
flutter run --dart-define=STEALTH=true
|
|
@ -13,7 +13,7 @@ import 'package:dialer/main.dart';
|
|||||||
void main() {
|
void main() {
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||||
// Build our app and trigger a frame.
|
// Build our app and trigger a frame.
|
||||||
await tester.pumpWidget(const Dialer());
|
await tester.pumpWidget(const MyApp());
|
||||||
|
|
||||||
// Verify that our counter starts at 0.
|
// Verify that our counter starts at 0.
|
||||||
expect(find.text('0'), findsOneWidget);
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
Loading…
Reference in New Issue
Block a user