Android

Entorno

Compilación AOSP

Documentación clave

Tips varios

Librerías

Tutoriales

Snippets

Estructuras de datos

Sobre Diseño

Mapas

Tu clave es:

0V21y2J7NRVSVKA...5C9Ag

Esta clave es válida para todas las aplicaciones firmadas con el certificado cuya huella dactilar sea:

C2:EC:22:8F:...:56:39

Incluimos un diseño xml de ejemplo para que puedas iniciarte por los senderos de la creación de mapas:

<com.google.android.maps.MapView
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:apiKey="0V21y2J7NRVSVKA...5C9Ag"
   />

Depurar

Añadir el atributo siguiente al bloque “application”:

android:debuggable="true"

Solicitar el permiso siguiente:

<uses-permission android:name="android.permission.SET_DEBUG_APP" />

Logcat filtrado

Ejemplo de una expresión de filtrado que suprime (silencia) todos los mensajes de log excepto los que tienen el tag “ActivityManager” con una prioridad Info o superior, y los que tienen el tag “MyApp” con una prioridad Debug o superior:

$ adb logcat ActivityManager:I MyApp:D *:S

Las prioridades de log son:

Dumpsys y Dumpstate

Hace un volcado de los datos del sistema. Viene bien para encontrar componentes instalados (Aplicaciones, ContentProvider’s, etc.):

$ adb shell dumpsys
$ adb shell dumpstate

Buscar CallerID

import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;

Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode("+34976010101"));
Cursor c = getContentResolver().query(uri, new String[]{ContactsContract.PhoneLookup.DISPLAY_NAME}, null, null, null);

Manejo de URI’s de contactos

La API de Contacts de Android es muy potente y compleja. Los conceptos básicos se describen aquí.

Se manejan dos tipos de URI’s:

URI de la tabla raíz Contacts

import android.net.Uri;
import android.provider.ContactsContract;
Uri contactsUri = ContactsContract.Contacts.CONTENT_URI;

URI de la subtabla RawContacts

import android.net.Uri;
import android.provider.ContactsContract;
Uri rawContactsUri = ContactsContract.RawContacts.CONTENT_URI;

URI de la subtabla Data

import android.net.Uri;
import android.provider.ContactsContract;
Uri datasUri = ContactsContract.Data.CONTENT_URI;

URI de la subtabla Data sólo con los registros de un RawContact concreto

import android.net.Uri;
import android.provider.ContactsContract;
// Tenemos en rawContactUri el URI de un RawContact. Será del tipo content://com.android.contacts/raw_contacts/_id
Uri datasUri = Uri.withAppendedPath(rawContactUri, RawContacts.Entity.CONTENT_DIRECTORY);

Obtener ID a partir de una URI de tipo item

Por ejemplo el método para insertar un RawContact devuelve una URI de tipo item (no tabla). Para obtener el ID de dicho elemento podemos usar algunas funciones que hay en la clase ‘‘android.content.ContentUris’’.

import android.content.ContentUris;
long rawContactId = ContentUris.parseId(rawContactUri);

Obtener la URI de tipo item de un elemento concreto a partir de la tabla

Por ejemplo tenemos el ID de un elemento y queremos una URI para él.

import android.net.Uri;
import android.provider.ContactsContract;
import android.content.ContentUris;
Uri rawContactUri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId);

Resumiendo:

Significado URI (string) URI (constant)
URI de toda la tabla RawContacts content://com.android.contacts/raw_contacts Uri rawContactsUri = android.provider.ContactsContract.RawContacts.CONTENT_URI
URI de un registro concreto de RawContacts content://com.android.contacts/raw_contacts/<_id> Uri rawContactUri = ContentUris.withAppendedId(android.provider.ContactsContract.RawContacts.CONTENT_URI, rawContactId);
URI de una subtabla (subdirectorio lo llaman en la documentación) con los Datas asociados a un RawContact concreto content://com.android.contacts/raw_contacts/<_id>/entity Uri datasUri = Uri.withAppendedPath(rawContactUri, RawContacts.Entity.CONTENT_DIRECTORY);

Obtener el ID de un recurso en el ContentProvider de audio externo

