Archivo de swap en disco encriptado con Manjaro

Vamos a ver cómo crear un archivo de swap dentro de una partición encriptada que hace de root, para poder hibernar un laptop.

En este caso, lo he hecho en Manjaro Linux versión 21.1.3 (Pahvo). Con encriptación completa y sistema de ficheros Btrfs. El ordenador es un laptop HP x360 modelo 13-4100ns, equipado con 4 Gb de RAM y un procesador Intel i5. Bueno, pues empececemos.

En sistemas Btrfs

En un sistema Btrfs no puedes usar un fichero normal como swap. Primero hay que prepararlo un poco. El motivo es que hay que anular el sistema de copy on write (COW) propio de Btrfs, y además hay que poner el fichero fuera del subvolumen montado como root para que Timeshift no se lie y empiece a hacer snapshots del fichero de swap. Si tienes un sistema ext4, puedes omitir esta parte y pasar a la siguiente desde «Desactivamos nuestra swap».

Primero creamos el subvolumen aparte.

lacofi@rose$ sudo btrfs subvolume create /@swapfile
[sudo] password for lacofi:
Create subvolume '//@swapfile'
lacofi@rose$ __

Ahora vamos a poner nuestro fichero de swap en el subvolumen que hemos creado, es decir, el fichero será /@swapfile/swapfile. Bien, ahora vamos a crear el fichero vacío con el COW anulado.

lacofi@rose$ sudo truncate -s 0 /@swapfile/swapfile
lacofi@rose$ sudo chattr +C /@swapfile/swapfile
lacofi@rose$ sudo btrfs property set /@swapfile/swapfile compression none 
lacofi@rose$ __

Desactivamos nuestro swap

Ahora entramos en materia. Primero comprobamos si tenemos alguna swap activa. Si la hay, la desactivamos para quedarnos sin swap.

lacofi@rose$ sudo swapon -s
Nomfich.      Tipo         Tam.    Util.        Prioridad
/dev/dm-1     partition    4384588     0                   -2
lacofi@rose$ sudo swapoff -a
lacofi@rose$ __

Creamos y activamos nueva swap en nuestro fichero. Respecto al tamaño del fichero, se recomienda un poquito más de lo que tengas de RAM. En mi caso, tengo 4 Gb de RAM, así que vendrían a ser por ejemplo 6 Gb de swap. Pero como me gustan los números redondos vamos a poner 8 Gb, el doble de mi RAM. Como sabes, 8 Gb equivalen a 8*1024=8192 Mb.

lacofi@rose$ sudo dd if=/dev/zero of=/@swapfile/swapfile bs=1M count=8192                                    
8097+0 registros leídos
8097+0 registros escritos
8489271296 bytes (8,5 GB, 7,9 GiB) copied, 94,9865 s, 89,4 MB/s
lacofi@rose$ sudo mkswap /@swapfile/swapfile                                                           
mkswap: /@swapfile/swapfile: permisos 0644 no seguros; se arregla con: chmod 0600 /@swapfile/swapfile
Configurando espacio de intercambio versión 1, tamaño = 7,9 GiB (8489267200 bytes)
sin etiqueta, UUID=e7fd9694-74e9-4bfb-b50c-4a6157345d11
lacofi@rose$ sudo chmod 600 /@swapfile/swapfile                                                                     
lacofi@rose$ sudo swapon /@swapfile/swapfile                                                                        
lacofi@rose$ __                                                                           

Y ya está. Ya tenemos nuestra nueva swap activada y funcionando. Puedes comprobarlo con un «swapon -s».

Hacer estos cambios permanentes

Obviamente, ahora querrás que tu nueva swap se active siempre, en cuanto arranques el ordenador. Para ello tienes que editar tu fichero /etc/fstab y meter la línea que configurará la swap. Si tenías de antes una línea para la vieja swap, coméntala para desactivarla.

#/dev/mapper/luks-blablabla swap swap defaults,noatime  0   0
/@swapfile/swapfile         none swap defaults          0   0

