Skip to content

Commit

Permalink
Extract & Save Contacts feature
Browse files Browse the repository at this point in the history
  • Loading branch information
mtalhahabib committed Dec 26, 2023
1 parent e2a5ff3 commit 41bc854
Show file tree
Hide file tree
Showing 14 changed files with 804 additions and 108 deletions.
3 changes: 3 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>

<application
android:label="web_project"
android:name="${applicationName}"
Expand Down
13 changes: 13 additions & 0 deletions assets/key.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"type": "service_account",
"project_id": "lab6-402407",
"private_key_id": "a0c1e50a847467ff61b3b708db6dea724ec16be7",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDDVWJ1Tcfy7n4+\nG0GDCAa+jOIM2LXjKc8CVJQNXq/sknUMc3/fYkZ1I+sMXXQCzYjrycGL5jR4dcO4\n3ARBQYTVvUPmApax6HJUkHMkgkm44C9NLOR8eKW650GQfowpnvdCpEkkES9YGld0\nmUMaZw1M99BmFwTVevRznbamIXZQcvZvRx3P9z/W7hvMORg93k6ul/nrkubilP2Y\nCH+E/ZFau+ketnpmfnj58F2584y/D17+l3uLbzTLcWFZKJJPXYWtiiWuQdxkiNW0\n9AwELRtCIYNyla8zFQMv0miRKf7xTkCvVbQXQ73NRjvbMTLKBnd4uJI95MubMeFm\nWGWHAhdPAgMBAAECggEAPklh5gnxcnO+acuDLmdGz0hZZMNN7KwOSK3zr/e7nXJu\nRJQn+HF8Cipz7zN1pNcpjBZ/0z6LCMZ0rOPrr2tXElkR56x3pS98FZ8iOEgbfgpu\nyus/yLkoD9tiOtM/mp4INoXwlMGuGSf9Lz8X+LlXsm1rVMKOVgypGpz1+y+8bJZN\nhKKwuYjmCcdWLPSLJaV3orqtDXxYGITpQl6qnFA+CA6Pl5LYRTvRt88QlE9+BzZk\nYJDesoRFZ1pmwcVWZbHhFK2EnCyLT/5a8D49b2TBTyKgYuJmpE3+IBh5Wc+FrQO0\ngrjnhOwhRH+UCCZxyEfZpriGSFTsW2zO10PBuwC8gQKBgQDhPIUinYVlCSRlNrP3\neHPJlEQcRdPayqTYKpTTJq62HfbtFL+8r7Gyp7U8uPjWvrxu262jj4NfXyleo3ya\nhHb+gSqgGGzg3ctkMyWfBrl/gqVit59yDVdUHEPwy+3wzu5QNQZgIBQgN5eE6OqK\nXEQDKLjV6xo1TwKiTEeVnnVlLwKBgQDeA0x8wAKUxlK3rtgbrD9DBikK5Zm9Kyh/\nsbWqB2Vw7a0mfuWRiijujgBTBBDBumVd9TrykvOZ5IeJoPfXnIsPaSZyFkuqpTjW\nlibAg86XtPNf/sSh26t5pBMLO2+ojQfX5ipz3dt6+6pY3rHYljaokFuyOAwRKRxg\n1l1vkLYn4QKBgQC0Z1ELpLePcX8hQmtrL6MuNf9H1fYWLHFUYubJKRaO7/kzc4cP\njnn56rITbOSCvEsZUAMIGo7S+Nmd37yR1r+oobSOfoHWqk+adg0QDsbNBsuJAiJH\nq7/isrEIUY7HbjcbLx1oKMl19JD798LEB5rCHP/O93wKbrphXE+J212UKQKBgD7o\nXGH03kZds86MYnvk4MATHMoTqO5rLjyQlFMraVkIX8nKpy4IIfUGk5zyR/U85cAr\n3pohfZkbojQjQlb76oNhXh1xp9sgmexj+3MNZhqikVugCwN6BSqgjSzfmJH/9Dr+\n4wSV1r0QVoJ8B5TBa5dz4Cetre2m4n2mAC++p37BAoGAEcpGx67DNr2G2+91CNDA\nF/eOkRcn7MvjGoS7D3giKT4tvABM+ToeDtL+dsl6pbn9Ch+fd9Mq0nAjTmgCzLka\nSOoMDGURys5A/UW7swTwWLiAN8KEsD6UYQA2Rw4iCOsfs8zelcF4T8l50aarEoKM\nGmRtsc/a7Cvy+yWr14rNa7g=\n-----END PRIVATE KEY-----\n",
"client_email": "jalsa-630@lab6-402407.iam.gserviceaccount.com",
"client_id": "110685857023937735453",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/jalsa-630%40lab6-402407.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}
108 changes: 108 additions & 0 deletions lib/controllers/contactsController.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import 'dart:convert';
import 'dart:io';

import 'package:get/get.dart';
import 'package:flutter/material.dart';

import 'package:googleapis/vision/v1.dart';
import 'package:flutter/services.dart';
import 'package:googleapis_auth/auth_io.dart';

