From ebd6e6424e75b53fb3e510e6da9b507dc324dfda Mon Sep 17 00:00:00 2001 From: eric Date: Sat, 8 Sep 2018 23:34:27 -0500 Subject: [PATCH 1/6] Added a new dart api function to add the functionality to get just a contact name. If you only need a contact name, the phone number picker shows a dialog with repeated contacts that have multiple phone numbers. This does not work well. The API uses parameters to tell the native platform which type is being requested. The code was added for Android and not iOS. However, the iOS code should not break and simply gets the phone number as I cannot develop for iOS. --- .../contactpicker/ContactPickerPlugin.java | 39 ++++++++++++++----- example/lib/main.dart | 12 +++++- lib/contact_picker.dart | 13 ++++++- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/android/src/main/java/net/goderbauer/flutter/contactpicker/ContactPickerPlugin.java b/android/src/main/java/net/goderbauer/flutter/contactpicker/ContactPickerPlugin.java index 1c4c373..4367e3f 100644 --- a/android/src/main/java/net/goderbauer/flutter/contactpicker/ContactPickerPlugin.java +++ b/android/src/main/java/net/goderbauer/flutter/contactpicker/ContactPickerPlugin.java @@ -40,6 +40,7 @@ private ContactPickerPlugin(Activity activity) { @Override public void onMethodCall(MethodCall call, Result result) { + Uri intentUri; if (call.method.equals("selectContact")) { if (pendingResult != null) { pendingResult.error("multiple_requests", "Cancelled by a second request.", null); @@ -47,7 +48,22 @@ public void onMethodCall(MethodCall call, Result result) { } pendingResult = result; - Intent i = new Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI); + String requestType = "contacts"; + if (call.hasArgument("type")) { + requestType = call.argument("type"); + } + + switch (requestType) { + case "phone": + intentUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; + break; + case "contacts": + default: + intentUri = ContactsContract.Contacts.CONTENT_URI; + break; + } + + Intent i = new Intent(Intent.ACTION_PICK, intentUri); activity.startActivityForResult(i, PICK_CONTACT); } else { result.notImplemented(); @@ -68,17 +84,20 @@ public boolean onActivityResult(int requestCode, int resultCode, Intent data) { Cursor cursor = activity.getContentResolver().query(contactUri, null, null, null, null); cursor.moveToFirst(); - int phoneType = cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE)); - String customLabel = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.LABEL)); - String label = (String) ContactsContract.CommonDataKinds.Email.getTypeLabel(activity.getResources(), phoneType, customLabel); - String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); - String fullName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); - HashMap phoneNumber = new HashMap<>(); - phoneNumber.put("number", number); - phoneNumber.put("label", label); - HashMap contact = new HashMap<>(); + + int phoneTypeIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE); + if (phoneTypeIndex != -1) { + int phoneType = cursor.getInt(phoneTypeIndex); + String customLabel = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.LABEL)); + String label = (String) ContactsContract.CommonDataKinds.Email.getTypeLabel(activity.getResources(), phoneType, customLabel); + String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); + phoneNumber.put("number", number); + phoneNumber.put("label", label); + } + String fullName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); + contact.put("fullName", fullName); contact.put("phoneNumber", phoneNumber); diff --git a/example/lib/main.dart b/example/lib/main.dart index 1f635ef..1358dea 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -31,7 +31,7 @@ class _MyAppState extends State { children: [ new MaterialButton( color: Colors.blue, - child: new Text("CLICK ME"), + child: new Text("Phone"), onPressed: () async { Contact contact = await _contactPicker.selectContact(); setState(() { @@ -39,6 +39,16 @@ class _MyAppState extends State { }); }, ), + new MaterialButton( + color: Colors.blue, + child: new Text("Contact"), + onPressed: () async { + Contact contact = await _contactPicker.selectContactName(); + setState(() { + _contact = contact; + }); + }, + ), new Text( _contact == null ? 'No contact selected.' : _contact.toString(), ), diff --git a/lib/contact_picker.dart b/lib/contact_picker.dart index 49734e0..8d3a859 100644 --- a/lib/contact_picker.dart +++ b/lib/contact_picker.dart @@ -19,8 +19,19 @@ class ContactPicker { /// Returns the [Contact] selected by the user, or `null` if the user canceled /// out of the dialog. Future selectContact() async { + return doSelect('phone'); + } + + Future selectContactName() async { + return doSelect('contact'); + } + + Future doSelect(String type) async { + final Map params = { + 'type': type, + }; final Map result = - await _channel.invokeMethod('selectContact'); + await _channel.invokeMethod('selectContact', params); if (result == null) { return null; } From d904671415fc46424d6d618bae474719c4e6bc2b Mon Sep 17 00:00:00 2001 From: Satya Date: Sun, 30 Sep 2018 12:31:31 -0500 Subject: [PATCH 2/6] Updated to select a contact without having to drill down to the contact details --- ios/Classes/ContactPickerPlugin.m | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ios/Classes/ContactPickerPlugin.m b/ios/Classes/ContactPickerPlugin.m index 6da8c9f..1feb841 100644 --- a/ios/Classes/ContactPickerPlugin.m +++ b/ios/Classes/ContactPickerPlugin.m @@ -55,6 +55,29 @@ - (void)contactPicker:(CNContactPickerViewController *)picker _result = nil; } +- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact { + + printf("didSelectContact Called!"); + NSString * firstName = contact.givenName; + NSString * lastName = contact.familyName; + + //Am sure there is a better way to append a space. + NSString *space = @" "; + NSString *fullNameStr= [firstName stringByAppendingString:space ]; + fullNameStr = [fullNameStr stringByAppendingString:lastName ]; + + //Since we dont really care about which phone number it is, just seelct the first one and call it home + // CNLabeledValue *phoneNumberValue = contact.phoneNumbers[0].value; + NSString *label = @"Home"; + + NSDictionary *phoneNumber = [NSDictionary + dictionaryWithObjectsAndKeys:[contact.phoneNumbers[0].value stringValue], @"number", + label, + @"label", nil]; + _result([NSDictionary + dictionaryWithObjectsAndKeys:fullNameStr, @"fullName", phoneNumber, @"phoneNumber", nil]); + _result = nil; +} - (void)contactPickerDidCancel:(CNContactPickerViewController *)picker { _result(nil); _result = nil; From ea26b258a0acec4bec7944211cf0d85e95c034fd Mon Sep 17 00:00:00 2001 From: Satya Date: Sun, 30 Sep 2018 15:05:40 -0500 Subject: [PATCH 3/6] Updated to select a contact without having to drill down to the contact details --- ios/Classes/ContactPickerPlugin.m | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ios/Classes/ContactPickerPlugin.m b/ios/Classes/ContactPickerPlugin.m index 1feb841..290464f 100644 --- a/ios/Classes/ContactPickerPlugin.m +++ b/ios/Classes/ContactPickerPlugin.m @@ -57,17 +57,13 @@ - (void)contactPicker:(CNContactPickerViewController *)picker - (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact { - printf("didSelectContact Called!"); NSString * firstName = contact.givenName; NSString * lastName = contact.familyName; - //Am sure there is a better way to append a space. - NSString *space = @" "; - NSString *fullNameStr= [firstName stringByAppendingString:space ]; - fullNameStr = [fullNameStr stringByAppendingString:lastName ]; + NSString *fullNameStr= [NSString stringWithFormat:@"%@ %@", firstName,lastName]; - //Since we dont really care about which phone number it is, just seelct the first one and call it home - // CNLabeledValue *phoneNumberValue = contact.phoneNumbers[0].value; + //Since we only need a phone number and dont really need the associated label, just select the first one and call it home + //CNLabeledValue *phoneNumberValue = contact.phoneNumbers[0].value; NSString *label = @"Home"; NSDictionary *phoneNumber = [NSDictionary @@ -78,6 +74,7 @@ - (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:( dictionaryWithObjectsAndKeys:fullNameStr, @"fullName", phoneNumber, @"phoneNumber", nil]); _result = nil; } + - (void)contactPickerDidCancel:(CNContactPickerViewController *)picker { _result(nil); _result = nil; From 6b9382ebb75d9a397768f55e2aaaeee4341ccc50 Mon Sep 17 00:00:00 2001 From: Satya Date: Sun, 30 Sep 2018 16:21:30 -0500 Subject: [PATCH 4/6] Check for phoneNumber to be present, and updated to use the right label. Still requires phone number to return a contact. --- ios/Classes/ContactPickerPlugin.m | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ios/Classes/ContactPickerPlugin.m b/ios/Classes/ContactPickerPlugin.m index 290464f..af3e610 100644 --- a/ios/Classes/ContactPickerPlugin.m +++ b/ios/Classes/ContactPickerPlugin.m @@ -62,14 +62,14 @@ - (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:( NSString *fullNameStr= [NSString stringWithFormat:@"%@ %@", firstName,lastName]; - //Since we only need a phone number and dont really need the associated label, just select the first one and call it home - //CNLabeledValue *phoneNumberValue = contact.phoneNumbers[0].value; - NSString *label = @"Home"; + NSDictionary *phoneNumber = nil; + if(contact.phoneNumbers && contact.phoneNumbers.count > 0) { + phoneNumber = [NSDictionary + dictionaryWithObjectsAndKeys:[contact.phoneNumbers[0].value stringValue], @"number", + [CNLabeledValue localizedStringForLabel:contact.phoneNumbers[0].label], + @"label", nil]; + } - NSDictionary *phoneNumber = [NSDictionary - dictionaryWithObjectsAndKeys:[contact.phoneNumbers[0].value stringValue], @"number", - label, - @"label", nil]; _result([NSDictionary dictionaryWithObjectsAndKeys:fullNameStr, @"fullName", phoneNumber, @"phoneNumber", nil]); _result = nil; From 788bb34058d8f80d516d728c3b59251fb93bb067 Mon Sep 17 00:00:00 2001 From: eric Date: Sun, 30 Sep 2018 21:13:38 -0500 Subject: [PATCH 5/6] Updated build tools to most recent --- example/android/app/build.gradle | 12 ++--- lib/contact_picker.dart | 75 -------------------------------- 2 files changed, 6 insertions(+), 81 deletions(-) delete mode 100644 lib/contact_picker.dart diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 255ae05..d9251cd 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -15,8 +15,8 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 25 - buildToolsVersion '25.0.3' + compileSdkVersion 28 + buildToolsVersion '28.0.3' lintOptions { disable 'InvalidPackage' @@ -26,7 +26,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.yourcompany.contactpickerexample" minSdkVersion 16 - targetSdkVersion 25 + targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -46,7 +46,7 @@ flutter { } dependencies { - androidTestCompile 'com.android.support:support-annotations:25.4.0' - androidTestCompile 'com.android.support.test:runner:0.5' - androidTestCompile 'com.android.support.test:rules:0.5' + androidTestCompile 'com.android.support:support-annotations:28.0.0' + androidTestCompile 'com.android.support.test:runner:1.0.2' + androidTestCompile 'com.android.support.test:rules:1.0.2' } diff --git a/lib/contact_picker.dart b/lib/contact_picker.dart deleted file mode 100644 index 8d3a859..0000000 --- a/lib/contact_picker.dart +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2017 Michael Goderbauer. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:flutter/services.dart'; - -/// Entry point for the ContactPicker plugin. -/// -/// Call [selectContact] to bring up a dialog where the user can pick a contact -/// from his/her address book. -class ContactPicker { - static const MethodChannel _channel = const MethodChannel('contact_picker'); - - /// Brings up a dialog where the user can select a contact from his/her - /// address book. - /// - /// Returns the [Contact] selected by the user, or `null` if the user canceled - /// out of the dialog. - Future selectContact() async { - return doSelect('phone'); - } - - Future selectContactName() async { - return doSelect('contact'); - } - - Future doSelect(String type) async { - final Map params = { - 'type': type, - }; - final Map result = - await _channel.invokeMethod('selectContact', params); - if (result == null) { - return null; - } - return new Contact.fromMap(result); - } -} - -/// Represents a contact selected by the user. -class Contact { - Contact({this.fullName, this.phoneNumber}); - - factory Contact.fromMap(Map map) => new Contact( - fullName: map['fullName'], - phoneNumber: new PhoneNumber.fromMap(map['phoneNumber'])); - - /// The full name of the contact, e.g. "Dr. Daniel Higgens Jr.". - final String fullName; - - /// The phone number of the contact. - final PhoneNumber phoneNumber; - - @override - String toString() => '$fullName: $phoneNumber'; -} - -/// Represents a phone number selected by the user. -class PhoneNumber { - PhoneNumber({this.number, this.label}); - - factory PhoneNumber.fromMap(Map map) => - new PhoneNumber(number: map['number'], label: map['label']); - - /// The formatted phone number, e.g. "+1 (555) 555-5555" - final String number; - - /// The label associated with the phone number, e.g. "home" or "work". - final String label; - - @override - String toString() => '$number ($label)'; -} From cc2e23aab01b60ed03366375fe2b1a0c6fac17ee Mon Sep 17 00:00:00 2001 From: eric Date: Sun, 30 Sep 2018 21:55:30 -0500 Subject: [PATCH 6/6] Somehow deleted the contact_picker.dart file. Bringing it back --- android/build.gradle | 6 +++--- example/android/app/build.gradle | 12 ++++++------ ios/Classes/ContactPickerPlugin.m | 20 -------------------- 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 8fc020d..b4aacdd 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -26,12 +26,12 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 25 - buildToolsVersion '25.0.3' + compileSdkVersion 28 + buildToolsVersion '28.0.3' defaultConfig { minSdkVersion 16 - targetSdkVersion 25 + targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 255ae05..d9251cd 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -15,8 +15,8 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 25 - buildToolsVersion '25.0.3' + compileSdkVersion 28 + buildToolsVersion '28.0.3' lintOptions { disable 'InvalidPackage' @@ -26,7 +26,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.yourcompany.contactpickerexample" minSdkVersion 16 - targetSdkVersion 25 + targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -46,7 +46,7 @@ flutter { } dependencies { - androidTestCompile 'com.android.support:support-annotations:25.4.0' - androidTestCompile 'com.android.support.test:runner:0.5' - androidTestCompile 'com.android.support.test:rules:0.5' + androidTestCompile 'com.android.support:support-annotations:28.0.0' + androidTestCompile 'com.android.support.test:runner:1.0.2' + androidTestCompile 'com.android.support.test:rules:1.0.2' } diff --git a/ios/Classes/ContactPickerPlugin.m b/ios/Classes/ContactPickerPlugin.m index af3e610..6da8c9f 100644 --- a/ios/Classes/ContactPickerPlugin.m +++ b/ios/Classes/ContactPickerPlugin.m @@ -55,26 +55,6 @@ - (void)contactPicker:(CNContactPickerViewController *)picker _result = nil; } -- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact { - - NSString * firstName = contact.givenName; - NSString * lastName = contact.familyName; - - NSString *fullNameStr= [NSString stringWithFormat:@"%@ %@", firstName,lastName]; - - NSDictionary *phoneNumber = nil; - if(contact.phoneNumbers && contact.phoneNumbers.count > 0) { - phoneNumber = [NSDictionary - dictionaryWithObjectsAndKeys:[contact.phoneNumbers[0].value stringValue], @"number", - [CNLabeledValue localizedStringForLabel:contact.phoneNumbers[0].label], - @"label", nil]; - } - - _result([NSDictionary - dictionaryWithObjectsAndKeys:fullNameStr, @"fullName", phoneNumber, @"phoneNumber", nil]); - _result = nil; -} - - (void)contactPickerDidCancel:(CNContactPickerViewController *)picker { _result(nil); _result = nil;