Add contact OK, working on qr scan

This commit is contained in:
stcb 2024-11-29 09:14:46 +02:00
parent ca2521fe29
commit 7e2f2a5d3b
11 changed files with 197 additions and 16 deletions

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/> <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-feature android:name="android.hardware.camera" android:required="false" />
<!-- The INTERNET permission is required for development. Specifically, <!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.

View File

@ -4,7 +4,8 @@
<application <application
android:label="com.example.dialer" android:label="com.example.dialer"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher"
android:enableOnBackInvokedCallback="true">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"

View File

@ -1,6 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/> <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" android:required="false" />
<!-- The INTERNET permission is required for development. Specifically, <!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.

View File

@ -1,3 +1,4 @@
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
dev.steenbakker.mobile_scanner.useUnbundled=true

View File

@ -5,7 +5,7 @@ import 'contact_service.dart';
class ContactState extends StatefulWidget { class ContactState extends StatefulWidget {
final Widget child; final Widget child;
const ContactState({Key? key, required this.child}) : super(key: key); const ContactState({super.key, required this.child});
static _ContactStateState of(BuildContext context) { static _ContactStateState of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<_InheritedContactState>()!.data; return context.dependOnInheritedWidgetOfExactType<_InheritedContactState>()!.data;
@ -92,8 +92,7 @@ class _ContactStateState extends State<ContactState> {
class _InheritedContactState extends InheritedWidget { class _InheritedContactState extends InheritedWidget {
final _ContactStateState data; final _ContactStateState data;
const _InheritedContactState({Key? key, required this.data, required Widget child}) const _InheritedContactState({required this.data, required super.child});
: super(key: key, child: child);
@override @override
bool updateShouldNotify(_InheritedContactState oldWidget) => true; bool updateShouldNotify(_InheritedContactState oldWidget) => true;

View File

@ -1,14 +1,23 @@
import 'package:android_intent_plus/android_intent.dart';
import 'package:dialer/widgets/qr_scanner.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class AddContactButton extends StatelessWidget { class AddContactButton extends StatelessWidget {
const AddContactButton({Key? key}) : super(key: key); const AddContactButton({super.key});
Future<void> createNewContact() async {
AndroidIntent intent = AndroidIntent(
action: 'android.intent.action.INSERT',
type: 'vnd.android.cursor.dir/contact',
);
await intent.launch();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return IconButton( return IconButton(
icon: Icon(Icons.add, color: Colors.blue), icon: Icon(Icons.add, color: Colors.blue),
onPressed: () { onPressed: () {
// Show pop-up with two mock choices
showDialog( showDialog(
context: context, context: context,
barrierDismissible: true, // Allows dismissal by tapping outside barrierDismissible: true, // Allows dismissal by tapping outside
@ -21,16 +30,20 @@ class AddContactButton extends StatelessWidget {
TextButton( TextButton(
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
// Action for Option 1 Navigator.push(
context,
MaterialPageRoute(builder: (context) => QRCodeScannerScreen()),
);
}, },
child: Text("Option 1", style: TextStyle(color: Colors.white)), child: Text("Scan QR code", style: TextStyle(color: Colors.white)),
), ),
TextButton( TextButton(
onPressed: () { onPressed: () async {
createNewContact();
Navigator.of(context).pop(); Navigator.of(context).pop();
// Action for Option 2
}, },
child: Text("Option 2", style: TextStyle(color: Colors.white)), child: Text("Create new contact", style: TextStyle(color: Colors.white)),
), ),
], ],
), ),

View File

@ -1,5 +1,4 @@
import 'package:dialer/widgets/username_color_generator.dart'; import 'package:dialer/widgets/username_color_generator.dart';
import 'package:flutter/foundation.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 '../../../widgets/color_darkener.dart'; import '../../../widgets/color_darkener.dart';
@ -11,7 +10,7 @@ class AlphabetScrollPage extends StatefulWidget {
final List<Contact> contacts; final List<Contact> contacts;
final double scrollOffset; final double scrollOffset;
const AlphabetScrollPage({Key? key, required this.contacts, required this.scrollOffset}) : super(key: key); const AlphabetScrollPage({super.key, required this.contacts, required this.scrollOffset});
@override @override
_AlphabetScrollPageState createState() => _AlphabetScrollPageState(); _AlphabetScrollPageState createState() => _AlphabetScrollPageState();

View File

@ -1,4 +1,3 @@
import 'package:dialer/features/contacts/contact_service.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -24,6 +23,8 @@ class _HistoryPageState extends State<HistoryPage> {
body: ListView.builder( body: ListView.builder(
itemCount: histories.length, itemCount: histories.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return null;
// //
}, },
), ),

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class LoadingIndicatorWidget extends StatelessWidget { class LoadingIndicatorWidget extends StatelessWidget {
const LoadingIndicatorWidget({Key? key}) : super(key: key); const LoadingIndicatorWidget({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

161
lib/widgets/qr_scanner.dart Normal file
View File

@ -0,0 +1,161 @@
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);
@override
_QRCodeScannerScreenState createState() => _QRCodeScannerScreenState();
}
class _QRCodeScannerScreenState extends State<QRCodeScannerScreen> {
MobileScannerController cameraController = MobileScannerController();
bool isScanning = true;
@override
void dispose() {
cameraController.dispose();
super.dispose();
}
Future<void> _createContactFromVCard(String vCardData) async {
// Parse VCard data
final Map<String, dynamic> 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<String, dynamic> _parseVCard(String vCardData) {
// Simple parser for VCard data
final Map<String, dynamic> 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.'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(); // Close dialog
isScanning = true; // Resume scanning
},
child: Text('Try Again'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Scan Contact QR Code'),
),
body: MobileScanner(
controller: cameraController,
onDetect: (capture) {
if (!isScanning) return;
isScanning = false; // Prevent multiple scans
final List<Barcode> barcodes = capture.barcodes;
for (final barcode in barcodes) {
final String? code = barcode.rawValue;
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;
}
}
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
}
},
),
);
}
}

View File

@ -36,9 +36,12 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
flutter_contacts: ^1.1.9+2 flutter_contacts: ^1.1.9+2
permission_handler: ^10.2.0 # 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
mobile_scanner: ^6.0.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: