Obtenga información de contacto específica de la URI devuelta por Intent.ACTION_PICK

Estoy escribiendo una aplicación para Android que tiene un tipo de datos que representa a una persona (específicamente, el padre o tutor de un niño). Me gustaría poder "importar" los campos de datos relevantes de la base de datos de contactos en el dispositivo Android. (Esto debe ser opcional, es decir, no será un requisito que el padre / guardián ya esté en la base de datos de contactos, ni la base de datos de contactos se actualizará si agregan nuevos padres / tutores).

Hasta ahora, he escrito código para iniciar un nuevo intento de elegir el contacto específico (con Intent.ACTION_PICK). A continuación, obtener un URI que representa un contacto específico en la base de datos.

Desafortunadamente, no sé cuál es el siguiente paso. Parece que esto debe ser la cosa más simple en el mundo para hacer, pero aparentemente no. He leído la documentación del sitio web de desarrolladores de Android y he visto más de un libro de Android. Sin alegría.

La información específica que me gustaría obtener, es:

  1. El nombre del contacto (primero y último por separado si es posible)

  2. Dirección de correo electrónico del contacto (principal)

  3. Número de teléfono celular del contacto

Me imagino que esto debería ser posible mediante la consulta utilizando el ContentResolver, pero no tengo idea de cómo hacer esto con el URI devuelto por el intento. La mayor parte de la documentación asume que tiene el ID de contacto, no el URI del contacto. Además, no tengo idea de qué tipo de campos puedo poner en la proyección de la consulta, suponiendo que esta es incluso la forma correcta de hacer lo que quiero.

Aquí está mi código inicial:

// In a button's onClick event handler: Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI); startActivityForResult(intent, PICK_CONTACT); // In onActivityResult: if (resultCode == RESULT_OK) { if (requestCode == PICK_CONTACT) { contactURI = data.getData(); // NOW WHAT? } } 

De acuerdo, después de mucha excavación, encontré lo que creo que son las respuestas. Las soluciones que encontré difieren según el nivel de API de Android que utilices. Sin embargo, no son bastante en absoluto, así que si hay mejores soluciones, me encantaría saber.

En cualquier caso, el primer paso es obtener el ID del contacto, haciendo una consulta en el URI devuelto de Intent.ACTION_PICK. Mientras estamos aquí, también debemos obtener el nombre para mostrar y la cadena que representa si el contacto tiene un número de teléfono o no. (No los necesitaremos para la solución moderna, pero los necesitamos para la solución heredada.)

 String id, name, phone, hasPhone; int idx; Cursor cursor = getContentResolver().query(contactUri, null, null, null, null); if (cursor.moveToFirst()) { idx = cursor.getColumnIndex(ContactsContract.Contacts._ID); id = cursor.getString(idx); idx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME); name = cursor.getString(idx); idx = cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER); hasPhone = cursor.getString(idx); } 

Para el registro, las columnas devueltas de este URI son la mayoría de los campos representados por constantes en la clase ContactsContract.Profile (incluidas las constantes heredadas de otras interfaces). No se incluyen PHOTO_FILE_ID, PHOTO_THUMBNAIL_URI o PHOTO_URI (pero se incluye PHOTO_ID).

Ahora que tenemos la identificación, necesitamos obtener los datos relevantes. La primera (y la más simple) solución es consultar una Entidad. Las consultas de entidades recuperan todos los datos de contactos de un contacto o contacto sin procesar a la vez. Cada fila representa un único contacto sin procesar, al que se accede mediante las constantes de ContactsContract.Contacts.Entity . Normalmente sólo se ocupará de RAW_CONTACT_ID, DATA1 y MIMETYPE. Sin embargo, si desea el nombre y los apellidos por separado, el tipo de nombre MIME contiene el primer nombre en DATA2 y el apellido en DATA3.

