Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/AndresLopezCorrales/La-casa-del-bordadito/llms.txt

Use this file to discover all available pages before exploring further.

La Casa del Bordadito uses two complementary notification mechanisms: OneSignal for push notifications delivered to the device when the app is in the background, and a custom PopupWindow displayed inside MainActivity for real-time in-app chat alerts. Both systems share the same trigger points — a new workshop flyer upload or an incoming chat message — but each targets a different user context.

OneSignal setup

1

SDK initialization

OneSignal is initialized in ApplicationClass.onCreate() before any activity launches. The app ID is read from BuildConfig.ONESIGNAL_APP_ID, which is populated at build time from local.properties.
OneSignal.Debug.logLevel = LogLevel.VERBOSE
OneSignal.initWithContext(this, ONESIGNAL_APP_ID)

CoroutineScope(Dispatchers.IO).launch {
    OneSignal.Notifications.requestPermission(false)
}
requestPermission(false) requests the POST_NOTIFICATIONS permission on Android 13+ (Tiramisu) without forcing a prompt — the false argument means the SDK will not re-prompt if the user has already denied it.
2

Linking the user identity

After a successful sign-in, the app calls OneSignal.login(uid) with the Firebase Auth UID. This sets the OneSignal external ID, enabling targeted push delivery to a specific user by UID rather than by device token.
3

Sending notifications via FcmUtil

All outbound push calls are made from FcmUtil (package com.example.applacasadelbordadito.notificaciones). The object reads both BuildConfig.ONESIGNAL_APP_ID and BuildConfig.ONESIGNAL_API_KEY, then posts to the OneSignal REST API using OkHttp.

FcmUtil functions

enviarNotificacionATodos — broadcast to all users

Used when a new workshop flyer is uploaded. Targets the built-in "All" segment so every subscribed device receives the notification.
fun enviarNotificacionATodos(titulo: String, mensaje: String) {
    val body = JSONObject().apply {
        put("app_id", ONESIGNAL_APP_ID)
        put("included_segments", JSONArray().put("All"))
        put("headings", JSONObject()
            .put("en", titulo)
            .put("es", titulo))
        put("contents", JSONObject()
            .put("en", mensaje)
            .put("es", mensaje))
        put("large_icon", "logo_app")
        put("small_icon", "ic_taller")
    }

    val request = Request.Builder()
        .url("https://api.onesignal.com/notifications")
        .post(body.toString().toRequestBody("application/json".toMediaType()))
        .addHeader("Authorization", "Key $ONESIGNAL_API_KEY")
        .addHeader("Content-Type", "application/json")
        .build()

    OkHttpClient().newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            Log.e("OneSignal", "Error: ${e.message}")
        }
        override fun onResponse(call: Call, response: Response) {
            val responseBody = response.body?.string()
            Log.d("OneSignal", "Código: ${response.code} | Body: $responseBody")
            response.close()
        }
    })
}

enviarNotificacionAUsuario — targeted push to a single user

Used for chat notifications. Identifies the recipient by their Firebase Auth UID via include_aliases.external_id, matching the UID set during OneSignal.login(). The sender’s profile photo is passed as imagenPerfil and used as the notification’s large_icon if it is a non-empty, non-null URL.
fun enviarNotificacionAUsuario(
    receptorUid: String,
    titulo: String,
    mensaje: String,
    imagenPerfil: String
) {
    val body = JSONObject().apply {
        put("app_id", ONESIGNAL_APP_ID)
        put("include_aliases", JSONObject().put("external_id", JSONArray().put(receptorUid)))
        put("target_channel", "push")

        put("headings", JSONObject()
            .put("en", titulo)
            .put("es", titulo))
        put("contents", JSONObject()
            .put("en", mensaje)
            .put("es", mensaje))

        if (imagenPerfil.isNotEmpty() && imagenPerfil != "null") {
            put("large_icon", imagenPerfil)
        } else {
            put("large_icon", "logo_app")
        }

        put("small_icon", "ic_bell")
        put("android_sound", "default")
        put("priority", 10)
    }

    val request = Request.Builder()
        .url("https://api.onesignal.com/notifications")
        .post(body.toString().toRequestBody("application/json".toMediaType()))
        .addHeader("Authorization", "Key $ONESIGNAL_API_KEY")
        .addHeader("Content-Type", "application/json")
        .build()

    OkHttpClient().newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            Log.e("OneSignal", "Error PUSH: ${e.message}")
        }
        override fun onResponse(call: Call, response: Response) {
            val responseBody = response.body?.string()
            Log.d("OneSignal", "Código: ${response.code} | Body: $responseBody")
            response.close()
        }
    })
}

When push notifications are sent

New workshop flyer

When an admin uploads a new flyer in TallerActivity, FcmUtil.enviarNotificacionATodos() is called with a title and message announcing the update. All subscribed users receive the push.

Incoming chat message

When a user sends a message in ChatActivity, FcmUtil.enviarNotificacionAUsuario() is called for the recipient — but only if the recipient is not currently online (online != true) and is not actively viewing the sender’s chat room (chattingWith != senderUid).
Push notifications for chat are suppressed when the recipient is online (online = true in Usuarios/{uid}) or is actively viewing the conversation (chattingWith == senderUid in their user node). This prevents duplicate alerts when the in-app popup notification is already visible.

In-app notification (PopupWindow)

MainActivity listens to the ChatsUnreadCount/{currentUid} node in Realtime Database. Whenever a new sender entry appears with a count greater than zero, a PopupWindow is inflated and displayed anchored to the top of the screen.

Content

Shows the sender’s avatar, display name, and a preview of the most recent message — fetched by reading the sender’s Usuarios node and the latest message from the Chats room.

Behaviour

The popup auto-dismisses after 4 000 ms. Tapping it navigates immediately to ChatActivity with the sender’s UID passed as an Intent extra ("uid").

Android permission

POST_NOTIFICATIONS is declared in AndroidManifest.xml and is also requested at runtime for devices running Android 13 (API 33, Tiramisu) or higher:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
There are two permission-request paths in the app: ApplicationClass calls OneSignal.Notifications.requestPermission(false) on a background coroutine at startup, and MainActivity.solicitarPermisoNotificaciones() additionally issues a direct requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) call for Android 13+ devices. Both guard against re-prompting: the false argument prevents the OneSignal SDK from re-prompting after a denial, and MainActivity checks ContextCompat.checkSelfPermission before launching the system dialog.
ONESIGNAL_APP_ID and ONESIGNAL_API_KEY are injected into BuildConfig at compile time from local.properties. Never commit these values to version control. Add local.properties to .gitignore and distribute the keys through a secrets manager or your CI/CD environment variables. Exposing the REST API key allows anyone to send push notifications on behalf of your app.

Build docs developers (and LLMs) love