diff --git a/dialer/android/app/src/main/AndroidManifest.xml b/dialer/android/app/src/main/AndroidManifest.xml
index a4877fe..db77a47 100644
--- a/dialer/android/app/src/main/AndroidManifest.xml
+++ b/dialer/android/app/src/main/AndroidManifest.xml
@@ -6,7 +6,7 @@
diff --git a/dialer/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/dialer/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
old mode 100644
new mode 100755
index db77bb4..1ed15a1
Binary files a/dialer/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/dialer/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/dialer/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/dialer/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
old mode 100644
new mode 100755
index 17987b7..16dc5ac
Binary files a/dialer/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/dialer/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/dialer/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/dialer/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
old mode 100644
new mode 100755
index 09d4391..5218267
Binary files a/dialer/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/dialer/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/dialer/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/dialer/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
old mode 100644
new mode 100755
index d5f1c8d..1b56c39
Binary files a/dialer/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/dialer/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/dialer/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/dialer/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
old mode 100644
new mode 100755
index 4d6372e..54652f0
Binary files a/dialer/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/dialer/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/dialer/build.sh b/dialer/build.sh
new file mode 100755
index 0000000..6416762
--- /dev/null
+++ b/dialer/build.sh
@@ -0,0 +1,5 @@
+#!/bin/bash -e
+
+IMG=git.gmoker.com/icing/flutter:main
+
+docker run --rm -v "$PWD:/app/" "$IMG" build apk
diff --git a/dialer/lib/features/contacts/contact_state.dart b/dialer/lib/features/contacts/contact_state.dart
index 82e21c2..b6cf003 100644
--- a/dialer/lib/features/contacts/contact_state.dart
+++ b/dialer/lib/features/contacts/contact_state.dart
@@ -102,6 +102,25 @@ class _ContactStateState extends State {
});
}
+ bool doesContactExist(Contact contact) {
+ // Example: consider it "existing" if there's a matching phone number
+ for (final existing in _allContacts) {
+ if (existing.toVCard() == contact.toVCard()) {
+ return true;
+ }
+ // for (final phone in existing.phones) {
+ // for (final newPhone in contact.phones) {
+ // // Simple exact match; you can do more advanced logic
+ // if (phone.normalizedNumber == newPhone.normalizedNumber) {
+ // return true;
+ // }
+ // }
+ // } We might switch to finer and smarter logic later, ex: remove trailing spaces, capitals
+ }
+ return false;
+ }
+
+
@override
Widget build(BuildContext context) {
return _InheritedContactState(
diff --git a/dialer/lib/features/contacts/widgets/add_contact_button.dart b/dialer/lib/features/contacts/widgets/add_contact_button.dart
index e096a6c..399ab48 100644
--- a/dialer/lib/features/contacts/widgets/add_contact_button.dart
+++ b/dialer/lib/features/contacts/widgets/add_contact_button.dart
@@ -1,22 +1,14 @@
-import 'package:android_intent_plus/android_intent.dart';
import 'package:dialer/widgets/qr_scanner.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_contacts/flutter_contacts.dart';
class AddContactButton extends StatelessWidget {
const AddContactButton({super.key});
- Future createNewContact() async {
- AndroidIntent intent = AndroidIntent(
- action: 'android.intent.action.INSERT',
- type: 'vnd.android.cursor.dir/contact',
- );
- await intent.launch();
- }
-
@override
Widget build(BuildContext context) {
return IconButton(
- icon: Icon(Icons.add, color: Colors.blue),
+ icon: const Icon(Icons.add, color: Colors.blue),
onPressed: () {
showDialog(
context: context,
@@ -28,22 +20,37 @@ class AddContactButton extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
- onPressed: () {
- Navigator.of(context).pop();
- Navigator.push(
- context,
- MaterialPageRoute(builder: (context) => QRCodeScannerScreen()),
- );
- },
- child: Text("Scan QR code", style: TextStyle(color: Colors.white)),
- ),
+ onPressed: () async {
+ Navigator.of(context).pop(); // close dialog
+ // Go to QR Scanner
+ final vCardString = await Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => const QRCodeScannerScreen(),
+ ),
+ );
+
+ if (vCardString != null && vCardString is String) {
+ await FlutterContacts.openExternalInsert(Contact
+ .fromVCard(vCardString));
+ }
+ },
+ child: const Text(
+ "Scan QR code",
+ style: TextStyle(color: Colors.white),
+ ),
+ ),
TextButton(
onPressed: () async {
- createNewContact();
Navigator.of(context).pop();
+ // Create a blank contact entry
+ await FlutterContacts.openExternalInsert();
},
- child: Text("Create new contact", style: TextStyle(color: Colors.white)),
+ child: const Text(
+ "Create new contact",
+ style: TextStyle(color: Colors.white),
+ ),
),
],
),
diff --git a/dialer/lib/widgets/qr_scanner.dart b/dialer/lib/widgets/qr_scanner.dart
index fcdf295..da0d80b 100644
--- a/dialer/lib/widgets/qr_scanner.dart
+++ b/dialer/lib/widgets/qr_scanner.dart
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
-import 'package:flutter_contacts/flutter_contacts.dart';
class QRCodeScannerScreen extends StatefulWidget {
const QRCodeScannerScreen({Key? key}) : super(key: key);
@@ -10,8 +9,7 @@ class QRCodeScannerScreen extends StatefulWidget {
}
class _QRCodeScannerScreenState extends State {
- MobileScannerController cameraController = MobileScannerController();
-
+ final MobileScannerController cameraController = MobileScannerController();
bool isScanning = true;
@override
@@ -20,104 +18,22 @@ class _QRCodeScannerScreenState extends State {
super.dispose();
}
- Future _createContactFromVCard(String vCardData) async {
- // Parse VCard data
- final Map contactInfo = _parseVCard(vCardData);
-
- debugPrint("contactInfo: $contactInfo");
-
- // // Create a new contact
- // Contact newContact = Contact();
- //
- // // Set contact's name
- // newContact.name = Name(
- // first: contactInfo['firstName'] ?? '',
- // last: contactInfo['lastName'] ?? '',
- // );
- //
- // // Set contact's phone numbers
- // if (contactInfo['phone'] != null) {
- // newContact.phones = [
- // Phone(contactInfo['phone'], label: PhoneLabel.mobile),
- // ];
- // }
- //
- // // Set contact's emails
- // if (contactInfo['email'] != null) {
- // newContact.emails = [
- // Email(contactInfo['email'], label: EmailLabel.home),
- // ];
- // }
- //
- // // Request permission to write contacts
- // bool permission = await FlutterContacts.requestPermission(readonly: false);
- // if (!permission) {
- // // Handle permission denied
- // ScaffoldMessenger.of(context).showSnackBar(
- // SnackBar(content: Text('Contacts permission denied')),
- // );
- // return;
- // }
- //
- // // Save the contact
- // await newContact.insert();
- }
-
- Map _parseVCard(String vCardData) {
- // Simple parser for VCard data
- final Map contactInfo = {};
-
- final lines = vCardData.split(RegExp(r'\r?\n'));
- for (var line in lines) {
- if (line.startsWith('FN:')) {
- contactInfo['fullName'] = line.substring(3).trim();
- } else if (line.startsWith('N:')) {
- final names = line.substring(2).split(';');
- contactInfo['lastName'] = names[0].trim();
- contactInfo['firstName'] = names.length > 1 ? names[1].trim() : '';
- } else if (line.startsWith('TEL:')) {
- contactInfo['phone'] = line.substring(4).trim();
- } else if (line.startsWith('EMAIL:')) {
- contactInfo['email'] = line.substring(6).trim();
- }
- // Add more fields as needed
- }
-
- return contactInfo;
- }
-
- void _showContactAddedDialog() {
- showDialog(
- context: context,
- builder: (context) => AlertDialog(
- title: Text('Contact Added'),
- content: Text('The contact has been added to your address book.'),
- actions: [
- TextButton(
- onPressed: () {
- Navigator.of(context).pop(); // Close dialog
- Navigator.of(context).pop(); // Go back to previous screen
- },
- child: Text('OK'),
- ),
- ],
- ),
- );
- }
-
void _showInvalidQRDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
- title: Text('Invalid QR Code'),
- content: Text('The scanned QR code does not contain valid contact information.'),
+ title: const Text('Invalid QR Code'),
+ content:
+ const Text('The scanned QR code does not contain valid vCard data.'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(); // Close dialog
- isScanning = true; // Resume scanning
+ setState(() {
+ isScanning = true; // Resume scanning
+ });
},
- child: Text('Try Again'),
+ child: const Text('Try Again'),
),
],
),
@@ -128,32 +44,27 @@ class _QRCodeScannerScreenState extends State {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
- title: Text('Scan Contact QR Code'),
+ title: const Text('Scan Contact QR Code'),
),
body: MobileScanner(
controller: cameraController,
+ // allowDuplicates: false, // or true, depending on your preference
onDetect: (capture) {
if (!isScanning) return;
+ isScanning = false; // Stop multiple triggers
- isScanning = false; // Prevent multiple scans
final List barcodes = capture.barcodes;
-
for (final barcode in barcodes) {
final String? code = barcode.rawValue;
+ // If the QR code contains 'BEGIN:VCARD', let's assume it's a valid vCard
if (code != null && code.contains('BEGIN:VCARD')) {
- // Handle valid vCard QR code
- _createContactFromVCard(code);
- _showContactAddedDialog(); // TODO: Be a confirmation button "do you want to add XXX to your contacts?"
- break;
+ Navigator.pop(context, code); // pop back with the full vCard text
+ return;
}
}
- if (!barcodes.any((barcode) =>
- barcode.rawValue != null && barcode.rawValue!.contains('BEGIN:VCARD'))) {
- // If no valid QR code is found
- _showInvalidQRDialog();
- isScanning = true; // Allow scanning again for invalid QR codes
- }
+ // If no valid vCard was found in any of the barcodes
+ _showInvalidQRDialog();
},
),
);
diff --git a/dialer/packages/mobile_number/example/ios/Runner/Info.plist b/dialer/packages/mobile_number/example/ios/Runner/Info.plist
index 08b3730..c984c96 100644
--- a/dialer/packages/mobile_number/example/ios/Runner/Info.plist
+++ b/dialer/packages/mobile_number/example/ios/Runner/Info.plist
@@ -11,7 +11,7 @@
CFBundleInfoDictionaryVersion
6.0
CFBundleName
- mobile_number_example
+ Icing Dialer
CFBundlePackageType
APPL
CFBundleShortVersionString
diff --git a/dialer/run.sh b/dialer/run.sh
new file mode 100755
index 0000000..3a8ccb7
--- /dev/null
+++ b/dialer/run.sh
@@ -0,0 +1,5 @@
+#!/bin/bash -e
+
+IMG=git.gmoker.com/icing/flutter:main
+
+docker run --rm -p 5037:5037 -v "$PWD:/app/" "$IMG" run