Se carga las variables haciendo coincidir la columna MIMETYPE con las constantes de ContactsContract.CommonDataKinds ; Por ejemplo, el tipo MIME de correo electrónico está en ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE .

 // Build the Entity URI. Uri.Builder b = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, id).buildUpon(); b.appendPath(ContactsContract.Contacts.Entity.CONTENT_DIRECTORY); URI contactUri = b.build(); // Create the projection (SQL fields) and sort order. String[] projection = { ContactsContract.Contacts.Entity.RAW_CONTACT_ID, ContactsContract.Contacts.Entity.DATA1, ContactsContract.Contacts.Entity.MIMETYPE }; String sortOrder = ContactsContract.Contacts.Entity.RAW_CONTACT_ID + " ASC"; cursor = getContentResolver().query(contactUri, projection, null, null, sortOrder); String mime; int mimeIdx = cursor.getColumnIndex(ContactsContract.Contacts.Entity.MIMETYPE); int dataIdx = cursor.getColumnIndex(ContactsContract.Contacts.Entity.DATA1); if (cursor.moveToFirst()) { do { mime = cursor.getString(mimeIdx); if (mime.equalsIgnoreCase(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)) { email = cursor.getString(dataIdx); } if (mime.equalsIgnoreCase(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) { phone = cursor.getString(dataIdx); } // ...etc. } while (cursor.moveToNext()); } 

Desafortunadamente, las entidades no fueron introducidas hasta la API 11 (Android 3.0, Honeycomb), lo que significa que este código es incompatible con aproximadamente el 65% de los dispositivos Android en el mercado (a partir de este escrito). Si lo intentas, obtendrás una IllegalArgumentException de la URI.

La segunda solución es crear una cadena de consulta y realizar una consulta para cada tipo de datos que desee utilizar:

 // Get phone number - if they have one if ("1".equalsIgnoreCase(hasPhone)) { cursor = getContentResolver().query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = "+ id, null, null); if (cursor.moveToFirst()) { colIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER); phone = cursor.getString(colIdx); } cursor.close(); } // Get email address cursor = getContentResolver().query( ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + id, null, null); if (cursor.moveToFirst()) { colIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS); email = cursor.getString(colIdx); } cursor.close(); // ...etc. 

Obviamente, esta manera dará lugar a una gran cantidad de consultas de base de datos por separado, por lo que no es recomendable por razones de eficiencia.