class ContactsViewModel extends GetxController {
RxString image = ''.obs;
RxBool isloading = false.obs;
RxBool isPicSelected = false.obs;
void picUploaded() {
isPicSelected.value = true;
}

void notUploaded() {
isPicSelected.value = false;
}

void uploadImage(String path) {
image.value = path;
}

//----------------------------------------------------------

//-----------------------------------------------------------
Future<ServiceAccountCredentials> get _credentials async {
String _file = await rootBundle.loadString('assets/key.json');
return ServiceAccountCredentials.fromJson(_file);
}

Future<AutoRefreshingAuthClient> get _client async {
AutoRefreshingAuthClient _client = await clientViaServiceAccount(
await _credentials, [VisionApi.cloudVisionScope]).then((c) => c);
return _client;
}
//............................................................

Future<List<List<String?>>> search(String assetImagePath) async {
try {
File imageFile = File(assetImagePath);
List<int> bytes = await imageFile.readAsBytes();

// Convert bytes to base64
String base64Image = base64Encode(bytes);

// Convert bytes to base64

// Vision API call
var _vision = VisionApi(await _client);
var _api = _vision.images;
var _response = await _api.annotate(BatchAnnotateImagesRequest.fromJson({
"requests": [
{
"image": {
"content": base64Image,
},
"features": [
{"type": "DOCUMENT_TEXT_DETECTION"}
],
}
]
}));

// Check if the response is not null and contains a valid result
if (_response != null &&
_response.responses != null &&
_response.responses!.isNotEmpty &&
_response.responses![0].fullTextAnnotation != null) {
List<String?> names = [];
List<String?> numbers = [];

String? apiResponse = _response.responses![0].fullTextAnnotation!.text;
List<String> namesList = apiResponse!.split('\n');

for (var annotation in namesList) {
String? description = annotation;

// Check if the description contains alphabets (names)
if (RegExp(r'[a-zA-Z]').hasMatch(description!)) {
names.add(description);
}
// Check if the description contains numbers (phone numbers)
else if (RegExp(r'\d').hasMatch(description)) {
numbers.add(description);
}
}
// if (names.length != numbers.length) {
// Get.snackbar('Error', 'Please upload a clear image\nNames and Phone Numbers are not equal\nTry Again with a clear image');
// return [];
// } else {return [names, numbers];}
return [names, numbers];
} else {
print("No valid text found in the image.");

Get.snackbar('Error', 'No valid text found in the image.');
return [];
}
} catch (e) {
print("Error during API call: $e");
Get.snackbar('Error Occured', e.toString());
return [];
}
}
}
2 changes: 1 addition & 1 deletion lib/design_course/home_design_course.dart
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ class _DesignCourseHomeScreenState extends State<DesignCourseHomeScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Popular Course',
'Popular Events',
textAlign: TextAlign.left,
style: TextStyle(
fontWeight: FontWeight.w600,
Expand Down
219 changes: 219 additions & 0 deletions lib/fitness_app/training/extractedContacts.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@

import 'package:flutter/material.dart';
import 'package:flutter_contacts/flutter_contacts.dart';
class ExtractedContacts extends StatefulWidget {

List<List<String?>> list;
ExtractedContacts({Key? key, required this.list}) : super(key: key);

@override
State<ExtractedContacts> createState() => _ExtractedContactsState();
}

class _ExtractedContactsState extends State<ExtractedContacts> {
Future<void> saveToPhone(list) async {
if (await FlutterContacts.requestPermission()) {
for (int i = 0; i < list[0].length; i++) {
final newContact = Contact()
..name.first = list[0][i]
..phones = [Phone(list[1][i])];
await newContact.insert();
}
print('Done with it');
}
}

String newValue = '';

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('CheckList',style: TextStyle(color: Colors.brown,fontWeight: FontWeight.bold),),
actions: [
TextButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text(
'Are you sure you want to Save the Cantacts !!'),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(
'Cancel',
style: TextStyle(color: Colors.white),
),
style: TextButton.styleFrom(
backgroundColor: Colors.grey),
),
TextButton(
style: TextButton.styleFrom(
backgroundColor: Colors.brown),
onPressed: () {
saveToPhone(widget.list);
Navigator.pop(context);
},
child: Text(
'Save',
style: TextStyle(color: Colors.white),
)),
],
);
});
},
child: Card(
elevation: 5,
color: Colors.brown,
child: Container(
height: 70,
width: 80,
child: Center(
child: Text(
'Save',
style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold,fontSize: 20),
),
),

),
)),
],
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.brown,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Add Prefix',
style: const TextStyle(color: Colors.white,fontWeight: FontWeight.bold,fontSize: 10),
),
),
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Insert Prefix"),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
decoration:
InputDecoration(hintText: "Add Prefix to Name"),
onChanged: (value) {
setState(() {
newValue = value;
});
},
),
],
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Cancel")),
TextButton(
onPressed: () {
for (int i = 0; i < widget.list[0].length; i++) {
widget.list[0][i] =
newValue + ' ' + widget.list[0][i]!;
}
setState(() {});
Navigator.pop(context);
},
child: Text("Save")),
],
);
});
},
),
body: ListView.builder(
itemCount: widget.list[0].length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(10),
),
child: ListTile(
title: Text(widget.list[0][index]!),
subtitle: Text(widget.list[1][index]!),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Edit Contact"),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: TextEditingController(
text: widget.list[0][index]),
decoration: InputDecoration(
hintText: "Enter Name"),
onChanged: (value) {
setState(() {
widget.list[0][index] = value;
});
},
),
TextField(
controller: TextEditingController(
text: widget.list[1][index]),
decoration: InputDecoration(
hintText: "Enter Number"),
onChanged: (value) {
setState(() {
widget.list[1][index] = value;
});
},
),
],
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Cancel")),
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Save")),
],
);
});
},
icon: Icon(Icons.edit),
),
IconButton(
onPressed: () {
setState(() {
widget.list[0].removeAt(index);
widget.list[1].removeAt(index);
});
},
icon: Icon(Icons.delete),
),
],
)),
),
);
},
));
}
}
Loading

0 comments on commit 41bc854

Please sign in to comment.