Merge pull request #14 from SimpleMobileTools/master

гзв
This commit is contained in:
solokot 2018-05-10 14:07:32 +03:00 committed by GitHub
commit 4cecfa4893
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
156 changed files with 587 additions and 123 deletions

View File

@ -1,6 +1,21 @@
Changelog Changelog
========== ==========
Version 4.0.1 *(2018-05-09)*
----------------------------
* Fixed a glitch happening at updating from old app version to 4.x
Version 4.0.0 *(2018-05-09)*
----------------------------
* Allow changing app icon color
* Add a toggle for trying to filter out duplicate contacts, enabled by default
* Fix some contacts not being visible
* Allow opening contacts with third party apps
* Couple misc fixes related to contacts syncing via CardDAV
* Allow moving contacts in a different contact source at the Edit screen
Version 3.5.3 *(2018-04-18)* Version 3.5.3 *(2018-04-18)*
---------------------------- ----------------------------

View File

@ -10,8 +10,8 @@ android {
applicationId "com.simplemobiletools.contacts" applicationId "com.simplemobiletools.contacts"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 27 targetSdkVersion 27
versionCode 19 versionCode 21
versionName "3.5.3" versionName "4.0.1"
setProperty("archivesBaseName", "contacts") setProperty("archivesBaseName", "contacts")
} }
@ -45,7 +45,7 @@ ext {
} }
dependencies { dependencies {
implementation 'com.simplemobiletools:commons:3.20.6' implementation 'com.simplemobiletools:commons:4.0.0'
implementation 'joda-time:joda-time:2.9.9' implementation 'joda-time:joda-time:2.9.9'
implementation 'com.facebook.stetho:stetho:1.5.0' implementation 'com.facebook.stetho:stetho:1.5.0'

View File

@ -210,6 +210,228 @@
android:icon="@mipmap/ic_launcher_red" android:icon="@mipmap/ic_launcher_red"
android:roundIcon="@mipmap/ic_launcher_red" android:roundIcon="@mipmap/ic_launcher_red"
android:targetActivity=".activities.SplashActivity"> android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Pink"
android:enabled="false"
android:icon="@mipmap/ic_launcher_pink"
android:roundIcon="@mipmap/ic_launcher_pink"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Purple"
android:enabled="false"
android:icon="@mipmap/ic_launcher_purple"
android:roundIcon="@mipmap/ic_launcher_purple"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Deep_purple"
android:enabled="false"
android:icon="@mipmap/ic_launcher_deep_purple"
android:roundIcon="@mipmap/ic_launcher_deep_purple"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Indigo"
android:enabled="false"
android:icon="@mipmap/ic_launcher_indigo"
android:roundIcon="@mipmap/ic_launcher_indigo"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Blue"
android:enabled="false"
android:icon="@mipmap/ic_launcher_blue"
android:roundIcon="@mipmap/ic_launcher_blue"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Light_blue"
android:enabled="false"
android:icon="@mipmap/ic_launcher_light_blue"
android:roundIcon="@mipmap/ic_launcher_light_blue"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Cyan"
android:enabled="false"
android:icon="@mipmap/ic_launcher_cyan"
android:roundIcon="@mipmap/ic_launcher_cyan"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Teal"
android:enabled="false"
android:icon="@mipmap/ic_launcher_teal"
android:roundIcon="@mipmap/ic_launcher_teal"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Green"
android:enabled="false"
android:icon="@mipmap/ic_launcher_green"
android:roundIcon="@mipmap/ic_launcher_green"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Light_green"
android:enabled="false"
android:icon="@mipmap/ic_launcher_light_green"
android:roundIcon="@mipmap/ic_launcher_light_green"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Lime"
android:enabled="false"
android:icon="@mipmap/ic_launcher_lime"
android:roundIcon="@mipmap/ic_launcher_lime"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Yellow"
android:enabled="false"
android:icon="@mipmap/ic_launcher_yellow"
android:roundIcon="@mipmap/ic_launcher_yellow"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Amber"
android:enabled="false"
android:icon="@mipmap/ic_launcher_amber"
android:roundIcon="@mipmap/ic_launcher_amber"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Deep_orange"
android:enabled="false"
android:icon="@mipmap/ic_launcher_deep_orange"
android:roundIcon="@mipmap/ic_launcher_deep_orange"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Brown"
android:enabled="false"
android:icon="@mipmap/ic_launcher_brown"
android:roundIcon="@mipmap/ic_launcher_brown"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Blue_grey"
android:enabled="false"
android:icon="@mipmap/ic_launcher_blue_grey"
android:roundIcon="@mipmap/ic_launcher_blue_grey"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Grey_black"
android:enabled="false"
android:icon="@mipmap/ic_launcher_grey_black"
android:roundIcon="@mipmap/ic_launcher_grey_black"
android:targetActivity=".activities.SplashActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>

View File

@ -52,6 +52,7 @@ class EditContactActivity : ContactActivity() {
private var lastPhotoIntentUri: Uri? = null private var lastPhotoIntentUri: Uri? = null
private var isSaving = false private var isSaving = false
private var isThirdPartyIntent = false private var isThirdPartyIntent = false
private var originalContactSource = ""
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -212,6 +213,7 @@ class EditContactActivity : ContactActivity() {
contact_events_add_new.setOnClickListener { addNewEventField() } contact_events_add_new.setOnClickListener { addNewEventField() }
contact_websites_add_new.setOnClickListener { addNewWebsiteField() } contact_websites_add_new.setOnClickListener { addNewWebsiteField() }
contact_groups_add_new.setOnClickListener { showSelectGroupsDialog() } contact_groups_add_new.setOnClickListener { showSelectGroupsDialog() }
contact_source.setOnClickListener { showSelectContactSourceDialog() }
setupFieldVisibility() setupFieldVisibility()
@ -504,21 +506,16 @@ class EditContactActivity : ContactActivity() {
private fun setupContactSource() { private fun setupContactSource() {
contact_source.text = getPublicContactSource(contact!!.source) contact_source.text = getPublicContactSource(contact!!.source)
originalContactSource = contact!!.source
} }
private fun setupNewContact() { private fun setupNewContact() {
supportActionBar?.title = resources.getString(R.string.new_contact) supportActionBar?.title = resources.getString(R.string.new_contact)
val contactSource = if (hasContactPermissions()) config.lastUsedContactSource else SMT_PRIVATE originalContactSource = if (hasContactPermissions()) config.lastUsedContactSource else SMT_PRIVATE
val organization = Organization("", "") val organization = Organization("", "")
contact = Contact(0, "", "", "", "", "", "", ArrayList(), ArrayList(), ArrayList(), ArrayList(), contactSource, 0, 0, "", null, "", contact = Contact(0, "", "", "", "", "", "", ArrayList(), ArrayList(), ArrayList(), ArrayList(), originalContactSource, 0, 0, "", null, "",
ArrayList(), organization, ArrayList()) ArrayList(), organization, ArrayList())
contact_source.text = getPublicContactSource(contact!!.source) contact_source.text = getPublicContactSource(contact!!.source)
contact_source.setOnClickListener {
showContactSourcePicker(contact!!.source) {
contact!!.source = if (it == getString(R.string.phone_storage_hidden)) SMT_PRIVATE else it
contact_source.text = getPublicContactSource(it)
}
}
} }
private fun setupTypePickers() { private fun setupTypePickers() {
@ -708,6 +705,13 @@ class EditContactActivity : ContactActivity() {
} }
} }
private fun showSelectContactSourceDialog() {
showContactSourcePicker(contact!!.source) {
contact!!.source = if (it == getString(R.string.phone_storage_hidden)) SMT_PRIVATE else it
contact_source.text = getPublicContactSource(it)
}
}
private fun saveContact() { private fun saveContact() {
if (isSaving || contact == null) { if (isSaving || contact == null) {
return return
@ -726,7 +730,6 @@ class EditContactActivity : ContactActivity() {
emails = getFilledEmails() emails = getFilledEmails()
addresses = getFilledAddresses() addresses = getFilledAddresses()
events = getFilledEvents() events = getFilledEvents()
source = contact!!.source
starred = if (isContactStarred()) 1 else 0 starred = if (isContactStarred()) 1 else 0
notes = contact_notes.value notes = contact_notes.value
websites = getFilledWebsites() websites = getFilledWebsites()
@ -737,12 +740,14 @@ class EditContactActivity : ContactActivity() {
Thread { Thread {
config.lastUsedContactSource = source config.lastUsedContactSource = source
if (id == 0) { when {
insertNewContact() id == 0 -> insertNewContact(false)
} else { originalContactSource != source -> insertNewContact(true)
else -> {
val photoUpdateStatus = getPhotoUpdateStatus(oldPhotoUri, photoUri) val photoUpdateStatus = getPhotoUpdateStatus(oldPhotoUri, photoUri)
updateContact(photoUpdateStatus) updateContact(photoUpdateStatus)
} }
}
}.start() }.start()
} }
} }
@ -821,10 +826,17 @@ class EditContactActivity : ContactActivity() {
return websites return websites
} }
private fun insertNewContact() { private fun insertNewContact(deleteCurrentContact: Boolean) {
isSaving = true isSaving = true
if (!deleteCurrentContact) {
toast(R.string.inserting) toast(R.string.inserting)
}
if (ContactsHelper(this@EditContactActivity).insertContact(contact!!)) { if (ContactsHelper(this@EditContactActivity).insertContact(contact!!)) {
if (deleteCurrentContact) {
contact!!.source = originalContactSource
ContactsHelper(this).deleteContact(contact!!)
}
finish() finish()
} else { } else {
toast(R.string.unknown_error_occurred) toast(R.string.unknown_error_occurred)

View File

@ -1,11 +1,15 @@
package com.simplemobiletools.contacts.activities package com.simplemobiletools.contacts.activities
import android.Manifest
import android.app.SearchManager import android.app.SearchManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.support.v4.view.MenuItemCompat import android.support.v4.view.MenuItemCompat
import android.support.v4.view.ViewPager import android.support.v4.view.ViewPager
import android.support.v7.widget.SearchView import android.support.v7.widget.SearchView
@ -48,11 +52,12 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
private var storedShowContactThumbnails = false private var storedShowContactThumbnails = false
private var storedShowPhoneNumbers = false private var storedShowPhoneNumbers = false
private var storedStartNameWithSurname = false private var storedStartNameWithSurname = false
private var storedFilterDuplicates = true
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
appLaunched() appLaunched(BuildConfig.APPLICATION_ID)
setupTabColors() setupTabColors()
// just get a reference to the database to make sure it is created properly // just get a reference to the database to make sure it is created properly
@ -62,6 +67,12 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
werePermissionsHandled = true werePermissionsHandled = true
if (it) { if (it) {
handlePermission(PERMISSION_WRITE_CONTACTS) { handlePermission(PERMISSION_WRITE_CONTACTS) {
// workaround for upgrading from version 3.x to 4.x as we added a new permission from an already granted permissions group
val hasGetAccountsPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.GET_ACCOUNTS) == PackageManager.PERMISSION_GRANTED
if (!hasGetAccountsPermission) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.GET_ACCOUNTS), 34)
}
storeLocalAccountData() storeLocalAccountData()
initFragments() initFragments()
} }
@ -118,6 +129,10 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
favorites_fragment?.startNameWithSurnameChanged(configStartNameWithSurname) favorites_fragment?.startNameWithSurnameChanged(configStartNameWithSurname)
} }
if (storedFilterDuplicates != config.filterDuplicates) {
refreshContacts(ALL_TABS_MASK)
}
if (werePermissionsHandled && !isFirstResume) { if (werePermissionsHandled && !isFirstResume) {
if (viewpager.adapter == null) { if (viewpager.adapter == null) {
initFragments() initFragments()
@ -175,6 +190,7 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
storedShowContactThumbnails = showContactThumbnails storedShowContactThumbnails = showContactThumbnails
storedShowPhoneNumbers = showPhoneNumbers storedShowPhoneNumbers = showPhoneNumbers
storedStartNameWithSurname = startNameWithSurname storedStartNameWithSurname = startNameWithSurname
storedFilterDuplicates = filterDuplicates
} }
} }

View File

@ -6,7 +6,6 @@ import android.os.Bundle
import android.provider.ContactsContract import android.provider.ContactsContract
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import com.simplemobiletools.commons.extensions.appLaunched
import com.simplemobiletools.commons.extensions.baseConfig import com.simplemobiletools.commons.extensions.baseConfig
import com.simplemobiletools.commons.extensions.isActivityDestroyed import com.simplemobiletools.commons.extensions.isActivityDestroyed
import com.simplemobiletools.commons.extensions.toast import com.simplemobiletools.commons.extensions.toast
@ -18,6 +17,7 @@ import com.simplemobiletools.contacts.dialogs.ChangeSortingDialog
import com.simplemobiletools.contacts.dialogs.FilterContactSourcesDialog import com.simplemobiletools.contacts.dialogs.FilterContactSourcesDialog
import com.simplemobiletools.contacts.extensions.config import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.extensions.getContactPublicUri import com.simplemobiletools.contacts.extensions.getContactPublicUri
import com.simplemobiletools.contacts.extensions.getVisibleContactSources
import com.simplemobiletools.contacts.helpers.ContactsHelper import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.helpers.SMT_PRIVATE import com.simplemobiletools.contacts.helpers.SMT_PRIVATE
import com.simplemobiletools.contacts.models.Contact import com.simplemobiletools.contacts.models.Contact
@ -29,7 +29,6 @@ class SelectContactActivity : SimpleActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.layout_select_contact) setContentView(R.layout.layout_select_contact)
appLaunched()
handlePermission(PERMISSION_READ_CONTACTS) { handlePermission(PERMISSION_READ_CONTACTS) {
if (it) { if (it) {
@ -98,7 +97,7 @@ class SelectContactActivity : SimpleActivity() {
} }
} as ArrayList<Contact> } as ArrayList<Contact>
val contactSources = config.displayContactSources val contactSources = getVisibleContactSources()
contacts = contacts.filter { contactSources.contains(it.source) } as ArrayList<Contact> contacts = contacts.filter { contactSources.contains(it.source) } as ArrayList<Contact>
Contact.sorting = config.sorting Contact.sorting = config.sorting

View File

@ -31,6 +31,7 @@ class SettingsActivity : SimpleActivity() {
setupShowContactThumbnails() setupShowContactThumbnails()
setupShowPhoneNumbers() setupShowPhoneNumbers()
setupStartNameWithSurname() setupStartNameWithSurname()
setupFilterDuplicates()
setupOnContactClick() setupOnContactClick()
updateTextColors(settings_holder) updateTextColors(settings_holder)
} }
@ -97,6 +98,14 @@ class SettingsActivity : SimpleActivity() {
} }
} }
private fun setupFilterDuplicates() {
settings_filter_duplicates.isChecked = config.filterDuplicates
settings_filter_duplicates_holder.setOnClickListener {
settings_filter_duplicates.toggle()
config.filterDuplicates = settings_filter_duplicates.isChecked
}
}
private fun setupOnContactClick() { private fun setupOnContactClick() {
settings_on_contact_click.text = getOnContactClickText() settings_on_contact_click.text = getOnContactClickText()
settings_on_contact_click_holder.setOnClickListener { settings_on_contact_click_holder.setOnClickListener {

View File

@ -6,7 +6,24 @@ import com.simplemobiletools.contacts.R
open class SimpleActivity : BaseSimpleActivity() { open class SimpleActivity : BaseSimpleActivity() {
override fun getAppIconIDs() = arrayListOf( override fun getAppIconIDs() = arrayListOf(
R.mipmap.ic_launcher_red, R.mipmap.ic_launcher_red,
R.mipmap.ic_launcher R.mipmap.ic_launcher_pink,
R.mipmap.ic_launcher_purple,
R.mipmap.ic_launcher_deep_purple,
R.mipmap.ic_launcher_indigo,
R.mipmap.ic_launcher_blue,
R.mipmap.ic_launcher_light_blue,
R.mipmap.ic_launcher_cyan,
R.mipmap.ic_launcher_teal,
R.mipmap.ic_launcher_green,
R.mipmap.ic_launcher_light_green,
R.mipmap.ic_launcher_lime,
R.mipmap.ic_launcher_yellow,
R.mipmap.ic_launcher_amber,
R.mipmap.ic_launcher,
R.mipmap.ic_launcher_deep_orange,
R.mipmap.ic_launcher_brown,
R.mipmap.ic_launcher_blue_grey,
R.mipmap.ic_launcher_grey_black
) )
override fun getAppLauncherName() = getString(R.string.app_launcher_name) override fun getAppLauncherName() = getString(R.string.app_launcher_name)

View File

@ -125,6 +125,7 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Cont
textToHighlight = highlightText textToHighlight = highlightText
notifyDataSetChanged() notifyDataSetChanged()
} }
fastScroller?.measureRecyclerView()
} }
private fun editContact() { private fun editContact() {

View File

@ -14,7 +14,7 @@ import com.simplemobiletools.contacts.models.ContactSource
import kotlinx.android.synthetic.main.item_filter_contact_source.view.* import kotlinx.android.synthetic.main.item_filter_contact_source.view.*
import java.util.* import java.util.*
class FilterContactSourcesAdapter(val activity: SimpleActivity, private val contactSources: List<ContactSource>, private val displayContactSources: Set<String>) : class FilterContactSourcesAdapter(val activity: SimpleActivity, private val contactSources: List<ContactSource>, private val displayContactSources: ArrayList<String>) :
RecyclerView.Adapter<FilterContactSourcesAdapter.ViewHolder>() { RecyclerView.Adapter<FilterContactSourcesAdapter.ViewHolder>() {
private val itemViews = SparseArray<View>() private val itemViews = SparseArray<View>()
private val selectedPositions = HashSet<Int>() private val selectedPositions = HashSet<Int>()

View File

@ -77,6 +77,7 @@ class GroupsAdapter(activity: SimpleActivity, var groups: ArrayList<Group>, val
groups = newItems groups = newItems
notifyDataSetChanged() notifyDataSetChanged()
finishActMode() finishActMode()
fastScroller?.measureRecyclerView()
} }
private fun editGroup() { private fun editGroup() {

View File

@ -119,7 +119,9 @@ class SelectContactsAdapter(val activity: SimpleActivity, val contacts: List<Con
.error(contactDrawable) .error(contactDrawable)
.centerCrop() .centerCrop()
if (activity.isActivityDestroyed()) {
Glide.with(activity).load(contact.photoUri).transition(DrawableTransitionOptions.withCrossFade()).apply(options).into(contact_tmb) Glide.with(activity).load(contact.photoUri).transition(DrawableTransitionOptions.withCrossFade()).apply(options).into(contact_tmb)
}
} else { } else {
contact_tmb.setImageDrawable(contactDrawable) contact_tmb.setImageDrawable(contactDrawable)
} }

View File

@ -6,7 +6,7 @@ import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.contacts.R import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.SimpleActivity import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.adapters.FilterContactSourcesAdapter import com.simplemobiletools.contacts.adapters.FilterContactSourcesAdapter
import com.simplemobiletools.contacts.extensions.config import com.simplemobiletools.contacts.extensions.getVisibleContactSources
import com.simplemobiletools.contacts.helpers.ContactsHelper import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.helpers.SMT_PRIVATE import com.simplemobiletools.contacts.helpers.SMT_PRIVATE
import com.simplemobiletools.contacts.models.ContactSource import com.simplemobiletools.contacts.models.ContactSource
@ -25,7 +25,7 @@ class ExportContactsDialog(val activity: SimpleActivity, val path: String, priva
ContactsHelper(activity).getContactSources { ContactsHelper(activity).getContactSources {
it.mapTo(contactSources, { it.copy() }) it.mapTo(contactSources, { it.copy() })
activity.runOnUiThread { activity.runOnUiThread {
export_contacts_list.adapter = FilterContactSourcesAdapter(activity, it, activity.config.displayContactSources) export_contacts_list.adapter = FilterContactSourcesAdapter(activity, it, activity.getVisibleContactSources())
} }
} }
} }

View File

@ -6,6 +6,7 @@ import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.SimpleActivity import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.adapters.FilterContactSourcesAdapter import com.simplemobiletools.contacts.adapters.FilterContactSourcesAdapter
import com.simplemobiletools.contacts.extensions.config import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.extensions.getVisibleContactSources
import com.simplemobiletools.contacts.helpers.ContactsHelper import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.helpers.SMT_PRIVATE import com.simplemobiletools.contacts.helpers.SMT_PRIVATE
import com.simplemobiletools.contacts.models.ContactSource import com.simplemobiletools.contacts.models.ContactSource
@ -24,7 +25,7 @@ class FilterContactSourcesDialog(val activity: SimpleActivity, private val callb
} }
it.mapTo(contactSources, { it.copy() }) it.mapTo(contactSources, { it.copy() })
val selectedSources = activity.config.displayContactSources val selectedSources = activity.getVisibleContactSources()
activity.runOnUiThread { activity.runOnUiThread {
view.filter_contact_sources_list.adapter = FilterContactSourcesAdapter(activity, it, selectedSources) view.filter_contact_sources_list.adapter = FilterContactSourcesAdapter(activity, it, selectedSources)
@ -40,13 +41,20 @@ class FilterContactSourcesDialog(val activity: SimpleActivity, private val callb
private fun confirmEventTypes() { private fun confirmEventTypes() {
val selectedIndexes = (view.filter_contact_sources_list.adapter as FilterContactSourcesAdapter).getSelectedItemsSet() val selectedIndexes = (view.filter_contact_sources_list.adapter as FilterContactSourcesAdapter).getSelectedItemsSet()
val selectedContactSources = HashSet<String>() val ignoredIndexes = ArrayList<Int>()
selectedIndexes.forEach { for (i in 0 until contactSources.size) {
selectedContactSources.add(if (contactSources[it].type == SMT_PRIVATE) SMT_PRIVATE else contactSources[it].name) if (!selectedIndexes.contains(i)) {
ignoredIndexes.add(i)
}
} }
if (activity.config.displayContactSources != selectedContactSources) { val ignoredContactSources = HashSet<String>()
activity.config.displayContactSources = selectedContactSources ignoredIndexes.forEach {
ignoredContactSources.add(if (contactSources[it].type == SMT_PRIVATE) SMT_PRIVATE else contactSources[it].name)
}
if (activity.getVisibleContactSources() != ignoredContactSources) {
activity.config.ignoredContactSources = ignoredContactSources
callback() callback()
} }
dialog?.dismiss() dialog?.dismiss()

View File

@ -7,6 +7,7 @@ import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.SimpleActivity import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.adapters.SelectContactsAdapter import com.simplemobiletools.contacts.adapters.SelectContactsAdapter
import com.simplemobiletools.contacts.extensions.config import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.extensions.getVisibleContactSources
import com.simplemobiletools.contacts.models.Contact import com.simplemobiletools.contacts.models.Contact
import kotlinx.android.synthetic.main.layout_select_contact.view.* import kotlinx.android.synthetic.main.layout_select_contact.view.*
@ -18,7 +19,7 @@ class SelectContactsDialog(val activity: SimpleActivity, initialContacts: ArrayL
init { init {
var allContacts = initialContacts var allContacts = initialContacts
if (selectContacts == null) { if (selectContacts == null) {
val contactSources = activity.config.displayContactSources val contactSources = activity.getVisibleContactSources()
allContacts = allContacts.filter { contactSources.contains(it.source) } as ArrayList<Contact> allContacts = allContacts.filter { contactSources.contains(it.source) } as ArrayList<Contact>
initiallySelectedContacts = allContacts.filter { it.starred == 1 } as ArrayList<Contact> initiallySelectedContacts = allContacts.filter { it.starred == 1 } as ArrayList<Contact>

View File

@ -1,5 +1,6 @@
package com.simplemobiletools.contacts.extensions package com.simplemobiletools.contacts.extensions
import android.app.Activity
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.provider.ContactsContract import android.provider.ContactsContract
@ -17,6 +18,7 @@ import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.helpers.SMT_PRIVATE import com.simplemobiletools.contacts.helpers.SMT_PRIVATE
import com.simplemobiletools.contacts.helpers.VcfExporter import com.simplemobiletools.contacts.helpers.VcfExporter
import com.simplemobiletools.contacts.models.Contact import com.simplemobiletools.contacts.models.Contact
import com.simplemobiletools.contacts.models.ContactSource
import java.io.File import java.io.File
fun SimpleActivity.startCallIntent(recipient: String) { fun SimpleActivity.startCallIntent(recipient: String) {
@ -181,3 +183,11 @@ fun BaseSimpleActivity.getContactPublicUri(contact: Contact): Uri {
val lookupKey = ContactsHelper(this).getContactLookupKey(contact.id.toString()) val lookupKey = ContactsHelper(this).getContactLookupKey(contact.id.toString())
return Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey) return Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey)
} }
fun Activity.getVisibleContactSources(): ArrayList<String> {
val sources = ContactsHelper(this).getDeviceContactSources()
sources.add(ContactSource(getString(R.string.phone_storage_hidden), SMT_PRIVATE))
val sourceNames = ArrayList(sources).map { if (it.type == SMT_PRIVATE) SMT_PRIVATE else it.name }.toMutableList() as ArrayList<String>
sourceNames.removeAll(config.ignoredContactSources)
return sourceNames
}

View File

@ -15,10 +15,7 @@ import com.simplemobiletools.contacts.activities.MainActivity
import com.simplemobiletools.contacts.activities.SimpleActivity import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.adapters.ContactsAdapter import com.simplemobiletools.contacts.adapters.ContactsAdapter
import com.simplemobiletools.contacts.adapters.GroupsAdapter import com.simplemobiletools.contacts.adapters.GroupsAdapter
import com.simplemobiletools.contacts.extensions.config import com.simplemobiletools.contacts.extensions.*
import com.simplemobiletools.contacts.extensions.editContact
import com.simplemobiletools.contacts.extensions.tryStartCall
import com.simplemobiletools.contacts.extensions.viewContact
import com.simplemobiletools.contacts.helpers.* import com.simplemobiletools.contacts.helpers.*
import com.simplemobiletools.contacts.models.Contact import com.simplemobiletools.contacts.models.Contact
import com.simplemobiletools.contacts.models.Group import com.simplemobiletools.contacts.models.Group
@ -103,7 +100,7 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
this is GroupsFragment -> contacts this is GroupsFragment -> contacts
this is FavoritesFragment -> contacts.filter { it.starred == 1 } as ArrayList<Contact> this is FavoritesFragment -> contacts.filter { it.starred == 1 } as ArrayList<Contact>
else -> { else -> {
val contactSources = config.displayContactSources val contactSources = activity!!.getVisibleContactSources()
contacts.filter { contactSources.contains(it.source) } as ArrayList<Contact> contacts.filter { contactSources.contains(it.source) } as ArrayList<Contact>
} }
} }

View File

@ -8,9 +8,9 @@ class Config(context: Context) : BaseConfig(context) {
fun newInstance(context: Context) = Config(context) fun newInstance(context: Context) = Config(context)
} }
var displayContactSources: Set<String> var ignoredContactSources: HashSet<String>
get() = prefs.getStringSet(DISPLAY_CONTACT_SOURCES, hashSetOf("-1")) get() = prefs.getStringSet(IGNORED_CONTACT_SOURCES, hashSetOf(".")) as HashSet
set(displayContactSources) = prefs.edit().remove(DISPLAY_CONTACT_SOURCES).putStringSet(DISPLAY_CONTACT_SOURCES, displayContactSources).apply() set(ignoreContactSources) = prefs.edit().remove(IGNORED_CONTACT_SOURCES).putStringSet(IGNORED_CONTACT_SOURCES, ignoreContactSources).apply()
var showContactThumbnails: Boolean var showContactThumbnails: Boolean
get() = prefs.getBoolean(SHOW_CONTACT_THUMBNAILS, true) get() = prefs.getBoolean(SHOW_CONTACT_THUMBNAILS, true)
@ -44,4 +44,8 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getInt(SHOW_CONTACT_FIELDS, SHOW_FIRST_NAME_FIELD or SHOW_SURNAME_FIELD or SHOW_PHONE_NUMBERS_FIELD or SHOW_EMAILS_FIELD or 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) 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() set(showContactFields) = prefs.edit().putInt(SHOW_CONTACT_FIELDS, showContactFields).apply()
var filterDuplicates: Boolean
get() = prefs.getBoolean(FILTER_DUPLICATES, true)
set(filterDuplicates) = prefs.edit().putBoolean(FILTER_DUPLICATES, filterDuplicates).apply()
} }

View File

@ -5,13 +5,14 @@ import android.provider.ContactsContract.CommonDataKinds
// shared prefs // shared prefs
const val SHOW_CONTACT_THUMBNAILS = "show_contact_thumbnails" const val SHOW_CONTACT_THUMBNAILS = "show_contact_thumbnails"
const val SHOW_PHONE_NUMBERS = "show_phone_numbers" const val SHOW_PHONE_NUMBERS = "show_phone_numbers"
const val DISPLAY_CONTACT_SOURCES = "display_contact_sources" const val IGNORED_CONTACT_SOURCES = "ignored_contact_sources"
const val START_NAME_WITH_SURNAME = "start_name_with_surname" const val START_NAME_WITH_SURNAME = "start_name_with_surname"
const val LAST_USED_CONTACT_SOURCE = "last_used_contact_source" const val LAST_USED_CONTACT_SOURCE = "last_used_contact_source"
const val LOCAL_ACCOUNT_NAME = "local_account_name" const val LOCAL_ACCOUNT_NAME = "local_account_name"
const val LOCAL_ACCOUNT_TYPE = "local_account_type" const val LOCAL_ACCOUNT_TYPE = "local_account_type"
const val ON_CONTACT_CLICK = "on_contact_click" const val ON_CONTACT_CLICK = "on_contact_click"
const val SHOW_CONTACT_FIELDS = "show_contact_fields" const val SHOW_CONTACT_FIELDS = "show_contact_fields"
const val FILTER_DUPLICATES = "filter_duplicates"
const val CONTACT_ID = "contact_id" const val CONTACT_ID = "contact_id"
const val SMT_PRIVATE = "smt_private" // used at the contact source of local contacts hidden from other apps const val SMT_PRIVATE = "smt_private" // used at the contact source of local contacts hidden from other apps

View File

@ -1,6 +1,7 @@
package com.simplemobiletools.contacts.helpers package com.simplemobiletools.contacts.helpers
import android.accounts.AccountManager import android.accounts.AccountManager
import android.app.Activity
import android.content.* import android.content.*
import android.database.Cursor import android.database.Cursor
import android.graphics.Bitmap import android.graphics.Bitmap
@ -11,7 +12,6 @@ import android.provider.ContactsContract.CommonDataKinds.Note
import android.provider.MediaStore import android.provider.MediaStore
import android.text.TextUtils import android.text.TextUtils
import android.util.SparseArray import android.util.SparseArray
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.SORT_BY_FIRST_NAME import com.simplemobiletools.commons.helpers.SORT_BY_FIRST_NAME
import com.simplemobiletools.commons.helpers.SORT_BY_MIDDLE_NAME import com.simplemobiletools.commons.helpers.SORT_BY_MIDDLE_NAME
@ -20,24 +20,32 @@ import com.simplemobiletools.commons.helpers.SORT_DESCENDING
import com.simplemobiletools.contacts.R import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.extensions.* import com.simplemobiletools.contacts.extensions.*
import com.simplemobiletools.contacts.models.* import com.simplemobiletools.contacts.models.*
import com.simplemobiletools.contacts.overloads.times
class ContactsHelper(val activity: BaseSimpleActivity) { class ContactsHelper(val activity: Activity) {
private val BATCH_SIZE = 100 private val BATCH_SIZE = 100
private var displayContactSources = ArrayList<String>()
fun getContacts(callback: (ArrayList<Contact>) -> Unit) { fun getContacts(callback: (ArrayList<Contact>) -> Unit) {
Thread { Thread {
val contacts = SparseArray<Contact>() val contacts = SparseArray<Contact>()
displayContactSources = activity.getVisibleContactSources()
getDeviceContacts(contacts) getDeviceContacts(contacts)
if (displayContactSources.contains(SMT_PRIVATE)) {
activity.dbHelper.getContacts(activity).forEach { activity.dbHelper.getContacts(activity).forEach {
contacts.put(it.id, it) contacts.put(it.id, it)
} }
}
val contactsSize = contacts.size() val contactsSize = contacts.size()
var resultContacts = ArrayList<Contact>(contactsSize) var resultContacts = ArrayList<Contact>(contactsSize)
(0 until contactsSize).mapTo(resultContacts) { contacts.valueAt(it) } (0 until contactsSize).mapTo(resultContacts) { contacts.valueAt(it) }
if (activity.config.filterDuplicates) {
resultContacts = resultContacts.distinctBy { resultContacts = resultContacts.distinctBy {
it.getHashToCompare() it.getHashToCompare()
} as ArrayList<Contact> } as ArrayList<Contact>
}
// groups are obtained with contactID, not rawID, so assign them to proper contacts like this // groups are obtained with contactID, not rawID, so assign them to proper contacts like this
val groups = getContactGroups(getStoredGroups()) val groups = getContactGroups(getStoredGroups())
@ -60,8 +68,8 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
val uri = ContactsContract.Data.CONTENT_URI val uri = ContactsContract.Data.CONTENT_URI
val projection = getContactProjection() val projection = getContactProjection()
val selection = "${ContactsContract.Data.MIMETYPE} = ?" val selection = getSourcesSelection(true)
val selectionArgs = arrayOf(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) val selectionArgs = getSourcesSelectionArgs(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
val sortOrder = getSortString() val sortOrder = getSortString()
var cursor: Cursor? = null var cursor: Cursor? = null
@ -100,7 +108,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
cursor?.close() cursor?.close()
} }
val phoneNumbers = getPhoneNumbers() val phoneNumbers = getPhoneNumbers(null)
var size = phoneNumbers.size() var size = phoneNumbers.size()
for (i in 0 until size) { for (i in 0 until size) {
val key = phoneNumbers.keyAt(i) val key = phoneNumbers.keyAt(i)
@ -159,8 +167,8 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
CommonDataKinds.Phone.TYPE CommonDataKinds.Phone.TYPE
) )
val selection = if (contactId == null) null else "${ContactsContract.Data.RAW_CONTACT_ID} = ?" val selection = if (contactId == null) getSourcesSelection() else "${ContactsContract.Data.RAW_CONTACT_ID} = ?"
val selectionArgs = if (contactId == null) null else arrayOf(contactId.toString()) val selectionArgs = if (contactId == null) getSourcesSelectionArgs() else arrayOf(contactId.toString())
var cursor: Cursor? = null var cursor: Cursor? = null
try { try {
@ -184,6 +192,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
} finally { } finally {
cursor?.close() cursor?.close()
} }
return phoneNumbers return phoneNumbers
} }
@ -196,8 +205,8 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
CommonDataKinds.Email.TYPE CommonDataKinds.Email.TYPE
) )
val selection = if (contactId == null) null else "${ContactsContract.Data.RAW_CONTACT_ID} = ?" val selection = if (contactId == null) getSourcesSelection() else "${ContactsContract.Data.RAW_CONTACT_ID} = ?"
val selectionArgs = if (contactId == null) null else arrayOf(contactId.toString()) val selectionArgs = if (contactId == null) getSourcesSelectionArgs() else arrayOf(contactId.toString())
var cursor: Cursor? = null var cursor: Cursor? = null
try { try {
@ -215,7 +224,6 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
emails[id]!!.add(Email(email, type)) emails[id]!!.add(Email(email, type))
} while (cursor.moveToNext()) } while (cursor.moveToNext())
} }
} catch (e: Exception) { } catch (e: Exception) {
activity.showErrorToast(e) activity.showErrorToast(e)
} finally { } finally {
@ -234,8 +242,8 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
CommonDataKinds.StructuredPostal.TYPE CommonDataKinds.StructuredPostal.TYPE
) )
val selection = if (contactId == null) null else "${ContactsContract.Data.RAW_CONTACT_ID} = ?" val selection = if (contactId == null) getSourcesSelection() else "${ContactsContract.Data.RAW_CONTACT_ID} = ?"
val selectionArgs = if (contactId == null) null else arrayOf(contactId.toString()) val selectionArgs = if (contactId == null) getSourcesSelectionArgs() else arrayOf(contactId.toString())
var cursor: Cursor? = null var cursor: Cursor? = null
try { try {
@ -253,7 +261,6 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
addresses[id]!!.add(Address(address, type)) addresses[id]!!.add(Address(address, type))
} while (cursor.moveToNext()) } while (cursor.moveToNext())
} }
} catch (e: Exception) { } catch (e: Exception) {
activity.showErrorToast(e) activity.showErrorToast(e)
} finally { } finally {
@ -272,13 +279,8 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
CommonDataKinds.Event.TYPE CommonDataKinds.Event.TYPE
) )
var selection = "${ContactsContract.Data.MIMETYPE} = ?" val selection = getSourcesSelection(true, contactId != null)
var selectionArgs = arrayOf(CommonDataKinds.Event.CONTENT_ITEM_TYPE) val selectionArgs = getSourcesSelectionArgs(CommonDataKinds.Event.CONTENT_ITEM_TYPE, contactId)
if (contactId != null) {
selection += " AND ${ContactsContract.Data.RAW_CONTACT_ID} = ?"
selectionArgs = arrayOf(CommonDataKinds.Event.CONTENT_ITEM_TYPE, contactId.toString())
}
var cursor: Cursor? = null var cursor: Cursor? = null
try { try {
@ -313,13 +315,8 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
Note.NOTE Note.NOTE
) )
var selection = "${ContactsContract.Data.MIMETYPE} = ?" val selection = getSourcesSelection(true, contactId != null)
var selectionArgs = arrayOf(Note.CONTENT_ITEM_TYPE) val selectionArgs = getSourcesSelectionArgs(Note.CONTENT_ITEM_TYPE, contactId)
if (contactId != null) {
selection += " AND ${ContactsContract.Data.RAW_CONTACT_ID} = ?"
selectionArgs = arrayOf(Note.CONTENT_ITEM_TYPE, contactId.toString())
}
var cursor: Cursor? = null var cursor: Cursor? = null
try { try {
@ -349,13 +346,8 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
CommonDataKinds.Organization.TITLE CommonDataKinds.Organization.TITLE
) )
var selection = "${ContactsContract.Data.MIMETYPE} = ?" val selection = getSourcesSelection(true, contactId != null)
var selectionArgs = arrayOf(CommonDataKinds.Organization.CONTENT_ITEM_TYPE) val selectionArgs = getSourcesSelectionArgs(CommonDataKinds.Organization.CONTENT_ITEM_TYPE, contactId)
if (contactId != null) {
selection += " AND ${ContactsContract.Data.RAW_CONTACT_ID} = ?"
selectionArgs = arrayOf(CommonDataKinds.Organization.CONTENT_ITEM_TYPE, contactId.toString())
}
var cursor: Cursor? = null var cursor: Cursor? = null
try { try {
@ -386,13 +378,8 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
CommonDataKinds.Website.URL CommonDataKinds.Website.URL
) )
var selection = "${ContactsContract.Data.MIMETYPE} = ?" val selection = getSourcesSelection(true, contactId != null)
var selectionArgs = arrayOf(CommonDataKinds.Website.CONTENT_ITEM_TYPE) val selectionArgs = getSourcesSelectionArgs(CommonDataKinds.Website.CONTENT_ITEM_TYPE, contactId)
if (contactId != null) {
selection += " AND ${ContactsContract.Data.RAW_CONTACT_ID} = ?"
selectionArgs = arrayOf(CommonDataKinds.Website.CONTENT_ITEM_TYPE, contactId.toString())
}
var cursor: Cursor? = null var cursor: Cursor? = null
try { try {
@ -430,13 +417,8 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
ContactsContract.Data.DATA1 ContactsContract.Data.DATA1
) )
var selection = "${ContactsContract.Data.MIMETYPE} = ?" val selection = getSourcesSelection(true, contactId != null, false)
var selectionArgs = arrayOf(CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE) val selectionArgs = getSourcesSelectionArgs(CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE, contactId)
if (contactId != null) {
selection += " AND ${ContactsContract.Data.CONTACT_ID} = ?"
selectionArgs = arrayOf(CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE, contactId.toString())
}
var cursor: Cursor? = null var cursor: Cursor? = null
try { try {
@ -463,6 +445,48 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
return groups return groups
} }
private fun getQuestionMarks() = "?,".times(displayContactSources.filter { it.isNotEmpty() }.size).trimEnd(',')
private fun getSourcesSelection(addMimeType: Boolean = false, addContactId: Boolean = false, useRawContactId: Boolean = true): String {
val strings = ArrayList<String>()
if (addMimeType) {
strings.add("${ContactsContract.Data.MIMETYPE} = ?")
}
if (addContactId) {
strings.add("${if (useRawContactId) ContactsContract.Data.RAW_CONTACT_ID else ContactsContract.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("${ContactsContract.RawContacts.ACCOUNT_NAME} IN (${getQuestionMarks()})")
if (displayContactSources.contains("")) {
accountnameString.append(" OR ${ContactsContract.RawContacts.ACCOUNT_NAME} IS NULL)")
}
strings.add(accountnameString.toString())
}
return TextUtils.join(" AND ", strings)
}
private fun getSourcesSelectionArgs(mimetype: String? = null, contactId: Int? = null): Array<String> {
val args = ArrayList<String>()
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(): ArrayList<Group> { fun getStoredGroups(): ArrayList<Group> {
val groups = getDeviceStoredGroups() val groups = getDeviceStoredGroups()
groups.addAll(activity.dbHelper.getGroups()) groups.addAll(activity.dbHelper.getGroups())
@ -622,13 +646,17 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
fun getContactSources(callback: (ArrayList<ContactSource>) -> Unit) { fun getContactSources(callback: (ArrayList<ContactSource>) -> Unit) {
Thread { Thread {
val sources = getDeviceContactSources() callback(getContactSourcesSync())
sources.add(ContactSource(activity.getString(R.string.phone_storage_hidden), SMT_PRIVATE))
callback(ArrayList(sources))
}.start() }.start()
} }
private fun getDeviceContactSources(): LinkedHashSet<ContactSource> { private fun getContactSourcesSync(): ArrayList<ContactSource> {
val sources = getDeviceContactSources()
sources.add(ContactSource(activity.getString(R.string.phone_storage_hidden), SMT_PRIVATE))
return ArrayList(sources)
}
fun getDeviceContactSources(): LinkedHashSet<ContactSource> {
val sources = LinkedHashSet<ContactSource>() val sources = LinkedHashSet<ContactSource>()
if (!activity.hasContactPermissions()) { if (!activity.hasContactPermissions()) {
return sources return sources
@ -1220,13 +1248,18 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
try { try {
val operations = ArrayList<ContentProviderOperation>() val operations = ArrayList<ContentProviderOperation>()
val selection = "${ContactsContract.Data.CONTACT_ID} = ?" val selection = "${ContactsContract.RawContacts._ID} = ?"
contacts.filter { it.source != SMT_PRIVATE }.forEach { contacts.filter { it.source != SMT_PRIVATE }.forEach {
ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply { ContentProviderOperation.newDelete(ContactsContract.RawContacts.CONTENT_URI).apply {
val selectionArgs = arrayOf(it.contactId.toString()) val selectionArgs = arrayOf(it.id.toString())
withSelection(selection, selectionArgs) withSelection(selection, selectionArgs)
operations.add(build()) operations.add(build())
} }
if (operations.size % BATCH_SIZE == 0) {
activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
operations.clear()
}
} }
activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations) activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)

View File

@ -1,5 +1,6 @@
package com.simplemobiletools.contacts.helpers package com.simplemobiletools.contacts.helpers
import android.app.Activity
import android.content.ContentValues import android.content.ContentValues
import android.content.Context import android.content.Context
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
@ -11,7 +12,6 @@ import android.provider.MediaStore
import android.text.TextUtils import android.text.TextUtils
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.getBlobValue import com.simplemobiletools.commons.extensions.getBlobValue
import com.simplemobiletools.commons.extensions.getIntValue import com.simplemobiletools.commons.extensions.getIntValue
import com.simplemobiletools.commons.extensions.getLongValue import com.simplemobiletools.commons.extensions.getLongValue
@ -249,7 +249,7 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
} }
} }
fun getContacts(activity: BaseSimpleActivity, selection: String? = null, selectionArgs: Array<String>? = null): ArrayList<Contact> { fun getContacts(activity: Activity, selection: String? = null, selectionArgs: Array<String>? = null): ArrayList<Contact> {
val storedGroups = ContactsHelper(activity).getStoredGroups() val storedGroups = ContactsHelper(activity).getStoredGroups()
val contacts = ArrayList<Contact>() val contacts = ArrayList<Contact>()
val projection = arrayOf(COL_ID, COL_PREFIX, COL_FIRST_NAME, COL_MIDDLE_NAME, COL_SURNAME, COL_SUFFIX, COL_PHONE_NUMBERS, COL_EMAILS, val projection = arrayOf(COL_ID, COL_PREFIX, COL_FIRST_NAME, COL_MIDDLE_NAME, COL_SURNAME, COL_SUFFIX, COL_PHONE_NUMBERS, COL_EMAILS,
@ -319,7 +319,7 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
return contacts return contacts
} }
fun getContactWithId(activity: BaseSimpleActivity, id: Int): Contact? { fun getContactWithId(activity: Activity, id: Int): Contact? {
val selection = "$COL_ID = ?" val selection = "$COL_ID = ?"
val selectionArgs = arrayOf(id.toString()) val selectionArgs = arrayOf(id.toString())
return getContacts(activity, selection, selectionArgs).firstOrNull() return getContacts(activity, selection, selectionArgs).firstOrNull()

View File

@ -86,6 +86,7 @@ data class Contact(val id: Int, var prefix: String, var firstName: String, var m
fun getHashToCompare(): Int { fun getHashToCompare(): Int {
val newPhoneNumbers = ArrayList<PhoneNumber>() val newPhoneNumbers = ArrayList<PhoneNumber>()
phoneNumbers.mapTo(newPhoneNumbers, { PhoneNumber(it.value.replace(pattern, ""), 0) }) phoneNumbers.mapTo(newPhoneNumbers, { PhoneNumber(it.value.replace(pattern, ""), 0) })
return copy(id = 0, phoneNumbers = newPhoneNumbers).hashCode() return copy(id = 0, prefix = "", firstName = getFullName().toLowerCase(), middleName = "", surname = "", suffix = "", photoUri = "",
phoneNumbers = newPhoneNumbers, source = "", starred = 0, contactId = 0, thumbnailUri = "", notes = "").hashCode()
} }
} }

View File

@ -1,10 +0,0 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#FFFFFF"
android:pathData="m55,53.39c5.06,0 9.22,-4.08 9.22,-9.22 0,-5.14 -4.08,-9.22 -9.22,-9.22 -5.14,0 -9.22,4.08 -9.22,9.22 0,5.14 4.16,9.22 9.22,9.22zM55,57.96c-6.12,0 -18.36,3.1 -18.36,9.22l0,4.57 36.72,0 0,-4.57C73.36,61.06 61.12,57.96 55,57.96Z"/>
</vector>

View File

@ -193,6 +193,29 @@
</RelativeLayout> </RelativeLayout>
<RelativeLayout
android:id="@+id/settings_filter_duplicates_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MySwitchCompat
android:id="@+id/settings_filter_duplicates"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:clickable="false"
android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin"
android:text="@string/filter_duplicates"/>
</RelativeLayout>
<RelativeLayout <RelativeLayout
android:id="@+id/settings_on_contact_click_holder" android:id="@+id/settings_on_contact_click_holder"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/color_primary"/> <background android:drawable="@color/md_orange_700"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_amber_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_blue_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_blue_grey_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_brown_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_cyan_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_deep_orange_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_deep_purple_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_green_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_grey_black"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_indigo_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_light_blue_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_light_green_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_lime_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_pink_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_purple_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_red_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_teal_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_yellow_700"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Some files were not shown because too many files have changed in this diff Show More