Merge pull request #10 from SimpleMobileTools/master

upd
This commit is contained in:
solokot
2018-03-23 13:55:57 +03:00
committed by GitHub
58 changed files with 1608 additions and 453 deletions

View File

@ -1,6 +1,13 @@
Changelog
==========
Version 3.4.0 *(2018-03-21)*
----------------------------
* Added groups
* Make phone numbers, emails and addresses clickable on the view screen
* Many smaller improvements and bugfixes
Version 3.3.3 *(2018-03-04)*
----------------------------

View File

@ -10,8 +10,8 @@ android {
applicationId "com.simplemobiletools.contacts"
minSdkVersion 16
targetSdkVersion 27
versionCode 14
versionName "3.3.3"
versionCode 15
versionName "3.4.0"
setProperty("archivesBaseName", "contacts")
}
@ -45,7 +45,7 @@ ext {
}
dependencies {
implementation 'com.simplemobiletools:commons:3.16.11'
implementation 'com.simplemobiletools:commons:3.16.12'
implementation 'joda-time:joda-time:2.9.9'
implementation 'com.facebook.stetho:stetho:1.5.0'
implementation 'com.google.code.gson:gson:2.8.2'

View File

@ -74,6 +74,10 @@
android:label="@string/settings"
android:parentActivityName=".activities.MainActivity"/>
<activity
android:name=".activities.GroupContactsActivity"
android:parentActivityName=".activities.MainActivity"/>
<activity
android:name=".activities.ViewContactActivity"
android:label="@string/details"

View File

@ -27,9 +27,9 @@ import com.simplemobiletools.contacts.models.*
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.*
import kotlinx.android.synthetic.main.item_edit_group.view.*
import kotlinx.android.synthetic.main.item_edit_phone_number.view.*
import kotlinx.android.synthetic.main.item_event.view.*
import kotlinx.android.synthetic.main.item_group.view.*
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
import java.util.*
@ -331,7 +331,7 @@ class EditContactActivity : ContactActivity() {
groups.forEachIndexed { index, group ->
var groupHolder = contact_groups_holder.getChildAt(index)
if (groupHolder == null) {
groupHolder = layoutInflater.inflate(R.layout.item_group, contact_groups_holder, false)
groupHolder = layoutInflater.inflate(R.layout.item_edit_group, contact_groups_holder, false)
contact_groups_holder.addView(groupHolder)
}
@ -359,7 +359,7 @@ class EditContactActivity : ContactActivity() {
}
if (groups.isEmpty()) {
layoutInflater.inflate(R.layout.item_group, contact_groups_holder, false).apply {
layoutInflater.inflate(R.layout.item_edit_group, contact_groups_holder, false).apply {
contact_group.apply {
alpha = 0.5f
text = getString(R.string.no_groups)

View File

@ -0,0 +1,118 @@
package com.simplemobiletools.contacts.activities
import android.os.Bundle
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.adapters.ContactsAdapter
import com.simplemobiletools.contacts.dialogs.SelectContactsDialog
import com.simplemobiletools.contacts.extensions.*
import com.simplemobiletools.contacts.helpers.*
import com.simplemobiletools.contacts.interfaces.RefreshContactsListener
import com.simplemobiletools.contacts.interfaces.RemoveFromGroupListener
import com.simplemobiletools.contacts.models.Contact
import com.simplemobiletools.contacts.models.Group
import kotlinx.android.synthetic.main.activity_group_contacts.*
class GroupContactsActivity : SimpleActivity(), RemoveFromGroupListener, RefreshContactsListener {
private var allContacts = ArrayList<Contact>()
private var groupContacts = ArrayList<Contact>()
private var wasInit = false
lateinit var group: Group
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_group_contacts)
updateTextColors(group_contacts_coordinator)
group = intent.extras.getSerializable(GROUP) as Group
supportActionBar?.title = group.title
group_contacts_fab.setOnClickListener {
if (wasInit) {
fabClicked()
}
}
group_contacts_placeholder_2.setOnClickListener {
fabClicked()
}
group_contacts_placeholder_2.underlineText()
group_contacts_placeholder_2.setTextColor(getAdjustedPrimaryColor())
}
override fun onResume() {
super.onResume()
refreshContacts()
}
private fun fabClicked() {
SelectContactsDialog(this, allContacts, groupContacts) { addedContacts, removedContacts ->
Thread {
addContactsToGroup(addedContacts, group.id)
removeContactsFromGroup(removedContacts, group.id)
refreshContacts()
}.start()
}
}
private fun refreshContacts() {
ContactsHelper(this).getContacts {
wasInit = true
allContacts = it
groupContacts = it.filter { it.groups.map { it.id }.contains(group.id) } as ArrayList<Contact>
group_contacts_placeholder_2.beVisibleIf(groupContacts.isEmpty())
group_contacts_placeholder.beVisibleIf(groupContacts.isEmpty())
group_contacts_list.beVisibleIf(groupContacts.isNotEmpty())
Contact.sorting = config.sorting
groupContacts.sort()
updateContacts(groupContacts)
}
}
private fun updateContacts(contacts: ArrayList<Contact>) {
val currAdapter = group_contacts_list.adapter
if (currAdapter == null) {
ContactsAdapter(this, contacts, this, LOCATION_GROUP_CONTACTS, this, group_contacts_list, group_contacts_fastscroller) {
when (config.onContactClick) {
ON_CLICK_CALL_CONTACT -> {
val contact = it as Contact
if (contact.phoneNumbers.isNotEmpty()) {
tryStartCall(it)
} else {
toast(R.string.no_phone_number_found)
}
}
ON_CLICK_VIEW_CONTACT -> viewContact(it as Contact)
ON_CLICK_EDIT_CONTACT -> editContact(it as Contact)
}
}.apply {
setupDragListener(true)
addVerticalDividers(true)
group_contacts_list.adapter = this
}
group_contacts_fastscroller.setScrollTo(0)
group_contacts_fastscroller.setViews(group_contacts_list) {
val item = (group_contacts_list.adapter as ContactsAdapter).contactItems.getOrNull(it)
group_contacts_fastscroller.updateBubbleText(item?.getBubbleText() ?: "")
}
} else {
(currAdapter as ContactsAdapter).updateItems(contacts)
}
}
override fun refreshContacts(refreshTabsMask: Int) {
refreshContacts()
}
override fun removeFromGroup(contacts: ArrayList<Contact>) {
ContactsHelper(this).removeContactsFromGroup(contacts, group.id)
if (groupContacts.size == 0) {
refreshContacts()
}
}
}

View File

@ -26,9 +26,7 @@ import com.simplemobiletools.contacts.dialogs.ImportContactsDialog
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.extensions.dbHelper
import com.simplemobiletools.contacts.extensions.getTempFile
import com.simplemobiletools.contacts.fragments.MyViewPagerFragment
import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.helpers.VcfExporter
import com.simplemobiletools.contacts.helpers.*
import com.simplemobiletools.contacts.interfaces.RefreshContactsListener
import com.simplemobiletools.contacts.models.Contact
import kotlinx.android.synthetic.main.activity_main.*
@ -93,8 +91,9 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
val configShowContactThumbnails = config.showContactThumbnails
if (storedShowContactThumbnails != configShowContactThumbnails) {
contacts_fragment?.showContactThumbnailsChanged(configShowContactThumbnails)
favorites_fragment?.showContactThumbnailsChanged(configShowContactThumbnails)
getAllFragments().forEach {
it?.showContactThumbnailsChanged(configShowContactThumbnails)
}
}
val configTextColor = config.textColor
@ -102,8 +101,9 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
getInactiveTabIndexes(viewpager.currentItem).forEach {
main_tabs_holder.getTabAt(it)?.icon?.applyColorFilter(configTextColor)
}
contacts_fragment?.textColorChanged(configTextColor)
favorites_fragment?.textColorChanged(configTextColor)
getAllFragments().forEach {
it?.textColorChanged(configTextColor)
}
}
val configBackgroundColor = config.backgroundColor
@ -115,8 +115,9 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
if (storedPrimaryColor != configPrimaryColor) {
main_tabs_holder.setSelectedTabIndicatorColor(getAdjustedPrimaryColor())
main_tabs_holder.getTabAt(viewpager.currentItem)?.icon?.applyColorFilter(getAdjustedPrimaryColor())
contacts_fragment?.primaryColorChanged(configPrimaryColor)
favorites_fragment?.primaryColorChanged(configPrimaryColor)
getAllFragments().forEach {
it?.primaryColorChanged()
}
}
val configStartNameWithSurname = config.startNameWithSurname
@ -130,9 +131,10 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
initFragments()
}
contacts_fragment?.onActivityResume()
favorites_fragment?.onActivityResume()
refreshContacts(true, true)
getAllFragments().forEach {
it?.onActivityResume()
}
refreshContacts(ALL_TABS_MASK)
}
if (hasPermission(PERMISSION_WRITE_CONTACTS)) {
@ -152,6 +154,12 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu, menu)
val currentPage = viewpager?.currentItem
menu.apply {
findItem(R.id.search).isVisible = currentPage != LOCATION_GROUPS_TAB
findItem(R.id.sort).isVisible = currentPage != LOCATION_GROUPS_TAB
findItem(R.id.filter).isVisible = currentPage != LOCATION_GROUPS_TAB
}
setupSearch(menu)
return true
}
@ -193,7 +201,7 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
override fun onQueryTextChange(newText: String): Boolean {
if (isSearchOpen) {
(getCurrentFragment() as? MyViewPagerFragment)?.onSearchQueryChanged(newText)
getCurrentFragment()?.onSearchQueryChanged(newText)
}
return true
}
@ -202,13 +210,13 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
MenuItemCompat.setOnActionExpandListener(searchMenuItem, object : MenuItemCompat.OnActionExpandListener {
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
(getCurrentFragment() as? MyViewPagerFragment)?.onSearchOpened()
getCurrentFragment()?.onSearchOpened()
isSearchOpen = true
return true
}
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
(getCurrentFragment() as? MyViewPagerFragment)?.onSearchClosed()
getCurrentFragment()?.onSearchClosed()
isSearchOpen = false
return true
}
@ -266,12 +274,12 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
private fun getInactiveTabIndexes(activeIndex: Int) = arrayListOf(0, 1, 2).filter { it != activeIndex }
private fun initFragments() {
refreshContacts(true, true)
refreshContacts(ALL_TABS_MASK)
viewpager.offscreenPageLimit = 2
viewpager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {
if (isSearchOpen) {
(getCurrentFragment() as? MyViewPagerFragment)?.onSearchQueryChanged("")
getCurrentFragment()?.onSearchQueryChanged("")
searchMenuItem?.collapseActionView()
}
}
@ -281,12 +289,12 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
override fun onPageSelected(position: Int) {
main_tabs_holder.getTabAt(position)?.select()
contacts_fragment?.finishActMode()
favorites_fragment?.finishActMode()
getAllFragments().forEach {
it?.finishActMode()
}
invalidateOptionsMenu()
}
})
viewpager.currentItem = config.lastUsedViewPagerPage
main_tabs_holder.onTabSelectionChanged(
tabUnselectedAction = {
@ -305,14 +313,14 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
private fun showSortingDialog() {
ChangeSortingDialog(this) {
refreshContacts(true, true)
refreshContacts(CONTACTS_TAB_MASK or FAVORITES_TAB_MASK)
}
}
fun showFilterDialog() {
FilterContactSourcesDialog(this) {
contacts_fragment?.forceListRedraw = true
refreshContacts(true, false)
refreshContacts(CONTACTS_TAB_MASK)
}
}
@ -334,7 +342,7 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
ImportContactsDialog(this, path) {
if (it) {
runOnUiThread {
refreshContacts(true, true)
refreshContacts(ALL_TABS_MASK)
}
}
}
@ -397,7 +405,7 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
BuildConfig.VERSION_NAME, faqItems)
}
override fun refreshContacts(refreshContactsTab: Boolean, refreshFavoritesTab: Boolean) {
override fun refreshContacts(refreshTabsMask: Int) {
if (isActivityDestroyed()) {
return
}
@ -409,21 +417,27 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
if (viewpager.adapter == null) {
viewpager.adapter = ViewPagerAdapter(this, it)
viewpager.currentItem = config.lastUsedViewPagerPage
}
if (refreshContactsTab) {
if (refreshTabsMask and CONTACTS_TAB_MASK != 0) {
contacts_fragment?.refreshContacts(it)
}
if (refreshFavoritesTab) {
if (refreshTabsMask and FAVORITES_TAB_MASK != 0) {
favorites_fragment?.refreshContacts(it)
}
if (refreshTabsMask and GROUPS_TAB_MASK != 0) {
if (refreshTabsMask == GROUPS_TAB_MASK) {
groups_fragment.skipHashComparing = true
}
groups_fragment?.refreshContacts(it)
}
}
}
override fun refreshFavorites() {
refreshContacts(false, true)
}
private fun getAllFragments() = arrayListOf(contacts_fragment, favorites_fragment, groups_fragment)
private fun checkWhatsNewDialog() {
arrayListOf<Release>().apply {

View File

@ -23,7 +23,7 @@ import com.simplemobiletools.contacts.models.Contact
import kotlinx.android.synthetic.main.layout_select_contact.*
class SelectContactActivity : SimpleActivity() {
private var isGetEmailIntent = false
private var specialMimeType: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -34,7 +34,11 @@ class SelectContactActivity : SimpleActivity() {
if (it) {
handlePermission(PERMISSION_WRITE_CONTACTS) {
if (it) {
isGetEmailIntent = intent.data == ContactsContract.CommonDataKinds.Email.CONTENT_URI
specialMimeType = when (intent.data) {
ContactsContract.CommonDataKinds.Email.CONTENT_URI -> ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
ContactsContract.CommonDataKinds.Phone.CONTENT_URI -> ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
else -> null
}
initContacts()
} else {
toast(R.string.no_contacts_permission)
@ -81,8 +85,13 @@ class SelectContactActivity : SimpleActivity() {
}
var contacts = it.filter {
if (isGetEmailIntent) {
(it.source != SMT_PRIVATE && it.emails.isNotEmpty())
if (specialMimeType != null) {
val hasRequiredValues = when (specialMimeType) {
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE -> it.emails.isNotEmpty()
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> it.phoneNumbers.isNotEmpty()
else -> true
}
it.source != SMT_PRIVATE && hasRequiredValues
} else {
true
}
@ -119,12 +128,15 @@ class SelectContactActivity : SimpleActivity() {
}
private fun getResultUri(contact: Contact): Uri {
return if (isGetEmailIntent) {
val emailID = ContactsHelper(this).getContactDataId(contact.id.toString())
Uri.withAppendedPath(ContactsContract.Data.CONTENT_URI, emailID)
} else {
return when {
specialMimeType != null -> {
val contactId = ContactsHelper(this).getContactMimeTypeId(contact.id.toString(), specialMimeType!!)
Uri.withAppendedPath(ContactsContract.Data.CONTENT_URI, contactId)
}
else -> {
val lookupKey = ContactsHelper(this).getContactLookupKey(contact.id.toString())
Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey)
}
}
}
}

View File

@ -60,6 +60,7 @@ class ViewContactActivity : ContactActivity() {
}
private fun initContact() {
var wasLookupKeyUsed = false
var contactId = intent.getIntExtra(CONTACT_ID, 0)
val action = intent.action
if (contactId == 0 && (action == ContactsContract.QuickContact.ACTION_QUICK_CONTACT || action == Intent.ACTION_VIEW)) {
@ -69,6 +70,7 @@ class ViewContactActivity : ContactActivity() {
val lookupKey = getLookupKeyFromUri(data)
if (lookupKey != null) {
contact = ContactsHelper(this).getContactWithLookupKey(lookupKey)
wasLookupKeyUsed = true
}
getLookupUriRawId(data)
@ -82,7 +84,7 @@ class ViewContactActivity : ContactActivity() {
}
}
if (contactId != 0 && contact == null) {
if (contactId != 0 && !wasLookupKeyUsed) {
contact = ContactsHelper(this).getContactWithId(contactId, intent.getBooleanExtra(IS_PRIVATE, false))
if (contact == null) {
toast(R.string.unknown_error_occurred)

View File

@ -11,28 +11,34 @@ import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.signature.ObjectKey
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.getColoredDrawableWithColor
import com.simplemobiletools.commons.extensions.isActivityDestroyed
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.commons.views.FastScroller
import com.simplemobiletools.commons.views.MyRecyclerView
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.dialogs.CreateNewGroupDialog
import com.simplemobiletools.contacts.extensions.addContactsToGroup
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.extensions.editContact
import com.simplemobiletools.contacts.extensions.shareContacts
import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.helpers.*
import com.simplemobiletools.contacts.interfaces.RefreshContactsListener
import com.simplemobiletools.contacts.interfaces.RemoveFromGroupListener
import com.simplemobiletools.contacts.models.Contact
import kotlinx.android.synthetic.main.item_contact_with_number.view.*
import java.util.*
class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Contact>, private val listener: RefreshContactsListener?,
private val isFavoritesFragment: Boolean, recyclerView: MyRecyclerView, fastScroller: FastScroller, itemClick: (Any) -> Unit) :
class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Contact>, private val refreshListener: RefreshContactsListener?,
private val location: Int, private val removeListener: RemoveFromGroupListener?, recyclerView: MyRecyclerView,
fastScroller: FastScroller, itemClick: (Any) -> Unit) :
MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
private lateinit var contactDrawable: Drawable
var config = activity.config
private var config = activity.config
var startNameWithSurname: Boolean
var showContactThumbnails: Boolean
var showPhoneNumbers: Boolean
@ -52,10 +58,14 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Cont
override fun prepareActionMode(menu: Menu) {
menu.apply {
findItem(R.id.cab_edit).isVisible = isOneItemSelected()
findItem(R.id.cab_remove).isVisible = isFavoritesFragment
findItem(R.id.cab_select_all).isVisible = isFavoritesFragment
findItem(R.id.cab_add_to_favorites).isVisible = !isFavoritesFragment
findItem(R.id.cab_delete).isVisible = !isFavoritesFragment
findItem(R.id.cab_remove).isVisible = location == LOCATION_FAVORITES_TAB || location == LOCATION_GROUP_CONTACTS
findItem(R.id.cab_add_to_favorites).isVisible = location == LOCATION_CONTACTS_TAB
findItem(R.id.cab_add_to_group).isVisible = location == LOCATION_CONTACTS_TAB || location == LOCATION_FAVORITES_TAB
findItem(R.id.cab_delete).isVisible = location == LOCATION_CONTACTS_TAB || location == LOCATION_GROUP_CONTACTS
if (location == LOCATION_GROUP_CONTACTS) {
findItem(R.id.cab_remove).title = activity.getString(R.string.remove_from_group)
}
}
}
@ -74,8 +84,9 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Cont
R.id.cab_edit -> editContact()
R.id.cab_select_all -> selectAll()
R.id.cab_add_to_favorites -> addToFavorites()
R.id.cab_add_to_group -> addToGroup()
R.id.cab_share -> shareContacts()
R.id.cab_remove -> removeFavorites()
R.id.cab_remove -> removeContacts()
R.id.cab_delete -> askConfirmDelete()
}
}
@ -102,10 +113,12 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Cont
}
fun updateItems(newItems: ArrayList<Contact>) {
if (newItems.hashCode() != contactItems.hashCode()) {
contactItems = newItems
notifyDataSetChanged()
finishActMode()
}
}
private fun editContact() {
activity.editContact(contactItems[selectedPositions.first()])
@ -130,38 +143,77 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Cont
ContactsHelper(activity).deleteContacts(contactsToRemove)
if (contactItems.isEmpty()) {
listener?.refreshContacts(true, true)
refreshListener?.refreshContacts(ALL_TABS_MASK)
finishActMode()
} else {
removeSelectedItems()
listener?.refreshFavorites()
refreshListener?.refreshContacts(FAVORITES_TAB_MASK)
}
}
private fun removeFavorites() {
val favoritesToRemove = ArrayList<Contact>()
private fun removeContacts() {
val contactsToRemove = ArrayList<Contact>()
selectedPositions.sortedDescending().forEach {
favoritesToRemove.add(contactItems[it])
contactsToRemove.add(contactItems[it])
}
contactItems.removeAll(favoritesToRemove)
contactItems.removeAll(contactsToRemove)
ContactsHelper(activity).removeFavorites(favoritesToRemove)
if (location == LOCATION_FAVORITES_TAB) {
ContactsHelper(activity).removeFavorites(contactsToRemove)
if (contactItems.isEmpty()) {
listener?.refreshFavorites()
refreshListener?.refreshContacts(FAVORITES_TAB_MASK)
finishActMode()
} else {
removeSelectedItems()
}
} else if (location == LOCATION_GROUP_CONTACTS) {
removeListener?.removeFromGroup(contactsToRemove)
removeSelectedItems()
}
}
private fun addToFavorites() {
val newFavorites = ArrayList<Contact>()
selectedPositions.forEach { newFavorites.add(contactItems[it]) }
selectedPositions.forEach {
newFavorites.add(contactItems[it])
}
ContactsHelper(activity).addFavorites(newFavorites)
listener?.refreshFavorites()
refreshListener?.refreshContacts(FAVORITES_TAB_MASK)
finishActMode()
}
private fun addToGroup() {
val selectedContacts = ArrayList<Contact>()
selectedPositions.forEach {
selectedContacts.add(contactItems[it])
}
val NEW_GROUP_ID = -1
val items = ArrayList<RadioItem>()
ContactsHelper(activity).getStoredGroups().forEach {
items.add(RadioItem(it.id.toInt(), it.title))
}
items.add(RadioItem(NEW_GROUP_ID, activity.getString(R.string.create_new_group)))
RadioGroupDialog(activity, items, 0) {
if (it as Int == NEW_GROUP_ID) {
CreateNewGroupDialog(activity) {
Thread {
activity.addContactsToGroup(selectedContacts, it.id)
refreshListener?.refreshContacts(GROUPS_TAB_MASK)
}.start()
finishActMode()
}
} else {
Thread {
activity.addContactsToGroup(selectedContacts, it.toLong())
refreshListener?.refreshContacts(GROUPS_TAB_MASK)
}.start()
finishActMode()
}
}
}
private fun shareContacts() {
val contactsIDs = ArrayList<Int>()
selectedPositions.forEach {

View File

@ -0,0 +1,131 @@
package com.simplemobiletools.contacts.adapters
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.extensions.applyColorFilter
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.views.FastScroller
import com.simplemobiletools.commons.views.MyRecyclerView
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.dialogs.RenameGroupDialog
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.extensions.dbHelper
import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.helpers.GROUPS_TAB_MASK
import com.simplemobiletools.contacts.interfaces.RefreshContactsListener
import com.simplemobiletools.contacts.models.Group
import kotlinx.android.synthetic.main.item_group.view.*
import java.util.*
class GroupsAdapter(activity: SimpleActivity, var groups: ArrayList<Group>, val refreshListener: RefreshContactsListener?, recyclerView: MyRecyclerView,
fastScroller: FastScroller, itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
private var config = activity.config
private var smallPadding = activity.resources.getDimension(R.dimen.small_margin).toInt()
private var bigPadding = activity.resources.getDimension(R.dimen.normal_margin).toInt()
var showContactThumbnails = config.showContactThumbnails
override fun getActionMenuId() = R.menu.cab_groups
override fun prepareActionMode(menu: Menu) {
menu.apply {
findItem(R.id.cab_edit).isVisible = isOneItemSelected()
}
}
override fun prepareItemSelection(view: View) {}
override fun markItemSelection(select: Boolean, view: View?) {
view?.group_frame?.isSelected = select
}
override fun actionItemPressed(id: Int) {
if (selectedPositions.isEmpty()) {
return
}
when (id) {
R.id.cab_edit -> editGroup()
R.id.cab_select_all -> selectAll()
R.id.cab_delete -> askConfirmDelete()
}
}
override fun getSelectableItemCount() = groups.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_group, parent)
override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) {
val group = groups[position]
val view = holder.bindView(group, true) { itemView, layoutPosition ->
setupView(itemView, group)
}
bindViewHolder(holder, position, view)
}
override fun getItemCount() = groups.size
fun updateItems(newItems: ArrayList<Group>) {
groups = newItems
notifyDataSetChanged()
finishActMode()
}
private fun editGroup() {
RenameGroupDialog(activity, groups[selectedPositions.first()]) {
finishActMode()
refreshListener?.refreshContacts(GROUPS_TAB_MASK)
}
}
private fun askConfirmDelete() {
ConfirmationDialog(activity) {
deleteContacts()
}
}
private fun deleteContacts() {
if (selectedPositions.isEmpty()) {
return
}
val groupsToRemove = ArrayList<Group>()
selectedPositions.sortedDescending().forEach {
val group = groups[it]
groupsToRemove.add(group)
if (group.isPrivateSecretGroup()) {
activity.dbHelper.deleteGroup(group.id)
} else {
ContactsHelper(activity).deleteGroup(group.id)
}
}
groups.removeAll(groupsToRemove)
if (groups.isEmpty()) {
refreshListener?.refreshContacts(GROUPS_TAB_MASK)
finishActMode()
} else {
removeSelectedItems()
}
}
private fun setupView(view: View, group: Group) {
view.apply {
group_name.apply {
setTextColor(textColor)
text = String.format(activity.getString(R.string.groups_placeholder), group.title, group.contactsCount.toString())
setPadding(if (showContactThumbnails) smallPadding else bigPadding, smallPadding, smallPadding, 0)
}
group_tmb.beVisibleIf(showContactThumbnails)
if (showContactThumbnails) {
group_tmb.applyColorFilter(textColor)
}
}
}
}

View File

@ -23,7 +23,7 @@ import com.simplemobiletools.contacts.models.Contact
import kotlinx.android.synthetic.main.item_add_favorite_with_number.view.*
import java.util.*
class SelectContactsAdapter(val activity: SimpleActivity, val contacts: List<Contact>, private val selectedContacts: ArrayList<String>, private val allowPickMultiple: Boolean,
class SelectContactsAdapter(val activity: SimpleActivity, val contacts: List<Contact>, private val selectedContacts: ArrayList<Contact>, private val allowPickMultiple: Boolean,
private val itemClick: ((Contact) -> Unit)? = null) : RecyclerView.Adapter<SelectContactsAdapter.ViewHolder>() {
private val itemViews = SparseArray<View>()
private val selectedPositions = HashSet<Int>()
@ -39,7 +39,7 @@ class SelectContactsAdapter(val activity: SimpleActivity, val contacts: List<Con
init {
contacts.forEachIndexed { index, contact ->
if (selectedContacts.contains(contact.id.toString())) {
if (selectedContacts.map { it.id }.contains(contact.id)) {
selectedPositions.add(index)
}
}

View File

@ -5,7 +5,7 @@ import android.view.View
import android.view.ViewGroup
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.MainActivity
import com.simplemobiletools.contacts.interfaces.FragmentInterface
import com.simplemobiletools.contacts.fragments.MyViewPagerFragment
import com.simplemobiletools.contacts.models.Contact
class ViewPagerAdapter(val activity: MainActivity, val contacts: ArrayList<Contact>) : PagerAdapter() {
@ -14,7 +14,7 @@ class ViewPagerAdapter(val activity: MainActivity, val contacts: ArrayList<Conta
val layout = getFragment(position)
val view = activity.layoutInflater.inflate(layout, container, false)
container.addView(view)
(view as FragmentInterface).apply {
(view as MyViewPagerFragment).apply {
setupFragment(activity)
refreshContacts(contacts)
}

View File

@ -1,67 +0,0 @@
package com.simplemobiletools.contacts.dialogs
import android.support.v7.app.AlertDialog
import com.simplemobiletools.commons.extensions.baseConfig
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.adapters.SelectContactsAdapter
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.models.Contact
import kotlinx.android.synthetic.main.layout_select_contact.view.*
class AddFavoritesDialog(val activity: SimpleActivity, private val callback: () -> Unit) {
private var view = activity.layoutInflater.inflate(R.layout.layout_select_contact, null)
private val config = activity.config
private var allContacts = ArrayList<Contact>()
init {
ContactsHelper(activity).getContacts {
allContacts = it
val contactSources = config.displayContactSources
if (!activity.config.showAllContacts()) {
allContacts = allContacts.filter { contactSources.contains(it.source) } as ArrayList<Contact>
}
val favorites = allContacts.filter { it.starred == 1 }.map { it.id.toString() } as ArrayList<String>
Contact.sorting = config.sorting
allContacts.sort()
activity.runOnUiThread {
view.apply {
select_contact_list.adapter = SelectContactsAdapter(activity, allContacts, favorites, true)
select_contact_fastscroller.allowBubbleDisplay = activity.baseConfig.showInfoBubble
select_contact_fastscroller.setViews(select_contact_list) {
select_contact_fastscroller.updateBubbleText(allContacts[it].getBubbleText())
}
}
}
}
AlertDialog.Builder(activity)
.setPositiveButton(R.string.ok, { dialog, which -> dialogConfirmed() })
.setNegativeButton(R.string.cancel, null)
.create().apply {
activity.setupDialogStuff(view, this)
}
}
private fun dialogConfirmed() {
Thread {
val contactsHelper = ContactsHelper(activity)
val allDisplayedContacts = ArrayList<Contact>()
allContacts.mapTo(allDisplayedContacts, { it })
val selectedContacts = (view?.select_contact_list?.adapter as? SelectContactsAdapter)?.getSelectedItemsSet() ?: LinkedHashSet()
val contactsToAdd = selectedContacts.map { it } as ArrayList<Contact>
contactsHelper.addFavorites(contactsToAdd)
allDisplayedContacts.removeAll(selectedContacts)
contactsHelper.removeFavorites(allDisplayedContacts)
callback()
}.start()
}
}

View File

@ -3,12 +3,17 @@ package com.simplemobiletools.contacts.dialogs
import android.support.v7.app.AlertDialog
import android.view.View
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.extensions.showKeyboard
import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.commons.extensions.value
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.helpers.SMT_PRIVATE
import com.simplemobiletools.contacts.models.ContactSource
import com.simplemobiletools.contacts.models.Group
import kotlinx.android.synthetic.main.dialog_create_new_group.view.*
@ -29,13 +34,41 @@ class CreateNewGroupDialog(val activity: BaseSimpleActivity, val callback: (newG
return@OnClickListener
}
val newGroup = ContactsHelper(activity).createNewGroup(name)
if (newGroup != null) {
callback(newGroup)
val contactSources = ArrayList<ContactSource>()
if (activity.config.localAccountName.isNotEmpty()) {
contactSources.add(ContactSource(activity.config.localAccountName, activity.config.localAccountType))
}
ContactsHelper(activity).getContactSources {
it.filter { it.type.contains("google", true) }.mapTo(contactSources, { ContactSource(it.name, it.type) })
contactSources.add(ContactSource(activity.getString(R.string.phone_storage_hidden), SMT_PRIVATE))
val items = ArrayList<RadioItem>()
contactSources.forEachIndexed { index, contactSource ->
items.add(RadioItem(index, contactSource.name))
}
activity.runOnUiThread {
if (items.size == 1) {
createGroupUnder(name, contactSources.first(), this)
} else {
RadioGroupDialog(activity, items, titleId = R.string.create_group_under_account) {
val contactSource = contactSources[it as Int]
createGroupUnder(name, contactSource, this)
}
}
}
}
dismiss()
})
}
}
}
private fun createGroupUnder(name: String, contactSource: ContactSource, dialog: AlertDialog) {
val newGroup = ContactsHelper(activity).createNewGroup(name, contactSource.name, contactSource.type)
if (newGroup != null) {
callback(newGroup)
}
dialog.dismiss()
}
}

View File

@ -0,0 +1,49 @@
package com.simplemobiletools.contacts.dialogs
import android.support.v7.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.extensions.dbHelper
import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.models.Group
import kotlinx.android.synthetic.main.dialog_rename_group.view.*
class RenameGroupDialog(val activity: BaseSimpleActivity, val group: Group, val callback: () -> Unit) {
init {
val view = activity.layoutInflater.inflate(R.layout.dialog_rename_group, null).apply {
rename_group_title.setText(group.title)
}
AlertDialog.Builder(activity)
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.cancel, null)
.create().apply {
activity.setupDialogStuff(view, this, R.string.rename) {
showKeyboard(view.rename_group_title)
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
val newTitle = view.rename_group_title.value
if (newTitle.isEmpty()) {
activity.toast(R.string.empty_name)
return@setOnClickListener
}
if (!newTitle.isAValidFilename()) {
activity.toast(R.string.invalid_name)
return@setOnClickListener
}
group.title = newTitle
if (group.isPrivateSecretGroup()) {
activity.dbHelper.renameGroup(group)
} else {
ContactsHelper(activity).renameGroup(group)
}
callback()
dismiss()
}
}
}
}
}

View File

@ -0,0 +1,62 @@
package com.simplemobiletools.contacts.dialogs
import android.support.v7.app.AlertDialog
import com.simplemobiletools.commons.extensions.baseConfig
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.adapters.SelectContactsAdapter
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.models.Contact
import kotlinx.android.synthetic.main.layout_select_contact.view.*
class SelectContactsDialog(val activity: SimpleActivity, initialContacts: ArrayList<Contact>, val selectContacts: ArrayList<Contact>? = null,
val callback: (addedContacts: ArrayList<Contact>, removedContacts: ArrayList<Contact>) -> Unit) {
private var view = activity.layoutInflater.inflate(R.layout.layout_select_contact, null)
private var initiallySelectedContacts = ArrayList<Contact>()
init {
var allContacts = initialContacts
if (selectContacts == null) {
val contactSources = activity.config.displayContactSources
if (!activity.config.showAllContacts()) {
allContacts = allContacts.filter { contactSources.contains(it.source) } as ArrayList<Contact>
}
initiallySelectedContacts = allContacts.filter { it.starred == 1 } as ArrayList<Contact>
} else {
initiallySelectedContacts = selectContacts
}
Contact.sorting = activity.config.sorting
allContacts.sort()
activity.runOnUiThread {
view.apply {
select_contact_list.adapter = SelectContactsAdapter(activity, allContacts, initiallySelectedContacts, true)
select_contact_fastscroller.allowBubbleDisplay = activity.baseConfig.showInfoBubble
select_contact_fastscroller.setViews(select_contact_list) {
select_contact_fastscroller.updateBubbleText(allContacts[it].getBubbleText())
}
}
}
AlertDialog.Builder(activity)
.setPositiveButton(R.string.ok, { dialog, which -> dialogConfirmed() })
.setNegativeButton(R.string.cancel, null)
.create().apply {
activity.setupDialogStuff(view, this)
}
}
private fun dialogConfirmed() {
Thread {
val adapter = view?.select_contact_list?.adapter as? SelectContactsAdapter
val selectedContacts = adapter?.getSelectedItemsSet()?.toList() ?: ArrayList()
val newlySelectedContacts = selectedContacts.filter { !initiallySelectedContacts.contains(it) } as ArrayList
val unselectedContacts = initiallySelectedContacts.filter { !selectedContacts.contains(it) } as ArrayList
callback(newlySelectedContacts, unselectedContacts)
}.start()
}
}

View File

@ -5,6 +5,7 @@ import android.net.Uri
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.sharePathIntent
import com.simplemobiletools.commons.extensions.showErrorToast
import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.commons.helpers.PERMISSION_CALL_PHONE
import com.simplemobiletools.commons.models.RadioItem
@ -93,6 +94,8 @@ fun BaseSimpleActivity.shareContacts(contacts: ArrayList<Contact>) {
VcfExporter().exportContacts(this, file, contacts) {
if (it == VcfExporter.ExportResult.EXPORT_OK) {
sharePathIntent(file.absolutePath, BuildConfig.APPLICATION_ID)
} else {
showErrorToast("$it")
}
}
}
@ -108,3 +111,27 @@ fun BaseSimpleActivity.getTempFile(): File? {
return File(folder, "contacts.vcf")
}
fun BaseSimpleActivity.addContactsToGroup(contacts: ArrayList<Contact>, groupId: Long) {
val publicContacts = contacts.filter { it.source != SMT_PRIVATE }
val privateContacts = contacts.filter { it.source == SMT_PRIVATE }
if (publicContacts.isNotEmpty()) {
ContactsHelper(this).addContactsToGroup(contacts, groupId)
}
if (privateContacts.isNotEmpty()) {
dbHelper.addContactsToGroup(contacts, groupId)
}
}
fun BaseSimpleActivity.removeContactsFromGroup(contacts: ArrayList<Contact>, groupId: Long) {
val publicContacts = contacts.filter { it.source != SMT_PRIVATE }
val privateContacts = contacts.filter { it.source == SMT_PRIVATE }
if (publicContacts.isNotEmpty()) {
ContactsHelper(this).removeContactsFromGroup(contacts, groupId)
}
if (privateContacts.isNotEmpty()) {
dbHelper.removeContactsFromGroup(contacts, groupId)
}
}

View File

@ -2,7 +2,10 @@ package com.simplemobiletools.contacts.fragments
import android.content.Context
import android.util.AttributeSet
import com.simplemobiletools.contacts.dialogs.AddFavoritesDialog
import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.dialogs.SelectContactsDialog
import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.helpers.FAVORITES_TAB_MASK
class FavoritesFragment(context: Context, attributeSet: AttributeSet) : MyViewPagerFragment(context, attributeSet) {
override fun fabClicked() {
@ -15,8 +18,13 @@ class FavoritesFragment(context: Context, attributeSet: AttributeSet) : MyViewPa
}
private fun showAddFavoritesDialog() {
AddFavoritesDialog(activity!!) {
activity!!.refreshContacts(false, true)
SelectContactsDialog(activity!!, allContacts) { addedContacts, removedContacts ->
ContactsHelper(activity as SimpleActivity).apply {
addFavorites(addedContacts)
removeFavorites(removedContacts)
}
activity!!.refreshContacts(FAVORITES_TAB_MASK)
}
}
}

View File

@ -1,22 +1,24 @@
package com.simplemobiletools.contacts.fragments
import android.content.Context
import android.support.design.widget.CoordinatorLayout
import android.util.AttributeSet
import com.simplemobiletools.contacts.activities.MainActivity
import com.simplemobiletools.contacts.interfaces.FragmentInterface
import com.simplemobiletools.contacts.models.Contact
import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.dialogs.CreateNewGroupDialog
import com.simplemobiletools.contacts.helpers.GROUPS_TAB_MASK
class GroupsFragment(context: Context, attributeSet: AttributeSet) : CoordinatorLayout(context, attributeSet), FragmentInterface {
override fun setupFragment(activity: MainActivity) {
class GroupsFragment(context: Context, attributeSet: AttributeSet) : MyViewPagerFragment(context, attributeSet) {
override fun fabClicked() {
finishActMode()
showNewGroupsDialog()
}
override fun textColorChanged(color: Int) {
override fun placeholderClicked() {
showNewGroupsDialog()
}
override fun primaryColorChanged(color: Int) {
private fun showNewGroupsDialog() {
CreateNewGroupDialog(activity as SimpleActivity) {
activity!!.refreshContacts(GROUPS_TAB_MASK)
}
override fun refreshContacts(contacts: ArrayList<Contact>) {
}
}

View File

@ -1,37 +1,41 @@
package com.simplemobiletools.contacts.fragments
import android.content.Context
import android.content.Intent
import android.support.design.widget.CoordinatorLayout
import android.util.AttributeSet
import android.view.ViewGroup
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.SORT_BY_FIRST_NAME
import com.simplemobiletools.commons.helpers.SORT_BY_SURNAME
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.GroupContactsActivity
import com.simplemobiletools.contacts.activities.MainActivity
import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.adapters.ContactsAdapter
import com.simplemobiletools.contacts.adapters.GroupsAdapter
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.extensions.editContact
import com.simplemobiletools.contacts.extensions.tryStartCall
import com.simplemobiletools.contacts.extensions.viewContact
import com.simplemobiletools.contacts.helpers.Config
import com.simplemobiletools.contacts.helpers.ON_CLICK_CALL_CONTACT
import com.simplemobiletools.contacts.helpers.ON_CLICK_EDIT_CONTACT
import com.simplemobiletools.contacts.helpers.ON_CLICK_VIEW_CONTACT
import com.simplemobiletools.contacts.interfaces.FragmentInterface
import com.simplemobiletools.contacts.helpers.*
import com.simplemobiletools.contacts.models.Contact
import com.simplemobiletools.contacts.models.Group
import kotlinx.android.synthetic.main.fragment_layout.view.*
abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet) : CoordinatorLayout(context, attributeSet), FragmentInterface {
abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet) : CoordinatorLayout(context, attributeSet) {
protected var activity: MainActivity? = null
protected var allContacts = ArrayList<Contact>()
private var lastHashCode = 0
private var contactsIgnoringSearch = ArrayList<Contact>()
private lateinit var config: Config
var skipHashComparing = false
var forceListRedraw = false
override fun setupFragment(activity: MainActivity) {
fun setupFragment(activity: MainActivity) {
config = activity.config
if (this.activity == null) {
this.activity = activity
@ -49,36 +53,51 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
if (this is FavoritesFragment) {
fragment_placeholder.text = activity.getString(R.string.no_favorites)
fragment_placeholder_2.text = activity.getString(R.string.add_favorites)
} else if (this is GroupsFragment) {
fragment_placeholder.text = activity.getString(R.string.no_group_created)
fragment_placeholder_2.text = activity.getString(R.string.create_group)
}
}
}
override fun textColorChanged(color: Int) {
fun textColorChanged(color: Int) {
if (this is GroupsFragment) {
(fragment_list.adapter as GroupsAdapter).updateTextColor(color)
} else {
(fragment_list.adapter as ContactsAdapter).apply {
updateTextColor(color)
initDrawables()
}
}
}
override fun primaryColorChanged(color: Int) {
fun primaryColorChanged() {
fragment_fastscroller.updatePrimaryColor()
fragment_fastscroller.updateBubblePrimaryColor()
}
fun startNameWithSurnameChanged(startNameWithSurname: Boolean) {
if (this !is GroupsFragment) {
(fragment_list.adapter as ContactsAdapter).apply {
config.sorting = if (startNameWithSurname) SORT_BY_SURNAME else SORT_BY_FIRST_NAME
this@MyViewPagerFragment.activity!!.refreshContacts(true, true)
this@MyViewPagerFragment.activity!!.refreshContacts(CONTACTS_TAB_MASK or FAVORITES_TAB_MASK)
}
}
}
override fun refreshContacts(contacts: ArrayList<Contact>) {
fun refreshContacts(contacts: ArrayList<Contact>) {
if (config.lastUsedContactSource.isEmpty()) {
val grouped = contacts.groupBy { it.source }.maxWith(compareBy { it.value.size })
config.lastUsedContactSource = grouped?.key ?: ""
}
val filtered = if (this is FavoritesFragment) {
Contact.sorting = config.sorting
contacts.sort()
allContacts = contacts
val filtered = if (this is GroupsFragment) {
contacts
} else if (this is FavoritesFragment) {
contacts.filter { it.starred == 1 } as ArrayList<Contact>
} else {
val contactSources = config.displayContactSources
@ -89,10 +108,8 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
}
}
Contact.sorting = config.sorting
filtered.sort()
if (filtered.hashCode() != lastHashCode) {
if (filtered.hashCode() != lastHashCode || skipHashComparing) {
skipHashComparing = false
lastHashCode = filtered.hashCode()
activity?.runOnUiThread {
setupContacts(filtered)
@ -101,6 +118,56 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
}
private fun setupContacts(contacts: ArrayList<Contact>) {
if (this is GroupsFragment) {
setupGroupsAdapter(contacts)
} else {
setupContactsFavoritesAdapter(contacts)
}
}
private fun setupGroupsAdapter(contacts: ArrayList<Contact>) {
var storedGroups = ContactsHelper(activity!!).getStoredGroups()
contacts.forEach {
it.groups.forEach {
val group = it
val storedGroup = storedGroups.firstOrNull { it.id == group.id }
storedGroup?.addContact()
}
}
storedGroups = storedGroups.sortedWith(compareBy { it.title }).toMutableList() as ArrayList<Group>
fragment_placeholder_2.beVisibleIf(storedGroups.isEmpty())
fragment_placeholder.beVisibleIf(storedGroups.isEmpty())
fragment_list.beVisibleIf(storedGroups.isNotEmpty())
val currAdapter = fragment_list.adapter
if (currAdapter == null) {
GroupsAdapter(activity as SimpleActivity, storedGroups, activity, fragment_list, fragment_fastscroller) {
Intent(activity, GroupContactsActivity::class.java).apply {
putExtra(GROUP, it as Group)
activity!!.startActivity(this)
}
}.apply {
setupDragListener(true)
addVerticalDividers(true)
fragment_list.adapter = this
}
fragment_fastscroller.setScrollTo(0)
fragment_fastscroller.setViews(fragment_list) {
val item = (fragment_list.adapter as GroupsAdapter).groups.getOrNull(it)
fragment_fastscroller.updateBubbleText(item?.getBubbleText() ?: "")
}
} else {
(currAdapter as GroupsAdapter).apply {
showContactThumbnails = activity.config.showContactThumbnails
updateItems(storedGroups)
}
}
}
private fun setupContactsFavoritesAdapter(contacts: ArrayList<Contact>) {
fragment_placeholder_2.beVisibleIf(contacts.isEmpty())
fragment_placeholder.beVisibleIf(contacts.isEmpty())
fragment_list.beVisibleIf(contacts.isNotEmpty())
@ -108,7 +175,8 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
val currAdapter = fragment_list.adapter
if (currAdapter == null || forceListRedraw) {
forceListRedraw = false
ContactsAdapter(activity as SimpleActivity, contacts, activity, this is FavoritesFragment, fragment_list, fragment_fastscroller) {
val location = if (this is FavoritesFragment) LOCATION_FAVORITES_TAB else LOCATION_CONTACTS_TAB
ContactsAdapter(activity as SimpleActivity, contacts, activity, location, null, fragment_list, fragment_fastscroller) {
when (config.onContactClick) {
ON_CLICK_CALL_CONTACT -> {
val contact = it as Contact
@ -143,18 +211,25 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
}
fun showContactThumbnailsChanged(showThumbnails: Boolean) {
if (this is GroupsFragment) {
(fragment_list.adapter as? GroupsAdapter)?.apply {
showContactThumbnails = showThumbnails
notifyDataSetChanged()
}
} else {
(fragment_list.adapter as? ContactsAdapter)?.apply {
showContactThumbnails = showThumbnails
notifyDataSetChanged()
}
}
}
fun onActivityResume() {
updateViewStuff()
}
fun finishActMode() {
(fragment_list.adapter as? ContactsAdapter)?.finishActMode()
(fragment_list.adapter as? MyRecyclerViewAdapter)?.finishActMode()
}
fun onSearchQueryChanged(text: String) {

View File

@ -13,6 +13,18 @@ const val ON_CONTACT_CLICK = "on_contact_click"
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 IS_PRIVATE = "is_private"
const val GROUP = "group"
const val FIRST_GROUP_ID = 10000
const val LOCATION_CONTACTS_TAB = 0
const val LOCATION_FAVORITES_TAB = 1
const val LOCATION_GROUPS_TAB = 2
const val LOCATION_GROUP_CONTACTS = 3
const val CONTACTS_TAB_MASK = 1
const val FAVORITES_TAB_MASK = 2
const val GROUPS_TAB_MASK = 4
const val ALL_TABS_MASK = 7
// contact photo changes
const val PHOTO_ADDED = 1

View File

@ -11,6 +11,7 @@ import android.provider.ContactsContract
import android.provider.ContactsContract.CommonDataKinds
import android.provider.ContactsContract.CommonDataKinds.Note
import android.provider.MediaStore
import android.text.TextUtils
import android.util.SparseArray
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.*
@ -101,14 +102,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
contacts[key]?.notes = notes.valueAt(i)
}
val groups = getContactGroups(getStoredGroups())
size = groups.size()
for (i in 0 until size) {
val key = groups.keyAt(i)
contacts[key]?.groups = groups.valueAt(i)
}
activity.dbHelper.getContacts().forEach {
activity.dbHelper.getContacts(activity).forEach {
contacts.put(it.id, it)
}
@ -116,6 +110,15 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
var resultContacts = ArrayList<Contact>(contactsSize)
(0 until contactsSize).mapTo(resultContacts) { contacts.valueAt(it) }
resultContacts = resultContacts.distinctBy { it.contactId } as ArrayList<Contact>
// groups are obtained with contactID, not rawID, so assign them to proper contacts like this
val groups = getContactGroups(getStoredGroups())
size = groups.size()
for (i in 0 until size) {
val key = groups.keyAt(i)
resultContacts.firstOrNull { it.contactId == key }?.groups = groups.valueAt(i)
}
activity.runOnUiThread {
callback(resultContacts)
}
@ -336,12 +339,11 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
val id = cursor.getIntValue(ContactsContract.Data.CONTACT_ID)
val newRowId = cursor.getLongValue(ContactsContract.Data.DATA1)
val groupTitle = storedGroups.firstOrNull { it.id == newRowId }?.title ?: continue
val group = Group(newRowId, groupTitle)
if (groups[id] == null) {
groups.put(id, ArrayList())
}
val groupTitle = storedGroups.firstOrNull { it.id == newRowId }?.title ?: continue
val group = Group(newRowId, groupTitle)
groups[id]!!.add(group)
} while (cursor.moveToNext())
}
@ -359,10 +361,11 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
val uri = ContactsContract.Groups.CONTENT_URI
val projection = arrayOf(
ContactsContract.Groups._ID,
ContactsContract.Groups.TITLE
ContactsContract.Groups.TITLE,
ContactsContract.Groups.SYSTEM_ID
)
val selection = "${ContactsContract.Groups.AUTO_ADD} = ? AND ${ContactsContract.Groups.FAVORITES} = ? AND ${ContactsContract.Groups.SYSTEM_ID} IS NULL"
val selection = "${ContactsContract.Groups.AUTO_ADD} = ? AND ${ContactsContract.Groups.FAVORITES} = ?"
val selectionArgs = arrayOf("0", "0")
var cursor: Cursor? = null
@ -372,6 +375,12 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
do {
val id = cursor.getLongValue(ContactsContract.Groups._ID)
val title = cursor.getStringValue(ContactsContract.Groups.TITLE)
val systemId = cursor.getStringValue(ContactsContract.Groups.SYSTEM_ID)
if (groups.map { it.title }.contains(title) && systemId != null) {
continue
}
groups.add(Group(id, title))
} while (cursor.moveToNext())
}
@ -381,17 +390,25 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
cursor?.close()
}
groups.addAll(activity.dbHelper.getGroups())
return groups
}
fun createNewGroup(title: String): Group? {
try {
fun createNewGroup(title: String, accountName: String, accountType: String): Group? {
if (accountType == SMT_PRIVATE) {
return activity.dbHelper.insertGroup(Group(0, title))
}
val operations = ArrayList<ContentProviderOperation>()
ContentProviderOperation.newInsert(ContactsContract.Groups.CONTENT_URI).apply {
withValue(ContactsContract.Groups.TITLE, title)
withValue(ContactsContract.Groups.GROUP_VISIBLE, 1)
withValue(ContactsContract.Groups.ACCOUNT_NAME, accountName)
withValue(ContactsContract.Groups.ACCOUNT_TYPE, accountType)
operations.add(build())
}
try {
val results = activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
val rawId = ContentUris.parseId(results[0].uri)
return Group(rawId, title)
@ -401,14 +418,32 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
return null
}
fun deleteGroup(id: Long) {
fun renameGroup(group: Group) {
val operations = ArrayList<ContentProviderOperation>()
ContentProviderOperation.newUpdate(ContactsContract.Groups.CONTENT_URI).apply {
val selection = "${ContactsContract.Groups._ID} = ?"
val selectionArgs = arrayOf(group.id.toString())
withSelection(selection, selectionArgs)
withValue(ContactsContract.Groups.TITLE, group.title)
operations.add(build())
}
try {
activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
} catch (e: Exception) {
activity.showErrorToast(e)
}
}
fun deleteGroup(id: Long) {
val operations = ArrayList<ContentProviderOperation>()
val uri = ContentUris.withAppendedId(ContactsContract.Groups.CONTENT_URI, id).buildUpon()
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.build()
operations.add(ContentProviderOperation.newDelete(uri).build())
try {
activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
} catch (e: Exception) {
activity.showErrorToast(e)
@ -419,7 +454,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
if (id == 0) {
return null
} else if (isLocalPrivate) {
return activity.dbHelper.getContactWithId(id)
return activity.dbHelper.getContactWithId(activity, id)
}
val selection = "${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.Data.RAW_CONTACT_ID} = ?"
@ -489,6 +524,10 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
cursor?.close()
}
if (sources.isEmpty() && activity.config.localAccountName.isEmpty() && activity.config.localAccountType.isEmpty()) {
sources.add(ContactSource("", ""))
}
sources.add(ContactSource(activity.getString(R.string.phone_storage_hidden), SMT_PRIVATE))
callback(ArrayList(sources))
}.start()
@ -563,10 +602,12 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
}
fun updateContact(contact: Contact, photoUpdateStatus: Int): Boolean {
return if (contact.source == SMT_PRIVATE) {
activity.dbHelper.update(contact)
} else try {
activity.toast(R.string.updating)
if (contact.source == SMT_PRIVATE) {
return activity.dbHelper.updateContact(contact)
}
try {
val operations = ArrayList<ContentProviderOperation>()
ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI).apply {
val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ?"
@ -664,12 +705,16 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
}
// delete groups
val relevantGroupIDs = getStoredGroups().map { it.id }
if (relevantGroupIDs.isNotEmpty()) {
val IDsString = TextUtils.join(",", relevantGroupIDs)
ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE)
val selection = "${ContactsContract.Data.CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.Data.DATA1} IN ($IDsString)"
val selectionArgs = arrayOf(contact.contactId.toString(), CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs)
operations.add(build())
}
}
// add groups
contact.groups.forEach {
@ -698,10 +743,10 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
}
activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
true
return true
} catch (e: Exception) {
activity.showErrorToast(e)
false
return false
}
}
@ -741,10 +786,37 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
return operations
}
fun addContactsToGroup(contacts: ArrayList<Contact>, groupId: Long) {
val operations = ArrayList<ContentProviderOperation>()
contacts.forEach {
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValue(ContactsContract.Data.RAW_CONTACT_ID, it.id)
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.GroupMembership.GROUP_ROW_ID, groupId)
operations.add(build())
}
}
activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
}
fun removeContactsFromGroup(contacts: ArrayList<Contact>, groupId: Long) {
val operations = ArrayList<ContentProviderOperation>()
contacts.forEach {
ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
val selection = "${ContactsContract.Data.CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.Data.DATA1} = ?"
val selectionArgs = arrayOf(it.contactId.toString(), CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE, groupId.toString())
withSelection(selection, selectionArgs)
operations.add(build())
}
}
activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
}
fun insertContact(contact: Contact): Boolean {
return if (contact.source == SMT_PRIVATE) {
insertLocalContact(contact)
} else {
if (contact.source == SMT_PRIVATE) {
return insertLocalContact(contact)
}
try {
val operations = ArrayList<ContentProviderOperation>()
ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI).apply {
@ -870,15 +942,14 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
activity.contentResolver.update(uri, contentValues, null, null)
}
true
return true
} catch (e: Exception) {
activity.showErrorToast(e)
false
}
return false
}
}
private fun insertLocalContact(contact: Contact) = activity.dbHelper.insert(contact)
private fun insertLocalContact(contact: Contact) = activity.dbHelper.insertContact(contact)
private fun addFullSizePhoto(contactId: Long, fullSizePhotoData: ByteArray) {
val baseUri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, contactId)
@ -909,11 +980,11 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
return ""
}
fun getContactDataId(contactId: String): String {
fun getContactMimeTypeId(contactId: String, mimeType: String): String {
val uri = ContactsContract.Data.CONTENT_URI
val projection = arrayOf(ContactsContract.Data._ID, ContactsContract.Data.RAW_CONTACT_ID, ContactsContract.Data.MIMETYPE)
val selection = "${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.Data.RAW_CONTACT_ID} = ?"
val selectionArgs = arrayOf(CommonDataKinds.Email.CONTENT_ITEM_TYPE, contactId)
val selectionArgs = arrayOf(mimeType, contactId)
var cursor: Cursor? = null
try {
@ -979,10 +1050,10 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
try {
val contactIDs = HashSet<String>()
val operations = ArrayList<ContentProviderOperation>()
val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ?"
val selection = "${ContactsContract.Data.CONTACT_ID} = ?"
contacts.filter { it.source != SMT_PRIVATE }.forEach {
ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
val selectionArgs = arrayOf(it.id.toString())
val selectionArgs = arrayOf(it.contactId.toString())
withSelection(selection, selectionArgs)
operations.add(this.build())
}

View File

@ -11,8 +11,10 @@ import android.provider.MediaStore
import android.text.TextUtils
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.getBlobValue
import com.simplemobiletools.commons.extensions.getIntValue
import com.simplemobiletools.commons.extensions.getLongValue
import com.simplemobiletools.commons.extensions.getStringValue
import com.simplemobiletools.contacts.extensions.getByteArray
import com.simplemobiletools.contacts.extensions.getPhotoThumbnailSize
@ -31,13 +33,17 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
private val COL_STARRED = "starred"
private val COL_ADDRESSES = "addresses"
private val COL_NOTES = "notes"
private val COL_GROUPS = "groups"
private val GROUPS_TABLE_NAME = "groups"
private val COL_TITLE = "title"
private val FIRST_CONTACT_ID = 1000000
private val mDb = writableDatabase
companion object {
private const val DB_VERSION = 2
private const val DB_VERSION = 3
const val DB_NAME = "contacts.db"
var dbInstance: DBHelper? = null
@ -52,10 +58,12 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
override fun onCreate(db: SQLiteDatabase) {
db.execSQL("CREATE TABLE $CONTACTS_TABLE_NAME ($COL_ID INTEGER PRIMARY KEY AUTOINCREMENT, $COL_FIRST_NAME TEXT, $COL_MIDDLE_NAME TEXT, " +
"$COL_SURNAME TEXT, $COL_PHOTO BLOB, $COL_PHONE_NUMBERS TEXT, $COL_EMAILS TEXT, $COL_EVENTS TEXT, $COL_STARRED INTEGER, " +
"$COL_ADDRESSES TEXT, $COL_NOTES TEXT)")
"$COL_ADDRESSES TEXT, $COL_NOTES TEXT, $COL_GROUPS TEXT)")
// start autoincrement ID from FIRST_CONTACT_ID to avoid conflicts
db.execSQL("REPLACE INTO sqlite_sequence (name, seq) VALUES ('$CONTACTS_TABLE_NAME', $FIRST_CONTACT_ID)")
createGroupsTable(db)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
@ -63,15 +71,27 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
db.execSQL("ALTER TABLE $CONTACTS_TABLE_NAME ADD COLUMN $COL_ADDRESSES TEXT DEFAULT ''")
db.execSQL("ALTER TABLE $CONTACTS_TABLE_NAME ADD COLUMN $COL_NOTES TEXT DEFAULT ''")
}
if (oldVersion < 3) {
createGroupsTable(db)
db.execSQL("ALTER TABLE $CONTACTS_TABLE_NAME ADD COLUMN $COL_GROUPS TEXT DEFAULT ''")
}
}
fun insert(contact: Contact): Boolean {
private fun createGroupsTable(db: SQLiteDatabase) {
db.execSQL("CREATE TABLE $GROUPS_TABLE_NAME ($COL_ID INTEGER PRIMARY KEY AUTOINCREMENT, $COL_TITLE TEXT)")
// start autoincrement ID from FIRST_GROUP_ID to avoid conflicts
db.execSQL("REPLACE INTO sqlite_sequence (name, seq) VALUES ('$GROUPS_TABLE_NAME', $FIRST_GROUP_ID)")
}
fun insertContact(contact: Contact): Boolean {
val contactValues = fillContactValues(contact)
val id = mDb.insert(CONTACTS_TABLE_NAME, null, contactValues).toInt()
return id != -1
}
fun update(contact: Contact): Boolean {
fun updateContact(contact: Contact): Boolean {
val contactValues = fillContactValues(contact)
val selection = "$COL_ID = ?"
val selectionArgs = arrayOf(contact.id.toString())
@ -97,6 +117,7 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
put(COL_EVENTS, Gson().toJson(contact.events))
put(COL_STARRED, contact.starred)
put(COL_NOTES, contact.notes)
put(COL_GROUPS, Gson().toJson(contact.groups.map { it.id }))
if (contact.photoUri.isNotEmpty()) {
put(COL_PHOTO, getPhotoByteArray(contact.photoUri))
@ -126,10 +147,87 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
mDb.update(CONTACTS_TABLE_NAME, contactValues, selection, null)
}
fun getContacts(selection: String? = null, selectionArgs: Array<String>? = null): ArrayList<Contact> {
fun insertGroup(group: Group): Group? {
val contactValues = fillGroupValues(group)
val id = mDb.insert(GROUPS_TABLE_NAME, null, contactValues)
return if (id == -1L) {
null
} else {
Group(id, group.title)
}
}
fun renameGroup(group: Group): Boolean {
val contactValues = fillGroupValues(group)
val selection = "$COL_ID = ?"
val selectionArgs = arrayOf(group.id.toString())
return mDb.update(GROUPS_TABLE_NAME, contactValues, selection, selectionArgs) == 1
}
fun deleteGroup(id: Long) = deleteGroups(arrayOf(id.toString()))
fun deleteGroups(ids: Array<String>) {
val args = TextUtils.join(", ", ids)
val selection = "$GROUPS_TABLE_NAME.$COL_ID IN ($args)"
mDb.delete(GROUPS_TABLE_NAME, selection, null)
}
fun getGroups(): ArrayList<Group> {
val groups = ArrayList<Group>()
val projection = arrayOf(COL_ID, COL_TITLE)
val cursor = mDb.query(GROUPS_TABLE_NAME, projection, null, null, null, null, null)
cursor.use {
while (cursor.moveToNext()) {
val id = cursor.getLongValue(COL_ID)
val title = cursor.getStringValue(COL_TITLE)
val group = Group(id, title)
groups.add(group)
}
}
return groups
}
private fun fillGroupValues(group: Group): ContentValues {
return ContentValues().apply {
put(COL_TITLE, group.title)
}
}
fun addContactsToGroup(contacts: ArrayList<Contact>, groupId: Long) {
contacts.forEach {
val currentGroupIds = it.groups.map { it.id } as ArrayList<Long>
currentGroupIds.add(groupId)
updateContactGroups(it, currentGroupIds)
}
}
fun removeContactsFromGroup(contacts: ArrayList<Contact>, groupId: Long) {
contacts.forEach {
val currentGroupIds = it.groups.map { it.id } as ArrayList<Long>
currentGroupIds.remove(groupId)
updateContactGroups(it, currentGroupIds)
}
}
fun updateContactGroups(contact: Contact, groupIds: ArrayList<Long>) {
val contactValues = fillContactGroupValues(groupIds)
val selection = "$COL_ID = ?"
val selectionArgs = arrayOf(contact.id.toString())
mDb.update(CONTACTS_TABLE_NAME, contactValues, selection, selectionArgs)
}
private fun fillContactGroupValues(groupIds: ArrayList<Long>): ContentValues {
return ContentValues().apply {
put(COL_GROUPS, Gson().toJson(groupIds))
}
}
fun getContacts(activity: BaseSimpleActivity, selection: String? = null, selectionArgs: Array<String>? = null): ArrayList<Contact> {
val storedGroups = ContactsHelper(activity).getStoredGroups()
val contacts = ArrayList<Contact>()
val projection = arrayOf(COL_ID, COL_FIRST_NAME, COL_MIDDLE_NAME, COL_SURNAME, COL_PHONE_NUMBERS, COL_EMAILS, COL_EVENTS, COL_STARRED,
COL_PHOTO, COL_ADDRESSES, COL_NOTES)
COL_PHOTO, COL_ADDRESSES, COL_NOTES, COL_GROUPS)
val cursor = mDb.query(CONTACTS_TABLE_NAME, projection, selection, selectionArgs, null, null, null)
cursor.use {
while (cursor.moveToNext()) {
@ -163,7 +261,11 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
val notes = cursor.getStringValue(COL_NOTES)
val starred = cursor.getIntValue(COL_STARRED)
val groups = ArrayList<Group>()
val groupIdsJson = cursor.getStringValue(COL_GROUPS)
val groupIdsToken = object : TypeToken<List<Long>>() {}.type
val groupIds = Gson().fromJson<ArrayList<Long>>(groupIdsJson, groupIdsToken) ?: ArrayList(1)
val groups = storedGroups.filter { groupIds.contains(it.id) } as ArrayList<Group>
val contact = Contact(id, firstName, middleName, surname, "", phoneNumbers, emails, addresses, events, SMT_PRIVATE, starred, id, "", photo, notes, groups)
contacts.add(contact)
@ -172,9 +274,9 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
return contacts
}
fun getContactWithId(id: Int): Contact? {
fun getContactWithId(activity: BaseSimpleActivity, id: Int): Contact? {
val selection = "$COL_ID = ?"
val selectionArgs = arrayOf(id.toString())
return getContacts(selection, selectionArgs).firstOrNull()
return getContacts(activity, selection, selectionArgs).firstOrNull()
}
}

View File

@ -1,14 +0,0 @@
package com.simplemobiletools.contacts.interfaces
import com.simplemobiletools.contacts.activities.MainActivity
import com.simplemobiletools.contacts.models.Contact
interface FragmentInterface {
fun setupFragment(activity: MainActivity)
fun textColorChanged(color: Int)
fun primaryColorChanged(color: Int)
fun refreshContacts(contacts: ArrayList<Contact>)
}

View File

@ -1,7 +1,5 @@
package com.simplemobiletools.contacts.interfaces
interface RefreshContactsListener {
fun refreshContacts(refreshContactsTab: Boolean, refreshFavoritesTab: Boolean)
fun refreshFavorites()
fun refreshContacts(refreshTabsMask: Int)
}

View File

@ -0,0 +1,7 @@
package com.simplemobiletools.contacts.interfaces
import com.simplemobiletools.contacts.models.Contact
interface RemoveFromGroupListener {
fun removeFromGroup(contacts: ArrayList<Contact>)
}

View File

@ -1,3 +1,16 @@
package com.simplemobiletools.contacts.models
data class Group(var id: Long, var title: String)
import com.simplemobiletools.contacts.helpers.FIRST_GROUP_ID
import java.io.Serializable
data class Group(var id: Long, var title: String, var contactsCount: Int = 0) : Serializable {
companion object {
private const val serialVersionUID = -1384515348451345L
}
fun addContact() = contactsCount++
fun getBubbleText() = title
fun isPrivateSecretGroup() = id >= FIRST_GROUP_ID
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 729 B

View File

@ -333,7 +333,7 @@
android:layout_toRightOf="@+id/contact_name_image"
android:orientation="vertical">
<include layout="@layout/item_group"/>
<include layout="@layout/item_edit_group"/>
</LinearLayout>

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/group_contacts_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/group_contacts_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/group_contacts_placeholder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/activity_margin"
android:gravity="center"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:text="@string/no_group_participants"
android:textSize="@dimen/bigger_text_size"
android:visibility="gone"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/group_contacts_placeholder_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/group_contacts_placeholder"
android:layout_centerHorizontal="true"
android:gravity="center"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:text="@string/add_contacts"
android:textSize="@dimen/bigger_text_size"
android:visibility="gone"/>
<com.simplemobiletools.commons.views.MyRecyclerView
android:id="@+id/group_contacts_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:scrollbars="none"
app:layoutManager="com.simplemobiletools.commons.views.MyLinearLayoutManager"/>
<com.simplemobiletools.commons.views.FastScroller
android:id="@+id/group_contacts_fastscroller"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:paddingLeft="@dimen/normal_margin"
android:paddingStart="@dimen/normal_margin">
<include layout="@layout/fastscroller_handle_vertical"/>
</com.simplemobiletools.commons.views.FastScroller>
</RelativeLayout>
<com.simplemobiletools.commons.views.MyFloatingActionButton
android:id="@+id/group_contacts_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/activity_margin"
android:src="@drawable/ic_plus"/>
</android.support.design.widget.CoordinatorLayout>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rename_group_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MyEditText
android:id="@+id/rename_group_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textCapWords"
android:singleLine="true"
android:textCursorDrawable="@null"
android:textSize="@dimen/normal_text_size"/>
</LinearLayout>

View File

@ -5,4 +5,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/fragment_layout"/>
</com.simplemobiletools.contacts.fragments.GroupsFragment>

View File

@ -19,6 +19,7 @@
android:id="@+id/contact_tmb"
android:layout_width="@dimen/normal_icon_size"
android:layout_height="@dimen/normal_icon_size"
android:layout_centerVertical="true"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_person"/>

View File

@ -19,6 +19,7 @@
android:id="@+id/contact_tmb"
android:layout_width="@dimen/normal_icon_size"
android:layout_height="@dimen/normal_icon_size"
android:layout_centerVertical="true"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_person"/>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contact_group_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/contact_group_remove"
android:layout_toStartOf="@+id/contact_group_remove"
android:alpha="0.5"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/normal_margin"
android:paddingLeft="@dimen/small_margin"
android:paddingRight="@dimen/small_margin"
android:paddingTop="@dimen/normal_margin"
android:text="@string/no_groups"
android:textSize="@dimen/bigger_text_size"/>
<ImageView
android:id="@+id/contact_group_remove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/contact_group"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/contact_group"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/medium_margin"
android:layout_marginLeft="@dimen/small_margin"
android:layout_marginRight="@dimen/small_margin"
android:layout_marginTop="@dimen/medium_margin"
android:background="@drawable/button_background"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_minus"
android:visibility="gone"/>
</RelativeLayout>

View File

@ -1,41 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contact_group_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_group"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/group_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/contact_group_remove"
android:layout_toStartOf="@+id/contact_group_remove"
android:alpha="0.5"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/normal_margin"
android:paddingLeft="@dimen/small_margin"
android:paddingRight="@dimen/small_margin"
android:paddingTop="@dimen/normal_margin"
android:text="@string/no_groups"
android:textSize="@dimen/bigger_text_size"/>
android:clickable="true"
android:focusable="true"
android:foreground="@drawable/selector">
<RelativeLayout
android:id="@+id/group_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingRight="@dimen/activity_margin">
<ImageView
android:id="@+id/contact_group_remove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/contact_group"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/contact_group"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/medium_margin"
android:layout_marginLeft="@dimen/small_margin"
android:layout_marginRight="@dimen/small_margin"
android:layout_marginTop="@dimen/medium_margin"
android:background="@drawable/button_background"
android:id="@+id/group_tmb"
android:layout_width="@dimen/normal_icon_size"
android:layout_height="@dimen/normal_icon_size"
android:layout_centerVertical="true"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_minus"
android:visibility="gone"/>
android:src="@drawable/ic_group"/>
</RelativeLayout>
<TextView
android:id="@+id/group_name"
android:layout_width="match_parent"
android:layout_height="@dimen/contact_item_height"
android:layout_toRightOf="@+id/group_tmb"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:textSize="@dimen/big_text_size"
tools:text="Family"/>
</RelativeLayout>
</FrameLayout>

View File

@ -16,6 +16,11 @@
android:icon="@drawable/ic_star_on"
android:title="@string/add_to_favorites"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/cab_add_to_group"
android:icon="@drawable/ic_group_add"
android:title="@string/add_to_group"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/cab_share"
android:icon="@drawable/ic_share"

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/cab_edit"
android:icon="@drawable/ic_rename"
android:title="@string/edit_contact"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/cab_select_all"
android:icon="@drawable/ic_select_all"
android:title="@string/select_all"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/cab_delete"
android:icon="@drawable/ic_delete"
android:title="@string/delete"
app:showAsAction="ifRoom"/>
</menu>

View File

@ -6,8 +6,6 @@
<string name="updating">Aktualisiere…</string>
<string name="phone_storage">Gerätespeicher</string>
<string name="phone_storage_hidden">Gerätespeicher (nicht sichtbar für andere Apps)</string>
<string name="no_groups">No groups</string>
<string name="create_new_group">Create a new group</string>
<string name="new_contact">Neuer Kontakt</string>
<string name="edit_contact">Kontakt bearbeiten</string>
@ -17,6 +15,17 @@
<string name="middle_name">Zweiter Vorname</string>
<string name="surname">Familienname</string>
<!-- Groups -->
<string name="no_groups">No groups</string>
<string name="create_new_group">Create a new group</string>
<string name="remove_from_group">Remove from group</string>
<string name="no_group_participants">This group is empty</string>
<string name="add_contacts">Add contacts</string>
<string name="no_group_created">There are no contact groups on the device</string>
<string name="create_group">Create group</string>
<string name="add_to_group">Add to group</string>
<string name="create_group_under_account">Create group under account</string>
<!-- Photo -->
<string name="take_photo">Foto machen</string>
<string name="choose_photo">Foto auswählen</string>
@ -80,7 +89,7 @@
Beinhaltet keine Werbung oder unnötige Berechtigungen. Sie ist komplett Open Source, alle verwendeten Farben sind anpassbar.
Diese App ist nur eine aus einer größeren Serie von schlichten Apps. Der Rest davon findet sich auf http://www.simplemobiletools.com
Diese App ist nur eine aus einer größeren Serie von schlichten Apps. Der Rest davon findet sich auf https://www.simplemobiletools.com
</string>
<!--

View File

@ -0,0 +1,99 @@
<resources>
<string name="app_name">Απλές Επαφές</string>
<string name="app_launcher_name">Επαφές</string>
<string name="address">Διεύθυνση</string>
<string name="inserting">Εισαγωγή...</string>
<string name="updating">Ενημέρωση...</string>
<string name="phone_storage">Μνήμη τηλεφώνου</string>
<string name="phone_storage_hidden">Μνήμη τηλεφώνου (δεν είναι ορατή από άλλες εφαρμογές)</string>
<string name="new_contact">Νέα επαφή</string>
<string name="edit_contact">Επεξεργασία επαφής</string>
<string name="select_contact">Επιλογή επαφής</string>
<string name="select_contacts">Επιλογή επαφών</string>
<string name="first_name">Όνομα</string>
<string name="middle_name">Μεσαίο όνομα</string>
<string name="surname">Επώνυμο</string>
<!-- Groups -->
<string name="no_groups">Δεν υπάρχουν ομάδες</string>
<string name="create_new_group">Δημιουργία νέας ομάδας</string>
<string name="remove_from_group">Αφαίρεση από ομάδα</string>
<string name="no_group_participants">Η ομάδα είναι άδεια</string>
<string name="add_contacts">Προσθήκη επαφών</string>
<string name="no_group_created">Δεν υπάρχουν ομάδες επαφών στη συσκευή</string>
<string name="create_group">Δημιουργία ομάδας</string>
<string name="add_to_group">Προσθήκη σε ομάδα</string>
<string name="create_group_under_account">Δημιουργία ομάδας κάτω από λογαριασμό</string>
<!-- Photo -->
<string name="take_photo">Λήψη φωτογραφίας</string>
<string name="choose_photo">Επιλογή φωτογραφίας</string>
<string name="remove_photo">Αφαίρεση φωτογραφίας</string>
<!-- Settings -->
<string name="start_name_with_surname">Το όνομα ξεκινά με το επώνυμο</string>
<string name="show_phone_numbers">Εμφάνιση τηλεφωνικών αριθμών στην κύρια οθόνη</string>
<string name="show_contact_thumbnails">Εμφάνιση μικρογραφιών επαφής</string>
<string name="on_contact_click">Στην επιλογή επαφής</string>
<string name="call_contact">Κλήση επαφής</string>
<string name="view_contact">Εμφάνιση λεπτομερειών επαφής</string>
<string name="show_favorites_tab">Εμφάνιση καρτέλας αγαπημένων</string>
<string name="show_groups_tab">Εμφάνιση καρτέλας ομάδων</string>
<!-- Emails -->
<string name="email">Email</string>
<string name="home">Σπίτι</string>
<string name="work">Εργασία</string>
<string name="other">Άλλο</string>
<!-- Phone numbers -->
<string name="number">Αριθμός</string>
<string name="mobile">Κινητό</string>
<string name="main_number">Κύριο</string>
<string name="work_fax">Φαξ εργασίας</string>
<string name="home_fax">Φαξ σπιτιού</string>
<string name="pager">Βομβητής</string>
<string name="no_phone_number_found">Δεν βρέθηκε τηλεφωνικός αριθμός</string>
<!-- Events -->
<string name="birthday">Γενέθλια</string>
<string name="anniversary">Επέτειος</string>
<!-- Favorites -->
<string name="no_favorites">Φαίνεται ότι δεν έχεις προσθέσει αγαπημένες επαφές ακόμα.</string>
<string name="add_favorites">Προσθήκη αγαπημένων</string>
<string name="add_to_favorites">Προσθήκη στα αγαπημένα</string>
<string name="remove_from_favorites">Αφαίρεση από τα αγαπημένα</string>
<!-- Search -->
<string name="search_contacts">Αναζήτηση επαφών</string>
<string name="search_favorites">Αναζήτηση αγαπημένων</string>
<!-- Export / Import -->
<string name="import_contacts">Εισαγωγή επαφών</string>
<string name="export_contacts">Εξαγωγή επαφών</string>
<string name="import_contacts_from_vcf">Εισαγωγή επαφών από .vcf αρχείο</string>
<string name="export_contacts_to_vcf">Εξαγωγή επαφών σε .vcf αρχείο</string>
<string name="target_contact_source">Πηγή επαφής προορισμού</string>
<string name="include_contact_sources">Συμπερίληψη πηγών επαφών</string>
<string name="filename_without_vcf">Όνομα αρχείου (χωρίς .vcf)</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Μια εφαρμογή επαφών για να διαχειρίζεσαι τις επαφές σου χωρίς διαφημίσεις.</string>
<string name="app_long_description">
Μια απλή εφαρμογή για δημιουργία και διαχείριση των επαφών σου από κάθε πηγή. Οι επαφές μπορεί να είναι αποθηκευμένες μόνο στη συσκευή σου, αλλά μπορούν να συγχρονίζονται στο Google, ή σε κάποιο άλλο λογαριασμό. Μπορείς να εμφανίσεις τις αγαπημένες σου επαφές σε ξεχωριστή λίστα.
Μπορείς να τη χρησιμοποιήσεις για τη διαχείριση των email των χρηστών και τα γεγονότα. Έχει τη δυνατότητα ταξινόμησης/φιλτραρίσματος με διάφορες παραμέτρους, προαιρετικά να εμφανίζεται το επώνυμο σαν όνομα.
Δεν περιέχει διαφημίσεις ή περιττές άδειες. Είναι πλήρως ανοικτού κώδικα, παρέχει δυνατότητα προσαρμογής των χρωμάτων.
Αυτή η εφαρμογή είναι ένα μικρό κομμάτι μιας μεγαλύτερης συλλογής εφαρμογών. Μπορείς να βρεις τις υπόλοιπες στο https://www.simplemobiletools.com
</string>
<!--
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>

View File

@ -6,8 +6,6 @@
<string name="updating">Mise à jour…</string>
<string name="phone_storage">Stockage du téléphone</string>
<string name="phone_storage_hidden">Stockage du téléphone (non visible par d\'autres applis)</string>
<string name="no_groups">No groups</string>
<string name="create_new_group">Create a new group</string>
<string name="new_contact">Nouveau contact</string>
<string name="edit_contact">Modifier contact</string>
@ -17,6 +15,17 @@
<string name="middle_name">Nom</string>
<string name="surname">Surnom</string>
<!-- Groups -->
<string name="no_groups">No groups</string>
<string name="create_new_group">Create a new group</string>
<string name="remove_from_group">Remove from group</string>
<string name="no_group_participants">This group is empty</string>
<string name="add_contacts">Add contacts</string>
<string name="no_group_created">There are no contact groups on the device</string>
<string name="create_group">Create group</string>
<string name="add_to_group">Add to group</string>
<string name="create_group_under_account">Create group under account</string>
<!-- Photo -->
<string name="take_photo">Prendre une photo</string>
<string name="choose_photo">Choisir une photo</string>
@ -80,7 +89,7 @@
Aucune publicité ni de permission inutile. Elle est entièrement open source et vous permet de personnaliser les couleurs.
Cette application fait parti d\'un groupe d\'applications. Vous pouvez trouver le reste des applis sur http://www.simplemobiletools.com
Cette application fait parti d\'un groupe d\'applications. Vous pouvez trouver le reste des applis sur https://www.simplemobiletools.com
</string>
<!--

View File

@ -6,8 +6,6 @@
<string name="updating">수정중…</string>
<string name="phone_storage">Phone storage</string>
<string name="phone_storage_hidden">Phone storage (not visible by other apps)</string>
<string name="no_groups">No groups</string>
<string name="create_new_group">Create a new group</string>
<string name="new_contact">새로운 연락처</string>
<string name="edit_contact">연락처 수정</string>
@ -17,6 +15,17 @@
<string name="middle_name">중간 이름</string>
<string name="surname"></string>
<!-- Groups -->
<string name="no_groups">No groups</string>
<string name="create_new_group">Create a new group</string>
<string name="remove_from_group">Remove from group</string>
<string name="no_group_participants">This group is empty</string>
<string name="add_contacts">Add contacts</string>
<string name="no_group_created">There are no contact groups on the device</string>
<string name="create_group">Create group</string>
<string name="add_to_group">Add to group</string>
<string name="create_group_under_account">Create group under account</string>
<!-- Photo -->
<string name="take_photo">사진 촬영</string>
<string name="choose_photo">사진 선택</string>
@ -80,7 +89,7 @@
광고가 포함되어 있거나, 불필요한 권한을 요청하지 않습니다. 이 앱의 모든 소스는 오픈소스이며, 사용자가 직접 애플리케이션의 컬러를 설정 할 수 있습니다.
이 앱은 다양한 시리즈의 모바일앱 중 하나입니다. 나머지는 http://www.simplemobiletools.com 에서 찾아 보실 수 있습니다.
이 앱은 다양한 시리즈의 모바일앱 중 하나입니다. 나머지는 https://www.simplemobiletools.com 에서 찾아 보실 수 있습니다.
</string>
<!--

View File

@ -0,0 +1,99 @@
<resources>
<string name="app_name">Paprasti kontaktai</string>
<string name="app_launcher_name">Kontaktai</string>
<string name="address">Adresas</string>
<string name="inserting">Įterpiama…</string>
<string name="updating">Atnaujinama…</string>
<string name="phone_storage">Telefono atmintis</string>
<string name="phone_storage_hidden">Telefono atmintis (nematoma kitų programėlių)</string>
<string name="new_contact">Naujas kontaktas</string>
<string name="edit_contact">Redaguoti kontaktą</string>
<string name="select_contact">Pasirinkti kontaktą</string>
<string name="select_contacts">pasirinkti kontaktus</string>
<string name="first_name">Vardas</string>
<string name="middle_name">Antras vardas</string>
<string name="surname">Pavardė</string>
<!-- Groups -->
<string name="no_groups">Nėra grupių</string>
<string name="create_new_group">Sukurti naują grupę</string>
<string name="remove_from_group">Remove from group</string>
<string name="no_group_participants">This group is empty</string>
<string name="add_contacts">Add contacts</string>
<string name="no_group_created">There are no contact groups on the device</string>
<string name="create_group">Create group</string>
<string name="add_to_group">Add to group</string>
<string name="create_group_under_account">Create group under account</string>
<!-- Photo -->
<string name="take_photo">Nufotografuoti</string>
<string name="choose_photo">Pasirinkti nuotrauką</string>
<string name="remove_photo">Pašalinti nuotrauką</string>
<!-- Settings -->
<string name="start_name_with_surname">Pavardė rodoma pirma</string>
<string name="show_phone_numbers">Rodyti telefono numerius pagrindiniame programos ekrane</string>
<string name="show_contact_thumbnails">Rodyti kontaktų miniatiūras</string>
<string name="on_contact_click">Ant kontakto paspaudimo</string>
<string name="call_contact">Skambinti kontaktui</string>
<string name="view_contact">Žiūrėti kontakto detales</string>
<string name="show_favorites_tab">Rodyti mėgiamiausiųjų skirtuką</string>
<string name="show_groups_tab">Rodyti grupių skirtuką</string>
<!-- Emails -->
<string name="email">Elektroninis paštas</string>
<string name="home">Namų</string>
<string name="work">Darbo</string>
<string name="other">Kitas</string>
<!-- Phone numbers -->
<string name="number">Numeris</string>
<string name="mobile">Mobilus</string>
<string name="main_number">Pagrindinis</string>
<string name="work_fax">Darbo faksas</string>
<string name="home_fax">Namų faksas</string>
<string name="pager">Pranešimų gaviklis</string>
<string name="no_phone_number_found">Nerasta telefono numerio</string>
<!-- Events -->
<string name="birthday">Gimtadienis</string>
<string name="anniversary">Sukaktis</string>
<!-- Favorites -->
<string name="no_favorites">Atrodo jog Jūs dar neįvedėte nė vieno mėgiamiausiojo kontakto.</string>
<string name="add_favorites">Pridėti mėgiamiausiuosius</string>
<string name="add_to_favorites">Pridėti į mėgiamiausiuosius</string>
<string name="remove_from_favorites">Pašalinti iš mėgiamiausiųjų</string>
<!-- Search -->
<string name="search_contacts">Ieškoti kontaktų</string>
<string name="search_favorites">Ieškoti mėgiamiausiųjų</string>
<!-- Export / Import -->
<string name="import_contacts">Importuoti kontaktus</string>
<string name="export_contacts">Eksportuoti kontaktus</string>
<string name="import_contacts_from_vcf">Importuoti kontaktus iš .vcf bylos</string>
<string name="export_contacts_to_vcf">Eksportuoti kontaktus į .vcf bylą</string>
<string name="target_contact_source">Tikslinis kontakto šaltinis</string>
<string name="include_contact_sources">Įtraukti kontaktų šaltinius</string>
<string name="filename_without_vcf">Bylos vardas (be .vcf)</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Kontaktų programėlė įrenginio kontaktų tvarkymui, be reklamų.</string>
<string name="app_long_description">
Paprasta programėlė įrenginio kontaktų kūrimui ir tvarkymui iš įvairių šaltinių. Kontaktai gali būti saugomi Jūsų įrenginyje, taip pat sinchronizuojami per Google, ar kitas paskyras. Jūs galite matyti mėgiamiausiuosius kontaktus atskirame sąraše.
Jūs taip pat galite naudoti programėlę elektroninių paštų adresų tvarkymui. Programėlė turi galimybę rikiuoti/filtruoti pagal įvairius parametrus, taip pat rodyti pavardę pirma vardo.
Neturi reklamų ar nereikalingų leidimų. Programėlė visiškai atviro kodo, yra galimybė keisti spalvas.
Ši programėle yra vienintelė iš keletos mūsų programėlių. Likusias Jūs galite rasti čia https://www.simplemobiletools.com
</string>
<!--
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>

View File

@ -6,8 +6,6 @@
<string name="updating">A atualizar…</string>
<string name="phone_storage">Armazenamento do telefone</string>
<string name="phone_storage_hidden">Armazenamento do telefone (não visível por outras alicações)</string>
<string name="no_groups">No groups</string>
<string name="create_new_group">Create a new group</string>
<string name="new_contact">Novo contacto</string>
<string name="edit_contact">Editar contacto</string>
@ -17,6 +15,17 @@
<string name="middle_name">Segundo nome</string>
<string name="surname">Apelido</string>
<!-- Groups -->
<string name="no_groups">No groups</string>
<string name="create_new_group">Create a new group</string>
<string name="remove_from_group">Remove from group</string>
<string name="no_group_participants">This group is empty</string>
<string name="add_contacts">Add contacts</string>
<string name="no_group_created">There are no contact groups on the device</string>
<string name="create_group">Create group</string>
<string name="add_to_group">Add to group</string>
<string name="create_group_under_account">Create group under account</string>
<!-- Photo -->
<string name="take_photo">Tirar foto</string>
<string name="choose_photo">Escolher foto</string>
@ -80,7 +89,7 @@
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
This app is just one piece of a bigger series of apps. You can find the rest of them at http://www.simplemobiletools.com
This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
</string>
<!--

View File

@ -6,8 +6,6 @@
<string name="updating">Обновление…</string>
<string name="phone_storage">Память устройства</string>
<string name="phone_storage_hidden">Память устройства (не видна другим приложениям)</string>
<string name="no_groups">Нет групп</string>
<string name="create_new_group">Создать новую группу</string>
<string name="new_contact">Новый контакт</string>
<string name="edit_contact">Редактировать контакт</string>
@ -17,6 +15,17 @@
<string name="middle_name">Отчество</string>
<string name="surname">Фамилия</string>
<!-- Groups -->
<string name="no_groups">Нет групп</string>
<string name="create_new_group">Создать новую группу</string>
<string name="remove_from_group">Remove from group</string>
<string name="no_group_participants">This group is empty</string>
<string name="add_contacts">Add contacts</string>
<string name="no_group_created">There are no contact groups on the device</string>
<string name="create_group">Create group</string>
<string name="add_to_group">Add to group</string>
<string name="create_group_under_account">Create group under account</string>
<!-- Photo -->
<string name="take_photo">Снять фото</string>
<string name="choose_photo">Выбрать фото</string>
@ -80,7 +89,7 @@
Не содержит рекламы или ненужных разрешений, полностью с открытым исходным кодом. Есть возможность настраивать цвета.
Это приложение — всего лишь частица из большой серии приложений. Вы можете найти остальные на http://www.simplemobiletools.com
Это приложение — всего лишь частица из большой серии приложений. Вы можете найти остальные на https://www.simplemobiletools.com
</string>
<!--

View File

@ -6,8 +6,6 @@
<string name="updating">Upravuje sa…</string>
<string name="phone_storage">Úložisko mobilu</string>
<string name="phone_storage_hidden">Úložisko mobilu (neviditeľné pre ostatné apky)</string>
<string name="no_groups">Žiadne skupiny</string>
<string name="create_new_group">Vytvoriť novú skupinu</string>
<string name="new_contact">Nový kontakt</string>
<string name="edit_contact">Upraviť kontakt</string>
@ -17,6 +15,17 @@
<string name="middle_name">Stredné meno</string>
<string name="surname">Priezvisko</string>
<!-- Groups -->
<string name="no_groups">Žiadne skupiny</string>
<string name="create_new_group">Vytvoriť novú skupinu</string>
<string name="remove_from_group">Odstrániť zo skupiny</string>
<string name="no_group_participants">Skupina je prázdna</string>
<string name="add_contacts">Pridať kontakty</string>
<string name="no_group_created">Nemáte v zariadení vytvorené žiadne skupiny kontaktov</string>
<string name="create_group">Vytvoriť skupinu</string>
<string name="add_to_group">Pridať do skupiny</string>
<string name="create_group_under_account">Vytvoriť skupinu pod účet</string>
<!-- Photo -->
<string name="take_photo">Vytvoriť foto</string>
<string name="choose_photo">Zvoliť foto</string>
@ -80,7 +89,7 @@
Neobsahuje žiadne reklamy a nepotrebné oprávnenia. Je opensource, poskytuje možnosť zmeny farieb.
Táto aplikácia je iba jednou zo skupiny aplikácií. Ostatné viete nájsť na http://www.simplemobiletools.com
Táto aplikácia je iba jednou zo skupiny aplikácií. Ostatné viete nájsť na https://www.simplemobiletools.com
</string>
<!--

View File

@ -6,8 +6,6 @@
<string name="updating">Uppdaterar…</string>
<string name="phone_storage">Telefonens lagringsutrymme</string>
<string name="phone_storage_hidden">Telefonens lagringsutrymme (inte synligt för andra appar)</string>
<string name="no_groups">No groups</string>
<string name="create_new_group">Create a new group</string>
<string name="new_contact">Ny kontakt</string>
<string name="edit_contact">Redigera kontakt</string>
@ -17,6 +15,17 @@
<string name="middle_name">Mellannamn</string>
<string name="surname">Efternamn</string>
<!-- Groups -->
<string name="no_groups">No groups</string>
<string name="create_new_group">Create a new group</string>
<string name="remove_from_group">Remove from group</string>
<string name="no_group_participants">This group is empty</string>
<string name="add_contacts">Add contacts</string>
<string name="no_group_created">There are no contact groups on the device</string>
<string name="create_group">Create group</string>
<string name="add_to_group">Add to group</string>
<string name="create_group_under_account">Create group under account</string>
<!-- Photo -->
<string name="take_photo">Ta foto</string>
<string name="choose_photo">Välj foto</string>
@ -80,7 +89,7 @@
Innehåller ingen reklam eller onödiga behörigheter. Den har helt öppen källkod och anpassningsbara färger.
Denna app är bara en del av en större serie appar. Du hittar resten av dem på http://www.simplemobiletools.com
Denna app är bara en del av en större serie appar. Du hittar resten av dem på https://www.simplemobiletools.com
</string>
<!--

View File

@ -6,8 +6,6 @@
<string name="updating">更新中…</string>
<string name="phone_storage">手機空間</string>
<string name="phone_storage_hidden">手機空間 (其他程式不可見)</string>
<string name="no_groups">沒有群組</string>
<string name="create_new_group">Create a new group</string>
<string name="new_contact">新聯絡人</string>
<string name="edit_contact">編輯聯絡人</string>
@ -17,6 +15,17 @@
<string name="middle_name">中間名</string>
<string name="surname">姓氏</string>
<!-- Groups -->
<string name="no_groups">沒有群組</string>
<string name="create_new_group">建立一個新群組</string>
<string name="remove_from_group">從群組內移除</string>
<string name="no_group_participants">這群組是空白的</string>
<string name="add_contacts">添加聯絡人</string>
<string name="no_group_created">裝置內沒有聯絡人群組</string>
<string name="create_group">建立群組</string>
<string name="add_to_group">添加到群組</string>
<string name="create_group_under_account">Create group under account</string>
<!-- Photo -->
<string name="take_photo">拍照</string>
<string name="choose_photo">選擇相片</string>
@ -80,7 +89,7 @@
不包含廣告及非必要的權限,而且完全開放原始碼,並提供自訂顏色。
這程式只是一系列眾多應用程式的其中一項,你可以在這發現更多 http://www.simplemobiletools.com
這程式只是一系列眾多應用程式的其中一項,你可以在這發現更多 https://www.simplemobiletools.com
</string>
<!--

View File

@ -5,4 +5,6 @@
<string name="release_11">Added Address and Notes fields</string>
<string name="release_10">Allow storing contacts in a local database, hidden from other apps</string>
<string name="groups_placeholder">%1$s (%2$s)</string>
</resources>

View File

@ -6,8 +6,6 @@
<string name="updating">Updating…</string>
<string name="phone_storage">Phone storage</string>
<string name="phone_storage_hidden">Phone storage (not visible by other apps)</string>
<string name="no_groups">No groups</string>
<string name="create_new_group">Create a new group</string>
<string name="new_contact">New contact</string>
<string name="edit_contact">Edit contact</string>
@ -17,6 +15,17 @@
<string name="middle_name">Middle name</string>
<string name="surname">Surname</string>
<!-- Groups -->
<string name="no_groups">No groups</string>
<string name="create_new_group">Create a new group</string>
<string name="remove_from_group">Remove from group</string>
<string name="no_group_participants">This group is empty</string>
<string name="add_contacts">Add contacts</string>
<string name="no_group_created">There are no contact groups on the device</string>
<string name="create_group">Create group</string>
<string name="add_to_group">Add to group</string>
<string name="create_group_under_account">Create group under account</string>
<!-- Photo -->
<string name="take_photo">Take photo</string>
<string name="choose_photo">Choose photo</string>
@ -80,7 +89,7 @@
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
This app is just one piece of a bigger series of apps. You can find the rest of them at http://www.simplemobiletools.com
This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
</string>
<!--

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
<root-path name="external_files" path="/storage/" />
<cache-path name="cache_files" path="my_cache/" />
<cache-path name="shared_contacts" path="contacts/"/>
<root-path name="external_files" path="/" />
</paths>

View File

@ -4,4 +4,4 @@ You can use it for managing user emails and events too. It has the ability to so
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
This app is just one piece of a bigger series of apps. You can find the rest of them at http://www.simplemobiletools.com
This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 90 KiB