Skip to content

Latest commit

 

History

History
248 lines (144 loc) · 8.29 KB

File metadata and controls

248 lines (144 loc) · 8.29 KB

Kotlin Avanzado > Sesión 04 > Ejemplo 1

Ejemplo 1: Broadcast Receivers

1. Objetivos 🎯

  • Emitir una señal para un receiver específico

  • Emitir una señal implícita desde una aplicación

  • Escuchar señales explícitas y explícitas

  • Implementar callbacks al recibir emisiones

2. Requisitos 📋

3. Desarrollo 💻

Definición

Un Broadcast Receiver es una clase que recibe una emisión hecha por una aplicación o por parte del sistema (por ejemplo, un aviso de batería baja) a partir de un Intent, estas emisiones las podemos interpretar como eventos. Para poder utilizarlos, hay que crear una clase que herede de este elemento y registrarlo en un componente de la app para que comience a recibir los mensajes.

Comunicación interna

Nuestro primer receiver escuchará un evento detonado desde nuestra misma aplicaciǿn.

Para esto, creamos un botón para detonar la emisión.

<androidx.constraintlayout.widget.ConstraintLayout ...>

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Enviar emisión"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Ahora, definiremos nuestro receiver; para esto, crearemos una subclase de BroadcastReceiver que implemente el método onReceive(), dicho método se detona después de recibir una señal correspondiente y aquí es donde reaccionaremos a dicho evento. En nuestro ejemplo, obtendremos del evento los parámetros NAME y EMAIL mediante un bundle, como si de una comunicación entre activities se tratase. La información será mostrada en nuestro siempre confiable Toast.

class ReceiverOne : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        val bundle = intent.extras
        val name = bundle?.getString("NAME")
        val email = bundle?.getString("EMAIL")
        Toast.makeText(context,"$name $email",Toast.LENGTH_SHORT).show()
    }
}

En nuestro archivo Manifest haremos la declaración estática de nuestro receiver creando nuestro tag receiver . Este recibirá como atributo el nombre de la clase relacionada a este y contendrá un intent filter, que determinaa qué tipo de intent responderá nuestro componente, y se definirá a través de una llave que permita identificar el evento cuando sea emitido que en este caso, será "org.bedu.actions.SALUDO". Consulta Este enlace para visualizar todos sus atributos.

<receiver
            android:name=".ReceiverOne"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action
                    android:name="org.debu.actions.SALUDO"/>
            </intent-filter>
        </receiver>

Finalmente, para poder recibir la señal, hemos de detonarla mediante un Intent y con apoyo del método sendBroadcast() dentro del listener de nuestro botón. Hay qué recordar que como en el receiver obtenemos info extra, tenemos qué declararla en nuestro Intent.

// Esta es nuestra información extra a enviar
val bundle = Bundle().apply {
                putString("NAME","Pedro")
                putString("EMAIL","[email protected]")
            }

// Creamos un Intent con nuestro bundle como extra y detonamos el evento con sendBroadcast()
Intent(this,ReceiverOne::class.java).apply {
                putExtras(bundle)
            }.let(::sendBroadcast)

si la sintaxis anterior (específicamente en el último bloque) no te cuadra, te mostramos el código equivalente sin scope functions

val intent = Intent(this,ReceiverOne::class.java)
intent.putExtras(bundle)
sendBroadcast(intent)

La pantalla debe verse así al pulsar el botón:

Si lo requieres, puedes dar un repaso al tema de Scope Functions en la documentación oficial y para ejemplos más detallados, en este blog.

Recibiendo señales del sistema

Ya escuchamos emisiones desde la propia aplicación, pero el sistema operativo también puede arrojar varias emisiones correspondientes a eventos que suceden en el sistema, algunos de esto son:

  • ACTION_DATE_CHANGED (al cambiar la fecha del sistema)
  • DATA_SMS_RECEIVED_ACTION (Al recibir un SMS)
  • ACTION_WIFI_AWARE_STATED_CHANGED
  • ACTION_BATTERY_LOW (Batería baja)
  • ACTION_BATTERY_OKAY (Batería con carga considerable)

Para consultar todas las acciones disponibles para la API 30 consulta aquí, ten en cuenta que pueden haber algunas incompatibilidades si las acciones se prohibieron en esta o en anteriores APIs.

En este ejemplo utilizaremos el aviso de modo avión.

Primero crearemos nuestro receiver y luego lo registraremos (forzosamente dinámico porque la llamada será implícita).

Creamos un nuevo receiver

class AirplaneReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {

}

de acuerdo a la documentación, el nombre de la acción para el modo avión es ACTION_AIRPLANE_MODE_CHANGED y nos avisa que su estado cambió, para obtener el valor del estado, hay qué extraer la propiedad state que es un boolenao, por lo que lo extraeremos e imprimiremos su estado en un Toast.

       val airplaneState = intent?.let {
            if(it.getBooleanExtra("state",false)) "Prendido" else "Apagado"
        }

        Toast.makeText(context,
            "Modo avión $airplaneState",
            Toast.LENGTH_SHORT)
            .show()
    }

Creamos un miembro en nuestro Activity para nuestro receiver:

private val airplaneReceiver = AirplaneReceiver()

Ahora lo registraremos. Como se desea que el mensaje se muestre aun cuando la app está en segundo plano debido a que abriremos nuestra menú de opciones, el registro sucederá en onCreate.

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

				...
        IntentFilter().apply {
                  addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED)
              }.also { filter -> registerReceiver(airplaneReceiver,filter) }
  
    }

Y le damos de baja en onDestroy

 override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(airplaneReceiver)
    }

De esta forma, al prender o apagar el modo avión, la app debe hacer lo siguiente:

También debe funcionar con la aplicación minimizada (en segundo plano):

Anterior | Siguiente