Merge pull request #13 from SimpleMobileTools/master

upd
This commit is contained in:
solokot
2018-04-30 11:11:35 +03:00
committed by GitHub
29 changed files with 143 additions and 100 deletions

View File

@ -45,7 +45,7 @@ ext {
} }
dependencies { dependencies {
implementation 'com.simplemobiletools:commons:3.19.21' implementation 'com.simplemobiletools:commons:3.20.6'
implementation 'joda-time:joda-time:2.9.9' implementation 'joda-time:joda-time:2.9.9'
implementation 'com.facebook.stetho:stetho:1.5.0' implementation 'com.facebook.stetho:stetho:1.5.0'

View File

@ -9,6 +9,8 @@
<uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/> <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission <uses-permission
android:name="android.permission.USE_FINGERPRINT" android:name="android.permission.USE_FINGERPRINT"
@ -26,8 +28,6 @@
android:name=".activities.SplashActivity" android:name=".activities.SplashActivity"
android:theme="@style/SplashTheme"> android:theme="@style/SplashTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
@ -190,5 +190,30 @@
android:name="android.support.FILE_PROVIDER_PATHS" android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/> android:resource="@xml/provider_paths"/>
</provider> </provider>
<!-- Do not append ".Orange" to the default alias "name", it would remove the old homescreen launcher of users at upgrade -->
<activity-alias
android:name=".activities.SplashActivity"
android:enabled="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Red"
android:enabled="false"
android:icon="@mipmap/ic_launcher_red"
android:roundIcon="@mipmap/ic_launcher_red"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
</application> </application>
</manifest> </manifest>

View File

@ -87,6 +87,7 @@ class EditContactActivity : ContactActivity() {
if (wasActivityInitialized) { if (wasActivityInitialized) {
menu.findItem(R.id.delete).isVisible = contact?.id != 0 menu.findItem(R.id.delete).isVisible = contact?.id != 0
menu.findItem(R.id.share).isVisible = contact?.id != 0 menu.findItem(R.id.share).isVisible = contact?.id != 0
menu.findItem(R.id.open_with).isVisible = contact?.id != 0 && contact?.source != SMT_PRIVATE
} }
return true return true
} }
@ -94,8 +95,9 @@ class EditContactActivity : ContactActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.save -> saveContact() R.id.save -> saveContact()
R.id.delete -> deleteContact()
R.id.share -> shareContact() R.id.share -> shareContact()
R.id.open_with -> openWith()
R.id.delete -> deleteContact()
else -> return super.onOptionsItemSelected(item) else -> return super.onOptionsItemSelected(item)
} }
return true return true
@ -224,6 +226,14 @@ class EditContactActivity : ContactActivity() {
invalidateOptionsMenu() invalidateOptionsMenu()
} }
private fun openWith() {
Intent().apply {
action = Intent.ACTION_EDIT
data = getContactPublicUri(contact!!)
startActivity(this)
}
}
private fun startCropPhotoIntent(uri: Uri?) { private fun startCropPhotoIntent(uri: Uri?) {
if (uri == null) { if (uri == null) {
toast(R.string.unknown_error_occurred) toast(R.string.unknown_error_occurred)

View File

@ -234,16 +234,6 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
private fun storeLocalAccountData() { private fun storeLocalAccountData() {
if (config.localAccountType == "-1") { if (config.localAccountType == "-1") {
// some manufacturer contact account types from https://stackoverflow.com/a/44802016/1967672
val localAccountTypes = arrayListOf("vnd.sec.contact.phone",
"com.htc.android.pcsc",
"com.sonyericsson.localcontacts",
"com.lge.sync",
"com.lge.phone",
"vnd.tmobileus.contact.phone",
"com.android.huawei.phone",
"Local Phone Account")
ContactsHelper(this).getContactSources { ContactsHelper(this).getContactSources {
var localAccountType = "" var localAccountType = ""
var localAccountName = "" var localAccountName = ""

View File

@ -17,6 +17,7 @@ import com.simplemobiletools.contacts.adapters.SelectContactsAdapter
import com.simplemobiletools.contacts.dialogs.ChangeSortingDialog import com.simplemobiletools.contacts.dialogs.ChangeSortingDialog
import com.simplemobiletools.contacts.dialogs.FilterContactSourcesDialog import com.simplemobiletools.contacts.dialogs.FilterContactSourcesDialog
import com.simplemobiletools.contacts.extensions.config import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.extensions.getContactPublicUri
import com.simplemobiletools.contacts.helpers.ContactsHelper import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.helpers.SMT_PRIVATE import com.simplemobiletools.contacts.helpers.SMT_PRIVATE
import com.simplemobiletools.contacts.models.Contact import com.simplemobiletools.contacts.models.Contact
@ -98,9 +99,7 @@ class SelectContactActivity : SimpleActivity() {
} as ArrayList<Contact> } as ArrayList<Contact>
val contactSources = config.displayContactSources val contactSources = config.displayContactSources
if (!config.showAllContacts()) {
contacts = contacts.filter { contactSources.contains(it.source) } as ArrayList<Contact> contacts = contacts.filter { contactSources.contains(it.source) } as ArrayList<Contact>
}
Contact.sorting = config.sorting Contact.sorting = config.sorting
Contact.startWithSurname = config.startNameWithSurname Contact.startWithSurname = config.startNameWithSurname
@ -135,8 +134,7 @@ class SelectContactActivity : SimpleActivity() {
Uri.withAppendedPath(ContactsContract.Data.CONTENT_URI, contactId) Uri.withAppendedPath(ContactsContract.Data.CONTENT_URI, contactId)
} }
else -> { else -> {
val lookupKey = ContactsHelper(this).getContactLookupKey(contact.id.toString()) getContactPublicUri(contact)
Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey)
} }
} }
} }

View File

@ -1,5 +1,13 @@
package com.simplemobiletools.contacts.activities package com.simplemobiletools.contacts.activities
import com.simplemobiletools.commons.activities.BaseSimpleActivity import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.contacts.R
open class SimpleActivity : BaseSimpleActivity() open class SimpleActivity : BaseSimpleActivity() {
override fun getAppIconIDs() = arrayListOf(
R.mipmap.ic_launcher_red,
R.mipmap.ic_launcher
)
override fun getAppLauncherName() = getString(R.string.app_launcher_name)
}

View File

@ -49,13 +49,18 @@ class ViewContactActivity : ContactActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_view_contact, menu) menuInflater.inflate(R.menu.menu_view_contact, menu)
menu.apply {
findItem(R.id.open_with).isVisible = contact?.source != SMT_PRIVATE
}
return true return true
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.edit -> editContact(contact!!) R.id.edit -> editContact(contact!!)
R.id.share -> shareContact() R.id.share -> shareContact()
R.id.open_with -> openWith()
R.id.delete -> deleteContact() R.id.delete -> deleteContact()
else -> return super.onOptionsItemSelected(item) else -> return super.onOptionsItemSelected(item)
} }
@ -151,6 +156,14 @@ class ViewContactActivity : ContactActivity() {
setupContactSource() setupContactSource()
} }
private fun openWith() {
Intent().apply {
action = ContactsContract.QuickContact.ACTION_QUICK_CONTACT
data = getContactPublicUri(contact!!)
startActivity(this)
}
}
private fun setupFavorite() { private fun setupFavorite() {
contact_toggle_favorite.apply { contact_toggle_favorite.apply {
beVisible() beVisible()

View File

@ -21,7 +21,7 @@ class FilterContactSourcesAdapter(val activity: SimpleActivity, private val cont
init { init {
contactSources.forEachIndexed { index, contactSource -> contactSources.forEachIndexed { index, contactSource ->
if (activity.config.showAllContacts() || displayContactSources.contains(contactSource.name)) { if (displayContactSources.contains(contactSource.name)) {
selectedPositions.add(index) selectedPositions.add(index)
} }

View File

@ -19,9 +19,7 @@ class SelectContactsDialog(val activity: SimpleActivity, initialContacts: ArrayL
var allContacts = initialContacts var allContacts = initialContacts
if (selectContacts == null) { if (selectContacts == null) {
val contactSources = activity.config.displayContactSources val contactSources = activity.config.displayContactSources
if (!activity.config.showAllContacts()) {
allContacts = allContacts.filter { contactSources.contains(it.source) } as ArrayList<Contact> allContacts = allContacts.filter { contactSources.contains(it.source) } as ArrayList<Contact>
}
initiallySelectedContacts = allContacts.filter { it.starred == 1 } as ArrayList<Contact> initiallySelectedContacts = allContacts.filter { it.starred == 1 } as ArrayList<Contact>
} else { } else {

View File

@ -2,6 +2,7 @@ package com.simplemobiletools.contacts.extensions
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.provider.ContactsContract
import com.simplemobiletools.commons.activities.BaseSimpleActivity import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.dialogs.RadioGroupDialog import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.sharePathIntent import com.simplemobiletools.commons.extensions.sharePathIntent
@ -175,3 +176,8 @@ fun BaseSimpleActivity.removeContactsFromGroup(contacts: ArrayList<Contact>, gro
dbHelper.removeContactsFromGroup(contacts, groupId) dbHelper.removeContactsFromGroup(contacts, groupId)
} }
} }
fun BaseSimpleActivity.getContactPublicUri(contact: Contact): Uri {
val lookupKey = ContactsHelper(this).getContactLookupKey(contact.id.toString())
return Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey)
}

View File

@ -99,15 +99,11 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
contacts.sort() contacts.sort()
allContacts = contacts allContacts = contacts
val filtered = if (this is GroupsFragment) { val filtered = when {
contacts this is GroupsFragment -> contacts
} else if (this is FavoritesFragment) { this is FavoritesFragment -> contacts.filter { it.starred == 1 } as ArrayList<Contact>
contacts.filter { it.starred == 1 } as ArrayList<Contact> else -> {
} else {
val contactSources = config.displayContactSources val contactSources = config.displayContactSources
if (config.showAllContacts()) {
contacts
} else {
contacts.filter { contactSources.contains(it.source) } as ArrayList<Contact> contacts.filter { contactSources.contains(it.source) } as ArrayList<Contact>
} }
} }

View File

@ -12,8 +12,6 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getStringSet(DISPLAY_CONTACT_SOURCES, hashSetOf("-1")) get() = prefs.getStringSet(DISPLAY_CONTACT_SOURCES, hashSetOf("-1"))
set(displayContactSources) = prefs.edit().remove(DISPLAY_CONTACT_SOURCES).putStringSet(DISPLAY_CONTACT_SOURCES, displayContactSources).apply() set(displayContactSources) = prefs.edit().remove(DISPLAY_CONTACT_SOURCES).putStringSet(DISPLAY_CONTACT_SOURCES, displayContactSources).apply()
fun showAllContacts() = displayContactSources.size == 1 && displayContactSources.first() == "-1"
var showContactThumbnails: Boolean var showContactThumbnails: Boolean
get() = prefs.getBoolean(SHOW_CONTACT_THUMBNAILS, true) get() = prefs.getBoolean(SHOW_CONTACT_THUMBNAILS, true)
set(showContactThumbnails) = prefs.edit().putBoolean(SHOW_CONTACT_THUMBNAILS, showContactThumbnails).apply() set(showContactThumbnails) = prefs.edit().putBoolean(SHOW_CONTACT_THUMBNAILS, showContactThumbnails).apply()

View File

@ -93,3 +93,14 @@ const val DEFAULT_ADDRESS_TYPE = CommonDataKinds.StructuredPostal.TYPE_HOME
const val DEFAULT_EVENT_TYPE = CommonDataKinds.Event.TYPE_BIRTHDAY const val DEFAULT_EVENT_TYPE = CommonDataKinds.Event.TYPE_BIRTHDAY
const val DEFAULT_ORGANIZATION_TYPE = CommonDataKinds.Organization.TYPE_WORK const val DEFAULT_ORGANIZATION_TYPE = CommonDataKinds.Organization.TYPE_WORK
const val DEFAULT_WEBSITE_TYPE = CommonDataKinds.Website.TYPE_HOMEPAGE const val DEFAULT_WEBSITE_TYPE = CommonDataKinds.Website.TYPE_HOMEPAGE
// some manufacturer contact account types from https://stackoverflow.com/a/44802016/1967672
val localAccountTypes = arrayListOf("vnd.sec.contact.phone",
"com.htc.android.pcsc",
"com.sonyericsson.localcontacts",
"com.lge.sync",
"com.lge.phone",
"vnd.tmobileus.contact.phone",
"com.android.huawei.phone",
"Local Phone Account"
)

View File

@ -1,9 +1,7 @@
package com.simplemobiletools.contacts.helpers package com.simplemobiletools.contacts.helpers
import android.content.ContentProviderOperation import android.accounts.AccountManager
import android.content.ContentProviderResult import android.content.*
import android.content.ContentUris
import android.content.ContentValues
import android.database.Cursor import android.database.Cursor
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
@ -37,7 +35,9 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
val contactsSize = contacts.size() val contactsSize = contacts.size()
var resultContacts = ArrayList<Contact>(contactsSize) var resultContacts = ArrayList<Contact>(contactsSize)
(0 until contactsSize).mapTo(resultContacts) { contacts.valueAt(it) } (0 until contactsSize).mapTo(resultContacts) { contacts.valueAt(it) }
resultContacts = resultContacts.distinctBy { it.contactId } as ArrayList<Contact> resultContacts = resultContacts.distinctBy {
it.getHashToCompare()
} as ArrayList<Contact>
// groups are obtained with contactID, not rawID, so assign them to proper contacts like this // groups are obtained with contactID, not rawID, so assign them to proper contacts like this
val groups = getContactGroups(getStoredGroups()) val groups = getContactGroups(getStoredGroups())
@ -90,6 +90,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
val websites = ArrayList<String>() val websites = ArrayList<String>()
val contact = Contact(id, prefix, firstName, middleName, surname, suffix, photoUri, number, emails, addresses, events, val contact = Contact(id, prefix, firstName, middleName, surname, suffix, photoUri, number, emails, addresses, events,
accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites) accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites)
contacts.put(id, contact) contacts.put(id, contact)
} while (cursor.moveToNext()) } while (cursor.moveToNext())
} }
@ -621,64 +622,34 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
fun getContactSources(callback: (ArrayList<ContactSource>) -> Unit) { fun getContactSources(callback: (ArrayList<ContactSource>) -> Unit) {
Thread { Thread {
val sources = LinkedHashSet<ContactSource>() val sources = getDeviceContactSources()
getDeviceContactSources(sources)
sources.add(ContactSource(activity.getString(R.string.phone_storage_hidden), SMT_PRIVATE)) sources.add(ContactSource(activity.getString(R.string.phone_storage_hidden), SMT_PRIVATE))
callback(ArrayList(sources)) callback(ArrayList(sources))
}.start() }.start()
} }
private fun getDeviceContactSources(sources: LinkedHashSet<ContactSource>) { private fun getDeviceContactSources(): LinkedHashSet<ContactSource> {
val sources = LinkedHashSet<ContactSource>()
if (!activity.hasContactPermissions()) { if (!activity.hasContactPermissions()) {
return return sources
} }
val uri = ContactsContract.RawContacts.CONTENT_URI val accountManager = AccountManager.get(activity)
val projection = arrayOf(ContactsContract.RawContacts.ACCOUNT_NAME, ContactsContract.RawContacts.ACCOUNT_TYPE) accountManager.accounts.filter { it.name.contains("@") || localAccountTypes.contains(it.type) }.forEach {
if (ContentResolver.getIsSyncable(it, ContactsContract.AUTHORITY) == 1) {
var cursor: Cursor? = null val contactSource = ContactSource(it.name, it.type)
try {
cursor = activity.contentResolver.query(uri, projection, null, null, null)
if (cursor?.moveToFirst() == true) {
do {
val name = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME) ?: continue
val type = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_TYPE) ?: continue
val contactSource = ContactSource(name, type)
sources.add(contactSource) sources.add(contactSource)
} while (cursor.moveToNext())
} }
} catch (e: Exception) {
activity.showErrorToast(e)
} finally {
cursor?.close()
} }
if (sources.isEmpty() && activity.config.localAccountName.isEmpty() && activity.config.localAccountType.isEmpty()) { if (sources.isEmpty() && activity.config.localAccountName.isEmpty() && activity.config.localAccountType.isEmpty()) {
sources.add(ContactSource("", "")) sources.add(ContactSource("", ""))
} }
return sources
} }
private fun getContactSourceType(accountName: String): String { private fun getContactSourceType(accountName: String) = getDeviceContactSources().firstOrNull { it.name == accountName }?.type ?: ""
if (accountName.isEmpty()) {
return ""
}
val uri = ContactsContract.RawContacts.CONTENT_URI
val projection = arrayOf(ContactsContract.RawContacts.ACCOUNT_TYPE)
val selection = "${ContactsContract.RawContacts.ACCOUNT_NAME} = ?"
val selectionArgs = arrayOf(accountName)
var cursor: Cursor? = null
try {
cursor = activity.contentResolver.query(uri, projection, selection, selectionArgs, null)
if (cursor?.moveToFirst() == true) {
return cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_TYPE)
}
} finally {
cursor?.close()
}
return ""
}
private fun getContactProjection() = arrayOf( private fun getContactProjection() = arrayOf(
ContactsContract.Data.CONTACT_ID, ContactsContract.Data.CONTACT_ID,
@ -1060,6 +1031,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
} }
// organization // organization
if (!contact.organization.isEmpty()) {
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply { ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE) withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
@ -1069,6 +1041,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
withValue(CommonDataKinds.Organization.TYPE, DEFAULT_ORGANIZATION_TYPE) withValue(CommonDataKinds.Organization.TYPE, DEFAULT_ORGANIZATION_TYPE)
operations.add(build()) operations.add(build())
} }
}
// websites // websites
contact.websites.forEach { contact.websites.forEach {
@ -1246,16 +1219,14 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
activity.dbHelper.deleteContacts(localContacts) activity.dbHelper.deleteContacts(localContacts)
try { try {
val contactIDs = HashSet<String>()
val operations = ArrayList<ContentProviderOperation>() val operations = ArrayList<ContentProviderOperation>()
val selection = "${ContactsContract.Data.CONTACT_ID} = ?" val selection = "${ContactsContract.Data.CONTACT_ID} = ?"
contacts.filter { it.source != SMT_PRIVATE }.forEach { contacts.filter { it.source != SMT_PRIVATE }.forEach {
ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply { ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
val selectionArgs = arrayOf(it.contactId.toString()) val selectionArgs = arrayOf(it.contactId.toString())
withSelection(selection, selectionArgs) withSelection(selection, selectionArgs)
operations.add(this.build()) operations.add(build())
} }
contactIDs.add(it.id.toString())
} }
activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations) activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)

View File

@ -119,6 +119,10 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
fun deleteContact(id: Int) = deleteContacts(arrayOf(id.toString())) fun deleteContact(id: Int) = deleteContacts(arrayOf(id.toString()))
fun deleteContacts(ids: Array<String>) { fun deleteContacts(ids: Array<String>) {
if (ids.isEmpty()) {
return
}
val args = TextUtils.join(", ", ids) val args = TextUtils.join(", ", ids)
val selection = "$CONTACTS_TABLE_NAME.$COL_ID IN ($args)" val selection = "$CONTACTS_TABLE_NAME.$COL_ID IN ($args)"
mDb.delete(CONTACTS_TABLE_NAME, selection, null) mDb.delete(CONTACTS_TABLE_NAME, selection, null)

View File

@ -12,6 +12,7 @@ data class Contact(val id: Int, var prefix: String, var firstName: String, var m
companion object { companion object {
var sorting = 0 var sorting = 0
var startWithSurname = false var startWithSurname = false
val pattern = "\\D+".toRegex()
} }
override fun compareTo(other: Contact): Int { override fun compareTo(other: Contact): Int {
@ -81,4 +82,10 @@ data class Contact(val id: Int, var prefix: String, var firstName: String, var m
fullName fullName
} }
} }
fun getHashToCompare(): Int {
val newPhoneNumbers = ArrayList<PhoneNumber>()
phoneNumbers.mapTo(newPhoneNumbers, { PhoneNumber(it.value.replace(pattern, ""), 0) })
return copy(id = 0, phoneNumbers = newPhoneNumbers).hashCode()
}
} }

View File

@ -341,7 +341,7 @@
android:layout_marginTop="@dimen/normal_margin" android:layout_marginTop="@dimen/normal_margin"
android:layout_toRightOf="@+id/contact_notes_image" android:layout_toRightOf="@+id/contact_notes_image"
android:hint="@string/notes" android:hint="@string/notes"
android:inputType="textCapWords|textMultiLine" android:inputType="textCapSentences|textMultiLine"
android:textCursorDrawable="@null" android:textCursorDrawable="@null"
android:textSize="@dimen/bigger_text_size"/> android:textSize="@dimen/bigger_text_size"/>

View File

@ -11,6 +11,10 @@
android:icon="@drawable/ic_share" android:icon="@drawable/ic_share"
android:title="@string/share" android:title="@string/share"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item
android:id="@+id/open_with"
android:title="@string/open_with"
app:showAsAction="never"/>
<item <item
android:id="@+id/delete" android:id="@+id/delete"
android:icon="@drawable/ic_delete" android:icon="@drawable/ic_delete"

View File

@ -11,6 +11,10 @@
android:icon="@drawable/ic_share" android:icon="@drawable/ic_share"
android:title="@string/share" android:title="@string/share"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item
android:id="@+id/open_with"
android:title="@string/open_with"
app:showAsAction="never"/>
<item <item
android:id="@+id/delete" android:id="@+id/delete"
android:icon="@drawable/ic_delete" android:icon="@drawable/ic_delete"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -9,10 +9,10 @@
<string name="company">Organização</string> <string name="company">Organização</string>
<string name="job_position">Cargo</string> <string name="job_position">Cargo</string>
<string name="website">Site</string> <string name="website">Site</string>
<string name="send_sms_to_contacts">Send SMS to contacts</string> <string name="send_sms_to_contacts">Enviar SMS aos contactos</string>
<string name="send_email_to_contacts">Send email to contacts</string> <string name="send_email_to_contacts">Enviar e-mail aos contactos</string>
<string name="send_sms_to_group">Send SMS to group</string> <string name="send_sms_to_group">Enviar SMS para o grupo</string>
<string name="send_email_to_group">Send email to group</string> <string name="send_email_to_group">Enviar e-mail para o grupo</string>
<string name="new_contact">Novo contacto</string> <string name="new_contact">Novo contacto</string>
<string name="edit_contact">Editar contacto</string> <string name="edit_contact">Editar contacto</string>
@ -73,7 +73,7 @@
<string name="add_favorites">Adicionar favoritos</string> <string name="add_favorites">Adicionar favoritos</string>
<string name="add_to_favorites">Adicionar aos favoritos</string> <string name="add_to_favorites">Adicionar aos favoritos</string>
<string name="remove_from_favorites">Remover dos favoritos</string> <string name="remove_from_favorites">Remover dos favoritos</string>
<string name="must_be_at_edit">You must be at the Edit screen to modify a contact</string> <string name="must_be_at_edit">Tem que estar no ecrã de edição para alterar um contacto</string>
<!-- Search --> <!-- Search -->
<string name="search_contacts">Pesquisar contactos</string> <string name="search_contacts">Pesquisar contactos</string>

View File

@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.2.40' ext.kotlin_version = '1.2.41'
repositories { repositories {
google() google()