diff --git a/src/org/kde/kdeconnect/Helpers/ContactsHelper.java b/src/org/kde/kdeconnect/Helpers/ContactsHelper.java index a6b94907..4dec8850 100644 --- a/src/org/kde/kdeconnect/Helpers/ContactsHelper.java +++ b/src/org/kde/kdeconnect/Helpers/ContactsHelper.java @@ -1,418 +1,410 @@ /* * Copyright 2014 Albert Vaca Cintora * Copyright 2018 Simon Redman * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.kde.kdeconnect.Helpers; import android.annotation.TargetApi; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.provider.ContactsContract; import android.provider.ContactsContract.PhoneLookup; import android.util.Base64; import android.util.Base64OutputStream; import android.util.Log; import org.json.JSONArray; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; public class ContactsHelper { @TargetApi(Build.VERSION_CODES.HONEYCOMB) /** * Lookup the name and photoID of a contact given a phone number */ public static Map phoneNumberLookup(Context context, String number) { //Log.e("PhoneNumberLookup", number); Map contactInfo = new HashMap<>(); Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); Cursor cursor = null; try { cursor = context.getContentResolver().query( uri, new String[] { PhoneLookup.DISPLAY_NAME, ContactsContract.PhoneLookup.PHOTO_URI /*, PhoneLookup.TYPE , PhoneLookup.LABEL , PhoneLookup.ID */ }, null, null, null); } catch (Exception e) { return contactInfo; } // 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)); } try { cursor.close(); } catch (Exception e) {} if (!contactInfo.isEmpty()) { return contactInfo; } } return contactInfo; } public static String photoId64Encoded(Context context, String photoId) { if (photoId == null) { return ""; } Uri photoUri = Uri.parse(photoId); InputStream input = null; Base64OutputStream output= null; try { ByteArrayOutputStream encodedPhoto = new ByteArrayOutputStream(); output = new Base64OutputStream(encodedPhoto, Base64.DEFAULT); input = context.getContentResolver().openInputStream(photoUri); byte[] buffer = new byte[1024]; int len; while ((len = input.read(buffer)) != -1) { output.write(buffer, 0, len); } return encodedPhoto.toString(); } catch (Exception ex) { Log.e("ContactsHelper", ex.toString()); return ""; } finally { try { input.close(); } catch(Exception ignored) { }; try { output.close(); } catch(Exception ignored) { }; } } /** * Return all the NAME_RAW_CONTACT_IDS which contribute an entry to a Contact in the database - * + *

* If the user has, for example, joined several contacts, on the phone, the IDs returned will * be representative of the joined contact - * + *

* 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 getAllContactRawContactIDs(Context context) - { + public static List getAllContactRawContactIDs(Context context) { ArrayList toReturn = new ArrayList(); // Define the columns we want to read from the Contacts database - final String[] projection = new String[] { - ContactsContract.Contacts.NAME_RAW_CONTACT_ID + final String[] projection = new String[]{ + ContactsContract.Contacts.NAME_RAW_CONTACT_ID }; Uri contactsUri = ContactsContract.Contacts.CONTENT_URI; Cursor contactsCursor = context.getContentResolver().query( contactsUri, projection, null, null, null); - if (contactsCursor != null && contactsCursor.moveToFirst()) - { + if (contactsCursor != null && contactsCursor.moveToFirst()) { do { Long contactID; int idIndex = contactsCursor.getColumnIndex(ContactsContract.Contacts.NAME_RAW_CONTACT_ID); if (idIndex != -1) { contactID = contactsCursor.getLong(idIndex); } else { // Something went wrong with this contact // TODO: Investigate why this would happen Log.e("ContactsHelper", "Got a contact which does not have a NAME_RAW_CONTACT_ID"); continue; } toReturn.add(contactID); } while (contactsCursor.moveToNext()); - try { contactsCursor.close(); } catch (Exception e) {} + try { + contactsCursor.close(); + } catch (Exception e) { + } } return toReturn; } /** * Return a mapping of raw contact IDs to a map of the requested data from the Contacts database - * + *

* If for some reason there is no row associated with the raw contact ID in the database, * there will not be a corresponding field in the returned map * - * @param context android.content.Context running the request - * @param IDs collection of raw contact IDs to look up + * @param context android.content.Context running the request + * @param IDs collection of raw contact IDs to look up * @param contactsProjection List of column names to extract, defined in ContactsContract.Contacts * @return mapping of raw contact IDs to desired values, which are a mapping of column names to the data contained there */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) // Needed for Cursor.getType(..) - public static Map> getColumnsFromContactsForRawContactIDs(Context context, Set IDs, String[] contactsProjection) - { + public static Map> getColumnsFromContactsForRawContactIDs(Context context, Set IDs, String[] contactsProjection) { HashMap> toReturn = new HashMap<>(); // Define the columns we want to read from the RawContacts database - final String[] rawContactsProjection = new String[] { + final String[] rawContactsProjection = new String[]{ ContactsContract.RawContacts._ID, ContactsContract.RawContacts.CONTACT_ID }; Uri rawContactsUri = ContactsContract.RawContacts.CONTENT_URI; Uri contactsUri = ContactsContract.Contacts.CONTENT_URI; Cursor rawContactsCursor = context.getContentResolver().query( rawContactsUri, rawContactsProjection, null, null, null); - if (rawContactsCursor != null && rawContactsCursor.moveToFirst()) - { + if (rawContactsCursor != null && rawContactsCursor.moveToFirst()) { do { Long rawContactID; Long contactID; int rawContactIDIndex = rawContactsCursor.getColumnIndex(ContactsContract.RawContacts._ID); if (rawContactIDIndex != -1) { rawContactID = rawContactsCursor.getLong(rawContactIDIndex); } else { // This raw contact didn't have an ID? Something is very wrong. // TODO: Investigate why this would happen Log.e("ContactsHelper", "Got a raw contact which does not have an _ID"); continue; } // Filter only for the rawContactIDs we were asked to look up - if (! IDs.contains(rawContactID)) - { + if (!IDs.contains(rawContactID)) { // This should be achievable (and faster) by providing a selection // and selectionArgs when fetching rawContactsCursor, but I can't // figure that out continue; } int contactIDIndex = rawContactsCursor.getColumnIndex(ContactsContract.RawContacts.CONTACT_ID); if (contactIDIndex != -1) { contactID = rawContactsCursor.getLong(contactIDIndex); } else { // Something went wrong with this contact // TODO: Investigate why this would happen Log.e("ContactsHelper", "Got a raw contact which does not have a CONTACT_ID"); continue; } // Filter on only the contact we are interested in final String contactsSelection = ContactsContract.Contacts._ID + " == ? "; - final String[] contactsArgs = new String[] {contactID.toString()}; + final String[] contactsArgs = new String[]{contactID.toString()}; Cursor contactsCursor = context.getContentResolver().query( contactsUri, contactsProjection, contactsSelection, contactsArgs, null ); Map requestedData = new HashMap<>(); - if (contactsCursor != null && contactsCursor.moveToFirst()) - { + if (contactsCursor != null && contactsCursor.moveToFirst()) { // For each column, collect the data from that column - for (String column : contactsProjection) - { + for (String column : contactsProjection) { int index = contactsCursor.getColumnIndex(column); // Since we might be getting various kinds of data, Object is the best we can do Object data; int type; if (index == -1) { // This raw contact didn't have an ID? Something is very wrong. // TODO: Investigate why this would happen Log.e("ContactsHelper", "Got a raw contact which does not have an _ID"); continue; } type = contactsCursor.getType(index); - switch(type) { + switch (type) { case Cursor.FIELD_TYPE_INTEGER: data = contactsCursor.getInt(index); break; case Cursor.FIELD_TYPE_FLOAT: data = contactsCursor.getFloat(index); break; case Cursor.FIELD_TYPE_STRING: data = contactsCursor.getString(index); break; case Cursor.FIELD_TYPE_BLOB: data = contactsCursor.getBlob(index); break; default: Log.e("ContactsHelper", "Got an undefined type of column " + column); continue; } requestedData.put(column, data); } } contactsCursor.close(); toReturn.put(rawContactID, requestedData); } while (rawContactsCursor.moveToNext()); rawContactsCursor.close(); } return toReturn; } /** * Return a mapping of raw contact IDs to a map of the requested data from the Data database - * + *

* If for some reason there is no row associated with the raw contact ID in the database, * there will not be a corresponding field in the returned map - * + *

* For some types of data, there may be many entries in the Data database with the same raw contact ID, * so a list of the relevant data is returned * - * @param context android.content.Context running the request - * @param IDs collection of raw contact IDs to look up - * @param dataMimetype Mimetype of the column to look up, defined in ContactsContract.CommonDataKinds..CONTENT_ITEM_TYPE + * @param context android.content.Context running the request + * @param IDs collection of raw contact IDs to look up + * @param dataMimetype Mimetype of the column to look up, defined in ContactsContract.CommonDataKinds..CONTENT_ITEM_TYPE * @param dataProjection List of column names to extract, defined in ContactsContract.CommonDataKinds. * @return mapping of raw contact IDs to desired values, which are a mapping of column names to the data contained there */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) // Needed for Cursor.getType(..) - public static Map>> getColumnsFromDataForRawContactIDs(Context context, Set IDs, String dataMimetype, String[] dataProjection) - { + public static Map>> getColumnsFromDataForRawContactIDs(Context context, Set IDs, String dataMimetype, String[] dataProjection) { HashMap>> toReturn = new HashMap<>(); // Define a filter for the type of data we were asked to get final String dataSelection = ContactsContract.Data.MIMETYPE + " == ?"; final String[] dataSelectionArgs = {dataMimetype}; Uri dataUri = ContactsContract.Data.CONTENT_URI; // Regardless of what the user requested, we need the RAW_CONTACT_ID field // This will not be returned to the user if it wasn't asked for Set actualDataProjectionSet = new HashSet(); actualDataProjectionSet.addAll(Arrays.asList(dataProjection)); actualDataProjectionSet.add(ContactsContract.Data.RAW_CONTACT_ID); String[] actualDataProjection = new String[0]; actualDataProjection = actualDataProjectionSet.toArray(actualDataProjection); Cursor dataCursor = context.getContentResolver().query( dataUri, actualDataProjection, dataSelection, dataSelectionArgs, null); - if (dataCursor != null && dataCursor.moveToFirst()) - { + if (dataCursor != null && dataCursor.moveToFirst()) { do { Long rawContactID; Map requestedData = new HashMap<>(); int rawContactIDIndex = dataCursor.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID); if (rawContactIDIndex != -1) { rawContactID = dataCursor.getLong(rawContactIDIndex); } else { // This didn't have a RAW_CONTACT_ID? Something is very wrong. // TODO: Investigate why this would happen Log.e("ContactsHelper", "Got a data contact which does not have a RAW_CONTACT_ID"); continue; } // Filter only for the rawContactIDs we were asked to look up - if (! IDs.contains(rawContactID)) - { + if (!IDs.contains(rawContactID)) { // This should be achievable (and faster) by providing a selection // and selectionArgs when fetching dataCursor, but I can't // figure that out continue; } // For each column, collect the data from that column for (String column : dataProjection) { int index = dataCursor.getColumnIndex(column); // Since we might be getting various kinds of data, Object is the best we can do Object data; int type; if (index == -1) { // This raw contact didn't have an ID? Something is very wrong. // TODO: Investigate why this would happen Log.e("ContactsHelper", "Got a raw contact which does not have an _ID"); continue; } type = dataCursor.getType(index); switch (type) { case Cursor.FIELD_TYPE_INTEGER: data = dataCursor.getInt(index); break; case Cursor.FIELD_TYPE_FLOAT: data = dataCursor.getFloat(index); break; case Cursor.FIELD_TYPE_STRING: data = dataCursor.getString(index); break; case Cursor.FIELD_TYPE_BLOB: data = dataCursor.getBlob(index); break; default: - Log.w("ContactsHelper", "Got an undefined type of column " + column + " -- Skipping"); + Log.w("ContactsHelper", "Got an undefined type of column " + column + " -- Skipping"); continue; } requestedData.put(column, data); } // If we have not already stored some data for this contact, make a new list - if (!toReturn.containsKey(rawContactID)) - { + if (!toReturn.containsKey(rawContactID)) { toReturn.put(rawContactID, new ArrayList>()); } toReturn.get(rawContactID).add(requestedData); } while (dataCursor.moveToNext()); dataCursor.close(); } return toReturn; } } diff --git a/src/org/kde/kdeconnect/Plugins/ContactsPlugin/ContactsPlugin.java b/src/org/kde/kdeconnect/Plugins/ContactsPlugin/ContactsPlugin.java index 869ba486..eefffa8e 100644 --- a/src/org/kde/kdeconnect/Plugins/ContactsPlugin/ContactsPlugin.java +++ b/src/org/kde/kdeconnect/Plugins/ContactsPlugin/ContactsPlugin.java @@ -1,480 +1,452 @@ /* * ContactsPlugin.java - This file is part of KDE Connect's Android App * Implement a way to request and send contact information * * Copyright 2018 Simon Redman * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.kde.kdeconnect.Plugins.ContactsPlugin; import android.Manifest; import android.provider.ContactsContract; import android.util.Log; import org.json.JSONArray; import org.kde.kdeconnect.Helpers.ContactsHelper; import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect_tp.R; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; public class ContactsPlugin extends Plugin { /** * Used to request the device send the unique ID of every contact */ public static final String PACKAGE_TYPE_CONTACTS_REQUEST_ALL_UIDS = "kdeconnect.contacts.request_all_uids"; /** * Used to request the names for the contacts corresponding to a list of UIDs - * + *

* It shall contain the key "uids", which will have a list of uIDs (long int, as string) */ public static final String PACKAGE_TYPE_CONTACTS_REQUEST_NAMES_BY_UIDS = "kdeconnect.contacts.request_names_by_uid"; /** * Used to request the phone numbers for the contacts corresponding to a list of UIDs - * + *

* It shall contain the key "uids", which will have a list of uIDs (long int, as string) */ public static final String PACKAGE_TYPE_CONTACTS_REQUEST_PHONES_BY_UIDS = "kdeconnect.contacts.request_phones_by_uid"; /** * Used to request the email addresses for the contacts corresponding to a list of UIDs - * + *

* It shall contain the key "uids", which will have a list of uIDs (long int, as string) */ public static final String PACKAGE_TYPE_CONTACTS_REQUEST_EMAILS_BY_UIDS = "kdeconnect.contacts.request_emails_by_uid"; /** * Response indicating the package contains a list of contact uIDs - * + *

* It shall contain the key "uids", which will mark a list of uIDs (long int, as string) * The returned IDs can be used in future requests for more information about the contact */ public static final String PACKAGE_TYPE_CONTACTS_RESPONSE_UIDS = "kdeconnect.contacts.response_uids"; /** * Response indicating the package contains a list of contact names - * + *

* It shall contain the key "uids", which will mark a list of uIDs (long int, as string) * then, for each UID, there shall be a field with the key of that UID and the value of the name of the contact - * + *

* For example: * ( 'uids' : ['1', '3', '15'], - * '1' : 'John Smith', - * '3' : 'Abe Lincoln', - * '15' : 'Mom' ) + * '1' : 'John Smith', + * '3' : 'Abe Lincoln', + * '15' : 'Mom' ) */ public static final String PACKAGE_TYPE_CONTACTS_RESPONSE_NAMES = "kdeconnect.contacts.response_names"; /** * Response indicating the package contains a list of contact numbers - * + *

* It shall contain the key "uids", which will mark a list of uIDs (long int, as string) * then, for each UID, there shall be a 3-field list containing the phone number, the type, and the label - * + *

* For now, the values in types are undefined, but coincidentally match the list here: * https://developer.android.com/reference/android/provider/ContactsContract.CommonDataKinds.Phone.html - * + *

* The label field is defined to be the custom label if the number is a custom type, otherwise the empty string - * + *

* For example: * ( 'uids' : ['1', '3', '15'], - * '1' : [ [ '+221234', '2', '' ] ] - * '3' : [ [ '+1(222)333-4444', '0', 'Big Red Button' ] ] // This number has a custom type - * '15' : [ [ '6061234', '1', '' ] ] ) + * '1' : [ [ '+221234', '2', '' ] ] + * '3' : [ [ '+1(222)333-4444', '0', 'Big Red Button' ] ] // This number has a custom type + * '15' : [ [ '6061234', '1', '' ] ] ) */ public static final String PACKAGE_TYPE_CONTACTS_RESPONSE_PHONES = "kdeconnect.contacts.response_phones"; /** * Response indicating the package contains a list of contact email addresses - * + *

* It shall contain the key "uids", which will mark a list of uIDs (long int, as string) * then, for each UID, there shall be a 3-field list containing the email address, the type, and the label - * + *

* For now, the values in types are undefined, but coincidentally match the list here: * https://developer.android.com/reference/android/provider/ContactsContract.CommonDataKinds.Email.html - * + *

* The label field is defined to be the custom label if the number is a custom type, otherwise the empty string - * + *

* For example: * ( 'uids' : ['1', '3', '15'], - * '1' : [ [ 'john@example.com', '2', '' ] ] - * '3' : [ [ 'abel@example.com', '0', 'Priority' ] ] // This email address has a custom type - * '15' : [ [ 'mom@example.com', '1', '' ] ] ) + * '1' : [ [ 'john@example.com', '2', '' ] ] + * '3' : [ [ 'abel@example.com', '0', 'Priority' ] ] // This email address has a custom type + * '15' : [ [ 'mom@example.com', '1', '' ] ] ) */ public static final String PACKAGE_TYPE_CONTACTS_RESPONSE_EMAILS = "kdeconnect.contacts.response_emails"; private int contactsPermissionExplanation = R.string.contacts_permission_explanation; @Override public String getDisplayName() { return context.getResources().getString(R.string.pref_plugin_contacts); } @Override public String getDescription() { return context.getResources().getString(R.string.pref_plugin_contacts_desc); } @Override public String[] getSupportedPackageTypes() { - return new String[] { + return new String[]{ PACKAGE_TYPE_CONTACTS_REQUEST_ALL_UIDS, PACKAGE_TYPE_CONTACTS_REQUEST_NAMES_BY_UIDS, PACKAGE_TYPE_CONTACTS_REQUEST_PHONES_BY_UIDS, PACKAGE_TYPE_CONTACTS_REQUEST_EMAILS_BY_UIDS }; } @Override public String[] getOutgoingPackageTypes() { - return new String[] { + return new String[]{ PACKAGE_TYPE_CONTACTS_RESPONSE_UIDS, PACKAGE_TYPE_CONTACTS_RESPONSE_NAMES, PACKAGE_TYPE_CONTACTS_RESPONSE_PHONES, PACKAGE_TYPE_CONTACTS_RESPONSE_EMAILS }; } @Override - public boolean onCreate() - { + public boolean onCreate() { permissionExplanation = contactsPermissionExplanation; return true; } @Override /** * Since this plugin could leak sensitive information, probably best to leave disabled by default */ - public boolean isEnabledByDefault() - { + public boolean isEnabledByDefault() { return false; } @Override public String[] getRequiredPermissions() { return new String[]{Manifest.permission.READ_CONTACTS}; // One day maybe we will also support WRITE_CONTACTS, but not yet } /** * Return a unique identifier (long int) for all contacts in the Contacts database - * + *

* The identifiers returned can be used in future requests to get more information * about the contact * * @param np The package containing the request * @return true if successfully handled, false otherwise */ protected boolean handleRequestAllUIDs(NetworkPackage np) { NetworkPackage reply = new NetworkPackage(PACKAGE_TYPE_CONTACTS_RESPONSE_UIDS); List uIDs = ContactsHelper.getAllContactRawContactIDs(context); List uIDsAsStrings = new ArrayList(uIDs.size()); - for (Long uID : uIDs) - { + for (Long uID : uIDs) { uIDsAsStrings.add(uID.toString()); } reply.set("uids", uIDsAsStrings); device.sendPackage(reply); return true; } protected boolean handleRequestNamesByUIDs(NetworkPackage np) { - if (!np.has("uids")) - { + if (!np.has("uids")) { Log.e("ContactsPlugin", "handleRequestNamesByUIDs received a malformed packet with no uids key"); return false; } List uIDsAsStrings = np.getStringList("uids"); // Convert to Set to call getColumnsFromContactsForRawContactIDs Set uIDs = new HashSet(uIDsAsStrings.size()); - for (String uID : uIDsAsStrings) - { + for (String uID : uIDsAsStrings) { uIDs.add(Long.parseLong(uID)); } - final String[] contactsProjection = new String[] { + final String[] contactsProjection = new String[]{ ContactsContract.Contacts.DISPLAY_NAME_PRIMARY }; Map> uIDsToNames = ContactsHelper.getColumnsFromContactsForRawContactIDs(context, uIDs, contactsProjection); // ContactsHelper.getColumnsFromContactsForRawContactIDs(..) is allowed to reply without // some of the requested uIDs if they were not in the database, so update our list uIDsAsStrings = new ArrayList<>(uIDsToNames.size()); NetworkPackage reply = new NetworkPackage(PACKAGE_TYPE_CONTACTS_RESPONSE_NAMES); // Add the names to the packet - for (Long uID : uIDsToNames.keySet()) - { + for (Long uID : uIDsToNames.keySet()) { Map data = uIDsToNames.get(uID); String name; - if (! data.containsKey(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY)) { + if (!data.containsKey(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY)) { // This contact apparently does not have a name Log.w("ContactsPlugin", "Got a uID " + uID + " which does not have a name"); continue; } - name = (String)data.get(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY); + name = (String) data.get(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY); // Store this as a valid uID uIDsAsStrings.add(uID.toString()); // Add the uid : name pairing to the packet reply.set(uID.toString(), name); } // Add the valid uIDs to the packet reply.set("uids", uIDsAsStrings); device.sendPackage(reply); return true; } protected boolean handleRequestPhonesByUIDs(NetworkPackage np) { - if (!np.has("uids")) - { + if (!np.has("uids")) { Log.e("ContactsPlugin", "handleRequestPhonesByUIDs received a malformed packet with no uids key"); return false; } List uIDsAsStrings = np.getStringList("uids"); // Convert to Set to call getColumnsFromDataForRawContactIDs Set uIDs = new HashSet(uIDsAsStrings.size()); - for (String uID : uIDsAsStrings) - { + for (String uID : uIDsAsStrings) { uIDs.add(Long.parseLong(uID)); } final String dataMimetype = ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE; final String[] dataProjection = { // We want the actual phone number ContactsContract.CommonDataKinds.Phone.NUMBER // As well as what type it is , ContactsContract.CommonDataKinds.Phone.TYPE // Stores the label of the type of the number if it is a custom type , ContactsContract.CommonDataKinds.Phone.LABEL}; Map>> uIDsToPhones = ContactsHelper.getColumnsFromDataForRawContactIDs(context, uIDs, dataMimetype, dataProjection); // ContactsHelper.getColumnsFromContactsForRawContactIDs(..) is allowed to reply without // some of the requested uIDs if they were not in the database, so update our list uIDsAsStrings = new ArrayList<>(uIDsToPhones.size()); NetworkPackage reply = new NetworkPackage(PACKAGE_TYPE_CONTACTS_RESPONSE_PHONES); // Add the phone numbers to the packet - for (Long uID : uIDsToPhones.keySet()) - { + for (Long uID : uIDsToPhones.keySet()) { List> allPhoneNumbers = new ArrayList<>(); - for (Map data : uIDsToPhones.get(uID)) - { + for (Map data : uIDsToPhones.get(uID)) { HashMap numberTypesToNumbers = new HashMap<>(); String number; Integer type; String label; // Label appears to only be defined if type is custom if (!data.containsKey(ContactsContract.CommonDataKinds.Phone.NUMBER)) { // This is the wrong data type? Log.w("ContactsPlugin", "Got a uID " + uID + " which does not have a phone number"); continue; } number = (String) data.get(ContactsContract.CommonDataKinds.Phone.NUMBER); // The Android docs say type should be an int // However, my phone has that field stored as a string... Object typeField = data.get(ContactsContract.CommonDataKinds.Phone.TYPE); - if (typeField instanceof String) - { - type = Integer.parseInt((String)typeField); - } - else if (typeField instanceof Integer){ + if (typeField instanceof String) { + type = Integer.parseInt((String) typeField); + } else if (typeField instanceof Integer) { type = (Integer) typeField; - } else - { + } else { Log.w("ContactsPlugin", "Android docs are wrong -- cannot get Java type of 'type' field"); continue; // Continue in case something works... } - if (type == ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM) - { + if (type == ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM) { // If the label is defined, use it label = (String) data.get(ContactsContract.CommonDataKinds.Phone.LABEL); - } - else - { + } else { // Otherwise, use the empty string label = ""; } allPhoneNumbers.add(Arrays.asList(new String[]{number, type.toString(), label})); } // Store this as a valid uID uIDsAsStrings.add(uID.toString()); // Add the uid : [ ['number', 'type', 'label'] ... ] pairing to the packet reply.set(uID.toString(), new JSONArray(allPhoneNumbers)); } // Add the valid uIDs to the packet reply.set("uids", uIDsAsStrings); device.sendPackage(reply); return true; } protected boolean handleRequestEmailsByUIDs(NetworkPackage np) { - if (!np.has("uids")) - { + if (!np.has("uids")) { Log.e("ContactsPlugin", "handleRequestEmailsByUIDs received a malformed packet with no uids key"); return false; } List uIDsAsStrings = np.getStringList("uids"); // Convert to Set to call getColumnsFromDataForRawContactIDs Set uIDs = new HashSet(uIDsAsStrings.size()); - for (String uID : uIDsAsStrings) - { + for (String uID : uIDsAsStrings) { uIDs.add(Long.parseLong(uID)); } final String dataMimetype = ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE; final String[] dataProjection = { // We want the actual phone number ContactsContract.CommonDataKinds.Email.ADDRESS // As well as what type it is , ContactsContract.CommonDataKinds.Email.TYPE // Stores the label of the type of the number if it is a custom type , ContactsContract.CommonDataKinds.Email.LABEL}; Map>> uIDsToEmails = ContactsHelper.getColumnsFromDataForRawContactIDs(context, uIDs, dataMimetype, dataProjection); // ContactsHelper.getColumnsFromContactsForRawContactIDs(..) is allowed to reply without // some of the requested uIDs if they were not in the database, so update our list uIDsAsStrings = new ArrayList<>(uIDsToEmails.size()); NetworkPackage reply = new NetworkPackage(PACKAGE_TYPE_CONTACTS_RESPONSE_EMAILS); // Add the email addresses to the packet - for (Long uID : uIDsToEmails.keySet()) - { + for (Long uID : uIDsToEmails.keySet()) { List> allEmailAddresses = new ArrayList<>(); - for (Map data : uIDsToEmails.get(uID)) - { + for (Map data : uIDsToEmails.get(uID)) { HashMap numberTypesToNumbers = new HashMap<>(); String email; Integer type; String label; // Label appears to only be defined if type is custom if (!data.containsKey(ContactsContract.CommonDataKinds.Email.ADDRESS)) { // This is the wrong data mimetype? Log.w("ContactsPlugin", "Got a uID " + uID + " which does not have an email address"); continue; } email = (String) data.get(ContactsContract.CommonDataKinds.Email.ADDRESS); // The Android docs say type should be an int // However, my phone has that field stored as a string... Object typeField = data.get(ContactsContract.CommonDataKinds.Email.TYPE); - if (typeField instanceof String) - { - type = Integer.parseInt((String)typeField); - } - else if (typeField instanceof Integer){ + if (typeField instanceof String) { + type = Integer.parseInt((String) typeField); + } else if (typeField instanceof Integer) { type = (Integer) typeField; - } else - { + } else { Log.w("ContactsPlugin", "Android docs are wrong -- cannot get Java type of 'type' field"); continue; // Continue in case something works... } - if (type == ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM) - { + if (type == ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM) { // If the label is defined, use it label = (String) data.get(ContactsContract.CommonDataKinds.Email.LABEL); - } - else - { + } else { // Otherwise, use the empty string label = ""; } allEmailAddresses.add(Arrays.asList(new String[]{email, type.toString(), label})); } // Store this as a valid uID uIDsAsStrings.add(uID.toString()); // Add the uid : [ ['number', 'type', 'label'] ... ] pairing to the packet reply.set(uID.toString(), new JSONArray(allEmailAddresses)); } // Add the valid uIDs to the packet reply.set("uids", uIDsAsStrings); device.sendPackage(reply); return true; } @Override public boolean onPackageReceived(NetworkPackage np) { - if (np.getType().equals(PACKAGE_TYPE_CONTACTS_REQUEST_ALL_UIDS)) - { + if (np.getType().equals(PACKAGE_TYPE_CONTACTS_REQUEST_ALL_UIDS)) { return this.handleRequestAllUIDs(np); } else if (np.getType().equals(PACKAGE_TYPE_CONTACTS_REQUEST_NAMES_BY_UIDS)) { return this.handleRequestNamesByUIDs(np); } else if (np.getType().equals(PACKAGE_TYPE_CONTACTS_REQUEST_PHONES_BY_UIDS)) { return this.handleRequestPhonesByUIDs(np); - } else if (np.getType().equals(PACKAGE_TYPE_CONTACTS_REQUEST_EMAILS_BY_UIDS)){ + } else if (np.getType().equals(PACKAGE_TYPE_CONTACTS_REQUEST_EMAILS_BY_UIDS)) { return this.handleRequestEmailsByUIDs(np); - } else - { + } else { Log.e("ContactsPlugin", "Contacts plugin received an unexpected packet!"); return false; } } }