Android¶
Entorno¶
Compilación AOSP¶
Documentación clave¶
Tips varios¶
Librerías¶
- RoboGuice la librería que exprime nuestro código
- OrmLite - Lightweight Object Relational Mapping (ORM) Java Package
- Mobile Framework that uses the actual features of #HTML5, #CSS3 and #JavaScript
Tutoriales¶
- Android Development Tutorial (Lars Vogel)
- Android ListView and ListActivity (Lars Vogel)
- Pasando información entre Actividades (Activity) en Android
- Avoid memory leaks on Android
Snippets¶
Estructuras de datos¶
- Android DataFramework, creando la estructura de la Base de Datos
- Usando Android DataFramework en nuestro proyecto
Sobre Diseño¶
- Hello, Views
- Common Layout Objects
- Android Interaction Desing Patterns :!:
- Icon Design Guidelines
- Widget Design Guidelines
- Android App Developers GUI Kits, Icons, Fonts and Tools
- Android R Drawable Explorer
- 20 herramientas útiles para Desarrolladores Android
- Android Design
- El cambio de tendencia en la UI de Android: el Menú Lateral
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:
- V — Verbose (lowest priority)
- D — Debug
- I — Info
- W — Warning
- E — Error
- F — Fatal
- S — Silent (highest priority, on which nothing is ever printed)
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's de tipo tabla: Apuntan por ejemplo a una tabla completa como puede ser la de RawContacts.
- URI's de tipo item: Apuntan a un registro concreto de una tabla, por ejemplo un RawContact.
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";
}
}