String outText = "";
Uri externalMediaUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String[] projection = new String[] {android.provider.BaseColumns._ID};
String where = android.provider.MediaStore.MediaColumns.TITLE + "=?";
String[] whereArgs = new String[] { "Space Oddity" };
Cursor c = managedQuery(externalMediaUri, projection, where, whereArgs, null);
while (c.moveToNext()) {
    for (int i = 0; i < c.getColumnCount(); i++) {
        outText += c.getColumnName(i) + "= " + c.getString(i) + "\n";
    }
}

Inserción de un RawContact de tipo “vnd.sec.contact.phone”

ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
Builder builder = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);
builder.withValue(RawContacts.ACCOUNT_NAME, "vnd.sec.contact.phone");
builder.withValue(RawContacts.ACCOUNT_TYPE, "vnd.sec.contact.phone");
operationList.add(builder.build());

builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0);
builder.withValue(ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
builder.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, "Felipito");
operationList.add(builder.build());

builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0);
builder.withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
builder.withValue(CommonDataKinds.Phone.TYPE, CommonDataKinds.Phone.TYPE_MAIN);
builder.withValue(CommonDataKinds.Phone.NUMBER, "976010101");
operationList.add(builder.build());

builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0);
builder.withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 75, stream);
builder.withValue(CommonDataKinds.Photo.PHOTO, stream.toByteArray());
builder.withValue(RawContacts.Data.IS_SUPER_PRIMARY, 1);
operationList.add(builder.build());

try {
    getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
} catch (RemoteException e) {
    e.printStackTrace();
} catch (OperationApplicationException e) {
    e.printStackTrace();
}

Extraer el nombre, el número y la foto de los Contacts de tipo “vnd.sec.contact.phone”

Uri rawContactsUri = ContactsContract.RawContacts.CONTENT_URI;
String[] projection = new String[] { ContactsContract.RawContacts._ID };
String where = ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND "
        + ContactsContract.RawContacts.ACCOUNT_NAME + "=? AND "
        + ContactsContract.RawContacts.DELETED + "=?";
String[] whereArgs = new String[] { "vnd.sec.contact.phone", "vnd.sec.contact.phone", "0" };
String sortOrder = "display_name ASC";
Cursor c = managedQuery(rawContactsUri, projection, where, whereArgs, sortOrder);
outText += rawContactsUri.toString() + "\n";
outText += c.getCount() + "\n";
while (c.moveToNext()) {
    int colPos = c.getColumnIndex(ContactsContract.RawContacts._ID);
    long rawContactId = Long.parseLong(c.getString(colPos));
    Uri rawContactUri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI,
            rawContactId);
    Uri datasUri = Uri.withAppendedPath(rawContactUri, RawContacts.Entity.CONTENT_DIRECTORY);
    String name = "";
    String number = "";
    Bitmap bitmap = null;

    String[] projection1 = new String[] { CommonDataKinds.StructuredName.DISPLAY_NAME };
    String where1 = Data.MIMETYPE + "=?";
    String[] whereArgs1 = new String[] { CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE };
    Cursor c1 = managedQuery(datasUri, projection1, where1, whereArgs1, null);
    if (c1.moveToNext()) {
        name = c1.getString(c1.getColumnIndex(CommonDataKinds.StructuredName.DISPLAY_NAME));
        c1.close();
    }

    String[] projection2 = new String[] { CommonDataKinds.Phone.NUMBER };
    String where2 = Data.MIMETYPE + "=?";
    String[] whereArgs2 = new String[] { CommonDataKinds.Phone.CONTENT_ITEM_TYPE };
    Cursor c2 = managedQuery(datasUri, projection2, where2, whereArgs2, null);
    if (c2.moveToNext()) {
        number = c2.getString(c2.getColumnIndex(CommonDataKinds.Phone.NUMBER));
        c2.close();
    }

    String[] projection3 = new String[] { CommonDataKinds.Photo.PHOTO };
    String where3 = Data.MIMETYPE + "=?";
    String[] whereArgs3 = new String[] { CommonDataKinds.Photo.CONTENT_ITEM_TYPE };
    Cursor c3 = managedQuery(datasUri, projection3, where3, whereArgs3, null);
    if (c3.moveToNext()) {
        byte[] photo = c3.getBlob(c3.getColumnIndex(CommonDataKinds.Photo.PHOTO));
        bitmap = BitmapFactory.decodeByteArray(photo, 0, photo.length);
        c3.close();
    }

    outText += "ID= " + rawContactId + "; Nombre= " + name + "; Número= " + number;
    if (bitmap != null) {
        outText += "; Ancho foto= " + bitmap.getWidth() + "; Alto foto= " + bitmap.getHeight();
    }
    outText += "\n";
}
c.close();

