Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/talk-android.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md5
-rw-r--r--app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt3
-rw-r--r--app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt320
-rw-r--r--app/src/main/res/values/strings.xml2
4 files changed, 177 insertions, 153 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fc3c29dd3..4a888d738 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,11 +9,14 @@ Types of changes can be: Added/Changed/Deprecated/Removed/Fixed/Security
### Added
### Changed
-- improve conversation list design wand dark/light theming
+- improve conversation list design and dark/light theming
### Fixed
- @ in username is allowed for phonebook sync
- avoid sync when phonebook is empty
+- avoid creation of multiple "chat via"-links in phonebook
+- delete "chat via"-link from phonebook if phone number was deleted on server
+- remove all "chat via"-links from phonebook when sync is disabled
## [11.1.0] - 2021-03-12
### Added
diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt
index ce059de71..349f82d3c 100644
--- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt
@@ -176,7 +176,8 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (userUtils.currentUser?.baseUrl?.endsWith(baseUrl) == true) {
startConversation(user)
} else {
- Snackbar.make(container, "Account not found", Snackbar.LENGTH_LONG).show()
+ Snackbar.make(container, R.string.nc_phone_book_integration_account_not_found, Snackbar
+ .LENGTH_LONG).show()
}
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt
index 833c8bd48..f015ab7ab 100644
--- a/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt
+++ b/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt
@@ -30,7 +30,6 @@ import android.content.pm.PackageManager
import android.net.Uri
import android.os.RemoteException
import android.provider.ContactsContract
-import android.provider.ContactsContract.CommonDataKinds.Phone.NUMBER
import android.util.Log
import androidx.core.content.ContextCompat
import androidx.core.os.ConfigurationCompat
@@ -99,15 +98,14 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
Log.d(TAG, "Account already exists")
}
- // collect all contacts with phone number
- val contactsWithNumbers = collectPhoneNumbers()
+ val deviceContactsWithNumbers = collectContactsWithPhoneNumbersFromDevice()
- if(contactsWithNumbers.isNotEmpty()){
+ if(deviceContactsWithNumbers.isNotEmpty()){
val currentLocale = ConfigurationCompat.getLocales(context.resources.configuration)[0].country
val map = mutableMapOf<String, Any>()
map["location"] = currentLocale
- map["search"] = contactsWithNumbers
+ map["search"] = deviceContactsWithNumbers
val json = Gson().toJson(map)
@@ -125,13 +123,14 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
}
override fun onNext(foundContacts: ContactsByNumberOverall) {
- up(foundContacts)
+ val contactsWithAssociatedPhoneNumbers = foundContacts.ocs.map
+ deleteLinkedAccounts(contactsWithAssociatedPhoneNumbers)
+ createLinkedAccounts(contactsWithAssociatedPhoneNumbers)
}
override fun onError(e: Throwable) {
Log.e(javaClass.simpleName, "Failed to searchContactsByPhoneNumber", e)
}
-
})
}
@@ -141,8 +140,8 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
return Result.success()
}
- private fun collectPhoneNumbers(): MutableMap<String, List<String>> {
- val result: MutableMap<String, List<String>> = mutableMapOf()
+ private fun collectContactsWithPhoneNumbersFromDevice(): MutableMap<String, List<String>> {
+ val deviceContactsWithNumbers: MutableMap<String, List<String>> = mutableMapOf()
val contactCursor = context.contentResolver.query(
ContactsContract.Contacts.CONTENT_URI,
@@ -156,42 +155,32 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
if (contactCursor.count > 0) {
contactCursor.moveToFirst()
for (i in 0 until contactCursor.count) {
- val numbers: MutableList<String> = mutableListOf()
-
val id = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts._ID))
val lookup = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY))
-
- val phonesCursor = context.contentResolver.query(
- ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
- null,
- ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + id,
- null,
- null)
-
- if (phonesCursor != null) {
- while (phonesCursor.moveToNext()) {
- numbers.add(phonesCursor.getString(phonesCursor.getColumnIndex(NUMBER)))
- }
-
- result[lookup] = numbers
-
- phonesCursor.close()
- }
-
+ deviceContactsWithNumbers[lookup] = getPhoneNumbersFromDeviceContact(id)
contactCursor.moveToNext()
}
}
-
contactCursor.close()
}
-
- return result
+ Log.d(TAG, "collected contacts with phonenumbers: " + deviceContactsWithNumbers.size)
+ return deviceContactsWithNumbers
}
- private fun up(foundContacts: ContactsByNumberOverall) {
- val map = foundContacts.ocs.map
-
- // Delete all old associations (those that are associated on phone, but not in server response)
+ private fun deleteLinkedAccounts(contactsWithAssociatedPhoneNumbers: Map<String, String>?) {
+ Log.d(TAG, "deleteLinkedAccount")
+ fun deleteLinkedAccount(id: String) {
+ val rawContactUri = ContactsContract.RawContacts.CONTENT_URI
+ .buildUpon()
+ .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
+ .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
+ .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
+ .build()
+ val count = context.contentResolver.delete(rawContactUri, ContactsContract.RawContacts.CONTACT_ID + " " +
+ "LIKE \"" + id + "\"", null)
+ Log.d(TAG, "deleted $count linked accounts for id $id")
+ }
+
val rawContactUri = ContactsContract.Data.CONTENT_URI
.buildUpon()
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
@@ -200,7 +189,6 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
.appendQueryParameter(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat")
.build()
- // get all raw contacts
val rawContactsCursor = context.contentResolver.query(
rawContactUri,
null,
@@ -212,147 +200,177 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
if (rawContactsCursor != null) {
if (rawContactsCursor.count > 0) {
while (rawContactsCursor.moveToNext()) {
- val id = rawContactsCursor.getString(rawContactsCursor.getColumnIndex(ContactsContract.RawContacts._ID))
- val sync1 = rawContactsCursor.getString(rawContactsCursor.getColumnIndex(ContactsContract.Data.SYNC1))
val lookupKey = rawContactsCursor.getString(rawContactsCursor.getColumnIndex(ContactsContract.Data.LOOKUP_KEY))
- Log.d("Contact", "Found associated: $id")
+ val contactId = rawContactsCursor.getString(rawContactsCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID))
- if (map == null || !map.containsKey(lookupKey)) {
- if (sync1 != null) {
- deleteAssociation(sync1)
- }
+ if (contactsWithAssociatedPhoneNumbers == null || !contactsWithAssociatedPhoneNumbers.containsKey(lookupKey)) {
+ deleteLinkedAccount(contactId)
}
}
+ } else {
+ Log.d(TAG, "no contacts with linked Talk Accounts found. Nothing to delete...")
}
-
rawContactsCursor.close()
}
+ }
- // update / change found
- if (map != null && map.isNotEmpty()) {
- for (contact in foundContacts.ocs.map) {
- val lookupKey = contact.key
- val cloudId = contact.value
-
- update(lookupKey, cloudId)
+ private fun createLinkedAccounts(contactsWithAssociatedPhoneNumbers: Map<String, String>?) {
+ fun hasLinkedAccount(id: String) : Boolean {
+ var hasLinkedAccount = false
+ val where = ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?"
+ val params = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id)
+
+ val rawContactUri = ContactsContract.Data.CONTENT_URI
+ .buildUpon()
+ .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
+ .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
+ .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
+ .appendQueryParameter(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat")
+ .build()
+
+ val rawContactsCursor = context.contentResolver.query(
+ rawContactUri,
+ null,
+ where,
+ params,
+ null
+ )
+
+ if (rawContactsCursor != null) {
+ if (rawContactsCursor.count > 0) {
+ hasLinkedAccount = true
+ Log.d(TAG, "contact with id $id already has a linked account")
+ } else {
+ hasLinkedAccount = false
+ }
+ rawContactsCursor.close()
}
+ return hasLinkedAccount
}
- }
- private fun update(uniqueId: String, cloudId: String) {
- val lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, uniqueId)
- val lookupContactUri = ContactsContract.Contacts.lookupContact(context.contentResolver, lookupUri)
- val contactCursor = context.contentResolver.query(
- lookupContactUri,
- null,
- null,
- null,
- null)
+ fun createLinkedAccount(lookupKey: String, cloudId: String) {
+ val lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey)
+ val lookupContactUri = ContactsContract.Contacts.lookupContact(context.contentResolver, lookupUri)
+ val contactCursor = context.contentResolver.query(
+ lookupContactUri,
+ null,
+ null,
+ null,
+ null)
- if (contactCursor != null) {
- if (contactCursor.count > 0) {
- contactCursor.moveToFirst()
+ if (contactCursor != null) {
+ if (contactCursor.count > 0) {
+ contactCursor.moveToFirst()
- val id = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts._ID))
-
- val phonesCursor = context.contentResolver.query(
- ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
- null,
- ContactsContract.Data.CONTACT_ID + " = " + id,
- null,
- null)
-
- val numbers = mutableListOf<String>()
- if (phonesCursor != null) {
- while (phonesCursor.moveToNext()) {
- numbers.add(phonesCursor.getString(phonesCursor.getColumnIndex(NUMBER)))
+ val id = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts._ID))
+ if(hasLinkedAccount(id)){
+ return
}
- phonesCursor.close()
- }
+ val numbers = getPhoneNumbersFromDeviceContact(id)
+ val displayName = getDisplayNameFromDeviceContact(id)
- var displayName: String? = null
-
- val whereName = ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?"
- val whereNameParams = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id)
- val nameCursor = context.contentResolver.query(
- ContactsContract.Data.CONTENT_URI,
- null,
- whereName,
- whereNameParams,
- ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME)
- if (nameCursor != null) {
- while (nameCursor.moveToNext()) {
- displayName = nameCursor.getString(nameCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME))
+ if (displayName == null) {
+ return
}
- nameCursor.close()
- }
- if (displayName == null) {
- return
- }
+ val ops = ArrayList<ContentProviderOperation>()
+ val rawContactsUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().build()
+ val dataUri = ContactsContract.Data.CONTENT_URI.buildUpon().build()
+
+ ops.add(ContentProviderOperation
+ .newInsert(rawContactsUri)
+ .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
+ .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
+ .withValue(ContactsContract.RawContacts.AGGREGATION_MODE,
+ ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT)
+ .withValue(ContactsContract.RawContacts.SYNC2, cloudId)
+ .build())
+ ops.add(ContentProviderOperation
+ .newInsert(dataUri)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
+ .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, numbers[0])
+ .build())
+ ops.add(ContentProviderOperation
+ .newInsert(dataUri)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
+ .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName)
+ .build())
+ ops.add(ContentProviderOperation
+ .newInsert(dataUri)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+ .withValue(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat")
+ .withValue(ContactsContract.Data.DATA1, cloudId)
+ .withValue(ContactsContract.Data.DATA2, String.format(context.resources.getString(R
+ .string.nc_phone_book_integration_chat_via), accountName))
+ .build())
+
+ try {
+ context.contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)
+ } catch (e: OperationApplicationException) {
+ Log.e(javaClass.simpleName, "", e)
+ } catch (e: RemoteException) {
+ Log.e(javaClass.simpleName, "", e)
+ }
- // update entries
- val ops = ArrayList<ContentProviderOperation>()
- val rawContactsUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon()
- .build()
- val dataUri = ContactsContract.Data.CONTENT_URI.buildUpon()
- .build()
-
- ops.add(ContentProviderOperation
- .newInsert(rawContactsUri)
- .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
- .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
- .withValue(ContactsContract.RawContacts.AGGREGATION_MODE,
- ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT)
- .withValue(ContactsContract.RawContacts.SYNC2, cloudId)
- .build())
- ops.add(ContentProviderOperation
- .newInsert(dataUri)
- .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
- .withValue(NUMBER, numbers[0])
- .build())
- ops.add(ContentProviderOperation
- .newInsert(dataUri)
- .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
- .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName)
- .build())
- ops.add(ContentProviderOperation
- .newInsert(dataUri)
- .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- .withValue(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat")
- .withValue(ContactsContract.Data.DATA1, cloudId)
- .withValue(ContactsContract.Data.DATA2, "Chat via " + context.resources.getString(R.string.nc_app_name))
- .build())
-
- try {
- context.contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)
- } catch (e: OperationApplicationException) {
- Log.e(javaClass.simpleName, "", e)
- } catch (e: RemoteException) {
- Log.e(javaClass.simpleName, "", e)
+ Log.d(TAG, "added new entry for contact $displayName (cloudId: $cloudId | lookupKey: $lookupKey" +
+ " | id: $id)")
}
+ contactCursor.close()
}
+ }
- contactCursor.close()
+ if (contactsWithAssociatedPhoneNumbers != null && contactsWithAssociatedPhoneNumbers.isNotEmpty()) {
+ for (contact in contactsWithAssociatedPhoneNumbers) {
+ val lookupKey = contact.key
+ val cloudId = contact.value
+ createLinkedAccount(lookupKey, cloudId)
+ }
+ } else {
+ Log.d(TAG, "no contacts with linked Talk Accounts found. No linked accounts created.")
}
}
- private fun deleteAssociation(id: String) {
- Log.d("Contact", "Delete associated: $id")
-
- val rawContactUri = ContactsContract.RawContacts.CONTENT_URI
- .buildUpon()
- .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
- .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
- .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
- .build()
+ private fun getDisplayNameFromDeviceContact(id: String?): String? {
+ var displayName:String? = null
+ val whereName = ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?"
+ val whereNameParams = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id)
+ val nameCursor = context.contentResolver.query(
+ ContactsContract.Data.CONTENT_URI,
+ null,
+ whereName,
+ whereNameParams,
+ ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME)
+ if (nameCursor != null) {
+ while (nameCursor.moveToNext()) {
+ displayName = nameCursor.getString(nameCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME))
+ }
+ nameCursor.close()
+ }
+ return displayName
+ }
+ private fun getPhoneNumbersFromDeviceContact(id: String?): MutableList<String> {
+ val numbers = mutableListOf<String>()
+ val phonesNumbersCursor = context.contentResolver.query(
+ ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
+ null,
+ ContactsContract.Data.CONTACT_ID + " = " + id,
+ null,
+ null)
- val count = context.contentResolver.delete(rawContactUri, ContactsContract.RawContacts.SYNC2 + " LIKE \"" + id + "\"", null)
- Log.d("Contact", "deleted $count for id $id")
+ if (phonesNumbersCursor != null) {
+ while (phonesNumbersCursor.moveToNext()) {
+ numbers.add(phonesNumbersCursor.getString(phonesNumbersCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)))
+ }
+ phonesNumbersCursor.close()
+ }
+ if(numbers.size > 0){
+ Log.d(TAG, "Found ${numbers.size} phone numbers for contact with id $id")
+ }
+ return numbers
}
companion object {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 68066ecfa..97d8f9b49 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -354,6 +354,8 @@
<string name="nc_settings_phone_book_integration_phone_number_dialog_invalid">Invalid phone number</string>
<string name="nc_settings_phone_book_integration_phone_number_dialog_success">Phone number set successfully</string>
<string name="no_phone_book_integration_due_to_permissions">No phone book integration due to missing permissions</string>
+ <string name="nc_phone_book_integration_chat_via">Chat via %s</string>
+ <string name="nc_phone_book_integration_account_not_found">Account not found</string>
<!-- Non-translatable strings -->
<string name="path_password_strike_through" translatable="false"