@aalzehla/capacitor-contacts 0.0.1
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/CapacitorContacts.podspec +17 -0
- package/Package.swift +28 -0
- package/README.md +67 -0
- package/android/build.gradle +58 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/aalzehla/capacitor/contacts/CapacitorContactsPlugin.java +177 -0
- package/android/src/main/java/com/aalzehla/capacitor/contacts/ContactDataExtractorVisitor.java +122 -0
- package/android/src/main/java/com/aalzehla/capacitor/contacts/ContactExtractorVisitor.java +31 -0
- package/android/src/main/java/com/aalzehla/capacitor/contacts/PluginContactFields.java +17 -0
- package/android/src/main/java/com/aalzehla/capacitor/contacts/contentQuery/ContentQuery.java +83 -0
- package/android/src/main/java/com/aalzehla/capacitor/contacts/contentQuery/ContentQueryService.java +60 -0
- package/android/src/main/java/com/aalzehla/capacitor/contacts/utils/Utils.java +19 -0
- package/android/src/main/java/com/aalzehla/capacitor/contacts/utils/Visitable.java +5 -0
- package/android/src/main/java/com/aalzehla/capacitor/contacts/utils/Visitor.java +5 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +145 -0
- package/dist/esm/definitions.d.ts +20 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +9 -0
- package/dist/esm/web.js +14 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +31 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +34 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/CapacitorContactsPlugin/CapacitorContactsPlugin.swift +166 -0
- package/ios/Tests/CapacitorContactsPluginTests/CapacitorContactsPluginTests.swift +15 -0
- package/package.json +80 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'CapacitorContacts'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.license = package['license']
|
|
10
|
+
s.homepage = package['repository']['url']
|
|
11
|
+
s.author = package['author']
|
|
12
|
+
s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
|
|
13
|
+
s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
|
|
14
|
+
s.ios.deployment_target = '13.0'
|
|
15
|
+
s.dependency 'Capacitor'
|
|
16
|
+
s.swift_version = '5.1'
|
|
17
|
+
end
|
package/Package.swift
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// swift-tools-version: 5.9
|
|
2
|
+
import PackageDescription
|
|
3
|
+
|
|
4
|
+
let package = Package(
|
|
5
|
+
name: "CapacitorContacts",
|
|
6
|
+
platforms: [.iOS(.v13)],
|
|
7
|
+
products: [
|
|
8
|
+
.library(
|
|
9
|
+
name: "CapacitorContacts",
|
|
10
|
+
targets: ["CapacitorContactsPlugin"])
|
|
11
|
+
],
|
|
12
|
+
dependencies: [
|
|
13
|
+
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", branch: "main")
|
|
14
|
+
],
|
|
15
|
+
targets: [
|
|
16
|
+
.target(
|
|
17
|
+
name: "CapacitorContactsPlugin",
|
|
18
|
+
dependencies: [
|
|
19
|
+
.product(name: "Capacitor", package: "capacitor-swift-pm"),
|
|
20
|
+
.product(name: "Cordova", package: "capacitor-swift-pm")
|
|
21
|
+
],
|
|
22
|
+
path: "ios/Sources/CapacitorContactsPlugin"),
|
|
23
|
+
.testTarget(
|
|
24
|
+
name: "CapacitorContactsPluginTests",
|
|
25
|
+
dependencies: ["CapacitorContactsPlugin"],
|
|
26
|
+
path: "ios/Tests/CapacitorContactsPluginTests")
|
|
27
|
+
]
|
|
28
|
+
)
|
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# capacitor-contacts
|
|
2
|
+
|
|
3
|
+
This capacitor plugin allows you to use the native contact picker UI on Android or iOS for single contact selection. Both platforms will return the same payload structure, where the data exists.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @aalzehla/capacitor-contacts
|
|
9
|
+
npx cap sync
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## API
|
|
13
|
+
|
|
14
|
+
<docgen-index>
|
|
15
|
+
|
|
16
|
+
* [`open()`](#open)
|
|
17
|
+
* [`close()`](#close)
|
|
18
|
+
* [Interfaces](#interfaces)
|
|
19
|
+
|
|
20
|
+
</docgen-index>
|
|
21
|
+
|
|
22
|
+
<docgen-api>
|
|
23
|
+
<!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
|
|
24
|
+
|
|
25
|
+
### open()
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
open() => Promise<Contact[]>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Returns:** <code>Promise<Contact[]></code>
|
|
32
|
+
|
|
33
|
+
--------------------
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### close()
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
close() => Promise<void>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
--------------------
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
### Interfaces
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
#### Contact
|
|
49
|
+
|
|
50
|
+
| Prop | Type |
|
|
51
|
+
| ----------------------------- | ------------------- |
|
|
52
|
+
| **`identifier`** | <code>string</code> |
|
|
53
|
+
| **`androidContactLookupKey`** | <code>string</code> |
|
|
54
|
+
| **`contactId`** | <code>string</code> |
|
|
55
|
+
| **`givenName`** | <code>string</code> |
|
|
56
|
+
| **`familyName`** | <code>string</code> |
|
|
57
|
+
| **`nickname`** | <code>string</code> |
|
|
58
|
+
| **`fullName`** | <code>string</code> |
|
|
59
|
+
| **`jobTitle`** | <code>string</code> |
|
|
60
|
+
| **`departmentName`** | <code>string</code> |
|
|
61
|
+
| **`organizationName`** | <code>string</code> |
|
|
62
|
+
| **`note`** | <code>string</code> |
|
|
63
|
+
| **`phoneNumbers`** | <code>any[]</code> |
|
|
64
|
+
| **`emailAddresses`** | <code>any[]</code> |
|
|
65
|
+
| **`postalAddresses`** | <code>any[]</code> |
|
|
66
|
+
|
|
67
|
+
</docgen-api>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
ext {
|
|
2
|
+
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
|
3
|
+
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.6.1'
|
|
4
|
+
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.5'
|
|
5
|
+
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.5.1'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
buildscript {
|
|
9
|
+
repositories {
|
|
10
|
+
google()
|
|
11
|
+
mavenCentral()
|
|
12
|
+
}
|
|
13
|
+
dependencies {
|
|
14
|
+
classpath 'com.android.tools.build:gradle:8.2.1'
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
apply plugin: 'com.android.library'
|
|
19
|
+
|
|
20
|
+
android {
|
|
21
|
+
namespace "com.aalzehla.capacitor.contacts"
|
|
22
|
+
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
|
|
23
|
+
defaultConfig {
|
|
24
|
+
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22
|
|
25
|
+
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
|
|
26
|
+
versionCode 1
|
|
27
|
+
versionName "1.0"
|
|
28
|
+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
29
|
+
}
|
|
30
|
+
buildTypes {
|
|
31
|
+
release {
|
|
32
|
+
minifyEnabled false
|
|
33
|
+
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
lintOptions {
|
|
37
|
+
abortOnError false
|
|
38
|
+
}
|
|
39
|
+
compileOptions {
|
|
40
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
41
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
repositories {
|
|
46
|
+
google()
|
|
47
|
+
mavenCentral()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
dependencies {
|
|
52
|
+
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
53
|
+
implementation project(':capacitor-android')
|
|
54
|
+
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
55
|
+
testImplementation "junit:junit:$junitVersion"
|
|
56
|
+
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
57
|
+
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
58
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
package com.aalzehla.capacitor.contacts;
|
|
2
|
+
|
|
3
|
+
import android.Manifest;
|
|
4
|
+
import android.content.Intent;
|
|
5
|
+
import android.provider.ContactsContract;
|
|
6
|
+
import android.provider.ContactsContract.CommonDataKinds;
|
|
7
|
+
import android.util.Log;
|
|
8
|
+
|
|
9
|
+
import androidx.activity.result.ActivityResult;
|
|
10
|
+
|
|
11
|
+
import com.getcapacitor.JSArray;
|
|
12
|
+
import com.getcapacitor.JSObject;
|
|
13
|
+
import com.getcapacitor.PermissionState;
|
|
14
|
+
import com.getcapacitor.Plugin;
|
|
15
|
+
import com.getcapacitor.PluginCall;
|
|
16
|
+
import com.getcapacitor.PluginMethod;
|
|
17
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
18
|
+
import com.getcapacitor.annotation.Permission;
|
|
19
|
+
import com.getcapacitor.annotation.PermissionCallback;
|
|
20
|
+
import com.getcapacitor.annotation.ActivityCallback;
|
|
21
|
+
|
|
22
|
+
import com.aalzehla.capacitor.contacts.contentQuery.ContentQuery;
|
|
23
|
+
import com.aalzehla.capacitor.contacts.contentQuery.ContentQueryService;
|
|
24
|
+
import com.aalzehla.capacitor.contacts.utils.Utils;
|
|
25
|
+
|
|
26
|
+
import java.io.IOException;
|
|
27
|
+
import java.util.HashMap;
|
|
28
|
+
import java.util.List;
|
|
29
|
+
import java.util.Map;
|
|
30
|
+
|
|
31
|
+
// import android.util.Log;
|
|
32
|
+
|
|
33
|
+
@CapacitorPlugin(
|
|
34
|
+
name = "CapacitorContacts",
|
|
35
|
+
permissions={
|
|
36
|
+
@Permission(strings = {Manifest.permission.READ_CONTACTS}, alias = "contacts"),
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
public class CapacitorContactsPlugin extends Plugin {
|
|
40
|
+
|
|
41
|
+
// Messages
|
|
42
|
+
public static final String ERROR_READ_CONTACT = "Unable to read contact data.";
|
|
43
|
+
public static final String ERROR_NO_PERMISSION = "User denied permission";
|
|
44
|
+
|
|
45
|
+
// Queries
|
|
46
|
+
public static final String CONTACT_DATA_SELECT_CLAUSE = ContactsContract.Data.LOOKUP_KEY + " = ? AND " + ContactsContract.Data.MIMETYPE + " IN('" + CommonDataKinds.Email.CONTENT_ITEM_TYPE + "', '" + CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "', '" + CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE + "')"; //
|
|
47
|
+
|
|
48
|
+
@PluginMethod()
|
|
49
|
+
public void open(PluginCall call) {
|
|
50
|
+
if (getPermissionState("contacts") != PermissionState.GRANTED) {
|
|
51
|
+
//saveCall(call);
|
|
52
|
+
call.setKeepAlive(true);
|
|
53
|
+
requestPermissionForAlias("contacts", call, "contactsPermsCallback");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
//saveCall(call);
|
|
57
|
+
call.setKeepAlive(true);
|
|
58
|
+
Intent contactPickerIntent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
|
|
59
|
+
startActivityForResult(call, contactPickerIntent, "activityCallback");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@PermissionCallback
|
|
63
|
+
private void contactsPermsCallback(PluginCall call) {
|
|
64
|
+
if (getPermissionState("contacts") == PermissionState.GRANTED) {
|
|
65
|
+
open(call);
|
|
66
|
+
} else {
|
|
67
|
+
call.reject(ERROR_NO_PERMISSION);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@ActivityCallback
|
|
72
|
+
private void activityCallback(PluginCall call, ActivityResult result) {
|
|
73
|
+
try {
|
|
74
|
+
//Log.v("File: ", String.valueOf(result));
|
|
75
|
+
JSObject contact = readContactData(result.getData(), call);
|
|
76
|
+
call.resolve(contact); // since it is just the contact object and not an array, no need to return the value field but instead just return the selected contact. use contact to replace Utils.wrapIntoResult(contact)
|
|
77
|
+
} catch (IOException e) {
|
|
78
|
+
call.reject(ERROR_READ_CONTACT);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@PluginMethod()
|
|
83
|
+
public void close(PluginCall call) {
|
|
84
|
+
call.unimplemented();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private JSObject readContactData(Intent intent, PluginCall savedCall) throws IOException {
|
|
88
|
+
final Map<String, String> projectionMap = getContactProjectionMap(); ////
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
ContentQuery contactQuery = new ContentQuery.Builder()
|
|
92
|
+
.withUri(intent.getData())
|
|
93
|
+
.withProjection(projectionMap)
|
|
94
|
+
.build();
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
try (ContentQueryService.VisitableCursorWrapper contactVcw = ContentQueryService.query(getContext(), contactQuery)) {
|
|
98
|
+
|
|
99
|
+
ContactExtractorVisitor contactExtractor = new ContactExtractorVisitor(projectionMap);
|
|
100
|
+
contactVcw.accept(contactExtractor);
|
|
101
|
+
List<JSObject> contacts = contactExtractor.getContacts();
|
|
102
|
+
|
|
103
|
+
if (contacts.size() == 0) {
|
|
104
|
+
return null;
|
|
105
|
+
} else {
|
|
106
|
+
JSObject chosenContact = contacts.get(0);
|
|
107
|
+
|
|
108
|
+
Map<String, String> dataProjectionMap = getContactDataProjectionMap(); ////////
|
|
109
|
+
ContentQuery contactDataQuery = new ContentQuery.Builder()
|
|
110
|
+
.withUri(ContactsContract.Data.CONTENT_URI)
|
|
111
|
+
.withProjection(dataProjectionMap)
|
|
112
|
+
.withSelection(CONTACT_DATA_SELECT_CLAUSE)
|
|
113
|
+
.withSelectionArgs(new String[]{chosenContact.getString(PluginContactFields.ANDROID_CONTACT_LOOKUP_KEY)})
|
|
114
|
+
.withSortOrder(ContactsContract.Data.MIMETYPE)
|
|
115
|
+
.build();
|
|
116
|
+
|
|
117
|
+
try (ContentQueryService.VisitableCursorWrapper dataVcw = ContentQueryService.query(getContext(), contactDataQuery)) {
|
|
118
|
+
|
|
119
|
+
ContactDataExtractorVisitor contactDataExtractor = new ContactDataExtractorVisitor(dataProjectionMap);
|
|
120
|
+
dataVcw.accept(contactDataExtractor);
|
|
121
|
+
|
|
122
|
+
return transformContactObject(chosenContact, contactDataExtractor.getEmailAddresses(), contactDataExtractor.getPhoneNumbers(), contactDataExtractor.getPostalAddresses());
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} catch (Exception e) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private JSObject transformContactObject(JSObject tempContact, JSArray emailAddresses, JSArray phoneNumbers, JSArray postalAddresses) {
|
|
133
|
+
JSObject contact = new JSObject();
|
|
134
|
+
contact.put(PluginContactFields.IDENTIFIER, tempContact.getString(PluginContactFields.IDENTIFIER));
|
|
135
|
+
contact.put(PluginContactFields.ANDROID_CONTACT_LOOKUP_KEY, tempContact.getString(PluginContactFields.ANDROID_CONTACT_LOOKUP_KEY));
|
|
136
|
+
String displayName = tempContact.getString(PluginContactFields.DISPLAY_NAME);
|
|
137
|
+
contact.put(PluginContactFields.FULL_NAME, displayName);
|
|
138
|
+
if (displayName != null && displayName.contains(" ")) {
|
|
139
|
+
contact.put(PluginContactFields.DISPLAY_NAME, displayName);
|
|
140
|
+
contact.put(PluginContactFields.GIVEN_NAME, displayName.split(" ")[0]);
|
|
141
|
+
contact.put(PluginContactFields.FAMILY_NAME, displayName.split(" ")[1]);
|
|
142
|
+
}
|
|
143
|
+
contact.put(PluginContactFields.EMAIL_ADDRESSES, emailAddresses);
|
|
144
|
+
contact.put(PluginContactFields.PHONE_NUMBERS, phoneNumbers);
|
|
145
|
+
contact.put(PluginContactFields.POSTAL_ADDRESSES, postalAddresses);
|
|
146
|
+
//contact.put(PluginContactFields.PHOTO_URI, tempContact.getString(PluginContactFields.PHOTO_URI));
|
|
147
|
+
return contact;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private Map<String, String> getContactProjectionMap() {
|
|
151
|
+
Map<String, String> contactFieldsMap = new HashMap<>();
|
|
152
|
+
contactFieldsMap.put(ContactsContract.Contacts._ID, PluginContactFields.IDENTIFIER);
|
|
153
|
+
contactFieldsMap.put(ContactsContract.Contacts.LOOKUP_KEY, PluginContactFields.ANDROID_CONTACT_LOOKUP_KEY);
|
|
154
|
+
contactFieldsMap.put(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY, PluginContactFields.DISPLAY_NAME);
|
|
155
|
+
|
|
156
|
+
contactFieldsMap.put(ContactsContract.Contacts.PHOTO_URI, PluginContactFields.PHOTO_URI);
|
|
157
|
+
|
|
158
|
+
// contactFieldsMap.put(ContactsContract.Contacts.Data.DATA15, PluginContactFields.PHOTO_URI);
|
|
159
|
+
return contactFieldsMap;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private Map<String, String> getContactDataProjectionMap() {
|
|
163
|
+
Map<String, String> contactFieldsMap = new HashMap<>();
|
|
164
|
+
contactFieldsMap.put(CommonDataKinds.Email.MIMETYPE, PluginContactFields.MIME_TYPE);
|
|
165
|
+
contactFieldsMap.put(ContactsContract.Data.DATA1, ContactsContract.Data.DATA1);
|
|
166
|
+
contactFieldsMap.put(ContactsContract.Data.DATA2, ContactsContract.Data.DATA2);
|
|
167
|
+
contactFieldsMap.put(ContactsContract.Data.DATA3, ContactsContract.Data.DATA3);
|
|
168
|
+
contactFieldsMap.put(ContactsContract.Data.DATA4, ContactsContract.Data.DATA4);
|
|
169
|
+
contactFieldsMap.put(ContactsContract.Data.DATA5, ContactsContract.Data.DATA5);
|
|
170
|
+
contactFieldsMap.put(ContactsContract.Data.DATA6, ContactsContract.Data.DATA6);
|
|
171
|
+
contactFieldsMap.put(ContactsContract.Data.DATA7, ContactsContract.Data.DATA7);
|
|
172
|
+
contactFieldsMap.put(ContactsContract.Data.DATA8, ContactsContract.Data.DATA8);
|
|
173
|
+
contactFieldsMap.put(ContactsContract.Data.DATA9, ContactsContract.Data.DATA9);
|
|
174
|
+
contactFieldsMap.put(ContactsContract.Data.DATA10, ContactsContract.Data.DATA10);
|
|
175
|
+
return contactFieldsMap;
|
|
176
|
+
}
|
|
177
|
+
}
|
package/android/src/main/java/com/aalzehla/capacitor/contacts/ContactDataExtractorVisitor.java
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
package com.aalzehla.capacitor.contacts;
|
|
2
|
+
|
|
3
|
+
import android.database.Cursor;
|
|
4
|
+
import android.provider.ContactsContract;
|
|
5
|
+
import com.getcapacitor.JSArray;
|
|
6
|
+
import com.getcapacitor.JSObject;
|
|
7
|
+
import com.aalzehla.capacitor.contacts.contentQuery.ContentQueryService;
|
|
8
|
+
import com.aalzehla.capacitor.contacts.utils.Visitor;
|
|
9
|
+
|
|
10
|
+
import java.util.Map;
|
|
11
|
+
import android.util.Log;
|
|
12
|
+
|
|
13
|
+
public class ContactDataExtractorVisitor implements Visitor<Cursor> {
|
|
14
|
+
|
|
15
|
+
private Map<String, String> projectionMap;
|
|
16
|
+
|
|
17
|
+
private JSArray phoneNumbers = new JSArray();
|
|
18
|
+
private JSArray emailAddresses = new JSArray();
|
|
19
|
+
private JSArray postalAddresses = new JSArray();
|
|
20
|
+
|
|
21
|
+
public ContactDataExtractorVisitor(Map<String, String> projectionMap) {
|
|
22
|
+
this.projectionMap = projectionMap;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@Override
|
|
26
|
+
public void visit(Cursor cursor) {
|
|
27
|
+
JSObject currentDataRecord = ContentQueryService.extractDataFromResultSet(cursor, projectionMap);
|
|
28
|
+
String currentMimeType = currentDataRecord.getString(PluginContactFields.MIME_TYPE);
|
|
29
|
+
|
|
30
|
+
//Log.v("HELLO", String.valueOf(currentDataRecord));
|
|
31
|
+
|
|
32
|
+
if (ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE.equals(currentMimeType)) {
|
|
33
|
+
JSObject email = new JSObject();
|
|
34
|
+
if (currentDataRecord.getString(ContactsContract.Contacts.Data.DATA1) != null) {
|
|
35
|
+
email.put("emailAddress", currentDataRecord.getString(ContactsContract.Contacts.Data.DATA1));
|
|
36
|
+
}
|
|
37
|
+
switch (currentDataRecord.getString(ContactsContract.Contacts.Data.DATA2)) {
|
|
38
|
+
case "1":
|
|
39
|
+
email.put("type", "home");
|
|
40
|
+
break;
|
|
41
|
+
case "2":
|
|
42
|
+
email.put("type", "mobile");
|
|
43
|
+
break;
|
|
44
|
+
case "3":
|
|
45
|
+
email.put("type", "work");
|
|
46
|
+
break;
|
|
47
|
+
default:
|
|
48
|
+
email.put("type", "other");
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
emailAddresses.put(email);
|
|
52
|
+
} else if (ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equals(currentMimeType)) {
|
|
53
|
+
JSObject phone = new JSObject();
|
|
54
|
+
// https://developer.android.com/reference/android/provider/ContactsContract.CommonDataKinds.Phone
|
|
55
|
+
if (currentDataRecord.getString(ContactsContract.Contacts.Data.DATA1) != null) {
|
|
56
|
+
phone.put("phoneNumber", currentDataRecord.getString(ContactsContract.Contacts.Data.DATA1));
|
|
57
|
+
}
|
|
58
|
+
switch (currentDataRecord.getString(ContactsContract.Contacts.Data.DATA2)) {
|
|
59
|
+
case "1":
|
|
60
|
+
phone.put("type", "home");
|
|
61
|
+
break;
|
|
62
|
+
case "2":
|
|
63
|
+
phone.put("type", "mobile");
|
|
64
|
+
break;
|
|
65
|
+
case "3":
|
|
66
|
+
phone.put("type", "work");
|
|
67
|
+
break;
|
|
68
|
+
default:
|
|
69
|
+
phone.put("type", "other");
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
phoneNumbers.put(phone);
|
|
73
|
+
} else if (ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE.equals(currentMimeType)) {
|
|
74
|
+
JSObject address = new JSObject();
|
|
75
|
+
// https://developer.android.com/reference/android/provider/ContactsContract.CommonDataKinds.StructuredPostal
|
|
76
|
+
if (currentDataRecord.getString(ContactsContract.Contacts.Data.DATA1) != null) {
|
|
77
|
+
address.put("formattedAddress", currentDataRecord.getString(ContactsContract.Contacts.Data.DATA1));
|
|
78
|
+
}
|
|
79
|
+
if (currentDataRecord.getString(ContactsContract.Contacts.Data.DATA4) != null) {
|
|
80
|
+
address.put("street", currentDataRecord.getString(ContactsContract.Contacts.Data.DATA4));
|
|
81
|
+
}
|
|
82
|
+
if (currentDataRecord.getString(ContactsContract.Contacts.Data.DATA5) != null) {
|
|
83
|
+
address.put("pobox", currentDataRecord.getString(ContactsContract.Contacts.Data.DATA5));
|
|
84
|
+
}
|
|
85
|
+
if (currentDataRecord.getString(ContactsContract.Contacts.Data.DATA6) != null) {
|
|
86
|
+
address.put("neighborhood", currentDataRecord.getString(ContactsContract.Contacts.Data.DATA6));
|
|
87
|
+
}
|
|
88
|
+
if (currentDataRecord.getString(ContactsContract.Contacts.Data.DATA7) != null) {
|
|
89
|
+
address.put("city", currentDataRecord.getString(ContactsContract.Contacts.Data.DATA7));
|
|
90
|
+
}
|
|
91
|
+
if (currentDataRecord.getString(ContactsContract.Contacts.Data.DATA8) != null) {
|
|
92
|
+
address.put("state", currentDataRecord.getString(ContactsContract.Contacts.Data.DATA8));
|
|
93
|
+
}
|
|
94
|
+
if (currentDataRecord.getString(ContactsContract.Contacts.Data.DATA9) != null) {
|
|
95
|
+
address.put("postalCode", currentDataRecord.getString(ContactsContract.Contacts.Data.DATA9));
|
|
96
|
+
}
|
|
97
|
+
if (currentDataRecord.getString(ContactsContract.Contacts.Data.DATA10) != null) {
|
|
98
|
+
address.put("country", currentDataRecord.getString(ContactsContract.Contacts.Data.DATA10));
|
|
99
|
+
}
|
|
100
|
+
switch (currentDataRecord.getString(ContactsContract.Contacts.Data.DATA2)) {
|
|
101
|
+
case "1":
|
|
102
|
+
address.put("type", "home");
|
|
103
|
+
break;
|
|
104
|
+
case "2":
|
|
105
|
+
address.put("type", "work");
|
|
106
|
+
break;
|
|
107
|
+
default:
|
|
108
|
+
address.put("type", "other");
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
postalAddresses.put(address);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
public JSArray getPhoneNumbers() {
|
|
116
|
+
return phoneNumbers;
|
|
117
|
+
}
|
|
118
|
+
public JSArray getEmailAddresses() {
|
|
119
|
+
return emailAddresses;
|
|
120
|
+
}
|
|
121
|
+
public JSArray getPostalAddresses() { return postalAddresses; }
|
|
122
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package com.aalzehla.capacitor.contacts;
|
|
2
|
+
|
|
3
|
+
import android.database.Cursor;
|
|
4
|
+
import com.getcapacitor.JSObject;
|
|
5
|
+
import com.aalzehla.capacitor.contacts.contentQuery.ContentQueryService;
|
|
6
|
+
import com.aalzehla.capacitor.contacts.utils.Visitor;
|
|
7
|
+
|
|
8
|
+
import java.util.ArrayList;
|
|
9
|
+
import java.util.List;
|
|
10
|
+
import java.util.Map;
|
|
11
|
+
|
|
12
|
+
public class ContactExtractorVisitor implements Visitor<Cursor> {
|
|
13
|
+
|
|
14
|
+
private Map<String, String> projectionMap;
|
|
15
|
+
|
|
16
|
+
private List<JSObject> contacts = new ArrayList<>();
|
|
17
|
+
|
|
18
|
+
public ContactExtractorVisitor(Map<String, String> projectionMap) {
|
|
19
|
+
this.projectionMap = projectionMap;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@Override
|
|
23
|
+
public void visit(Cursor cursor) {
|
|
24
|
+
JSObject contact = ContentQueryService.extractDataFromResultSet(cursor, projectionMap);
|
|
25
|
+
contacts.add(contact);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public List<JSObject> getContacts() {
|
|
29
|
+
return contacts;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
package com.aalzehla.capacitor.contacts;
|
|
2
|
+
|
|
3
|
+
public class PluginContactFields {
|
|
4
|
+
public static final String IDENTIFIER = "identifier";
|
|
5
|
+
public static final String ANDROID_CONTACT_LOOKUP_KEY = "androidContactLookupKey";
|
|
6
|
+
public static final String DISPLAY_NAME = "displayName";
|
|
7
|
+
public static final String FULL_NAME = "fullName";
|
|
8
|
+
public static final String GIVEN_NAME = "givenName";
|
|
9
|
+
public static final String FAMILY_NAME = "familyName";
|
|
10
|
+
public static final String EMAIL_ADDRESSES = "emailAddresses";
|
|
11
|
+
public static final String PHONE_NUMBERS = "phoneNumbers";
|
|
12
|
+
public static final String PHONE_TYPES = "phoneNumberLabels";
|
|
13
|
+
public static final String POSTAL_ADDRESSES = "postalAddresses";
|
|
14
|
+
public static final String POSTAL_TYPES = "postalAddressLabels";
|
|
15
|
+
public static final String MIME_TYPE = "mimeType";
|
|
16
|
+
public static final String PHOTO_URI = "image";
|
|
17
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
package com.aalzehla.capacitor.contacts.contentQuery;
|
|
2
|
+
|
|
3
|
+
import android.net.Uri;
|
|
4
|
+
import android.os.CancellationSignal;
|
|
5
|
+
|
|
6
|
+
import java.util.Map;
|
|
7
|
+
|
|
8
|
+
public class ContentQuery {
|
|
9
|
+
|
|
10
|
+
private Uri uri;
|
|
11
|
+
private Map<String, String> projection;
|
|
12
|
+
private String selection;
|
|
13
|
+
private String[] selectionArgs;
|
|
14
|
+
private String sortOrder;
|
|
15
|
+
private CancellationSignal cancellationSignal;
|
|
16
|
+
|
|
17
|
+
private ContentQuery() {
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public Uri getUri() {
|
|
21
|
+
return uri;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public Map<String, String> getProjection() {
|
|
25
|
+
return projection;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public String getSelection() {
|
|
29
|
+
return selection;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public String[] getSelectionArgs() {
|
|
33
|
+
return selectionArgs;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public String getSortOrder() {
|
|
37
|
+
return sortOrder;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public CancellationSignal getCancellationSignal() {
|
|
41
|
+
return cancellationSignal;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public static class Builder {
|
|
45
|
+
|
|
46
|
+
private ContentQuery contentQuery = new ContentQuery();
|
|
47
|
+
|
|
48
|
+
public Builder withUri(Uri uri) {
|
|
49
|
+
contentQuery.uri = uri;
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public Builder withProjection(Map<String, String> projection) {
|
|
54
|
+
contentQuery.projection = projection;
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public Builder withSelection(String selection) {
|
|
59
|
+
contentQuery.selection = selection;
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public Builder withSelectionArgs(String[] selectionArgs) {
|
|
64
|
+
contentQuery.selectionArgs = selectionArgs;
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public Builder withSortOrder(String sortOrder) {
|
|
69
|
+
contentQuery.sortOrder = sortOrder;
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public Builder withCancellationSignal(CancellationSignal cancellationSignal) {
|
|
74
|
+
contentQuery.cancellationSignal = cancellationSignal;
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public ContentQuery build() {
|
|
79
|
+
return contentQuery;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
}
|
package/android/src/main/java/com/aalzehla/capacitor/contacts/contentQuery/ContentQueryService.java
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
package com.aalzehla.capacitor.contacts.contentQuery;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.database.Cursor;
|
|
5
|
+
import com.getcapacitor.JSObject;
|
|
6
|
+
import com.aalzehla.capacitor.contacts.utils.Utils;
|
|
7
|
+
import com.aalzehla.capacitor.contacts.utils.Visitable;
|
|
8
|
+
import com.aalzehla.capacitor.contacts.utils.Visitor;
|
|
9
|
+
|
|
10
|
+
import java.io.Closeable;
|
|
11
|
+
import java.util.Map;
|
|
12
|
+
|
|
13
|
+
public class ContentQueryService {
|
|
14
|
+
|
|
15
|
+
public static VisitableCursorWrapper query(Context context, ContentQuery query) {
|
|
16
|
+
try {
|
|
17
|
+
String[] projectionArray = Utils.getMapKeysAsArray(query.getProjection());
|
|
18
|
+
Cursor cursor = context.getContentResolver().query(query.getUri(), projectionArray, query.getSelection(), query.getSelectionArgs(), query.getSortOrder(), query.getCancellationSignal());
|
|
19
|
+
return new VisitableCursorWrapper(cursor);
|
|
20
|
+
} catch (Exception e) {
|
|
21
|
+
throw new RuntimeException(e);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public static JSObject extractDataFromResultSet(Cursor cursor, Map<String, String> projectionMap) {
|
|
26
|
+
try {
|
|
27
|
+
JSObject result = new JSObject();
|
|
28
|
+
for (Map.Entry<String, String> entry : projectionMap.entrySet()) {
|
|
29
|
+
int columnIndex = cursor.getColumnIndex(entry.getKey());
|
|
30
|
+
result.put(entry.getValue(), cursor.getString(columnIndex));
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
} catch (Exception e) {
|
|
34
|
+
throw new RuntimeException(e);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public static class VisitableCursorWrapper implements Visitable<Cursor>, Closeable, AutoCloseable {
|
|
39
|
+
|
|
40
|
+
private Cursor cursor;
|
|
41
|
+
|
|
42
|
+
private VisitableCursorWrapper(Cursor cursor) {
|
|
43
|
+
this.cursor = cursor;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public void accept(Visitor<Cursor> visitor) {
|
|
47
|
+
while (cursor != null && cursor.moveToNext()) {
|
|
48
|
+
visitor.visit(cursor);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@Override
|
|
53
|
+
public void close() {
|
|
54
|
+
if (cursor != null) {
|
|
55
|
+
cursor.close();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
package com.aalzehla.capacitor.contacts.utils;
|
|
2
|
+
|
|
3
|
+
import com.getcapacitor.JSObject;
|
|
4
|
+
|
|
5
|
+
import java.util.Map;
|
|
6
|
+
|
|
7
|
+
public class Utils {
|
|
8
|
+
|
|
9
|
+
public static String[] getMapKeysAsArray(Map<String, ?> map) {
|
|
10
|
+
return map.keySet().toArray(new String[]{});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public static JSObject wrapIntoResult(JSObject contact) {
|
|
14
|
+
JSObject result = new JSObject();
|
|
15
|
+
result.put("value", contact);
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
}
|
|
File without changes
|
package/dist/docs.json
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
{
|
|
2
|
+
"api": {
|
|
3
|
+
"name": "CapacitorContactsPlugin",
|
|
4
|
+
"slug": "capacitorcontactsplugin",
|
|
5
|
+
"docs": "",
|
|
6
|
+
"tags": [],
|
|
7
|
+
"methods": [
|
|
8
|
+
{
|
|
9
|
+
"name": "open",
|
|
10
|
+
"signature": "() => Promise<Contact[]>",
|
|
11
|
+
"parameters": [],
|
|
12
|
+
"returns": "Promise<Contact[]>",
|
|
13
|
+
"tags": [],
|
|
14
|
+
"docs": "",
|
|
15
|
+
"complexTypes": [
|
|
16
|
+
"Contact"
|
|
17
|
+
],
|
|
18
|
+
"slug": "open"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "close",
|
|
22
|
+
"signature": "() => Promise<void>",
|
|
23
|
+
"parameters": [],
|
|
24
|
+
"returns": "Promise<void>",
|
|
25
|
+
"tags": [],
|
|
26
|
+
"docs": "",
|
|
27
|
+
"complexTypes": [],
|
|
28
|
+
"slug": "close"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"properties": []
|
|
32
|
+
},
|
|
33
|
+
"interfaces": [
|
|
34
|
+
{
|
|
35
|
+
"name": "Contact",
|
|
36
|
+
"slug": "contact",
|
|
37
|
+
"docs": "",
|
|
38
|
+
"tags": [],
|
|
39
|
+
"methods": [],
|
|
40
|
+
"properties": [
|
|
41
|
+
{
|
|
42
|
+
"name": "identifier",
|
|
43
|
+
"tags": [],
|
|
44
|
+
"docs": "",
|
|
45
|
+
"complexTypes": [],
|
|
46
|
+
"type": "string | undefined"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "androidContactLookupKey",
|
|
50
|
+
"tags": [],
|
|
51
|
+
"docs": "",
|
|
52
|
+
"complexTypes": [],
|
|
53
|
+
"type": "string | undefined"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"name": "contactId",
|
|
57
|
+
"tags": [],
|
|
58
|
+
"docs": "",
|
|
59
|
+
"complexTypes": [],
|
|
60
|
+
"type": "string | undefined"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "givenName",
|
|
64
|
+
"tags": [],
|
|
65
|
+
"docs": "",
|
|
66
|
+
"complexTypes": [],
|
|
67
|
+
"type": "string | undefined"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"name": "familyName",
|
|
71
|
+
"tags": [],
|
|
72
|
+
"docs": "",
|
|
73
|
+
"complexTypes": [],
|
|
74
|
+
"type": "string | undefined"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"name": "nickname",
|
|
78
|
+
"tags": [],
|
|
79
|
+
"docs": "",
|
|
80
|
+
"complexTypes": [],
|
|
81
|
+
"type": "string | undefined"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "fullName",
|
|
85
|
+
"tags": [],
|
|
86
|
+
"docs": "",
|
|
87
|
+
"complexTypes": [],
|
|
88
|
+
"type": "string | undefined"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"name": "jobTitle",
|
|
92
|
+
"tags": [],
|
|
93
|
+
"docs": "",
|
|
94
|
+
"complexTypes": [],
|
|
95
|
+
"type": "string | undefined"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"name": "departmentName",
|
|
99
|
+
"tags": [],
|
|
100
|
+
"docs": "",
|
|
101
|
+
"complexTypes": [],
|
|
102
|
+
"type": "string | undefined"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"name": "organizationName",
|
|
106
|
+
"tags": [],
|
|
107
|
+
"docs": "",
|
|
108
|
+
"complexTypes": [],
|
|
109
|
+
"type": "string | undefined"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"name": "note",
|
|
113
|
+
"tags": [],
|
|
114
|
+
"docs": "",
|
|
115
|
+
"complexTypes": [],
|
|
116
|
+
"type": "string | undefined"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"name": "phoneNumbers",
|
|
120
|
+
"tags": [],
|
|
121
|
+
"docs": "",
|
|
122
|
+
"complexTypes": [],
|
|
123
|
+
"type": "any[] | undefined"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"name": "emailAddresses",
|
|
127
|
+
"tags": [],
|
|
128
|
+
"docs": "",
|
|
129
|
+
"complexTypes": [],
|
|
130
|
+
"type": "any[] | undefined"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"name": "postalAddresses",
|
|
134
|
+
"tags": [],
|
|
135
|
+
"docs": "",
|
|
136
|
+
"complexTypes": [],
|
|
137
|
+
"type": "any[] | undefined"
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
],
|
|
142
|
+
"enums": [],
|
|
143
|
+
"typeAliases": [],
|
|
144
|
+
"pluginConfigs": []
|
|
145
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface CapacitorContactsPlugin {
|
|
2
|
+
open?(): Promise<Contact[]>;
|
|
3
|
+
close?(): Promise<void>;
|
|
4
|
+
}
|
|
5
|
+
export interface Contact {
|
|
6
|
+
identifier?: string;
|
|
7
|
+
androidContactLookupKey?: string;
|
|
8
|
+
contactId?: string;
|
|
9
|
+
givenName?: string;
|
|
10
|
+
familyName?: string;
|
|
11
|
+
nickname?: string;
|
|
12
|
+
fullName?: string;
|
|
13
|
+
jobTitle?: string;
|
|
14
|
+
departmentName?: string;
|
|
15
|
+
organizationName?: string;
|
|
16
|
+
note?: string;
|
|
17
|
+
phoneNumbers?: any[];
|
|
18
|
+
emailAddresses?: any[];
|
|
19
|
+
postalAddresses?: any[];
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["export interface CapacitorContactsPlugin {\n open?(): Promise<Contact[]>;\n close?(): Promise<void>;\n}\n\nexport interface Contact {\n identifier?: string;\n androidContactLookupKey?: string;\n contactId?: string;\n givenName?: string;\n familyName?: string;\n nickname?: string;\n fullName?: string;\n jobTitle?: string;\n departmentName?: string;\n organizationName?: string;\n note?: string;\n phoneNumbers?: any[];\n emailAddresses?: any[]\n postalAddresses?: any[]\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { registerPlugin } from '@capacitor/core';
|
|
2
|
+
const CapacitorContacts = registerPlugin('CapacitorContacts', {
|
|
3
|
+
web: () => import('./web').then(m => new m.CapacitorContactsWeb()),
|
|
4
|
+
});
|
|
5
|
+
export * from './definitions';
|
|
6
|
+
export { CapacitorContacts };
|
|
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,MAAM,iBAAiB,GAAG,cAAc,CACtC,mBAAmB,EACnB;IACE,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,oBAAoB,EAAE,CAAC;CACnE,CACF,CAAC;AAEF,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { CapacitorContactsPlugin } from './definitions';\n\nconst CapacitorContacts = registerPlugin<CapacitorContactsPlugin>(\n 'CapacitorContacts',\n {\n web: () => import('./web').then(m => new m.CapacitorContactsWeb()),\n },\n);\n\nexport * from './definitions';\nexport { CapacitorContacts };\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { WebPlugin } from '@capacitor/core';
|
|
2
|
+
import type { CapacitorContactsPlugin } from './definitions';
|
|
3
|
+
export declare class CapacitorContactsWeb extends WebPlugin implements CapacitorContactsPlugin {
|
|
4
|
+
constructor();
|
|
5
|
+
close(): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
declare const ContactPicker: CapacitorContactsWeb;
|
|
8
|
+
declare const CapacitorContacts: CapacitorContactsWeb;
|
|
9
|
+
export { ContactPicker, CapacitorContacts };
|
package/dist/esm/web.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { WebPlugin } from '@capacitor/core';
|
|
2
|
+
export class CapacitorContactsWeb extends WebPlugin {
|
|
3
|
+
constructor() {
|
|
4
|
+
super({
|
|
5
|
+
name: 'CapacitorContacts',
|
|
6
|
+
platforms: ['web']
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
async close() { }
|
|
10
|
+
}
|
|
11
|
+
const ContactPicker = new CapacitorContactsWeb();
|
|
12
|
+
const CapacitorContacts = ContactPicker;
|
|
13
|
+
export { ContactPicker, CapacitorContacts };
|
|
14
|
+
//# 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;AAI5C,MAAM,OAAO,oBACX,SAAQ,SAAS;IAGjB;QACE,KAAK,CAAC;YACF,IAAI,EAAE,mBAAmB;YACzB,SAAS,EAAE,CAAC,KAAK,CAAC;SACrB,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,KAAK,KAAI,CAAC;CACjB;AAED,MAAM,aAAa,GAAG,IAAI,oBAAoB,EAAE,CAAC;AACjD,MAAM,iBAAiB,GAAG,aAAa,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,CAAC","sourcesContent":["import { WebPlugin } from '@capacitor/core';\n\nimport type { CapacitorContactsPlugin } from './definitions';\n\nexport class CapacitorContactsWeb\n extends WebPlugin\n implements CapacitorContactsPlugin\n{\n constructor() {\n super({\n name: 'CapacitorContacts',\n platforms: ['web']\n });\n }\n async close() {}\n}\n\nconst ContactPicker = new CapacitorContactsWeb();\nconst CapacitorContacts = ContactPicker\nexport { ContactPicker, CapacitorContacts };\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var core = require('@capacitor/core');
|
|
6
|
+
|
|
7
|
+
const CapacitorContacts$1 = core.registerPlugin('CapacitorContacts', {
|
|
8
|
+
web: () => Promise.resolve().then(function () { return web; }).then(m => new m.CapacitorContactsWeb()),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
class CapacitorContactsWeb extends core.WebPlugin {
|
|
12
|
+
constructor() {
|
|
13
|
+
super({
|
|
14
|
+
name: 'CapacitorContacts',
|
|
15
|
+
platforms: ['web']
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
async close() { }
|
|
19
|
+
}
|
|
20
|
+
const ContactPicker = new CapacitorContactsWeb();
|
|
21
|
+
const CapacitorContacts = ContactPicker;
|
|
22
|
+
|
|
23
|
+
var web = /*#__PURE__*/Object.freeze({
|
|
24
|
+
__proto__: null,
|
|
25
|
+
CapacitorContactsWeb: CapacitorContactsWeb,
|
|
26
|
+
ContactPicker: ContactPicker,
|
|
27
|
+
CapacitorContacts: CapacitorContacts
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
exports.CapacitorContacts = CapacitorContacts$1;
|
|
31
|
+
//# 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';\nconst CapacitorContacts = registerPlugin('CapacitorContacts', {\n web: () => import('./web').then(m => new m.CapacitorContactsWeb()),\n});\nexport * from './definitions';\nexport { CapacitorContacts };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class CapacitorContactsWeb extends WebPlugin {\n constructor() {\n super({\n name: 'CapacitorContacts',\n platforms: ['web']\n });\n }\n async close() { }\n}\nconst ContactPicker = new CapacitorContactsWeb();\nconst CapacitorContacts = ContactPicker;\nexport { ContactPicker, CapacitorContacts };\n//# sourceMappingURL=web.js.map"],"names":["CapacitorContacts","registerPlugin","WebPlugin"],"mappings":";;;;;;AACK,MAACA,mBAAiB,GAAGC,mBAAc,CAAC,mBAAmB,EAAE;AAC9D,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,oBAAoB,EAAE,CAAC;AACtE,CAAC;;ACFM,MAAM,oBAAoB,SAASC,cAAS,CAAC;AACpD,IAAI,WAAW,GAAG;AAClB,QAAQ,KAAK,CAAC;AACd,YAAY,IAAI,EAAE,mBAAmB;AACrC,YAAY,SAAS,EAAE,CAAC,KAAK,CAAC;AAC9B,SAAS,CAAC,CAAC;AACX,KAAK;AACL,IAAI,MAAM,KAAK,GAAG,GAAG;AACrB,CAAC;AACD,MAAM,aAAa,GAAG,IAAI,oBAAoB,EAAE,CAAC;AACjD,MAAM,iBAAiB,GAAG,aAAa;;;;;;;;;;;"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
var capacitorCapacitorContacts = (function (exports, core) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const CapacitorContacts$1 = core.registerPlugin('CapacitorContacts', {
|
|
5
|
+
web: () => Promise.resolve().then(function () { return web; }).then(m => new m.CapacitorContactsWeb()),
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
class CapacitorContactsWeb extends core.WebPlugin {
|
|
9
|
+
constructor() {
|
|
10
|
+
super({
|
|
11
|
+
name: 'CapacitorContacts',
|
|
12
|
+
platforms: ['web']
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
async close() { }
|
|
16
|
+
}
|
|
17
|
+
const ContactPicker = new CapacitorContactsWeb();
|
|
18
|
+
const CapacitorContacts = ContactPicker;
|
|
19
|
+
|
|
20
|
+
var web = /*#__PURE__*/Object.freeze({
|
|
21
|
+
__proto__: null,
|
|
22
|
+
CapacitorContactsWeb: CapacitorContactsWeb,
|
|
23
|
+
ContactPicker: ContactPicker,
|
|
24
|
+
CapacitorContacts: CapacitorContacts
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
exports.CapacitorContacts = CapacitorContacts$1;
|
|
28
|
+
|
|
29
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
30
|
+
|
|
31
|
+
return exports;
|
|
32
|
+
|
|
33
|
+
})({}, capacitorExports);
|
|
34
|
+
//# 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';\nconst CapacitorContacts = registerPlugin('CapacitorContacts', {\n web: () => import('./web').then(m => new m.CapacitorContactsWeb()),\n});\nexport * from './definitions';\nexport { CapacitorContacts };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class CapacitorContactsWeb extends WebPlugin {\n constructor() {\n super({\n name: 'CapacitorContacts',\n platforms: ['web']\n });\n }\n async close() { }\n}\nconst ContactPicker = new CapacitorContactsWeb();\nconst CapacitorContacts = ContactPicker;\nexport { ContactPicker, CapacitorContacts };\n//# sourceMappingURL=web.js.map"],"names":["CapacitorContacts","registerPlugin","WebPlugin"],"mappings":";;;AACK,UAACA,mBAAiB,GAAGC,mBAAc,CAAC,mBAAmB,EAAE;IAC9D,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,oBAAoB,EAAE,CAAC;IACtE,CAAC;;ICFM,MAAM,oBAAoB,SAASC,cAAS,CAAC;IACpD,IAAI,WAAW,GAAG;IAClB,QAAQ,KAAK,CAAC;IACd,YAAY,IAAI,EAAE,mBAAmB;IACrC,YAAY,SAAS,EAAE,CAAC,KAAK,CAAC;IAC9B,SAAS,CAAC,CAAC;IACX,KAAK;IACL,IAAI,MAAM,KAAK,GAAG,GAAG;IACrB,CAAC;IACD,MAAM,aAAa,GAAG,IAAI,oBAAoB,EAAE,CAAC;IACjD,MAAM,iBAAiB,GAAG,aAAa;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
import ContactsUI
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Please read the Capacitor iOS Plugin Development Guide
|
|
7
|
+
* here: https://capacitorjs.com/docs/plugins/ios
|
|
8
|
+
*/
|
|
9
|
+
@objc(CapacitorContactsPlugin)
|
|
10
|
+
public class CapacitorContactsPlugin: CAPPlugin, CAPBridgedPlugin, CNContactPickerDelegate {
|
|
11
|
+
public let identifier = "CapacitorContactsPlugin"
|
|
12
|
+
public let jsName = "CapacitorContacts"
|
|
13
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
14
|
+
CAPPluginMethod(name: "open", returnType: CAPPluginReturnPromise),
|
|
15
|
+
CAPPluginMethod(name: "close", returnType: CAPPluginReturnPromise)
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
var vc: CNContactPickerViewController?
|
|
19
|
+
var id: String?
|
|
20
|
+
|
|
21
|
+
@objc func open(_ call: CAPPluginCall) {
|
|
22
|
+
self.id = call.callbackId
|
|
23
|
+
call.keepAlive = true
|
|
24
|
+
Permissions.contactPermission { granted in
|
|
25
|
+
if granted {
|
|
26
|
+
DispatchQueue.main.async {
|
|
27
|
+
self.vc = CNContactPickerViewController()
|
|
28
|
+
self.vc!.delegate = self
|
|
29
|
+
self.bridge?.viewController?.present(self.vc!, animated: true, completion: nil)
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
call.reject("User denied access to contacts")
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@objc func close(_ call: CAPPluginCall) {
|
|
38
|
+
if vc == nil {
|
|
39
|
+
call.resolve()
|
|
40
|
+
}
|
|
41
|
+
DispatchQueue.main.async {
|
|
42
|
+
self.bridge?.dismissVC(animated: true) {
|
|
43
|
+
call.resolve()
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
func makeContact(_ contact: CNContact) -> Dictionary<String, Any> {
|
|
49
|
+
var res: [String:Any] = [:]
|
|
50
|
+
res["contactId"] = contact.identifier;
|
|
51
|
+
res["givenName"] = contact.givenName;
|
|
52
|
+
res["familyName"] = contact.familyName;
|
|
53
|
+
res["nickname"] = contact.nickname;
|
|
54
|
+
res["displayName"] = contact.givenName + " " + contact.familyName;
|
|
55
|
+
res["jobTitle"] = contact.jobTitle;
|
|
56
|
+
res["departmentName"] = contact.departmentName;
|
|
57
|
+
res["organizationName"] = contact.organizationName;
|
|
58
|
+
res["note"] = contact.note;
|
|
59
|
+
|
|
60
|
+
// process email addresses
|
|
61
|
+
var array: [JSObject] = [];
|
|
62
|
+
for emailAddress in contact.emailAddresses {
|
|
63
|
+
var object = JSObject()
|
|
64
|
+
object["type"] = CNLabeledValue<NSString>.localizedString(forLabel: emailAddress.label ?? "")
|
|
65
|
+
if emailAddress.value != "" {
|
|
66
|
+
object["emailAddress"] = emailAddress.value as String
|
|
67
|
+
}
|
|
68
|
+
array.append(object)
|
|
69
|
+
}
|
|
70
|
+
res["emailAddresses"] = array
|
|
71
|
+
|
|
72
|
+
// process phone numbers
|
|
73
|
+
array = [];
|
|
74
|
+
for phoneNumber in contact.phoneNumbers {
|
|
75
|
+
var object = JSObject()
|
|
76
|
+
object["type"] = CNLabeledValue<NSString>.localizedString(forLabel: phoneNumber.label ?? "")
|
|
77
|
+
if phoneNumber.value.stringValue != "" {
|
|
78
|
+
object["phoneNumber"] = phoneNumber.value.stringValue
|
|
79
|
+
}
|
|
80
|
+
array.append(object)
|
|
81
|
+
}
|
|
82
|
+
res["phoneNumbers"] = array
|
|
83
|
+
|
|
84
|
+
// process postal addresses
|
|
85
|
+
array = [];
|
|
86
|
+
for address in contact.postalAddresses {
|
|
87
|
+
var object = JSObject()
|
|
88
|
+
object["formattedAddress"] = CNPostalAddressFormatter().string(from: address.value)
|
|
89
|
+
object["type"] = CNLabeledValue<NSString>.localizedString(forLabel: address.label ?? "")
|
|
90
|
+
if address.value.street != "" {
|
|
91
|
+
object["street"] = address.value.street
|
|
92
|
+
}
|
|
93
|
+
if address.value.city != "" {
|
|
94
|
+
object["city"] = address.value.city
|
|
95
|
+
}
|
|
96
|
+
if address.value.state != "" {
|
|
97
|
+
object["state"] = address.value.state
|
|
98
|
+
}
|
|
99
|
+
if address.value.postalCode != "" {
|
|
100
|
+
object["postalCode"] = address.value.postalCode
|
|
101
|
+
}
|
|
102
|
+
if address.value.country != "" {
|
|
103
|
+
object["country"] = address.value.country
|
|
104
|
+
}
|
|
105
|
+
if address.value.isoCountryCode != "" {
|
|
106
|
+
object["isoCountryCode"] = address.value.isoCountryCode
|
|
107
|
+
}
|
|
108
|
+
if address.value.subAdministrativeArea != "" {
|
|
109
|
+
object["subAdministrativeArea"] = address.value.subAdministrativeArea
|
|
110
|
+
}
|
|
111
|
+
if address.value.subLocality != "" {
|
|
112
|
+
object["subLocality"] = address.value.subLocality
|
|
113
|
+
}
|
|
114
|
+
array.append(object)
|
|
115
|
+
}
|
|
116
|
+
res["postalAddresses"] = array
|
|
117
|
+
|
|
118
|
+
// temporarily disable returning contact imageData because base64 data overwhelms console log
|
|
119
|
+
/*if contact.imageData != nil {
|
|
120
|
+
let image = UIImage(data: contact.imageData!)?.pngData() ?? UIImage(data: contact.imageData!)?.jpegData(compressionQuality: 0);
|
|
121
|
+
res["image"] = image!.base64EncodedString(options: .lineLength64Characters);
|
|
122
|
+
}*/
|
|
123
|
+
|
|
124
|
+
return res
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// didSelect contacts: [CNContact]
|
|
128
|
+
public func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
|
|
129
|
+
picker.dismiss(animated: true, completion: nil)
|
|
130
|
+
guard let call = self.bridge?.savedCall(withID: self.id!) else {
|
|
131
|
+
print("call was not loaded correctly")
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
//print("result: " + String(describing: contact))
|
|
135
|
+
call.resolve(makeContact(contact));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
|
|
139
|
+
//print("closed!")
|
|
140
|
+
picker.dismiss(animated: true, completion: nil)
|
|
141
|
+
guard let call = self.bridge?.savedCall(withID: self.id!) else { return }
|
|
142
|
+
call.resolve()
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
class Permissions {
|
|
147
|
+
class func contactPermission(completionHandler: @escaping (_ accessGranted: Bool) -> Void) {
|
|
148
|
+
let contactStore = CNContactStore()
|
|
149
|
+
switch CNContactStore.authorizationStatus(for: .contacts) {
|
|
150
|
+
case .authorized:
|
|
151
|
+
completionHandler(true)
|
|
152
|
+
case .denied:
|
|
153
|
+
completionHandler(false)
|
|
154
|
+
case .restricted, .notDetermined:
|
|
155
|
+
contactStore.requestAccess(for: .contacts) { granted, _ in
|
|
156
|
+
if granted {
|
|
157
|
+
completionHandler(true)
|
|
158
|
+
} else {
|
|
159
|
+
DispatchQueue.main.async {
|
|
160
|
+
completionHandler(false)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
@testable import CapacitorContactsPlugin
|
|
3
|
+
|
|
4
|
+
class CapacitorContactsTests: XCTestCase {
|
|
5
|
+
func testEcho() {
|
|
6
|
+
// This is an example of a functional test case for a plugin.
|
|
7
|
+
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
|
8
|
+
|
|
9
|
+
let implementation = CapacitorContacts()
|
|
10
|
+
let value = "Hello, World!"
|
|
11
|
+
let result = implementation.echo(value)
|
|
12
|
+
|
|
13
|
+
XCTAssertEqual(value, result)
|
|
14
|
+
}
|
|
15
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aalzehla/capacitor-contacts",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "This capacitor plugin allows you to use the native contact picker UI on Android or iOS for single contact selection. Both platforms will return the same payload structure, where the data exists.",
|
|
5
|
+
"main": "dist/plugin.cjs.js",
|
|
6
|
+
"module": "dist/esm/index.js",
|
|
7
|
+
"types": "dist/esm/index.d.ts",
|
|
8
|
+
"unpkg": "dist/plugin.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"android/src/main/",
|
|
11
|
+
"android/build.gradle",
|
|
12
|
+
"dist/",
|
|
13
|
+
"ios/Sources",
|
|
14
|
+
"ios/Tests",
|
|
15
|
+
"Package.swift",
|
|
16
|
+
"CapacitorContacts.podspec"
|
|
17
|
+
],
|
|
18
|
+
"author": "Ateik Alzehla",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/aalzehla/capacitor-contacts.git"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/aalzehla/capacitor-contacts/issues"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"capacitor",
|
|
29
|
+
"plugin",
|
|
30
|
+
"native"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
|
|
34
|
+
"verify:ios": "xcodebuild -scheme CapacitorContacts -destination generic/platform=iOS",
|
|
35
|
+
"verify:android": "cd android && ./gradlew clean build test && cd ..",
|
|
36
|
+
"verify:web": "npm run build",
|
|
37
|
+
"lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
|
|
38
|
+
"fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
|
|
39
|
+
"eslint": "eslint . --ext ts",
|
|
40
|
+
"prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
|
|
41
|
+
"swiftlint": "node-swiftlint",
|
|
42
|
+
"docgen": "docgen --api CapacitorContactsPlugin --output-readme README.md --output-json dist/docs.json",
|
|
43
|
+
"build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.js",
|
|
44
|
+
"clean": "rimraf ./dist",
|
|
45
|
+
"watch": "tsc --watch",
|
|
46
|
+
"prepublishOnly": "npm run build"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@capacitor/android": "^6.0.0",
|
|
50
|
+
"@capacitor/core": "^6.0.0",
|
|
51
|
+
"@capacitor/docgen": "^0.2.2",
|
|
52
|
+
"@capacitor/ios": "^6.0.0",
|
|
53
|
+
"@ionic/eslint-config": "^0.4.0",
|
|
54
|
+
"@ionic/prettier-config": "^1.0.1",
|
|
55
|
+
"@ionic/swiftlint-config": "^1.1.2",
|
|
56
|
+
"eslint": "^8.57.0",
|
|
57
|
+
"prettier": "~2.3.0",
|
|
58
|
+
"prettier-plugin-java": "~1.0.2",
|
|
59
|
+
"rimraf": "^3.0.2",
|
|
60
|
+
"rollup": "^2.32.0",
|
|
61
|
+
"swiftlint": "^1.0.1",
|
|
62
|
+
"typescript": "~4.1.5"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"@capacitor/core": "^6.0.0"
|
|
66
|
+
},
|
|
67
|
+
"prettier": "@ionic/prettier-config",
|
|
68
|
+
"swiftlint": "@ionic/swiftlint-config",
|
|
69
|
+
"eslintConfig": {
|
|
70
|
+
"extends": "@ionic/eslint-config/recommended"
|
|
71
|
+
},
|
|
72
|
+
"capacitor": {
|
|
73
|
+
"ios": {
|
|
74
|
+
"src": "ios"
|
|
75
|
+
},
|
|
76
|
+
"android": {
|
|
77
|
+
"src": "android"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|