Creación de una aplicación POS de Android que no se puede cerrar

Publicado: 2022-03-11

El mundo del desarrollo de aplicaciones móviles es amplio y está en constante evolución, con nuevos marcos y tecnologías que aparecen casi a diario. Cuando piensa en un dispositivo móvil, probablemente piense en su teléfono o tal vez en su tableta, aunque no son tan populares como los teléfonos inteligentes.

iOS de Apple y Android de Google dominan el mercado móvil y cada uno ha tenido sus altibajos durante la última década. Hoy voy a hablar más sobre Android y su uso en dispositivos que no son necesariamente móviles.

Ser de código abierto tuvo un efecto secundario realmente interesante en el sistema operativo móvil de Google. Claro, podemos pensar en todas las diferentes bifurcaciones de Android de varias compañías de teléfonos inteligentes, pero ¿qué pasa con todos los dispositivos que ejecutan Android que no son móviles? Hoy en día, todo, desde refrigeradores, hornos inteligentes, cerraduras de puertas o incluso dispositivos de punto de venta (POS) puede ejecutar Android. Estos últimos son la razón por la que terminé escribiendo este artículo.

Los dispositivos de punto de venta (POS) pueden ejecutar Android hoy en día

Sistemas de punto de venta Android

Hace aproximadamente un año, pude jugar con un dispositivo Android que era todo menos ordinario, y no es algo que la mayoría de la gente pueda usar. El dispositivo en cuestión es un sistema POS basado en Android de un proveedor chino que también tiene una impresora térmica integrada (como las que se usan para imprimir recibos en las tiendas o en los cajeros automáticos).

Sin embargo, la mayor sorpresa fue su software: estaba ejecutando una versión básica de Android. Si no recuerdo mal, en ese momento estaba ejecutando Android 8 o Android Oreo si prefiere los nombres en clave de Google. El dispositivo en sí se parece a un dispositivo POS portátil de la vieja escuela, pero en lugar del teclado físico donde ingresaría su PIN, tiene una pantalla táctil capacitiva como las que se usaban en los teléfonos Android de antaño.

Mi requerimiento era fácil: tenía que ver si había alguna manera de que pudiéramos usar las funciones de este dispositivo, como la impresora térmica, mientras ejecutamos la aplicación que estábamos desarrollando. Tan pronto como me di cuenta de que el requisito en sí era posible, me llamó la atención otro problema: la seguridad .

La cuestión es que si tiene un dispositivo que maneja pagos con tarjeta y otros tipos de transacciones, es posible que no desee que ese mismo dispositivo pueda ejecutar TikTok, Gmail o Snapchat. Este dispositivo se comportaba exactamente como una tableta, e incluso venía con Play Store de Google preinstalado. Imagínese ir a una pequeña tienda de conveniencia y ver a su cajero tomando selfies, abriendo correos electrónicos de un príncipe nigeriano y navegando por sitios web extraños y plagados de malware.

Y después, el cajero te entrega el mismo dispositivo para ingresar tu PIN. Personalmente, no me sentiría seguro al proporcionar la información de mi tarjeta de crédito a través de dicho dispositivo.

Bloqueo de usuarios fuera de los menús de Android

Dejando a un lado la seguridad, tuve que asumir un desafío aún más importante: tenía que bloquear a la persona que usaba el dispositivo Android POS dentro de mi aplicación. Jugar con el sistema operativo no era una opción ya que estos dispositivos se entregaron a personas sin conocimientos técnicos.

Claro, los cajeros son más que capaces de instalar una aplicación, pero la mayoría de ellos no pueden flashear ROM personalizadas ni manejar otras operaciones de nivel inferior. La aplicación en sí fue escrita en React Native, aunque eso es irrelevante en este contexto. Todas las modificaciones que hice están en código Java nativo, por lo que no importa lo que esté usando para desarrollar su aplicación principal, estos ajustes deberían funcionar.

Como un pequeño descargo de responsabilidad, este procedimiento solo funciona para las aplicaciones de Android . Apple no nos da el control que necesitamos para lograr fácilmente algo como esto en un iPhone o iPad, lo cual es comprensible dada la naturaleza cerrada de iOS.

Hay cuatro formas en que un usuario podría salir de una aplicación:

  • Utilice el botón Inicio .
  • Utilice el botón Atrás .
  • Utilice el botón Recientes .
  • Deja tu aplicación a través de la barra de notificaciones .

Al hacer clic en una notificación reciente o ir a la configuración desde esa barra, el usuario saldría de nuestra aplicación. También tiene gestos, pero al final del día, estos gestos activan exactamente las mismas acciones que lo harían al presionar un botón normal.

Además, tener un sistema de PIN para desbloquear la aplicación puede ser realmente útil para que alguien administre el dispositivo. De esta manera, solo alguien que tenga un PIN podría instalar una versión diferente de la aplicación, sin ofrecer un acceso más profundo al usuario final.

