Merge pull request #9 from SimpleMobileTools/master

upd
This commit is contained in:
solokot 2018-03-19 15:06:10 +03:00 committed by GitHub
commit 7298b53f76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 830 additions and 102 deletions

View File

@ -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'

View File

@ -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<Group>
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
}

View File

@ -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() {

View File

@ -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)
}

View File

@ -130,7 +130,7 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Cont
ContactsHelper(activity).deleteContacts(contactsToRemove)
if (contactItems.isEmpty()) {
listener?.refreshContacts()
listener?.refreshContacts(true, true)
finishActMode()
} else {
removeSelectedItems()

View File

@ -5,15 +5,19 @@ import android.view.View
import android.view.ViewGroup
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.MainActivity
import com.simplemobiletools.contacts.fragments.MyViewPagerFragment
import com.simplemobiletools.contacts.interfaces.FragmentInterface
import com.simplemobiletools.contacts.models.Contact
class ViewPagerAdapter(val activity: MainActivity) : PagerAdapter() {
class ViewPagerAdapter(val activity: MainActivity, val contacts: ArrayList<Contact>) : 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
}
}

View File

@ -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()
})
}
}
}
}

View File

@ -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<Group>, val callback: (newGroups: ArrayList<Group>) -> Unit) {
private val view = activity.layoutInflater.inflate(R.layout.dialog_select_groups, null) as ViewGroup
private val checkboxes = ArrayList<MyAppCompatCheckbox>()
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<Group>()
checkboxes.filter { it.isChecked }.forEach {
val groupId = it.tag as Long
groups.firstOrNull { it.id == groupId }?.apply {
selectedGroups.add(this)
}
}
callback(selectedGroups)
}
}

View File

@ -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)

View File

@ -16,7 +16,7 @@ class FavoritesFragment(context: Context, attributeSet: AttributeSet) : MyViewPa
private fun showAddFavoritesDialog() {
AddFavoritesDialog(activity!!) {
initContacts()
activity!!.refreshContacts(false, true)
}
}
}

View File

@ -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<Contact>) {
}
}

View File

@ -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<Contact>()
@ -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,27 +68,17 @@ 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
}
ContactsHelper(activity!!).getContacts {
var contacts = it
if (activity == null || activity!!.isActivityDestroyed()) {
return@getContacts
}
override fun refreshContacts(contacts: ArrayList<Contact>) {
if (config.lastUsedContactSource.isEmpty()) {
val grouped = contacts.groupBy { it.source }.maxWith(compareBy { it.value.size })
config.lastUsedContactSource = grouped?.key ?: ""
}
contacts = if (this is FavoritesFragment) {
val filtered = if (this is FavoritesFragment) {
contacts.filter { it.starred == 1 } as ArrayList<Contact>
} else {
val contactSources = config.displayContactSources
@ -98,13 +90,12 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
}
Contact.sorting = config.sorting
contacts.sort()
filtered.sort()
if (contacts.hashCode() != lastHashCode) {
lastHashCode = contacts.hashCode()
activity!!.runOnUiThread {
setupContacts(contacts)
}
if (filtered.hashCode() != lastHashCode) {
lastHashCode = filtered.hashCode()
activity?.runOnUiThread {
setupContacts(filtered)
}
}
}

View File

@ -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<Group>()
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<Contact>(contactsSize)
(0 until contactsSize).mapTo(resultContacts) { contacts.valueAt(it) }
resultContacts = resultContacts.distinctBy { it.contactId } as ArrayList<Contact>
activity.runOnUiThread {
callback(resultContacts)
}
}.start()
}
@ -305,6 +312,109 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
return notes
}
private fun getContactGroups(storedGroups: ArrayList<Group>, contactId: Int? = null): SparseArray<ArrayList<Group>> {
val groups = SparseArray<ArrayList<Group>>()
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<Group> {
val groups = ArrayList<Group>()
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>()
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<ContentProviderOperation>()
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<String>): 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?

View File

@ -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<Group>()
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)
}
}

View File

@ -28,6 +28,7 @@ class VcfImporter(val activity: SimpleActivity) {
private var curEmails = ArrayList<Email>()
private var curEvents = ArrayList<Event>()
private var curAddresses = ArrayList<Address>()
private var curGroups = ArrayList<Group>()
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()

View File

@ -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<Contact>)
}

View File

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

