diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ContactActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ContactActivity.kt index 27f08382..4e8b5fd0 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ContactActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ContactActivity.kt @@ -24,12 +24,12 @@ import com.bumptech.glide.request.target.Target import com.simplemobiletools.commons.dialogs.ConfirmationDialog import com.simplemobiletools.commons.dialogs.RadioGroupDialog import com.simplemobiletools.commons.extensions.* +import com.simplemobiletools.commons.helpers.ContactsHelper import com.simplemobiletools.commons.helpers.letterBackgroundColors import com.simplemobiletools.commons.models.RadioItem import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.extensions.shareContacts -import com.simplemobiletools.contacts.pro.helpers.ContactsHelper -import com.simplemobiletools.contacts.pro.models.Contact +import com.simplemobiletools.commons.models.contacts.Contact abstract class ContactActivity : SimpleActivity() { protected val PICK_RINGTONE_INTENT_ID = 1500 diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/EditContactActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/EditContactActivity.kt index e65c1c2a..10c475fd 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/EditContactActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/EditContactActivity.kt @@ -29,6 +29,10 @@ import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.* import com.simplemobiletools.commons.models.PhoneNumber import com.simplemobiletools.commons.models.RadioItem +import com.simplemobiletools.commons.models.contacts.* +import com.simplemobiletools.commons.models.contacts.Email +import com.simplemobiletools.commons.models.contacts.Event +import com.simplemobiletools.commons.models.contacts.Organization import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.dialogs.CustomLabelDialog import com.simplemobiletools.contacts.pro.dialogs.ManageVisibleFieldsDialog @@ -36,10 +40,6 @@ import com.simplemobiletools.contacts.pro.dialogs.MyDatePickerDialog import com.simplemobiletools.contacts.pro.dialogs.SelectGroupsDialog import com.simplemobiletools.contacts.pro.extensions.* import com.simplemobiletools.contacts.pro.helpers.* -import com.simplemobiletools.contacts.pro.models.* -import com.simplemobiletools.contacts.pro.models.Email -import com.simplemobiletools.contacts.pro.models.Event -import com.simplemobiletools.contacts.pro.models.Organization import kotlinx.android.synthetic.main.activity_edit_contact.* import kotlinx.android.synthetic.main.item_edit_address.view.* import kotlinx.android.synthetic.main.item_edit_email.view.* diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/GroupContactsActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/GroupContactsActivity.kt index 1e8e2120..55618e44 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/GroupContactsActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/GroupContactsActivity.kt @@ -7,19 +7,18 @@ import android.net.Uri import android.os.Bundle import androidx.coordinatorlayout.widget.CoordinatorLayout import com.simplemobiletools.commons.extensions.* +import com.simplemobiletools.commons.helpers.ContactsHelper import com.simplemobiletools.commons.helpers.NavigationIcon import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.adapters.ContactsAdapter import com.simplemobiletools.contacts.pro.dialogs.SelectContactsDialog import com.simplemobiletools.contacts.pro.extensions.* -import com.simplemobiletools.contacts.pro.helpers.ContactsHelper import com.simplemobiletools.contacts.pro.helpers.GROUP import com.simplemobiletools.contacts.pro.helpers.LOCATION_GROUP_CONTACTS import com.simplemobiletools.contacts.pro.interfaces.RefreshContactsListener import com.simplemobiletools.contacts.pro.interfaces.RemoveFromGroupListener -import com.simplemobiletools.contacts.pro.models.Contact -import com.simplemobiletools.contacts.pro.models.Group +import com.simplemobiletools.commons.models.contacts.* import kotlinx.android.synthetic.main.activity_group_contacts.* class GroupContactsActivity : SimpleActivity(), RemoveFromGroupListener, RefreshContactsListener { diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/InsertOrEditContactActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/InsertOrEditContactActivity.kt index 7d3c0e6d..71773824 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/InsertOrEditContactActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/InsertOrEditContactActivity.kt @@ -13,19 +13,17 @@ import android.widget.TextView import androidx.viewpager.widget.ViewPager import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.* +import com.simplemobiletools.commons.models.contacts.* import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.adapters.ViewPagerAdapter import com.simplemobiletools.contacts.pro.dialogs.ChangeSortingDialog import com.simplemobiletools.contacts.pro.dialogs.FilterContactSourcesDialog import com.simplemobiletools.contacts.pro.extensions.config -import com.simplemobiletools.contacts.pro.extensions.getContactPublicUri import com.simplemobiletools.contacts.pro.fragments.MyViewPagerFragment import com.simplemobiletools.contacts.pro.helpers.ADD_NEW_CONTACT_NUMBER -import com.simplemobiletools.contacts.pro.helpers.ContactsHelper import com.simplemobiletools.contacts.pro.helpers.KEY_EMAIL import com.simplemobiletools.contacts.pro.helpers.KEY_NAME import com.simplemobiletools.contacts.pro.interfaces.RefreshContactsListener -import com.simplemobiletools.contacts.pro.models.Contact import kotlinx.android.synthetic.main.activity_insert_edit_contact.* import kotlinx.android.synthetic.main.fragment_contacts.* import kotlinx.android.synthetic.main.fragment_favorites.* diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/MainActivity.kt index 406d0940..122fbc6a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/MainActivity.kt @@ -14,31 +14,28 @@ import android.widget.ImageView import android.widget.TextView import android.widget.Toast import androidx.viewpager.widget.ViewPager +import com.simplemobiletools.commons.databases.ContactsDatabase import com.simplemobiletools.commons.dialogs.ConfirmationDialog import com.simplemobiletools.commons.dialogs.FilePickerDialog import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.* import com.simplemobiletools.commons.models.FAQItem import com.simplemobiletools.commons.models.Release +import com.simplemobiletools.commons.models.contacts.* import com.simplemobiletools.contacts.pro.BuildConfig import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.adapters.ViewPagerAdapter -import com.simplemobiletools.contacts.pro.databases.ContactsDatabase import com.simplemobiletools.contacts.pro.dialogs.ChangeSortingDialog import com.simplemobiletools.contacts.pro.dialogs.ExportContactsDialog import com.simplemobiletools.contacts.pro.dialogs.FilterContactSourcesDialog import com.simplemobiletools.contacts.pro.dialogs.ImportContactsDialog import com.simplemobiletools.contacts.pro.extensions.config -import com.simplemobiletools.contacts.pro.extensions.getTempFile import com.simplemobiletools.contacts.pro.extensions.handleGenericContactClick import com.simplemobiletools.contacts.pro.fragments.FavoritesFragment import com.simplemobiletools.contacts.pro.fragments.MyViewPagerFragment -import com.simplemobiletools.contacts.pro.helpers.ALL_TABS_MASK -import com.simplemobiletools.contacts.pro.helpers.ContactsHelper import com.simplemobiletools.contacts.pro.helpers.VcfExporter import com.simplemobiletools.contacts.pro.helpers.tabsList import com.simplemobiletools.contacts.pro.interfaces.RefreshContactsListener -import com.simplemobiletools.contacts.pro.models.Contact import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.fragment_contacts.* import kotlinx.android.synthetic.main.fragment_favorites.* diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SettingsActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SettingsActivity.kt index 33297cde..e45db403 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/SettingsActivity.kt @@ -9,9 +9,6 @@ import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.dialogs.ManageVisibleFieldsDialog import com.simplemobiletools.contacts.pro.dialogs.ManageVisibleTabsDialog import com.simplemobiletools.contacts.pro.extensions.config -import com.simplemobiletools.contacts.pro.helpers.ON_CLICK_CALL_CONTACT -import com.simplemobiletools.contacts.pro.helpers.ON_CLICK_EDIT_CONTACT -import com.simplemobiletools.contacts.pro.helpers.ON_CLICK_VIEW_CONTACT import kotlinx.android.synthetic.main.activity_settings.* import java.util.* diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ViewContactActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ViewContactActivity.kt index 44c9fbed..af8907a1 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ViewContactActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/activities/ViewContactActivity.kt @@ -22,12 +22,12 @@ import com.simplemobiletools.commons.dialogs.SelectAlarmSoundDialog import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.* import com.simplemobiletools.commons.models.PhoneNumber +import com.simplemobiletools.commons.models.contacts.* import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.dialogs.ChooseSocialDialog import com.simplemobiletools.contacts.pro.dialogs.ManageVisibleFieldsDialog import com.simplemobiletools.contacts.pro.extensions.* import com.simplemobiletools.contacts.pro.helpers.* -import com.simplemobiletools.contacts.pro.models.* import kotlinx.android.synthetic.main.activity_view_contact.* import kotlinx.android.synthetic.main.item_view_address.view.* import kotlinx.android.synthetic.main.item_view_contact_source.view.* diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/ContactsAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/ContactsAdapter.kt index 1fa90c5d..311dca86 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/ContactsAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/ContactsAdapter.kt @@ -28,10 +28,12 @@ import com.simplemobiletools.commons.dialogs.ConfirmationDialog import com.simplemobiletools.commons.dialogs.RadioGroupDialog import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.* +import com.simplemobiletools.commons.helpers.ContactsHelper import com.simplemobiletools.commons.interfaces.ItemMoveCallback import com.simplemobiletools.commons.interfaces.ItemTouchHelperContract import com.simplemobiletools.commons.interfaces.StartReorderDragListener import com.simplemobiletools.commons.models.RadioItem +import com.simplemobiletools.commons.models.contacts.Contact import com.simplemobiletools.commons.views.MyRecyclerView import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.activities.SimpleActivity @@ -41,7 +43,7 @@ import com.simplemobiletools.contacts.pro.extensions.* import com.simplemobiletools.contacts.pro.helpers.* import com.simplemobiletools.contacts.pro.interfaces.RefreshContactsListener import com.simplemobiletools.contacts.pro.interfaces.RemoveFromGroupListener -import com.simplemobiletools.contacts.pro.models.Contact +import com.simplemobiletools.commons.models.contacts.* import java.util.* class ContactsAdapter( diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/FilterContactSourcesAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/FilterContactSourcesAdapter.kt index d924829b..ceb5f091 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/FilterContactSourcesAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/FilterContactSourcesAdapter.kt @@ -7,9 +7,9 @@ import com.simplemobiletools.commons.extensions.getProperBackgroundColor import com.simplemobiletools.commons.extensions.getProperPrimaryColor import com.simplemobiletools.commons.extensions.getProperTextColor import com.simplemobiletools.commons.helpers.SMT_PRIVATE +import com.simplemobiletools.commons.models.contacts.ContactSource import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.activities.SimpleActivity -import com.simplemobiletools.contacts.pro.models.ContactSource import kotlinx.android.synthetic.main.item_filter_contact_source.view.* class FilterContactSourcesAdapter( diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/GroupsAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/GroupsAdapter.kt index 75dba883..91948e64 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/GroupsAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/GroupsAdapter.kt @@ -7,9 +7,7 @@ import android.view.ViewGroup import com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter import com.simplemobiletools.commons.dialogs.ConfirmationDialog -import com.simplemobiletools.commons.extensions.beVisibleIf -import com.simplemobiletools.commons.extensions.getTextSize -import com.simplemobiletools.commons.extensions.highlightTextPart +import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.SimpleContactsHelper import com.simplemobiletools.commons.helpers.TAB_GROUPS import com.simplemobiletools.commons.helpers.ensureBackgroundThread @@ -17,11 +15,10 @@ import com.simplemobiletools.commons.views.MyRecyclerView import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.activities.SimpleActivity import com.simplemobiletools.contacts.pro.dialogs.RenameGroupDialog +import com.simplemobiletools.commons.helpers.ContactsHelper +import com.simplemobiletools.commons.models.contacts.Group import com.simplemobiletools.contacts.pro.extensions.config -import com.simplemobiletools.contacts.pro.extensions.groupsDB -import com.simplemobiletools.contacts.pro.helpers.ContactsHelper import com.simplemobiletools.contacts.pro.interfaces.RefreshContactsListener -import com.simplemobiletools.contacts.pro.models.Group import kotlinx.android.synthetic.main.item_group.view.* import java.util.* diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/SelectContactsAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/SelectContactsAdapter.kt index 50ac9a8b..d7e64b07 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/SelectContactsAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/adapters/SelectContactsAdapter.kt @@ -12,11 +12,11 @@ import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.signature.ObjectKey import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.SimpleContactsHelper +import com.simplemobiletools.commons.models.contacts.* import com.simplemobiletools.commons.views.MyRecyclerView import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.activities.SimpleActivity import com.simplemobiletools.contacts.pro.extensions.config -import com.simplemobiletools.contacts.pro.models.Contact import kotlinx.android.synthetic.main.item_add_favorite_with_number.view.* class SelectContactsAdapter( diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/contentproviders/MyContactsContentProvider.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/contentproviders/MyContactsContentProvider.kt index 193a94c5..eb54f45d 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/contentproviders/MyContactsContentProvider.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/contentproviders/MyContactsContentProvider.kt @@ -7,8 +7,8 @@ import android.database.MatrixCursor import android.net.Uri import com.google.gson.Gson import com.simplemobiletools.commons.helpers.MyContactsContentProvider +import com.simplemobiletools.commons.helpers.LocalContactsHelper import com.simplemobiletools.contacts.pro.extensions.config -import com.simplemobiletools.contacts.pro.helpers.LocalContactsHelper class MyContactsContentProvider : ContentProvider() { override fun insert(uri: Uri, contentValues: ContentValues?) = null diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/databases/ContactsDatabase.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/databases/ContactsDatabase.kt deleted file mode 100644 index b078c7e5..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/databases/ContactsDatabase.kt +++ /dev/null @@ -1,90 +0,0 @@ -package com.simplemobiletools.contacts.pro.databases - -import android.content.Context -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import androidx.room.TypeConverters -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase -import com.simplemobiletools.contacts.pro.helpers.Converters -import com.simplemobiletools.contacts.pro.helpers.FIRST_CONTACT_ID -import com.simplemobiletools.contacts.pro.helpers.FIRST_GROUP_ID -import com.simplemobiletools.contacts.pro.helpers.getEmptyLocalContact -import com.simplemobiletools.contacts.pro.interfaces.ContactsDao -import com.simplemobiletools.contacts.pro.interfaces.GroupsDao -import com.simplemobiletools.contacts.pro.models.Group -import com.simplemobiletools.contacts.pro.models.LocalContact -import java.util.concurrent.Executors - -@Database(entities = [LocalContact::class, Group::class], version = 3) -@TypeConverters(Converters::class) -abstract class ContactsDatabase : RoomDatabase() { - - abstract fun ContactsDao(): ContactsDao - - abstract fun GroupsDao(): GroupsDao - - companion object { - private var db: ContactsDatabase? = null - - fun getInstance(context: Context): ContactsDatabase { - if (db == null) { - synchronized(ContactsDatabase::class) { - if (db == null) { - db = Room.databaseBuilder(context.applicationContext, ContactsDatabase::class.java, "local_contacts.db") - .addCallback(object : Callback() { - override fun onCreate(db: SupportSQLiteDatabase) { - super.onCreate(db) - increaseAutoIncrementIds() - } - }) - .addMigrations(MIGRATION_1_2) - .addMigrations(MIGRATION_2_3) - .build() - } - } - } - return db!! - } - - fun destroyInstance() { - db = null - } - - // start autoincrement ID from FIRST_CONTACT_ID/FIRST_GROUP_ID to avoid conflicts - // Room doesn't seem to have a built in way for it, so just create a contact/group and delete it - private fun increaseAutoIncrementIds() { - Executors.newSingleThreadExecutor().execute { - val emptyContact = getEmptyLocalContact() - emptyContact.id = FIRST_CONTACT_ID - db!!.ContactsDao().apply { - insertOrUpdate(emptyContact) - deleteContactId(FIRST_CONTACT_ID) - } - - val emptyGroup = Group(FIRST_GROUP_ID, "") - db!!.GroupsDao().apply { - insertOrUpdate(emptyGroup) - deleteGroupId(FIRST_GROUP_ID) - } - } - } - - private val MIGRATION_1_2 = object : Migration(1, 2) { - override fun migrate(database: SupportSQLiteDatabase) { - database.apply { - execSQL("ALTER TABLE contacts ADD COLUMN photo_uri TEXT NOT NULL DEFAULT ''") - } - } - } - - private val MIGRATION_2_3 = object : Migration(2, 3) { - override fun migrate(database: SupportSQLiteDatabase) { - database.apply { - execSQL("ALTER TABLE contacts ADD COLUMN ringtone TEXT DEFAULT ''") - } - } - } - } -} diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ChooseSocialDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ChooseSocialDialog.kt index 364b1e01..7b7b56a1 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ChooseSocialDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ChooseSocialDialog.kt @@ -8,9 +8,9 @@ import androidx.appcompat.app.AlertDialog import com.simplemobiletools.commons.extensions.beGone import com.simplemobiletools.commons.extensions.getAlertDialogBuilder import com.simplemobiletools.commons.extensions.setupDialogStuff +import com.simplemobiletools.commons.models.contacts.SocialAction import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.extensions.getPackageDrawable -import com.simplemobiletools.contacts.pro.models.SocialAction import kotlinx.android.synthetic.main.dialog_choose_social.view.* import kotlinx.android.synthetic.main.item_choose_social.view.* diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/CreateNewGroupDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/CreateNewGroupDialog.kt index 71f90c2e..df44401d 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/CreateNewGroupDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/CreateNewGroupDialog.kt @@ -5,13 +5,12 @@ import androidx.appcompat.app.AlertDialog import com.simplemobiletools.commons.activities.BaseSimpleActivity import com.simplemobiletools.commons.dialogs.RadioGroupDialog import com.simplemobiletools.commons.extensions.* +import com.simplemobiletools.commons.helpers.ContactsHelper import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.commons.models.RadioItem +import com.simplemobiletools.commons.models.contacts.ContactSource +import com.simplemobiletools.commons.models.contacts.Group import com.simplemobiletools.contacts.pro.R -import com.simplemobiletools.contacts.pro.extensions.getPrivateContactSource -import com.simplemobiletools.contacts.pro.helpers.ContactsHelper -import com.simplemobiletools.contacts.pro.models.ContactSource -import com.simplemobiletools.contacts.pro.models.Group import kotlinx.android.synthetic.main.dialog_create_new_group.view.* class CreateNewGroupDialog(val activity: BaseSimpleActivity, val callback: (newGroup: Group) -> Unit) { diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ExportContactsDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ExportContactsDialog.kt index 3ead5655..fdbab186 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ExportContactsDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ExportContactsDialog.kt @@ -5,15 +5,13 @@ import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import com.simplemobiletools.commons.dialogs.FilePickerDialog import com.simplemobiletools.commons.extensions.* +import com.simplemobiletools.commons.helpers.ContactsHelper import com.simplemobiletools.commons.helpers.ensureBackgroundThread +import com.simplemobiletools.commons.models.contacts.* import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.activities.SimpleActivity import com.simplemobiletools.contacts.pro.adapters.FilterContactSourcesAdapter import com.simplemobiletools.contacts.pro.extensions.config -import com.simplemobiletools.contacts.pro.extensions.getVisibleContactSources -import com.simplemobiletools.contacts.pro.helpers.ContactsHelper -import com.simplemobiletools.contacts.pro.models.Contact -import com.simplemobiletools.contacts.pro.models.ContactSource import kotlinx.android.synthetic.main.dialog_export_contacts.view.* import java.io.File diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/FilterContactSourcesDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/FilterContactSourcesDialog.kt index a496c3da..014c9947 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/FilterContactSourcesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/FilterContactSourcesDialog.kt @@ -7,11 +7,10 @@ import com.simplemobiletools.commons.helpers.SMT_PRIVATE import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.activities.SimpleActivity import com.simplemobiletools.contacts.pro.adapters.FilterContactSourcesAdapter +import com.simplemobiletools.commons.extensions.getVisibleContactSources +import com.simplemobiletools.commons.helpers.ContactsHelper +import com.simplemobiletools.commons.models.contacts.* import com.simplemobiletools.contacts.pro.extensions.config -import com.simplemobiletools.contacts.pro.extensions.getVisibleContactSources -import com.simplemobiletools.contacts.pro.helpers.ContactsHelper -import com.simplemobiletools.contacts.pro.models.Contact -import com.simplemobiletools.contacts.pro.models.ContactSource import kotlinx.android.synthetic.main.dialog_filter_contact_sources.view.* class FilterContactSourcesDialog(val activity: SimpleActivity, private val callback: () -> Unit) { diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ImportContactsDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ImportContactsDialog.kt index ad052d59..cc73aa0d 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ImportContactsDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ImportContactsDialog.kt @@ -2,17 +2,14 @@ package com.simplemobiletools.contacts.pro.dialogs import android.view.ViewGroup import androidx.appcompat.app.AlertDialog -import com.simplemobiletools.commons.extensions.getAlertDialogBuilder -import com.simplemobiletools.commons.extensions.setupDialogStuff -import com.simplemobiletools.commons.extensions.toast +import com.simplemobiletools.commons.extensions.* +import com.simplemobiletools.commons.helpers.ContactsHelper import com.simplemobiletools.commons.helpers.SMT_PRIVATE import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.activities.SimpleActivity import com.simplemobiletools.contacts.pro.extensions.config -import com.simplemobiletools.contacts.pro.extensions.getPublicContactSource import com.simplemobiletools.contacts.pro.extensions.showContactSourcePicker -import com.simplemobiletools.contacts.pro.helpers.ContactsHelper import com.simplemobiletools.contacts.pro.helpers.VcfImporter import com.simplemobiletools.contacts.pro.helpers.VcfImporter.ImportResult.IMPORT_FAIL import kotlinx.android.synthetic.main.dialog_import_contacts.view.* diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ManageVisibleFieldsDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ManageVisibleFieldsDialog.kt index 375a64f9..b0b16161 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ManageVisibleFieldsDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ManageVisibleFieldsDialog.kt @@ -5,8 +5,8 @@ import com.simplemobiletools.commons.extensions.getAlertDialogBuilder import com.simplemobiletools.commons.extensions.setupDialogStuff import com.simplemobiletools.commons.views.MyAppCompatCheckbox import com.simplemobiletools.contacts.pro.R +import com.simplemobiletools.commons.helpers.* import com.simplemobiletools.contacts.pro.extensions.config -import com.simplemobiletools.contacts.pro.helpers.* class ManageVisibleFieldsDialog(val activity: BaseSimpleActivity, val callback: (hasSomethingChanged: Boolean) -> Unit) { private var view = activity.layoutInflater.inflate(R.layout.dialog_manage_visible_fields, null) diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ManageVisibleTabsDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ManageVisibleTabsDialog.kt index 5806242d..ba40a6f3 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ManageVisibleTabsDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/ManageVisibleTabsDialog.kt @@ -8,8 +8,8 @@ import com.simplemobiletools.commons.helpers.TAB_FAVORITES import com.simplemobiletools.commons.helpers.TAB_GROUPS import com.simplemobiletools.commons.views.MyAppCompatCheckbox import com.simplemobiletools.contacts.pro.R +import com.simplemobiletools.commons.helpers.ALL_TABS_MASK import com.simplemobiletools.contacts.pro.extensions.config -import com.simplemobiletools.contacts.pro.helpers.ALL_TABS_MASK class ManageVisibleTabsDialog(val activity: BaseSimpleActivity) { private var view = activity.layoutInflater.inflate(R.layout.dialog_manage_visible_tabs, null) diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/RenameGroupDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/RenameGroupDialog.kt index 44bc8dad..ab7c411b 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/RenameGroupDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/RenameGroupDialog.kt @@ -3,11 +3,10 @@ package com.simplemobiletools.contacts.pro.dialogs import androidx.appcompat.app.AlertDialog import com.simplemobiletools.commons.activities.BaseSimpleActivity import com.simplemobiletools.commons.extensions.* +import com.simplemobiletools.commons.helpers.ContactsHelper import com.simplemobiletools.commons.helpers.ensureBackgroundThread +import com.simplemobiletools.commons.models.contacts.Group import com.simplemobiletools.contacts.pro.R -import com.simplemobiletools.contacts.pro.extensions.groupsDB -import com.simplemobiletools.contacts.pro.helpers.ContactsHelper -import com.simplemobiletools.contacts.pro.models.Group import kotlinx.android.synthetic.main.dialog_rename_group.view.* class RenameGroupDialog(val activity: BaseSimpleActivity, val group: Group, val callback: () -> Unit) { diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/SelectContactsDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/SelectContactsDialog.kt index 57fb9351..14287a76 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/SelectContactsDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/SelectContactsDialog.kt @@ -7,8 +7,7 @@ import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.activities.SimpleActivity import com.simplemobiletools.contacts.pro.adapters.SelectContactsAdapter -import com.simplemobiletools.contacts.pro.extensions.getVisibleContactSources -import com.simplemobiletools.contacts.pro.models.Contact +import com.simplemobiletools.commons.models.contacts.* import kotlinx.android.synthetic.main.dialog_select_contact.view.* import java.util.* diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/SelectGroupsDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/SelectGroupsDialog.kt index 91f9ea22..29afcbb7 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/SelectGroupsDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/dialogs/SelectGroupsDialog.kt @@ -3,11 +3,11 @@ package com.simplemobiletools.contacts.pro.dialogs import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import com.simplemobiletools.commons.extensions.* +import com.simplemobiletools.commons.helpers.ContactsHelper +import com.simplemobiletools.commons.models.contacts.Group import com.simplemobiletools.commons.views.MyAppCompatCheckbox import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.activities.SimpleActivity -import com.simplemobiletools.contacts.pro.helpers.ContactsHelper -import com.simplemobiletools.contacts.pro.models.Group import kotlinx.android.synthetic.main.dialog_select_groups.view.* import kotlinx.android.synthetic.main.item_checkbox.view.* import kotlinx.android.synthetic.main.item_textview.view.* diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Activity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Activity.kt index 20349305..53faecd8 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Activity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Activity.kt @@ -7,18 +7,16 @@ import com.simplemobiletools.commons.activities.BaseSimpleActivity import com.simplemobiletools.commons.dialogs.CallConfirmationDialog import com.simplemobiletools.commons.dialogs.RadioGroupDialog import com.simplemobiletools.commons.extensions.* -import com.simplemobiletools.commons.helpers.CONTACT_ID -import com.simplemobiletools.commons.helpers.IS_PRIVATE -import com.simplemobiletools.commons.helpers.PERMISSION_CALL_PHONE -import com.simplemobiletools.commons.helpers.SMT_PRIVATE +import com.simplemobiletools.commons.helpers.* import com.simplemobiletools.commons.models.RadioItem +import com.simplemobiletools.commons.models.contacts.Contact import com.simplemobiletools.contacts.pro.BuildConfig import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.activities.EditContactActivity import com.simplemobiletools.contacts.pro.activities.SimpleActivity import com.simplemobiletools.contacts.pro.activities.ViewContactActivity -import com.simplemobiletools.contacts.pro.helpers.* -import com.simplemobiletools.contacts.pro.models.Contact +import com.simplemobiletools.contacts.pro.helpers.DEFAULT_FILE_NAME +import com.simplemobiletools.contacts.pro.helpers.VcfExporter fun SimpleActivity.startCallIntent(recipient: String) { handlePermission(PERMISSION_CALL_PHONE) { diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Context.kt index d2052e8a..b4f18f1a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/extensions/Context.kt @@ -2,339 +2,21 @@ package com.simplemobiletools.contacts.pro.extensions import android.annotation.SuppressLint import android.content.Context -import android.content.Intent -import android.database.Cursor import android.graphics.drawable.Drawable -import android.net.Uri -import android.os.Handler -import android.os.Looper -import android.provider.ContactsContract import androidx.core.content.FileProvider -import com.simplemobiletools.commons.extensions.* -import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS -import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_CONTACTS -import com.simplemobiletools.commons.helpers.SMT_PRIVATE -import com.simplemobiletools.commons.helpers.SimpleContactsHelper +import com.simplemobiletools.commons.extensions.getCachePhoto +import com.simplemobiletools.commons.helpers.SIGNAL_PACKAGE +import com.simplemobiletools.commons.helpers.TELEGRAM_PACKAGE +import com.simplemobiletools.commons.helpers.VIBER_PACKAGE +import com.simplemobiletools.commons.helpers.WHATSAPP_PACKAGE import com.simplemobiletools.contacts.pro.BuildConfig import com.simplemobiletools.contacts.pro.R -import com.simplemobiletools.contacts.pro.databases.ContactsDatabase -import com.simplemobiletools.contacts.pro.helpers.* -import com.simplemobiletools.contacts.pro.interfaces.ContactsDao -import com.simplemobiletools.contacts.pro.interfaces.GroupsDao -import com.simplemobiletools.contacts.pro.models.Contact -import com.simplemobiletools.contacts.pro.models.ContactSource -import com.simplemobiletools.contacts.pro.models.Organization -import com.simplemobiletools.contacts.pro.models.SocialAction +import com.simplemobiletools.contacts.pro.helpers.Config import java.io.File val Context.config: Config get() = Config.newInstance(applicationContext) - -val Context.contactsDB: ContactsDao get() = ContactsDatabase.getInstance(applicationContext).ContactsDao() - -val Context.groupsDB: GroupsDao get() = ContactsDatabase.getInstance(applicationContext).GroupsDao() - -fun Context.getEmptyContact(): Contact { - val originalContactSource = if (hasContactPermissions()) config.lastUsedContactSource else SMT_PRIVATE - val organization = Organization("", "") - return Contact( - 0, "", "", "", "", "", "", "", ArrayList(), ArrayList(), ArrayList(), ArrayList(), originalContactSource, 0, 0, "", - null, "", ArrayList(), organization, ArrayList(), ArrayList(), DEFAULT_MIMETYPE, null - ) -} - -fun Context.sendAddressIntent(address: String) { - val location = Uri.encode(address) - val uri = Uri.parse("geo:0,0?q=$location") - - Intent(Intent.ACTION_VIEW, uri).apply { - launchActivityIntent(this) - } -} - -fun Context.openWebsiteIntent(url: String) { - val website = if (url.startsWith("http")) { - url - } else { - "https://$url" - } - - Intent(Intent.ACTION_VIEW).apply { - data = Uri.parse(website) - launchActivityIntent(this) - } -} - -fun Context.getLookupUriRawId(dataUri: Uri): Int { - val lookupKey = getLookupKeyFromUri(dataUri) - if (lookupKey != null) { - val uri = lookupContactUri(lookupKey, this) - if (uri != null) { - return getContactUriRawId(uri) - } - } - return -1 -} - -fun Context.getContactUriRawId(uri: Uri): Int { - val projection = arrayOf(ContactsContract.Contacts.NAME_RAW_CONTACT_ID) - var cursor: Cursor? = null - try { - cursor = contentResolver.query(uri, projection, null, null, null) - if (cursor!!.moveToFirst()) { - return cursor.getIntValue(ContactsContract.Contacts.NAME_RAW_CONTACT_ID) - } - } catch (ignored: Exception) { - } finally { - cursor?.close() - } - return -1 -} - -// from https://android.googlesource.com/platform/packages/apps/Dialer/+/68038172793ee0e2ab3e2e56ddfbeb82879d1f58/java/com/android/contacts/common/util/UriUtils.java -fun getLookupKeyFromUri(lookupUri: Uri): String? { - return if (!isEncodedContactUri(lookupUri)) { - val segments = lookupUri.pathSegments - if (segments.size < 3) null else Uri.encode(segments[2]) - } else { - null - } -} - -fun isEncodedContactUri(uri: Uri?): Boolean { - if (uri == null) { - return false - } - val lastPathSegment = uri.lastPathSegment ?: return false - return lastPathSegment == "encoded" -} - -fun lookupContactUri(lookup: String, context: Context): Uri? { - val lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookup) - return try { - ContactsContract.Contacts.lookupContact(context.contentResolver, lookupUri) - } catch (e: Exception) { - null - } -} - -fun Context.getCachePhoto(): File { - val imagesFolder = File(cacheDir, "my_cache") - if (!imagesFolder.exists()) { - imagesFolder.mkdirs() - } - - val file = File(imagesFolder, "Photo_${System.currentTimeMillis()}.jpg") - file.createNewFile() - return file -} - fun Context.getCachePhotoUri(file: File = getCachePhoto()) = FileProvider.getUriForFile(this, "${BuildConfig.APPLICATION_ID}.provider", file) -fun Context.getPhotoThumbnailSize(): Int { - val uri = ContactsContract.DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI - val projection = arrayOf(ContactsContract.DisplayPhoto.THUMBNAIL_MAX_DIM) - var cursor: Cursor? = null - try { - cursor = contentResolver.query(uri, projection, null, null, null) - if (cursor?.moveToFirst() == true) { - return cursor.getIntValue(ContactsContract.DisplayPhoto.THUMBNAIL_MAX_DIM) - } - } catch (ignored: Exception) { - } finally { - cursor?.close() - } - return 0 -} - -fun Context.hasContactPermissions() = hasPermission(PERMISSION_READ_CONTACTS) && hasPermission(PERMISSION_WRITE_CONTACTS) - -fun Context.getPublicContactSource(source: String, callback: (String) -> Unit) { - when (source) { - SMT_PRIVATE -> callback(getString(R.string.phone_storage_hidden)) - else -> { - ContactsHelper(this).getContactSources { - var newSource = source - for (contactSource in it) { - if (contactSource.name == source && contactSource.type == TELEGRAM_PACKAGE) { - newSource = getString(R.string.telegram) - break - } else if (contactSource.name == source && contactSource.type == VIBER_PACKAGE) { - newSource = getString(R.string.viber) - break - } - } - Handler(Looper.getMainLooper()).post { - callback(newSource) - } - } - } - } -} - -fun Context.getPublicContactSourceSync(source: String, contactSources: ArrayList): String { - return when (source) { - SMT_PRIVATE -> getString(R.string.phone_storage_hidden) - else -> { - var newSource = source - for (contactSource in contactSources) { - if (contactSource.name == source && contactSource.type == TELEGRAM_PACKAGE) { - newSource = getString(R.string.telegram) - break - } else if (contactSource.name == source && contactSource.type == VIBER_PACKAGE) { - newSource = getString(R.string.viber) - break - } - } - - return newSource - } - } -} - -fun Context.sendSMSToContacts(contacts: ArrayList) { - val numbers = StringBuilder() - contacts.forEach { - val number = it.phoneNumbers.firstOrNull { it.type == ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE } - ?: it.phoneNumbers.firstOrNull() - if (number != null) { - numbers.append("${Uri.encode(number.value)};") - } - } - - val uriString = "smsto:${numbers.toString().trimEnd(';')}" - Intent(Intent.ACTION_SENDTO, Uri.parse(uriString)).apply { - launchActivityIntent(this) - } -} - -fun Context.sendEmailToContacts(contacts: ArrayList) { - val emails = ArrayList() - contacts.forEach { - it.emails.forEach { - if (it.value.isNotEmpty()) { - emails.add(it.value) - } - } - } - - Intent(Intent.ACTION_SEND_MULTIPLE).apply { - type = "message/rfc822" - putExtra(Intent.EXTRA_EMAIL, emails.toTypedArray()) - launchActivityIntent(this) - } -} - -fun Context.getTempFile(filename: String = DEFAULT_FILE_NAME): File? { - val folder = File(cacheDir, "contacts") - if (!folder.exists()) { - if (!folder.mkdir()) { - toast(R.string.unknown_error_occurred) - return null - } - } - - return File(folder, filename) -} - -fun Context.addContactsToGroup(contacts: ArrayList, groupId: Long) { - val publicContacts = contacts.filter { !it.isPrivate() }.toMutableList() as ArrayList - val privateContacts = contacts.filter { it.isPrivate() }.toMutableList() as ArrayList - if (publicContacts.isNotEmpty()) { - ContactsHelper(this).addContactsToGroup(publicContacts, groupId) - } - - if (privateContacts.isNotEmpty()) { - LocalContactsHelper(this).addContactsToGroup(privateContacts, groupId) - } -} - -fun Context.removeContactsFromGroup(contacts: ArrayList, groupId: Long) { - val publicContacts = contacts.filter { !it.isPrivate() }.toMutableList() as ArrayList - val privateContacts = contacts.filter { it.isPrivate() }.toMutableList() as ArrayList - if (publicContacts.isNotEmpty() && hasContactPermissions()) { - ContactsHelper(this).removeContactsFromGroup(publicContacts, groupId) - } - - if (privateContacts.isNotEmpty()) { - LocalContactsHelper(this).removeContactsFromGroup(privateContacts, groupId) - } -} - -fun Context.getContactPublicUri(contact: Contact): Uri { - val lookupKey = if (contact.isPrivate()) { - "local_${contact.id}" - } else { - SimpleContactsHelper(this).getContactLookupKey(contact.id.toString()) - } - return Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey) -} - -fun Context.getVisibleContactSources(): ArrayList { - val sources = getAllContactSources() - val ignoredContactSources = config.ignoredContactSources - return ArrayList(sources).filter { !ignoredContactSources.contains(it.getFullIdentifier()) } - .map { it.name }.toMutableList() as ArrayList -} - -fun Context.getAllContactSources(): ArrayList { - val sources = ContactsHelper(this).getDeviceContactSources() - sources.add(getPrivateContactSource()) - return sources.toMutableList() as ArrayList -} - -fun Context.getPrivateContactSource() = ContactSource(SMT_PRIVATE, SMT_PRIVATE, getString(R.string.phone_storage_hidden)) - -fun Context.getSocialActions(id: Int): ArrayList { - val uri = ContactsContract.Data.CONTENT_URI - val projection = arrayOf( - ContactsContract.Data._ID, - ContactsContract.Data.DATA3, - ContactsContract.Data.MIMETYPE, - ContactsContract.Data.ACCOUNT_TYPE_AND_DATA_SET - ) - - val socialActions = ArrayList() - var curActionId = 0 - val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ?" - val selectionArgs = arrayOf(id.toString()) - queryCursor(uri, projection, selection, selectionArgs, null, true) { cursor -> - val mimetype = cursor.getStringValue(ContactsContract.Data.MIMETYPE) - val type = when (mimetype) { - // WhatsApp - "vnd.android.cursor.item/vnd.com.whatsapp.profile" -> SOCIAL_MESSAGE - "vnd.android.cursor.item/vnd.com.whatsapp.voip.call" -> SOCIAL_VOICE_CALL - "vnd.android.cursor.item/vnd.com.whatsapp.video.call" -> SOCIAL_VIDEO_CALL - - // Viber - "vnd.android.cursor.item/vnd.com.viber.voip.viber_number_call" -> SOCIAL_VOICE_CALL - "vnd.android.cursor.item/vnd.com.viber.voip.viber_out_call_viber" -> SOCIAL_VOICE_CALL - "vnd.android.cursor.item/vnd.com.viber.voip.viber_out_call_none_viber" -> SOCIAL_VOICE_CALL - "vnd.android.cursor.item/vnd.com.viber.voip.viber_number_message" -> SOCIAL_MESSAGE - - // Signal - "vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.contact" -> SOCIAL_MESSAGE - "vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call" -> SOCIAL_VOICE_CALL - - // Telegram - "vnd.android.cursor.item/vnd.org.telegram.messenger.android.call" -> SOCIAL_VOICE_CALL - "vnd.android.cursor.item/vnd.org.telegram.messenger.android.call.video" -> SOCIAL_VIDEO_CALL - "vnd.android.cursor.item/vnd.org.telegram.messenger.android.profile" -> SOCIAL_MESSAGE - - // Threema - "vnd.android.cursor.item/vnd.ch.threema.app.profile" -> SOCIAL_MESSAGE - "vnd.android.cursor.item/vnd.ch.threema.app.call" -> SOCIAL_VOICE_CALL - else -> return@queryCursor - } - - val label = cursor.getStringValue(ContactsContract.Data.DATA3) - val realID = cursor.getLongValue(ContactsContract.Data._ID) - val packageName = cursor.getStringValue(ContactsContract.Data.ACCOUNT_TYPE_AND_DATA_SET) - val socialAction = SocialAction(curActionId++, type, label, mimetype, realID, packageName) - socialActions.add(socialAction) - } - return socialActions -} - @SuppressLint("UseCompatLoadingForDrawables") fun Context.getPackageDrawable(packageName: String): Drawable { return resources.getDrawable( diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/fragments/FavoritesFragment.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/fragments/FavoritesFragment.kt index f8460f1f..c3afa1be 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/fragments/FavoritesFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/fragments/FavoritesFragment.kt @@ -2,11 +2,11 @@ package com.simplemobiletools.contacts.pro.fragments import android.content.Context import android.util.AttributeSet +import com.simplemobiletools.commons.helpers.ContactsHelper import com.simplemobiletools.commons.helpers.TAB_FAVORITES import com.simplemobiletools.contacts.pro.activities.MainActivity import com.simplemobiletools.contacts.pro.activities.SimpleActivity import com.simplemobiletools.contacts.pro.dialogs.SelectContactsDialog -import com.simplemobiletools.contacts.pro.helpers.ContactsHelper class FavoritesFragment(context: Context, attributeSet: AttributeSet) : MyViewPagerFragment(context, attributeSet) { override fun fabClicked() { diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/fragments/MyViewPagerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/fragments/MyViewPagerFragment.kt index 0fcd0019..c286de85 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/fragments/MyViewPagerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/fragments/MyViewPagerFragment.kt @@ -17,12 +17,11 @@ import com.simplemobiletools.contacts.pro.activities.MainActivity import com.simplemobiletools.contacts.pro.activities.SimpleActivity import com.simplemobiletools.contacts.pro.adapters.ContactsAdapter import com.simplemobiletools.contacts.pro.adapters.GroupsAdapter -import com.simplemobiletools.contacts.pro.extensions.config -import com.simplemobiletools.contacts.pro.extensions.getVisibleContactSources +import com.simplemobiletools.commons.helpers.Converters import com.simplemobiletools.contacts.pro.helpers.* import com.simplemobiletools.contacts.pro.interfaces.RefreshContactsListener -import com.simplemobiletools.contacts.pro.models.Contact -import com.simplemobiletools.contacts.pro.models.Group +import com.simplemobiletools.commons.models.contacts.* +import com.simplemobiletools.contacts.pro.extensions.config import kotlinx.android.synthetic.main.fragment_layout.view.* import kotlinx.android.synthetic.main.fragment_layout.view.fragment_fab import kotlinx.android.synthetic.main.fragment_layout.view.fragment_list diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Config.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Config.kt index 4c584f9c..2edb94fd 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Config.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Config.kt @@ -7,71 +7,4 @@ class Config(context: Context) : BaseConfig(context) { companion object { fun newInstance(context: Context) = Config(context) } - - var ignoredContactSources: HashSet - get() = prefs.getStringSet(IGNORED_CONTACT_SOURCES, hashSetOf(".")) as HashSet - set(ignoreContactSources) = prefs.edit().remove(IGNORED_CONTACT_SOURCES).putStringSet(IGNORED_CONTACT_SOURCES, ignoreContactSources).apply() - - var showContactThumbnails: Boolean - get() = prefs.getBoolean(SHOW_CONTACT_THUMBNAILS, true) - set(showContactThumbnails) = prefs.edit().putBoolean(SHOW_CONTACT_THUMBNAILS, showContactThumbnails).apply() - - var showPhoneNumbers: Boolean - get() = prefs.getBoolean(SHOW_PHONE_NUMBERS, false) - set(showPhoneNumbers) = prefs.edit().putBoolean(SHOW_PHONE_NUMBERS, showPhoneNumbers).apply() - - var showOnlyContactsWithNumbers: Boolean - get() = prefs.getBoolean(SHOW_ONLY_CONTACTS_WITH_NUMBERS, false) - set(showOnlyContactsWithNumbers) = prefs.edit().putBoolean(SHOW_ONLY_CONTACTS_WITH_NUMBERS, showOnlyContactsWithNumbers).apply() - - var lastUsedContactSource: String - get() = prefs.getString(LAST_USED_CONTACT_SOURCE, "")!! - set(lastUsedContactSource) = prefs.edit().putString(LAST_USED_CONTACT_SOURCE, lastUsedContactSource).apply() - - var onContactClick: Int - get() = prefs.getInt(ON_CONTACT_CLICK, ON_CLICK_VIEW_CONTACT) - set(onContactClick) = prefs.edit().putInt(ON_CONTACT_CLICK, onContactClick).apply() - - var showContactFields: Int - get() = prefs.getInt( - SHOW_CONTACT_FIELDS, SHOW_FIRST_NAME_FIELD or SHOW_SURNAME_FIELD or SHOW_PHONE_NUMBERS_FIELD or SHOW_EMAILS_FIELD or - SHOW_ADDRESSES_FIELD or SHOW_EVENTS_FIELD or SHOW_NOTES_FIELD or SHOW_GROUPS_FIELD or SHOW_CONTACT_SOURCE_FIELD - ) - set(showContactFields) = prefs.edit().putInt(SHOW_CONTACT_FIELDS, showContactFields).apply() - - var showTabs: Int - get() = prefs.getInt(SHOW_TABS, ALL_TABS_MASK) - set(showTabs) = prefs.edit().putInt(SHOW_TABS, showTabs).apply() - - var showDialpadButton: Boolean - get() = prefs.getBoolean(SHOW_DIALPAD_BUTTON, true) - set(showDialpadButton) = prefs.edit().putBoolean(SHOW_DIALPAD_BUTTON, showDialpadButton).apply() - - var wasLocalAccountInitialized: Boolean - get() = prefs.getBoolean(WAS_LOCAL_ACCOUNT_INITIALIZED, false) - set(wasLocalAccountInitialized) = prefs.edit().putBoolean(WAS_LOCAL_ACCOUNT_INITIALIZED, wasLocalAccountInitialized).apply() - - var lastExportPath: String - get() = prefs.getString(LAST_EXPORT_PATH, "")!! - set(lastExportPath) = prefs.edit().putString(LAST_EXPORT_PATH, lastExportPath).apply() - - var speedDial: String - get() = prefs.getString(SPEED_DIAL, "")!! - set(speedDial) = prefs.edit().putString(SPEED_DIAL, speedDial).apply() - - var showPrivateContacts: Boolean - get() = prefs.getBoolean(SHOW_PRIVATE_CONTACTS, true) - set(showPrivateContacts) = prefs.edit().putBoolean(SHOW_PRIVATE_CONTACTS, showPrivateContacts).apply() - - var mergeDuplicateContacts: Boolean - get() = prefs.getBoolean(MERGE_DUPLICATE_CONTACTS, true) - set(mergeDuplicateContacts) = prefs.edit().putBoolean(MERGE_DUPLICATE_CONTACTS, mergeDuplicateContacts).apply() - - var favoritesContactsOrder: String - get() = prefs.getString(FAVORITES_CONTACTS_ORDER, "")!! - set(order) = prefs.edit().putString(FAVORITES_CONTACTS_ORDER, order).apply() - - var isCustomOrderSelected: Boolean - get() = prefs.getBoolean(FAVORITES_CUSTOM_ORDER_SELECTED, false) - set(selected) = prefs.edit().putBoolean(FAVORITES_CUSTOM_ORDER_SELECTED, selected).apply() } diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Constants.kt index 9d00ac3f..958524b9 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Constants.kt @@ -1,29 +1,8 @@ package com.simplemobiletools.contacts.pro.helpers -import android.provider.ContactsContract.CommonDataKinds -import com.simplemobiletools.commons.extensions.normalizeString import com.simplemobiletools.commons.helpers.TAB_CONTACTS import com.simplemobiletools.commons.helpers.TAB_FAVORITES import com.simplemobiletools.commons.helpers.TAB_GROUPS -import com.simplemobiletools.contacts.pro.models.LocalContact - -// shared prefs -const val SHOW_CONTACT_THUMBNAILS = "show_contact_thumbnails" -const val SHOW_PHONE_NUMBERS = "show_phone_numbers" -const val SHOW_ONLY_CONTACTS_WITH_NUMBERS = "show_only_contacts_with_numbers" -const val IGNORED_CONTACT_SOURCES = "ignored_contact_sources_2" -const val LAST_USED_CONTACT_SOURCE = "last_used_contact_source" -const val ON_CONTACT_CLICK = "on_contact_click" -const val SHOW_CONTACT_FIELDS = "show_contact_fields" -const val SHOW_TABS = "show_tabs" -const val SHOW_DIALPAD_BUTTON = "show_dialpad_button" -const val SPEED_DIAL = "speed_dial" -const val LAST_EXPORT_PATH = "last_export_path" -const val WAS_LOCAL_ACCOUNT_INITIALIZED = "was_local_account_initialized" -const val SHOW_PRIVATE_CONTACTS = "show_private_contacts" -const val MERGE_DUPLICATE_CONTACTS = "merge_duplicate_contacts" -const val FAVORITES_CONTACTS_ORDER = "favorites_contacts_order" -const val FAVORITES_CUSTOM_ORDER_SELECTED = "favorites_custom_order_selected" const val GROUP = "group" const val IS_FROM_SIMPLE_CONTACTS = "is_from_simple_contacts" @@ -44,20 +23,12 @@ const val LOCATION_FAVORITES_TAB = 1 const val LOCATION_GROUP_CONTACTS = 2 const val LOCATION_INSERT_OR_EDIT = 3 -const val ALL_TABS_MASK = TAB_CONTACTS or TAB_FAVORITES or TAB_GROUPS - val tabsList = arrayListOf( TAB_CONTACTS, TAB_FAVORITES, TAB_GROUPS ) -// contact photo changes -const val PHOTO_ADDED = 1 -const val PHOTO_REMOVED = 2 -const val PHOTO_CHANGED = 3 -const val PHOTO_UNCHANGED = 4 - // phone number/email types const val CELL = "CELL" const val WORK = "WORK" @@ -76,77 +47,8 @@ const val HANGOUTS = "Hangouts" const val QQ = "QQ" const val JABBER = "Jabber" -const val ON_CLICK_CALL_CONTACT = 1 -const val ON_CLICK_VIEW_CONTACT = 2 -const val ON_CLICK_EDIT_CONTACT = 3 - -// visible fields filtering -const val SHOW_PREFIX_FIELD = 1 -const val SHOW_FIRST_NAME_FIELD = 2 -const val SHOW_MIDDLE_NAME_FIELD = 4 -const val SHOW_SURNAME_FIELD = 8 -const val SHOW_SUFFIX_FIELD = 16 -const val SHOW_PHONE_NUMBERS_FIELD = 32 -const val SHOW_EMAILS_FIELD = 64 -const val SHOW_ADDRESSES_FIELD = 128 -const val SHOW_EVENTS_FIELD = 256 -const val SHOW_NOTES_FIELD = 512 -const val SHOW_ORGANIZATION_FIELD = 1024 -const val SHOW_GROUPS_FIELD = 2048 -const val SHOW_CONTACT_SOURCE_FIELD = 4096 -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 DEFAULT_EMAIL_TYPE = CommonDataKinds.Email.TYPE_HOME -const val DEFAULT_PHONE_NUMBER_TYPE = CommonDataKinds.Phone.TYPE_MOBILE -const val DEFAULT_ADDRESS_TYPE = CommonDataKinds.StructuredPostal.TYPE_HOME -const val DEFAULT_EVENT_TYPE = CommonDataKinds.Event.TYPE_BIRTHDAY -const val DEFAULT_ORGANIZATION_TYPE = CommonDataKinds.Organization.TYPE_WORK -const val DEFAULT_WEBSITE_TYPE = CommonDataKinds.Website.TYPE_HOMEPAGE -const val DEFAULT_IM_TYPE = CommonDataKinds.Im.PROTOCOL_SKYPE -const val DEFAULT_MIMETYPE = CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE - -// apps with special handling -const val TELEGRAM_PACKAGE = "org.telegram.messenger" -const val SIGNAL_PACKAGE = "org.thoughtcrime.securesms" -const val WHATSAPP_PACKAGE = "com.whatsapp" -const val VIBER_PACKAGE = "com.viber.voip" -const val THREEMA_PACKAGE = "ch.threema.app" - const val WHATSAPP = "whatsapp" const val SIGNAL = "signal" const val VIBER = "viber" const val TELEGRAM = "telegram" const val THREEMA = "threema" - -const val SOCIAL_VOICE_CALL = 0 -const val SOCIAL_VIDEO_CALL = 1 -const val SOCIAL_MESSAGE = 2 - -fun getEmptyLocalContact() = LocalContact( - 0, - "", - "", - "", - "", - "", - "", - null, - "", - ArrayList(), - ArrayList(), - ArrayList(), - 0, - ArrayList(), - "", - ArrayList(), - "", - "", - ArrayList(), - ArrayList(), - null -) - -fun getProperText(text: String, shouldNormalize: Boolean) = if (shouldNormalize) text.normalizeString() else text diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/ContactsHelper.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/ContactsHelper.kt deleted file mode 100644 index c619bd97..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/ContactsHelper.kt +++ /dev/null @@ -1,1547 +0,0 @@ -package com.simplemobiletools.contacts.pro.helpers - -import android.accounts.Account -import android.accounts.AccountManager -import android.content.* -import android.graphics.Bitmap -import android.net.Uri -import android.os.Handler -import android.os.Looper -import android.provider.ContactsContract.* -import android.provider.ContactsContract.CommonDataKinds.* -import android.provider.MediaStore -import android.text.TextUtils -import android.util.SparseArray -import com.simplemobiletools.commons.extensions.* -import com.simplemobiletools.commons.helpers.* -import com.simplemobiletools.commons.models.PhoneNumber -import com.simplemobiletools.commons.overloads.times -import com.simplemobiletools.contacts.pro.R -import com.simplemobiletools.contacts.pro.extensions.* -import com.simplemobiletools.contacts.pro.models.* -import com.simplemobiletools.contacts.pro.models.Email -import com.simplemobiletools.contacts.pro.models.Event -import com.simplemobiletools.contacts.pro.models.Organization -import java.util.* - -class ContactsHelper(val context: Context) { - private val BATCH_SIZE = 50 - private var displayContactSources = ArrayList() - - fun getContacts( - getAll: Boolean = false, - gettingDuplicates: Boolean = false, - ignoredContactSources: HashSet = HashSet(), - callback: (ArrayList) -> Unit - ) { - ensureBackgroundThread { - val contacts = SparseArray() - displayContactSources = context.getVisibleContactSources() - - if (getAll) { - displayContactSources = if (ignoredContactSources.isEmpty()) { - context.getAllContactSources().map { it.name }.toMutableList() as ArrayList - } else { - context.getAllContactSources().filter { - it.getFullIdentifier().isNotEmpty() && !ignoredContactSources.contains(it.getFullIdentifier()) - }.map { it.name }.toMutableList() as ArrayList - } - } - - getDeviceContacts(contacts, ignoredContactSources, gettingDuplicates) - - if (displayContactSources.contains(SMT_PRIVATE)) { - LocalContactsHelper(context).getAllContacts().forEach { - contacts.put(it.id, it) - } - } - - val contactsSize = contacts.size() - val showOnlyContactsWithNumbers = context.config.showOnlyContactsWithNumbers - val tempContacts = ArrayList(contactsSize) - val resultContacts = ArrayList(contactsSize) - - (0 until contactsSize).filter { - if (ignoredContactSources.isEmpty() && showOnlyContactsWithNumbers) { - contacts.valueAt(it).phoneNumbers.isNotEmpty() - } else { - true - } - }.mapTo(tempContacts) { - contacts.valueAt(it) - } - - if (context.config.mergeDuplicateContacts && ignoredContactSources.isEmpty() && !getAll) { - tempContacts.filter { displayContactSources.contains(it.source) }.groupBy { it.getNameToDisplay().toLowerCase() }.values.forEach { it -> - if (it.size == 1) { - resultContacts.add(it.first()) - } else { - val sorted = it.sortedByDescending { it.getStringToCompare().length } - resultContacts.add(sorted.first()) - } - } - } else { - resultContacts.addAll(tempContacts) - } - - // groups are obtained with contactID, not rawID, so assign them to proper contacts like this - val groups = getContactGroups(getStoredGroupsSync()) - val size = groups.size() - for (i in 0 until size) { - val key = groups.keyAt(i) - resultContacts.firstOrNull { it.contactId == key }?.groups = groups.valueAt(i) - } - - Contact.sorting = context.config.sorting - Contact.startWithSurname = context.config.startNameWithSurname - resultContacts.sort() - - Handler(Looper.getMainLooper()).post { - callback(resultContacts) - } - } - } - - private fun getContentResolverAccounts(): HashSet { - val sources = HashSet() - arrayOf(Groups.CONTENT_URI, Settings.CONTENT_URI, RawContacts.CONTENT_URI).forEach { - fillSourcesFromUri(it, sources) - } - - return sources - } - - private fun fillSourcesFromUri(uri: Uri, sources: HashSet) { - val projection = arrayOf( - RawContacts.ACCOUNT_NAME, - RawContacts.ACCOUNT_TYPE - ) - - context.queryCursor(uri, projection) { cursor -> - val name = cursor.getStringValue(RawContacts.ACCOUNT_NAME) ?: "" - val type = cursor.getStringValue(RawContacts.ACCOUNT_TYPE) ?: "" - var publicName = name - if (type == TELEGRAM_PACKAGE) { - publicName = context.getString(R.string.telegram) - } - - val source = ContactSource(name, type, publicName) - sources.add(source) - } - } - - private fun getDeviceContacts(contacts: SparseArray, ignoredContactSources: HashSet?, gettingDuplicates: Boolean) { - if (!context.hasContactPermissions()) { - return - } - - val ignoredSources = ignoredContactSources ?: context.config.ignoredContactSources - val uri = Data.CONTENT_URI - val projection = getContactProjection() - - arrayOf(CommonDataKinds.Organization.CONTENT_ITEM_TYPE, StructuredName.CONTENT_ITEM_TYPE).forEach { mimetype -> - val selection = "${Data.MIMETYPE} = ?" - val selectionArgs = arrayOf(mimetype) - val sortOrder = getSortString() - - context.queryCursor(uri, projection, selection, selectionArgs, sortOrder, true) { cursor -> - val accountName = cursor.getStringValue(RawContacts.ACCOUNT_NAME) ?: "" - val accountType = cursor.getStringValue(RawContacts.ACCOUNT_TYPE) ?: "" - - if (ignoredSources.contains("$accountName:$accountType")) { - return@queryCursor - } - - val id = cursor.getIntValue(Data.RAW_CONTACT_ID) - var prefix = "" - var firstName = "" - var middleName = "" - var surname = "" - var suffix = "" - - // ignore names at Organization type contacts - if (mimetype == StructuredName.CONTENT_ITEM_TYPE) { - prefix = cursor.getStringValue(StructuredName.PREFIX) ?: "" - firstName = cursor.getStringValue(StructuredName.GIVEN_NAME) ?: "" - middleName = cursor.getStringValue(StructuredName.MIDDLE_NAME) ?: "" - surname = cursor.getStringValue(StructuredName.FAMILY_NAME) ?: "" - suffix = cursor.getStringValue(StructuredName.SUFFIX) ?: "" - } - - var photoUri = "" - var starred = 0 - var contactId = 0 - var thumbnailUri = "" - var ringtone: String? = null - - if (!gettingDuplicates) { - photoUri = cursor.getStringValue(StructuredName.PHOTO_URI) ?: "" - starred = cursor.getIntValue(StructuredName.STARRED) - contactId = cursor.getIntValue(Data.CONTACT_ID) - thumbnailUri = cursor.getStringValue(StructuredName.PHOTO_THUMBNAIL_URI) ?: "" - ringtone = cursor.getStringValue(StructuredName.CUSTOM_RINGTONE) - } - - val nickname = "" - val numbers = ArrayList() // proper value is obtained below - val emails = ArrayList() - val addresses = ArrayList
() - val events = ArrayList() - val notes = "" - val groups = ArrayList() - val organization = Organization("", "") - val websites = ArrayList() - val ims = ArrayList() - val contact = Contact( - id, prefix, firstName, middleName, surname, suffix, nickname, photoUri, numbers, emails, addresses, - events, accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites, ims, mimetype, ringtone - ) - - contacts.put(id, contact) - } - } - - val emails = getEmails() - var size = emails.size() - for (i in 0 until size) { - val key = emails.keyAt(i) - contacts[key]?.emails = emails.valueAt(i) - } - - val organizations = getOrganizations() - size = organizations.size() - for (i in 0 until size) { - val key = organizations.keyAt(i) - contacts[key]?.organization = organizations.valueAt(i) - } - - // no need to fetch some fields if we are only getting duplicates of the current contact - if (gettingDuplicates) { - return - } - - val phoneNumbers = getPhoneNumbers(null) - size = phoneNumbers.size() - for (i in 0 until size) { - val key = phoneNumbers.keyAt(i) - if (contacts[key] != null) { - val numbers = phoneNumbers.valueAt(i) - contacts[key].phoneNumbers = numbers - } - } - - val addresses = getAddresses() - size = addresses.size() - for (i in 0 until size) { - val key = addresses.keyAt(i) - contacts[key]?.addresses = addresses.valueAt(i) - } - - val IMs = getIMs() - size = IMs.size() - for (i in 0 until size) { - val key = IMs.keyAt(i) - contacts[key]?.IMs = IMs.valueAt(i) - } - - val events = getEvents() - size = events.size() - for (i in 0 until size) { - val key = events.keyAt(i) - contacts[key]?.events = events.valueAt(i) - } - - val notes = getNotes() - size = notes.size() - for (i in 0 until size) { - val key = notes.keyAt(i) - contacts[key]?.notes = notes.valueAt(i) - } - - val nicknames = getNicknames() - size = nicknames.size() - for (i in 0 until size) { - val key = nicknames.keyAt(i) - contacts[key]?.nickname = nicknames.valueAt(i) - } - - val websites = getWebsites() - size = websites.size() - for (i in 0 until size) { - val key = websites.keyAt(i) - contacts[key]?.websites = websites.valueAt(i) - } - } - - private fun getPhoneNumbers(contactId: Int? = null): SparseArray> { - val phoneNumbers = SparseArray>() - val uri = Phone.CONTENT_URI - val projection = arrayOf( - Data.RAW_CONTACT_ID, - Phone.NUMBER, - Phone.NORMALIZED_NUMBER, - Phone.TYPE, - Phone.LABEL, - Phone.IS_PRIMARY - ) - - val selection = if (contactId == null) getSourcesSelection() else "${Data.RAW_CONTACT_ID} = ?" - val selectionArgs = if (contactId == null) getSourcesSelectionArgs() else arrayOf(contactId.toString()) - - context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor -> - val id = cursor.getIntValue(Data.RAW_CONTACT_ID) - val number = cursor.getStringValue(Phone.NUMBER) ?: return@queryCursor - val normalizedNumber = cursor.getStringValue(Phone.NORMALIZED_NUMBER) ?: number.normalizePhoneNumber() - val type = cursor.getIntValue(Phone.TYPE) - val label = cursor.getStringValue(Phone.LABEL) ?: "" - val isPrimary = cursor.getIntValue(Phone.IS_PRIMARY) != 0 - - if (phoneNumbers[id] == null) { - phoneNumbers.put(id, ArrayList()) - } - - val phoneNumber = PhoneNumber(number, type, label, normalizedNumber, isPrimary) - phoneNumbers[id].add(phoneNumber) - } - - return phoneNumbers - } - - private fun getNicknames(contactId: Int? = null): SparseArray { - val nicknames = SparseArray() - val uri = Data.CONTENT_URI - val projection = arrayOf( - Data.RAW_CONTACT_ID, - Nickname.NAME - ) - - val selection = getSourcesSelection(true, contactId != null) - val selectionArgs = getSourcesSelectionArgs(Nickname.CONTENT_ITEM_TYPE, contactId) - - context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor -> - val id = cursor.getIntValue(Data.RAW_CONTACT_ID) - val nickname = cursor.getStringValue(Nickname.NAME) ?: return@queryCursor - nicknames.put(id, nickname) - } - - return nicknames - } - - private fun getEmails(contactId: Int? = null): SparseArray> { - val emails = SparseArray>() - val uri = CommonDataKinds.Email.CONTENT_URI - val projection = arrayOf( - Data.RAW_CONTACT_ID, - CommonDataKinds.Email.DATA, - CommonDataKinds.Email.TYPE, - CommonDataKinds.Email.LABEL - ) - - val selection = if (contactId == null) getSourcesSelection() else "${Data.RAW_CONTACT_ID} = ?" - val selectionArgs = if (contactId == null) getSourcesSelectionArgs() else arrayOf(contactId.toString()) - - context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor -> - val id = cursor.getIntValue(Data.RAW_CONTACT_ID) - val email = cursor.getStringValue(CommonDataKinds.Email.DATA) ?: return@queryCursor - val type = cursor.getIntValue(CommonDataKinds.Email.TYPE) - val label = cursor.getStringValue(CommonDataKinds.Email.LABEL) ?: "" - - if (emails[id] == null) { - emails.put(id, ArrayList()) - } - - emails[id]!!.add(Email(email, type, label)) - } - - return emails - } - - private fun getAddresses(contactId: Int? = null): SparseArray> { - val addresses = SparseArray>() - val uri = StructuredPostal.CONTENT_URI - val projection = arrayOf( - Data.RAW_CONTACT_ID, - StructuredPostal.FORMATTED_ADDRESS, - StructuredPostal.TYPE, - StructuredPostal.LABEL - ) - - val selection = if (contactId == null) getSourcesSelection() else "${Data.RAW_CONTACT_ID} = ?" - val selectionArgs = if (contactId == null) getSourcesSelectionArgs() else arrayOf(contactId.toString()) - - context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor -> - val id = cursor.getIntValue(Data.RAW_CONTACT_ID) - val address = cursor.getStringValue(StructuredPostal.FORMATTED_ADDRESS) ?: return@queryCursor - val type = cursor.getIntValue(StructuredPostal.TYPE) - val label = cursor.getStringValue(StructuredPostal.LABEL) ?: "" - - if (addresses[id] == null) { - addresses.put(id, ArrayList()) - } - - addresses[id]!!.add(Address(address, type, label)) - } - - return addresses - } - - private fun getIMs(contactId: Int? = null): SparseArray> { - val IMs = SparseArray>() - val uri = Data.CONTENT_URI - val projection = arrayOf( - Data.RAW_CONTACT_ID, - Im.DATA, - Im.PROTOCOL, - Im.CUSTOM_PROTOCOL - ) - - val selection = getSourcesSelection(true, contactId != null) - val selectionArgs = getSourcesSelectionArgs(Im.CONTENT_ITEM_TYPE, contactId) - - context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor -> - val id = cursor.getIntValue(Data.RAW_CONTACT_ID) - val IM = cursor.getStringValue(Im.DATA) ?: return@queryCursor - val type = cursor.getIntValue(Im.PROTOCOL) - val label = cursor.getStringValue(Im.CUSTOM_PROTOCOL) ?: "" - - if (IMs[id] == null) { - IMs.put(id, ArrayList()) - } - - IMs[id]!!.add(IM(IM, type, label)) - } - - return IMs - } - - private fun getEvents(contactId: Int? = null): SparseArray> { - val events = SparseArray>() - val uri = Data.CONTENT_URI - val projection = arrayOf( - Data.RAW_CONTACT_ID, - CommonDataKinds.Event.START_DATE, - CommonDataKinds.Event.TYPE - ) - - val selection = getSourcesSelection(true, contactId != null) - val selectionArgs = getSourcesSelectionArgs(CommonDataKinds.Event.CONTENT_ITEM_TYPE, contactId) - - context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor -> - val id = cursor.getIntValue(Data.RAW_CONTACT_ID) - val startDate = cursor.getStringValue(CommonDataKinds.Event.START_DATE) ?: return@queryCursor - val type = cursor.getIntValue(CommonDataKinds.Event.TYPE) - - if (events[id] == null) { - events.put(id, ArrayList()) - } - - events[id]!!.add(Event(startDate, type)) - } - - return events - } - - private fun getNotes(contactId: Int? = null): SparseArray { - val notes = SparseArray() - val uri = Data.CONTENT_URI - val projection = arrayOf( - Data.RAW_CONTACT_ID, - Note.NOTE - ) - - val selection = getSourcesSelection(true, contactId != null) - val selectionArgs = getSourcesSelectionArgs(Note.CONTENT_ITEM_TYPE, contactId) - - context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor -> - val id = cursor.getIntValue(Data.RAW_CONTACT_ID) - val note = cursor.getStringValue(Note.NOTE) ?: return@queryCursor - notes.put(id, note) - } - - return notes - } - - private fun getOrganizations(contactId: Int? = null): SparseArray { - val organizations = SparseArray() - val uri = Data.CONTENT_URI - val projection = arrayOf( - Data.RAW_CONTACT_ID, - CommonDataKinds.Organization.COMPANY, - CommonDataKinds.Organization.TITLE - ) - - val selection = getSourcesSelection(true, contactId != null) - val selectionArgs = getSourcesSelectionArgs(CommonDataKinds.Organization.CONTENT_ITEM_TYPE, contactId) - - context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor -> - val id = cursor.getIntValue(Data.RAW_CONTACT_ID) - val company = cursor.getStringValue(CommonDataKinds.Organization.COMPANY) ?: "" - val title = cursor.getStringValue(CommonDataKinds.Organization.TITLE) ?: "" - if (company.isEmpty() && title.isEmpty()) { - return@queryCursor - } - - val organization = Organization(company, title) - organizations.put(id, organization) - } - - return organizations - } - - private fun getWebsites(contactId: Int? = null): SparseArray> { - val websites = SparseArray>() - val uri = Data.CONTENT_URI - val projection = arrayOf( - Data.RAW_CONTACT_ID, - Website.URL - ) - - val selection = getSourcesSelection(true, contactId != null) - val selectionArgs = getSourcesSelectionArgs(Website.CONTENT_ITEM_TYPE, contactId) - - context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor -> - val id = cursor.getIntValue(Data.RAW_CONTACT_ID) - val url = cursor.getStringValue(Website.URL) ?: return@queryCursor - - if (websites[id] == null) { - websites.put(id, ArrayList()) - } - - websites[id]!!.add(url) - } - - return websites - } - - private fun getContactGroups(storedGroups: ArrayList, contactId: Int? = null): SparseArray> { - val groups = SparseArray>() - if (!context.hasContactPermissions()) { - return groups - } - - val uri = Data.CONTENT_URI - val projection = arrayOf( - Data.CONTACT_ID, - Data.DATA1 - ) - - val selection = getSourcesSelection(true, contactId != null, false) - val selectionArgs = getSourcesSelectionArgs(GroupMembership.CONTENT_ITEM_TYPE, contactId) - - context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor -> - val id = cursor.getIntValue(Data.CONTACT_ID) - val newRowId = cursor.getLongValue(Data.DATA1) - - val groupTitle = storedGroups.firstOrNull { it.id == newRowId }?.title ?: return@queryCursor - val group = Group(newRowId, groupTitle) - if (groups[id] == null) { - groups.put(id, ArrayList()) - } - groups[id]!!.add(group) - } - - return groups - } - - private fun getQuestionMarks() = ("?," * displayContactSources.filter { it.isNotEmpty() }.size).trimEnd(',') - - private fun getSourcesSelection(addMimeType: Boolean = false, addContactId: Boolean = false, useRawContactId: Boolean = true): String { - val strings = ArrayList() - if (addMimeType) { - strings.add("${Data.MIMETYPE} = ?") - } - - if (addContactId) { - strings.add("${if (useRawContactId) Data.RAW_CONTACT_ID else Data.CONTACT_ID} = ?") - } else { - // sometimes local device storage has null account_name, handle it properly - val accountnameString = StringBuilder() - if (displayContactSources.contains("")) { - accountnameString.append("(") - } - accountnameString.append("${RawContacts.ACCOUNT_NAME} IN (${getQuestionMarks()})") - if (displayContactSources.contains("")) { - accountnameString.append(" OR ${RawContacts.ACCOUNT_NAME} IS NULL)") - } - strings.add(accountnameString.toString()) - } - - return TextUtils.join(" AND ", strings) - } - - private fun getSourcesSelectionArgs(mimetype: String? = null, contactId: Int? = null): Array { - val args = ArrayList() - - if (mimetype != null) { - args.add(mimetype) - } - - if (contactId != null) { - args.add(contactId.toString()) - } else { - args.addAll(displayContactSources.filter { it.isNotEmpty() }) - } - - return args.toTypedArray() - } - - fun getStoredGroups(callback: (ArrayList) -> Unit) { - ensureBackgroundThread { - val groups = getStoredGroupsSync() - Handler(Looper.getMainLooper()).post { - callback(groups) - } - } - } - - fun getStoredGroupsSync(): ArrayList { - val groups = getDeviceStoredGroups() - groups.addAll(context.groupsDB.getGroups()) - return groups - } - - private fun getDeviceStoredGroups(): ArrayList { - val groups = ArrayList() - if (!context.hasContactPermissions()) { - return groups - } - - val uri = Groups.CONTENT_URI - val projection = arrayOf( - Groups._ID, - Groups.TITLE, - Groups.SYSTEM_ID - ) - - val selection = "${Groups.AUTO_ADD} = ? AND ${Groups.FAVORITES} = ?" - val selectionArgs = arrayOf("0", "0") - - context.queryCursor(uri, projection, selection, selectionArgs, showErrors = true) { cursor -> - val id = cursor.getLongValue(Groups._ID) - val title = cursor.getStringValue(Groups.TITLE) ?: return@queryCursor - - val systemId = cursor.getStringValue(Groups.SYSTEM_ID) - if (groups.map { it.title }.contains(title) && systemId != null) { - return@queryCursor - } - - groups.add(Group(id, title)) - } - return groups - } - - fun createNewGroup(title: String, accountName: String, accountType: String): Group? { - if (accountType == SMT_PRIVATE) { - val newGroup = Group(null, title) - val id = context.groupsDB.insertOrUpdate(newGroup) - newGroup.id = id - return newGroup - } - - val operations = ArrayList() - ContentProviderOperation.newInsert(Groups.CONTENT_URI).apply { - withValue(Groups.TITLE, title) - withValue(Groups.GROUP_VISIBLE, 1) - withValue(Groups.ACCOUNT_NAME, accountName) - withValue(Groups.ACCOUNT_TYPE, accountType) - operations.add(build()) - } - - try { - val results = context.contentResolver.applyBatch(AUTHORITY, operations) - val rawId = ContentUris.parseId(results[0].uri!!) - return Group(rawId, title) - } catch (e: Exception) { - context.showErrorToast(e) - } - return null - } - - fun renameGroup(group: Group) { - val operations = ArrayList() - ContentProviderOperation.newUpdate(Groups.CONTENT_URI).apply { - val selection = "${Groups._ID} = ?" - val selectionArgs = arrayOf(group.id.toString()) - withSelection(selection, selectionArgs) - withValue(Groups.TITLE, group.title) - operations.add(build()) - } - - try { - context.contentResolver.applyBatch(AUTHORITY, operations) - } catch (e: Exception) { - context.showErrorToast(e) - } - } - - fun deleteGroup(id: Long) { - val operations = ArrayList() - val uri = ContentUris.withAppendedId(Groups.CONTENT_URI, id).buildUpon() - .appendQueryParameter(CALLER_IS_SYNCADAPTER, "true") - .build() - - operations.add(ContentProviderOperation.newDelete(uri).build()) - - try { - context.contentResolver.applyBatch(AUTHORITY, operations) - } catch (e: Exception) { - context.showErrorToast(e) - } - } - - fun getContactWithId(id: Int, isLocalPrivate: Boolean): Contact? { - if (id == 0) { - return null - } else if (isLocalPrivate) { - return LocalContactsHelper(context).getContactWithId(id) - } - - val selection = "(${Data.MIMETYPE} = ? OR ${Data.MIMETYPE} = ?) AND ${Data.RAW_CONTACT_ID} = ?" - val selectionArgs = arrayOf(StructuredName.CONTENT_ITEM_TYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE, id.toString()) - return parseContactCursor(selection, selectionArgs) - } - - fun getContactWithLookupKey(key: String): Contact? { - val selection = "(${Data.MIMETYPE} = ? OR ${Data.MIMETYPE} = ?) AND ${Data.LOOKUP_KEY} = ?" - val selectionArgs = arrayOf(StructuredName.CONTENT_ITEM_TYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE, key) - return parseContactCursor(selection, selectionArgs) - } - - private fun parseContactCursor(selection: String, selectionArgs: Array): Contact? { - val storedGroups = getStoredGroupsSync() - val uri = Data.CONTENT_URI - val projection = getContactProjection() - - val cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null) - cursor?.use { - if (cursor.moveToFirst()) { - val id = cursor.getIntValue(Data.RAW_CONTACT_ID) - - var prefix = "" - var firstName = "" - var middleName = "" - var surname = "" - var suffix = "" - var mimetype = cursor.getStringValue(Data.MIMETYPE) - - // if first line is an Organization type contact, go to next line - if (mimetype != StructuredName.CONTENT_ITEM_TYPE) { - if (cursor.moveToNext()) { - mimetype = cursor.getStringValue(Data.MIMETYPE) - } - } - // ignore names at Organization type contacts - if (mimetype == StructuredName.CONTENT_ITEM_TYPE) { - prefix = cursor.getStringValue(StructuredName.PREFIX) ?: "" - firstName = cursor.getStringValue(StructuredName.GIVEN_NAME) ?: "" - middleName = cursor.getStringValue(StructuredName.MIDDLE_NAME) ?: "" - surname = cursor.getStringValue(StructuredName.FAMILY_NAME) ?: "" - suffix = cursor.getStringValue(StructuredName.SUFFIX) ?: "" - } - - val nickname = getNicknames(id)[id] ?: "" - val photoUri = cursor.getStringValueOrNull(Phone.PHOTO_URI) ?: "" - val number = getPhoneNumbers(id)[id] ?: ArrayList() - val emails = getEmails(id)[id] ?: ArrayList() - val addresses = getAddresses(id)[id] ?: ArrayList() - val events = getEvents(id)[id] ?: ArrayList() - val notes = getNotes(id)[id] ?: "" - val accountName = cursor.getStringValue(RawContacts.ACCOUNT_NAME) ?: "" - val starred = cursor.getIntValue(StructuredName.STARRED) - val ringtone = cursor.getStringValue(StructuredName.CUSTOM_RINGTONE) - val contactId = cursor.getIntValue(Data.CONTACT_ID) - val groups = getContactGroups(storedGroups, contactId)[contactId] ?: ArrayList() - val thumbnailUri = cursor.getStringValue(StructuredName.PHOTO_THUMBNAIL_URI) ?: "" - val organization = getOrganizations(id)[id] ?: Organization("", "") - val websites = getWebsites(id)[id] ?: ArrayList() - val ims = getIMs(id)[id] ?: ArrayList() - return Contact( - id, prefix, firstName, middleName, surname, suffix, nickname, photoUri, number, emails, addresses, events, - accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites, ims, mimetype, ringtone - ) - } - } - - return null - } - - fun getContactSources(callback: (ArrayList) -> Unit) { - ensureBackgroundThread { - callback(getContactSourcesSync()) - } - } - - private fun getContactSourcesSync(): ArrayList { - val sources = getDeviceContactSources() - sources.add(context.getPrivateContactSource()) - return ArrayList(sources) - } - - fun getSaveableContactSources(callback: (ArrayList) -> Unit) { - ensureBackgroundThread { - val ignoredTypes = arrayListOf( - SIGNAL_PACKAGE, - TELEGRAM_PACKAGE, - WHATSAPP_PACKAGE, - THREEMA_PACKAGE - ) - - val contactSources = getContactSourcesSync() - val filteredSources = contactSources.filter { !ignoredTypes.contains(it.type) }.toMutableList() as ArrayList - callback(filteredSources) - } - } - - fun getDeviceContactSources(): LinkedHashSet { - val sources = LinkedHashSet() - if (!context.hasContactPermissions()) { - return sources - } - - if (!context.config.wasLocalAccountInitialized) { - initializeLocalPhoneAccount() - context.config.wasLocalAccountInitialized = true - } - - val accounts = AccountManager.get(context).accounts - accounts.forEach { - if (ContentResolver.getIsSyncable(it, AUTHORITY) == 1) { - var publicName = it.name - if (it.type == TELEGRAM_PACKAGE) { - publicName = context.getString(R.string.telegram) - } else if (it.type == VIBER_PACKAGE) { - publicName = context.getString(R.string.viber) - } - val contactSource = ContactSource(it.name, it.type, publicName) - sources.add(contactSource) - } - } - - var hadEmptyAccount = false - val allAccounts = getContentResolverAccounts() - val contentResolverAccounts = allAccounts.filter { - if (it.name.isEmpty() && it.type.isEmpty() && allAccounts.none { it.name.lowercase(Locale.getDefault()) == "phone" }) { - hadEmptyAccount = true - } - - it.name.isNotEmpty() && it.type.isNotEmpty() && !accounts.contains(Account(it.name, it.type)) - } - sources.addAll(contentResolverAccounts) - - if (hadEmptyAccount) { - sources.add(ContactSource("", "", context.getString(R.string.phone_storage))) - } - - return sources - } - - // make sure the local Phone contact source is initialized and available - // https://stackoverflow.com/a/6096508/1967672 - private fun initializeLocalPhoneAccount() { - try { - val operations = ArrayList() - ContentProviderOperation.newInsert(RawContacts.CONTENT_URI).apply { - withValue(RawContacts.ACCOUNT_NAME, null) - withValue(RawContacts.ACCOUNT_TYPE, null) - operations.add(build()) - } - - val results = context.contentResolver.applyBatch(AUTHORITY, operations) - val rawContactUri = results.firstOrNull()?.uri ?: return - context.contentResolver.delete(rawContactUri, null, null) - } catch (ignored: Exception) { - } - } - - private fun getContactSourceType(accountName: String) = getDeviceContactSources().firstOrNull { it.name == accountName }?.type ?: "" - - private fun getContactProjection() = arrayOf( - Data.MIMETYPE, - Data.CONTACT_ID, - Data.RAW_CONTACT_ID, - StructuredName.PREFIX, - StructuredName.GIVEN_NAME, - StructuredName.MIDDLE_NAME, - StructuredName.FAMILY_NAME, - StructuredName.SUFFIX, - StructuredName.PHOTO_URI, - StructuredName.PHOTO_THUMBNAIL_URI, - StructuredName.STARRED, - StructuredName.CUSTOM_RINGTONE, - RawContacts.ACCOUNT_NAME, - RawContacts.ACCOUNT_TYPE - ) - - private fun getSortString(): String { - val sorting = context.config.sorting - return when { - sorting and SORT_BY_FIRST_NAME != 0 -> "${StructuredName.GIVEN_NAME} COLLATE NOCASE" - sorting and SORT_BY_MIDDLE_NAME != 0 -> "${StructuredName.MIDDLE_NAME} COLLATE NOCASE" - sorting and SORT_BY_SURNAME != 0 -> "${StructuredName.FAMILY_NAME} COLLATE NOCASE" - sorting and SORT_BY_FULL_NAME != 0 -> StructuredName.DISPLAY_NAME - else -> Data.RAW_CONTACT_ID - } - } - - private fun getRealContactId(id: Long): Int { - val uri = Data.CONTENT_URI - val projection = getContactProjection() - val selection = "(${Data.MIMETYPE} = ? OR ${Data.MIMETYPE} = ?) AND ${Data.RAW_CONTACT_ID} = ?" - val selectionArgs = arrayOf(StructuredName.CONTENT_ITEM_TYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE, id.toString()) - - val cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null) - cursor?.use { - if (cursor.moveToFirst()) { - return cursor.getIntValue(Data.CONTACT_ID) - } - } - - return 0 - } - - fun updateContact(contact: Contact, photoUpdateStatus: Int): Boolean { - context.toast(R.string.updating) - if (contact.isPrivate()) { - return LocalContactsHelper(context).insertOrUpdateContact(contact) - } - - try { - val operations = ArrayList() - ContentProviderOperation.newUpdate(Data.CONTENT_URI).apply { - val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ?" - val selectionArgs = arrayOf(contact.id.toString(), contact.mimetype) - withSelection(selection, selectionArgs) - withValue(StructuredName.PREFIX, contact.prefix) - withValue(StructuredName.GIVEN_NAME, contact.firstName) - withValue(StructuredName.MIDDLE_NAME, contact.middleName) - withValue(StructuredName.FAMILY_NAME, contact.surname) - withValue(StructuredName.SUFFIX, contact.suffix) - operations.add(build()) - } - - // delete nickname - ContentProviderOperation.newDelete(Data.CONTENT_URI).apply { - val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? " - val selectionArgs = arrayOf(contact.id.toString(), Nickname.CONTENT_ITEM_TYPE) - withSelection(selection, selectionArgs) - operations.add(build()) - } - - // add nickname - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValue(Data.RAW_CONTACT_ID, contact.id) - withValue(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE) - withValue(Nickname.NAME, contact.nickname) - operations.add(build()) - } - - // delete phone numbers - ContentProviderOperation.newDelete(Data.CONTENT_URI).apply { - val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? " - val selectionArgs = arrayOf(contact.id.toString(), Phone.CONTENT_ITEM_TYPE) - withSelection(selection, selectionArgs) - operations.add(build()) - } - - // add phone numbers - contact.phoneNumbers.forEach { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValue(Data.RAW_CONTACT_ID, contact.id) - withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE) - withValue(Phone.NUMBER, it.value) - withValue(Phone.NORMALIZED_NUMBER, it.normalizedNumber) - withValue(Phone.TYPE, it.type) - withValue(Phone.LABEL, it.label) - withValue(Phone.IS_PRIMARY, it.isPrimary) - operations.add(build()) - } - } - - // delete emails - ContentProviderOperation.newDelete(Data.CONTENT_URI).apply { - val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? " - val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Email.CONTENT_ITEM_TYPE) - withSelection(selection, selectionArgs) - operations.add(build()) - } - - // add emails - contact.emails.forEach { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValue(Data.RAW_CONTACT_ID, contact.id) - withValue(Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE) - withValue(CommonDataKinds.Email.DATA, it.value) - withValue(CommonDataKinds.Email.TYPE, it.type) - withValue(CommonDataKinds.Email.LABEL, it.label) - operations.add(build()) - } - } - - // delete addresses - ContentProviderOperation.newDelete(Data.CONTENT_URI).apply { - val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? " - val selectionArgs = arrayOf(contact.id.toString(), StructuredPostal.CONTENT_ITEM_TYPE) - withSelection(selection, selectionArgs) - operations.add(build()) - } - - // add addresses - contact.addresses.forEach { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValue(Data.RAW_CONTACT_ID, contact.id) - withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE) - withValue(StructuredPostal.FORMATTED_ADDRESS, it.value) - withValue(StructuredPostal.TYPE, it.type) - withValue(StructuredPostal.LABEL, it.label) - operations.add(build()) - } - } - - // delete IMs - ContentProviderOperation.newDelete(Data.CONTENT_URI).apply { - val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? " - val selectionArgs = arrayOf(contact.id.toString(), Im.CONTENT_ITEM_TYPE) - withSelection(selection, selectionArgs) - operations.add(build()) - } - - // add IMs - contact.IMs.forEach { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValue(Data.RAW_CONTACT_ID, contact.id) - withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE) - withValue(Im.DATA, it.value) - withValue(Im.PROTOCOL, it.type) - withValue(Im.CUSTOM_PROTOCOL, it.label) - operations.add(build()) - } - } - - // delete events - ContentProviderOperation.newDelete(Data.CONTENT_URI).apply { - val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? " - val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Event.CONTENT_ITEM_TYPE) - withSelection(selection, selectionArgs) - operations.add(build()) - } - - // add events - contact.events.forEach { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValue(Data.RAW_CONTACT_ID, contact.id) - withValue(Data.MIMETYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE) - withValue(CommonDataKinds.Event.START_DATE, it.value) - withValue(CommonDataKinds.Event.TYPE, it.type) - operations.add(build()) - } - } - - // delete notes - ContentProviderOperation.newDelete(Data.CONTENT_URI).apply { - val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? " - val selectionArgs = arrayOf(contact.id.toString(), Note.CONTENT_ITEM_TYPE) - withSelection(selection, selectionArgs) - operations.add(build()) - } - - // add notes - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValue(Data.RAW_CONTACT_ID, contact.id) - withValue(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE) - withValue(Note.NOTE, contact.notes) - operations.add(build()) - } - - // delete organization - ContentProviderOperation.newDelete(Data.CONTENT_URI).apply { - val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? " - val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Organization.CONTENT_ITEM_TYPE) - withSelection(selection, selectionArgs) - operations.add(build()) - } - - // add organization - if (contact.organization.isNotEmpty()) { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValue(Data.RAW_CONTACT_ID, contact.id) - withValue(Data.MIMETYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE) - withValue(CommonDataKinds.Organization.COMPANY, contact.organization.company) - withValue(CommonDataKinds.Organization.TYPE, DEFAULT_ORGANIZATION_TYPE) - withValue(CommonDataKinds.Organization.TITLE, contact.organization.jobPosition) - withValue(CommonDataKinds.Organization.TYPE, DEFAULT_ORGANIZATION_TYPE) - operations.add(build()) - } - } - - // delete websites - ContentProviderOperation.newDelete(Data.CONTENT_URI).apply { - val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? " - val selectionArgs = arrayOf(contact.id.toString(), Website.CONTENT_ITEM_TYPE) - withSelection(selection, selectionArgs) - operations.add(build()) - } - - // add websites - contact.websites.forEach { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValue(Data.RAW_CONTACT_ID, contact.id) - withValue(Data.MIMETYPE, Website.CONTENT_ITEM_TYPE) - withValue(Website.URL, it) - withValue(Website.TYPE, DEFAULT_WEBSITE_TYPE) - operations.add(build()) - } - } - - // delete groups - val relevantGroupIDs = getStoredGroupsSync().map { it.id } - if (relevantGroupIDs.isNotEmpty()) { - val IDsString = TextUtils.join(",", relevantGroupIDs) - ContentProviderOperation.newDelete(Data.CONTENT_URI).apply { - val selection = "${Data.CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? AND ${Data.DATA1} IN ($IDsString)" - val selectionArgs = arrayOf(contact.contactId.toString(), GroupMembership.CONTENT_ITEM_TYPE) - withSelection(selection, selectionArgs) - operations.add(build()) - } - } - - // add groups - contact.groups.forEach { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValue(Data.RAW_CONTACT_ID, contact.id) - withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE) - withValue(GroupMembership.GROUP_ROW_ID, it.id) - operations.add(build()) - } - } - - // favorite, ringtone - try { - val uri = Uri.withAppendedPath(Contacts.CONTENT_URI, contact.contactId.toString()) - val contentValues = ContentValues(2) - contentValues.put(Contacts.STARRED, contact.starred) - contentValues.put(Contacts.CUSTOM_RINGTONE, contact.ringtone) - context.contentResolver.update(uri, contentValues, null, null) - } catch (e: Exception) { - context.showErrorToast(e) - } - - // photo - when (photoUpdateStatus) { - PHOTO_ADDED, PHOTO_CHANGED -> addPhoto(contact, operations) - PHOTO_REMOVED -> removePhoto(contact, operations) - } - - context.contentResolver.applyBatch(AUTHORITY, operations) - return true - } catch (e: Exception) { - context.showErrorToast(e) - return false - } - } - - private fun addPhoto(contact: Contact, operations: ArrayList): ArrayList { - if (contact.photoUri.isNotEmpty()) { - val photoUri = Uri.parse(contact.photoUri) - val bitmap = MediaStore.Images.Media.getBitmap(context.contentResolver, photoUri) - - val thumbnailSize = context.getPhotoThumbnailSize() - val scaledPhoto = Bitmap.createScaledBitmap(bitmap, thumbnailSize, thumbnailSize, false) - val scaledSizePhotoData = scaledPhoto.getByteArray() - scaledPhoto.recycle() - - val fullSizePhotoData = bitmap.getByteArray() - bitmap.recycle() - - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValue(Data.RAW_CONTACT_ID, contact.id) - withValue(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE) - withValue(Photo.PHOTO, scaledSizePhotoData) - operations.add(build()) - } - - addFullSizePhoto(contact.id.toLong(), fullSizePhotoData) - } - return operations - } - - private fun removePhoto(contact: Contact, operations: ArrayList): ArrayList { - ContentProviderOperation.newDelete(Data.CONTENT_URI).apply { - val selection = "${Data.RAW_CONTACT_ID} = ? AND ${Data.MIMETYPE} = ?" - val selectionArgs = arrayOf(contact.id.toString(), Photo.CONTENT_ITEM_TYPE) - withSelection(selection, selectionArgs) - operations.add(build()) - } - - return operations - } - - fun addContactsToGroup(contacts: ArrayList, groupId: Long) { - try { - val operations = ArrayList() - contacts.forEach { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValue(Data.RAW_CONTACT_ID, it.id) - withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE) - withValue(GroupMembership.GROUP_ROW_ID, groupId) - operations.add(build()) - } - - if (operations.size % BATCH_SIZE == 0) { - context.contentResolver.applyBatch(AUTHORITY, operations) - operations.clear() - } - } - - context.contentResolver.applyBatch(AUTHORITY, operations) - } catch (e: Exception) { - context.showErrorToast(e) - } - } - - fun removeContactsFromGroup(contacts: ArrayList, groupId: Long) { - try { - val operations = ArrayList() - contacts.forEach { - ContentProviderOperation.newDelete(Data.CONTENT_URI).apply { - val selection = "${Data.CONTACT_ID} = ? AND ${Data.MIMETYPE} = ? AND ${Data.DATA1} = ?" - val selectionArgs = arrayOf(it.contactId.toString(), GroupMembership.CONTENT_ITEM_TYPE, groupId.toString()) - withSelection(selection, selectionArgs) - operations.add(build()) - } - - if (operations.size % BATCH_SIZE == 0) { - context.contentResolver.applyBatch(AUTHORITY, operations) - operations.clear() - } - } - context.contentResolver.applyBatch(AUTHORITY, operations) - } catch (e: Exception) { - context.showErrorToast(e) - } - } - - fun insertContact(contact: Contact): Boolean { - if (contact.isPrivate()) { - return LocalContactsHelper(context).insertOrUpdateContact(contact) - } - - try { - val operations = ArrayList() - ContentProviderOperation.newInsert(RawContacts.CONTENT_URI).apply { - withValue(RawContacts.ACCOUNT_NAME, contact.source) - withValue(RawContacts.ACCOUNT_TYPE, getContactSourceType(contact.source)) - operations.add(build()) - } - - // names - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValueBackReference(Data.RAW_CONTACT_ID, 0) - withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) - withValue(StructuredName.PREFIX, contact.prefix) - withValue(StructuredName.GIVEN_NAME, contact.firstName) - withValue(StructuredName.MIDDLE_NAME, contact.middleName) - withValue(StructuredName.FAMILY_NAME, contact.surname) - withValue(StructuredName.SUFFIX, contact.suffix) - operations.add(build()) - } - - // nickname - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValueBackReference(Data.RAW_CONTACT_ID, 0) - withValue(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE) - withValue(Nickname.NAME, contact.nickname) - operations.add(build()) - } - - // phone numbers - contact.phoneNumbers.forEach { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValueBackReference(Data.RAW_CONTACT_ID, 0) - withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE) - withValue(Phone.NUMBER, it.value) - withValue(Phone.NORMALIZED_NUMBER, it.normalizedNumber) - withValue(Phone.TYPE, it.type) - withValue(Phone.LABEL, it.label) - withValue(Phone.IS_PRIMARY, it.isPrimary) - operations.add(build()) - } - } - - // emails - contact.emails.forEach { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValueBackReference(Data.RAW_CONTACT_ID, 0) - withValue(Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE) - withValue(CommonDataKinds.Email.DATA, it.value) - withValue(CommonDataKinds.Email.TYPE, it.type) - withValue(CommonDataKinds.Email.LABEL, it.label) - operations.add(build()) - } - } - - // addresses - contact.addresses.forEach { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValueBackReference(Data.RAW_CONTACT_ID, 0) - withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE) - withValue(StructuredPostal.FORMATTED_ADDRESS, it.value) - withValue(StructuredPostal.TYPE, it.type) - withValue(StructuredPostal.LABEL, it.label) - operations.add(build()) - } - } - - // IMs - contact.IMs.forEach { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValueBackReference(Data.RAW_CONTACT_ID, 0) - withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE) - withValue(Im.DATA, it.value) - withValue(Im.PROTOCOL, it.type) - withValue(Im.CUSTOM_PROTOCOL, it.label) - operations.add(build()) - } - } - - // events - contact.events.forEach { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValueBackReference(Data.RAW_CONTACT_ID, 0) - withValue(Data.MIMETYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE) - withValue(CommonDataKinds.Event.START_DATE, it.value) - withValue(CommonDataKinds.Event.TYPE, it.type) - operations.add(build()) - } - } - - // notes - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValueBackReference(Data.RAW_CONTACT_ID, 0) - withValue(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE) - withValue(Note.NOTE, contact.notes) - operations.add(build()) - } - - // organization - if (contact.organization.isNotEmpty()) { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValueBackReference(Data.RAW_CONTACT_ID, 0) - withValue(Data.MIMETYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE) - withValue(CommonDataKinds.Organization.COMPANY, contact.organization.company) - withValue(CommonDataKinds.Organization.TYPE, DEFAULT_ORGANIZATION_TYPE) - withValue(CommonDataKinds.Organization.TITLE, contact.organization.jobPosition) - withValue(CommonDataKinds.Organization.TYPE, DEFAULT_ORGANIZATION_TYPE) - operations.add(build()) - } - } - - // websites - contact.websites.forEach { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValueBackReference(Data.RAW_CONTACT_ID, 0) - withValue(Data.MIMETYPE, Website.CONTENT_ITEM_TYPE) - withValue(Website.URL, it) - withValue(Website.TYPE, DEFAULT_WEBSITE_TYPE) - operations.add(build()) - } - } - - // groups - contact.groups.forEach { - ContentProviderOperation.newInsert(Data.CONTENT_URI).apply { - withValueBackReference(Data.RAW_CONTACT_ID, 0) - withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE) - withValue(GroupMembership.GROUP_ROW_ID, it.id) - operations.add(build()) - } - } - - // photo (inspired by https://gist.github.com/slightfoot/5985900) - var fullSizePhotoData: ByteArray? = null - if (contact.photoUri.isNotEmpty()) { - val photoUri = Uri.parse(contact.photoUri) - fullSizePhotoData = context.contentResolver.openInputStream(photoUri)?.readBytes() - } - - val results = context.contentResolver.applyBatch(AUTHORITY, operations) - - // storing contacts on some devices seems to be messed up and they move on Phone instead, or disappear completely - // try storing a lighter contact version with this oldschool version too just so it wont disappear, future edits work well - if (getContactSourceType(contact.source).contains(".sim")) { - val simUri = Uri.parse("content://icc/adn") - ContentValues().apply { - put("number", contact.phoneNumbers.firstOrNull()?.value ?: "") - put("tag", contact.getNameToDisplay()) - context.contentResolver.insert(simUri, this) - } - } - - // fullsize photo - val rawId = ContentUris.parseId(results[0].uri!!) - if (contact.photoUri.isNotEmpty() && fullSizePhotoData != null) { - addFullSizePhoto(rawId, fullSizePhotoData) - } - - // favorite, ringtone - val userId = getRealContactId(rawId) - if (userId != 0) { - val uri = Uri.withAppendedPath(Contacts.CONTENT_URI, userId.toString()) - val contentValues = ContentValues(2) - contentValues.put(Contacts.STARRED, contact.starred) - contentValues.put(Contacts.CUSTOM_RINGTONE, contact.ringtone) - context.contentResolver.update(uri, contentValues, null, null) - } - - return true - } catch (e: Exception) { - context.showErrorToast(e) - return false - } - } - - private fun addFullSizePhoto(contactId: Long, fullSizePhotoData: ByteArray) { - val baseUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, contactId) - val displayPhotoUri = Uri.withAppendedPath(baseUri, RawContacts.DisplayPhoto.CONTENT_DIRECTORY) - val fileDescriptor = context.contentResolver.openAssetFileDescriptor(displayPhotoUri, "rw") - val photoStream = fileDescriptor!!.createOutputStream() - photoStream.write(fullSizePhotoData) - photoStream.close() - fileDescriptor.close() - } - - fun getContactMimeTypeId(contactId: String, mimeType: String): String { - val uri = Data.CONTENT_URI - val projection = arrayOf(Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE) - val selection = "${Data.MIMETYPE} = ? AND ${Data.RAW_CONTACT_ID} = ?" - val selectionArgs = arrayOf(mimeType, contactId) - - - val cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null) - cursor?.use { - if (cursor.moveToFirst()) { - return cursor.getStringValue(Data._ID) - } - } - return "" - } - - fun addFavorites(contacts: ArrayList) { - ensureBackgroundThread { - toggleLocalFavorites(contacts, true) - if (context.hasContactPermissions()) { - toggleFavorites(contacts, true) - } - } - } - - fun removeFavorites(contacts: ArrayList) { - ensureBackgroundThread { - toggleLocalFavorites(contacts, false) - if (context.hasContactPermissions()) { - toggleFavorites(contacts, false) - } - } - } - - private fun toggleFavorites(contacts: ArrayList, addToFavorites: Boolean) { - try { - val operations = ArrayList() - contacts.filter { !it.isPrivate() }.map { it.contactId.toString() }.forEach { - val uri = Uri.withAppendedPath(Contacts.CONTENT_URI, it) - ContentProviderOperation.newUpdate(uri).apply { - withValue(Contacts.STARRED, if (addToFavorites) 1 else 0) - operations.add(build()) - } - - if (operations.size % BATCH_SIZE == 0) { - context.contentResolver.applyBatch(AUTHORITY, operations) - operations.clear() - } - } - context.contentResolver.applyBatch(AUTHORITY, operations) - } catch (e: Exception) { - context.showErrorToast(e) - } - } - - private fun toggleLocalFavorites(contacts: ArrayList, addToFavorites: Boolean) { - val localContacts = contacts.filter { it.isPrivate() }.map { it.id }.toTypedArray() - LocalContactsHelper(context).toggleFavorites(localContacts, addToFavorites) - } - - fun updateRingtone(contactId: String, newUri: String) { - try { - val operations = ArrayList() - val uri = Uri.withAppendedPath(Contacts.CONTENT_URI, contactId) - ContentProviderOperation.newUpdate(uri).apply { - withValue(Contacts.CUSTOM_RINGTONE, newUri) - operations.add(build()) - } - - context.contentResolver.applyBatch(AUTHORITY, operations) - } catch (e: Exception) { - context.showErrorToast(e) - } - } - - fun deleteContact(originalContact: Contact, deleteClones: Boolean = false, callback: (success: Boolean) -> Unit) { - ensureBackgroundThread { - if (deleteClones) { - getDuplicatesOfContact(originalContact, true) { contacts -> - ensureBackgroundThread { - if (deleteContacts(contacts)) { - callback(true) - } - } - } - } else { - if (deleteContacts(arrayListOf(originalContact))) { - callback(true) - } - } - } - } - - fun deleteContacts(contacts: ArrayList): Boolean { - val localContacts = contacts.filter { it.isPrivate() }.map { it.id.toLong() }.toMutableList() - LocalContactsHelper(context).deleteContactIds(localContacts) - - return try { - val operations = ArrayList() - val selection = "${RawContacts._ID} = ?" - contacts.filter { !it.isPrivate() }.forEach { - ContentProviderOperation.newDelete(RawContacts.CONTENT_URI).apply { - val selectionArgs = arrayOf(it.id.toString()) - withSelection(selection, selectionArgs) - operations.add(build()) - } - - if (operations.size % BATCH_SIZE == 0) { - context.contentResolver.applyBatch(AUTHORITY, operations) - operations.clear() - } - } - - if (context.hasPermission(PERMISSION_WRITE_CONTACTS)) { - context.contentResolver.applyBatch(AUTHORITY, operations) - } - true - } catch (e: Exception) { - context.showErrorToast(e) - false - } - } - - fun getDuplicatesOfContact(contact: Contact, addOriginal: Boolean, callback: (ArrayList) -> Unit) { - ensureBackgroundThread { - getContacts(true, true) { contacts -> - val duplicates = - contacts.filter { it.id != contact.id && it.getHashToCompare() == contact.getHashToCompare() }.toMutableList() as ArrayList - if (addOriginal) { - duplicates.add(contact) - } - callback(duplicates) - } - } - } -} diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Converters.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Converters.kt deleted file mode 100644 index ee8aa14c..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/Converters.kt +++ /dev/null @@ -1,76 +0,0 @@ -package com.simplemobiletools.contacts.pro.helpers - -import androidx.room.TypeConverter -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken -import com.simplemobiletools.commons.models.PhoneNumber -import com.simplemobiletools.contacts.pro.models.* - -class Converters { - private val gson = Gson() - private val longType = object : TypeToken>() {}.type - private val stringType = object : TypeToken>() {}.type - private val numberType = object : TypeToken>() {}.type - private val numberConverterType = object : TypeToken>() {}.type - private val emailType = object : TypeToken>() {}.type - private val addressType = object : TypeToken>() {}.type - private val eventType = object : TypeToken>() {}.type - private val imType = object : TypeToken>() {}.type - - @TypeConverter - fun jsonToStringList(value: String) = gson.fromJson>(value, stringType) - - @TypeConverter - fun stringListToJson(list: ArrayList) = gson.toJson(list) - - @TypeConverter - fun jsonToLongList(value: String) = gson.fromJson>(value, longType) - - @TypeConverter - fun longListToJson(list: ArrayList) = gson.toJson(list) - - // some hacky converting is needed since PhoneNumber model has been added to proguard rules, but obfuscated json was stored in database - // convert [{"a":"678910","b":2,"c":"","d":"678910","e":false}] to PhoneNumber(value=678910, type=2, label=, normalizedNumber=678910, isPrimary=false) - @TypeConverter - fun jsonToPhoneNumberList(value: String): ArrayList { - val numbers = gson.fromJson>(value, numberType) - return if (numbers.any { it.value == null }) { - val phoneNumbers = ArrayList() - val numberConverters = gson.fromJson>(value, numberConverterType) - numberConverters.forEach { converter -> - val phoneNumber = PhoneNumber(converter.a, converter.b, converter.c, converter.d, converter.e) - phoneNumbers.add(phoneNumber) - } - phoneNumbers - } else { - numbers - } - } - - @TypeConverter - fun phoneNumberListToJson(list: ArrayList) = gson.toJson(list) - - @TypeConverter - fun jsonToEmailList(value: String) = gson.fromJson>(value, emailType) - - @TypeConverter - fun emailListToJson(list: ArrayList) = gson.toJson(list) - - @TypeConverter - fun jsonToAddressList(value: String) = gson.fromJson>(value, addressType) - - @TypeConverter - fun addressListToJson(list: ArrayList
) = gson.toJson(list) - - @TypeConverter - fun jsonToEventList(value: String) = gson.fromJson>(value, eventType) - - @TypeConverter - fun eventListToJson(list: ArrayList) = gson.toJson(list) - - @TypeConverter - fun jsonToIMsList(value: String) = gson.fromJson>(value, imType) - - @TypeConverter - fun IMsListToJson(list: ArrayList) = gson.toJson(list) -} diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/LocalContactsHelper.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/LocalContactsHelper.kt deleted file mode 100644 index de3b760e..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/LocalContactsHelper.kt +++ /dev/null @@ -1,173 +0,0 @@ -package com.simplemobiletools.contacts.pro.helpers - -import android.content.Context -import android.graphics.BitmapFactory -import android.net.Uri -import android.provider.ContactsContract.CommonDataKinds.Event -import android.provider.MediaStore -import com.simplemobiletools.commons.extensions.getByteArray -import com.simplemobiletools.commons.helpers.SMT_PRIVATE -import com.simplemobiletools.commons.models.SimpleContact -import com.simplemobiletools.contacts.pro.extensions.contactsDB -import com.simplemobiletools.contacts.pro.extensions.getEmptyContact -import com.simplemobiletools.contacts.pro.models.Contact -import com.simplemobiletools.contacts.pro.models.Group -import com.simplemobiletools.contacts.pro.models.LocalContact -import com.simplemobiletools.contacts.pro.models.Organization - -class LocalContactsHelper(val context: Context) { - fun getAllContacts(favoritesOnly: Boolean = false): ArrayList { - val contacts = if (favoritesOnly) context.contactsDB.getFavoriteContacts() else context.contactsDB.getContacts() - val storedGroups = ContactsHelper(context).getStoredGroupsSync() - return contacts.map { convertLocalContactToContact(it, storedGroups) }.toMutableList() as ArrayList - } - - fun getContactWithId(id: Int): Contact? { - val storedGroups = ContactsHelper(context).getStoredGroupsSync() - return convertLocalContactToContact(context.contactsDB.getContactWithId(id), storedGroups) - } - - fun insertOrUpdateContact(contact: Contact): Boolean { - val localContact = convertContactToLocalContact(contact) - return context.contactsDB.insertOrUpdate(localContact) > 0 - } - - fun addContactsToGroup(contacts: ArrayList, groupId: Long) { - contacts.forEach { - val localContact = convertContactToLocalContact(it) - val newGroups = localContact.groups - newGroups.add(groupId) - newGroups.distinct() - localContact.groups = newGroups - context.contactsDB.insertOrUpdate(localContact) - } - } - - fun removeContactsFromGroup(contacts: ArrayList, groupId: Long) { - contacts.forEach { - val localContact = convertContactToLocalContact(it) - val newGroups = localContact.groups - newGroups.remove(groupId) - localContact.groups = newGroups - context.contactsDB.insertOrUpdate(localContact) - } - } - - fun deleteContactIds(ids: MutableList) { - ids.chunked(30).forEach { - context.contactsDB.deleteContactIds(it) - } - } - - fun toggleFavorites(ids: Array, addToFavorites: Boolean) { - val isStarred = if (addToFavorites) 1 else 0 - ids.forEach { - context.contactsDB.updateStarred(isStarred, it) - } - } - - fun updateRingtone(id: Int, ringtone: String) { - context.contactsDB.updateRingtone(ringtone, id) - } - - private fun getPhotoByteArray(uri: String): ByteArray { - if (uri.isEmpty()) { - return ByteArray(0) - } - - val photoUri = Uri.parse(uri) - val bitmap = MediaStore.Images.Media.getBitmap(context.contentResolver, photoUri) - - val fullSizePhotoData = bitmap.getByteArray() - bitmap.recycle() - - return fullSizePhotoData - } - - private fun convertLocalContactToContact(localContact: LocalContact?, storedGroups: ArrayList): Contact? { - if (localContact == null) { - return null - } - - val contactPhoto = if (localContact.photo == null) { - null - } else { - try { - BitmapFactory.decodeByteArray(localContact.photo, 0, localContact.photo!!.size) - } catch (e: OutOfMemoryError) { - null - } - } - - return context.getEmptyContact().apply { - id = localContact.id!! - prefix = localContact.prefix - firstName = localContact.firstName - middleName = localContact.middleName - surname = localContact.surname - suffix = localContact.suffix - nickname = localContact.nickname - phoneNumbers = localContact.phoneNumbers - emails = localContact.emails - addresses = localContact.addresses - events = localContact.events - source = SMT_PRIVATE - starred = localContact.starred - contactId = localContact.id!! - thumbnailUri = "" - photo = contactPhoto - photoUri = localContact.photoUri - notes = localContact.notes - groups = storedGroups.filter { localContact.groups.contains(it.id) } as ArrayList - organization = Organization(localContact.company, localContact.jobPosition) - websites = localContact.websites - IMs = localContact.IMs - ringtone = localContact.ringtone - } - } - - private fun convertContactToLocalContact(contact: Contact): LocalContact { - val photoByteArray = if (contact.photoUri.isNotEmpty()) { - getPhotoByteArray(contact.photoUri) - } else { - contact.photo?.getByteArray() - } - - return getEmptyLocalContact().apply { - id = if (contact.id <= FIRST_CONTACT_ID) null else contact.id - prefix = contact.prefix - firstName = contact.firstName - middleName = contact.middleName - surname = contact.surname - suffix = contact.suffix - nickname = contact.nickname - photo = photoByteArray - phoneNumbers = contact.phoneNumbers - emails = contact.emails - events = contact.events - starred = contact.starred - addresses = contact.addresses - notes = contact.notes - groups = contact.groups.map { it.id }.toMutableList() as ArrayList - company = contact.organization.company - jobPosition = contact.organization.jobPosition - websites = contact.websites - IMs = contact.IMs - ringtone = contact.ringtone - } - } - - private fun convertContactToSimpleContact(contact: Contact?, withPhoneNumbersOnly: Boolean): SimpleContact? { - return if (contact == null || (withPhoneNumbersOnly && contact.phoneNumbers.isEmpty())) { - null - } else { - val birthdays = contact.events.filter { it.type == Event.TYPE_BIRTHDAY }.map { it.value }.toMutableList() as ArrayList - val anniversaries = contact.events.filter { it.type == Event.TYPE_ANNIVERSARY }.map { it.value }.toMutableList() as ArrayList - SimpleContact(contact.id, contact.id, contact.getNameToDisplay(), contact.photoUri, contact.phoneNumbers, birthdays, anniversaries) - } - } - - fun getPrivateSimpleContactsSync(favoritesOnly: Boolean, withPhoneNumbersOnly: Boolean) = getAllContacts(favoritesOnly).mapNotNull { - convertContactToSimpleContact(it, withPhoneNumbersOnly) - } -} diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfExporter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfExporter.kt index 6b8415c7..87a9b3fd 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfExporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfExporter.kt @@ -12,9 +12,9 @@ import com.simplemobiletools.commons.extensions.getByteArray import com.simplemobiletools.commons.extensions.getDateTimeFromDateString import com.simplemobiletools.commons.extensions.showErrorToast import com.simplemobiletools.commons.extensions.toast +import com.simplemobiletools.commons.models.contacts.Contact import com.simplemobiletools.contacts.pro.R import com.simplemobiletools.contacts.pro.helpers.VcfExporter.ExportResult.EXPORT_FAIL -import com.simplemobiletools.contacts.pro.models.Contact import ezvcard.Ezvcard import ezvcard.VCard import ezvcard.VCardVersion diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfImporter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfImporter.kt index c23c1738..a48f1f86 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/helpers/VcfImporter.kt @@ -7,17 +7,19 @@ import android.provider.ContactsContract.CommonDataKinds.Im import android.provider.ContactsContract.CommonDataKinds.Phone import android.provider.ContactsContract.CommonDataKinds.StructuredPostal import android.widget.Toast +import com.simplemobiletools.commons.extensions.groupsDB import com.simplemobiletools.commons.extensions.normalizePhoneNumber import com.simplemobiletools.commons.extensions.showErrorToast -import com.simplemobiletools.commons.models.PhoneNumber -import com.simplemobiletools.contacts.pro.activities.SimpleActivity -import com.simplemobiletools.contacts.pro.extensions.getCachePhoto +import com.simplemobiletools.commons.extensions.getCachePhoto import com.simplemobiletools.contacts.pro.extensions.getCachePhotoUri -import com.simplemobiletools.contacts.pro.extensions.groupsDB +import com.simplemobiletools.commons.helpers.ContactsHelper +import com.simplemobiletools.commons.helpers.DEFAULT_MIMETYPE +import com.simplemobiletools.commons.models.PhoneNumber +import com.simplemobiletools.commons.models.contacts.* +import com.simplemobiletools.contacts.pro.activities.SimpleActivity import com.simplemobiletools.contacts.pro.helpers.VcfImporter.ImportResult.IMPORT_FAIL import com.simplemobiletools.contacts.pro.helpers.VcfImporter.ImportResult.IMPORT_OK import com.simplemobiletools.contacts.pro.helpers.VcfImporter.ImportResult.IMPORT_PARTIAL -import com.simplemobiletools.contacts.pro.models.* import ezvcard.Ezvcard import ezvcard.VCard import ezvcard.util.PartialDate diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/interfaces/ContactsDao.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/interfaces/ContactsDao.kt deleted file mode 100644 index b4a7bda9..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/interfaces/ContactsDao.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.simplemobiletools.contacts.pro.interfaces - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import com.simplemobiletools.contacts.pro.models.LocalContact - -@Dao -interface ContactsDao { - @Query("SELECT * FROM contacts") - fun getContacts(): List - - @Query("SELECT * FROM contacts WHERE starred = 1") - fun getFavoriteContacts(): List - - @Query("SELECT * FROM contacts WHERE id = :id") - fun getContactWithId(id: Int): LocalContact? - - @Query("SELECT * FROM contacts WHERE phone_numbers LIKE :number") - fun getContactWithNumber(number: String): LocalContact? - - @Query("UPDATE contacts SET starred = :isStarred WHERE id = :id") - fun updateStarred(isStarred: Int, id: Int) - - @Query("UPDATE contacts SET ringtone = :ringtone WHERE id = :id") - fun updateRingtone(ringtone: String, id: Int) - - @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insertOrUpdate(contact: LocalContact): Long - - @Query("DELETE FROM contacts WHERE id = :id") - fun deleteContactId(id: Int) - - @Query("DELETE FROM contacts WHERE id IN (:ids)") - fun deleteContactIds(ids: List) -} diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/interfaces/GroupsDao.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/interfaces/GroupsDao.kt deleted file mode 100644 index 8991a6f0..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/interfaces/GroupsDao.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.simplemobiletools.contacts.pro.interfaces - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import com.simplemobiletools.contacts.pro.models.Group - -@Dao -interface GroupsDao { - @Query("SELECT * FROM groups") - fun getGroups(): List - - @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insertOrUpdate(group: Group): Long - - @Query("DELETE FROM groups WHERE id = :id") - fun deleteGroupId(id: Long) -} diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/interfaces/RefreshContactsListener.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/interfaces/RefreshContactsListener.kt index 1a917f56..ec68aa5c 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/interfaces/RefreshContactsListener.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/interfaces/RefreshContactsListener.kt @@ -1,6 +1,6 @@ package com.simplemobiletools.contacts.pro.interfaces -import com.simplemobiletools.contacts.pro.models.Contact +import com.simplemobiletools.commons.models.contacts.* interface RefreshContactsListener { fun refreshContacts(refreshTabsMask: Int) diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/interfaces/RemoveFromGroupListener.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/interfaces/RemoveFromGroupListener.kt index 753f0013..76b6f485 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/interfaces/RemoveFromGroupListener.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/interfaces/RemoveFromGroupListener.kt @@ -1,6 +1,6 @@ package com.simplemobiletools.contacts.pro.interfaces -import com.simplemobiletools.contacts.pro.models.Contact +import com.simplemobiletools.commons.models.contacts.* interface RemoveFromGroupListener { fun removeFromGroup(contacts: ArrayList) diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Address.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Address.kt deleted file mode 100644 index 3dc9ae35..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Address.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.simplemobiletools.contacts.pro.models - -data class Address(var value: String, var type: Int, var label: String) diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Contact.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Contact.kt deleted file mode 100644 index 1873f642..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Contact.kt +++ /dev/null @@ -1,180 +0,0 @@ -package com.simplemobiletools.contacts.pro.models - -import android.graphics.Bitmap -import com.simplemobiletools.commons.extensions.normalizeString -import com.simplemobiletools.commons.helpers.* -import com.simplemobiletools.commons.models.PhoneNumber - -data class Contact( - var id: Int, var prefix: String, var firstName: String, var middleName: String, var surname: String, var suffix: String, var nickname: String, - var photoUri: String, var phoneNumbers: ArrayList, var emails: ArrayList, var addresses: ArrayList
, - var events: ArrayList, var source: String, var starred: Int, var contactId: Int, var thumbnailUri: String, var photo: Bitmap?, var notes: String, - var groups: ArrayList, var organization: Organization, var websites: ArrayList, var IMs: ArrayList, var mimetype: String, - var ringtone: String? -) : - Comparable { - companion object { - var sorting = 0 - var startWithSurname = false - } - - override fun compareTo(other: Contact): Int { - var result = when { - sorting and SORT_BY_FIRST_NAME != 0 -> { - val firstString = firstName.normalizeString() - val secondString = other.firstName.normalizeString() - compareUsingStrings(firstString, secondString, other) - } - sorting and SORT_BY_MIDDLE_NAME != 0 -> { - val firstString = middleName.normalizeString() - val secondString = other.middleName.normalizeString() - compareUsingStrings(firstString, secondString, other) - } - sorting and SORT_BY_SURNAME != 0 -> { - val firstString = surname.normalizeString() - val secondString = other.surname.normalizeString() - compareUsingStrings(firstString, secondString, other) - } - sorting and SORT_BY_FULL_NAME != 0 -> { - val firstString = getNameToDisplay().normalizeString() - val secondString = other.getNameToDisplay().normalizeString() - compareUsingStrings(firstString, secondString, other) - } - else -> compareUsingIds(other) - } - - if (sorting and SORT_DESCENDING != 0) { - result *= -1 - } - - return result - } - - private fun compareUsingStrings(firstString: String, secondString: String, other: Contact): Int { - var firstValue = firstString - var secondValue = secondString - - if (firstValue.isEmpty() && firstName.isEmpty() && middleName.isEmpty() && surname.isEmpty()) { - val fullCompany = getFullCompany() - if (fullCompany.isNotEmpty()) { - firstValue = fullCompany.normalizeString() - } else if (emails.isNotEmpty()) { - firstValue = emails.first().value - } - } - - if (secondValue.isEmpty() && other.firstName.isEmpty() && other.middleName.isEmpty() && other.surname.isEmpty()) { - val otherFullCompany = other.getFullCompany() - if (otherFullCompany.isNotEmpty()) { - secondValue = otherFullCompany.normalizeString() - } else if (other.emails.isNotEmpty()) { - secondValue = other.emails.first().value - } - } - - return if (firstValue.firstOrNull()?.isLetter() == true && secondValue.firstOrNull()?.isLetter() == false) { - -1 - } else if (firstValue.firstOrNull()?.isLetter() == false && secondValue.firstOrNull()?.isLetter() == true) { - 1 - } else { - if (firstValue.isEmpty() && secondValue.isNotEmpty()) { - 1 - } else if (firstValue.isNotEmpty() && secondValue.isEmpty()) { - -1 - } else { - if (firstValue.equals(secondValue, ignoreCase = true)) { - getNameToDisplay().compareTo(other.getNameToDisplay(), true) - } else { - firstValue.compareTo(secondValue, true) - } - } - } - } - - private fun compareUsingIds(other: Contact): Int { - val firstId = id - val secondId = other.id - return firstId.compareTo(secondId) - } - - fun getBubbleText() = when { - sorting and SORT_BY_FIRST_NAME != 0 -> firstName - sorting and SORT_BY_MIDDLE_NAME != 0 -> middleName - else -> surname - } - - fun getNameToDisplay(): String { - val firstMiddle = "$firstName $middleName".trim() - val firstPart = if (startWithSurname) { - if (surname.isNotEmpty() && firstMiddle.isNotEmpty()) { - "$surname," - } else { - surname - } - } else { - firstMiddle - } - val lastPart = if (startWithSurname) firstMiddle else surname - val suffixComma = if (suffix.isEmpty()) "" else ", $suffix" - val fullName = "$prefix $firstPart $lastPart$suffixComma".trim() - return if (fullName.isEmpty()) { - if (organization.isNotEmpty()) { - getFullCompany() - } else { - emails.firstOrNull()?.value?.trim() ?: "" - } - } else { - fullName - } - } - - // photos stored locally always have different hashcodes. Avoid constantly refreshing the contact lists as the app thinks something changed. - fun getHashWithoutPrivatePhoto(): Int { - val photoToUse = if (isPrivate()) null else photo - return copy(photo = photoToUse).hashCode() - } - - fun getStringToCompare(): String { - val photoToUse = if (isPrivate()) null else photo - return copy( - id = 0, - prefix = "", - firstName = getNameToDisplay().toLowerCase(), - middleName = "", - surname = "", - suffix = "", - nickname = "", - photoUri = "", - phoneNumbers = ArrayList(), - emails = ArrayList(), - events = ArrayList(), - source = "", - addresses = ArrayList(), - starred = 0, - contactId = 0, - thumbnailUri = "", - photo = photoToUse, - notes = "", - groups = ArrayList(), - websites = ArrayList(), - organization = Organization("", ""), - IMs = ArrayList(), - ringtone = "" - ).toString() - } - - fun getHashToCompare() = getStringToCompare().hashCode() - - fun getFullCompany(): String { - var fullOrganization = if (organization.company.isEmpty()) "" else "${organization.company}, " - fullOrganization += organization.jobPosition - return fullOrganization.trim().trimEnd(',') - } - - fun isABusinessContact() = - prefix.isEmpty() && firstName.isEmpty() && middleName.isEmpty() && surname.isEmpty() && suffix.isEmpty() && organization.isNotEmpty() - - fun isPrivate() = source == SMT_PRIVATE - - fun getSignatureKey() = if (photoUri.isNotEmpty()) photoUri else hashCode() -} diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/ContactSource.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/ContactSource.kt deleted file mode 100644 index d159464d..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/ContactSource.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.simplemobiletools.contacts.pro.models - -import com.simplemobiletools.commons.helpers.SMT_PRIVATE - -data class ContactSource(var name: String, var type: String, var publicName: String, var count: Int = 0) { - fun getFullIdentifier(): String { - return if (type == SMT_PRIVATE) { - type - } else { - "$name:$type" - } - } -} diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Email.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Email.kt deleted file mode 100644 index 39bdaad3..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Email.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.simplemobiletools.contacts.pro.models - -data class Email(var value: String, var type: Int, var label: String) diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Event.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Event.kt deleted file mode 100644 index fc3fd7f7..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Event.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.simplemobiletools.contacts.pro.models - -data class Event(var value: String, var type: Int) diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Group.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Group.kt deleted file mode 100644 index 9628e664..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Group.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.simplemobiletools.contacts.pro.models - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.Index -import androidx.room.PrimaryKey -import com.simplemobiletools.contacts.pro.helpers.FIRST_GROUP_ID -import java.io.Serializable - -@Entity(tableName = "groups", indices = [(Index(value = ["id"], unique = true))]) -data class Group( - @PrimaryKey(autoGenerate = true) var id: Long?, - @ColumnInfo(name = "title") var title: String, - @ColumnInfo(name = "contacts_count") var contactsCount: Int = 0) : Serializable { - - fun addContact() = contactsCount++ - - fun getBubbleText() = title - - fun isPrivateSecretGroup() = id ?: 0 >= FIRST_GROUP_ID -} diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/IM.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/IM.kt deleted file mode 100644 index e3f7e0ab..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/IM.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.simplemobiletools.contacts.pro.models - -data class IM(var value: String, var type: Int, var label: String) diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/LocalContact.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/LocalContact.kt deleted file mode 100644 index 1f01cd82..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/LocalContact.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.simplemobiletools.contacts.pro.models - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.Index -import androidx.room.PrimaryKey -import com.simplemobiletools.commons.models.PhoneNumber - -@Entity(tableName = "contacts", indices = [(Index(value = ["id"], unique = true))]) -data class LocalContact( - @PrimaryKey(autoGenerate = true) var id: Int?, - @ColumnInfo(name = "prefix") var prefix: String, - @ColumnInfo(name = "first_name") var firstName: String, - @ColumnInfo(name = "middle_name") var middleName: String, - @ColumnInfo(name = "surname") var surname: String, - @ColumnInfo(name = "suffix") var suffix: String, - @ColumnInfo(name = "nickname") var nickname: String, - @ColumnInfo(name = "photo", typeAffinity = ColumnInfo.BLOB) var photo: ByteArray?, - @ColumnInfo(name = "photo_uri") var photoUri: String, - @ColumnInfo(name = "phone_numbers") var phoneNumbers: ArrayList, - @ColumnInfo(name = "emails") var emails: ArrayList, - @ColumnInfo(name = "events") var events: ArrayList, - @ColumnInfo(name = "starred") var starred: Int, - @ColumnInfo(name = "addresses") var addresses: ArrayList
, - @ColumnInfo(name = "notes") var notes: String, - @ColumnInfo(name = "groups") var groups: ArrayList, - @ColumnInfo(name = "company") var company: String, - @ColumnInfo(name = "job_position") var jobPosition: String, - @ColumnInfo(name = "websites") var websites: ArrayList, - @ColumnInfo(name = "ims") var IMs: ArrayList, - @ColumnInfo(name = "ringtone") var ringtone: String? -) { - override fun equals(other: Any?) = id == (other as? LocalContact?)?.id - - override fun hashCode() = id ?: 0 -} diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Organization.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Organization.kt deleted file mode 100644 index 91697c25..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/Organization.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.simplemobiletools.contacts.pro.models - -data class Organization(var company: String, var jobPosition: String) { - fun isEmpty() = company.isEmpty() && jobPosition.isEmpty() - - fun isNotEmpty() = !isEmpty() -} diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/PhoneNumberConverter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/PhoneNumberConverter.kt deleted file mode 100644 index d2072389..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/PhoneNumberConverter.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.simplemobiletools.contacts.pro.models - -// need for hacky parsing of no longer minified PhoneNumber model in Converters.kt -data class PhoneNumberConverter(var a: String, var b: Int, var c: String, var d: String, var e: Boolean = false) diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/SocialAction.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/SocialAction.kt deleted file mode 100644 index a0064fa4..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/pro/models/SocialAction.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.simplemobiletools.contacts.pro.models - -data class SocialAction(var actionId: Int, var type: Int, var label: String, var mimetype: String, val dataId: Long, val packageName: String)