El botón de inicio

Para evitar que un usuario presione el botón Inicio, no tenemos que deshabilitarlo.

Una característica útil de Android es la disponibilidad de diferentes lanzadores. Por lo general, estas aplicaciones le brindan diferentes pantallas de inicio, cajones de aplicaciones y acceso a varias personalizaciones de la interfaz de usuario. Cada dispositivo Android tiene uno preinstalado por el fabricante. En última instancia, estas son solo aplicaciones normales y regulares con una excepción pequeña pero crucial.

Lo que eso significa es que si el sistema operativo pudiera reconocer nuestra aplicación como un iniciador, podríamos configurarlo como un iniciador predeterminado. El efecto secundario de esto es que cada vez que presione el botón Inicio, el dispositivo lo llevará al iniciador de Inicio. Y si nuestra aplicación es el iniciador de inicio, básicamente, este botón de inicio se vuelve inútil. Para hacer eso, tenemos que editar el archivo XML AndroidManifest en nuestro proyecto de Android y agregar estas dos líneas de código:

 <activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>

La primera línea hará que nuestra aplicación sea elegible para ser seleccionada en caso de que el usuario presione el botón Inicio, y la segunda línea permitirá que nuestra aplicación se establezca como predeterminada cada vez que ocurra esta acción.

Ahora solo faltaba que el agente de campo instalara la aplicación en el dispositivo al momento de entregarla al cliente. Cada vez que instala una aplicación que tiene el potencial de ser un iniciador, Android le preguntará si desea usar otro iniciador y si desea configurarlo como predeterminado.

Ahora, si presionaste el botón Inicio o borraste todas tus aplicaciones recientes, el dispositivo te dirigirá automáticamente a mi aplicación.

Creación de una aplicación POS de Android que no se puede cerrar

El botón Atrás

A continuación, tenemos que manejar el botón Atrás. Las aplicaciones móviles generalmente brindan una forma en pantalla de regresar a través de las pantallas, especialmente porque muchos dispositivos no tienen una tecla "atrás" dedicada.

Hace unos años, este era el caso de los dispositivos iOS de Apple, que presentaban su ya icónico diseño con un solo botón físico debajo de la pantalla. Sin embargo, en los últimos años, la mayoría de los dispositivos Android también han eliminado los botones de inicio físicos. Primero, se trasladaron a los botones en pantalla, y ahora estamos viendo que los teléfonos se eliminan gradualmente en favor de los gestos, ya que los fabricantes de teléfonos se trasladan a dispositivos de pantalla completa con marcos y mentón pequeños.

Lo que esto significa es que el botón Atrás que proporciona Android de forma predeterminada no es realmente necesario, y para que este botón sea completamente inútil, solo necesitamos agregar un simple bloque de código en nuestra actividad:

 @Override public void onBackPressed() { } 

Creación de una aplicación POS de Android que no se puede cerrar

Este es un bloque de código bastante sencillo: nuestra actividad principal nos permite interceptar cada vez que un usuario presiona el botón Atrás. En nuestro caso, dado que no queremos que el usuario presione ese botón demasiadas veces para salir de la aplicación, simplemente podemos sobrescribir el método predeterminado con uno que no haga nada, diciéndole a nuestra aplicación que no haga nada en caso de que el botón Atrás se presiona el botón.

Así es como algunas aplicaciones piden confirmación antes de salir accidentalmente de ellas volviendo demasiadas veces.

El botón Recientes

Todavía tenemos que manejar el botón Recientes, y este es el más complicado. Además, esta definitivamente no es una buena práctica o algo que deba enviar a Play Store, pero funciona para nuestro caso de nicho aquí.

De la misma forma que la actividad principal nos permite saber cuándo se pulsa el botón Atrás, también nos permite saber cuándo la app está en pausa. ¿Qué significa esto? Este código se activa cada vez que nuestra aplicación pasa de ser la aplicación de primer plano a estar en segundo plano.

Al interceptar este evento, obtendremos el ID de la tarea de nuestra aplicación actual y le indicaremos al administrador de actividades que mueva esta tarea al frente. Para hacer esto, necesitamos un permiso especial en el mismo archivo de manifiesto de Android que editamos anteriormente.

 <manifest xmlns:andro package="com.johnwick"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.REORDER_TASKS" /> <application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" /> </application> </manifest>

Esto nos permitirá leer las tareas en curso y realizar cambios en estas tareas. Además, todavía necesitamos interceptar el momento en que nuestra aplicación se envía al fondo. Podemos volver a anular el método onPause en nuestra actividad.

Aquí, obtenemos el administrador de tareas y lo obligamos a mover una tarea específica al primer plano. En nuestro caso, esa tarea concreta es la que se acaba de enviar a segundo plano (nuestra aplicación).

 @Override public void onPause() { super.onPause(); ActivityManager activityManager = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); activityManager.moveTaskToFront(getTaskId(), 0); }

