@elizaos/capacitor-contacts 1.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.
@@ -0,0 +1,27 @@
1
+ ext {
2
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3
+ }
4
+
5
+ apply plugin: 'com.android.library'
6
+ android {
7
+ namespace = "ai.eliza.plugins.contacts"
8
+ compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
9
+ defaultConfig {
10
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 23
11
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
12
+ }
13
+ compileOptions {
14
+ sourceCompatibility JavaVersion.VERSION_17
15
+ targetCompatibility JavaVersion.VERSION_17
16
+ }
17
+ }
18
+
19
+ repositories {
20
+ google()
21
+ maven { url = uri(rootProject.ext.mavenCentralMirrorUrl) }
22
+ mavenCentral()
23
+ }
24
+
25
+ dependencies {
26
+ implementation project(':capacitor-android')
27
+ }
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
3
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
4
+ <uses-permission android:name="android.permission.WRITE_CONTACTS" />
5
+ </manifest>
@@ -0,0 +1,409 @@
1
+ package ai.eliza.plugins.contacts
2
+
3
+ import android.Manifest
4
+ import android.content.ContentProviderOperation
5
+ import android.provider.ContactsContract
6
+ import com.getcapacitor.JSArray
7
+ import com.getcapacitor.JSObject
8
+ import com.getcapacitor.Plugin
9
+ import com.getcapacitor.PluginCall
10
+ import com.getcapacitor.PluginMethod
11
+ import com.getcapacitor.annotation.CapacitorPlugin
12
+
13
+ @CapacitorPlugin(name = "ElizaContacts")
14
+ class ContactsPlugin : Plugin() {
15
+ @PluginMethod
16
+ fun listContacts(call: PluginCall) {
17
+ if (!hasPermission(Manifest.permission.READ_CONTACTS)) {
18
+ call.reject("READ_CONTACTS permission is required")
19
+ return
20
+ }
21
+
22
+ val query = call.getString("query")?.trim()?.lowercase()
23
+ val limit = call.getInt("limit") ?: 100
24
+ if (limit <= 0 || limit > 500) {
25
+ call.reject("limit must be between 1 and 500")
26
+ return
27
+ }
28
+ val contacts = JSArray()
29
+ val projection = arrayOf(
30
+ ContactsContract.Contacts._ID,
31
+ ContactsContract.Contacts.LOOKUP_KEY,
32
+ ContactsContract.Contacts.DISPLAY_NAME_PRIMARY,
33
+ ContactsContract.Contacts.PHOTO_THUMBNAIL_URI,
34
+ ContactsContract.Contacts.HAS_PHONE_NUMBER,
35
+ ContactsContract.Contacts.STARRED
36
+ )
37
+ val cursor = context.contentResolver.query(
38
+ ContactsContract.Contacts.CONTENT_URI,
39
+ projection,
40
+ null,
41
+ null,
42
+ "${ContactsContract.Contacts.DISPLAY_NAME_PRIMARY} ASC"
43
+ )
44
+ if (cursor == null) {
45
+ call.reject("Contacts provider returned no cursor")
46
+ return
47
+ }
48
+ cursor.use {
49
+ val idCol = cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID)
50
+ val lookupCol = cursor.getColumnIndexOrThrow(ContactsContract.Contacts.LOOKUP_KEY)
51
+ val nameCol = cursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY)
52
+ val photoCol = cursor.getColumnIndexOrThrow(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI)
53
+ val phoneCol = cursor.getColumnIndexOrThrow(ContactsContract.Contacts.HAS_PHONE_NUMBER)
54
+ val starredCol = cursor.getColumnIndexOrThrow(ContactsContract.Contacts.STARRED)
55
+ var count = 0
56
+ while (cursor.moveToNext() && count < limit) {
57
+ val id = cursor.getString(idCol)
58
+ val displayName = cursor.getString(nameCol) ?: ""
59
+ val phoneNumbers = readPhoneNumbers(id, cursor.getInt(phoneCol) > 0)
60
+ val emailAddresses = readEmailAddresses(id)
61
+ if (!matchesQuery(query, displayName, phoneNumbers, emailAddresses)) continue
62
+ contacts.put(
63
+ contactJson(
64
+ id = id,
65
+ lookupKey = cursor.getString(lookupCol) ?: "",
66
+ displayName = displayName,
67
+ photoUri = cursor.getString(photoCol),
68
+ phoneNumbers = phoneNumbers,
69
+ emailAddresses = emailAddresses,
70
+ starred = cursor.getInt(starredCol) == 1
71
+ )
72
+ )
73
+ count += 1
74
+ }
75
+ }
76
+
77
+ val result = JSObject()
78
+ result.put("contacts", contacts)
79
+ call.resolve(result)
80
+ }
81
+
82
+ @PluginMethod
83
+ fun createContact(call: PluginCall) {
84
+ if (!hasPermission(Manifest.permission.WRITE_CONTACTS)) {
85
+ call.reject("WRITE_CONTACTS permission is required")
86
+ return
87
+ }
88
+ val displayName = call.getString("displayName")?.trim()
89
+ if (displayName.isNullOrEmpty()) {
90
+ call.reject("displayName is required")
91
+ return
92
+ }
93
+ val phoneNumbers = readStringList(call, "phoneNumbers", call.getString("phoneNumber"))
94
+ val emailAddresses = readStringList(call, "emailAddresses", call.getString("emailAddress"))
95
+ val contactId = insertContact(displayName, phoneNumbers, emailAddresses)
96
+ val result = JSObject()
97
+ result.put("id", contactId)
98
+ call.resolve(result)
99
+ }
100
+
101
+ @PluginMethod
102
+ fun importVCard(call: PluginCall) {
103
+ if (!hasPermission(Manifest.permission.WRITE_CONTACTS)) {
104
+ call.reject("WRITE_CONTACTS permission is required")
105
+ return
106
+ }
107
+ val vcardText = call.getString("vcardText")
108
+ if (vcardText.isNullOrBlank()) {
109
+ call.reject("vcardText is required")
110
+ return
111
+ }
112
+ val parsedContacts = parseVCards(vcardText)
113
+ if (parsedContacts.isEmpty()) {
114
+ call.reject("No importable contacts were found in the vCard data")
115
+ return
116
+ }
117
+
118
+ val imported = JSArray()
119
+ for (parsed in parsedContacts) {
120
+ val contactId = insertContact(parsed.displayName, parsed.phoneNumbers, parsed.emailAddresses)
121
+ val summary = readContactSummary(contactId)
122
+ summary.put("sourceName", parsed.displayName)
123
+ imported.put(summary)
124
+ }
125
+ val result = JSObject()
126
+ result.put("imported", imported)
127
+ call.resolve(result)
128
+ }
129
+
130
+ private fun insertContact(
131
+ displayName: String,
132
+ phoneNumbers: List<String>,
133
+ emailAddresses: List<String>
134
+ ): String {
135
+ val operations = ArrayList<ContentProviderOperation>()
136
+ operations.add(
137
+ ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
138
+ .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
139
+ .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null)
140
+ .build()
141
+ )
142
+ operations.add(
143
+ ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
144
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
145
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
146
+ .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName)
147
+ .build()
148
+ )
149
+ for (phoneNumber in phoneNumbers) {
150
+ operations.add(
151
+ ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
152
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
153
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
154
+ .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneNumber)
155
+ .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
156
+ .build()
157
+ )
158
+ }
159
+ for (emailAddress in emailAddresses) {
160
+ operations.add(
161
+ ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
162
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
163
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
164
+ .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, emailAddress)
165
+ .withValue(ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.TYPE_OTHER)
166
+ .build()
167
+ )
168
+ }
169
+ val results = context.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
170
+ val rawContactId = results.firstOrNull()?.uri?.lastPathSegment
171
+ if (rawContactId.isNullOrEmpty()) {
172
+ throw IllegalStateException("Contacts provider did not return a raw contact id")
173
+ }
174
+ return resolveContactId(rawContactId)
175
+ ?: throw IllegalStateException("Contacts provider did not link the inserted raw contact")
176
+ }
177
+
178
+ private fun resolveContactId(rawContactId: String): String? {
179
+ context.contentResolver.query(
180
+ ContactsContract.RawContacts.CONTENT_URI,
181
+ arrayOf(ContactsContract.RawContacts.CONTACT_ID),
182
+ "${ContactsContract.RawContacts._ID} = ?",
183
+ arrayOf(rawContactId),
184
+ null
185
+ )?.use { cursor ->
186
+ if (cursor.moveToFirst()) {
187
+ val contactIdCol = cursor.getColumnIndexOrThrow(ContactsContract.RawContacts.CONTACT_ID)
188
+ return cursor.getString(contactIdCol)
189
+ }
190
+ }
191
+ return null
192
+ }
193
+
194
+ private fun readContactSummary(contactId: String): JSObject {
195
+ val projection = arrayOf(
196
+ ContactsContract.Contacts._ID,
197
+ ContactsContract.Contacts.LOOKUP_KEY,
198
+ ContactsContract.Contacts.DISPLAY_NAME_PRIMARY,
199
+ ContactsContract.Contacts.PHOTO_THUMBNAIL_URI,
200
+ ContactsContract.Contacts.HAS_PHONE_NUMBER,
201
+ ContactsContract.Contacts.STARRED
202
+ )
203
+ val cursor = context.contentResolver.query(
204
+ ContactsContract.Contacts.CONTENT_URI,
205
+ projection,
206
+ "${ContactsContract.Contacts._ID} = ?",
207
+ arrayOf(contactId),
208
+ null
209
+ ) ?: throw IllegalStateException("Contacts provider returned no cursor for $contactId")
210
+ cursor.use {
211
+ if (!cursor.moveToFirst()) {
212
+ throw IllegalStateException("Inserted contact $contactId was not readable")
213
+ }
214
+ val idCol = cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID)
215
+ val lookupCol = cursor.getColumnIndexOrThrow(ContactsContract.Contacts.LOOKUP_KEY)
216
+ val nameCol = cursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY)
217
+ val photoCol = cursor.getColumnIndexOrThrow(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI)
218
+ val phoneCol = cursor.getColumnIndexOrThrow(ContactsContract.Contacts.HAS_PHONE_NUMBER)
219
+ val starredCol = cursor.getColumnIndexOrThrow(ContactsContract.Contacts.STARRED)
220
+ val id = cursor.getString(idCol)
221
+ return contactJson(
222
+ id = id,
223
+ lookupKey = cursor.getString(lookupCol) ?: "",
224
+ displayName = cursor.getString(nameCol) ?: "",
225
+ photoUri = cursor.getString(photoCol),
226
+ phoneNumbers = readPhoneNumbers(id, cursor.getInt(phoneCol) > 0),
227
+ emailAddresses = readEmailAddresses(id),
228
+ starred = cursor.getInt(starredCol) == 1
229
+ )
230
+ }
231
+ }
232
+
233
+ private fun readPhoneNumbers(contactId: String, hasPhone: Boolean): List<String> {
234
+ if (!hasPhone) return emptyList()
235
+ val numbers = mutableListOf<String>()
236
+ val cursor = context.contentResolver.query(
237
+ ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
238
+ arrayOf(ContactsContract.CommonDataKinds.Phone.NUMBER),
239
+ "${ContactsContract.CommonDataKinds.Phone.CONTACT_ID} = ?",
240
+ arrayOf(contactId),
241
+ null
242
+ ) ?: return numbers
243
+ cursor.use {
244
+ val numberCol = cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER)
245
+ while (cursor.moveToNext()) {
246
+ val number = cursor.getString(numberCol)?.trim()
247
+ if (!number.isNullOrEmpty()) numbers.add(number)
248
+ }
249
+ }
250
+ return numbers.distinct()
251
+ }
252
+
253
+ private fun readEmailAddresses(contactId: String): List<String> {
254
+ val emails = mutableListOf<String>()
255
+ val cursor = context.contentResolver.query(
256
+ ContactsContract.CommonDataKinds.Email.CONTENT_URI,
257
+ arrayOf(ContactsContract.CommonDataKinds.Email.ADDRESS),
258
+ "${ContactsContract.CommonDataKinds.Email.CONTACT_ID} = ?",
259
+ arrayOf(contactId),
260
+ null
261
+ ) ?: return emails
262
+ cursor.use {
263
+ val emailCol = cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Email.ADDRESS)
264
+ while (cursor.moveToNext()) {
265
+ val email = cursor.getString(emailCol)?.trim()
266
+ if (!email.isNullOrEmpty()) emails.add(email)
267
+ }
268
+ }
269
+ return emails.distinct()
270
+ }
271
+
272
+ private fun contactJson(
273
+ id: String,
274
+ lookupKey: String,
275
+ displayName: String,
276
+ photoUri: String?,
277
+ phoneNumbers: List<String>,
278
+ emailAddresses: List<String>,
279
+ starred: Boolean
280
+ ): JSObject {
281
+ val contact = JSObject()
282
+ contact.put("id", id)
283
+ contact.put("lookupKey", lookupKey)
284
+ contact.put("displayName", displayName)
285
+ contact.put("photoUri", photoUri)
286
+ contact.put("phoneNumbers", JSArray(phoneNumbers))
287
+ contact.put("emailAddresses", JSArray(emailAddresses))
288
+ contact.put("starred", starred)
289
+ return contact
290
+ }
291
+
292
+ private fun matchesQuery(
293
+ query: String?,
294
+ displayName: String,
295
+ phoneNumbers: List<String>,
296
+ emailAddresses: List<String>
297
+ ): Boolean {
298
+ if (query.isNullOrEmpty()) return true
299
+ if (displayName.lowercase().contains(query)) return true
300
+ if (phoneNumbers.any { it.lowercase().contains(query) }) return true
301
+ return emailAddresses.any { it.lowercase().contains(query) }
302
+ }
303
+
304
+ private fun readStringList(call: PluginCall, arrayKey: String, singleValue: String?): List<String> {
305
+ val values = mutableListOf<String>()
306
+ val array = call.getArray(arrayKey)
307
+ if (array != null) {
308
+ for (index in 0 until array.length()) {
309
+ val value = array.optString(index).trim()
310
+ if (value.isNotEmpty()) values.add(value)
311
+ }
312
+ }
313
+ val single = singleValue?.trim()
314
+ if (!single.isNullOrEmpty()) values.add(single)
315
+ return values.distinct()
316
+ }
317
+
318
+ private fun parseVCards(input: String): List<ParsedVCard> {
319
+ val unfolded = unfoldVCardLines(input)
320
+ val contacts = mutableListOf<ParsedVCard>()
321
+ var current = mutableListOf<String>()
322
+ var insideCard = false
323
+ for (line in unfolded) {
324
+ val upper = line.uppercase()
325
+ if (upper == "BEGIN:VCARD") {
326
+ insideCard = true
327
+ current = mutableListOf()
328
+ } else if (upper == "END:VCARD") {
329
+ if (insideCard) {
330
+ parseVCard(current)?.let { contacts.add(it) }
331
+ }
332
+ insideCard = false
333
+ current = mutableListOf()
334
+ } else if (insideCard) {
335
+ current.add(line)
336
+ }
337
+ }
338
+ if (contacts.isEmpty()) {
339
+ parseVCard(unfolded)?.let { contacts.add(it) }
340
+ }
341
+ return contacts
342
+ }
343
+
344
+ private fun unfoldVCardLines(input: String): List<String> {
345
+ val lines = mutableListOf<String>()
346
+ for (rawLine in input.replace("\r\n", "\n").replace('\r', '\n').split('\n')) {
347
+ if ((rawLine.startsWith(" ") || rawLine.startsWith("\t")) && lines.isNotEmpty()) {
348
+ lines[lines.lastIndex] = lines.last() + rawLine.drop(1)
349
+ } else {
350
+ lines.add(rawLine.trimEnd())
351
+ }
352
+ }
353
+ return lines
354
+ }
355
+
356
+ private fun parseVCard(lines: List<String>): ParsedVCard? {
357
+ var fullName: String? = null
358
+ var structuredName: String? = null
359
+ val phoneNumbers = mutableListOf<String>()
360
+ val emailAddresses = mutableListOf<String>()
361
+ for (line in lines) {
362
+ val separator = line.indexOf(':')
363
+ if (separator <= 0) continue
364
+ val key = line.substring(0, separator).substringBefore(';').uppercase()
365
+ val value = decodeVCardValue(line.substring(separator + 1)).trim()
366
+ if (value.isEmpty()) continue
367
+ when (key) {
368
+ "FN" -> fullName = value
369
+ "N" -> structuredName = structuredNameToDisplayName(value)
370
+ "TEL" -> phoneNumbers.add(value)
371
+ "EMAIL" -> emailAddresses.add(value)
372
+ }
373
+ }
374
+ val displayName = fullName ?: structuredName ?: phoneNumbers.firstOrNull() ?: emailAddresses.firstOrNull()
375
+ if (displayName.isNullOrBlank()) return null
376
+ return ParsedVCard(
377
+ displayName = displayName,
378
+ phoneNumbers = phoneNumbers.map { it.trim() }.filter { it.isNotEmpty() }.distinct(),
379
+ emailAddresses = emailAddresses.map { it.trim() }.filter { it.isNotEmpty() }.distinct()
380
+ )
381
+ }
382
+
383
+ private fun structuredNameToDisplayName(value: String): String {
384
+ val parts = value.split(';').map { decodeVCardValue(it).trim() }
385
+ val family = parts.getOrNull(0).orEmpty()
386
+ val given = parts.getOrNull(1).orEmpty()
387
+ val additional = parts.getOrNull(2).orEmpty()
388
+ val prefix = parts.getOrNull(3).orEmpty()
389
+ val suffix = parts.getOrNull(4).orEmpty()
390
+ return listOf(prefix, given, additional, family, suffix)
391
+ .filter { it.isNotEmpty() }
392
+ .joinToString(" ")
393
+ }
394
+
395
+ private fun decodeVCardValue(value: String): String {
396
+ return value
397
+ .replace("\\n", "\n")
398
+ .replace("\\N", "\n")
399
+ .replace("\\,", ",")
400
+ .replace("\\;", ";")
401
+ .replace("\\\\", "\\")
402
+ }
403
+
404
+ private data class ParsedVCard(
405
+ val displayName: String,
406
+ val phoneNumbers: List<String>,
407
+ val emailAddresses: List<String>
408
+ )
409
+ }
@@ -0,0 +1,38 @@
1
+ export interface ContactSummary {
2
+ id: string;
3
+ lookupKey: string;
4
+ displayName: string;
5
+ phoneNumbers: string[];
6
+ emailAddresses: string[];
7
+ photoUri?: string;
8
+ starred: boolean;
9
+ }
10
+ export interface ListContactsOptions {
11
+ query?: string;
12
+ limit?: number;
13
+ }
14
+ export interface CreateContactOptions {
15
+ displayName: string;
16
+ phoneNumber?: string;
17
+ phoneNumbers?: string[];
18
+ emailAddress?: string;
19
+ emailAddresses?: string[];
20
+ }
21
+ export interface ImportVCardOptions {
22
+ vcardText: string;
23
+ }
24
+ export interface ImportedContactSummary extends ContactSummary {
25
+ sourceName: string;
26
+ }
27
+ export interface ContactsPlugin {
28
+ listContacts(options?: ListContactsOptions): Promise<{
29
+ contacts: ContactSummary[];
30
+ }>;
31
+ createContact(options: CreateContactOptions): Promise<{
32
+ id: string;
33
+ }>;
34
+ importVCard(options: ImportVCardOptions): Promise<{
35
+ imported: ImportedContactSummary[];
36
+ }>;
37
+ }
38
+ //# sourceMappingURL=definitions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,sBAAuB,SAAQ,cAAc;IAC5D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,CACV,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC;QAAE,QAAQ,EAAE,cAAc,EAAE,CAAA;KAAE,CAAC,CAAC;IAC3C,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC;QAChD,QAAQ,EAAE,sBAAsB,EAAE,CAAC;KACpC,CAAC,CAAC;CACJ"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import type { ContactsPlugin } from "./definitions";
2
+ export * from "./definitions";
3
+ export declare const Contacts: ContactsPlugin;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,cAAc,eAAe,CAAC;AAI9B,eAAO,MAAM,QAAQ,gBAEnB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { registerPlugin } from "@capacitor/core";
2
+ export * from "./definitions";
3
+ const loadWeb = () => import("./web").then((m) => new m.ContactsWeb());
4
+ export const Contacts = registerPlugin("ElizaContacts", {
5
+ web: loadWeb,
6
+ });
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,cAAc,eAAe,CAAC;AAE9B,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAEvE,MAAM,CAAC,MAAM,QAAQ,GAAG,cAAc,CAAiB,eAAe,EAAE;IACtE,GAAG,EAAE,OAAO;CACb,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { WebPlugin } from "@capacitor/core";
2
+ import type { ContactSummary, ContactsPlugin, CreateContactOptions, ImportedContactSummary, ImportVCardOptions, ListContactsOptions } from "./definitions";
3
+ export declare class ContactsWeb extends WebPlugin implements ContactsPlugin {
4
+ listContacts(_options?: ListContactsOptions): Promise<{
5
+ contacts: ContactSummary[];
6
+ }>;
7
+ createContact(_options: CreateContactOptions): Promise<{
8
+ id: string;
9
+ }>;
10
+ importVCard(_options: ImportVCardOptions): Promise<{
11
+ imported: ImportedContactSummary[];
12
+ }>;
13
+ }
14
+ //# sourceMappingURL=web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,eAAe,CAAC;AAEvB,qBAAa,WAAY,SAAQ,SAAU,YAAW,cAAc;IAC5D,YAAY,CAChB,QAAQ,CAAC,EAAE,mBAAmB,GAC7B,OAAO,CAAC;QAAE,QAAQ,EAAE,cAAc,EAAE,CAAA;KAAE,CAAC;IAIpC,aAAa,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAItE,WAAW,CACf,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAAC;QAAE,QAAQ,EAAE,sBAAsB,EAAE,CAAA;KAAE,CAAC;CAGnD"}
@@ -0,0 +1,13 @@
1
+ import { WebPlugin } from "@capacitor/core";
2
+ export class ContactsWeb extends WebPlugin {
3
+ async listContacts(_options) {
4
+ return { contacts: [] };
5
+ }
6
+ async createContact(_options) {
7
+ throw new Error("Contacts are only available on Android.");
8
+ }
9
+ async importVCard(_options) {
10
+ throw new Error("Contact imports are only available on Android.");
11
+ }
12
+ }
13
+ //# sourceMappingURL=web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAW5C,MAAM,OAAO,WAAY,SAAQ,SAAS;IACxC,KAAK,CAAC,YAAY,CAChB,QAA8B;QAE9B,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,QAA8B;QAChD,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,WAAW,CACf,QAA4B;QAE5B,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;CACF"}
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ var core = require('@capacitor/core');
4
+
5
+ const loadWeb = () => Promise.resolve().then(function () { return web; }).then((m) => new m.ContactsWeb());
6
+ const Contacts = core.registerPlugin("ElizaContacts", {
7
+ web: loadWeb,
8
+ });
9
+
10
+ class ContactsWeb extends core.WebPlugin {
11
+ async listContacts(_options) {
12
+ return { contacts: [] };
13
+ }
14
+ async createContact(_options) {
15
+ throw new Error("Contacts are only available on Android.");
16
+ }
17
+ async importVCard(_options) {
18
+ throw new Error("Contact imports are only available on Android.");
19
+ }
20
+ }
21
+
22
+ var web = /*#__PURE__*/Object.freeze({
23
+ __proto__: null,
24
+ ContactsWeb: ContactsWeb
25
+ });
26
+
27
+ exports.Contacts = Contacts;
28
+ //# sourceMappingURL=plugin.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\nexport * from \"./definitions\";\nconst loadWeb = () => import(\"./web\").then((m) => new m.ContactsWeb());\nexport const Contacts = registerPlugin(\"ElizaContacts\", {\n web: loadWeb,\n});\n//# sourceMappingURL=index.js.map","import { WebPlugin } from \"@capacitor/core\";\nexport class ContactsWeb extends WebPlugin {\n async listContacts(_options) {\n return { contacts: [] };\n }\n async createContact(_options) {\n throw new Error(\"Contacts are only available on Android.\");\n }\n async importVCard(_options) {\n throw new Error(\"Contact imports are only available on Android.\");\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;AAEA,MAAM,OAAO,GAAG,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;AAC1D,MAAC,QAAQ,GAAGA,mBAAc,CAAC,eAAe,EAAE;AACxD,IAAI,GAAG,EAAE,OAAO;AAChB,CAAC;;ACJM,MAAM,WAAW,SAASC,cAAS,CAAC;AAC3C,IAAI,MAAM,YAAY,CAAC,QAAQ,EAAE;AACjC,QAAQ,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE;AAC/B,IAAI;AACJ,IAAI,MAAM,aAAa,CAAC,QAAQ,EAAE;AAClC,QAAQ,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC;AAClE,IAAI;AACJ,IAAI,MAAM,WAAW,CAAC,QAAQ,EAAE;AAChC,QAAQ,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC;AACzE,IAAI;AACJ;;;;;;;;;"}
package/dist/plugin.js ADDED
@@ -0,0 +1,31 @@
1
+ var capacitorContacts = (function (exports, core) {
2
+ 'use strict';
3
+
4
+ const loadWeb = () => Promise.resolve().then(function () { return web; }).then((m) => new m.ContactsWeb());
5
+ const Contacts = core.registerPlugin("ElizaContacts", {
6
+ web: loadWeb,
7
+ });
8
+
9
+ class ContactsWeb extends core.WebPlugin {
10
+ async listContacts(_options) {
11
+ return { contacts: [] };
12
+ }
13
+ async createContact(_options) {
14
+ throw new Error("Contacts are only available on Android.");
15
+ }
16
+ async importVCard(_options) {
17
+ throw new Error("Contact imports are only available on Android.");
18
+ }
19
+ }
20
+
21
+ var web = /*#__PURE__*/Object.freeze({
22
+ __proto__: null,
23
+ ContactsWeb: ContactsWeb
24
+ });
25
+
26
+ exports.Contacts = Contacts;
27
+
28
+ return exports;
29
+
30
+ })({}, capacitorExports);
31
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\nexport * from \"./definitions\";\nconst loadWeb = () => import(\"./web\").then((m) => new m.ContactsWeb());\nexport const Contacts = registerPlugin(\"ElizaContacts\", {\n web: loadWeb,\n});\n//# sourceMappingURL=index.js.map","import { WebPlugin } from \"@capacitor/core\";\nexport class ContactsWeb extends WebPlugin {\n async listContacts(_options) {\n return { contacts: [] };\n }\n async createContact(_options) {\n throw new Error(\"Contacts are only available on Android.\");\n }\n async importVCard(_options) {\n throw new Error(\"Contact imports are only available on Android.\");\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;IAEA,MAAM,OAAO,GAAG,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;AAC1D,UAAC,QAAQ,GAAGA,mBAAc,CAAC,eAAe,EAAE;IACxD,IAAI,GAAG,EAAE,OAAO;IAChB,CAAC;;ICJM,MAAM,WAAW,SAASC,cAAS,CAAC;IAC3C,IAAI,MAAM,YAAY,CAAC,QAAQ,EAAE;IACjC,QAAQ,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE;IAC/B,IAAI;IACJ,IAAI,MAAM,aAAa,CAAC,QAAQ,EAAE;IAClC,QAAQ,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC;IAClE,IAAI;IACJ,IAAI,MAAM,WAAW,CAAC,QAAQ,EAAE;IAChC,QAAQ,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC;IACzE,IAAI;IACJ;;;;;;;;;;;;;;;"}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@elizaos/capacitor-contacts",
3
+ "version": "1.0.0",
4
+ "description": "Android ContactsContract bridge for ElizaOS.",
5
+ "main": "./dist/plugin.cjs.js",
6
+ "module": "./dist/esm/index.js",
7
+ "types": "./dist/esm/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/esm/index.d.ts",
11
+ "import": "./dist/esm/index.js",
12
+ "require": "./dist/plugin.cjs.js"
13
+ },
14
+ "./package.json": "./package.json"
15
+ },
16
+ "files": [
17
+ "android/src/main/",
18
+ "android/build.gradle",
19
+ "dist/"
20
+ ],
21
+ "scripts": {
22
+ "build": "npm run clean && tsc && rollup -c rollup.config.mjs",
23
+ "clean": "rimraf ./dist",
24
+ "prepublishOnly": "npm run build"
25
+ },
26
+ "license": "MIT",
27
+ "capacitor": {
28
+ "android": {
29
+ "src": "android"
30
+ }
31
+ },
32
+ "devDependencies": {
33
+ "@capacitor/cli": "^8.0.0",
34
+ "@capacitor/core": "^8.3.1",
35
+ "rimraf": "^6.0.0",
36
+ "rollup": "^4.60.2",
37
+ "typescript": "^6.0.0"
38
+ },
39
+ "peerDependencies": {
40
+ "@capacitor/core": "^8.3.1"
41
+ },
42
+ "elizaos": {
43
+ "platforms": [
44
+ "browser",
45
+ "node"
46
+ ],
47
+ "runtime": "both",
48
+ "platformDetails": {
49
+ "browser": "Web fallback returns an empty contacts list.",
50
+ "android": true
51
+ }
52
+ },
53
+ "publishConfig": {
54
+ "access": "public"
55
+ }
56
+ }