expo-contacts 12.6.0 → 12.7.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/CHANGELOG.md +12 -0
- package/android/build.gradle +4 -4
- package/android/src/main/java/expo/modules/contacts/Columns.kt +26 -0
- package/android/src/main/java/expo/modules/contacts/CommonProvider.kt +8 -0
- package/android/src/main/java/expo/modules/contacts/Contact.kt +516 -0
- package/android/src/main/java/expo/modules/contacts/ContactsModule.kt +682 -0
- package/android/src/main/java/expo/modules/contacts/models/BaseModel.kt +146 -0
- package/android/src/main/java/expo/modules/contacts/models/DateModel.kt +56 -0
- package/android/src/main/java/expo/modules/contacts/models/EmailModel.kt +22 -0
- package/android/src/main/java/expo/modules/contacts/models/ExtraNameModel.kt +34 -0
- package/android/src/main/java/expo/modules/contacts/models/ImAddressModel.kt +39 -0
- package/android/src/main/java/expo/modules/contacts/models/PhoneNumberModel.kt +55 -0
- package/android/src/main/java/expo/modules/contacts/models/PostalAddressModel.kt +78 -0
- package/android/src/main/java/expo/modules/contacts/models/RelationshipModel.kt +32 -0
- package/android/src/main/java/expo/modules/contacts/models/UrlAddressModel.kt +25 -0
- package/build/Contacts.d.ts +4 -0
- package/build/Contacts.d.ts.map +1 -1
- package/build/Contacts.js.map +1 -1
- package/build/ExpoContacts.d.ts +1 -1
- package/build/ExpoContacts.d.ts.map +1 -1
- package/build/ExpoContacts.js +2 -2
- package/build/ExpoContacts.js.map +1 -1
- package/expo-module.config.json +6 -0
- package/ios/EXContacts.podspec +1 -1
- package/package.json +2 -2
- package/src/Contacts.ts +4 -0
- package/src/ExpoContacts.ts +2 -2
- package/android/src/main/java/expo/modules/contacts/CommonProvider.java +0 -9
- package/android/src/main/java/expo/modules/contacts/Contact.java +0 -577
- package/android/src/main/java/expo/modules/contacts/ContactsModule.java +0 -870
- package/android/src/main/java/expo/modules/contacts/ContactsPackage.java +0 -17
- package/android/src/main/java/expo/modules/contacts/EXColumns.java +0 -27
- package/android/src/main/java/expo/modules/contacts/models/BaseModel.java +0 -202
- package/android/src/main/java/expo/modules/contacts/models/DateModel.java +0 -85
- package/android/src/main/java/expo/modules/contacts/models/EmailModel.java +0 -37
- package/android/src/main/java/expo/modules/contacts/models/ExtraNameModel.java +0 -68
- package/android/src/main/java/expo/modules/contacts/models/ImAddressModel.java +0 -60
- package/android/src/main/java/expo/modules/contacts/models/PhoneNumberModel.java +0 -122
- package/android/src/main/java/expo/modules/contacts/models/PostalAddressModel.java +0 -108
- package/android/src/main/java/expo/modules/contacts/models/RelationshipModel.java +0 -60
- package/android/src/main/java/expo/modules/contacts/models/UrlAddressModel.java +0 -45
- package/unimodule.json +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,18 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 12.7.0 — 2023-11-14
|
|
14
|
+
|
|
15
|
+
### 🛠 Breaking changes
|
|
16
|
+
|
|
17
|
+
- Bumped iOS deployment target to 13.4. ([#25063](https://github.com/expo/expo/pull/25063) by [@gabrieldonadel](https://github.com/gabrieldonadel))
|
|
18
|
+
- On `Android` bump `compileSdkVersion` and `targetSdkVersion` to `34`. ([#24708](https://github.com/expo/expo/pull/24708) by [@alanjhughes](https://github.com/alanjhughes))
|
|
19
|
+
|
|
20
|
+
### 💡 Others
|
|
21
|
+
|
|
22
|
+
- Renamed `unimodule.json` to `expo-module.config.json`. ([#25100](https://github.com/expo/expo/pull/25100) by [@reichhartd](https://github.com/reichhartd))
|
|
23
|
+
- Migrated codebase to use Expo Modules API. ([#24991](https://github.com/expo/expo/pull/24991) by [@lukmccall](https://github.com/lukmccall))
|
|
24
|
+
|
|
13
25
|
## 12.6.0 — 2023-10-17
|
|
14
26
|
|
|
15
27
|
### 🛠 Breaking changes
|
package/android/build.gradle
CHANGED
|
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
|
|
|
3
3
|
apply plugin: 'maven-publish'
|
|
4
4
|
|
|
5
5
|
group = 'host.exp.exponent'
|
|
6
|
-
version = '12.
|
|
6
|
+
version = '12.7.0'
|
|
7
7
|
|
|
8
8
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
9
9
|
if (expoModulesCorePlugin.exists()) {
|
|
@@ -61,11 +61,11 @@ if (!safeExtGet("expoProvidesDefaultConfig", false)) {
|
|
|
61
61
|
android {
|
|
62
62
|
// Remove this if and it's contents, when support for SDK49 is dropped
|
|
63
63
|
if (!safeExtGet("expoProvidesDefaultConfig", false)) {
|
|
64
|
-
compileSdkVersion safeExtGet("compileSdkVersion",
|
|
64
|
+
compileSdkVersion safeExtGet("compileSdkVersion", 34)
|
|
65
65
|
|
|
66
66
|
defaultConfig {
|
|
67
67
|
minSdkVersion safeExtGet("minSdkVersion", 23)
|
|
68
|
-
targetSdkVersion safeExtGet("targetSdkVersion",
|
|
68
|
+
targetSdkVersion safeExtGet("targetSdkVersion", 34)
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
publishing {
|
|
@@ -94,7 +94,7 @@ android {
|
|
|
94
94
|
namespace "expo.modules.contacts"
|
|
95
95
|
defaultConfig {
|
|
96
96
|
versionCode 29
|
|
97
|
-
versionName "12.
|
|
97
|
+
versionName "12.7.0"
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
package expo.modules.contacts
|
|
2
|
+
|
|
3
|
+
import android.provider.ContactsContract
|
|
4
|
+
|
|
5
|
+
object Columns {
|
|
6
|
+
const val DATA_10 = "data10"
|
|
7
|
+
const val DATA_9 = "data9"
|
|
8
|
+
const val DATA_8 = "data8"
|
|
9
|
+
const val DATA_7 = "data7"
|
|
10
|
+
const val DATA_6 = "data6"
|
|
11
|
+
const val DATA_5 = "data5"
|
|
12
|
+
const val DATA_4 = "data4"
|
|
13
|
+
const val LABEL = "data3"
|
|
14
|
+
const val TYPE = "data2"
|
|
15
|
+
const val DATA = "data1"
|
|
16
|
+
const val ID = ContactsContract.Data._ID
|
|
17
|
+
const val IS_PRIMARY = ContactsContract.Data.IS_PRIMARY
|
|
18
|
+
const val CONTACT_ID = ContactsContract.Data.CONTACT_ID
|
|
19
|
+
const val LOOKUP_KEY = ContactsContract.Data.LOOKUP_KEY
|
|
20
|
+
const val DISPLAY_NAME = ContactsContract.Data.DISPLAY_NAME
|
|
21
|
+
const val PHOTO_URI = ContactsContract.CommonDataKinds.Contactables.PHOTO_URI
|
|
22
|
+
const val PHOTO_THUMBNAIL_URI = ContactsContract.CommonDataKinds.Contactables.PHOTO_THUMBNAIL_URI
|
|
23
|
+
const val IS_USER_PROFILE = ContactsContract.CommonDataKinds.Contactables.IS_USER_PROFILE
|
|
24
|
+
const val MIMETYPE = ContactsContract.Data.MIMETYPE
|
|
25
|
+
const val TYPE_CUSTOM = 0
|
|
26
|
+
}
|
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
package expo.modules.contacts
|
|
2
|
+
|
|
3
|
+
import android.content.ContentProviderOperation
|
|
4
|
+
import android.content.ContentValues
|
|
5
|
+
import android.database.Cursor
|
|
6
|
+
import android.graphics.Bitmap
|
|
7
|
+
import android.graphics.BitmapFactory
|
|
8
|
+
import android.net.Uri
|
|
9
|
+
import android.os.Bundle
|
|
10
|
+
import android.provider.ContactsContract
|
|
11
|
+
import android.provider.ContactsContract.CommonDataKinds
|
|
12
|
+
import android.provider.ContactsContract.CommonDataKinds.StructuredName
|
|
13
|
+
import android.provider.ContactsContract.RawContacts
|
|
14
|
+
import android.text.TextUtils
|
|
15
|
+
import android.util.Log
|
|
16
|
+
import expo.modules.contacts.models.BaseModel
|
|
17
|
+
import expo.modules.contacts.models.DateModel
|
|
18
|
+
import expo.modules.contacts.models.EmailModel
|
|
19
|
+
import expo.modules.contacts.models.ExtraNameModel
|
|
20
|
+
import expo.modules.contacts.models.ImAddressModel
|
|
21
|
+
import expo.modules.contacts.models.PhoneNumberModel
|
|
22
|
+
import expo.modules.contacts.models.PostalAddressModel
|
|
23
|
+
import expo.modules.contacts.models.RelationshipModel
|
|
24
|
+
import expo.modules.contacts.models.UrlAddressModel
|
|
25
|
+
import java.io.ByteArrayOutputStream
|
|
26
|
+
import java.text.ParseException
|
|
27
|
+
import java.text.SimpleDateFormat
|
|
28
|
+
import java.util.Calendar
|
|
29
|
+
import java.util.Locale
|
|
30
|
+
|
|
31
|
+
// TODO: MaidenName Nickname
|
|
32
|
+
class Contact(var contactId: String) {
|
|
33
|
+
private var rawContactId: String? = null
|
|
34
|
+
var lookupKey: String? = null
|
|
35
|
+
private var displayName: String? = null
|
|
36
|
+
var hasPhoto = false
|
|
37
|
+
var photoUri: String? = null
|
|
38
|
+
private var rawPhotoUri: String? = null
|
|
39
|
+
private var contactType = "person"
|
|
40
|
+
var firstName: String? = ""
|
|
41
|
+
var middleName: String? = ""
|
|
42
|
+
var lastName: String? = ""
|
|
43
|
+
|
|
44
|
+
var prefix: String? = ""
|
|
45
|
+
var suffix: String? = ""
|
|
46
|
+
var phoneticFirstName: String? = ""
|
|
47
|
+
var phoneticMiddleName: String? = ""
|
|
48
|
+
var phoneticLastName: String? = ""
|
|
49
|
+
var company: String? = ""
|
|
50
|
+
var department: String? = ""
|
|
51
|
+
var jobTitle: String? = ""
|
|
52
|
+
var note: String? = null
|
|
53
|
+
var dates: MutableList<DateModel> = ArrayList()
|
|
54
|
+
var emails: MutableList<EmailModel> = ArrayList()
|
|
55
|
+
var imAddresses: MutableList<ImAddressModel> = ArrayList()
|
|
56
|
+
var phones: MutableList<PhoneNumberModel> = ArrayList()
|
|
57
|
+
var addresses: MutableList<PostalAddressModel> = ArrayList()
|
|
58
|
+
var relationships: MutableList<RelationshipModel> = ArrayList()
|
|
59
|
+
var urlAddresses: MutableList<UrlAddressModel> = ArrayList()
|
|
60
|
+
var extraNames: MutableList<ExtraNameModel> = ArrayList()
|
|
61
|
+
|
|
62
|
+
fun fromCursor(cursor: Cursor) {
|
|
63
|
+
rawContactId = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Data.RAW_CONTACT_ID))
|
|
64
|
+
val mimeType = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Data.MIMETYPE))
|
|
65
|
+
val name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME))
|
|
66
|
+
if (!TextUtils.isEmpty(name) && TextUtils.isEmpty(displayName)) {
|
|
67
|
+
displayName = name
|
|
68
|
+
}
|
|
69
|
+
if (TextUtils.isEmpty(rawPhotoUri)) {
|
|
70
|
+
val rawPhotoURI = cursor.getString(cursor.getColumnIndexOrThrow(Columns.PHOTO_URI))
|
|
71
|
+
if (!TextUtils.isEmpty(rawPhotoURI)) {
|
|
72
|
+
hasPhoto = true
|
|
73
|
+
rawPhotoUri = rawPhotoURI
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (TextUtils.isEmpty(photoUri)) {
|
|
77
|
+
val rawPhotoURI = cursor.getString(cursor.getColumnIndexOrThrow(Columns.PHOTO_THUMBNAIL_URI))
|
|
78
|
+
if (!TextUtils.isEmpty(rawPhotoURI)) {
|
|
79
|
+
hasPhoto = true
|
|
80
|
+
photoUri = rawPhotoURI
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
when (mimeType) {
|
|
84
|
+
StructuredName.CONTENT_ITEM_TYPE -> {
|
|
85
|
+
lookupKey = cursor.getString(cursor.getColumnIndexOrThrow(StructuredName.LOOKUP_KEY))
|
|
86
|
+
firstName = cursor.getString(cursor.getColumnIndexOrThrow(StructuredName.GIVEN_NAME))
|
|
87
|
+
middleName = cursor.getString(cursor.getColumnIndexOrThrow(StructuredName.MIDDLE_NAME))
|
|
88
|
+
lastName = cursor.getString(cursor.getColumnIndexOrThrow(StructuredName.FAMILY_NAME))
|
|
89
|
+
prefix = cursor.getString(cursor.getColumnIndexOrThrow(StructuredName.PREFIX))
|
|
90
|
+
suffix = cursor.getString(cursor.getColumnIndexOrThrow(StructuredName.SUFFIX))
|
|
91
|
+
phoneticFirstName = cursor
|
|
92
|
+
.getString(cursor.getColumnIndexOrThrow(StructuredName.PHONETIC_GIVEN_NAME))
|
|
93
|
+
phoneticMiddleName = cursor
|
|
94
|
+
.getString(cursor.getColumnIndexOrThrow(StructuredName.PHONETIC_MIDDLE_NAME))
|
|
95
|
+
phoneticLastName = cursor
|
|
96
|
+
.getString(cursor.getColumnIndexOrThrow(StructuredName.PHONETIC_FAMILY_NAME))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
CommonDataKinds.Organization.CONTENT_ITEM_TYPE -> {
|
|
100
|
+
company = cursor.getString(cursor.getColumnIndexOrThrow(CommonDataKinds.Organization.COMPANY))
|
|
101
|
+
jobTitle = cursor.getString(cursor.getColumnIndexOrThrow(CommonDataKinds.Organization.TITLE))
|
|
102
|
+
department = cursor.getString(cursor.getColumnIndexOrThrow(CommonDataKinds.Organization.DEPARTMENT))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
CommonDataKinds.Note.CONTENT_ITEM_TYPE -> {
|
|
106
|
+
note = cursor.getString(cursor.getColumnIndexOrThrow(CommonDataKinds.Note.NOTE))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
CommonDataKinds.Event.CONTENT_ITEM_TYPE -> {
|
|
110
|
+
val item = DateModel()
|
|
111
|
+
item.fromCursor(cursor)
|
|
112
|
+
dates.add(item)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
CommonDataKinds.Email.CONTENT_ITEM_TYPE -> {
|
|
116
|
+
val item = EmailModel()
|
|
117
|
+
item.fromCursor(cursor)
|
|
118
|
+
emails.add(item)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
CommonDataKinds.Im.CONTENT_ITEM_TYPE -> {
|
|
122
|
+
val item = ImAddressModel()
|
|
123
|
+
item.fromCursor(cursor)
|
|
124
|
+
imAddresses.add(item)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> {
|
|
128
|
+
val item = PhoneNumberModel()
|
|
129
|
+
item.fromCursor(cursor)
|
|
130
|
+
phones.add(item)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE -> {
|
|
134
|
+
val item = PostalAddressModel()
|
|
135
|
+
item.fromCursor(cursor)
|
|
136
|
+
addresses.add(item)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
CommonDataKinds.Relation.CONTENT_ITEM_TYPE -> {
|
|
140
|
+
val item = RelationshipModel()
|
|
141
|
+
item.fromCursor(cursor)
|
|
142
|
+
relationships.add(item)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
CommonDataKinds.Website.CONTENT_ITEM_TYPE -> {
|
|
146
|
+
val urlAddressModel = UrlAddressModel()
|
|
147
|
+
urlAddressModel.fromCursor(cursor)
|
|
148
|
+
urlAddresses.add(urlAddressModel)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
CommonDataKinds.Nickname.CONTENT_ITEM_TYPE -> {
|
|
152
|
+
val item = ExtraNameModel()
|
|
153
|
+
item.fromCursor(cursor)
|
|
154
|
+
extraNames.add(item)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
val hasCompanyName = company != null && company != ""
|
|
158
|
+
contactType = if (hasCompanyName) {
|
|
159
|
+
val hasFirstName = firstName != null && firstName != ""
|
|
160
|
+
val hasMiddleName = middleName != null && middleName != ""
|
|
161
|
+
val hasLastName = lastName != null && lastName != ""
|
|
162
|
+
if (!hasFirstName && !hasMiddleName && !hasLastName) {
|
|
163
|
+
"company"
|
|
164
|
+
} else {
|
|
165
|
+
"person"
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
"person"
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
fun getFinalFirstName(): String {
|
|
173
|
+
return firstName ?: if (displayName == null) "" else displayName!!
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
fun getFinalLastName(): String {
|
|
177
|
+
return lastName ?: if (displayName == null) "" else displayName!!
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
fun getFinalDisplayName(): String? {
|
|
181
|
+
return if (displayName == null && firstName != null) {
|
|
182
|
+
if (lastName == null) firstName else String.format("%s %s", firstName, lastName).trim { it <= ' ' }
|
|
183
|
+
} else displayName
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private fun toByteArray(bitmap: Bitmap): ByteArray {
|
|
187
|
+
val stream = ByteArrayOutputStream()
|
|
188
|
+
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, stream)
|
|
189
|
+
return stream.toByteArray()
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
fun toInsertOperationList(): ArrayList<ContentProviderOperation> {
|
|
193
|
+
val ops = ArrayList<ContentProviderOperation>()
|
|
194
|
+
var op = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
|
|
195
|
+
.withValue(RawContacts.ACCOUNT_TYPE, null)
|
|
196
|
+
.withValue(RawContacts.ACCOUNT_NAME, null)
|
|
197
|
+
ops.add(op.build())
|
|
198
|
+
op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
|
199
|
+
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
|
200
|
+
.withValue(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
|
|
201
|
+
.withValue(StructuredName.DISPLAY_NAME, displayName)
|
|
202
|
+
.withValue(StructuredName.GIVEN_NAME, firstName)
|
|
203
|
+
.withValue(StructuredName.MIDDLE_NAME, middleName)
|
|
204
|
+
.withValue(StructuredName.FAMILY_NAME, lastName)
|
|
205
|
+
.withValue(StructuredName.PHONETIC_GIVEN_NAME, phoneticFirstName)
|
|
206
|
+
.withValue(StructuredName.PHONETIC_MIDDLE_NAME, phoneticMiddleName)
|
|
207
|
+
.withValue(StructuredName.PHONETIC_FAMILY_NAME, phoneticLastName)
|
|
208
|
+
.withValue(StructuredName.PREFIX, prefix)
|
|
209
|
+
.withValue(StructuredName.SUFFIX, suffix)
|
|
210
|
+
ops.add(op.build())
|
|
211
|
+
op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
|
212
|
+
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
|
213
|
+
.withValue(Columns.MIMETYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
|
|
214
|
+
.withValue(CommonDataKinds.Organization.COMPANY, company)
|
|
215
|
+
.withValue(CommonDataKinds.Organization.TITLE, jobTitle)
|
|
216
|
+
.withValue(CommonDataKinds.Organization.DEPARTMENT, department)
|
|
217
|
+
ops.add(op.build())
|
|
218
|
+
op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
|
219
|
+
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
|
220
|
+
.withValue(Columns.MIMETYPE, CommonDataKinds.Note.CONTENT_ITEM_TYPE)
|
|
221
|
+
.withValue(CommonDataKinds.Note.NOTE, note)
|
|
222
|
+
ops.add(op.build())
|
|
223
|
+
op.withYieldAllowed(true)
|
|
224
|
+
if (!TextUtils.isEmpty(photoUri) || !TextUtils.isEmpty(rawPhotoUri)) {
|
|
225
|
+
val photo = getThumbnailBitmap(if (TextUtils.isEmpty(rawPhotoUri)) photoUri else rawPhotoUri)
|
|
226
|
+
ops.add(
|
|
227
|
+
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
|
228
|
+
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
|
229
|
+
.withValue(Columns.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
|
|
230
|
+
.withValue(CommonDataKinds.Photo.PHOTO, toByteArray(photo))
|
|
231
|
+
.build()
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
for (map in baseModels) {
|
|
235
|
+
for (item in map) {
|
|
236
|
+
ops.add(item.insertOperation)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return ops
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
fun toUpdateOperationList(): ArrayList<ContentProviderOperation?> {
|
|
243
|
+
val selection = String.format("%s=? AND %s=?", ContactsContract.Data.CONTACT_ID, ContactsContract.Data.MIMETYPE)
|
|
244
|
+
val selectionArgs = arrayOf(contactId, StructuredName.CONTENT_ITEM_TYPE)
|
|
245
|
+
val ops = ArrayList<ContentProviderOperation?>()
|
|
246
|
+
var op: ContentProviderOperation.Builder = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
|
|
247
|
+
.withSelection(selection, selectionArgs)
|
|
248
|
+
.withValue(StructuredName.DISPLAY_NAME, displayName)
|
|
249
|
+
.withValue(StructuredName.GIVEN_NAME, firstName)
|
|
250
|
+
.withValue(StructuredName.MIDDLE_NAME, middleName)
|
|
251
|
+
.withValue(StructuredName.FAMILY_NAME, lastName)
|
|
252
|
+
.withValue(StructuredName.PHONETIC_GIVEN_NAME, phoneticFirstName)
|
|
253
|
+
.withValue(StructuredName.PHONETIC_MIDDLE_NAME, phoneticMiddleName)
|
|
254
|
+
.withValue(StructuredName.PHONETIC_FAMILY_NAME, phoneticLastName)
|
|
255
|
+
.withValue(StructuredName.PREFIX, prefix).withValue(StructuredName.SUFFIX, suffix)
|
|
256
|
+
ops.add(op.build())
|
|
257
|
+
op = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
|
|
258
|
+
.withSelection(selection, selectionArgs)
|
|
259
|
+
.withValue(CommonDataKinds.Organization.COMPANY, company)
|
|
260
|
+
.withValue(CommonDataKinds.Organization.TITLE, jobTitle)
|
|
261
|
+
.withValue(CommonDataKinds.Organization.DEPARTMENT, department)
|
|
262
|
+
ops.add(op.build())
|
|
263
|
+
op = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
|
|
264
|
+
.withSelection(selection, arrayOf(contactId, CommonDataKinds.Note.CONTENT_ITEM_TYPE))
|
|
265
|
+
.withValue(CommonDataKinds.Note.NOTE, note)
|
|
266
|
+
ops.add(op.build())
|
|
267
|
+
op.withYieldAllowed(true)
|
|
268
|
+
if (!TextUtils.isEmpty(photoUri) || !TextUtils.isEmpty(rawPhotoUri)) {
|
|
269
|
+
val photo = getThumbnailBitmap(if (TextUtils.isEmpty(rawPhotoUri)) photoUri else rawPhotoUri)
|
|
270
|
+
ops.add(
|
|
271
|
+
ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
|
|
272
|
+
.withSelection(selection, arrayOf(rawContactId, CommonDataKinds.Photo.CONTENT_ITEM_TYPE))
|
|
273
|
+
.build()
|
|
274
|
+
)
|
|
275
|
+
ops.add(
|
|
276
|
+
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
|
277
|
+
.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId)
|
|
278
|
+
.withValue(Columns.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
|
|
279
|
+
.withValue(CommonDataKinds.Photo.PHOTO, toByteArray(photo))
|
|
280
|
+
.build()
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
for (map in baseModels) {
|
|
284
|
+
for (item in map) {
|
|
285
|
+
ops.add(item.getDeleteOperation(rawContactId!!))
|
|
286
|
+
ops.add(item.getInsertOperation(rawContactId))
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return ops
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
private val baseModels: Array<List<BaseModel>>
|
|
293
|
+
get() = arrayOf(dates, emails, imAddresses, phones, addresses, relationships, urlAddresses, extraNames)
|
|
294
|
+
|
|
295
|
+
// convert to react native object
|
|
296
|
+
@Throws(ParseException::class)
|
|
297
|
+
fun toMap(fieldSet: Set<String>): Bundle {
|
|
298
|
+
val contact = Bundle().apply {
|
|
299
|
+
putString("lookupKey", lookupKey)
|
|
300
|
+
putString("id", contactId)
|
|
301
|
+
putString("name", if (!displayName.isNullOrEmpty()) displayName else "$firstName $lastName")
|
|
302
|
+
|
|
303
|
+
firstName
|
|
304
|
+
?.takeIf(String::isNotEmpty)
|
|
305
|
+
?.let { putString("firstName", it) }
|
|
306
|
+
|
|
307
|
+
middleName
|
|
308
|
+
?.takeIf(String::isNotEmpty)
|
|
309
|
+
?.let { putString("middleName", it) }
|
|
310
|
+
|
|
311
|
+
lastName
|
|
312
|
+
?.takeIf(String::isNotEmpty)
|
|
313
|
+
?.let { putString("lastName", it) }
|
|
314
|
+
|
|
315
|
+
suffix
|
|
316
|
+
?.takeIf(String::isNotEmpty)
|
|
317
|
+
?.let { putString("nameSuffix", it) }
|
|
318
|
+
|
|
319
|
+
prefix
|
|
320
|
+
?.takeIf(String::isNotEmpty)
|
|
321
|
+
?.let { putString("namePrefix", it) }
|
|
322
|
+
|
|
323
|
+
phoneticFirstName
|
|
324
|
+
?.takeIf(String::isNotEmpty)
|
|
325
|
+
?.let { putString("phoneticFirstName", it) }
|
|
326
|
+
|
|
327
|
+
phoneticLastName
|
|
328
|
+
?.takeIf(String::isNotEmpty)
|
|
329
|
+
?.let { putString("phoneticLastName", it) }
|
|
330
|
+
|
|
331
|
+
phoneticMiddleName
|
|
332
|
+
?.takeIf(String::isNotEmpty)
|
|
333
|
+
?.let { putString("phoneticMiddleName", it) }
|
|
334
|
+
|
|
335
|
+
putString("contactType", contactType)
|
|
336
|
+
|
|
337
|
+
company
|
|
338
|
+
?.takeIf(String::isNotEmpty)
|
|
339
|
+
?.let { putString("company", it) }
|
|
340
|
+
|
|
341
|
+
jobTitle
|
|
342
|
+
?.takeIf(String::isNotEmpty)
|
|
343
|
+
?.let { putString("jobTitle", it) }
|
|
344
|
+
|
|
345
|
+
department
|
|
346
|
+
?.takeIf(String::isNotEmpty)
|
|
347
|
+
?.let { putString("department", it) }
|
|
348
|
+
|
|
349
|
+
putBoolean("imageAvailable", hasPhoto)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (fieldSet.contains("image") && photoUri != null) {
|
|
353
|
+
contact.putBundle(
|
|
354
|
+
"image",
|
|
355
|
+
Bundle().apply {
|
|
356
|
+
putString("uri", photoUri)
|
|
357
|
+
}
|
|
358
|
+
)
|
|
359
|
+
}
|
|
360
|
+
if (fieldSet.contains("rawImage") && rawPhotoUri != null) {
|
|
361
|
+
contact.putBundle(
|
|
362
|
+
"image",
|
|
363
|
+
Bundle().apply {
|
|
364
|
+
putString("uri", rawPhotoUri)
|
|
365
|
+
}
|
|
366
|
+
)
|
|
367
|
+
}
|
|
368
|
+
if (fieldSet.contains("note") && !TextUtils.isEmpty(note)) {
|
|
369
|
+
contact.putString("note", note)
|
|
370
|
+
}
|
|
371
|
+
if (fieldSet.contains("phoneNumbers") && phones.size > 0) {
|
|
372
|
+
contact.putParcelableArrayList("phoneNumbers", ArrayList(phones.map { it.map }))
|
|
373
|
+
}
|
|
374
|
+
if (fieldSet.contains("emails") && emails.size > 0) {
|
|
375
|
+
contact.putParcelableArrayList("emails", ArrayList(emails.map { it.map }))
|
|
376
|
+
}
|
|
377
|
+
if (fieldSet.contains("addresses") && addresses.size > 0) {
|
|
378
|
+
contact.putParcelableArrayList("addresses", ArrayList(addresses.map { it.map }))
|
|
379
|
+
}
|
|
380
|
+
if (fieldSet.contains("instantMessageAddresses") && imAddresses.size > 0) {
|
|
381
|
+
contact.putParcelableArrayList("instantMessageAddresses", ArrayList(imAddresses.map { it.map }))
|
|
382
|
+
}
|
|
383
|
+
if (fieldSet.contains("urlAddresses") && urlAddresses.size > 0) {
|
|
384
|
+
contact.putParcelableArrayList("urlAddresses", ArrayList(urlAddresses.map { it.map }))
|
|
385
|
+
}
|
|
386
|
+
if (fieldSet.contains("relationships") && relationships.size > 0) {
|
|
387
|
+
contact.putParcelableArrayList("relationships", ArrayList(relationships.map { it.map }))
|
|
388
|
+
}
|
|
389
|
+
if (extraNames.size > 0) {
|
|
390
|
+
val showNickname = fieldSet.contains("nickname")
|
|
391
|
+
val showMaidenName = fieldSet.contains("maidenName")
|
|
392
|
+
for (i in extraNames.indices) {
|
|
393
|
+
val item = extraNames[i]
|
|
394
|
+
val data = item.data
|
|
395
|
+
val label = item.label
|
|
396
|
+
if (showMaidenName && label != null && label == "maidenName" && !TextUtils.isEmpty(data)) {
|
|
397
|
+
contact.putString(label, data)
|
|
398
|
+
}
|
|
399
|
+
if (showNickname && label != null && label == "nickname" && !TextUtils.isEmpty(data)) {
|
|
400
|
+
contact.putString(label, data)
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
val showBirthday = fieldSet.contains("birthday")
|
|
405
|
+
val showDates = fieldSet.contains("dates")
|
|
406
|
+
if (showDates || showBirthday) { // double if check with query with cursor
|
|
407
|
+
var hasYear: Boolean
|
|
408
|
+
val rawDatesArray = ArrayList<Bundle?>()
|
|
409
|
+
val datesArray = ArrayList<Bundle?>()
|
|
410
|
+
for (item in dates) {
|
|
411
|
+
val calendar = Calendar.getInstance()
|
|
412
|
+
val datePattern = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
|
413
|
+
val noYearPattern = SimpleDateFormat("--MM-dd", Locale.getDefault())
|
|
414
|
+
val details = Bundle()
|
|
415
|
+
val dateString = item.data
|
|
416
|
+
val label = item.label
|
|
417
|
+
val rawDate = Bundle().apply {
|
|
418
|
+
putString("type", label)
|
|
419
|
+
putString("value", dateString)
|
|
420
|
+
}
|
|
421
|
+
rawDatesArray.add(rawDate)
|
|
422
|
+
try {
|
|
423
|
+
hasYear = !dateString!!.startsWith("--")
|
|
424
|
+
if (hasYear) {
|
|
425
|
+
calendar.time = datePattern.parse(dateString)!!
|
|
426
|
+
} else {
|
|
427
|
+
calendar.time = noYearPattern.parse(dateString)!!
|
|
428
|
+
}
|
|
429
|
+
if (hasYear) {
|
|
430
|
+
details.putInt("year", calendar[Calendar.YEAR])
|
|
431
|
+
}
|
|
432
|
+
details.putInt("month", calendar[Calendar.MONTH])
|
|
433
|
+
details.putInt("day", calendar[Calendar.DAY_OF_MONTH])
|
|
434
|
+
// TODO: Evan: The type is only supported in 26+
|
|
435
|
+
details.putString("format", "gregorian")
|
|
436
|
+
if (showBirthday && label != null && label == "birthday") {
|
|
437
|
+
contact.putBundle("birthday", details)
|
|
438
|
+
} else {
|
|
439
|
+
details.putString("label", label)
|
|
440
|
+
datesArray.add(details)
|
|
441
|
+
}
|
|
442
|
+
} catch (e: Exception) {
|
|
443
|
+
Log.w("Contact", e.toString())
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (showDates) {
|
|
447
|
+
if (datesArray.size > 0) {
|
|
448
|
+
contact.putParcelableArrayList("dates", datesArray)
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (rawDatesArray.size > 0) {
|
|
452
|
+
contact.putParcelableArrayList("rawDates", rawDatesArray)
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return contact
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
val contentValues: ArrayList<ContentValues>
|
|
459
|
+
get() {
|
|
460
|
+
val contactData = ArrayList<ContentValues>()
|
|
461
|
+
val name = ContentValues().apply {
|
|
462
|
+
put(ContactsContract.Contacts.Data.MIMETYPE, CommonDataKinds.Identity.CONTENT_ITEM_TYPE)
|
|
463
|
+
put(StructuredName.GIVEN_NAME, firstName)
|
|
464
|
+
put(StructuredName.MIDDLE_NAME, middleName)
|
|
465
|
+
put(StructuredName.FAMILY_NAME, lastName)
|
|
466
|
+
put(StructuredName.PREFIX, prefix)
|
|
467
|
+
put(StructuredName.SUFFIX, suffix)
|
|
468
|
+
put(StructuredName.PHONETIC_GIVEN_NAME, phoneticFirstName)
|
|
469
|
+
put(StructuredName.PHONETIC_MIDDLE_NAME, phoneticMiddleName)
|
|
470
|
+
put(StructuredName.PHONETIC_FAMILY_NAME, phoneticLastName)
|
|
471
|
+
}
|
|
472
|
+
contactData.add(name)
|
|
473
|
+
|
|
474
|
+
val organization = ContentValues().apply {
|
|
475
|
+
put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
|
|
476
|
+
put(CommonDataKinds.Organization.COMPANY, company)
|
|
477
|
+
put(CommonDataKinds.Organization.TITLE, jobTitle)
|
|
478
|
+
put(CommonDataKinds.Organization.DEPARTMENT, department)
|
|
479
|
+
}
|
|
480
|
+
contactData.add(organization)
|
|
481
|
+
|
|
482
|
+
val notes = ContentValues().apply {
|
|
483
|
+
put(ContactsContract.Data.MIMETYPE, CommonDataKinds.Note.CONTENT_ITEM_TYPE)
|
|
484
|
+
put(CommonDataKinds.Note.NOTE, note)
|
|
485
|
+
}
|
|
486
|
+
contactData.add(notes)
|
|
487
|
+
|
|
488
|
+
if (!photoUri.isNullOrBlank()) {
|
|
489
|
+
val photo = getThumbnailBitmap(Uri.parse(photoUri).path)
|
|
490
|
+
val image = ContentValues().apply {
|
|
491
|
+
put(Columns.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
|
|
492
|
+
put(CommonDataKinds.Photo.PHOTO, toByteArray(photo))
|
|
493
|
+
}
|
|
494
|
+
contactData.add(image)
|
|
495
|
+
}
|
|
496
|
+
if (!rawPhotoUri.isNullOrBlank()) {
|
|
497
|
+
val photo = getThumbnailBitmap(rawPhotoUri)
|
|
498
|
+
val image = ContentValues().apply {
|
|
499
|
+
put(Columns.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
|
|
500
|
+
put(CommonDataKinds.Photo.PHOTO, toByteArray(photo))
|
|
501
|
+
}
|
|
502
|
+
contactData.add(image)
|
|
503
|
+
}
|
|
504
|
+
for (map in baseModels) {
|
|
505
|
+
for (item in map) {
|
|
506
|
+
contactData.add(item.contentValues)
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return contactData
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
private fun getThumbnailBitmap(photoUri: String?): Bitmap {
|
|
513
|
+
val path = Uri.parse(photoUri).path
|
|
514
|
+
return BitmapFactory.decodeFile(path)
|
|
515
|
+
}
|
|
516
|
+
}
|