11package flutter .plugins .contactsservice .contactsservice ;
22
3- import static android .provider .ContactsContract .CommonDataKinds ;
4- import static android .provider .ContactsContract .CommonDataKinds .Email ;
5- import static android .provider .ContactsContract .CommonDataKinds .Organization ;
6- import static android .provider .ContactsContract .CommonDataKinds .Phone ;
7- import static android .provider .ContactsContract .CommonDataKinds .StructuredName ;
8- import static android .provider .ContactsContract .CommonDataKinds .StructuredPostal ;
9-
103import android .annotation .TargetApi ;
114import android .content .ContentProviderOperation ;
125import android .content .ContentResolver ;
2114import android .os .Build ;
2215import android .provider .BaseColumns ;
2316import android .provider .ContactsContract ;
24- import android .provider .ContactsContract .RawContacts ;
2517import android .text .TextUtils ;
2618import android .util .Log ;
27- import io .flutter .embedding .engine .plugins .FlutterPlugin ;
28- import io .flutter .embedding .engine .plugins .activity .ActivityAware ;
29- import io .flutter .embedding .engine .plugins .activity .ActivityPluginBinding ;
30- import io .flutter .plugin .common .BinaryMessenger ;
31- import io .flutter .plugin .common .MethodCall ;
32- import io .flutter .plugin .common .MethodChannel ;
33- import io .flutter .plugin .common .MethodChannel .MethodCallHandler ;
34- import io .flutter .plugin .common .MethodChannel .Result ;
35- import io .flutter .plugin .common .PluginRegistry ;
36- import io .flutter .plugin .common .PluginRegistry .Registrar ;
19+
3720import java .io .ByteArrayOutputStream ;
3821import java .io .IOException ;
3922import java .io .InputStream ;
4023import java .util .ArrayList ;
24+ import java .util .Arrays ;
4125import java .util .Collections ;
4226import java .util .Comparator ;
4327import java .util .HashMap ;
4731import java .util .concurrent .ThreadPoolExecutor ;
4832import java .util .concurrent .TimeUnit ;
4933
34+ import io .flutter .embedding .engine .plugins .FlutterPlugin ;
35+ import io .flutter .embedding .engine .plugins .activity .ActivityAware ;
36+ import io .flutter .embedding .engine .plugins .activity .ActivityPluginBinding ;
37+ import io .flutter .plugin .common .BinaryMessenger ;
38+ import io .flutter .plugin .common .MethodCall ;
39+ import io .flutter .plugin .common .MethodChannel ;
40+ import io .flutter .plugin .common .MethodChannel .MethodCallHandler ;
41+ import io .flutter .plugin .common .MethodChannel .Result ;
42+ import io .flutter .plugin .common .PluginRegistry ;
43+ import io .flutter .plugin .common .PluginRegistry .Registrar ;
44+
45+ import static android .app .Activity .RESULT_CANCELED ;
46+ import static android .provider .ContactsContract .CommonDataKinds ;
47+ import static android .provider .ContactsContract .CommonDataKinds .Email ;
48+ import static android .provider .ContactsContract .CommonDataKinds .Organization ;
49+ import static android .provider .ContactsContract .CommonDataKinds .Phone ;
50+ import static android .provider .ContactsContract .CommonDataKinds .StructuredName ;
51+ import static android .provider .ContactsContract .CommonDataKinds .StructuredPostal ;
52+
5053@ TargetApi (Build .VERSION_CODES .ECLAIR )
5154public class ContactsServicePlugin implements MethodCallHandler , FlutterPlugin , ActivityAware {
5255
@@ -95,10 +98,10 @@ public void onDetachedFromEngine(FlutterPluginBinding binding) {
9598 public void onMethodCall (MethodCall call , Result result ) {
9699 switch (call .method ){
97100 case "getContacts" : {
98- this .getContacts ((String )call .argument ("query" ), (boolean )call .argument ("withThumbnails" ), (boolean )call .argument ("photoHighResolution" ), (boolean )call .argument ("orderByGivenName" ), result );
101+ this .getContacts (call . method , (String )call .argument ("query" ), (boolean )call .argument ("withThumbnails" ), (boolean )call .argument ("photoHighResolution" ), (boolean )call .argument ("orderByGivenName" ), result );
99102 break ;
100103 } case "getContactsForPhone" : {
101- this .getContactsForPhone ((String )call .argument ("phone" ), (boolean )call .argument ("withThumbnails" ), (boolean )call .argument ("photoHighResolution" ), (boolean )call .argument ("orderByGivenName" ), result );
104+ this .getContactsForPhone (call . method , (String )call .argument ("phone" ), (boolean )call .argument ("withThumbnails" ), (boolean )call .argument ("photoHighResolution" ), (boolean )call .argument ("orderByGivenName" ), result );
102105 break ;
103106 } case "getAvatar" : {
104107 final Contact contact = Contact .fromMap ((HashMap )call .argument ("contact" ));
@@ -145,6 +148,9 @@ public void onMethodCall(MethodCall call, Result result) {
145148 result .success (FORM_COULD_NOT_BE_OPEN );
146149 }
147150 break ;
151+ } case "openDeviceContactPicker" : {
152+ openDeviceContactPicker (result );
153+ break ;
148154 } default : {
149155 result .notImplemented ();
150156 break ;
@@ -189,12 +195,12 @@ public void onMethodCall(MethodCall call, Result result) {
189195
190196
191197 @ TargetApi (Build .VERSION_CODES .ECLAIR )
192- private void getContacts (String query , boolean withThumbnails , boolean photoHighResolution , boolean orderByGivenName , Result result ) {
193- new GetContactsTask (result , withThumbnails , photoHighResolution , orderByGivenName ).executeOnExecutor (executor , query , false );
198+ private void getContacts (String callMethod , String query , boolean withThumbnails , boolean photoHighResolution , boolean orderByGivenName , Result result ) {
199+ new GetContactsTask (callMethod , result , withThumbnails , photoHighResolution , orderByGivenName ).executeOnExecutor (executor , query , false );
194200 }
195201
196- private void getContactsForPhone (String phone , boolean withThumbnails , boolean photoHighResolution , boolean orderByGivenName , Result result ) {
197- new GetContactsTask (result , withThumbnails , photoHighResolution , orderByGivenName ).executeOnExecutor (executor , phone , true );
202+ private void getContactsForPhone (String callMethod , String phone , boolean withThumbnails , boolean photoHighResolution , boolean orderByGivenName , Result result ) {
203+ new GetContactsTask (callMethod , result , withThumbnails , photoHighResolution , orderByGivenName ).executeOnExecutor (executor , phone , true );
198204 }
199205
200206 @ Override
@@ -228,14 +234,15 @@ public void onDetachedFromActivity() {
228234 private class BaseContactsServiceDelegate implements PluginRegistry .ActivityResultListener {
229235 private static final int REQUEST_OPEN_CONTACT_FORM = 52941 ;
230236 private static final int REQUEST_OPEN_EXISTING_CONTACT = 52942 ;
237+ private static final int REQUEST_OPEN_CONTACT_PICKER = 52943 ;
231238 private Result result ;
232239
233240 void setResult (Result result ) {
234241 this .result = result ;
235242 }
236243
237244 void finishWithResult (Object result ) {
238- if (result != null ) {
245+ if (this . result != null ) {
239246 this .result .success (result );
240247 this .result = null ;
241248 }
@@ -251,9 +258,27 @@ public boolean onActivityResult(int requestCode, int resultCode, Intent intent)
251258 finishWithResult (FORM_OPERATION_CANCELED );
252259 }
253260 return true ;
254- } else {
255- finishWithResult (FORM_COULD_NOT_BE_OPEN );
256261 }
262+
263+ if (requestCode == REQUEST_OPEN_CONTACT_PICKER ) {
264+ if (resultCode == RESULT_CANCELED ) {
265+ finishWithResult (FORM_OPERATION_CANCELED );
266+ return true ;
267+ }
268+ Uri contactUri = intent .getData ();
269+ Cursor cursor = contentResolver .query (contactUri , null , null , null , null );
270+ if (cursor .moveToFirst ()) {
271+ String id = contactUri .getLastPathSegment ();
272+ getContacts ("openDeviceContactPicker" , id , false , false , false , this .result );
273+ } else {
274+ Log .e (LOG_TAG , "onActivityResult - cursor.moveToFirst() returns false" );
275+ finishWithResult (FORM_OPERATION_CANCELED );
276+ }
277+ cursor .close ();
278+ return true ;
279+ }
280+
281+ finishWithResult (FORM_COULD_NOT_BE_OPEN );
257282 return false ;
258283 }
259284
@@ -284,6 +309,12 @@ void openContactForm() {
284309 }
285310 }
286311
312+ void openContactPicker () {
313+ Intent intent = new Intent (Intent .ACTION_PICK );
314+ intent .setType (ContactsContract .Contacts .CONTENT_TYPE );
315+ startIntent (intent , REQUEST_OPEN_CONTACT_PICKER );
316+ }
317+
287318 void startIntent (Intent intent , int request ) {
288319 }
289320
@@ -310,7 +341,16 @@ HashMap getContactByIdentifier(String identifier) {
310341 return null ;
311342 }
312343 }
313-
344+
345+ private void openDeviceContactPicker (Result result ) {
346+ if (delegate != null ) {
347+ delegate .setResult (result );
348+ delegate .openContactPicker ();
349+ } else {
350+ result .success (FORM_COULD_NOT_BE_OPEN );
351+ }
352+ }
353+
314354 private class ContactServiceDelegateOld extends BaseContactsServiceDelegate {
315355 private final PluginRegistry .Registrar registrar ;
316356
@@ -350,7 +390,11 @@ void unbindActivity() {
350390 @ Override
351391 void startIntent (Intent intent , int request ) {
352392 if (this .activityPluginBinding != null ) {
353- activityPluginBinding .getActivity ().startActivityForResult (intent , request );
393+ if (intent .resolveActivity (context .getPackageManager ()) != null ) {
394+ activityPluginBinding .getActivity ().startActivityForResult (intent , request );
395+ } else {
396+ finishWithResult (FORM_COULD_NOT_BE_OPEN );
397+ }
354398 } else {
355399 context .startActivity (intent );
356400 }
@@ -360,12 +404,14 @@ void startIntent(Intent intent, int request) {
360404 @ TargetApi (Build .VERSION_CODES .CUPCAKE )
361405 private class GetContactsTask extends AsyncTask <Object , Void , ArrayList <HashMap >> {
362406
407+ private String callMethod ;
363408 private Result getContactResult ;
364409 private boolean withThumbnails ;
365410 private boolean photoHighResolution ;
366411 private boolean orderByGivenName ;
367412
368- public GetContactsTask (Result result , boolean withThumbnails , boolean photoHighResolution , boolean orderByGivenName ){
413+ public GetContactsTask (String callMethod , Result result , boolean withThumbnails , boolean photoHighResolution , boolean orderByGivenName ){
414+ this .callMethod = callMethod ;
369415 this .getContactResult = result ;
370416 this .withThumbnails = withThumbnails ;
371417 this .photoHighResolution = photoHighResolution ;
@@ -375,10 +421,12 @@ public GetContactsTask(Result result, boolean withThumbnails, boolean photoHighR
375421 @ TargetApi (Build .VERSION_CODES .ECLAIR )
376422 protected ArrayList <HashMap > doInBackground (Object ... params ) {
377423 ArrayList <Contact > contacts ;
378- if ((Boolean ) params [1 ])
379- contacts = getContactsFrom (getCursorForPhone (((String ) params [0 ])));
380- else
381- contacts = getContactsFrom (getCursor (((String ) params [0 ])));
424+ switch (callMethod ) {
425+ case "openDeviceContactPicker" : contacts = getContactsFrom (getCursor (null , (String ) params [0 ])); break ;
426+ case "getContacts" : contacts = getContactsFrom (getCursor ((String ) params [0 ], null )); break ;
427+ case "getContactsForPhone" : contacts = getContactsFrom (getCursorForPhone (((String ) params [0 ]))); break ;
428+ default : return null ;
429+ }
382430
383431 if (withThumbnails ) {
384432 for (Contact c : contacts ){
@@ -418,26 +466,33 @@ public int compare(Contact contactA, Contact contactB) {
418466 }
419467
420468 protected void onPostExecute (ArrayList <HashMap > result ) {
421- getContactResult .success (result );
469+ if (result == null ) {
470+ getContactResult .notImplemented ();
471+ } else {
472+ getContactResult .success (result );
473+ }
422474 }
423475 }
424476
425477
426- private Cursor getCursor (String query ) {
427- String selection = ContactsContract .Data .MIMETYPE + "=? OR " + ContactsContract .Data .MIMETYPE + "=? OR "
478+ private Cursor getCursor (String query , String rawContactId ) {
479+ String selection = "(" + ContactsContract .Data .MIMETYPE + "=? OR " + ContactsContract .Data .MIMETYPE + "=? OR "
428480 + ContactsContract .Data .MIMETYPE + "=? OR " + ContactsContract .Data .MIMETYPE + "=? OR "
429481 + ContactsContract .Data .MIMETYPE + "=? OR " + ContactsContract .Data .MIMETYPE + "=? OR "
430- + ContactsContract .Data .MIMETYPE + "=? OR " + ContactsContract .RawContacts .ACCOUNT_TYPE + "=?" ;
431- String [] selectionArgs = new String [] { CommonDataKinds .Note .CONTENT_ITEM_TYPE , Email .CONTENT_ITEM_TYPE ,
482+ + ContactsContract .Data .MIMETYPE + "=? OR " + ContactsContract .RawContacts .ACCOUNT_TYPE + "=?" + ")" ;
483+ ArrayList < String > selectionArgs = new ArrayList <>( Arrays . asList ( CommonDataKinds .Note .CONTENT_ITEM_TYPE , Email .CONTENT_ITEM_TYPE ,
432484 Phone .CONTENT_ITEM_TYPE , StructuredName .CONTENT_ITEM_TYPE , Organization .CONTENT_ITEM_TYPE ,
433- StructuredPostal .CONTENT_ITEM_TYPE , CommonDataKinds .Event .CONTENT_ITEM_TYPE , ContactsContract .RawContacts .ACCOUNT_TYPE
434- };
435- if ( query != null ){
436- selectionArgs = new String []{ query + "%" } ;
485+ StructuredPostal .CONTENT_ITEM_TYPE , CommonDataKinds .Event .CONTENT_ITEM_TYPE , ContactsContract .RawContacts .ACCOUNT_TYPE ));
486+ if ( query != null ) {
487+ selectionArgs = new ArrayList <>();
488+ selectionArgs . add ( query + "%" ) ;
437489 selection = ContactsContract .Contacts .DISPLAY_NAME_PRIMARY + " LIKE ?" ;
438490 }
439-
440- return contentResolver .query (ContactsContract .Data .CONTENT_URI , PROJECTION , selection , selectionArgs , null );
491+ if (rawContactId != null ) {
492+ selectionArgs .add (rawContactId );
493+ selection += " AND " + ContactsContract .Data .CONTACT_ID + " =?" ;
494+ }
495+ return contentResolver .query (ContactsContract .Data .CONTENT_URI , PROJECTION , selection , selectionArgs .toArray (new String [selectionArgs .size ()]), null );
441496 }
442497
443498 private Cursor getCursorForPhone (String phone ) {
@@ -830,6 +885,9 @@ private boolean updateContact(Contact contact) {
830885 contentResolver .applyBatch (ContactsContract .AUTHORITY , ops );
831886 return true ;
832887 } catch (Exception e ) {
888+ // Log exception
889+ Log .e ("TAG" , "Exception encountered while inserting contact: " );
890+ e .printStackTrace ();
833891 return false ;
834892 }
835893 }
0 commit comments