86 lines
2.7 KiB
Dart
86 lines
2.7 KiB
Dart
import 'package:flutter_contacts/flutter_contacts.dart';
|
|
import 'package:get/get.dart';
|
|
|
|
class ContactInfo {
|
|
final String name;
|
|
final String? avatarPath;
|
|
ContactInfo({required this.name, this.avatarPath});
|
|
}
|
|
|
|
class ContactsService extends GetxService {
|
|
final RxMap<String, ContactInfo> _contactsMap = <String, ContactInfo>{}.obs;
|
|
final RxBool permissionGranted = false.obs;
|
|
|
|
Future<ContactsService> init() async {
|
|
await fetchContacts();
|
|
return this;
|
|
}
|
|
|
|
Future<void> fetchContacts() async {
|
|
try {
|
|
// Check and request permission
|
|
bool permission = await FlutterContacts.requestPermission(readonly: true);
|
|
permissionGranted.value = permission;
|
|
|
|
if (permission) {
|
|
// Fetch contacts with photos and phone numbers
|
|
final contacts = await FlutterContacts.getContacts(withProperties: true, withPhoto: true);
|
|
|
|
final Map<String, ContactInfo> tempMap = {};
|
|
for (var contact in contacts) {
|
|
final fullName = contact.displayName;
|
|
if (fullName.isEmpty) continue;
|
|
|
|
for (var phone in contact.phones) {
|
|
final normalized = normalizePhoneNumber(phone.number);
|
|
if (normalized.isNotEmpty) {
|
|
tempMap[normalized] = ContactInfo(
|
|
name: fullName,
|
|
avatarPath: null, // Custom local avatar path can be handled if needed
|
|
);
|
|
}
|
|
}
|
|
}
|
|
_contactsMap.assignAll(tempMap);
|
|
print('[CONTACTS] Successfully loaded ${_contactsMap.length} normalized phone contacts');
|
|
}
|
|
} catch (e) {
|
|
print('[CONTACTS ERROR] Failed to fetch system contacts: $e');
|
|
}
|
|
}
|
|
|
|
// Normalizes numbers to match them easily (e.g., removes spaces, dashes, brackets, and leading zeros)
|
|
String normalizePhoneNumber(String number) {
|
|
String clean = number.replaceAll(RegExp(r'[\s\-\(\)\+]'), '');
|
|
// If it starts with local country prefix or leading 0, we can do substring matches
|
|
if (clean.startsWith('00')) {
|
|
clean = clean.substring(2);
|
|
}
|
|
return clean;
|
|
}
|
|
|
|
String getContactName(String rawNumber, String fallback) {
|
|
if (!permissionGranted.value || _contactsMap.isEmpty) return fallback;
|
|
|
|
final clean = normalizePhoneNumber(rawNumber);
|
|
if (clean.isEmpty) return fallback;
|
|
|
|
// Direct match
|
|
if (_contactsMap.containsKey(clean)) {
|
|
return _contactsMap[clean]!.name;
|
|
}
|
|
|
|
// Partial match for varying country codes (match last 9 digits of the phone number)
|
|
if (clean.length >= 9) {
|
|
final suffix = clean.substring(clean.length - 9);
|
|
for (var key in _contactsMap.keys) {
|
|
if (key.endsWith(suffix)) {
|
|
return _contactsMap[key]!.name;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fallback;
|
|
}
|
|
}
|