diff --git a/app/build.gradle b/app/build.gradle index 16c50719..c6cb783b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -45,7 +45,7 @@ ext { } dependencies { - implementation 'com.simplemobiletools:commons:3.14.12' + implementation 'com.simplemobiletools:commons:3.16.11' 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' diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/EditContactActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/EditContactActivity.kt index 38b30572..e5455c46 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/EditContactActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/EditContactActivity.kt @@ -20,6 +20,7 @@ import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_CONTACTS import com.simplemobiletools.commons.models.RadioItem import com.simplemobiletools.contacts.R +import com.simplemobiletools.contacts.dialogs.SelectGroupsDialog import com.simplemobiletools.contacts.extensions.* import com.simplemobiletools.contacts.helpers.* import com.simplemobiletools.contacts.models.* @@ -28,15 +29,16 @@ 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_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.* class EditContactActivity : ContactActivity() { - val DEFAULT_EMAIL_TYPE = CommonDataKinds.Email.TYPE_HOME - val DEFAULT_PHONE_NUMBER_TYPE = CommonDataKinds.Phone.TYPE_MOBILE - val DEFAULT_ADDRESS_TYPE = CommonDataKinds.StructuredPostal.TYPE_HOME - val DEFAULT_EVENT_TYPE = CommonDataKinds.Event.TYPE_BIRTHDAY + private val DEFAULT_EMAIL_TYPE = CommonDataKinds.Email.TYPE_HOME + private val DEFAULT_PHONE_NUMBER_TYPE = CommonDataKinds.Phone.TYPE_MOBILE + private val DEFAULT_ADDRESS_TYPE = CommonDataKinds.StructuredPostal.TYPE_HOME + private val DEFAULT_EVENT_TYPE = CommonDataKinds.Event.TYPE_BIRTHDAY private val INTENT_TAKE_PHOTO = 1 private val INTENT_CHOOSE_PHOTO = 2 @@ -166,6 +168,7 @@ class EditContactActivity : ContactActivity() { contact_event_image.applyColorFilter(textColor) contact_notes_image.applyColorFilter(textColor) contact_source_image.applyColorFilter(textColor) + contact_groups_image.applyColorFilter(textColor) val adjustedPrimaryColor = getAdjustedPrimaryColor() contact_number_add_new.applyColorFilter(adjustedPrimaryColor) @@ -176,6 +179,8 @@ class EditContactActivity : ContactActivity() { contact_address_add_new.background.applyColorFilter(textColor) contact_event_add_new.applyColorFilter(adjustedPrimaryColor) contact_event_add_new.background.applyColorFilter(textColor) + contact_groups_add_new.applyColorFilter(adjustedPrimaryColor) + contact_groups_add_new.background.applyColorFilter(textColor) contact_toggle_favorite.setOnClickListener { toggleFavorite() } contact_photo.setOnClickListener { trySetPhoto() } @@ -186,6 +191,7 @@ class EditContactActivity : ContactActivity() { contact_email_add_new.setOnClickListener { addNewEmailField() } contact_address_add_new.setOnClickListener { addNewAddressField() } contact_event_add_new.setOnClickListener { addNewEventField() } + contact_groups_add_new.setOnClickListener { showSelectGroupsDialog() } contact_toggle_favorite.apply { setImageDrawable(getStarDrawable(contact!!.starred == 1)) @@ -238,6 +244,7 @@ class EditContactActivity : ContactActivity() { setupAddresses() setupNotes() setupEvents() + setupGroups() } private fun setupPhoneNumbers() { @@ -318,10 +325,60 @@ class EditContactActivity : ContactActivity() { } } + private fun setupGroups() { + contact_groups_holder.removeAllViews() + val groups = contact!!.groups + 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) + contact_groups_holder.addView(groupHolder) + } + + (groupHolder as ViewGroup).apply { + contact_group.apply { + text = group.title + setTextColor(config.textColor) + tag = group.id + alpha = 1f + } + + setOnClickListener { + showSelectGroupsDialog() + } + + contact_group_remove.apply { + beVisible() + applyColorFilter(getAdjustedPrimaryColor()) + background.applyColorFilter(config.textColor) + setOnClickListener { + removeGroup(group.id) + } + } + } + } + + if (groups.isEmpty()) { + layoutInflater.inflate(R.layout.item_group, contact_groups_holder, false).apply { + contact_group.apply { + alpha = 0.5f + text = getString(R.string.no_groups) + setTextColor(config.textColor) + } + + contact_groups_holder.addView(this) + contact_group_remove.beGone() + setOnClickListener { + showSelectGroupsDialog() + } + } + } + } + private fun setupNewContact() { window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE) supportActionBar?.title = resources.getString(R.string.new_contact) - contact = Contact(0, "", "", "", "", ArrayList(), ArrayList(), ArrayList(), ArrayList(), config.lastUsedContactSource, 0, 0, "", null, "") + contact = Contact(0, "", "", "", "", ArrayList(), ArrayList(), ArrayList(), ArrayList(), config.lastUsedContactSource, 0, 0, "", null, "", ArrayList()) contact_source.text = getPublicContactSource(contact!!.source) contact_source.setOnClickListener { showContactSourcePicker(contact!!.source) { @@ -359,6 +416,13 @@ class EditContactActivity : ContactActivity() { setupEventTypePicker(this) } } + + if (contact!!.groups.isEmpty()) { + val groupsHolder = contact_groups_holder.getChildAt(0) + (groupsHolder as? ViewGroup)?.contact_group?.apply { + setupGroupsPicker(this) + } + } } private fun setupPhoneNumberTypePicker(numberTypeField: TextView, type: Int = DEFAULT_PHONE_NUMBER_TYPE) { @@ -422,6 +486,16 @@ class EditContactActivity : ContactActivity() { } } + private fun setupGroupsPicker(groupTitleField: TextView, group: Group? = null) { + groupTitleField.apply { + text = group?.title ?: getString(R.string.no_groups) + alpha = if (group == null) 0.5f else 1f + setOnClickListener { + showSelectGroupsDialog() + } + } + } + private fun resetContactEvent(contactEvent: TextView, removeContactEventButton: ImageView) { contactEvent.apply { text = getString(R.string.unknown) @@ -431,6 +505,11 @@ class EditContactActivity : ContactActivity() { removeContactEventButton.beGone() } + private fun removeGroup(id: Long) { + contact!!.groups = contact!!.groups.filter { it.id != id } as ArrayList + setupGroups() + } + private fun showNumberTypePicker(numberTypeField: TextView) { val items = arrayListOf( RadioItem(CommonDataKinds.Phone.TYPE_MOBILE, getString(R.string.mobile)), @@ -489,8 +568,15 @@ class EditContactActivity : ContactActivity() { } } + private fun showSelectGroupsDialog() { + SelectGroupsDialog(this@EditContactActivity, contact!!.groups) { + contact!!.groups = it + setupGroups() + } + } + private fun saveContact() { - if (isSaving) { + if (isSaving || contact == null) { return } diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/MainActivity.kt index 0dee0381..27a0cadd 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/MainActivity.kt @@ -26,6 +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.interfaces.RefreshContactsListener @@ -33,6 +34,7 @@ import com.simplemobiletools.contacts.models.Contact import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.fragment_contacts.* import kotlinx.android.synthetic.main.fragment_favorites.* +import kotlinx.android.synthetic.main.fragment_groups.* import java.io.FileOutputStream class MainActivity : SimpleActivity(), RefreshContactsListener { @@ -91,15 +93,17 @@ class MainActivity : SimpleActivity(), RefreshContactsListener { val configShowContactThumbnails = config.showContactThumbnails if (storedShowContactThumbnails != configShowContactThumbnails) { - contacts_fragment.showContactThumbnailsChanged(configShowContactThumbnails) - favorites_fragment.showContactThumbnailsChanged(configShowContactThumbnails) + contacts_fragment?.showContactThumbnailsChanged(configShowContactThumbnails) + favorites_fragment?.showContactThumbnailsChanged(configShowContactThumbnails) } val configTextColor = config.textColor if (storedTextColor != configTextColor) { - main_tabs_holder.getTabAt(getOtherViewPagerItem(viewpager.currentItem))?.icon?.applyColorFilter(configTextColor) - contacts_fragment.textColorChanged(configTextColor) - favorites_fragment.textColorChanged(configTextColor) + getInactiveTabIndexes(viewpager.currentItem).forEach { + main_tabs_holder.getTabAt(it)?.icon?.applyColorFilter(configTextColor) + } + contacts_fragment?.textColorChanged(configTextColor) + favorites_fragment?.textColorChanged(configTextColor) } val configBackgroundColor = config.backgroundColor @@ -111,14 +115,14 @@ class MainActivity : SimpleActivity(), RefreshContactsListener { if (storedPrimaryColor != configPrimaryColor) { main_tabs_holder.setSelectedTabIndicatorColor(getAdjustedPrimaryColor()) main_tabs_holder.getTabAt(viewpager.currentItem)?.icon?.applyColorFilter(getAdjustedPrimaryColor()) - contacts_fragment.primaryColorChanged() - favorites_fragment.primaryColorChanged() + contacts_fragment?.primaryColorChanged(configPrimaryColor) + favorites_fragment?.primaryColorChanged(configPrimaryColor) } val configStartNameWithSurname = config.startNameWithSurname if (storedStartNameWithSurname != configStartNameWithSurname) { - contacts_fragment.startNameWithSurnameChanged(configStartNameWithSurname) - favorites_fragment.startNameWithSurnameChanged(configStartNameWithSurname) + contacts_fragment?.startNameWithSurnameChanged(configStartNameWithSurname) + favorites_fragment?.startNameWithSurnameChanged(configStartNameWithSurname) } if (!isFirstResume) { @@ -126,10 +130,9 @@ class MainActivity : SimpleActivity(), RefreshContactsListener { initFragments() } - contacts_fragment?.initContacts() contacts_fragment?.onActivityResume() - favorites_fragment?.initContacts() favorites_fragment?.onActivityResume() + refreshContacts(true, true) } if (hasPermission(PERMISSION_WRITE_CONTACTS)) { @@ -190,7 +193,7 @@ class MainActivity : SimpleActivity(), RefreshContactsListener { override fun onQueryTextChange(newText: String): Boolean { if (isSearchOpen) { - getCurrentFragment()?.onSearchQueryChanged(newText) + (getCurrentFragment() as? MyViewPagerFragment)?.onSearchQueryChanged(newText) } return true } @@ -199,20 +202,24 @@ class MainActivity : SimpleActivity(), RefreshContactsListener { MenuItemCompat.setOnActionExpandListener(searchMenuItem, object : MenuItemCompat.OnActionExpandListener { override fun onMenuItemActionExpand(item: MenuItem?): Boolean { - getCurrentFragment()?.onSearchOpened() + (getCurrentFragment() as? MyViewPagerFragment)?.onSearchOpened() isSearchOpen = true return true } override fun onMenuItemActionCollapse(item: MenuItem?): Boolean { - getCurrentFragment()?.onSearchClosed() + (getCurrentFragment() as? MyViewPagerFragment)?.onSearchClosed() isSearchOpen = false return true } }) } - private fun getCurrentFragment() = if (viewpager.currentItem == 0) contacts_fragment else favorites_fragment + private fun getCurrentFragment() = when (viewpager.currentItem) { + 0 -> contacts_fragment + 1 -> favorites_fragment + else -> groups_fragment + } private fun setupTabColors() { val lastUsedPage = config.lastUsedViewPagerPage @@ -221,7 +228,10 @@ class MainActivity : SimpleActivity(), RefreshContactsListener { setSelectedTabIndicatorColor(getAdjustedPrimaryColor()) getTabAt(lastUsedPage)?.select() getTabAt(lastUsedPage)?.icon?.applyColorFilter(getAdjustedPrimaryColor()) - getTabAt(getOtherViewPagerItem(lastUsedPage))?.icon?.applyColorFilter(config.textColor) + + getInactiveTabIndexes(lastUsedPage).forEach { + getTabAt(it)?.icon?.applyColorFilter(config.textColor) + } } } @@ -253,14 +263,15 @@ class MainActivity : SimpleActivity(), RefreshContactsListener { } } - private fun getOtherViewPagerItem(used: Int) = if (used == 1) 0 else 1 + private fun getInactiveTabIndexes(activeIndex: Int) = arrayListOf(0, 1, 2).filter { it != activeIndex } private fun initFragments() { - viewpager.adapter = ViewPagerAdapter(this) + refreshContacts(true, true) + viewpager.offscreenPageLimit = 2 viewpager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { override fun onPageScrollStateChanged(state: Int) { if (isSearchOpen) { - getCurrentFragment().onSearchQueryChanged("") + (getCurrentFragment() as? MyViewPagerFragment)?.onSearchQueryChanged("") searchMenuItem?.collapseActionView() } } @@ -294,15 +305,14 @@ class MainActivity : SimpleActivity(), RefreshContactsListener { private fun showSortingDialog() { ChangeSortingDialog(this) { - contacts_fragment?.initContacts() - favorites_fragment?.initContacts() + refreshContacts(true, true) } } fun showFilterDialog() { FilterContactSourcesDialog(this) { contacts_fragment?.forceListRedraw = true - contacts_fragment?.initContacts() + refreshContacts(true, false) } } @@ -324,7 +334,7 @@ class MainActivity : SimpleActivity(), RefreshContactsListener { ImportContactsDialog(this, path) { if (it) { runOnUiThread { - refreshContacts() + refreshContacts(true, true) } } } @@ -383,17 +393,36 @@ class MainActivity : SimpleActivity(), RefreshContactsListener { private fun launchAbout() { val faqItems = arrayListOf(FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons)) - startAboutActivity(R.string.app_name, LICENSE_KOTLIN or LICENSE_MULTISELECT or LICENSE_JODA or LICENSE_GLIDE or LICENSE_GSON or LICENSE_STETHO, + startAboutActivity(R.string.app_name, LICENSE_MULTISELECT or LICENSE_JODA or LICENSE_GLIDE or LICENSE_GSON or LICENSE_STETHO, BuildConfig.VERSION_NAME, faqItems) } - override fun refreshContacts() { - contacts_fragment.initContacts() - favorites_fragment.initContacts() + override fun refreshContacts(refreshContactsTab: Boolean, refreshFavoritesTab: Boolean) { + if (isActivityDestroyed()) { + return + } + + ContactsHelper(this).getContacts { + if (isActivityDestroyed()) { + return@getContacts + } + + if (viewpager.adapter == null) { + viewpager.adapter = ViewPagerAdapter(this, it) + } + + if (refreshContactsTab) { + contacts_fragment?.refreshContacts(it) + } + + if (refreshFavoritesTab) { + favorites_fragment?.refreshContacts(it) + } + } } override fun refreshFavorites() { - favorites_fragment?.initContacts() + refreshContacts(false, true) } private fun checkWhatsNewDialog() { diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/ViewContactActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/ViewContactActivity.kt index f27ef8d8..1bffbd05 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/ViewContactActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/ViewContactActivity.kt @@ -19,6 +19,7 @@ import kotlinx.android.synthetic.main.activity_view_contact.* import kotlinx.android.synthetic.main.item_event.view.* import kotlinx.android.synthetic.main.item_view_address.view.* import kotlinx.android.synthetic.main.item_view_email.view.* +import kotlinx.android.synthetic.main.item_view_group.view.* import kotlinx.android.synthetic.main.item_view_phone_number.view.* class ViewContactActivity : ContactActivity() { @@ -65,6 +66,11 @@ class ViewContactActivity : ContactActivity() { val data = intent.data if (data != null) { val rawId = if (data.path.contains("lookup")) { + val lookupKey = getLookupKeyFromUri(data) + if (lookupKey != null) { + contact = ContactsHelper(this).getContactWithLookupKey(lookupKey) + } + getLookupUriRawId(data) } else { getContactUriRawId(data) @@ -76,7 +82,7 @@ class ViewContactActivity : ContactActivity() { } } - if (contactId != 0) { + if (contactId != 0 && contact == null) { contact = ContactsHelper(this).getContactWithId(contactId, intent.getBooleanExtra(IS_PRIVATE, false)) if (contact == null) { toast(R.string.unknown_error_occurred) @@ -114,6 +120,7 @@ class ViewContactActivity : ContactActivity() { contact_event_image.applyColorFilter(textColor) contact_source_image.applyColorFilter(textColor) contact_notes_image.applyColorFilter(textColor) + contact_groups_image.applyColorFilter(textColor) contact_send_sms.setOnClickListener { trySendSMS() } contact_start_call.setOnClickListener { tryStartCall(contact!!) } @@ -154,6 +161,7 @@ class ViewContactActivity : ContactActivity() { setupAddresses() setupEvents() setupNotes() + setupGroups() } private fun setupPhoneNumbers() { @@ -161,9 +169,14 @@ class ViewContactActivity : ContactActivity() { val phoneNumbers = contact!!.phoneNumbers phoneNumbers.forEach { layoutInflater.inflate(R.layout.item_view_phone_number, contact_numbers_holder, false).apply { + val phoneNumber = it contact_numbers_holder.addView(this) - contact_number.text = it.value - contact_number_type.setText(getPhoneNumberTextId(it.type)) + contact_number.text = phoneNumber.value + contact_number_type.setText(getPhoneNumberTextId(phoneNumber.type)) + + setOnClickListener { + startCallIntent(phoneNumber.value) + } } } @@ -176,9 +189,14 @@ class ViewContactActivity : ContactActivity() { val emails = contact!!.emails emails.forEach { layoutInflater.inflate(R.layout.item_view_email, contact_emails_holder, false).apply { + val email = it contact_emails_holder.addView(this) - contact_email.text = it.value - contact_email_type.setText(getEmailTextId(it.type)) + contact_email.text = email.value + contact_email_type.setText(getEmailTextId(email.type)) + + setOnClickListener { + sendEmailIntent(email.value) + } } } @@ -191,9 +209,14 @@ class ViewContactActivity : ContactActivity() { val addresses = contact!!.addresses addresses.forEach { layoutInflater.inflate(R.layout.item_view_address, contact_addresses_holder, false).apply { + val address = it contact_addresses_holder.addView(this) - contact_address.text = it.value - contact_address_type.setText(getAddressTextId(it.type)) + contact_address.text = address.value + contact_address_type.setText(getAddressTextId(address.type)) + + setOnClickListener { + sendAddressIntent(address.value) + } } } @@ -225,5 +248,20 @@ class ViewContactActivity : ContactActivity() { contact_notes.beVisibleIf(notes.isNotEmpty()) } + private fun setupGroups() { + contact_groups_holder.removeAllViews() + val groups = contact!!.groups + groups.forEach { + layoutInflater.inflate(R.layout.item_view_group, contact_groups_holder, false).apply { + val group = it + contact_groups_holder.addView(this) + contact_group.text = group.title + } + } + + contact_groups_image.beVisibleIf(groups.isNotEmpty()) + contact_groups_holder.beVisibleIf(groups.isNotEmpty()) + } + private fun getStarDrawable(on: Boolean) = resources.getDrawable(if (on) R.drawable.ic_star_on_big else R.drawable.ic_star_off_big) } diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/adapters/ContactsAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/adapters/ContactsAdapter.kt index 7b704bb6..95a04a55 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/adapters/ContactsAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/adapters/ContactsAdapter.kt @@ -130,7 +130,7 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList) : PagerAdapter() { override fun instantiateItem(container: ViewGroup, position: Int): Any { - val layout = if (position == 0) R.layout.fragment_contacts else R.layout.fragment_favorites + val layout = getFragment(position) val view = activity.layoutInflater.inflate(layout, container, false) container.addView(view) - (view as MyViewPagerFragment).setupFragment(activity) + (view as FragmentInterface).apply { + setupFragment(activity) + refreshContacts(contacts) + } return view } @@ -21,6 +25,12 @@ class ViewPagerAdapter(val activity: MainActivity) : PagerAdapter() { container.removeView(item as View) } - override fun getCount() = 2 + override fun getCount() = 3 override fun isViewFromObject(view: View, item: Any) = view == item + + private fun getFragment(position: Int) = when (position) { + 0 -> R.layout.fragment_contacts + 1 -> R.layout.fragment_favorites + else -> R.layout.fragment_groups + } } diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/dialogs/CreateNewGroupDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/dialogs/CreateNewGroupDialog.kt new file mode 100644 index 00000000..5897b671 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/dialogs/CreateNewGroupDialog.kt @@ -0,0 +1,41 @@ +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.extensions.setupDialogStuff +import com.simplemobiletools.commons.extensions.showKeyboard +import com.simplemobiletools.commons.extensions.toast +import com.simplemobiletools.commons.extensions.value +import com.simplemobiletools.contacts.R +import com.simplemobiletools.contacts.helpers.ContactsHelper +import com.simplemobiletools.contacts.models.Group +import kotlinx.android.synthetic.main.dialog_create_new_group.view.* + +class CreateNewGroupDialog(val activity: BaseSimpleActivity, val callback: (newGroup: Group) -> Unit) { + init { + val view = activity.layoutInflater.inflate(R.layout.dialog_create_new_group, null) + + AlertDialog.Builder(activity) + .setPositiveButton(R.string.ok, null) + .setNegativeButton(R.string.cancel, null) + .create().apply { + activity.setupDialogStuff(view, this, R.string.create_new_group) { + showKeyboard(view.group_name) + getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(View.OnClickListener { + val name = view.group_name.value + if (name.isEmpty()) { + activity.toast(R.string.empty_name) + return@OnClickListener + } + + val newGroup = ContactsHelper(activity).createNewGroup(name) + if (newGroup != null) { + callback(newGroup) + } + dismiss() + }) + } + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/dialogs/SelectGroupsDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/dialogs/SelectGroupsDialog.kt new file mode 100644 index 00000000..66d1344e --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/dialogs/SelectGroupsDialog.kt @@ -0,0 +1,85 @@ +package com.simplemobiletools.contacts.dialogs + +import android.support.v7.app.AlertDialog +import android.view.ViewGroup +import com.simplemobiletools.commons.extensions.setupDialogStuff +import com.simplemobiletools.commons.views.MyAppCompatCheckbox +import com.simplemobiletools.contacts.R +import com.simplemobiletools.contacts.activities.SimpleActivity +import com.simplemobiletools.contacts.extensions.config +import com.simplemobiletools.contacts.helpers.ContactsHelper +import com.simplemobiletools.contacts.models.Group +import kotlinx.android.synthetic.main.dialog_select_groups.view.* +import kotlinx.android.synthetic.main.item_checkbox.view.* +import kotlinx.android.synthetic.main.item_textview.view.* +import java.util.* + +class SelectGroupsDialog(val activity: SimpleActivity, val selectedGroups: ArrayList, val callback: (newGroups: ArrayList) -> Unit) { + private val view = activity.layoutInflater.inflate(R.layout.dialog_select_groups, null) as ViewGroup + private val checkboxes = ArrayList() + private val groups = ContactsHelper(activity).getStoredGroups() + private val config = activity.config + private val dialog: AlertDialog? + + init { + groups.forEach { + addGroupCheckbox(it) + } + + addCreateNewGroupButton() + + dialog = AlertDialog.Builder(activity) + .setPositiveButton(R.string.ok, { dialog, which -> dialogConfirmed() }) + .setNegativeButton(R.string.cancel, null) + .create().apply { + activity.setupDialogStuff(view, this) + } + } + + private fun addGroupCheckbox(group: Group) { + activity.layoutInflater.inflate(R.layout.item_checkbox, null, false).apply { + checkboxes.add(item_checkbox) + item_checkbox_holder.setOnClickListener { + item_checkbox.toggle() + } + item_checkbox.apply { + isChecked = selectedGroups.contains(group) + text = group.title + tag = group.id + setColors(config.textColor, config.primaryColor, config.backgroundColor) + } + view.dialog_groups_holder.addView(this) + } + } + + private fun addCreateNewGroupButton() { + val newGroup = Group(0, activity.getString(R.string.create_new_group)) + activity.layoutInflater.inflate(R.layout.item_textview, null, false).item_textview.apply { + text = newGroup.title + tag = newGroup.id + setTextColor(config.textColor) + view.dialog_groups_holder.addView(this) + setOnClickListener { + CreateNewGroupDialog(activity) { + selectedGroups.add(it) + groups.add(it) + view.dialog_groups_holder.removeViewAt(view.dialog_groups_holder.childCount - 1) + addGroupCheckbox(it) + addCreateNewGroupButton() + } + } + } + } + + private fun dialogConfirmed() { + val selectedGroups = ArrayList() + checkboxes.filter { it.isChecked }.forEach { + val groupId = it.tag as Long + groups.firstOrNull { it.id == groupId }?.apply { + selectedGroups.add(this) + } + } + + callback(selectedGroups) + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/extensions/Context.kt index 19d18f9b..8731909a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/extensions/Context.kt @@ -9,8 +9,8 @@ import android.os.Build import android.provider.ContactsContract import android.support.v4.content.FileProvider import com.simplemobiletools.commons.extensions.getIntValue -import com.simplemobiletools.commons.extensions.isLollipopPlus import com.simplemobiletools.commons.extensions.toast +import com.simplemobiletools.commons.helpers.isLollipopPlus import com.simplemobiletools.contacts.BuildConfig import com.simplemobiletools.contacts.R import com.simplemobiletools.contacts.activities.EditContactActivity @@ -61,6 +61,18 @@ fun Context.sendSMSIntent(recipient: String) { } } +fun Context.sendAddressIntent(address: String) { + val location = Uri.encode(address) + val uri = Uri.parse("geo:0,0?q=$location") + + val intent = Intent(Intent.ACTION_VIEW, uri) + if (intent.resolveActivity(packageManager) != null) { + startActivity(intent) + } else { + toast(R.string.no_app_found) + } +} + @TargetApi(Build.VERSION_CODES.LOLLIPOP) fun Context.getLookupUriRawId(dataUri: Uri): Int { val lookupKey = getLookupKeyFromUri(dataUri) diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/FavoritesFragment.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/FavoritesFragment.kt index 3e42d3aa..104eaa41 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/FavoritesFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/FavoritesFragment.kt @@ -16,7 +16,7 @@ class FavoritesFragment(context: Context, attributeSet: AttributeSet) : MyViewPa private fun showAddFavoritesDialog() { AddFavoritesDialog(activity!!) { - initContacts() + activity!!.refreshContacts(false, true) } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/GroupsFragment.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/GroupsFragment.kt new file mode 100644 index 00000000..e52fe7e7 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/GroupsFragment.kt @@ -0,0 +1,22 @@ +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 + +class GroupsFragment(context: Context, attributeSet: AttributeSet) : CoordinatorLayout(context, attributeSet), FragmentInterface { + override fun setupFragment(activity: MainActivity) { + } + + override fun textColorChanged(color: Int) { + } + + override fun primaryColorChanged(color: Int) { + } + + override fun refreshContacts(contacts: ArrayList) { + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/MyViewPagerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/MyViewPagerFragment.kt index 7c767a66..e273e94c 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/MyViewPagerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/MyViewPagerFragment.kt @@ -15,11 +15,15 @@ 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.* +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.models.Contact import kotlinx.android.synthetic.main.fragment_layout.view.* -abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet) : CoordinatorLayout(context, attributeSet) { +abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet) : CoordinatorLayout(context, attributeSet), FragmentInterface { protected var activity: MainActivity? = null private var lastHashCode = 0 private var contactsIgnoringSearch = ArrayList() @@ -27,7 +31,7 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet) var forceListRedraw = false - fun setupFragment(activity: MainActivity) { + override fun setupFragment(activity: MainActivity) { config = activity.config if (this.activity == null) { this.activity = activity @@ -47,18 +51,16 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet) fragment_placeholder_2.text = activity.getString(R.string.add_favorites) } } - - initContacts() } - fun textColorChanged(color: Int) { + override fun textColorChanged(color: Int) { (fragment_list.adapter as ContactsAdapter).apply { updateTextColor(color) initDrawables() } } - fun primaryColorChanged() { + override fun primaryColorChanged(color: Int) { fragment_fastscroller.updatePrimaryColor() fragment_fastscroller.updateBubblePrimaryColor() } @@ -66,45 +68,34 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet) fun startNameWithSurnameChanged(startNameWithSurname: Boolean) { (fragment_list.adapter as ContactsAdapter).apply { config.sorting = if (startNameWithSurname) SORT_BY_SURNAME else SORT_BY_FIRST_NAME - initContacts() + this@MyViewPagerFragment.activity!!.refreshContacts(true, true) } } - fun initContacts() { - if (activity == null || activity!!.isActivityDestroyed()) { - return + override fun refreshContacts(contacts: ArrayList) { + if (config.lastUsedContactSource.isEmpty()) { + val grouped = contacts.groupBy { it.source }.maxWith(compareBy { it.value.size }) + config.lastUsedContactSource = grouped?.key ?: "" } - ContactsHelper(activity!!).getContacts { - var contacts = it - if (activity == null || activity!!.isActivityDestroyed()) { - return@getContacts - } - - if (config.lastUsedContactSource.isEmpty()) { - val grouped = contacts.groupBy { it.source }.maxWith(compareBy { it.value.size }) - config.lastUsedContactSource = grouped?.key ?: "" - } - - contacts = if (this is FavoritesFragment) { - contacts.filter { it.starred == 1 } as ArrayList + val filtered = if (this is FavoritesFragment) { + contacts.filter { it.starred == 1 } as ArrayList + } else { + val contactSources = config.displayContactSources + if (config.showAllContacts()) { + contacts } else { - val contactSources = config.displayContactSources - if (config.showAllContacts()) { - contacts - } else { - contacts.filter { contactSources.contains(it.source) } as ArrayList - } + contacts.filter { contactSources.contains(it.source) } as ArrayList } + } - Contact.sorting = config.sorting - contacts.sort() + Contact.sorting = config.sorting + filtered.sort() - if (contacts.hashCode() != lastHashCode) { - lastHashCode = contacts.hashCode() - activity!!.runOnUiThread { - setupContacts(contacts) - } + if (filtered.hashCode() != lastHashCode) { + lastHashCode = filtered.hashCode() + activity?.runOnUiThread { + setupContacts(filtered) } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/ContactsHelper.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/ContactsHelper.kt index c1423506..ee8c0980 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/ContactsHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/ContactsHelper.kt @@ -13,10 +13,7 @@ import android.provider.ContactsContract.CommonDataKinds.Note import android.provider.MediaStore import android.util.SparseArray import com.simplemobiletools.commons.activities.BaseSimpleActivity -import com.simplemobiletools.commons.extensions.getIntValue -import com.simplemobiletools.commons.extensions.getStringValue -import com.simplemobiletools.commons.extensions.showErrorToast -import com.simplemobiletools.commons.extensions.toast +import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.SORT_BY_FIRST_NAME import com.simplemobiletools.commons.helpers.SORT_BY_MIDDLE_NAME import com.simplemobiletools.commons.helpers.SORT_BY_SURNAME @@ -57,8 +54,9 @@ class ContactsHelper(val activity: BaseSimpleActivity) { val contactId = cursor.getIntValue(ContactsContract.Data.CONTACT_ID) val thumbnailUri = cursor.getStringValue(CommonDataKinds.StructuredName.PHOTO_THUMBNAIL_URI) ?: "" val notes = "" + val groups = ArrayList() val contact = Contact(id, firstName, middleName, surname, photoUri, number, emails, addresses, events, accountName, - starred, contactId, thumbnailUri, null, notes) + starred, contactId, thumbnailUri, null, notes, groups) contacts.put(id, contact) } while (cursor.moveToNext()) } @@ -103,6 +101,13 @@ 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 { contacts.put(it.id, it) } @@ -111,7 +116,9 @@ class ContactsHelper(val activity: BaseSimpleActivity) { var resultContacts = ArrayList(contactsSize) (0 until contactsSize).mapTo(resultContacts) { contacts.valueAt(it) } resultContacts = resultContacts.distinctBy { it.contactId } as ArrayList - callback(resultContacts) + activity.runOnUiThread { + callback(resultContacts) + } }.start() } @@ -305,6 +312,109 @@ class ContactsHelper(val activity: BaseSimpleActivity) { return notes } + private fun getContactGroups(storedGroups: ArrayList, contactId: Int? = null): SparseArray> { + val groups = SparseArray>() + val uri = ContactsContract.Data.CONTENT_URI + val projection = arrayOf( + ContactsContract.Data.CONTACT_ID, + ContactsContract.Data.DATA1 + ) + + var selection = "${ContactsContract.Data.MIMETYPE} = ?" + var selectionArgs = arrayOf(CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE) + + if (contactId != null) { + selection += " AND ${ContactsContract.Data.CONTACT_ID} = ?" + selectionArgs = arrayOf(CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE, contactId.toString()) + } + + var cursor: Cursor? = null + try { + cursor = activity.contentResolver.query(uri, projection, selection, selectionArgs, null) + if (cursor?.moveToFirst() == true) { + do { + val id = cursor.getIntValue(ContactsContract.Data.CONTACT_ID) + val newRowId = cursor.getLongValue(ContactsContract.Data.DATA1) + + 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()) + } + } catch (e: Exception) { + activity.showErrorToast(e) + } finally { + cursor?.close() + } + + return groups + } + + fun getStoredGroups(): ArrayList { + val groups = ArrayList() + val uri = ContactsContract.Groups.CONTENT_URI + val projection = arrayOf( + ContactsContract.Groups._ID, + ContactsContract.Groups.TITLE + ) + + val selection = "${ContactsContract.Groups.AUTO_ADD} = ? AND ${ContactsContract.Groups.FAVORITES} = ? AND ${ContactsContract.Groups.SYSTEM_ID} IS NULL" + val selectionArgs = arrayOf("0", "0") + + var cursor: Cursor? = null + try { + cursor = activity.contentResolver.query(uri, projection, selection, selectionArgs, null) + if (cursor?.moveToFirst() == true) { + do { + val id = cursor.getLongValue(ContactsContract.Groups._ID) + val title = cursor.getStringValue(ContactsContract.Groups.TITLE) + groups.add(Group(id, title)) + } while (cursor.moveToNext()) + } + } catch (e: Exception) { + activity.showErrorToast(e) + } finally { + cursor?.close() + } + + return groups + } + + fun createNewGroup(title: String): Group? { + try { + val operations = ArrayList() + ContentProviderOperation.newInsert(ContactsContract.Groups.CONTENT_URI).apply { + withValue(ContactsContract.Groups.TITLE, title) + operations.add(build()) + } + + val results = activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations) + val rawId = ContentUris.parseId(results[0].uri) + return Group(rawId, title) + } catch (e: Exception) { + activity.showErrorToast(e) + } + return null + } + + fun deleteGroup(id: Long) { + try { + val operations = ArrayList() + val uri = ContentUris.withAppendedId(ContactsContract.Groups.CONTENT_URI, id).buildUpon() + .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") + .build() + + operations.add(ContentProviderOperation.newDelete(uri).build()) + activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations) + } catch (e: Exception) { + activity.showErrorToast(e) + } + } + fun getContactWithId(id: Int, isLocalPrivate: Boolean): Contact? { if (id == 0) { return null @@ -312,14 +422,26 @@ class ContactsHelper(val activity: BaseSimpleActivity) { return activity.dbHelper.getContactWithId(id) } - val uri = ContactsContract.Data.CONTENT_URI - val projection = getContactProjection() val selection = "${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.Data.RAW_CONTACT_ID} = ?" val selectionArgs = arrayOf(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id.toString()) + return parseContactCursor(selection, selectionArgs) + } + + fun getContactWithLookupKey(key: String): Contact? { + val selection = "${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.Data.LOOKUP_KEY} = ?" + val selectionArgs = arrayOf(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, key) + return parseContactCursor(selection, selectionArgs) + } + + private fun parseContactCursor(selection: String, selectionArgs: Array): Contact? { + val storedGroups = getStoredGroups() + val uri = ContactsContract.Data.CONTENT_URI + val projection = getContactProjection() var cursor: Cursor? = null try { cursor = activity.contentResolver.query(uri, projection, selection, selectionArgs, null) if (cursor?.moveToFirst() == true) { + val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID) val firstName = cursor.getStringValue(CommonDataKinds.StructuredName.GIVEN_NAME) ?: "" val middleName = cursor.getStringValue(CommonDataKinds.StructuredName.MIDDLE_NAME) ?: "" val surname = cursor.getStringValue(CommonDataKinds.StructuredName.FAMILY_NAME) ?: "" @@ -332,8 +454,10 @@ class ContactsHelper(val activity: BaseSimpleActivity) { val accountName = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME) ?: "" val starred = cursor.getIntValue(CommonDataKinds.StructuredName.STARRED) val contactId = cursor.getIntValue(ContactsContract.Data.CONTACT_ID) + val groups = getContactGroups(storedGroups, contactId)[contactId] ?: ArrayList() val thumbnailUri = cursor.getStringValue(CommonDataKinds.StructuredName.PHOTO_THUMBNAIL_URI) ?: "" - return Contact(id, firstName, middleName, surname, photoUri, number, emails, addresses, events, accountName, starred, contactId, thumbnailUri, null, notes) + return Contact(id, firstName, middleName, surname, photoUri, number, emails, addresses, events, accountName, starred, contactId, + thumbnailUri, null, notes, groups) } } finally { cursor?.close() @@ -539,6 +663,24 @@ class ContactsHelper(val activity: BaseSimpleActivity) { operations.add(build()) } + // delete groups + 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) + withSelection(selection, selectionArgs) + operations.add(build()) + } + + // add groups + contact.groups.forEach { + ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply { + withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id) + withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE) + withValue(CommonDataKinds.GroupMembership.GROUP_ROW_ID, it.id) + operations.add(build()) + } + } + // favorite try { val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, contact.contactId.toString()) @@ -673,6 +815,16 @@ class ContactsHelper(val activity: BaseSimpleActivity) { operations.add(build()) } + // groups + contact.groups.forEach { + ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply { + withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE) + withValue(CommonDataKinds.GroupMembership.GROUP_ROW_ID, it.id) + operations.add(build()) + } + } + // photo (inspired by https://gist.github.com/slightfoot/5985900) var fullSizePhotoData: ByteArray? = null var scaledSizePhotoData: ByteArray? diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/DBHelper.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/DBHelper.kt index 2703ffa8..965c5256 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/DBHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/DBHelper.kt @@ -163,8 +163,9 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont val notes = cursor.getStringValue(COL_NOTES) val starred = cursor.getIntValue(COL_STARRED) + val groups = ArrayList() - val contact = Contact(id, firstName, middleName, surname, "", phoneNumbers, emails, addresses, events, SMT_PRIVATE, starred, id, "", photo, notes) + val contact = Contact(id, firstName, middleName, surname, "", phoneNumbers, emails, addresses, events, SMT_PRIVATE, starred, id, "", photo, notes, groups) contacts.add(contact) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/VcfImporter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/VcfImporter.kt index e644d5eb..602e5160 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/VcfImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/VcfImporter.kt @@ -28,6 +28,7 @@ class VcfImporter(val activity: SimpleActivity) { private var curEmails = ArrayList() private var curEvents = ArrayList() private var curAddresses = ArrayList
() + private var curGroups = ArrayList() private var isGettingPhoto = false private var currentPhotoString = StringBuilder() @@ -234,7 +235,8 @@ class VcfImporter(val activity: SimpleActivity) { } private fun saveContact(source: String) { - val contact = Contact(0, curFirstName, curMiddleName, curSurname, curPhotoUri, curPhoneNumbers, curEmails, curAddresses, curEvents, source, 0, 0, "", null, curNotes) + val contact = Contact(0, curFirstName, curMiddleName, curSurname, curPhotoUri, curPhoneNumbers, curEmails, curAddresses, curEvents, + source, 0, 0, "", null, curNotes, curGroups) if (ContactsHelper(activity).insertContact(contact)) { contactsImported++ } @@ -250,6 +252,7 @@ class VcfImporter(val activity: SimpleActivity) { curEmails = ArrayList() curEvents = ArrayList() curAddresses = ArrayList() + curGroups = ArrayList() isGettingPhoto = false currentPhotoString = StringBuilder() diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/interfaces/FragmentInterface.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/interfaces/FragmentInterface.kt new file mode 100644 index 00000000..0f49e205 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/interfaces/FragmentInterface.kt @@ -0,0 +1,14 @@ +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) +} diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/interfaces/RefreshContactsListener.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/interfaces/RefreshContactsListener.kt index 3d0c687b..7819aa1f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/interfaces/RefreshContactsListener.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/interfaces/RefreshContactsListener.kt @@ -1,7 +1,7 @@ package com.simplemobiletools.contacts.interfaces interface RefreshContactsListener { - fun refreshContacts() + fun refreshContacts(refreshContactsTab: Boolean, refreshFavoritesTab: Boolean) fun refreshFavorites() } diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/models/Contact.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/models/Contact.kt index 498e1a52..488f4a14 100644 --- a/app/src/main/kotlin/com/simplemobiletools/contacts/models/Contact.kt +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/models/Contact.kt @@ -7,7 +7,8 @@ import com.simplemobiletools.commons.helpers.SORT_DESCENDING data class Contact(val id: Int, var firstName: String, var middleName: String, var surname: String, var photoUri: String, var phoneNumbers: ArrayList, var emails: ArrayList, var addresses: ArrayList
, var events: ArrayList, - var source: String, var starred: Int, val contactId: Int, val thumbnailUri: String, var photo: Bitmap?, var notes: String) : Comparable { + var source: String, var starred: Int, val contactId: Int, val thumbnailUri: String, var photo: Bitmap?, var notes: String, + var groups: ArrayList) : Comparable { companion object { var sorting = 0 } diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/models/Group.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/models/Group.kt new file mode 100644 index 00000000..dbd23558 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/contacts/models/Group.kt @@ -0,0 +1,3 @@ +package com.simplemobiletools.contacts.models + +data class Group(var id: Long, var title: String) diff --git a/app/src/main/res/drawable-hdpi/ic_group.png b/app/src/main/res/drawable-hdpi/ic_group.png new file mode 100644 index 00000000..ff698afc Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_group.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_group.png b/app/src/main/res/drawable-xhdpi/ic_group.png new file mode 100644 index 00000000..323981cc Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_group.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_group.png b/app/src/main/res/drawable-xxhdpi/ic_group.png new file mode 100644 index 00000000..6c68435f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_group.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_group.png b/app/src/main/res/drawable-xxxhdpi/ic_group.png new file mode 100644 index 00000000..5676f704 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_group.png differ diff --git a/app/src/main/res/layout/activity_edit_contact.xml b/app/src/main/res/layout/activity_edit_contact.xml index b5c8cc52..66cbe244 100644 --- a/app/src/main/res/layout/activity_edit_contact.xml +++ b/app/src/main/res/layout/activity_edit_contact.xml @@ -313,6 +313,44 @@ android:textCursorDrawable="@null" android:textSize="@dimen/bigger_text_size"/> + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_select_groups.xml b/app/src/main/res/layout/dialog_select_groups.xml new file mode 100644 index 00000000..21a468d9 --- /dev/null +++ b/app/src/main/res/layout/dialog_select_groups.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/app/src/main/res/layout/fragment_groups.xml b/app/src/main/res/layout/fragment_groups.xml new file mode 100644 index 00000000..02751c10 --- /dev/null +++ b/app/src/main/res/layout/fragment_groups.xml @@ -0,0 +1,8 @@ + + + + diff --git a/app/src/main/res/layout/item_checkbox.xml b/app/src/main/res/layout/item_checkbox.xml new file mode 100644 index 00000000..8aa72cae --- /dev/null +++ b/app/src/main/res/layout/item_checkbox.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/layout/item_group.xml b/app/src/main/res/layout/item_group.xml new file mode 100644 index 00000000..5a5620e1 --- /dev/null +++ b/app/src/main/res/layout/item_group.xml @@ -0,0 +1,41 @@ + + + + + + + + diff --git a/app/src/main/res/layout/item_textview.xml b/app/src/main/res/layout/item_textview.xml new file mode 100644 index 00000000..486dfee7 --- /dev/null +++ b/app/src/main/res/layout/item_textview.xml @@ -0,0 +1,11 @@ + + diff --git a/app/src/main/res/layout/item_view_address.xml b/app/src/main/res/layout/item_view_address.xml index fce8cbf1..fa3497b8 100644 --- a/app/src/main/res/layout/item_view_address.xml +++ b/app/src/main/res/layout/item_view_address.xml @@ -4,6 +4,7 @@ android:id="@+id/contact_address_holder" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" android:paddingBottom="@dimen/normal_margin" android:paddingTop="@dimen/normal_margin"> diff --git a/app/src/main/res/layout/item_view_email.xml b/app/src/main/res/layout/item_view_email.xml index 9de6e320..7169d289 100644 --- a/app/src/main/res/layout/item_view_email.xml +++ b/app/src/main/res/layout/item_view_email.xml @@ -4,6 +4,7 @@ android:id="@+id/contact_email_holder" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" android:paddingBottom="@dimen/normal_margin" android:paddingTop="@dimen/normal_margin"> diff --git a/app/src/main/res/layout/item_view_group.xml b/app/src/main/res/layout/item_view_group.xml new file mode 100644 index 00000000..8aed14d6 --- /dev/null +++ b/app/src/main/res/layout/item_view_group.xml @@ -0,0 +1,14 @@ + + diff --git a/app/src/main/res/layout/item_view_phone_number.xml b/app/src/main/res/layout/item_view_phone_number.xml index d37bf04e..d4dea4a8 100644 --- a/app/src/main/res/layout/item_view_phone_number.xml +++ b/app/src/main/res/layout/item_view_phone_number.xml @@ -4,6 +4,7 @@ android:id="@+id/contact_number_holder" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" android:paddingBottom="@dimen/normal_margin" android:paddingTop="@dimen/normal_margin"> diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 1e54adbf..bda8c04a 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -6,6 +6,8 @@ Aktualisiere… Gerätespeicher Gerätespeicher (nicht sichtbar für andere Apps) + No groups + Create a new group Neuer Kontakt Kontakt bearbeiten @@ -27,6 +29,8 @@ Beim Klicken auf den Kontakt Kontakt anrufen Kontaktdetails ansehen + Show favorites tab + Show groups tab Email diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 323ce16a..b6e38037 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -6,6 +6,8 @@ Mise à jour… Stockage du téléphone Stockage du téléphone (non visible par d\'autres applis) + No groups + Create a new group Nouveau contact Modifier contact @@ -27,6 +29,8 @@ Sur appui du contact Appeler le contact Voir les détails du contact + Show favorites tab + Show groups tab E-mail diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index 9db78c56..612be50b 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -6,6 +6,8 @@ 수정중… Phone storage Phone storage (not visible by other apps) + No groups + Create a new group 새로운 연락처 연락처 수정 @@ -27,6 +29,8 @@ On contact click Call contact View contact details + Show favorites tab + Show groups tab 이메일 diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index d932c2b0..2d19395b 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -6,6 +6,8 @@ A atualizar… Armazenamento do telefone Armazenamento do telefone (não visível por outras alicações) + No groups + Create a new group Novo contacto Editar contacto @@ -27,6 +29,8 @@ Ao clicar no contacto Ligar Ver detalhes + Show favorites tab + Show groups tab E-mail diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index cc0a7cc6..c6aecba9 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -6,6 +6,8 @@ Обновление… Память устройства Память устройства (не видна другим приложениям) + No groups + Create a new group Новый контакт Редактировать контакт @@ -27,6 +29,8 @@ При нажатии на контакт Позвонить контакту Просмотреть подробности о контакте + Show favorites tab + Show groups tab Эл. почта diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 00f2d6fb..648c6524 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -6,6 +6,8 @@ Upravuje sa… Úložisko mobilu Úložisko mobilu (neviditeľné pre ostatné apky) + Žiadne skupiny + Vytvoriť novú skupinu Nový kontakt Upraviť kontakt @@ -27,6 +29,8 @@ Po kliknutí na kontakt Zavolať kontakt Zobraziť údaje kontaktu + Zobraziť okno s obľúbenými + Zobraziť okno so skupinami Email diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 2d9fa6a1..3fddf676 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -6,6 +6,8 @@ Uppdaterar… Telefonens lagringsutrymme Telefonens lagringsutrymme (inte synligt för andra appar) + No groups + Create a new group Ny kontakt Redigera kontakt @@ -27,6 +29,8 @@ Vid kontakttryckning Ring kontakt Visa kontaktuppgifter + Show favorites tab + Show groups tab E-post diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 061e7bc3..5d0c8b3c 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -6,6 +6,8 @@ 更新中… 手機空間 手機空間 (其他程式不可見) + 沒有群組 + Create a new group 新聯絡人 編輯聯絡人 @@ -27,6 +29,8 @@ 點擊聯絡人 打電話給聯絡人 顯示聯絡人資料 + 顯示我的最愛頁面 + 顯示群組頁面 信箱 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1e05a942..a941a193 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,6 +6,8 @@ Updating… Phone storage Phone storage (not visible by other apps) + No groups + Create a new group New contact Edit contact @@ -27,6 +29,8 @@ On contact click Call contact View contact details + Show favorites tab + Show groups tab Email