Le SDK Android de Hooky
La conversation Hooky, native dans ton app Android : écrite avec Jetpack Compose (Material 3), sans WebView, aux couleurs de ton site. Elle marche dans une app Compose comme dans une app à vues XML, en Kotlin comme en Java.
Prérequis
- Android 6.0 ou plus (
minSdk 23). - Le SDK est compilé avec
compileSdk 36(Android 16) : compile ton app avec la même version ou une plus récente. - Kotlin ou Java. Ton app n’a pas besoin d’utiliser Compose : le SDK l’apporte pour son propre écran.
- Dépendances : AndroidX (Activity, Core, Compose), les coroutines Kotlin et OkHttp. Pas de bibliothèque d’images, de JSON ni d’analyse.
- Un compte Hooky et un site : sa clé publique est dans Sites › Installer, la même que le
data-site-keyde la bulle web.
Installation
Le SDK est servi depuis son dépôt GitHub, sous forme de dépôt Maven (GitHub Pages). Ajoute ce dépôt à ton projet, dans settings.gradle.kts :
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven("https://defdjamel.github.io/hooky-android")
}
}puis la dépendance au module de ton app :
// app/build.gradle.kts
dependencies {
implementation("com.heyhooky:hooky-android:1.0.0")
}En Groovy : maven { url 'https://defdjamel.github.io/hooky-android' } et implementation 'com.heyhooky:hooky-android:1.0.0'. Les dépendances du SDK (AndroidX, Compose, OkHttp, coroutines) viennent de google() et mavenCentral(). Le code source est sous licence MIT : github.com/Defdjamel/hooky-android.
Démarrer en 3 lignes
// 1. Une fois, au lancement (Application.onCreate est le meilleur endroit)
Hooky.configure(context, "TA_CLE_PUBLIQUE")
// 2. La bulle toute faite, dans un coin de ton écran (Compose)
HookyBubble(Modifier.align(Alignment.BottomEnd).navigationBarsPadding().padding(20.dp))
// 3. Ou la conversation depuis ton propre bouton
Hooky.open(context)configure charge le nom, la couleur et le message d’accueil du site, reprend la conversation enregistrée et garde la connexion en direct ouverte tant que l’app est au premier plan.
class MonApp : Application() {
override fun onCreate() {
super.onCreate()
Hooky.configure(this, "TA_CLE_PUBLIQUE")
}
}
// AndroidManifest.xml : <application android:name=".MonApp" …>Tous les appels se font sur le fil principal et ne lèvent jamais d’erreur. configure prend aussi translation = false, pour retirer la traduction des messages de l’équipe (voir Traduction des messages), et apiUrl, qui ne sert qu’à tester contre ton propre serveur.
La bulle
Un bouton à la couleur de ton site (64 dp), avec le logo Hooky et une pastille pour les réponses pas encore lues. Le toucher ouvre la conversation. Il apparaît dès que l’apparence du site est chargée.
Jetpack Compose
Box(Modifier.fillMaxSize()) {
MonEcran()
HookyBubble(Modifier.align(Alignment.BottomEnd).navigationBarsPadding().padding(20.dp))
}
// Avec ta propre action
HookyBubble(onClick = { afficherAide = true })Vues XML
<FrameLayout …>
<!-- ton écran -->
<com.heyhooky.sdk.HookyBubbleView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="20dp" />
</FrameLayout>L’activité qui l’affiche doit être une AppCompatActivity ou une ComponentActivity.
Ouvrir et fermer la conversation
Hooky.open(context) // plein écran (une Activity), depuis n’importe quel Context
Hooky.close() // la refermeL’écran de conversation, HookyActivity, est déclaré par le SDK : tu n’as rien à ajouter à ton manifeste.
Messages non lus
La bulle porte déjà sa pastille. Pour tes propres boutons, le nombre de réponses de l’équipe pas encore vues se suit en direct ; il retombe à 0 quand la conversation s’ouvre.
// Compose : un StateFlow<Int>
val nonLus by Hooky.unreadCount.collectAsState()
BadgedBox(badge = { if (nonLus > 0) Badge { Text("$nonLus") } }) { Icon(Icons.Default.Email, null) }
// Vues ou Java : un écouteur (la valeur actuelle, puis chaque changement)
val ecoute: Closeable = Hooky.addUnreadCountListener { n -> badge.isVisible = n > 0 }
ecoute.close() // pour arrêterEn Java :
Hooky.configure(this, "TA_CLE_PUBLIQUE");
Closeable ecoute = Hooky.addUnreadCountListener(n -> badge.setText(String.valueOf(n)));Les réponses arrivent en direct tant que l’app est au premier plan ; au retour, le SDK rattrape ce qu’il a manqué. La version 1.0.0 n’envoie pas de notification push à tes utilisateurs. Hooky.isConfigured et Hooky.VERSION complètent l’API.
Identifier l’utilisateur
Ton app sait qui est connecté : dis-le à Hooky. Ton équipe lit, dans la fiche de la conversation, le nom, l’e-mail, ton identifiant de l’utilisateur, l’appareil et les données que tu y joins. Avec un e-mail, la conversation ne demande plus ni prénom ni e-mail.
// Après la connexion
Hooky.identify(
name = "Lou",
email = "lou@exemple.fr",
userId = "u_1042", // ton identifiant à toi
attributes = mapOf("offre" to "Pro", "panier" to 49.9, "beta" to true),
)
// Plus tard : fusionnées avec les précédentes
Hooky.setAttributes(mapOf("panier" to 12, "dernier_ecran" to "Paiement"))
Hooky.setAttributes(mapOf("beta" to null)) // null efface une clé
// À la déconnexion
Hooky.reset()En Java : Hooky.identify("Lou", "lou@exemple.fr", "u_1042");, ou avec une Map<String, Object> de données en quatrième paramètre.
Comment ça marche
- Pas encore de conversation : tout reste en mémoire (même avant
configure) et part avec le premier message. Ensuite, chaque changement part aussitôt, regroupé (un seul envoi pour tout ce qui change dans la même seconde). Hors ligne, il repart au message suivant ou au retour de l’app au premier plan. nameetemailremplacent ceux de la conversation (nullles efface sur l’appareil) ;userId = nullle laisse tel quel.- Le
userIdest gardé avec le jeton de la conversation. UnuserIddifférent, c’est un autre compte sur le même appareil : la conversation est oubliée, comme avecreset(). Le nouvel utilisateur ne voit pas le fil du précédent. Hooky.reset()referme la conversation et oublie le fil, l’identité et les données. Appelle-le à la déconnexion.
Les règles des données
| Champ | Règle |
|---|---|
| Données libres | Une Map à plat : 50 clés au plus, 8 Ko en tout. Les nouvelles clés remplacent les anciennes, les autres restent. |
| Clé | 1 à 64 caractères : lettres, chiffres, espaces, _, . et -. Sinon, elle est ignorée. |
| Valeur | Un texte (coupé à 500 caractères), un nombre ou un booléen. null efface la clé. Les listes et les maps sont ignorées, les autres objets envoyés comme texte. |
userId | 200 caractères au plus. |
name, email | Le nom : 100 caractères au plus. L’e-mail : une adresse valide, sinon il est ignoré. |
Hooky nettoie ce qui dépasse, sans jamais refuser le message de ton utilisateur.
Ces données sont déclaratives : la clé du site est publique, et n’importe qui peut l’utiliser pour envoyer un nom, un e-mail ou un userId de son choix. Elles aident ton équipe à situer la personne, elles ne prouvent pas qui elle est. N’y mets jamais de secret (mot de passe, jeton, numéro de carte) et ne t’en sers jamais pour donner un accès ou agir sur un compte : vérifie toujours de ton côté.
Stats
Compte ce qui se passe dans ton app, comme Hooky.track sur le web : les cartes s’affichent dans Stats, avec celles du site. Appelle-le juste après que l’action a réussi.
Hooky.track("inscription")
Hooky.track("commande", 49.9, mapOf("offre" to "pro", "places" to 3, "essai" to false))- Le deuxième paramètre,
value: un montant ou une quantité, additionné dans la carte. Les propriétés sont à plat (textes, nombres, booléens). - Appelle
configured’abord : avant,trackne fait rien. Les échecs sont silencieux ; hors ligne, l’événement est perdu. - Mêmes règles que sur le web (nom, 20 propriétés au plus, débit) : voir Stats dans la page.
N’envoie jamais de donnée personnelle (e-mail, nom, téléphone) dans un événement. Compte des actions, pas des personnes.
Ce que fait la conversation
- Le premier message, avec prénom et e-mail facultatifs (ou ceux d’
identify), puis un fil comme celui de la bulle web : séparateurs de jours, noms de l’équipe, liens, emoji en grand, « Lu », « … écrit », messages plus anciens à la demande. - Jusqu’à 4 pièces jointes par message, 10 Mo chacune, avec une barre de progression : photos du sélecteur du système (aucune autorisation à demander), de l’appareil photo (par l’app photo du téléphone) et PDF de Fichiers.
- Les photos reçues s’ouvrent en plein écran (pincer ou toucher deux fois pour zoomer) ; les fichiers dans l’app qui sait les lire, ou se partagent.
- Le brouillon survit à la fermeture de l’écran et de l’app. Le clavier ne couvre jamais le champ de saisie.
- Des erreurs en clair : hors ligne, trop de messages d’un coup, fichier trop lourd ou refusé.
Ce que le SDK ajoute à ton app
- L’autorisation
INTERNET, et aucune autre. HookyActivityet unFileProvider(autorité<ton.package>.hooky.fichiers, limité au dossierhooky/du cache de ton app) pour les photos de l’appareil photo et les fichiers reçus.- Des
<queries>pour les apps qui prennent des photos ou ouvrent les PDF (Android 11 et plus).
Si ton app déclare elle-même l’autorisation CAMERA, Android l’exige aussi pour ouvrir l’app photo : le SDK la demande alors avant.
Traduction des messages
Ton utilisateur et ton équipe écrivent chacun dans leur langue, comme avec la bulle web (voir Traduction des messages).
- Sous un message de l’équipe écrit dans une autre langue que celle de la conversation (celle de l’appareil), un lien discret Traduire. Traduit, le message s’affiche dans la langue de l’utilisateur, avec « Traduit de l’anglais · Voir l’original » pour revenir au texte d’origine.
- Le bouton de traduction de l’en-tête propose Toujours traduire : les messages de l’équipe arrivent alors déjà traduits. Le choix est gardé sur l’appareil, pour chaque clé de site.
- Une traduction ratée affiche une courte erreur avec Réessayer. Les messages de l’utilisateur ne sont jamais traduits chez lui ; ton équipe a son propre bouton Traduire, dans le tableau de bord et l’app Hooky.
- Seul le texte est traduit, pas les pièces jointes. La traduction est active par défaut ; pour la retirer de ton app :
Hooky.configure(context, "TA_CLE_PUBLIQUE", translation = false).
Ce que le SDK envoie
Ce qu’envoie la bulle web, et rien d’autre. Sans identifiant publicitaire, sans identifiant d’appareil, sans bibliothèque d’analyse. Pour remplir la section Sécurité des données de ta fiche Google Play :
| Donnée | Quand | Sécurité des données |
|---|---|---|
| Messages | Quand l’utilisateur écrit | Messages : autres messages dans l’application |
| Photos et PDF | Quand il les joint | Photos et vidéos : photos ; fichiers et documents |
| Prénom et e-mail | S’il les donne, ou si tu les passes à identify | Informations personnelles : nom, adresse e-mail |
Ton userId | Si tu le passes à identify | Informations personnelles : ID utilisateur |
| Données libres | Ce que tu passes à identify et setAttributes | Selon ce que tu y mets |
Événements track | Quand tu les envoies, sans identifiant de personne | Activité dans l’application : autres actions (analyses) |
| Appareil, langue, fuseau horaire, app | Avec la conversation | Informations techniques pour ton équipe (voir ci-dessous) |
- Ces données servent au fonctionnement de l’app (le support client), et les stats à tes analyses. Elles sont chiffrées en transit (HTTPS et WebSocket sécurisé). Hooky les traite pour ton compte : au sens de Google Play, ce n’est pas un partage avec un tiers.
- L’appareil est lisible : « Samsung SM-S918B · Android 15 » (fabricant, modèle et version d’Android). La page de la conversation est
android-app://<ton.package>. Chaque requête porteX-Hooky-Client: hooky-android/1.0.0etX-Hooky-App: <package> <versionName> (<versionCode>): ton équipe voit la conversation venir de « Android · com.exemple.app 2.3.1 (231) ». - Sur l’appareil : le jeton de la conversation (la seule clé de l’utilisateur), le
userIdet le brouillon, par clé de site, dans un fichier privé de ton app sousnoBackupFilesDir: jamais sauvegardé dans le cloud ni restauré sur un autre appareil. Une traduction n’envoie que l’identifiant du message et la langue de la conversation. - Hooky est sous-traitant au sens du RGPD : voir la politique de confidentialité. Tu restes responsable de ta fiche : déclare ce que ton app envoie vraiment.
Langue, mode sombre, accessibilité
- Langue
- L’interface existe en français, anglais, espagnol, portugais, italien et allemand. Elle suit la langue de l’appareil au moment de
configure, l’anglais par défaut. La langue part avec le premier message, pour que ton équipe réponde dans la bonne langue et à la bonne heure (avec le fuseau horaire). - Mode sombre
- La conversation et la bulle suivent le thème clair ou sombre du système, avec la couleur de ton site. Rien à régler.
- Accessibilité
- Libellés TalkBack sur chaque contrôle (la bulle : « Ouvrir le chat avec … », et le nombre de non-lus), taille du texte du système respectée.
R8 et ProGuard
Rien à ajouter. Le SDK n’utilise pas la réflexion et embarque ses propres règles : R8 peut tout réduire dans ton build de production.
Dépannage
- Mon site a déclaré ses adresses : l’app est-elle refusée ?
- Non. Une app n’a pas d’adresse : elle se présente par son en-tête
X-Hooky-Clientet passe même quand le site a déclaré ses domaines. N’ajoute rien dans Sites. - La bulle n’apparaît pas
- Elle attend l’apparence du site. Vérifie que
configureest appelé, avec la clé publique du site (pas la clé secrètehks_…, ni une clé APIhka_…). Sans réseau au lancement, elle apparaît au prochain retour de l’app au premier plan. - Plantage avec
HookyBubbleView - L’activité doit être une
AppCompatActivityou uneComponentActivity. - Hors ligne
- Le message affiche l’erreur et reste dans le champ ;
identifyetsetAttributesrepartent plus tard. Les événementstrackenvoyés hors ligne sont perdus. - Un autre utilisateur voit l’ancienne conversation
- Appelle
Hooky.reset()à la déconnexion, et passe unuserIdàidentify: un identifiant différent repart d’une conversation vierge.
Journal des versions
- 1.0.0
- Première version :
configure, la bulle Compose et XML,openetclose,identify(avecuserIdet données libres),setAttributes,track,reset, les non-lus en direct (StateFlowet écouteur Java), photos et PDF, la traduction des messages de l’équipe, six langues, mode sombre et TalkBack.