@aalzehla/capacitor-contacts 0.0.4 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{AalzehlaCapacitorContacts.podspec → CapacitorCommunityContacts.podspec} +3 -3
- package/README.md +69 -45
- package/android/build.gradle +15 -12
- package/android/src/main/java/getcapacitor/community/contacts/BiMap.java +45 -0
- package/android/src/main/java/getcapacitor/community/contacts/ContactPayload.java +286 -0
- package/android/src/main/java/getcapacitor/community/contacts/Contacts.java +384 -0
- package/android/src/main/java/getcapacitor/community/contacts/ContactsPlugin.java +255 -0
- package/android/src/main/java/getcapacitor/community/contacts/CreateContactInput.java +233 -0
- package/android/src/main/java/getcapacitor/community/contacts/GetContactsProjectionInput.java +214 -0
- package/dist/esm/definitions.d.ts +285 -19
- package/dist/esm/definitions.js +47 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.d.ts +3 -3
- package/dist/esm/index.js +3 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +9 -7
- package/dist/esm/web.js +21 -7
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +73 -14
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +74 -15
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/BiMap.swift +42 -0
- package/ios/Plugin/ContactPayload.swift +227 -0
- package/ios/Plugin/Contacts.swift +227 -0
- package/ios/Plugin/ContactsPlugin.h +10 -0
- package/ios/Plugin/ContactsPlugin.m +14 -0
- package/ios/Plugin/ContactsPlugin.swift +221 -0
- package/ios/Plugin/CreateContactInput.swift +187 -0
- package/ios/Plugin/GetContactsProjectionInput.swift +119 -0
- package/ios/Plugin/Info.plist +24 -0
- package/package.json +33 -30
- package/Package.swift +0 -28
- package/android/src/main/java/com/aalzehla/capacitor/contacts/CapacitorContactsPlugin.java +0 -177
- package/android/src/main/java/com/aalzehla/capacitor/contacts/ContactDataExtractorVisitor.java +0 -122
- package/android/src/main/java/com/aalzehla/capacitor/contacts/ContactExtractorVisitor.java +0 -31
- package/android/src/main/java/com/aalzehla/capacitor/contacts/PluginContactFields.java +0 -17
- package/android/src/main/java/com/aalzehla/capacitor/contacts/contentQuery/ContentQuery.java +0 -83
- package/android/src/main/java/com/aalzehla/capacitor/contacts/contentQuery/ContentQueryService.java +0 -60
- package/android/src/main/java/com/aalzehla/capacitor/contacts/utils/Utils.java +0 -19
- package/android/src/main/java/com/aalzehla/capacitor/contacts/utils/Visitable.java +0 -5
- package/android/src/main/java/com/aalzehla/capacitor/contacts/utils/Visitor.java +0 -5
- package/dist/docs.json +0 -145
- package/ios/Sources/CapacitorContactsPlugin/CapacitorContactsPlugin.swift +0 -166
- package/ios/Tests/CapacitorContactsPluginTests/CapacitorContactsPluginTests.swift +0 -15
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
package getcapacitor.community.contacts;
|
|
2
|
+
|
|
3
|
+
import android.app.Activity;
|
|
4
|
+
import android.content.ContentProviderOperation;
|
|
5
|
+
import android.content.ContentProviderResult;
|
|
6
|
+
import android.content.ContentResolver;
|
|
7
|
+
import android.database.Cursor;
|
|
8
|
+
import android.net.Uri;
|
|
9
|
+
import android.provider.ContactsContract;
|
|
10
|
+
import android.provider.ContactsContract.CommonDataKinds.Email;
|
|
11
|
+
import android.provider.ContactsContract.CommonDataKinds.Event;
|
|
12
|
+
import android.provider.ContactsContract.CommonDataKinds.Note;
|
|
13
|
+
import android.provider.ContactsContract.CommonDataKinds.Organization;
|
|
14
|
+
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
|
15
|
+
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
|
|
16
|
+
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
|
|
17
|
+
import android.provider.ContactsContract.CommonDataKinds.Website;
|
|
18
|
+
import android.provider.ContactsContract.RawContacts;
|
|
19
|
+
import android.util.Base64;
|
|
20
|
+
import androidx.annotation.NonNull;
|
|
21
|
+
import androidx.annotation.Nullable;
|
|
22
|
+
import java.util.ArrayList;
|
|
23
|
+
import java.util.HashMap;
|
|
24
|
+
|
|
25
|
+
public class Contacts {
|
|
26
|
+
|
|
27
|
+
private final Activity mActivity;
|
|
28
|
+
|
|
29
|
+
public static BiMap<String, Integer> phoneTypeMap = new BiMap<String, Integer>(
|
|
30
|
+
new HashMap<String, Integer>() {
|
|
31
|
+
{
|
|
32
|
+
// Home / Work
|
|
33
|
+
put("home", Phone.TYPE_HOME);
|
|
34
|
+
put("work", Phone.TYPE_WORK);
|
|
35
|
+
// Other
|
|
36
|
+
put("other", Phone.TYPE_OTHER);
|
|
37
|
+
// Custom
|
|
38
|
+
put("custom", Phone.TYPE_CUSTOM);
|
|
39
|
+
// Phone specific
|
|
40
|
+
put("main", Phone.TYPE_MAIN);
|
|
41
|
+
put("mobile", Phone.TYPE_MOBILE);
|
|
42
|
+
put("pager", Phone.TYPE_PAGER);
|
|
43
|
+
put("fax_work", Phone.TYPE_FAX_WORK);
|
|
44
|
+
put("fax_home", Phone.TYPE_FAX_HOME);
|
|
45
|
+
put("fax_other", Phone.TYPE_OTHER_FAX);
|
|
46
|
+
//
|
|
47
|
+
//
|
|
48
|
+
// Android only:
|
|
49
|
+
// Phone specific
|
|
50
|
+
put("callback", Phone.TYPE_CALLBACK);
|
|
51
|
+
put("car", Phone.TYPE_CAR);
|
|
52
|
+
put("company_main", Phone.TYPE_COMPANY_MAIN);
|
|
53
|
+
put("isdn", Phone.TYPE_ISDN);
|
|
54
|
+
put("radio", Phone.TYPE_RADIO);
|
|
55
|
+
put("telex", Phone.TYPE_TELEX);
|
|
56
|
+
put("tty_tdd", Phone.TYPE_TTY_TDD);
|
|
57
|
+
put("work_mobile", Phone.TYPE_WORK_MOBILE);
|
|
58
|
+
put("work_pager", Phone.TYPE_WORK_PAGER);
|
|
59
|
+
put("assistant", Phone.TYPE_ASSISTANT);
|
|
60
|
+
put("mms", Phone.TYPE_MMS);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"custom",
|
|
64
|
+
Phone.TYPE_CUSTOM
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
public static BiMap<String, Integer> emailTypeMap = new BiMap<String, Integer>(
|
|
68
|
+
new HashMap<String, Integer>() {
|
|
69
|
+
{
|
|
70
|
+
// Home / Work
|
|
71
|
+
put("home", Email.TYPE_HOME);
|
|
72
|
+
put("work", Email.TYPE_WORK);
|
|
73
|
+
// Other
|
|
74
|
+
put("other", Email.TYPE_OTHER);
|
|
75
|
+
// Custom
|
|
76
|
+
put("custom", Email.TYPE_CUSTOM);
|
|
77
|
+
//
|
|
78
|
+
//
|
|
79
|
+
// Android only:
|
|
80
|
+
// Email specific
|
|
81
|
+
put("mobile", Email.TYPE_MOBILE);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"custom",
|
|
85
|
+
Email.TYPE_CUSTOM
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
public static BiMap<String, Integer> postalAddressTypeMap = new BiMap<String, Integer>(
|
|
89
|
+
new HashMap<String, Integer>() {
|
|
90
|
+
{
|
|
91
|
+
// Home / Work
|
|
92
|
+
put("home", StructuredPostal.TYPE_HOME);
|
|
93
|
+
put("work", StructuredPostal.TYPE_WORK);
|
|
94
|
+
// Other
|
|
95
|
+
put("other", StructuredPostal.TYPE_OTHER);
|
|
96
|
+
// Custom
|
|
97
|
+
put("custom", StructuredPostal.TYPE_CUSTOM);
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
"custom",
|
|
101
|
+
StructuredPostal.TYPE_CUSTOM
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
Contacts(Activity activity) {
|
|
105
|
+
this.mActivity = activity;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public ContactPayload getContact(@NonNull String contactId, GetContactsProjectionInput projectionInput) {
|
|
109
|
+
String[] projection = projectionInput.getProjection();
|
|
110
|
+
|
|
111
|
+
String selection = ContactsContract.RawContacts.CONTACT_ID + " = ?";
|
|
112
|
+
String[] selectionArgs = new String[] { contactId };
|
|
113
|
+
|
|
114
|
+
ContentResolver cr = this.mActivity.getContentResolver();
|
|
115
|
+
Cursor cursor = cr.query(ContactsContract.Data.CONTENT_URI, projection, selection, selectionArgs, null);
|
|
116
|
+
|
|
117
|
+
if (cursor != null && cursor.getCount() > 0) {
|
|
118
|
+
ContactPayload contact = new ContactPayload(contactId);
|
|
119
|
+
|
|
120
|
+
while (cursor.moveToNext()) {
|
|
121
|
+
contact.fillDataByCursor(cursor);
|
|
122
|
+
}
|
|
123
|
+
cursor.close();
|
|
124
|
+
|
|
125
|
+
return contact;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (cursor != null) {
|
|
129
|
+
cursor.close();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public HashMap<String, ContactPayload> getContacts(GetContactsProjectionInput projectionInput) {
|
|
136
|
+
String[] projection = projectionInput.getProjection();
|
|
137
|
+
|
|
138
|
+
// String[] selectionArgs = projectionInput.getSelectionArgs();
|
|
139
|
+
|
|
140
|
+
// String selection = GetContactsProjectionInput.getSelection(selectionArgs);
|
|
141
|
+
|
|
142
|
+
HashMap<String, ContactPayload> contacts = new HashMap<>();
|
|
143
|
+
|
|
144
|
+
ContentResolver cr = this.mActivity.getContentResolver();
|
|
145
|
+
// Cursor cursor = contentResolver.query(ContactsContract.Data.CONTENT_URI, projection, selectionArgs.length > 0 ? selection : null, selectionArgs.length > 0 ? selectionArgs : null, null);
|
|
146
|
+
Cursor cursor = cr.query(ContactsContract.Data.CONTENT_URI, projection, null, null, null);
|
|
147
|
+
|
|
148
|
+
if (cursor != null && cursor.getCount() > 0) {
|
|
149
|
+
while (cursor.moveToNext()) {
|
|
150
|
+
// String _id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
|
|
151
|
+
String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.CONTACT_ID));
|
|
152
|
+
|
|
153
|
+
ContactPayload contact;
|
|
154
|
+
|
|
155
|
+
if (!contacts.containsKey(contactId)) {
|
|
156
|
+
contact = new ContactPayload(contactId);
|
|
157
|
+
contacts.put(contactId, contact);
|
|
158
|
+
} else {
|
|
159
|
+
contact = contacts.get(contactId);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (contact != null) {
|
|
163
|
+
contact.fillDataByCursor(cursor);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (cursor != null) {
|
|
169
|
+
cursor.close();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return contacts;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
public String createContact(CreateContactInput contactInput) {
|
|
176
|
+
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
|
|
177
|
+
ContentProviderOperation.Builder op = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
|
|
178
|
+
.withValue(RawContacts.ACCOUNT_TYPE, null)
|
|
179
|
+
.withValue(RawContacts.ACCOUNT_NAME, null);
|
|
180
|
+
ops.add(op.build());
|
|
181
|
+
|
|
182
|
+
// Name
|
|
183
|
+
op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
|
184
|
+
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
|
185
|
+
// Add to this key
|
|
186
|
+
.withValue(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
|
|
187
|
+
// Data
|
|
188
|
+
// .withValue(StructuredName.DISPLAY_NAME, name)
|
|
189
|
+
.withValue(StructuredName.GIVEN_NAME, contactInput.nameGiven)
|
|
190
|
+
.withValue(StructuredName.MIDDLE_NAME, contactInput.nameMiddle)
|
|
191
|
+
.withValue(StructuredName.FAMILY_NAME, contactInput.nameFamily)
|
|
192
|
+
.withValue(StructuredName.PREFIX, contactInput.namePrefix)
|
|
193
|
+
.withValue(StructuredName.SUFFIX, contactInput.nameSuffix);
|
|
194
|
+
ops.add(op.build());
|
|
195
|
+
|
|
196
|
+
// Organization
|
|
197
|
+
op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
|
198
|
+
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
|
199
|
+
// Add to this key
|
|
200
|
+
.withValue(ContactsContract.Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE)
|
|
201
|
+
// Data
|
|
202
|
+
.withValue(Organization.COMPANY, contactInput.organizationName)
|
|
203
|
+
.withValue(Organization.TITLE, contactInput.organizationJobTitle)
|
|
204
|
+
.withValue(Organization.DEPARTMENT, contactInput.organizationDepartment);
|
|
205
|
+
ops.add(op.build());
|
|
206
|
+
|
|
207
|
+
// Birthday
|
|
208
|
+
op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
|
209
|
+
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
|
210
|
+
// Add to this key
|
|
211
|
+
.withValue(ContactsContract.Data.MIMETYPE, Event.CONTENT_ITEM_TYPE)
|
|
212
|
+
// Data
|
|
213
|
+
.withValue(Event.START_DATE, contactInput.birthday)
|
|
214
|
+
.withValue(Event.TYPE, ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY);
|
|
215
|
+
ops.add(op.build());
|
|
216
|
+
|
|
217
|
+
// Note
|
|
218
|
+
op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
|
219
|
+
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
|
220
|
+
// Add to this key
|
|
221
|
+
.withValue(ContactsContract.Data.MIMETYPE, Note.CONTENT_ITEM_TYPE)
|
|
222
|
+
// Data
|
|
223
|
+
.withValue(Note.NOTE, contactInput.note);
|
|
224
|
+
ops.add(op.build());
|
|
225
|
+
|
|
226
|
+
// @TODO not sure where to allow yields
|
|
227
|
+
// https://www.grokkingandroid.com/androids-contentprovideroperation-withyieldallowed-explained/
|
|
228
|
+
op.withYieldAllowed(true);
|
|
229
|
+
|
|
230
|
+
// Phones
|
|
231
|
+
for (int i = 0; i < contactInput.phones.size(); i++) {
|
|
232
|
+
CreateContactInput.PhoneInput phone = contactInput.phones.get(i);
|
|
233
|
+
|
|
234
|
+
op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
|
235
|
+
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
|
236
|
+
// Add to this key
|
|
237
|
+
.withValue(ContactsContract.Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
|
|
238
|
+
// Data
|
|
239
|
+
.withValue(Phone.TYPE, phone.type)
|
|
240
|
+
.withValue(Phone.LABEL, phone.label)
|
|
241
|
+
.withValue(Phone.NUMBER, phone.number);
|
|
242
|
+
if (phone.isPrimary) {
|
|
243
|
+
op.withValue(Phone.IS_PRIMARY, true);
|
|
244
|
+
}
|
|
245
|
+
ops.add(op.build());
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Emails
|
|
249
|
+
for (int i = 0; i < contactInput.emails.size(); i++) {
|
|
250
|
+
CreateContactInput.EmailInput email = contactInput.emails.get(i);
|
|
251
|
+
|
|
252
|
+
op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
|
253
|
+
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
|
254
|
+
// Add to this key
|
|
255
|
+
.withValue(ContactsContract.Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
|
|
256
|
+
// Data
|
|
257
|
+
.withValue(Email.TYPE, email.type)
|
|
258
|
+
.withValue(Email.LABEL, email.label)
|
|
259
|
+
.withValue(Email.ADDRESS, email.address);
|
|
260
|
+
if (email.isPrimary) {
|
|
261
|
+
op.withValue(Email.IS_PRIMARY, true);
|
|
262
|
+
}
|
|
263
|
+
ops.add(op.build());
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// URLs
|
|
267
|
+
for (int i = 0; i < contactInput.urls.size(); i++) {
|
|
268
|
+
op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
|
269
|
+
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
|
270
|
+
// Add to this key
|
|
271
|
+
.withValue(ContactsContract.Data.MIMETYPE, Website.CONTENT_ITEM_TYPE)
|
|
272
|
+
// Data
|
|
273
|
+
.withValue(Website.URL, contactInput.urls.get(i));
|
|
274
|
+
ops.add(op.build());
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Postal Addresses
|
|
278
|
+
for (int i = 0; i < contactInput.postalAddresses.size(); i++) {
|
|
279
|
+
CreateContactInput.PostalAddressInput address = contactInput.postalAddresses.get(i);
|
|
280
|
+
|
|
281
|
+
op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
|
282
|
+
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
|
283
|
+
// Add to this key
|
|
284
|
+
.withValue(ContactsContract.Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE)
|
|
285
|
+
// Data
|
|
286
|
+
.withValue(StructuredPostal.TYPE, address.type)
|
|
287
|
+
.withValue(StructuredPostal.LABEL, address.label)
|
|
288
|
+
.withValue(StructuredPostal.STREET, address.street)
|
|
289
|
+
.withValue(StructuredPostal.NEIGHBORHOOD, address.neighborhood)
|
|
290
|
+
.withValue(StructuredPostal.CITY, address.city)
|
|
291
|
+
.withValue(StructuredPostal.REGION, address.region)
|
|
292
|
+
.withValue(StructuredPostal.POSTCODE, address.postcode)
|
|
293
|
+
.withValue(StructuredPostal.COUNTRY, address.country);
|
|
294
|
+
if (address.isPrimary) {
|
|
295
|
+
op.withValue(StructuredPostal.IS_PRIMARY, true);
|
|
296
|
+
}
|
|
297
|
+
ops.add(op.build());
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Image
|
|
301
|
+
if (contactInput.image != null && contactInput.image.base64String != null) {
|
|
302
|
+
byte[] photoData = Base64.decode(contactInput.image.base64String, Base64.DEFAULT);
|
|
303
|
+
|
|
304
|
+
op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
|
305
|
+
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
|
306
|
+
// Add to this key
|
|
307
|
+
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
|
|
308
|
+
// Data
|
|
309
|
+
.withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, photoData);
|
|
310
|
+
ops.add(op.build());
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
ContentResolver cr = this.mActivity.getContentResolver();
|
|
315
|
+
ContentProviderResult[] result = cr.applyBatch(ContactsContract.AUTHORITY, ops);
|
|
316
|
+
|
|
317
|
+
if (result != null && result.length > 0 && result[0] != null) {
|
|
318
|
+
// This will return a URI for retrieving the contact, e.g.: "content://com.android.contacts/raw_contacts/1234"
|
|
319
|
+
Uri uri = result[0].uri;
|
|
320
|
+
// Parse the rawId from this URI.
|
|
321
|
+
String rawId = getIdFromUri(uri);
|
|
322
|
+
if (rawId != null) {
|
|
323
|
+
// Get the contactId from the rawId and return it.
|
|
324
|
+
return getContactIdByRawId(rawId);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
} catch (Exception e) {
|
|
328
|
+
// @TODO: we might want to throw an error somehow
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
public boolean deleteContact(@NonNull String contactId) {
|
|
335
|
+
try {
|
|
336
|
+
// This will compose a URI, used for deleting that specific contact,
|
|
337
|
+
// e.g.: "content://com.android.contacts/contacts/1234"
|
|
338
|
+
Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, contactId);
|
|
339
|
+
ContentResolver cr = this.mActivity.getContentResolver();
|
|
340
|
+
int deleted = cr.delete(uri, null, null);
|
|
341
|
+
return deleted > 0;
|
|
342
|
+
} catch (Exception e) {
|
|
343
|
+
// Something went wrong
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
public static @Nullable String getIdFromUri(@Nullable Uri uri) {
|
|
349
|
+
if (uri != null) {
|
|
350
|
+
return uri.getLastPathSegment();
|
|
351
|
+
}
|
|
352
|
+
return null;
|
|
353
|
+
// If we want to support long, we can do something like this instead:
|
|
354
|
+
// long id = ContentUris.parseId(uri);
|
|
355
|
+
// if (id == -1) {
|
|
356
|
+
// return null;
|
|
357
|
+
// }
|
|
358
|
+
// return id;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private String getContactIdByRawId(@NonNull String contactRawId) {
|
|
362
|
+
String[] projection = new String[] { ContactsContract.RawContacts.CONTACT_ID };
|
|
363
|
+
|
|
364
|
+
String selection = ContactsContract.RawContacts._ID + "= ?";
|
|
365
|
+
String[] selectionArgs = new String[] { contactRawId };
|
|
366
|
+
|
|
367
|
+
ContentResolver cr = this.mActivity.getContentResolver();
|
|
368
|
+
Cursor cursor = cr.query(ContactsContract.RawContacts.CONTENT_URI, projection, selection, selectionArgs, null);
|
|
369
|
+
|
|
370
|
+
String contactId = null;
|
|
371
|
+
|
|
372
|
+
if (cursor.moveToNext()) {
|
|
373
|
+
int index = cursor.getColumnIndex(ContactsContract.RawContacts.CONTACT_ID);
|
|
374
|
+
|
|
375
|
+
if (index >= 0) {
|
|
376
|
+
contactId = cursor.getString(index);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
cursor.close();
|
|
381
|
+
|
|
382
|
+
return contactId;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
package getcapacitor.community.contacts;
|
|
2
|
+
|
|
3
|
+
import android.Manifest;
|
|
4
|
+
import android.app.Activity;
|
|
5
|
+
import android.content.Intent;
|
|
6
|
+
import android.net.Uri;
|
|
7
|
+
import android.provider.ContactsContract;
|
|
8
|
+
import androidx.activity.result.ActivityResult;
|
|
9
|
+
import com.getcapacitor.JSArray;
|
|
10
|
+
import com.getcapacitor.JSObject;
|
|
11
|
+
import com.getcapacitor.Logger;
|
|
12
|
+
import com.getcapacitor.PermissionState;
|
|
13
|
+
import com.getcapacitor.Plugin;
|
|
14
|
+
import com.getcapacitor.PluginCall;
|
|
15
|
+
import com.getcapacitor.PluginMethod;
|
|
16
|
+
import com.getcapacitor.annotation.ActivityCallback;
|
|
17
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
18
|
+
import com.getcapacitor.annotation.Permission;
|
|
19
|
+
import com.getcapacitor.annotation.PermissionCallback;
|
|
20
|
+
import java.util.HashMap;
|
|
21
|
+
import java.util.Map;
|
|
22
|
+
import java.util.concurrent.ExecutorService;
|
|
23
|
+
import java.util.concurrent.Executors;
|
|
24
|
+
|
|
25
|
+
@CapacitorPlugin(
|
|
26
|
+
name = "Contacts",
|
|
27
|
+
permissions = { @Permission(strings = { Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS }, alias = "contacts") }
|
|
28
|
+
)
|
|
29
|
+
public class ContactsPlugin extends Plugin {
|
|
30
|
+
|
|
31
|
+
public static final String TAG = "Contacts";
|
|
32
|
+
|
|
33
|
+
private Contacts implementation;
|
|
34
|
+
|
|
35
|
+
@Override
|
|
36
|
+
public void load() {
|
|
37
|
+
implementation = new Contacts(getActivity());
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private void requestContactsPermission(PluginCall call) {
|
|
41
|
+
requestPermissionForAlias("contacts", call, "permissionCallback");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Checks the the given permission is granted or not
|
|
46
|
+
* @return Returns true if the permission is granted and false if it is denied.
|
|
47
|
+
*/
|
|
48
|
+
private boolean isContactsPermissionGranted() {
|
|
49
|
+
return getPermissionState("contacts") == PermissionState.GRANTED;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@PermissionCallback
|
|
53
|
+
private void permissionCallback(PluginCall call) {
|
|
54
|
+
if (!isContactsPermissionGranted()) {
|
|
55
|
+
call.reject("Permission is required to access contacts.");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
switch (call.getMethodName()) {
|
|
60
|
+
case "getContact":
|
|
61
|
+
getContact(call);
|
|
62
|
+
break;
|
|
63
|
+
case "getContacts":
|
|
64
|
+
getContacts(call);
|
|
65
|
+
break;
|
|
66
|
+
case "createContact":
|
|
67
|
+
createContact(call);
|
|
68
|
+
break;
|
|
69
|
+
case "deleteContact":
|
|
70
|
+
deleteContact(call);
|
|
71
|
+
break;
|
|
72
|
+
case "pickContact":
|
|
73
|
+
pickContact(call);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@PluginMethod
|
|
79
|
+
public void getContact(PluginCall call) {
|
|
80
|
+
try {
|
|
81
|
+
if (!isContactsPermissionGranted()) {
|
|
82
|
+
requestContactsPermission(call);
|
|
83
|
+
} else {
|
|
84
|
+
String contactId = call.getString("contactId");
|
|
85
|
+
|
|
86
|
+
if (contactId == null) {
|
|
87
|
+
call.reject("Parameter `contactId` not provided.");
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
GetContactsProjectionInput projectionInput = new GetContactsProjectionInput(call.getObject("projection"));
|
|
92
|
+
|
|
93
|
+
ContactPayload contact = implementation.getContact(contactId, projectionInput);
|
|
94
|
+
|
|
95
|
+
if (contact == null) {
|
|
96
|
+
call.reject("Contact not found.");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
JSObject result = new JSObject();
|
|
101
|
+
result.put("contact", contact.getJSObject());
|
|
102
|
+
call.resolve(result);
|
|
103
|
+
}
|
|
104
|
+
} catch (Exception exception) {
|
|
105
|
+
rejectCall(call, exception);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@PluginMethod
|
|
110
|
+
public void getContacts(PluginCall call) {
|
|
111
|
+
try {
|
|
112
|
+
if (!isContactsPermissionGranted()) {
|
|
113
|
+
requestContactsPermission(call);
|
|
114
|
+
} else {
|
|
115
|
+
ExecutorService executor = Executors.newSingleThreadExecutor();
|
|
116
|
+
|
|
117
|
+
executor.execute(
|
|
118
|
+
new Runnable() {
|
|
119
|
+
@Override
|
|
120
|
+
public void run() {
|
|
121
|
+
try {
|
|
122
|
+
HashMap<String, ContactPayload> contacts = implementation.getContacts(
|
|
123
|
+
new GetContactsProjectionInput(call.getObject("projection"))
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
JSArray contactsJSArray = new JSArray();
|
|
127
|
+
for (Map.Entry<String, ContactPayload> entry : contacts.entrySet()) {
|
|
128
|
+
ContactPayload value = entry.getValue();
|
|
129
|
+
contactsJSArray.put(value.getJSObject());
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
JSObject result = new JSObject();
|
|
133
|
+
result.put("contacts", contactsJSArray);
|
|
134
|
+
|
|
135
|
+
bridge
|
|
136
|
+
.getActivity()
|
|
137
|
+
.runOnUiThread(
|
|
138
|
+
new Runnable() {
|
|
139
|
+
@Override
|
|
140
|
+
public void run() {
|
|
141
|
+
call.resolve(result);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
} catch (Exception exception) {
|
|
146
|
+
rejectCall(call, exception);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
executor.shutdown();
|
|
153
|
+
}
|
|
154
|
+
} catch (Exception exception) {
|
|
155
|
+
rejectCall(call, exception);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@PluginMethod
|
|
160
|
+
public void createContact(PluginCall call) {
|
|
161
|
+
try {
|
|
162
|
+
if (!isContactsPermissionGranted()) {
|
|
163
|
+
requestContactsPermission(call);
|
|
164
|
+
} else {
|
|
165
|
+
String contactId = implementation.createContact(new CreateContactInput(call.getObject("contact")));
|
|
166
|
+
|
|
167
|
+
if (contactId == null) {
|
|
168
|
+
call.reject("Something went wrong.");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
JSObject result = new JSObject();
|
|
173
|
+
result.put("contactId", contactId);
|
|
174
|
+
|
|
175
|
+
call.resolve(result);
|
|
176
|
+
}
|
|
177
|
+
} catch (Exception exception) {
|
|
178
|
+
rejectCall(call, exception);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@PluginMethod
|
|
183
|
+
public void deleteContact(PluginCall call) {
|
|
184
|
+
try {
|
|
185
|
+
if (!isContactsPermissionGranted()) {
|
|
186
|
+
requestContactsPermission(call);
|
|
187
|
+
} else {
|
|
188
|
+
String contactId = call.getString("contactId");
|
|
189
|
+
|
|
190
|
+
if (contactId == null) {
|
|
191
|
+
call.reject("Parameter `contactId` not provided.");
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (!implementation.deleteContact(contactId)) {
|
|
196
|
+
call.reject("Something went wrong.");
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
call.resolve();
|
|
201
|
+
}
|
|
202
|
+
} catch (Exception exception) {
|
|
203
|
+
rejectCall(call, exception);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
@PluginMethod
|
|
208
|
+
public void pickContact(PluginCall call) {
|
|
209
|
+
try {
|
|
210
|
+
if (!isContactsPermissionGranted()) {
|
|
211
|
+
requestContactsPermission(call);
|
|
212
|
+
} else {
|
|
213
|
+
Intent contactPickerIntent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
|
|
214
|
+
startActivityForResult(call, contactPickerIntent, "pickContactResult");
|
|
215
|
+
}
|
|
216
|
+
} catch (Exception exception) {
|
|
217
|
+
rejectCall(call, exception);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
@ActivityCallback
|
|
222
|
+
private void pickContactResult(PluginCall call, ActivityResult activityResult) {
|
|
223
|
+
if (call != null && activityResult.getResultCode() == Activity.RESULT_OK && activityResult.getData() != null) {
|
|
224
|
+
// This will return a URI for retrieving the contact, e.g.: "content://com.android.contacts/contacts/1234"
|
|
225
|
+
Uri uri = activityResult.getData().getData();
|
|
226
|
+
// Parse the contactId from this URI.
|
|
227
|
+
String contactId = Contacts.getIdFromUri(uri);
|
|
228
|
+
|
|
229
|
+
if (contactId == null) {
|
|
230
|
+
call.reject("Parameter `contactId` not returned from pick. Please raise an issue in GitHub if this problem persists.");
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
GetContactsProjectionInput projectionInput = new GetContactsProjectionInput(call.getObject("projection"));
|
|
235
|
+
|
|
236
|
+
ContactPayload contact = implementation.getContact(contactId, projectionInput);
|
|
237
|
+
|
|
238
|
+
if (contact == null) {
|
|
239
|
+
call.reject("Contact not found.");
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
JSObject result = new JSObject();
|
|
244
|
+
result.put("contact", contact.getJSObject());
|
|
245
|
+
call.resolve(result);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private void rejectCall(PluginCall call, Exception exception) {
|
|
250
|
+
String message = exception.getMessage();
|
|
251
|
+
message = (message != null) ? message : "An error occurred.";
|
|
252
|
+
Logger.error(TAG, message, exception);
|
|
253
|
+
call.reject(message);
|
|
254
|
+
}
|
|
255
|
+
}
|