From 711d15e03193e0f936bf9ffa10d02b2bf6f6e104 Mon Sep 17 00:00:00 2001 From: sajanIocod Date: Mon, 3 Feb 2025 11:30:08 +0530 Subject: [PATCH 01/11] Add namespace for contacts service plugin in build.gradle --- android/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/android/build.gradle b/android/build.gradle index 2feed78f..413e913b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -22,6 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { + namespace flutter.plugins.contactsservice compileSdkVersion 30 defaultConfig { From 7d4cb375bcb5698c71a2cac3c68e356130b474f2 Mon Sep 17 00:00:00 2001 From: sajanIocod Date: Mon, 3 Feb 2025 11:35:33 +0530 Subject: [PATCH 02/11] Update compileSdkVersion to 34 in build.gradle --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 413e913b..22712f7e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -23,11 +23,11 @@ apply plugin: 'com.android.library' android { namespace flutter.plugins.contactsservice - compileSdkVersion 30 defaultConfig { minSdkVersion 16 targetSdkVersion 30 + compileSdkVersion 34 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" From 54093cecb43783fa5f11cb1389f279a135417fac Mon Sep 17 00:00:00 2001 From: sajanIocod Date: Mon, 3 Feb 2025 11:38:28 +0530 Subject: [PATCH 03/11] Update compileSdkVersion to 35 and gradle wrapper to 8.10 --- android/build.gradle | 4 ++-- android/gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 22712f7e..7e5ae962 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -23,11 +23,10 @@ apply plugin: 'com.android.library' android { namespace flutter.plugins.contactsservice - + compileSdkVersion 35 defaultConfig { minSdkVersion 16 targetSdkVersion 30 - compileSdkVersion 34 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -40,4 +39,5 @@ android { dependencies { testImplementation 'junit:junit:4.12' testImplementation 'com.google.truth:truth:1.0' + classpath 'com.android.tools.build:gradle:8.1.0' } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index c5a6358e..72eb3ce4 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip From 9fec92ed2721afc505771c903a5df629ccdde4a4 Mon Sep 17 00:00:00 2001 From: sajanIocod Date: Mon, 3 Feb 2025 12:08:12 +0530 Subject: [PATCH 04/11] Refactor contact handling to support null safety and update dependencies in pubspec.yaml --- example/lib/contacts_list_page.dart | 82 +++++++++++++-------------- example/lib/contacts_picker_page.dart | 9 ++- example/pubspec.yaml | 6 +- 3 files changed, 47 insertions(+), 50 deletions(-) diff --git a/example/lib/contacts_list_page.dart b/example/lib/contacts_list_page.dart index 5f6b773e..de89d062 100644 --- a/example/lib/contacts_list_page.dart +++ b/example/lib/contacts_list_page.dart @@ -1,8 +1,6 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:contacts_service_example/main.dart'; - import 'package:contacts_service/contacts_service.dart'; +import 'package:contacts_service_example/main.dart'; +import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; class ContactListPage extends StatefulWidget { @@ -11,7 +9,7 @@ class ContactListPage extends StatefulWidget { } class _ContactListPageState extends State { - List _contacts; + List? _contacts; @override void initState() { @@ -39,8 +37,9 @@ class _ContactListPageState extends State { } void updateContact() async { - Contact ninja = _contacts - .firstWhere((contact) => contact.familyName.startsWith("Ninja")); + Contact ninja = _contacts?.firstWhere( + (contact) => contact.familyName?.startsWith("Ninja") ?? false) ?? + Contact(); ninja.avatar = null; await ContactsService.updateContact(ninja); @@ -90,7 +89,7 @@ class _ContactListPageState extends State { ? ListView.builder( itemCount: _contacts?.length ?? 0, itemBuilder: (BuildContext context, int index) { - Contact c = _contacts?.elementAt(index); + Contact c = _contacts?.elementAt(index) ?? Contact(); return ListTile( onTap: () { Navigator.of(context).push(MaterialPageRoute( @@ -100,8 +99,8 @@ class _ContactListPageState extends State { contactOnDeviceHasBeenUpdated, ))); }, - leading: (c.avatar != null && c.avatar.length > 0) - ? CircleAvatar(backgroundImage: MemoryImage(c.avatar)) + leading: (c.avatar != null && ((c.avatar?.length ?? 0) > 0)) + ? CircleAvatar(backgroundImage: MemoryImage(c.avatar!)) : CircleAvatar(child: Text(c.initials())), title: Text(c.displayName ?? ""), ); @@ -116,14 +115,14 @@ class _ContactListPageState extends State { void contactOnDeviceHasBeenUpdated(Contact contact) { this.setState(() { - var id = _contacts.indexWhere((c) => c.identifier == contact.identifier); - _contacts[id] = contact; + var id = _contacts?.indexWhere((c) => c.identifier == contact.identifier); + _contacts?[id ?? 0] = contact; }); } } class ContactDetailsPage extends StatelessWidget { - ContactDetailsPage(this._contact, {this.onContactDeviceSave}); + ContactDetailsPage(this._contact, {required this.onContactDeviceSave}); final Contact _contact; final Function(Contact) onContactDeviceSave; @@ -132,9 +131,7 @@ class ContactDetailsPage extends StatelessWidget { try { var contact = await ContactsService.openExistingContact(_contact, iOSLocalizedLabels: iOSLocalizedLabels); - if (onContactDeviceSave != null) { - onContactDeviceSave(contact); - } + onContactDeviceSave(contact); Navigator.of(context).pop(); } on FormOperationException catch (e) { switch (e.errorCode) { @@ -202,7 +199,8 @@ class ContactDetailsPage extends StatelessWidget { ListTile( title: Text("Birthday"), trailing: Text(_contact.birthday != null - ? DateFormat('dd-MM-yyyy').format(_contact.birthday) + ? DateFormat('dd-MM-yyyy') + .format(_contact.birthday ?? DateTime.now()) : ""), ), ListTile( @@ -219,9 +217,9 @@ class ContactDetailsPage extends StatelessWidget { ? _contact.androidAccountType.toString() : ""), ), - AddressesTile(_contact.postalAddresses), - ItemsTile("Phones", _contact.phones), - ItemsTile("Emails", _contact.emails) + AddressesTile(_contact.postalAddresses ?? []), + ItemsTile("Phones", _contact.phones ?? []), + ItemsTile("Emails", _contact.emails ?? []) ], ), ), @@ -323,7 +321,7 @@ class _AddContactPageState extends State { actions: [ TextButton( onPressed: () { - _formKey.currentState.save(); + _formKey.currentState?.save(); contact.postalAddresses = [address]; ContactsService.addContact(contact); Navigator.of(context).pop(); @@ -407,7 +405,7 @@ class _AddContactPageState extends State { } class UpdateContactsPage extends StatefulWidget { - UpdateContactsPage({@required this.contact}); + UpdateContactsPage({required this.contact}); final Contact contact; @@ -416,7 +414,7 @@ class UpdateContactsPage extends StatefulWidget { } class _UpdateContactsPageState extends State { - Contact contact; + Contact? contact; PostalAddress address = PostalAddress(label: "Home"); final GlobalKey _formKey = GlobalKey(); @@ -438,9 +436,9 @@ class _UpdateContactsPageState extends State { color: Colors.white, ), onPressed: () async { - _formKey.currentState.save(); - contact.postalAddresses = [address]; - await ContactsService.updateContact(contact); + _formKey.currentState?.save(); + contact?.postalAddresses = [address]; + await ContactsService.updateContact(contact ?? Contact()); Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (context) => ContactListPage())); }, @@ -454,51 +452,51 @@ class _UpdateContactsPageState extends State { child: ListView( children: [ TextFormField( - initialValue: contact.givenName ?? "", + initialValue: contact?.givenName ?? "", decoration: const InputDecoration(labelText: 'First name'), - onSaved: (v) => contact.givenName = v, + onSaved: (v) => contact?.givenName = v, ), TextFormField( - initialValue: contact.middleName ?? "", + initialValue: contact?.middleName ?? "", decoration: const InputDecoration(labelText: 'Middle name'), - onSaved: (v) => contact.middleName = v, + onSaved: (v) => contact?.middleName = v, ), TextFormField( - initialValue: contact.familyName ?? "", + initialValue: contact?.familyName ?? "", decoration: const InputDecoration(labelText: 'Last name'), - onSaved: (v) => contact.familyName = v, + onSaved: (v) => contact?.familyName = v, ), TextFormField( - initialValue: contact.prefix ?? "", + initialValue: contact?.prefix ?? "", decoration: const InputDecoration(labelText: 'Prefix'), - onSaved: (v) => contact.prefix = v, + onSaved: (v) => contact?.prefix = v, ), TextFormField( - initialValue: contact.suffix ?? "", + initialValue: contact?.suffix ?? "", decoration: const InputDecoration(labelText: 'Suffix'), - onSaved: (v) => contact.suffix = v, + onSaved: (v) => contact?.suffix = v, ), TextFormField( decoration: const InputDecoration(labelText: 'Phone'), onSaved: (v) => - contact.phones = [Item(label: "mobile", value: v)], + contact?.phones = [Item(label: "mobile", value: v)], keyboardType: TextInputType.phone, ), TextFormField( decoration: const InputDecoration(labelText: 'E-mail'), onSaved: (v) => - contact.emails = [Item(label: "work", value: v)], + contact?.emails = [Item(label: "work", value: v)], keyboardType: TextInputType.emailAddress, ), TextFormField( - initialValue: contact.company ?? "", + initialValue: contact?.company ?? "", decoration: const InputDecoration(labelText: 'Company'), - onSaved: (v) => contact.company = v, + onSaved: (v) => contact?.company = v, ), TextFormField( - initialValue: contact.jobTitle ?? "", + initialValue: contact?.jobTitle ?? "", decoration: const InputDecoration(labelText: 'Job'), - onSaved: (v) => contact.jobTitle = v, + onSaved: (v) => contact?.jobTitle = v, ), TextFormField( initialValue: address.street ?? "", diff --git a/example/lib/contacts_picker_page.dart b/example/lib/contacts_picker_page.dart index e3ebaadd..0a230d82 100644 --- a/example/lib/contacts_picker_page.dart +++ b/example/lib/contacts_picker_page.dart @@ -1,15 +1,14 @@ +import 'package:contacts_service/contacts_service.dart'; import 'package:contacts_service_example/main.dart'; import 'package:flutter/material.dart'; -import 'package:contacts_service/contacts_service.dart'; - class ContactPickerPage extends StatefulWidget { @override _ContactPickerPageState createState() => _ContactPickerPageState(); } class _ContactPickerPageState extends State { - Contact _contact; + Contact? _contact; @override void initState() { @@ -18,7 +17,7 @@ class _ContactPickerPageState extends State { Future _pickContact() async { try { - final Contact contact = await ContactsService.openDeviceContactPicker( + final Contact? contact = await ContactsService.openDeviceContactPicker( iOSLocalizedLabels: iOSLocalizedLabels); setState(() { _contact = contact; @@ -40,7 +39,7 @@ class _ContactPickerPageState extends State { onPressed: _pickContact, ), if (_contact != null) - Text('Contact selected: ${_contact.displayName}'), + Text('Contact selected: ${_contact?.displayName}'), ], )), ); diff --git a/example/pubspec.yaml b/example/pubspec.yaml index f2c15eb8..515e05a9 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,15 +1,15 @@ name: contacts_service_example description: Demonstrates how to use the contacts_service plugin. environment: - sdk: '>=2.10.0 <3.0.0' + sdk: '>=2.12.0 <3.0.0' dependencies: flutter: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - intl: ^0.17.0 + cupertino_icons: ^1.0.8 + intl: ^0.20.2 dev_dependencies: flutter_test: From c9613bc10d084fbe8215c010fe8223ca6439a524 Mon Sep 17 00:00:00 2001 From: sajanIocod Date: Mon, 3 Feb 2025 12:14:32 +0530 Subject: [PATCH 05/11] Update Android build configuration to use dynamic compileSdkVersion and add namespaces for contacts service --- android/build.gradle | 6 ++++-- example/android/app/build.gradle | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 7e5ae962..511f2525 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -22,11 +22,13 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - namespace flutter.plugins.contactsservice - compileSdkVersion 35 + namespace "flutter.plugins.contactsservice.contactsservice" + compileSdk flutter.compileSdkVersion + ndkVersion flutter.ndkVersion defaultConfig { minSdkVersion 16 targetSdkVersion 30 + compileSdkVersion 35 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index f8e8aad1..139a942b 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -15,8 +15,10 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 30 + namespace "flutter.plugins.contactsservice.contactsserviceexample" + compileSdk flutter.compileSdkVersion + ndkVersion flutter.ndkVersion lintOptions { disable 'InvalidPackage' } @@ -26,6 +28,7 @@ android { applicationId "flutter.plugins.contactsservice.contactsserviceexample" minSdkVersion 16 targetSdkVersion 30 + compileSdkVersion 35 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" From 6cfa560185f9c63e66ffd9bf5060e3ba78051ce7 Mon Sep 17 00:00:00 2001 From: sajanIocod Date: Mon, 3 Feb 2025 12:18:09 +0530 Subject: [PATCH 06/11] Add Flutter Gradle plugin to build.gradle for contacts service --- android/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/android/build.gradle b/android/build.gradle index 511f2525..9cd0caca 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -20,6 +20,7 @@ rootProject.allprojects { } apply plugin: 'com.android.library' +apply plugin: 'dev.flutter.flutter-gradle-plugin' android { namespace "flutter.plugins.contactsservice.contactsservice" From fa340e970b0da7e3da3f6898fa4556f296a2a9ec Mon Sep 17 00:00:00 2001 From: sajanIocod Date: Mon, 3 Feb 2025 13:55:27 +0530 Subject: [PATCH 07/11] Remove Flutter Gradle plugin from build.gradle for contacts service --- android/build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 9cd0caca..e3f2814a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -20,8 +20,6 @@ rootProject.allprojects { } apply plugin: 'com.android.library' -apply plugin: 'dev.flutter.flutter-gradle-plugin' - android { namespace "flutter.plugins.contactsservice.contactsservice" compileSdk flutter.compileSdkVersion From c8b19928c2f69f20e6fd2592d01042a9ce8fe05e Mon Sep 17 00:00:00 2001 From: sajanIocod Date: Mon, 3 Feb 2025 13:58:56 +0530 Subject: [PATCH 08/11] Update Gradle plugin version to 8.1.0 in build.gradle --- android/build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index e3f2814a..602d47dc 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.0.1' + classpath 'com.android.tools.build:gradle:8.1.0' } } @@ -40,5 +40,4 @@ android { dependencies { testImplementation 'junit:junit:4.12' testImplementation 'com.google.truth:truth:1.0' - classpath 'com.android.tools.build:gradle:8.1.0' } From 2ae44c46677fa1acfb8137c9942f1d99521dbb2b Mon Sep 17 00:00:00 2001 From: sajanIocod Date: Mon, 3 Feb 2025 14:53:54 +0530 Subject: [PATCH 09/11] Enable AndroidX and Jetifier in gradle.properties --- android/gradle.properties | 3 +++ 1 file changed, 3 insertions(+) diff --git a/android/gradle.properties b/android/gradle.properties index 8bd86f68..e60119f3 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1 +1,4 @@ org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true + From 5e5c29b9b2e2db695c0db957d42e88197d7df41c Mon Sep 17 00:00:00 2001 From: sajanIocod Date: Mon, 3 Feb 2025 14:59:18 +0530 Subject: [PATCH 10/11] Fix potential NullPointerException by checking cursor before closing in ContactsServicePlugin --- .../contactsservice/ContactsServicePlugin.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/flutter/plugins/contactsservice/contactsservice/ContactsServicePlugin.java b/android/src/main/java/flutter/plugins/contactsservice/contactsservice/ContactsServicePlugin.java index 8c69de78..acb7f0b9 100644 --- a/android/src/main/java/flutter/plugins/contactsservice/contactsservice/ContactsServicePlugin.java +++ b/android/src/main/java/flutter/plugins/contactsservice/contactsservice/ContactsServicePlugin.java @@ -296,7 +296,9 @@ public boolean onActivityResult(int requestCode, int resultCode, Intent intent) Log.e(LOG_TAG, "onActivityResult - cursor.moveToFirst() returns false"); finishWithResult(FORM_OPERATION_CANCELED); }}else{return true;} - cursor.close(); + if(cursor != null){ + cursor.close(); + } return true; } From e6f90c130127bc67ed75c3f42f93398d18dc17cd Mon Sep 17 00:00:00 2001 From: sajanIocod Date: Mon, 3 Feb 2025 15:21:09 +0530 Subject: [PATCH 11/11] Fix cursor handling in ContactsServicePlugin to prevent NullPointerException --- .../org.eclipse.buildship.core.prefs | 13 +++++++++++ .../ContactsServicePlugin.java | 23 +++++++++++-------- 2 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 android/.settings/org.eclipse.buildship.core.prefs diff --git a/android/.settings/org.eclipse.buildship.core.prefs b/android/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 00000000..c835726a --- /dev/null +++ b/android/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,13 @@ +arguments=--init-script /var/folders/0h/1wd_6skn0h3gppnj15t0tbl40000gn/T/d146c9752a26f79b52047fb6dc6ed385d064e120494f96f08ca63a317c41f94c.gradle +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(7.4.2)) +connection.project.dir= +eclipse.preferences.version=1 +gradle.user.home= +java.home=/Library/Java/JavaVirtualMachines/jdk-23.jdk/Contents/Home +jvm.arguments= +offline.mode=false +override.workspace.settings=true +show.console.view=true +show.executions.view=true diff --git a/android/src/main/java/flutter/plugins/contactsservice/contactsservice/ContactsServicePlugin.java b/android/src/main/java/flutter/plugins/contactsservice/contactsservice/ContactsServicePlugin.java index acb7f0b9..35114428 100644 --- a/android/src/main/java/flutter/plugins/contactsservice/contactsservice/ContactsServicePlugin.java +++ b/android/src/main/java/flutter/plugins/contactsservice/contactsservice/ContactsServicePlugin.java @@ -287,17 +287,20 @@ public boolean onActivityResult(int requestCode, int resultCode, Intent intent) return true; } Uri contactUri = intent.getData(); - if (intent != null){ - Cursor cursor = contentResolver.query(contactUri, null, null, null, null); - if (cursor.moveToFirst()) { - String id = contactUri.getLastPathSegment(); - getContacts("openDeviceContactPicker", id, false, false, false, localizedLabels, this.result); + if (intent != null) { + Cursor cursor = contentResolver.query(contactUri, null, null, null, null); + if (cursor != null && cursor.moveToFirst()) { + String id = contactUri.getLastPathSegment(); + getContacts("openDeviceContactPicker", id, false, false, false, localizedLabels, this.result); + } else { + Log.e(LOG_TAG, "onActivityResult - cursor.moveToFirst() returns false"); + finishWithResult(FORM_OPERATION_CANCELED); + } + if (cursor != null) { + cursor.close(); + } } else { - Log.e(LOG_TAG, "onActivityResult - cursor.moveToFirst() returns false"); - finishWithResult(FORM_OPERATION_CANCELED); - }}else{return true;} - if(cursor != null){ - cursor.close(); + return true; } return true; }