Borrado de los contactos de un tipo

Uri rawContacts = ContactsContract.RawContacts.CONTENT_URI;
String where = ContactsContract.RawContacts.ACCOUNT_TYPE + "=?";
String[] whereArgs = new String[] { "es.eduardofilo.app" };
getContentResolver().delete(rawContacts, where, whereArgs);

Borrado de un contacto

Uri rawContacts = ContactsContract.RawContacts.CONTENT_URI;
Uri rawContactUri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, 13);
getContentResolver().delete(rawContactUri, null, null);

Actualizar el nombre de un contacto

long rawContactId = 14;
Uri data = ContactsContract.Data.CONTENT_URI;
ContentValues values = new ContentValues();
values.put(CommonDataKinds.StructuredName.DISPLAY_NAME, "Jorgito");
String where = Data.MIMETYPE + "=? AND " + ContactsContract.Data.RAW_CONTACT_ID + "=?";
String[] whereArgs = new String[] { CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, Long.toString(rawContactId) };
getContentResolver().update(data, values, where, whereArgs);

Inserción de un RawContact de tipo “vnd.sec.contact.phone”

ContentValues values = new ContentValues();
values.put(RawContacts.ACCOUNT_TYPE, "vnd.sec.contact.phone");
values.put(RawContacts.ACCOUNT_NAME, "vnd.sec.contact.phone");
Uri rawContactUri = getContentResolver().insert(RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);

values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
values.put(CommonDataKinds.StructuredName.DISPLAY_NAME, "Pepito");
Uri data1Uri = getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);
values.clear();

values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
values.put(CommonDataKinds.Phone.TYPE, CommonDataKinds.Phone.TYPE_MAIN);
values.put(CommonDataKinds.Phone.NUMBER, "976010101");
Uri data2Uri = getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);

Inserción de un RawContact de tipo “vnd.sec.contact.phone”

Igual que antes pero hecho con el sistema de operaciones por lotes de los ContentProvider’s.

ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
Builder builder = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);
builder.withValue(RawContacts.ACCOUNT_NAME, "vnd.sec.contact.phone");
builder.withValue(RawContacts.ACCOUNT_TYPE, "vnd.sec.contact.phone");
operationList.add(builder.build());

builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0);
builder.withValue(ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
builder.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, "Debianito");
operationList.add(builder.build());

builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0);
builder.withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
builder.withValue(CommonDataKinds.Phone.TYPE, CommonDataKinds.Phone.TYPE_MAIN);
builder.withValue(CommonDataKinds.Phone.NUMBER, "976010101");
operationList.add(builder.build());

try {
    getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
} catch (RemoteException e) {
    e.printStackTrace();
} catch (OperationApplicationException e) {
    e.printStackTrace();
}

Localizar un Launcher que no sea el nuestro

PackageManager pm = getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
List<ResolveInfo> launchers = pm.queryIntentActivities(intent, 0);
String packageName = null;
String name = null;
for (ResolveInfo ri : launchers) {
    if (!ri.activityInfo.name.equals("es.eduardofilo.app.mobile.IdentificarTerminal")) {
        name = ri.activityInfo.name;
        packageName = ri.activityInfo.packageName;
        break;
    }
}

Intent myIntent = new Intent();
myIntent.setClassName(packageName, name);
startActivity(myIntent);

Obtener un authToken de un Account

mAccountManager = AccountManager.get(this);
Account[] accounts = mAccountManager.getAccountsByType(ACCOUNT_TYPE);
for (Account account : accounts) {
    outText += account.toString() + "\n";
    AccountManagerFuture<Bundle> accountManagerFuture = mAccountManager.getAuthToken(account,
                    "es.eduardofilo.app", true, null, null);
    Bundle authTokenBundle;
    String token = null;
    try {
        authTokenBundle = accountManagerFuture.getResult();
        token = authTokenBundle.get(AccountManager.KEY_AUTHTOKEN).toString();
    } catch (OperationCanceledException e) {
        e.printStackTrace();
    } catch (AuthenticatorException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    if (!TextUtils.isEmpty(token)) {
        outText += token + "\n";
    }
}