Si quieres, es recomendable comprobar que la nueva línea no da errores haciendo un «sudo mount -a».

Activar la hibernación

Vale, ahora vamos a activar la hibernación en ese swapfile. Hay que tener en cuenta que cuando el ordenador intente recuperarse de la hibernación, va a leer el swapfile lo primerito de todo, antes de cargar el sistema operativo y antes de cargar el sistema de ficheros, así que lo leerá de seguido y sin usar btrfs ni ext4. Por eso es necesario un número que se llama «offset» y que viene a ser el sector concreto del disco duro que es donde tiene que empezar a leer. Para encontrar ese offset tienes que hacer esto:

lacofi@rose$ sudo filefrag -v /@swapfile/swapfile
Filesystem type is: 9123683e
File size of /@swapfile/swapfile is 8489271296 (2072576 blocks of 4096 bytes)
ext: logical_offset:   physical_offset:    length: expected: flags:
0: 0..       259931:   1973504.. 2233435:  259932:
1: 259932..  520581:   2235648.. 2496297:  260650: 2233436:
2: 520582..  534674:   1885765.. 1899857:  14093:  2496298:
3: 534675..  534973:   1866884.. 1867182:  299:    1899858:
4: 534974..  797017:   2497792.. 2759835:  262044: 1867183:
5: 797018..  1309595:  2759936.. 3272513:  512578: 2759836:
6: 1309596.. 1539583:  3284224.. 3514211:  229988: 3272514:

Vale, intimida un poquito, pero en realidad de toda esa retahila solo nos interesa UN número que es el que he marcado en color rojo. Es en la línea 0, el tercer número por la izquierda, que es donde empieza el fichero (offset físico).

Vale, ahora tenemos que editar el fichero /etc/default/grub. Dentro de él, hay una línea que dice GRUB_CMDLINE_LINUX_DEFAULT que es la que hay que cambiar. Fíjate en ella, vamos a analizarla un poquito, que nunca viene mal. Será algo parecido a esto.

GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice=UUID=94d0287f-6c87-4003-aa6b-286993de7e77:luks-94d0287f-6c87-4003-aa6b-28
6993de7e77 root=/dev/mapper/luks-94d0287f-6c87-4003-aa6b-286993de7e77 splash apparmor=1 security=apparmor resume=/dev/m
apper/luks-97668c40-2fdd-43a0-9d2f-7831dfdf69e4 udev.log_priority=3"

Bien, vamos por parte, los argumentos que pone son estos:

quiet

La opción quiet indica al sistema que no muestre los mensajes iniciales.

cryptdevice=UUID=94d0287f-6c87-4003-aa6b-286993de7e77:luks-94d0287f-6c87-4003-aa6b-28 6993de7e77

Esta opción le dice al sistema que cuando arranque tendrá una unidad de disco con UUID 94d0287etc que es una unidad encriptada que tendra un identificador de nombre luks-94d0287etc. Esto le dice al sistema que hay un disco que tiene que desencriptar.

root=/dev/mapper/luks-94d0287f-6c87-4003-aa6b-286993de7e77

Esta opción, le dice al sistema que esa unidad que acaba de desencriptar es la unidad root (es decir, se montará como «/»).

splash apparmor=1 security=apparmor

Las opciones splash, apparmor y security no nos interesan para el tema en el que estamos, así que las ignoraremos y las dejaremos como están.

resume=/dev/mapper/luks-97668c40-2fdd-43a0-9d2f-7831dfdf69e4

Ésta. Esta es la opción que nos interesa. Está apuntando a otra unidad de disco totalmente diferente, en este caso una partición encriptada. Tenemos que cambiar esta opción para que apunte a nuestro fichero de swap. Es decir, tendrá que apuntar a nuestra partición que está montada como root, y luego ir al sector que dice el offset del fichero. Pues eso es lo que hay que poner, la partición root y el offset. Con lo cual esta opción quedará así:

