android - studio - Obtenga información de contacto específica del URI devuelto por Intent.ACTION_PICK
seleccionar contactos android studio (3)
Estoy escribiendo una aplicación de 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 debería ser opcional, es decir, no será un requisito que el padre / tutor 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 el código para iniciar una nueva intención para elegir el contacto específico (usando Intent.ACTION_PICK). Luego obtengo un URI que representa un Contacto específico en la base de datos.
Lamentablemente, no sé cuál es el próximo paso. Parece que esto debería ser lo más simple del mundo, pero aparentemente no. He leído la documentación en el sitio web para desarrolladores de Android y he consultado más de un libro de Android. Sin alegría.
La información específica que me gustaría obtener es:
El nombre del contacto (primero y último por separado si es posible)
La dirección de correo electrónico del contacto (principal)
El número de teléfono celular del contacto
Imagino que esto debería ser posible mediante consultas utilizando ContentResolver, pero no tengo idea de cómo hacerlo con el URI devuelto por Intent. La mayoría de la documentación supone que tiene la ID de contacto, no el URI del contacto. Además, no tengo idea de qué tipo de campos puedo poner en la proyección para la consulta, suponiendo que esta sea la forma correcta de hacer lo que quiero.
Aquí está mi código de inicio:
// 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 muchas excavaciones, encontré lo que creo que son las respuestas. Las soluciones que encontré difieren según el nivel de API de Android que estés usando. Sin embargo, no son nada bonitos, 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 por Intent.ACTION_PICK. Mientras estemos aquí, también deberíamos 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 necesitaremos 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 desde este URI son la mayoría de los campos representados por las constantes en la clase ContactsContract.Profile (incluidas las constantes heredadas de otras interfaces). No están incluidos PHOTO_FILE_ID, PHOTO_THUMBNAIL_URI o PHOTO_URI (pero PHOTO_ID está incluido).
Ahora que tenemos la ID, necesitamos obtener los datos relevantes. La primera (y 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 formato, al que se accede usando las constantes en ContactsContract.Contacts.Entity . Por lo general, solo le interesarán RAW_CONTACT_ID, DATA1 y MIMETYPE. Sin embargo, si desea los nombres y apellidos por separado, el tipo Nombre MIME tiene el primer nombre en DATA2 y el apellido en DATA3.
Cargue 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 se presentaron 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 (al momento de escribir esto). Si lo intenta, obtendrá una excepción IllegalArgumentException del 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, de esta manera se generarán muchas consultas de bases de datos separadas, por lo que no se recomienda por razones de eficiencia.
La solución que he encontrado es probar la versión que usa consultas de Entidad, atrapar 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 formas de hacerlo, soy todo oídos.
Como resultado, hay una mejor manera de hacer esto.
Como mencioné, la clase ContactsContract.Contacts.Entity no estuvo disponible hasta la API 11. Sin embargo, la clase ContactsContract.Data estaba disponible en la API 5, y puedes usar esa clase en gran parte de la misma manera que usas la clase Entity .
He actualizado mi código. Es muy similar al código de la clase Entity, y funciona básicamente de la misma manera. Sin embargo, lo probé con mi teléfono con Gingerbread, y funciona bien.
Un cambio que tuve que hacer fue esto: no parece haber una manera de obtener ContactsContract.Data.RAW_CONTACT_ID
de la consulta inicial, y esa ID no es la misma que la ID que obtienes, por ejemplo, ContactsContract.Contacts._ID
. En su lugar, consulté sobre la constante ContactsContract.Contacts.DISPLAY_NAME
, que es consistente en casi 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 de nivel 9), realizar una consulta en el proveedor de contactos (como el que se muestra arriba) requiere que su aplicación declare el permiso READ_CONTACTS (consulte Seguridad y permisos). Sin embargo, a partir de Android 2.3, la aplicación Contactos / Personas otorga a su aplicación un permiso temporal para leer desde el Proveedor de Contactos cuando le devuelve un resultado. El permiso temporal se aplica solo al contacto específico solicitado, por lo que no puede consultar a un contacto que no sea el especificado por el Uri del intento, a menos que declare el permiso READ_CONTACTS.