Ahora, cada vez que quieras ir al menú de recientes, la aplicación volverá a enfocar automáticamente. Claro, es posible que a veces la pantalla parpadee un poco, pero no podrá salir de esta aplicación. Y aquí hay otra cosa genial: ¿recuerdas que dije que también podías salir haciendo clic en una notificación o yendo directamente a la configuración a través de la bandeja de notificaciones? Bueno, realizar esas acciones pondrá la aplicación en segundo plano, lo que activará nuestro código, y luego el usuario volverá a entrar.

Todo esto sucede tan rápido que el usuario no se dará cuenta de lo que sucede en segundo plano. Además, otra parte buena de este enfoque es que sus cambios rápidos todavía están disponibles. Todavía puede seleccionar una red wifi, por ejemplo, o deshabilitar los sonidos, pero no se permite nada que requiera que ingrese a la aplicación Configuración real.

Creación de una aplicación POS de Android que no se puede cerrar

La solución

No estoy seguro de que esta sea la mejor manera de hacerlo, pero sin embargo fue un proceso realmente interesante mientras investigaba un tema que ni siquiera sabía que era posible. ¡Y funciona! Una palabra de advertencia: en este punto, solo hay dos formas de salir de la aplicación como desarrollador: o reinstala el sistema operativo o elimina/desinstala la aplicación a través de ADB.

Si de alguna manera pierde la conexión ADB con el dispositivo, no conozco una manera fácil de sacarlo. Para evitar eso, terminé construyendo un sistema PIN.

Casos de borde

Hay algunas situaciones que debemos asegurarnos de tener en cuenta. En primer lugar, ¿qué sucede si un dispositivo se reinicia? No tiene que ser un reinicio manual, también puede ser un bloqueo del sistema operativo.

Dado que configuramos nuestra aplicación como el iniciador predeterminado anteriormente, tan pronto como el sistema operativo se reinicie, debería iniciar automáticamente nuestra aplicación. Pero, ¿cómo sabe Android que debe cargar la pantalla de inicio al arrancar? Es porque básicamente solo carga tu lanzador predeterminado. Dado que somos el iniciador predeterminado en este punto, los reinicios no deberían ser un problema. ¿Y podría Android acabar con nuestra aplicación en algún momento? En teoría, podría matar la aplicación si la memoria RAM se llena, pero en la vida real, esto es casi imposible. Dado que nuestra aplicación no se puede cerrar, nadie puede abrir otras aplicaciones, por lo que la memoria RAM no debería llenarse.

La única forma en que puedo pensar en que se llene es si nuestra aplicación tiene una gran pérdida de memoria, pero en ese caso, tendría problemas mayores que mantener a su usuario dentro de la aplicación. Aún así, incluso si Android de alguna manera activa una señal de eliminación de nuestra aplicación, siempre que intente volver a casa, el sistema operativo intentará iniciar nuestra aplicación nuevamente, ya que es el iniciador predeterminado, lo que mantiene al usuario bloqueado.

Construyendo una puerta trasera

Como explicación rápida, había un lugar en la configuración de la aplicación donde podía ingresar un PIN para desbloquear la aplicación. Si el PIN es correcto, deshabilitará las limitaciones establecidas por nuestros métodos onPause y onBackPressed mediante una declaración condicional simple. A partir de ahí, un usuario podría ingresar a la configuración a través del menú de alternancia rápida. Después, siempre puede volver a configurar el iniciador predeterminado en el original, y eso lo sacaría completamente de la aplicación. Hay muchas formas en las que se puede manejar esta parte, pero es bueno tener un mecanismo para deshabilitar las mismas limitaciones que usted implementó. Tal vez podrías hacer una autenticación de huellas dactilares para desbloquear. Las posibilidades son casi infinitas.

Creación de una aplicación POS de Android que no se puede cerrar

Terminando

Eventualmente, me quedé con una aplicación que nadie podía cerrar o matar. Incluso reiniciar el dispositivo no ayudaría, ya que volvería a encenderse directamente en el iniciador predeterminado, que actualmente es nuestra aplicación. Resultó útil para nuestro proyecto, y la satisfacción de probar algo tan loco y fuera de lugar fue realmente grandiosa y muy motivadora.

Hay muchos dispositivos y casos de uso en los que Android ha facilitado la vida de los desarrolladores. En estos días, escribir una aplicación de Android es mucho más fácil que usar muchos lenguajes y herramientas diferentes específicos de la plataforma. Piense en dispositivos IoT, aplicaciones de quiosco, sistemas de punto de venta, pasarelas de navegación y pago para taxis, y muchos más.

Estos son casos de uso en los que Android facilitó el desarrollo de aplicaciones, pero también casos de uso de nicho en los que le gustaría restringir el acceso de manera similar a lo que demostramos en este artículo.