resume=/dev/mapper/luks-94d0287f-6c87-4003-aa6b-286993de7e77 resume_offset=1973504

Con lo cual, la linea GRUB_CMDLINE_LINUX_DEFAULT al final va a quedar así:

GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice=UUID=94d0287f-6c87-4003-aa6b-286993de7e77:luks-94d0287f-6c87-4003-aa6b-28 6993de7e77 root=/dev/mapper/luks-94d0287f-6c87-4003-aa6b-286993de7e77 splash apparmor=1 security=apparmor resume=/dev/mapper/luks-94d0287f-6c87-4003-aa6b-286993de7e77 resume_offset=1973504 udev.log_priority=3"

Y ya está. Ahora hay que hacer un «update-grub» y la hibernación y el swap deberían funcionar sin problemas.

Todo lo que te he contado aquí lo tienes con más detalle explicado en las páginas Use a swap file and enable hibernation on Arch Linux – including on a LUKS root partition de Jay Ta’ala y el Wiki de Manjaro sobre el Swap. Te recomiendo que les hagas una visita porque saben mucho más que yo.

Manjaro Linux en un laptop HP Spectre x360

Recientemente he instalado la distribución Manjaro Linux (21.1.3 Pahvo) en un ordenador portátil Hewlett Packard Spectre x360. Se trata de uno de los primeros modelos de esta serie, concretamente el modelo 13-4100ns, equipado con 4 Gb de RAM y un procesador Intel i5.

Este modelo en concreto venía con el UEFI desactivado por defecto, en modo BIOS. Obviamente, hice un clonado completo del disco duro con CloneDisk para poder dar marcha atrás si algo salía terriblemente mal, algo siempre muy recomendable, obligatorio de hecho salvo que seas un suicida. Después, para poder instalar, desactivé el modo de «arranque seguro» en la BIOS. Y ya estamos listos para empezar.

Bien, no voy a enrollarme con la instalación de Manjaro, porque eso está de sobras disponible en la Web, y de todas formas es bastante automático. En concreto, solo comentaré que instalé la versión Gnome, que es siempre mi preferida. E instalé con borrado completo del disco duro (Manjaro como único sistema operativo), con sistema de archivos BTRFS, encriptación completa (full encryption) y Swap encriptada con hibernación.

Con estos datos, lo principal que quería comentar es que casi todo el hardware de la máquina es soportado inmediatamente por Manjaro directamente y sin hacer nada, out-of-the-box. Esto incluye la webcam, la pantalla touch, el trackpad, el sonido (si, el sonido también), e incluso cosas accesorias externas, como un lápiz dongle 4G de marca Huawei, y un mando para usar con diapositivas marca Targus. El soporte del Hardware de Manjaro solo puedo definirlo como excelente.

Con todo, hay algunas excepciones, todas ella sutilezas. Por ejemplo, no se detecta el modo tablet out of the box ni funcionan los giroscopios. Es decir, la pantalla no girará al cambiar de posición el ordenador, y no detectará el giro de la pantalla para pasarlo a modo tablet. Puede arreglarse y que funcione todo para usarse como ordenador y como tablet de forma automática, pero necesitas instalar algunos paquetes adicionales que te cuento más abajo. Tampoco funcionarán algunas teclas especiales, como las de ajuste de brillo de pantalla. Si quieres cambiar el brillo de la pantalla, lo tienes muy fácil en el menú de Gnome.