View File

@ -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<PhoneNumber>, var emails: ArrayList<Email>, var addresses: ArrayList<Address>, var events: ArrayList<Event>,
var source: String, var starred: Int, val contactId: Int, val thumbnailUri: String, var photo: Bitmap?, var notes: String) : Comparable<Contact> {
var source: String, var starred: Int, val contactId: Int, val thumbnailUri: String, var photo: Bitmap?, var notes: String,
var groups: ArrayList<Group>) : Comparable<Contact> {
companion object {
var sorting = 0
}

View File

@ -0,0 +1,3 @@
package com.simplemobiletools.contacts.models
data class Group(var id: Long, var title: String)

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

View File

@ -313,6 +313,44 @@
android:textCursorDrawable="@null"
android:textSize="@dimen/bigger_text_size"/>
<ImageView
android:id="@+id/contact_groups_image"
android:layout_width="@dimen/contact_icons_size"
android:layout_height="@dimen/contact_icons_size"
android:layout_alignTop="@+id/contact_groups_holder"
android:paddingBottom="@dimen/small_margin"
android:paddingEnd="@dimen/small_margin"
android:paddingRight="@dimen/small_margin"
android:paddingTop="@dimen/medium_margin"
android:src="@drawable/ic_group"/>
<LinearLayout
android:id="@+id/contact_groups_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_notes"
android:layout_marginTop="@dimen/medium_margin"
android:layout_toRightOf="@+id/contact_name_image"
android:orientation="vertical">
<include layout="@layout/item_group"/>
</LinearLayout>
<ImageView
android:id="@+id/contact_groups_add_new"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_groups_holder"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/small_margin"
android:background="@drawable/button_background"
android:paddingBottom="@dimen/medium_margin"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:paddingTop="@dimen/medium_margin"
android:src="@drawable/ic_plus"/>
<ImageView
android:id="@+id/contact_source_image"
android:layout_width="@dimen/contact_icons_size"
@ -328,7 +366,7 @@
android:id="@+id/contact_source"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_notes"
android:layout_below="@+id/contact_groups_add_new"
android:layout_centerVertical="true"
android:layout_marginTop="@dimen/medium_margin"
android:layout_toRightOf="@+id/contact_name_image"

View File

@ -28,6 +28,12 @@
android:layout_height="wrap_content"
android:icon="@drawable/ic_star_on"/>
<android.support.design.widget.TabItem
android:id="@+id/tab_groups"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@drawable/ic_group"/>
</android.support.design.widget.TabLayout>
<com.booking.rtlviewpager.RtlViewPager

View File

@ -239,6 +239,26 @@
android:paddingTop="@dimen/normal_margin"
android:textSize="@dimen/bigger_text_size"/>
<ImageView
android:id="@+id/contact_groups_image"
android:layout_width="@dimen/contact_icons_size"
android:layout_height="@dimen/contact_icons_size"
android:layout_alignTop="@+id/contact_groups_holder"
android:paddingBottom="@dimen/small_margin"
android:paddingEnd="@dimen/small_margin"
android:paddingRight="@dimen/small_margin"
android:paddingTop="@dimen/medium_margin"
android:src="@drawable/ic_group"/>
<LinearLayout
android:id="@+id/contact_groups_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_notes"
android:layout_toRightOf="@+id/contact_name_image"
android:orientation="vertical"
android:paddingLeft="@dimen/small_margin"/>
<ImageView
android:id="@+id/contact_source_image"
android:layout_width="@dimen/contact_icons_size"
@ -254,7 +274,7 @@
android:id="@+id/contact_source"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_notes"
android:layout_below="@+id/contact_groups_holder"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/contact_name_image"
android:lines="1"

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dialog_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/group_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/name"/>
<com.simplemobiletools.commons.views.MyEditText
android:id="@+id/group_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/activity_margin"
android:inputType="textCapSentences"
android:singleLine="true"
android:textCursorDrawable="@null"
android:textSize="@dimen/normal_text_size"/>
</LinearLayout>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dialog_radio_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/dialog_groups_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:paddingTop="@dimen/normal_margin"/>
</ScrollView>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<com.simplemobiletools.contacts.fragments.GroupsFragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/groups_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.simplemobiletools.contacts.fragments.GroupsFragment>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/item_checkbox_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">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/item_checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:clickable="false"/>
</RelativeLayout>

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

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<com.simplemobiletools.commons.views.MyTextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/item_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_margin"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/medium_margin"
android:paddingTop="@dimen/activity_margin"/>

View File

@ -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">

View File

@ -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">

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<com.simplemobiletools.commons.views.MyTextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contact_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:ellipsize="end"
android:lines="1"
android:maxLines="1"
android:paddingBottom="@dimen/normal_margin"
android:paddingTop="@dimen/normal_margin"
android:singleLine="true"
android:textSize="@dimen/bigger_text_size"/>

View File

@ -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">

View File

@ -6,6 +6,8 @@
<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>
@ -27,6 +29,8 @@
<string name="on_contact_click">Beim Klicken auf den Kontakt</string>
<string name="call_contact">Kontakt anrufen</string>
<string name="view_contact">Kontaktdetails ansehen</string>
<string name="show_favorites_tab">Show favorites tab</string>
<string name="show_groups_tab">Show groups tab</string>
<!-- Emails -->
<string name="email">Email</string>

View File

@ -6,6 +6,8 @@
<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>
@ -27,6 +29,8 @@
<string name="on_contact_click">Sur appui du contact</string>
<string name="call_contact">Appeler le contact</string>
<string name="view_contact">Voir les détails du contact</string>
<string name="show_favorites_tab">Show favorites tab</string>
<string name="show_groups_tab">Show groups tab</string>
<!-- Emails -->
<string name="email">E-mail</string>

View File

@ -6,6 +6,8 @@
<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>
@ -27,6 +29,8 @@
<string name="on_contact_click">On contact click</string>
<string name="call_contact">Call contact</string>
<string name="view_contact">View contact details</string>
<string name="show_favorites_tab">Show favorites tab</string>
<string name="show_groups_tab">Show groups tab</string>
<!-- Emails -->
<string name="email">이메일</string>

View File

@ -6,6 +6,8 @@
<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>
@ -27,6 +29,8 @@
<string name="on_contact_click">Ao clicar no contacto</string>
<string name="call_contact">Ligar</string>
<string name="view_contact">Ver detalhes</string>
<string name="show_favorites_tab">Show favorites tab</string>
<string name="show_groups_tab">Show groups tab</string>
<!-- Emails -->
<string name="email">E-mail</string>

View File

@ -6,6 +6,8 @@
<string name="updating">Обновление…</string>
<string name="phone_storage">Память устройства</string>
<string name="phone_storage_hidden">Память устройства (не видна другим приложениям)</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>
@ -27,6 +29,8 @@
<string name="on_contact_click">При нажатии на контакт</string>
<string name="call_contact">Позвонить контакту</string>
<string name="view_contact">Просмотреть подробности о контакте</string>
<string name="show_favorites_tab">Show favorites tab</string>
<string name="show_groups_tab">Show groups tab</string>
<!-- Emails -->
<string name="email">Эл. почта</string>

View File

@ -6,6 +6,8 @@
<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>
@ -27,6 +29,8 @@
<string name="on_contact_click">Po kliknutí na kontakt</string>
<string name="call_contact">Zavolať kontakt</string>
<string name="view_contact">Zobraziť údaje kontaktu</string>
<string name="show_favorites_tab">Zobraziť okno s obľúbenými</string>
<string name="show_groups_tab">Zobraziť okno so skupinami</string>
<!-- Emails -->
<string name="email">Email</string>

View File

@ -6,6 +6,8 @@
<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>
@ -27,6 +29,8 @@
<string name="on_contact_click">Vid kontakttryckning</string>
<string name="call_contact">Ring kontakt</string>
<string name="view_contact">Visa kontaktuppgifter</string>
<string name="show_favorites_tab">Show favorites tab</string>
<string name="show_groups_tab">Show groups tab</string>
<!-- Emails -->
<string name="email">E-post</string>

View File

@ -6,6 +6,8 @@
<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>
@ -27,6 +29,8 @@
<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">信箱</string>

View File

@ -6,6 +6,8 @@
<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>
@ -27,6 +29,8 @@
<string name="on_contact_click">On contact click</string>
<string name="call_contact">Call contact</string>
<string name="view_contact">View contact details</string>
<string name="show_favorites_tab">Show favorites tab</string>
<string name="show_groups_tab">Show groups tab</string>
<!-- Emails -->
<string name="email">Email</string>