La solución que he encontrado es probar la versión que usa consultas de Entity, capturar la IllegalArgumentException y poner el código heredado dentro del bloque catch:

 try { // Build the Entity URI. Uri.Builder b = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, id).buildUpon(); b.appendPath(ContactsContract.Contacts.Entity.CONTENT_DIRECTORY); // ...etc... } catch (IllegalArgumentException e) { // Get phone number - if they have one if ("1".equalsIgnoreCase(hasPhone)) { // ...etc... } finally { // If you want to display the info in GUI objects, put the code here } 

Espero que esto ayude a alguien. Y, de nuevo, si hay mejores maneras de hacer esto, soy todo oídos.

Como resulta, hay una mejor manera de hacer esto.

Como mencioné, la clase ContactsContract.Contacts.Entity no estaba disponible hasta la API 11. Sin embargo, la clase ContactsContract.Data estaba disponible en la API 5, y puede utilizar esa clase en gran parte de la misma manera que utiliza la clase Entity .

He actualizado mi código. Es muy similar al código de la clase Entity, y funciona en gran medida de la misma manera. Sin embargo, lo he probado con mi teléfono con Gingerbread, y funciona bien.

Un cambio que tuve que hacer es esto: No parece haber una manera de obtener el ContactsContract.Data.RAW_CONTACT_ID de la consulta inicial, y ese ID no es el mismo que el ID que obtiene de, por ejemplo, ContactsContract.Contacts._ID . En su lugar, consultaba la constante ContactsContract.Contacts.DISPLAY_NAME , que es consistente en prácticamente todas las clases de ContactsContract.

Aquí está el código de trabajo:

  Cursor cursor; // Cursor object String mime; // MIME type int dataIdx; // Index of DATA1 column int mimeIdx; // Index of MIMETYPE column int nameIdx; // Index of DISPLAY_NAME column // Get the name cursor = getContentResolver().query(params[0], new String[] { ContactsContract.Contacts.DISPLAY_NAME }, null, null, null); if (cursor.moveToFirst()) { nameIdx = cursor.getColumnIndex( ContactsContract.Contacts.DISPLAY_NAME); name = cursor.getString(nameIdx); // Set up the projection String[] projection = { ContactsContract.Data.DISPLAY_NAME, ContactsContract.Contacts.Data.DATA1, ContactsContract.Contacts.Data.MIMETYPE }; // Query ContactsContract.Data cursor = getContentResolver().query( ContactsContract.Data.CONTENT_URI, projection, ContactsContract.Data.DISPLAY_NAME + " = ?", new String[] { name }, null); if (cursor.moveToFirst()) { // Get the indexes of the MIME type and data mimeIdx = cursor.getColumnIndex( ContactsContract.Contacts.Data.MIMETYPE); dataIdx = cursor.getColumnIndex( ContactsContract.Contacts.Data.DATA1); // Match the data to the MIME type, store in variables do { mime = cursor.getString(mimeIdx); if (ContactsContract.CommonDataKinds.Email .CONTENT_ITEM_TYPE.equalsIgnoreCase(mime)) { email = cursor.getString(dataIdx); } if (ContactsContract.CommonDataKinds.Phone .CONTENT_ITEM_TYPE.equalsIgnoreCase(mime)) { phone = cursor.getString(dataIdx); phone = PhoneNumberUtils.formatNumber(phone); } } while (cursor.moveToNext()); } } 
 //Add a permission to read contacts data to your application manifest. <uses-permission android:name="android.permission.READ_CONTACTS"/> //Use Intent.ACTION_PICK in your Activity Intent contactPickerIntent = new Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI); startActivityForResult(contactPickerIntent, RESULT_PICK_CONTACT); //Then Override the onActivityResult() and retrieve the ID,Phone number and Name in the data. @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // check whether the result is ok if (resultCode == RESULT_OK) { // Check for the request code, we might be usign multiple startActivityForReslut switch (requestCode) { case RESULT_PICK_CONTACT: Cursor cursor = null; try { String phoneNo = null ; String name = null; Uri uri = data.getData(); cursor = getContentResolver().query(uri, null, null, null, null); cursor.moveToFirst(); int phoneIndex =cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER); phoneNo = cursor.getString(phoneIndex); textView2.setText(phoneNo); } catch (Exception e) { e.printStackTrace(); } break; } } else { Log.e("MainActivity", "Failed to pick contact"); } } 

Nota: Antes de Android 2.3 (API nivel 9), realizar una consulta en el proveedor de contactos (como el que se muestra arriba) requiere que la aplicación declare el permiso READ_CONTACTS (consulte Seguridad y permisos). Sin embargo, a partir de Android 2.3, la aplicación Contactos / Personas concede a tu aplicación un permiso temporal para leer del proveedor de contactos cuando te devuelve un resultado. El permiso temporal se aplica sólo al contacto específico solicitado, por lo que no puede consultar un contacto distinto del especificado por el Uri de la intención, a menos que declare el permiso READ_CONTACTS.

  • ¿Cómo encontrar y recopilar toda la información disponible en un contacto de dar en cualquier teléfono de Android?
  • Obtener dirección desde la tabla "canonical_addresses"
  • Compartir y persistir datos entre varias aplicaciones de Android
  • Cómo cargar un URI con el prefijo "content: //" usando Glide Android?
  • ¿Es posible falsificar un SMS entrante en Android?
  • ¿Cuándo debo llamar a close () en SQLiteOpenHelper utilizado por ContentProvider
  • ¿Cómo puedo capturar el proveedor de contenido inicializar?
  • Imponga los permisos de propiedad de artículos en el proveedor de contenido
  • anular el registro de las llamadas de difusión antes de onReceive () listenens
  • Especificación de límite / desplazamiento para consultas de ContentProvider
  • ContentProvider con varias tablas
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.