Si se quiere afinar aún más el software:

  • Es altamente recomendable la instalación del paquete auto-cpufreq. No lo instala por defecto, pero está en los repositorios oficiales y es muy recomendable si quieres prolongar la duración de tu batería. Hay que instalarlo, y luego activarlo con «sudo systemctl enable auto-cpufreq.service».
  • Timeshift se instala por defecto y queda activado en modo Btrfs, con lo que en cuanto empieces a hacer las primeras actualizaciones, se empezarán a crear los primeros snapshots. Grande, si señor. Sin embargo, grub arrancará en modo quiet y no mostrará ningún menú que permita elegir snapshot en caso de problemas. Hay que activar la aparición de menú, pero es muy fácil cambiando la opción GRUB_TIMEOUT_STYLE de Grub, te lo cuento más abajo.
  • Para la contraseña de la encriptación puedes usar cualquier carácter, no solo números y letras. Pero recuerda que cuando arranque y te pida la contraseña, espera un teclado norteamericano US, así que tendrás que memorizar dónde están las teclas de los caracteres especiales (guiones, interrogantes, asteriscos…) en un teclado US. Si quieres evitarte estos problemas, tienes que meter una contraseña con letras y números solo. Eso si, no se te ocurra poner una eñe ni una vocal acentuada, porque a ver cómo las metes luego con la distribución US del teclado.
  • Cuando cierres la tapa del portátil entrará en suspensión. Correcto. Pero cuando abras por completo la tapa para usarlo como tablet, no será detectado out-of-the-box. La mejor opción que tienes es instalar paquetes AUR y usar opciones experimentales. Si instalas los paquetes «iio-sensor-proxy.git», «tablet-mode» y «detect-tablet-mode», dispondrás de un modo tablet bastante decente y automático: al girar la pantalla para ponerlo en tablet, será detectado, se desactivará el teclado y el trackpad, y cuando escribas aparecerá el teclado en pantalla, todo automático. Si además instalas la extensión de gnome «Screen autorotate» y luego reinicias el ordenador (imprescindible), la pantalla también girará correctamente cuando lo pongas de lado o al revés, así que tendrás un modo tablet bastante correcto. Ojo, el paquete que tienes que instalar es «iio-sensor-proxy.git» y no la versión oficial de los repositorios «iio-sensor-proxy». Solo girará la pantalla con la versión git del AUR.
  • Suspend funciona sin problemas, tanto por tiempo de inactividad como bajando la tapa. También la hibernación, pero solo si la Swap no está encriptada. Cuando instalas en modo Swap encriptada con hibernación, resulta que la hibernación no funciona. No sé todavía por qué, pero sé que puede funcionar porque ha funcionado automáticamente cuando la Swap estaba sin encriptar, así que no es un fallo del hardware sino de la configuración. De momento te diré que he hecho funcionar la hibernación sin problemas, pero usando un archivo de swap dentro del disco encriptado, no la partición. Pero eso te lo contaré en otro post. :-)

Vale, activar el menú del grub es muy fácil. Manjaro no lo cree necesario porque por el tipo de instalación que hice, sabe que es el único sistema operativo de la máquina, pero la verdad es que sí lo necesitas si quieres la posibilidad de cambiar de snapshot si ha habido problemas en la última actualización de software. Para activar el menú del grub solo tienes que editar /etc/default/grub. Tienes que cambiar la línea que pone GRUB_TIMEOUT_STYLE para que ponga GRUB_TIMEOUT_STYLE=menu. Y luego hay que aplicar los cambios con «update-grub». Eso es todo. Después de reiniciar verás como aparece el menú grub con todos los snapshots.

Y si quieres ver todos los mensajes del arranque, también puedes hacerlo. Solo tienes que quitar las opciones «quiet» y «splash» del grub.

He instalado Manjaro encriptado y no puedo entrar

Quien dice Manjaro, dice cualquier sistema Arch, como por ejemplo Garuda. Es muy posible que cualquier sistema linux, no lo he probado. El caso es que si instalas Manjaro con full encription, es posible que luego no puedas arrancar el sistema diciéndote «error: access denied». ¿Por qué?.

El mensaje que te da dice algo así:

Attempting to decrypt master key...
Enter passphrase for hd0, msdos1 (aquí vendría una UUID):
error: access denied.
error: no such cryptodisk found.
error: disk 'cryptouid:aquí viene la UUID' not found.
Entering rescue mode...
grub rescue> _ 

