[network/kdeconnect-android] src/main/java/org/kde/kdeconnect: refactor: migrate `ContactsHelper` to Kotlin
Simon Redman <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit e4a5f9a36b78f7059aade4296ac5f70a7b5ac27d by Simon Redman, on behalf of ShellWen Chen. Committed on 17/08/2026 at 21:56. Pushed by sredman into branch 'master'. refactor: migrate `ContactsHelper` to Kotlin D +0 -380 src/main/java/org/kde/kdeconnect/helpers/ContactsHelper.java A +344 -0 src/main/java/org/kde/kdeconnect/helpers/ContactsHelper.kt M +4 -4 src/main/java/org/kde/kdeconnect/plugins/sms/SMSPlugin.kt M +11 -13 src/main/java/org/kde/kdeconnect/plugins/telephony/TelephonyPlugin.kt https://invent.kde.org/network/kdeconnect-android/-/commit/e4a5f9a36b78f7059aade4296ac5f70a7b5ac27d diff --git a/src/main/java/org/kde/kdeconnect/helpers/ContactsHelper.java b/src/main/java/org/kde/kdeconnect/helpers/ContactsHelper.java deleted file mode 100644 index 754c876d4..000000000 --- a/src/main/java/org/kde/kdeconnect/helpers/ContactsHelper.java +++ /dev/null @@ -1,380 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <[email protected]> - * SPDX-FileCopyrightText: 2018 Simon Redman <[email protected]> - * - * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL -*/ - -package org.kde.kdeconnect.helpers; - -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.provider.ContactsContract; -import android.provider.ContactsContract.PhoneLookup; -import android.util.Base64; -import android.util.Base64OutputStream; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.apache.commons.io.IOUtils; - -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import kotlin.text.Charsets; - -public class ContactsHelper { - - static final String LOG_TAG = "ContactsHelper"; - - /** - * Lookup the name and photoID of a contact given a phone number - */ - public static Map<String, String> phoneNumberLookup(Context context, String number) { - - Map<String, String> contactInfo = new HashMap<>(); - - Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); - String[] columns = new String[]{ - PhoneLookup.DISPLAY_NAME, - PhoneLookup.PHOTO_URI - /*, PhoneLookup.TYPE - , PhoneLookup.LABEL - , PhoneLookup.ID */ - }; - try (Cursor cursor = context.getContentResolver().query(uri, columns,null, null, null)) { - // Take the first match only - if (cursor != null && cursor.moveToFirst()) { - int nameIndex = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME); - if (nameIndex != -1) { - contactInfo.put("name", cursor.getString(nameIndex)); - } - - nameIndex = cursor.getColumnIndex(PhoneLookup.PHOTO_URI); - if (nameIndex != -1) { - contactInfo.put("photoID", cursor.getString(nameIndex)); - } - } - } catch (Exception ignored) { - } - return contactInfo; - } - - public static String photoId64Encoded(Context context, String photoId) { - if (photoId == null) { - return ""; - } - Uri photoUri = Uri.parse(photoId); - - ByteArrayOutputStream encodedPhoto = new ByteArrayOutputStream(); - try (InputStream input = context.getContentResolver().openInputStream(photoUri); - Base64OutputStream output = new Base64OutputStream(encodedPhoto, Base64.DEFAULT)) { - IOUtils.copy(input, output, 1024); - return encodedPhoto.toString(); - } catch (Exception ex) { - Log.e(LOG_TAG, ex.toString()); - return ""; - } - } - - /** - * Return all the NAME_RAW_CONTACT_IDS which contribute an entry to a Contact in the database - * <p> - * If the user has, for example, joined several contacts, on the phone, the IDs returned will - * be representative of the joined contact - * <p> - * See here: https://developer.android.com/reference/android/provider/ContactsContract.Contacts.html - * for more information about the connection between contacts and raw contacts - * - * @param context android.content.Context running the request - * @return List of each NAME_RAW_CONTACT_ID in the Contacts database - */ - public static List<uID> getAllContactContactIDs(Context context) { - ArrayList<uID> toReturn = new ArrayList<>(); - - // Define the columns we want to read from the Contacts database - final String[] columns = new String[]{ - ContactsContract.Contacts.LOOKUP_KEY - }; - - Uri contactsUri = ContactsContract.Contacts.CONTENT_URI; - try (Cursor contactsCursor = context.getContentResolver().query(contactsUri, columns, null, null, null)) { - if (contactsCursor != null && contactsCursor.moveToFirst()) { - do { - uID contactID; - - int idIndex = contactsCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY); - if (idIndex != -1) { - contactID = new uID(contactsCursor.getString(idIndex)); - } else { - // Something went wrong with this contact - // If you are experiencing this, please open a bug report indicating how you got here - Log.e(LOG_TAG, "Got a contact which does not have a LOOKUP_KEY"); - continue; - } - - if (!toReturn.contains(contactID)) { - toReturn.add(contactID); - } - } while (contactsCursor.moveToNext()); - } - } - - return toReturn; - } - - /** - * Get VCards using serial database lookups. This is tragically slow, so call only when needed. - * - * There is a faster API specified using ContactsContract.Contacts.CONTENT_MULTI_VCARD_URI, - * but there does not seem to be a way to figure out which ID resulted in which VCard using that API - * - * @param context android.content.Context running the request - * @param IDs collection of uIDs to look up - * @return Mapping of uIDs to the corresponding VCard - */ - private static Map<uID, VCardBuilder> getVCardsSlow(Context context, Collection<uID> IDs) { - Map<uID, VCardBuilder> toReturn = new HashMap<>(); - - for (uID ID : IDs) { - String lookupKey = ID.toString(); - Uri vcardURI = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey); - - try (InputStream input = context.getContentResolver().openInputStream(vcardURI)) { - if (input == null) { - Log.w("Contacts", "ContentResolver did not give us a stream for the VCard for uID " + ID); - continue; - } - toReturn.put(ID, new VCardBuilder(IOUtils.toString(input, Charsets.UTF_8))); - } catch (Exception e) { - // If you are experiencing this, please open a bug report indicating how you got here - Log.e("Contacts", "Exception while fetching vcards", e); - } - } - - return toReturn; - } - - /** - * Get the VCard for every specified raw contact ID - * - * @param context android.content.Context running the request - * @param IDs collection of raw contact IDs to look up - * @return Mapping of raw contact IDs to the corresponding VCard - */ - public static Map<uID, VCardBuilder> getVCardsForContactIDs(Context context, Collection<uID> IDs) { - return getVCardsSlow(context, IDs); - } - - /** - * Get the last-modified timestamp for every contact in the database - * - * @param context android.content.Context running the request - * @return Mapping of contact uID to last-modified timestamp - */ - public static Map<uID, Long> getAllContactTimestamps(Context context) { - String[] projection = { uID.COLUMN, ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP }; - - Map<uID, Map<String, String>> databaseValues = accessContactsDatabase(context, projection, null, null, null); - - Map<uID, Long> timestamps = new HashMap<>(); - for (uID contactID : databaseValues.keySet()) { - Map<String, String> data = databaseValues.get(contactID); - timestamps.put( - contactID, - Long.parseLong(data.get(ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP)) - ); - } - - return timestamps; - } - - /** - * Get the last-modified timestamp for the specified contact - * - * @param context android.content.Context running the request - * @param contactID Contact uID to read - * @throws ContactNotFoundException If the given ID for some reason does not match a contact - * @return Last-modified timestamp of the contact - */ - public static Long getContactTimestamp(Context context, uID contactID) throws ContactNotFoundException { - String[] projection = { uID.COLUMN, ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP }; - String selection = uID.COLUMN + " = ?"; - String[] selectionArgs = { contactID.toString() }; - - Map<uID, Map<String, String>> databaseValue = accessContactsDatabase(context, projection, selection, selectionArgs, null); - - if (databaseValue.isEmpty()) { - throw new ContactNotFoundException("Querying for contact with id " + contactID + " returned no results."); - } - - if (databaseValue.size() != 1) { - Log.w(LOG_TAG, "Received an improper number of return values from the database in getContactTimestamp: " + databaseValue.size()); - } - - return Long.parseLong(databaseValue.get(contactID).get(ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP)); - } - - /** - * Return a mapping of contact IDs to a map of the requested data from the Contacts database. - * - * @param context android.content.Context running the request - * @param projection List of column names to extract, defined in ContactsContract.Contacts. Must contain uID.COLUMN - * @param selection Parameterizable filter to use with the ContentResolver query. May be null. - * @param selectionArgs Parameters for selection. May be null. - * @param sortOrder Sort order to request from the ContentResolver query. May be null. - * @return mapping of contact uIDs to desired values, which are a mapping of column names to the data contained there - */ - private static Map<uID, Map<String, String>> accessContactsDatabase( - @NonNull Context context, - @NonNull String[] projection, - @Nullable String selection, - @Nullable String[] selectionArgs, - @Nullable String sortOrder - ) { - Uri contactsUri = ContactsContract.Contacts.CONTENT_URI; - - HashMap<uID, Map<String, String>> toReturn = new HashMap<>(); - - try (Cursor contactsCursor = context.getContentResolver().query( - contactsUri, - projection, - selection, - selectionArgs, - sortOrder - )) { - if (contactsCursor != null && contactsCursor.moveToFirst()) { - do { - Map<String, String> requestedData = new HashMap<>(); - - int uIDIndex = contactsCursor.getColumnIndexOrThrow(uID.COLUMN); - uID uID = new uID(contactsCursor.getString(uIDIndex)); - - // For each column, collect the data from that column - for (String column : projection) { - int index = contactsCursor.getColumnIndex(column); - // Since we might be getting various kinds of data, Object is the best we can do - String data; - if (index == -1) { - // This contact didn't have the requested column? Something is very wrong. - // If you are experiencing this, please open a bug report indicating how you got here - Log.e(LOG_TAG, "Got a contact which does not have a requested column"); - continue; - } - data = contactsCursor.getString(index); - - requestedData.put(column, data); - } - - toReturn.put(uID, requestedData); - } while (contactsCursor.moveToNext()); - } - } - return toReturn; - } - - /** - * This is a cheap ripoff of com.android.vcard.VCardBuilder - * <p> - * Maybe in the future that library will be made public and we can switch to using that! - * <p> - * The main similarity is the usage of .toString() to produce the finalized VCard and the - * usage of .appendLine(String, String) to add stuff to the vcard - */ - public static class VCardBuilder { - static final String VCARD_END = "END:VCARD"; // Written to terminate the vcard - static final String VCARD_DATA_SEPARATOR = ":"; - - final StringBuilder vcardBody; - - /** - * Take a partial vcard as a string and make a VCardBuilder - * - * @param vcard vcard to build upon - */ - VCardBuilder(String vcard) { - // Remove the end tag. We will add it back on in .toString() - vcard = vcard.substring(0, vcard.indexOf(VCARD_END)); - - vcardBody = new StringBuilder(vcard); - } - - /** - * Appends one line with a given property name and value. - */ - public void appendLine(final String propertyName, final String rawValue) { - vcardBody.append(propertyName) - .append(VCARD_DATA_SEPARATOR) - .append(rawValue) - .append("\n"); - } - - @NonNull - public String toString() { - return vcardBody.toString() + VCARD_END; - } - } - - /** - * Essentially a typedef of the type used for a unique identifier - */ - public static class uID { - /** - * We use the LOOKUP_KEY column of the Contacts table as a unique ID, since that's what it's - * for - */ - final String contactLookupKey; - - /** - * Which Contacts column this uID is pulled from - */ - static final String COLUMN = ContactsContract.Contacts.LOOKUP_KEY; - - public uID(String lookupKey) { - - if (lookupKey == null) - throw new IllegalArgumentException("lookUpKey should not be null"); - - contactLookupKey = lookupKey; - } - - @NonNull - public String toString() { - return this.contactLookupKey; - } - - @Override - public int hashCode() { - return contactLookupKey.hashCode(); - } - - @Override - public boolean equals(Object other) { - if (other instanceof uID) { - return contactLookupKey.equals(((uID) other).contactLookupKey); - } - return contactLookupKey.equals(other); - } - } - - /** - * Exception to indicate that a specified contact was not found - */ - public static class ContactNotFoundException extends Exception { - public ContactNotFoundException(uID contactID) { - super("Unable to find contact with ID " + contactID); - } - - public ContactNotFoundException(String message) { - super(message); - } - } -} diff --git a/src/main/java/org/kde/kdeconnect/helpers/ContactsHelper.kt b/src/main/java/org/kde/kdeconnect/helpers/ContactsHelper.kt new file mode 100644 index 000000000..5312e60de --- /dev/null +++ b/src/main/java/org/kde/kdeconnect/helpers/ContactsHelper.kt @@ -0,0 +1,344 @@ +/* + * SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <[email protected]> + * SPDX-FileCopyrightText: 2018 Simon Redman <[email protected]> + * + * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL +*/ +package org.kde.kdeconnect.helpers + +import android.content.ContentResolver +import android.content.Context +import android.net.Uri +import android.provider.ContactsContract +import android.provider.ContactsContract.PhoneLookup +import android.util.Base64 +import android.util.Base64OutputStream +import android.util.Log +import java.io.ByteArrayOutputStream + +/** + * Helper object for contacts-related operations. + */ +object ContactsHelper { + /** + * The tag used for logging. + */ + private const val TAG: String = "ContactsHelper" + + data class PhoneNumberLookupResult( + val name: String? = null, + val photoId: String? = null + ) + + /** + * Lookup the name and photoID of a contact given a phone number. + * + * Only the first match is returned. + * + * @param context the context running the request. + * @param number the phone number to lookup. + * + * @return a map containing the `name` and `photoID` of the contact, or an empty map if the contact could not be found. + */ + @JvmStatic + fun phoneNumberLookup( + context: Context, + number: String + ): PhoneNumberLookupResult { + val uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)) + val columns = arrayOf( + PhoneLookup.DISPLAY_NAME, + PhoneLookup.PHOTO_URI, + ) + try { + context.contentResolver.query(uri, columns, null, null, null).use { cursor -> + // Take the first match only + if (cursor != null && cursor.moveToFirst()) { + val name = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME).takeIf { it != -1 }?.let { + cursor.getString(it) + } + val photoId = cursor.getColumnIndex(PhoneLookup.PHOTO_URI).takeIf { it != -1 }?.let { + cursor.getString(it) + } + return PhoneNumberLookupResult(name, photoId) + } + } + } catch (ignore: Exception) { + } + return PhoneNumberLookupResult() + } + + /** + * Get the base64 encoded photo for a contact. + * + * @param context the context running the request. + * @param photoId the photoId of the contact. + * + * @return the base64 encoded photo, or an empty string if the photo could not be encoded or [photoId] is null. + */ + @JvmStatic + fun photoId64Encoded( + context: Context, + photoId: String? + ): String { // TODO: Make photoId notnull, make return type nullable to tag error + if (photoId == null) { + return "" + } + val photoUri = Uri.parse(photoId) + + val encodedPhoto = ByteArrayOutputStream() + try { + context.contentResolver.openInputStream(photoUri).use { input -> + Base64OutputStream(encodedPhoto, Base64.DEFAULT).use { output -> + input!!.copyTo(output, 1024) + } + } + return encodedPhoto.toString() + } catch (ex: Exception) { + Log.e(TAG, "Error encoding photo", ex) + return "" + } + } + + /** + * Get VCards using serial database lookups. This is tragically slow, so call only when needed. + * + * There is a faster API specified using ContactsContract.Contacts.CONTENT_MULTI_VCARD_URI, + * but there does not seem to be a way to figure out which ID resulted in which VCard using that API. + * + * @param context [android.content.Context] running the request. + * @param ids collection of uIDs to look up. + * @return map of uIDs to the corresponding VCard. + */ + private fun getVCardsSlow(context: Context, ids: Collection<uID>): Map<uID, VCardBuilder> { + return ids.mapNotNull { id -> + val lookupKey = id.toString() + val vcardURI = + Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey) + + try { + context.contentResolver.openInputStream(vcardURI).use { input -> + if (input == null) { + Log.w("Contacts", "ContentResolver did not give us a stream for the VCard for uID $id") + return@mapNotNull null + } + id to VCardBuilder(input.bufferedReader().readText()) + } + } catch (e: Exception) { + Log.e("Contacts", "Exception while fetching vcards", e) + null + } + }.toMap() + } + + /** + * Get the VCard for every specified raw contact ID. + * + * @param context [android.content.Context] running the request. + * @param ids collection of raw contact IDs to look up. + * @return map of raw contact IDs to the corresponding VCard. + */ + @JvmStatic + fun getVCardsForContactIDs(context: Context, ids: Collection<uID>): Map<uID, VCardBuilder> { + return getVCardsSlow(context, ids) + } + + /** + * Get the last-modified timestamp for every contact in the database. + * + * @param context [android.content.Context] running the request. + * @return map of contact uID to last-modified timestamp. + */ + @JvmStatic + fun getAllContactTimestamps(context: Context): Map<uID, Long> { + val projection = + arrayOf(uID.COLUMN, ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP) + + val databaseValues = accessContactsDatabase(context, projection, null, null, null) + + val timestamps: Map<uID, Long> = databaseValues.keys.associateWith { contactId -> + val data = databaseValues[contactId]!! + data[ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP]!!.toLong() + } + + return timestamps + } + + /** + * Get the last-modified timestamp for the specified contact. + * + * @param context [android.content.Context] running the request. + * @param contactID contact uID to read. + * @throws ContactNotFoundException if the given ID for some reason does not match a contact. + * @return last-modified timestamp of the contact. + */ + @JvmStatic + @Throws(ContactNotFoundException::class) + fun getContactTimestamp(context: Context, contactID: uID): Long { + val projection = + arrayOf(uID.COLUMN, ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP) + val selection = uID.COLUMN + " = ?" + val selectionArgs = arrayOf(contactID.toString()) + + val databaseValue = + accessContactsDatabase(context, projection, selection, selectionArgs, null) + + if (databaseValue.isEmpty()) { + throw ContactNotFoundException("Querying for contact with id $contactID returned no results.") + } + + if (databaseValue.size != 1) { + Log.w( + TAG, + "Received an improper number of return values from the database in getContactTimestamp: ${databaseValue.size}" + ) + } + + val timestamp = + databaseValue[contactID]!![ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP]!!.toLong() + + return timestamp + } + + /** + * Return a mapping of contact IDs to a map of the requested data from the Contacts database. + * + * @param context [android.content.Context] running the request. + * @param projection list of column names to extract, defined in [ContactsContract.Contacts], must contain [uID.COLUMN]. + * @param selection parameterizable filter to use with the [ContentResolver] query. + * @param selectionArgs parameters for selection. + * @param sortOrder sort order to request from the [ContentResolver] query. + * @return map of contact uIDs to desired values, which are a mapping of column names to the data contained there. + */ + private fun accessContactsDatabase( + context: Context, + projection: Array<String>, + selection: String?, + selectionArgs: Array<String>?, + sortOrder: String? + ): Map<uID, Map<String, String>> { + val contactsUri = ContactsContract.Contacts.CONTENT_URI + + val toReturn = HashMap<uID, Map<String, String>>() + + context.contentResolver.query( + contactsUri, + projection, + selection, + selectionArgs, + sortOrder + ).use { contactsCursor -> + if (contactsCursor != null && contactsCursor.moveToFirst()) { + do { + val requestedData: MutableMap<String, String> = HashMap() + + val uIDIndex = contactsCursor.getColumnIndexOrThrow(uID.COLUMN) + val uID = uID(contactsCursor.getString(uIDIndex)!!) + + // For each column, collect the data from that column + for (column in projection) { + val index = contactsCursor.getColumnIndex(column) + if (index == -1) { + // This contact didn't have the requested column? Something is very wrong. + // If you are experiencing this, please open a bug report indicating how you got here + Log.e(TAG, "Got a contact which does not have a requested column") + continue + } + // Since we might be getting various kinds of data, Object is the best we can do + val data = contactsCursor.getString(index) + + requestedData[column] = data + } + + toReturn[uID] = requestedData + } while (contactsCursor.moveToNext()) + } + } + return toReturn + } + + /** + * This is a cheap ripoff of com.android.vcard.VCardBuilder. + * + * Maybe in the future that library will be made public and we can switch to using that! + * + * The main similarity is the usage of .toString() to produce the finalized VCard and the + * usage of .appendLine(String, String) to add stuff to the vcard. + * + * @param vcard the vcard to build upon. + */ + class VCardBuilder internal constructor(vcard: String) { + private val vcardBody: StringBuilder = StringBuilder( + // Remove the end tag. We will add it back on in .toString() + // Throws if VCARD_END is missing, so a malformed vcard fails closed rather than being silently accepted. + vcard.substring(0, vcard.indexOf(VCARD_END)) + ) + + /** + * Appends one line with a given property name and value. + * + * Please note that this method does not check the validity of the property name and value. + * So, you need to make sure that the property name and value are valid. + * + * @param propertyName the name of the property to append. + * @param rawValue the value of the property to append. + */ + fun appendLine(propertyName: String, rawValue: String) { + vcardBody.append(propertyName) + .append(VCARD_DATA_SEPARATOR) + .append(rawValue) + .append("\n") + } + + /** + * Converts the VCard to standard VCard format. + * + * Please note that this method does not check the validity of the VCard. + * So, you need to make sure that the VCard is valid. + * + * @return the VCard in standard VCard format. + */ + override fun toString(): String = vcardBody.toString() + VCARD_END + + companion object { + const val VCARD_END: String = "END:VCARD" // Written to terminate the vcard + const val VCARD_DATA_SEPARATOR: String = ":" + } + } + + /** + * Essentially a typedef of the type used for a unique identifier. + * + * @param contactLookupKey the lookup key of the contact. + * We use the LOOKUP_KEY column of the Contacts table as a unique ID, since that's what it's. + */ + @Suppress("ClassName") + class uID(val contactLookupKey: String) { + override fun toString(): String = this.contactLookupKey + + override fun hashCode(): Int = contactLookupKey.hashCode() + + override fun equals(other: Any?): Boolean { + if (other is uID) { + return contactLookupKey == other.contactLookupKey + } + return contactLookupKey == other + } + + companion object { + /** + * Which Contacts column this uID is pulled from + */ + const val COLUMN: String = ContactsContract.Contacts.LOOKUP_KEY + } + } + + /** + * Exception to indicate that a specified contact was not found. + */ + class ContactNotFoundException : Exception { + constructor(contactId: uID) : super("Unable to find contact with ID $contactId") + + constructor(message: String?) : super(message) + } +} diff --git a/src/main/java/org/kde/kdeconnect/plugins/sms/SMSPlugin.kt b/src/main/java/org/kde/kdeconnect/plugins/sms/SMSPlugin.kt index 2fdb10991..58ed8a731 100644 --- a/src/main/java/org/kde/kdeconnect/plugins/sms/SMSPlugin.kt +++ b/src/main/java/org/kde/kdeconnect/plugins/sms/SMSPlugin.kt @@ -201,15 +201,15 @@ class SMSPlugin : Plugin() { val permissionCheck: Int = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) - if (permissionCheck == PackageManager.PERMISSION_GRANTED) { - val contactInfo: Map<String, String> = ContactsHelper.phoneNumberLookup(context, phoneNumber) + if (permissionCheck == PackageManager.PERMISSION_GRANTED && phoneNumber != null) { + val result = ContactsHelper.phoneNumberLookup(context, phoneNumber) - val name = contactInfo["name"] + val name = result.name if (name != null) { np["contactName"] = name } - val photoID = contactInfo["photoID"] + val photoID = result.photoId if (photoID != null) { np["phoneThumbnail"] = ContactsHelper.photoId64Encoded(context, photoID) } diff --git a/src/main/java/org/kde/kdeconnect/plugins/telephony/TelephonyPlugin.kt b/src/main/java/org/kde/kdeconnect/plugins/telephony/TelephonyPlugin.kt index 0cb1d8c7f..098757c77 100644 --- a/src/main/java/org/kde/kdeconnect/plugins/telephony/TelephonyPlugin.kt +++ b/src/main/java/org/kde/kdeconnect/plugins/telephony/TelephonyPlugin.kt @@ -70,25 +70,23 @@ class TelephonyPlugin : Plugin() { val permissionCheck = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) - if (permissionCheck == PackageManager.PERMISSION_GRANTED) { - val contactInfo = ContactsHelper.phoneNumberLookup(context, phoneNumber) + if (permissionCheck == PackageManager.PERMISSION_GRANTED && phoneNumber != null) { + val result = ContactsHelper.phoneNumberLookup(context, phoneNumber) - val name = contactInfo["name"] + val name = result.name if (name != null) { np["contactName"] = name } - if (contactInfo.containsKey("photoID")) { - val photoUri = contactInfo["photoID"] - if (photoUri != null) { - try { - val base64photo = ContactsHelper.photoId64Encoded(context, photoUri) - if (!base64photo.isNullOrEmpty()) { - np["phoneThumbnail"] = base64photo - } - } catch (e: Exception) { - Log.e("TelephonyPlugin", "Failed to get contact photo") + val photoUri = result.photoId + if (photoUri != null) { + try { + val base64photo = ContactsHelper.photoId64Encoded(context, photoUri) + if (!base64photo.isNullOrEmpty()) { + np["phoneThumbnail"] = base64photo } + } catch (e: Exception) { + Log.e("TelephonyPlugin", "Failed to get contact photo") } } } else if (phoneNumber != null) {