Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple-Contacts: Added support for relations #1649

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import com.simplemobiletools.commons.models.contacts.LocalContact
import com.simplemobiletools.commons.interfaces.GroupsDao
import java.util.concurrent.Executors

@Database(entities = [LocalContact::class, Group::class], version = 3)
@Database(entities = [LocalContact::class, Group::class], version = 4)
@TypeConverters(Converters::class)
abstract class ContactsDatabase : RoomDatabase() {

Expand All @@ -41,6 +41,7 @@ abstract class ContactsDatabase : RoomDatabase() {
})
.addMigrations(MIGRATION_1_2)
.addMigrations(MIGRATION_2_3)
.addMigrations(MIGRATION_3_4)
.build()
}
}
Expand Down Expand Up @@ -86,5 +87,14 @@ abstract class ContactsDatabase : RoomDatabase() {
}
}
}

private val MIGRATION_3_4 = object : Migration(3, 4) {
override fun migrate(database: SupportSQLiteDatabase) {
database.apply {
execSQL("ALTER TABLE contacts ADD COLUMN relations TEXT NOT NULL DEFAULT ''")
}
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fun Context.getEmptyContact(): Contact {
val organization = Organization("", "")
return Contact(
0, "", "", "", "", "", "", "", ArrayList(), ArrayList(), ArrayList(), ArrayList(), originalContactSource, 0, 0, "",
null, "", ArrayList(), organization, ArrayList(), ArrayList(), DEFAULT_MIMETYPE, null
null, "", ArrayList(), organization, ArrayList(), ArrayList(), ArrayList(), DEFAULT_MIMETYPE, null
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,13 +571,15 @@ const val SHOW_WEBSITES_FIELD = 8192
const val SHOW_NICKNAME_FIELD = 16384
const val SHOW_IMS_FIELD = 32768
const val SHOW_RINGTONE_FIELD = 65536
const val SHOW_RELATIONS_FIELD = (1 shl 17)

const val DEFAULT_EMAIL_TYPE = ContactsContract.CommonDataKinds.Email.TYPE_HOME
const val DEFAULT_PHONE_NUMBER_TYPE = ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE
const val DEFAULT_ADDRESS_TYPE = ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME
const val DEFAULT_EVENT_TYPE = ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY
const val DEFAULT_ORGANIZATION_TYPE = ContactsContract.CommonDataKinds.Organization.TYPE_WORK
const val DEFAULT_WEBSITE_TYPE = ContactsContract.CommonDataKinds.Website.TYPE_HOMEPAGE
const val DEFAULT_RELATION_TYPE = ContactsContract.CommonDataKinds.Relation.TYPE_FRIEND
const val DEFAULT_IM_TYPE = ContactsContract.CommonDataKinds.Im.PROTOCOL_SKYPE
const val DEFAULT_MIMETYPE = ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE

Expand Down Expand Up @@ -623,6 +625,7 @@ fun getEmptyLocalContact() = LocalContact(
"",
ArrayList(),
ArrayList(),
ArrayList(),
null
)

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Converters {
private val addressType = object : TypeToken<List<Address>>() {}.type
private val eventType = object : TypeToken<List<Event>>() {}.type
private val imType = object : TypeToken<List<IM>>() {}.type
private val relationType = object : TypeToken<List<ContactRelation>>() {}.type

@TypeConverter
fun jsonToStringList(value: String) = gson.fromJson<ArrayList<String>>(value, stringType)
Expand Down Expand Up @@ -73,4 +74,13 @@ class Converters {

@TypeConverter
fun IMsListToJson(list: ArrayList<IM>) = gson.toJson(list)

@TypeConverter
fun jsonToRelationList(value: String): ArrayList<ContactRelation> {
return (gson.fromJson<ArrayList<ContactRelation>>(value, relationType) ?: ArrayList())
}

@TypeConverter
fun relationListToJson(list: ArrayList<ContactRelation>) = gson.toJson(list)

}
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class LocalContactsHelper(val context: Context) {
groups = storedGroups.filter { localContact.groups.contains(it.id) } as ArrayList<Group>
organization = Organization(localContact.company, localContact.jobPosition)
websites = localContact.websites
relations = localContact.relations
IMs = localContact.IMs
ringtone = localContact.ringtone
}
Expand Down Expand Up @@ -150,6 +151,7 @@ class LocalContactsHelper(val context: Context) {
company = contact.organization.company
jobPosition = contact.organization.jobPosition
websites = contact.websites
relations = contact.relations
IMs = contact.IMs
ringtone = contact.ringtone
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,21 @@ data class Contact(
var suffix: String= "",
var nickname: String= "",
var photoUri: String= "",
var phoneNumbers: ArrayList<PhoneNumber> = arrayListOf(),
var emails: ArrayList<Email> = arrayListOf(),
var addresses: ArrayList<Address> = arrayListOf(),
var events: ArrayList<Event> = arrayListOf(),
var phoneNumbers: ArrayList<PhoneNumber> = ArrayList(),
var emails: ArrayList<Email> = ArrayList(),
var addresses: ArrayList<Address> = ArrayList(),
var events: ArrayList<Event> = ArrayList(),
var source: String= "",
var starred: Int = 0,
var contactId: Int,
var thumbnailUri: String= "",
var photo: Bitmap? = null,
var notes: String= "",
var groups: ArrayList<Group> = arrayListOf(),
var groups: ArrayList<Group> = ArrayList(),
var organization: Organization = Organization("",""),
var websites: ArrayList<String> = arrayListOf(),
var IMs: ArrayList<IM> = arrayListOf(),
var websites: ArrayList<String> = ArrayList(),
var relations: ArrayList<ContactRelation> = ArrayList(),
var IMs: ArrayList<IM> = ArrayList(),
var mimetype: String = "",
var ringtone: String? = ""
) : Comparable<Contact> {
Expand Down Expand Up @@ -184,6 +185,7 @@ data class Contact(
groups = ArrayList(),
websites = ArrayList(),
organization = Organization("", ""),
relations= ArrayList(),
IMs = ArrayList(),
ringtone = ""
).toString()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/* *********************************************************************
* *
* ContactRelation.kt *
* *
***********************************************************************
* 321098765432109876543210987654321+123456789012345678901234567890123 *
*
* This file is part of "Simple Mobile Tools"
* https://www.simplemobiletools.com/
* https://github.com/SimpleMobileTools
*/
/**
* ContactRelation is a Kotlin class designed to store information about
* a relations between the person in a contact and other persons.
*
* The stored information consists of
* .) the name of the other person
* .) standardized relation information (e.g. FATHER, DAUGHTER, FRIEND)
* .) a label containing additional type information if the
* standardized type is CUSTOM (and an empty string otherwise)
*
* The Android Contacts Provider system for managing contact information
* also has a similar structure for relations:
* See: https://developer.android.com/guide/topics/providers/contacts-provider
* https://developer.android.com/reference/android/provider/ContactsContract.CommonDataKinds.Relation
* The supported data fields are:
* Relation.NAME (= ContactsContract.DataColumns.DATA1)
* Relation.TYPE (= ContactsContract.DataColumns.DATA2)
* Relation.LABEL (= ContactsContract.DataColumns.DATA3) (Description if (TYPE == TYPE_CUSTOM))
*
* Note: The 'type' field in the ContactRelation is an integer, when it
* actually should be an enum. This is due to the fact that Android also
* uses plain integers to store the Relation.TYPE field.
* Available values are:
* ContactsContract.CommonDataKinds.Relation.TYPE_CUSTOM (= 0)
* ContactsContract.CommonDataKinds.Relation.TYPE_ASSISTANT (= 1)
* ContactsContract.CommonDataKinds.Relation.TYPE_BROTHER (= 2)
* ContactsContract.CommonDataKinds.Relation.TYPE_CHILD (= 3)
* ContactsContract.CommonDataKinds.Relation.TYPE_DOMESTIC_PARTNER (= 4)
* ContactsContract.CommonDataKinds.Relation.TYPE_FATHER (= 5)
* ContactsContract.CommonDataKinds.Relation.TYPE_FRIEND (= 6)
* ContactsContract.CommonDataKinds.Relation.TYPE_MANAGER (= 7)
* ContactsContract.CommonDataKinds.Relation.TYPE_MOTHER (= 8)
* ContactsContract.CommonDataKinds.Relation.TYPE_PARENT (= 9)
* ContactsContract.CommonDataKinds.Relation.TYPE_PARTNER (=10)
* ContactsContract.CommonDataKinds.Relation.TYPE_REFERRED_BY (=11)
* ContactsContract.CommonDataKinds.Relation.TYPE_RELATIVE (=12)
* ContactsContract.CommonDataKinds.Relation.TYPE_SISTER (=13)
* ContactsContract.CommonDataKinds.Relation.TYPE_SPOUSE (=14)
* (requires: import android.provider.ContactsContract)
*
* The structure of relations between persons is also reflected in the
* vCard 4.0 standard. See: https://www.rfc-editor.org/rfc/rfc6350#section-6.6.6
* Section 6.6.6. - vCard Item "RELATED"
* To specify a relationship between another entity and the
* entity represented by this vCard..
* vCard supports the following kinds of relations:
* "contact" / "acquaintance" / "friend" / "met" / "co-worker" /
* "colleague" / "co-resident" / "neighbor" / "child" / "parent" /
* "sibling" / "spouse" / "kin" / "muse" / "crush" / "date" /
* "sweetheart" / "me" / "agent" / "emergency"
* Thus while there are some types of relations that are common to Android
* and vCard there is no 1-to-1 mapping. Additionally vCard permits URLs
* as target information (e.g. links to vCards of the target persons),
* while Android expects a plain name. All programs that try to synchronize
* Android contacts via CardDAV will run in interesting transcoding issues...
*
**********************************************************************/

package com.simplemobiletools.commons.models.contacts

data class ContactRelation(var name: String, var type: Int, var label: String) {
fun deepCopy(): ContactRelation = ContactRelation(name, type, label)
companion object {
/**
* Relation Types:
*
* Android defines only a few types of 'standard'-relations, that
* are just copied to TYPE_* #1..14. Most 'advanced' types of
* relations must be handled using TYPE_CUSTOM.
*
* vCard defines some more types (TYPE_* #51..66 plus some duplicates
* from Android), but these too show wide gaps. Note that vCard
* does NOT support a 'TYPE_CUSTOM' equivalent. Thus all types
* that are not in the vCard standard must somehow be squeezed
* into these types when exporting an Android contact to vCard/CardDAV.
*
* For our purposes we shall define many more types (in particular
* family relations and some business hierarchy). These types are
* neither part of the standard Android types of relations nor
* part of the vCard standard. Thus we can not store these types
* directly to Android or vCard. For Android, we can use the escape
* route via TYPE_CUSTOM and LABEL. For vCard we need to coerce
* these types to standard vCard types, generally under a severe
* loss of precision (e.g. 'grandfather', 'brother-in-law' and 'aunt'
* all map to 'kin').
*
* Note: These constants are defined as plain integers, rather
* than as enum, since the Android type field is also an integer.
*/
// Relation types defined in Android: ContactContract.Relation
const val TYPE_CUSTOM: Int = 0
const val TYPE_ASSISTANT: Int = 1
const val TYPE_BROTHER: Int = 2
const val TYPE_CHILD: Int = 3
const val TYPE_DOMESTIC_PARTNER = 4
const val TYPE_FATHER: Int = 5
const val TYPE_FRIEND: Int = 6
const val TYPE_MANAGER: Int = 7
const val TYPE_MOTHER: Int = 8
const val TYPE_PARENT: Int = 9
const val TYPE_PARTNER: Int = 10
const val TYPE_REFERRED_BY: Int = 11
const val TYPE_RELATIVE: Int = 12
const val TYPE_SISTER: Int = 13
const val TYPE_SPOUSE: Int = 14

// Relation types defined in vCard 4.0
const val TYPE_CONTACT: Int = 51
const val TYPE_ACQUAINTANCE: Int = 52
// const val TYPE_FRIEND: Int = 6
const val TYPE_MET: Int = 53
const val TYPE_CO_WORKER: Int = 54
const val TYPE_COLLEAGUE: Int = 55
const val TYPE_CO_RESIDENT: Int = 56
const val TYPE_NEIGHBOR: Int = 57
// const val TYPE_CHILD: Int = 3
// const val TYPE_PARENT: Int = 9
const val TYPE_SIBLING: Int = 58
// const val TYPE_SPOUSE: Int = 14
const val TYPE_KIN: Int = 59
const val TYPE_MUSE: Int = 60
const val TYPE_CRUSH: Int = 61
const val TYPE_DATE: Int = 62
const val TYPE_SWEETHEART: Int = 63
const val TYPE_ME: Int = 64
const val TYPE_AGENT: Int = 65
const val TYPE_EMERGENCY: Int = 66

// Additional custom types
const val TYPE_SUPERIOR: Int = 101
const val TYPE_SUBORDINATE: Int = 102
const val TYPE_HUSBAND: Int = 103
const val TYPE_WIFE: Int = 104
const val TYPE_SON: Int = 105
const val TYPE_DAUGHTER: Int = 106
const val TYPE_GRANDPARENT: Int = 107
const val TYPE_GRANDFATHER: Int = 108
const val TYPE_GRANDMOTHER: Int = 109
const val TYPE_GRANDCHILD: Int = 110
const val TYPE_GRANDSON: Int = 111
const val TYPE_GRANDDAUGHTER: Int = 112
const val TYPE_UNCLE: Int = 113
const val TYPE_AUNT: Int = 114
const val TYPE_NEPHEW: Int = 115
const val TYPE_NIECE: Int = 116
const val TYPE_FATHER_IN_LAW: Int = 117
const val TYPE_MOTHER_IN_LAW: Int = 118
const val TYPE_SON_IN_LAW: Int = 119
const val TYPE_DAUGHTER_IN_LAW: Int = 120
const val TYPE_BROTHER_IN_LAW: Int = 121
const val TYPE_SISTER_IN_LAW: Int = 122
} // companion object
} // class ContactRelation
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ data class LocalContact(
@ColumnInfo(name = "company") var company: String,
@ColumnInfo(name = "job_position") var jobPosition: String,
@ColumnInfo(name = "websites") var websites: ArrayList<String>,
@ColumnInfo(name = "relations") var relations: ArrayList<ContactRelation>,
@ColumnInfo(name = "ims") var IMs: ArrayList<IM>,
@ColumnInfo(name = "ringtone") var ringtone: String?
) {
Expand Down
62 changes: 61 additions & 1 deletion commons/src/main/res/values-ar/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,66 @@
<string name="home_fax">فاكس المنزل</string>
<string name="pager">بايجر</string>
<string name="no_phone_number_found">لم يتم العثور على رقم هاتف</string>
<!-- Relation types -->
<string name="relation">Relation</string>
<string name="relations">Related Persons</string>
<string name="relation_hint">Related Person</string>
<!-- Android Relation types -->
<string name="relation_assistant">Assistant</string>
<string name="relation_brother">Brother</string>
<string name="relation_child">Child</string>
<string name="relation_domestic_partner">Domestic Partner</string>
<string name="relation_father">Father</string>
<string name="relation_friend">Friend</string>
<string name="relation_manager">Manager</string>
<string name="relation_mother">Mother</string>
<string name="relation_parent">Parent</string>
<string name="relation_partner">Partner</string>
<string name="relation_referred_by">Referred by</string>
<string name="relation_relative">Relative</string>
<string name="relation_sister">Sister</string>
<string name="relation_spouse">Spouse</string>
<!-- vCard 4.0 Relation types -->
<string name="relation_contact">Contact</string>
<string name="relation_acquaintance">Acquaintance</string>
<string name="relation_met">Met</string>
<string name="relation_co_worker">Co-worker</string>
<string name="relation_colleague">Colleague</string>
<string name="relation_co_resident">Co-resident</string>
<string name="relation_neighbor">Neighbor</string>
<string name="relation_sibling">Sibling</string>
<string name="relation_kin">Kin</string>
<string name="relation_kin_alt">Relative</string>
<string name="relation_muse">Muse</string>
<string name="relation_crush">Crush</string>
<string name="relation_date">Date</string>
<string name="relation_sweetheart">Sweetheart</string>
<string name="relation_agent">Agent</string>
<string name="relation_emergency">Emergency Contact</string>
<string name="relation_me">Me</string>
<!-- Additional Relation types -->
<string name="relation_superior">Boss</string>
<string name="relation_subordinate">Subordinate</string>
<string name="relation_husband">Husband</string>
<string name="relation_wife">Wife</string>
<string name="relation_son">Son</string>
<string name="relation_daughter">Daughter</string>
<string name="relation_grandparent">Grandparent</string>
<string name="relation_grandfather">Grandfather</string>
<string name="relation_grandmother">Grandmother</string>
<string name="relation_grandchild">Grandchild</string>
<string name="relation_grandson">Grandson</string>
<string name="relation_granddaughter">Granddaughter</string>
<string name="relation_uncle">Uncle</string>
<string name="relation_aunt">Aunt</string>
<string name="relation_nephew">Nephew</string>
<string name="relation_niece">Niece</string>
<string name="relation_father_in_law">Father-in-law</string>
<string name="relation_mother_in_law">Mother-in-law</string>
<string name="relation_son_in_law">Son-in-law</string>
<string name="relation_daughter_in_law">Daughter-in-law</string>
<string name="relation_brother_in_law">Brother-in-law</string>
<string name="relation_sister_in_law">Sister-in-law</string>
<!-- View types -->
<string name="change_view_type">تغيير نوع العرض</string>
<string name="grid">الشبكة</string>
Expand Down Expand Up @@ -1234,4 +1294,4 @@
<string name="pro_app_refund">لا تنسى أنه إذا قمت بإلغاء تثبيت أي تطبيق مدفوع في غضون ساعتين ، فسيتم إسترداد أموالك تلقائياً. إذا كنت تريد إسترداد الأموال في أي وقت لاحق ، فأتصل بنا على [email protected] وستحصل عليه. هذا يجعل من السهل تجربته :)</string>
<!-- Description of our developer profile on Google Play, it can have max 140 characters -->
<string name="developer_description">مجموعة بسيطة، من تطبيقات أندرويد المفتوحة المصدر ذات الودجات القابلة للتخصيص، بدون إعلانات أو أذونات غير ضرورية.</string>
</resources>
</resources>
Loading