Es un poco complicado encontrar por qué, por eso lo pongo aquí, pero en realidad es una tontería. Tu sistema funciona perfectamente, no es un error de Manjaro. Simplemente no está reconociendo tu contraseña. Es muy posible que ésta incluya algún símbolo especial, como un guión, un interrogante, un asterisco, cualquier cosa que no sea un número o una letra. Y ahí es donde está el problema.

El sistema intenta arrancar tu disco duro. Todavía no se ha cargado el sistema operativo, aún no sabe que tienes un teclado con layout español, a todos los efectos cree que usas un teclado estándar norteamericano. Y la posición de las teclas especiales es totalmente distinta a la de los teclados no americanos. Cuando entres al sistema tendrás que teclear tu contraseña como si tuvieras un teclado US. O bien poner una contraseña que tenga solo números y letras.

Y eso es todo. :-)

Falla la actualización del paquete antergos-keyring en Antergos

Es un fallo que me ha ocurrido estos días. Intento actualizar el paquete antergos-keyring pero falla diciendo que el paquete no es válido o está dañado (firma PGP). Aún peor, en los días siguientes empiezan a fallar las actualizaciones de nuevos paquetes por el mismo motivo. Si haces un pacman en consola te dice esto:

fish [:0]-[pts/1:0.32]-[lacofi@cecile (~) <0>]->sudo pacman -S antergos-keyring
resolviendo dependencias…
buscando conflictos entre paquetes…
Paquetes (1) antergos-keyring-20170524-1
Tamaño total de la descarga: 0,01 MiB
Tamaño total de la instalación: 0,04 MiB
Tamaño neto tras actualizar: 0,00 MiB
:: ¿Continuar con la instalación? [S/n] 
:: Recibiendo los paquetes…
 antergos-keyring-20170524-1-any 14,6 KiB 488K/s 00:00 [####################################################] 100%
(1/1) comprobando las claves del depósito [####################################################] 100%
(1/1) verificando la integridad de los paquetes [####################################################] 100%
error: antergos-keyring: signature from "Antergos Build Server (Automated Package Build System) <admin@antergos.org>" is unknown trust
:: El archivo /var/cache/pacman/pkg/antergos-keyring-20170524-1-any.pkg.tar.xz está dañado (paquete no válido o dañado (firma PGP)).
¿Quiere eliminarlo? [S/n] 
error: no se pudo realizar la operación (paquete no válido o dañado (firma PGP))
Ocurrieron errores, por lo que no se actualizaron los paquetes

Vale, se trata de un error simple: intenta actualizar el paquete con las claves de autentificación, pero no puede actualizarlo porque no tiene las claves para autentificarlo. La solución es también simple. Edita tu fichero /etc/pacman.conf y dile que si no puede comprobar las claves da igual. Para ello cambia la línea que pone:

SigLevel = Required DatabaseOptional

Por esta otra:

SigLevel = Optional TrustAll

Ahora actualiza tu sistema. Y luego vuelve a dejar el fichero pacman.conf como estaba, obviamente.

Error INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES en Android Studio

Si cambias de computadora es muy posible que te salte este error cuando intentas instalar tu programa Android en tu tablet o teléfono. Es más, te dice que debes desinstalar la aplicación para volver a instalarla. Si buscas por Google todo Dios te dirá que tienes que desinstalarla y que así el error desaparecerá. Si, claro que si, ¡¡pero eso hará que pierdas todos los datos!!.

El problema está en que cuando instalas de cero Android Studio, o Eclipse (habitualmente porque cambias de ordenador), generan una firma digital que utilizarán para autentificar el software que instalan, metiéndola en el APK. Es lo mismo que cuando subes la aplicación a Google Play, te piden una firma digital, solo que en este caso hablamos de una firma provisional para el debug. Así que en el ordenador nuevo intenta instalar tu aplicación y zas, la firma no coincide.

Si desinstalas la aplicación, la firma vieja desaparece con el APK y ya no habrá conflicto, claro. Pero también perderás todos tus datos y puede que te interese conservarlos.

Sólo tienes que ir a tu copia de seguridad y transferir la firma digital del viejo Android Studio al nuevo, para que ambos utilicen la misma y no haya conflicto. La firma está en tu HOME, en la carpeta oculta .android, en un fichero que se llama debug.keystore. Copia ese fichero a tu nuevo ordenador y reinicia Android Studio.

 

 

LibreOffice hace crash cuando hago copia-pega

Es un bug realmente enojoso, porque habitualmente todo el mundo hace copia-pega de gráficos, tablas, literalmente de todo cuando hace diapositivas con Impress. No hay mensajes de error de ningún tipo, LibreOffice simplemente se cierra y pierdes todos los cambios que no hayas hecho en disco. Si arrancas el programa desde consola (precisamente para ver cualquier hipotético error), verás que no hay información ninguna. Haces un copy o un paste y el programa se cierra sin mensaje de error ninguno, aunque si tienes forma de capturar códigos de error verás que se cierra con código 139, es decir un SEGFAULT en toda regla.

Este fallo me aparece en Debian 8.6 Jessie, pero por lo que he visto por ahí es bastante ubicuo y aparece en todo tipo de sistemas, en múltiples versiones de LibreOffice, tanto en Linux como en Windows, y no he visto ningún lugar donde se aporte solución. Te dicen que elimines con fichero «~/.config/openoffice/4/user» y que vuelvas a intentarlo. Y efectivamente, durante unos minutos parece que funciona, pero luego vuelven a aparecer los crash.

Es un bug conocido con múltiples quejas pero no he visto workaround alguno, así que la solución que te propongo es de cosecha propia. :-)

La pista me la dio cuando observé que hacía crash sistemáticamente al copiar objetos relativamente complicados pero me permitía copiar objetos muy simples (un poco de texto, por ejemplo). ¿Qué es eso?. ¿Se queda sin memoria?.

Vale, vete a Herramientas -> Opciones -> LibreOffice -> Memoria.

Ahí verás varias opciones que se refieren a cuantos recursos pilla LibreOffice del sistema. Y parece todo como bastante escaso. Recorta pasos de deshacer y amplía la memoria caché. Yo puse esto:

  • Deshacer
    • Número de pasos: 20
  • Caché de Imágenes
    • Usar para LibreOffice: 100 Mb
    • Memoria por objeto: 20 Mb
    • Eliminar de la memoria después de: 00:10 hh:mm
  • Caché para los objetos insertados
    • Número de objetos: 20

Oye, y a la primera. En cuanto puse esos parámetros, LibreOffice empezó a funcionar bien y hacer sin problemas los copia-pega. Ya te diré como va la cosa si surgen novedades.

 

Crear una tabla dinámica, por código en vez de usar XML

A veces necesitas crear una tabla de forma dinámica, por código. Por ejemplo, si quieres mostrar resultados de una base de datos en una pantalla con ScrollView, no deberías hacerlo mediante ListView ni mediante GridView porque incorporan sus propios controles de scroll, lo que interfiere con los de ScrollView. La solución más práctica es poner una tabla. Pero si lo que muestra la tabla procede de una base de datos, a priori no sabes cuántas filas debes poner, con lo que no puedes usar XML sino código. No se si me explico.

Crear una tabla por código es bastante fácil. En primer lugar tienes que crear una tabla vacía en XML. Vacía, sin fila alguna.

<TableLayout
            android:id="@+id/tablacultivos"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:stretchColumns="2"
            android:shrinkColumns="2" >
</TableLayout>
Vale, ahora nos vamos al código. Vamos a crear una tabla con 3 columnas y n filas. Primero enlazamos con la tabla y creamos unas cuantos patrones Layout básicos: para la fila y para cada una de las columnas.
TextView txtCultivos=(TextView) getView().findViewById(R.id.LblCultivos);
TableLayout tabladatos=(TableLayout) getView().findViewById(R.id.tablacultivos);
TableRow.LayoutParams layoutFila=new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
TableRow.LayoutParams layoutFecha=new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
TableRow.LayoutParams layoutMuestra=new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
TableRow.LayoutParams layoutResultado=new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
Como ves, vamos a poner las tres columnas y la fila todas iguales: con anchura WRAP_CONTENT y altura WRAP_CONTENT;
Ahora creamos variables para la fila y las columnas.
TableRow fila;
TextView txtFecha;
TextView txtMuestra;
TextView txtResultado;
Es conveniente que reseteemos la tabla a cero. En mi experiencia, si entras en una tabla dinámica todo bien. Pero si sales y vuelves a entrar, las filas se añaden otra vez con lo que aparecen duplicadas. Para evitarlo, empieza reseteando la tabla.
tabladatos.removeAllViews();
Ahora tienes que consultar tu base de datos y obtener un cursor. Eso es material para unos cuantos temas aparte y no te lo voy a explicar aquí. Si no sabes cómo consultar SQLite ni lo que es un cursor, nada de todo esto tiene sentido. Así que vete a tu base de datos, consúltala y obtén un listado en forma de Cursor. ¿Vale?.
De acuerdo, ya tienes un Cursor. Ahora vamos a recorrerlo y al tiempo vamos creando la tabla añadiendo una fila nueva en cada pasada. Observa que para añadir la fila hay que crear los TextView, puedes alterar luego sus propiedades con setGravity, setPadding, etc. También tienes que añadir los parámetros que creaste al principio. Y por último, añades los TextView al layout de fila. Y para terminar añades el layout de Fila a la tabla. Esto se repetirá una vez por pasada a medida que avanzamos por el cursor. Pongo sólo el código relevante.
No me digas que no es fácil.
if (cursor.moveToFirst()) {
         do {
               fila=new TableRow(this); // Si, necesita contexto
               fila.setLayoutParams(layoutFila);

               txtFecha=new TextView(this);
               txtMuestra=new TextView(this);
               txtResultado=new TextView(this);

               txtFecha.setText(cursor.getString(0));
               txtFecha.setGravity(Gravity.RIGHT);
               txtFecha.setPadding(0, 0, 5, 0);
               txtFecha.setLayoutParams(layoutFecha);

               txtMuestra.setText(cursor.getString(1));
               txtMuestra.setPadding(0,0,5,0);
               txtMuestra.setLayoutParams(layoutMuestra);

               txtResultado.setText(cursor.getString(2));
               txtResultado.setLayoutParams(layoutMuestra);
               fila.addView(txtFecha);

               fila.addView(txtMuestra);
               fila.addView(txtResultado);

               tabladatos.addView(fila);
         } while (cursor.moveToNext());
}

Variables globales en Android

Vale, estás haciendo un programa Android y a veces te interesa tener variables globales, accesibles desde cualquier Activity. Una forma muy sencilla de hacerlo es crear una clase que extienda de Application. Con esta clase, podrás almacenar cualquier cosa, desde variables básicas hasta objetos completos, lo que resulta muy práctico. Crea un fichero que contenga una clase tal que así:

public class ClaseGlobal extends Application {
       private final static int versiondb=1;
       private int posicionlistview;
       private int toplistview;
       private FichaPaciente paciente;
       private FichaEnvin envin;

       public void ponPaciente(FichaPaciente p) {
                paciente=p;
       }
       public FichaPaciente dimePaciente() {
                return paciente;
       }
       // etc...
}

Para simplificar, solo he puesto los métodos para meter y extraer un objeto de clase FichaPaciente, pero sería lo mismo para objetos de otra clase o variables básicas.

Para poder utilizar la clase ClaseGlobal sólo tendríamos que instanciarla en cualquier Activity. No se instancia de la forma normal, teniendo en cuenta que funcionará un poco a lo Singleton, es decir, siempre se instanciará el mismo objeto de forma global.

ClaseGlobal global=(ClaseGlobal) getActivity().getApplicationContext();
global.ponPaciente(paciente);

Y ya.

Recordar la posición de un ListView

Si cambias a otra Activity, cuando regreses tu ListView habrá perdido la posición. Eso, a veces puede resultar bastante desconcertante. Una solución nos la cuentan en StackOverflow y es bastante sencilla de aplicar.

Puedes recoger la posición actual cuando abandonas la Activity obteniendo dos valores integer en tu onPause.

@Override 
public void onPause() {
	super.onPause();
	int index=lstListado.getFirstVisiblePosition();

	View v=lstListado.getChildAt(0);
	int top=(v==null) ? 0:(v.getTop()-lstListado.getPaddingTop());

	global.ponPosicion(index);
	global.ponTop(top);
}

Observa que que usamos la variable index para saber cual es la entrada que se veía en primer lugar en el listview. Pero puede ocurrir que esa entrada se viera a medias, así que usamos una segunda variable top para obtener cuánto se mostraba de ella.

Después usamos el método que queramos para guardar ambos valores. En mi caso, he almacenado ambos int usando variables globales, pero ese es otro tema para otra entrada.

Y ahora en el onResume, recogemos ambos valores y con ellos usamos el método setSelectionFromTop para poner el ListView como antes:

@Override
public void onResume() {
        super.onResume();
        int index=global.dimePosicion();
        int top=global.dimeTop();
        lstListado.setSelectionFromTop(index, top);
}
Fácil sí que es.

AutoCompleteTextView

Supongamos que quieres un EditText pero un poco más sofisticado, con sugerencias. Esto es, que según escribes en el EditText te vaya sugiriendo posibilidades que concuerden con lo que vas metiendo. Bueno, pues eso es AutoCompleteTextView. Crear uno es bastante fácil, pero es un widget que tiene su miga.

AutoCompleteTextView se describe en multitud de sitios. Pero pocos te hablan de que tiene un bug por culpa del cual, dependiendo del tema que uses, las sugerencias te salen de color blanco sobre blanco: o sea, no se lee nada. Nosotros vamos a obviarlo especificando nuestra propia plantilla, que es lo primero que vamos a hacer. Hala, crea un fichero XML en layout al que llamaremos item_simple.xml.

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
      android:id="@android:id/text1"
      style="?android:attr/spinnerDropDownItemStyle"
      android:singleLine="true"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:minHeight="40dip"
      android:ellipsize="marquee"
      android:textColor="#222222" />

Vale, ahora necesitas definir tres cosas:

private AutoCompleteTextView editGermen;
ArrayAdapter<String> adaptadorGermen;
String[] germenes;

Lo primero de todo, vamos a rellenar el array de Strings al que hemos llamado germenes, y que es el que alimentará las sugerencias. Por ejemplo, si estamos usando una base de datos SQLite, podemos cargar todos los gérmenes que hayamos introducido en ella. Lo más práctico es llamar a una subrutina desde el onCreate, por ejemplo:

public void rellenaGermenes() {
pacientesdb=new PacientesSQLiteHelper(getActivity(), "Pacientes", null, global.dimeVersionDB());
db=pacientesdb.getReadableDatabase();
String consultaDBInterna="select distinct germen from germenes order by germen";
Cursor c=db.rawQuery(consultaDBInterna, null);
int nGermenes=c.getCount();
germenes=new String[nGermenes];
int x=0;
if (c.moveToFirst()) {
do {
germenes[x]=c.getString(0);
x++;
} while (c.moveToNext());
}
db.close();
pacientesdb.close();
}

Vale, ahora llamamos a esta rutina desde el onCreate, y a continuación definimos el AutoCompleteView, el adaptador (especificando nuestro XML), y los ponemos en contacto:

editGermen=(AutoCompleteTextView) vista.findViewById(R.id.editGermen);
adaptadorGermen=new ArrayAdapter<String>(this, R.layout.item_simple, germenes);
editGermen.setThreshold(3);
editGermen.setAdapter(adaptadorGermen);