Zum Inhalt springen

Das Android-SDK von Hooky

Das Hooky-Gespräch, nativ in deiner Android-App: mit Jetpack Compose (Material 3) geschrieben, ohne WebView, in den Farben deiner Website. Es funktioniert in einer Compose-App genauso wie in einer App mit XML-Views, in Kotlin wie in Java.

Ein Android-App-Bildschirm mit der Hooky-Bubble unten rechts und einer ungelesenen Nachricht. Das Hooky-Gespräch auf Android: ein Foto, ein empfangenes PDF und ein Link.
Die Bubble mit ihrem Badge für Ungelesene, und das Gespräch mit einem Foto und einem PDF.

Voraussetzungen

  • Ab Android 6.0 (minSdk 23).
  • Das SDK ist mit compileSdk 36 (Android 16) kompiliert: Kompilier deine App mit derselben oder einer neueren Version.
  • Kotlin oder Java. Deine App muss Compose nicht nutzen: Das SDK bringt es für seinen eigenen Bildschirm mit.
  • Abhängigkeiten: AndroidX (Activity, Core, Compose), Kotlin-Coroutines und OkHttp. Keine Bild-, JSON- oder Analyse-Bibliothek.
  • Ein Hooky-Konto und eine Website: Ihr öffentlicher Schlüssel steht unter Websites › Bubble installieren, derselbe wie das data-site-key der Web-Bubble.

Installation

Das SDK kommt aus seinem GitHub-Repository, als Maven-Repository (GitHub Pages). Füg es zu deinem Projekt hinzu, in settings.gradle.kts:

// settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven("https://defdjamel.github.io/hooky-android")
    }
}

dann die Abhängigkeit zum Modul deiner App:

// app/build.gradle.kts
dependencies {
    implementation("com.heyhooky:hooky-android:1.0.0")
}

In Groovy: maven { url 'https://defdjamel.github.io/hooky-android' } und implementation 'com.heyhooky:hooky-android:1.0.0'. Die Abhängigkeiten des SDK (AndroidX, Compose, OkHttp, Coroutines) kommen von google() und mavenCentral(). Der Quellcode steht unter MIT-Lizenz: github.com/Defdjamel/hooky-android.

Loslegen in 3 Zeilen

// 1. Einmal, beim Start (Application.onCreate ist der beste Ort)
Hooky.configure(context, "DEIN_OEFFENTLICHER_SCHLUESSEL")

// 2. Die fertige Bubble, in einer Ecke deines Bildschirms (Compose)
HookyBubble(Modifier.align(Alignment.BottomEnd).navigationBarsPadding().padding(20.dp))

// 3. Oder das Gespräch über deinen eigenen Button
Hooky.open(context)

configure lädt Name, Farbe und Begrüßungsnachricht der Website, nimmt das gespeicherte Gespräch wieder auf und hält die Live-Verbindung offen, solange die App im Vordergrund ist.

class MeineApp : Application() {
    override fun onCreate() {
        super.onCreate()
        Hooky.configure(this, "DEIN_OEFFENTLICHER_SCHLUESSEL")
    }
}
// AndroidManifest.xml: <application android:name=".MeineApp" …>

Alle Aufrufe laufen auf dem Main-Thread und werfen nie einen Fehler. configure nimmt außerdem translation = false, um die Übersetzung der Nachrichten des Teams zu entfernen (siehe Nachrichten übersetzen), und apiUrl, das nur zum Testen gegen deinen eigenen Server dient.

Die Bubble

Ein Button in der Farbe deiner Website (64 dp), mit dem Hooky-Logo und einem Badge für noch nicht gelesene Antworten. Ein Tippen öffnet das Gespräch. Er erscheint, sobald das Aussehen der Website geladen ist.

Jetpack Compose

Box(Modifier.fillMaxSize()) {
    MeinBildschirm()
    HookyBubble(Modifier.align(Alignment.BottomEnd).navigationBarsPadding().padding(20.dp))
}

// Mit deiner eigenen Aktion
HookyBubble(onClick = { zeigeHilfe = true })

XML-Views

<FrameLayout …>
    <!-- dein Bildschirm -->
    <com.heyhooky.sdk.HookyBubbleView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="20dp" />
</FrameLayout>

Die Activity, die sie anzeigt, muss eine AppCompatActivity oder eine ComponentActivity sein.

Gespräch öffnen und schließen

Hooky.open(context)   // Vollbild (eine Activity), von jedem Context aus
Hooky.close()         // schließt es wieder

Der Gesprächsbildschirm, HookyActivity, wird vom SDK deklariert: Du musst deinem Manifest nichts hinzufügen.

Ungelesene Nachrichten

Die Bubble zeigt ihr Badge schon selbst. Für deine eigenen Buttons lässt sich die Zahl der noch nicht gesehenen Antworten des Teams live verfolgen; sie fällt auf 0, sobald sich das Gespräch öffnet.

// Compose: ein StateFlow<Int>
val ungelesen by Hooky.unreadCount.collectAsState()
BadgedBox(badge = { if (ungelesen > 0) Badge { Text("$ungelesen") } }) { Icon(Icons.Default.Email, null) }

// Views oder Java: ein Listener (der aktuelle Wert, dann jede Änderung)
val listener: Closeable = Hooky.addUnreadCountListener { n -> badge.isVisible = n > 0 }
listener.close()   // zum Beenden

In Java:

Hooky.configure(this, "DEIN_OEFFENTLICHER_SCHLUESSEL");
Closeable listener = Hooky.addUnreadCountListener(n -> badge.setText(String.valueOf(n)));

Antworten kommen live an, solange die App im Vordergrund ist; danach holt das SDK nach, was es verpasst hat. Version 1.0.0 schickt deinen Nutzern keine Push-Benachrichtigungen. Hooky.isConfigured und Hooky.VERSION ergänzen die API.

Nutzer identifizieren

Deine App weiß, wer angemeldet ist: Sag es Hooky. Dein Team sieht in den Details des Gesprächs den Namen, die E-Mail, deine eigene Nutzer-ID, das Gerät und die Daten, die du mitgibst. Mit einer E-Mail fragt das Gespräch weder nach Vornamen noch nach E-Mail.

// Nach der Anmeldung
Hooky.identify(
    name = "Lou",
    email = "lou@example.com",
    userId = "u_1042",                                  // deine eigene ID
    attributes = mapOf("tarif" to "Pro", "warenkorb" to 49.9, "beta" to true),
)

// Später: mit den bisherigen zusammengeführt
Hooky.setAttributes(mapOf("warenkorb" to 12, "letzter_bildschirm" to "Zahlung"))
Hooky.setAttributes(mapOf("beta" to null))             // null löscht einen Schlüssel

// Bei der Abmeldung
Hooky.reset()

In Java: Hooky.identify("Lou", "lou@example.com", "u_1042");, oder mit einer Map<String, Object> mit Daten als viertem Parameter.

So funktioniert es

  • Noch kein Gespräch: Alles bleibt im Speicher (sogar vor configure) und wird mit der ersten Nachricht gesendet. Danach wird jede Änderung sofort gesendet, gebündelt (eine einzige Anfrage für alles, was sich in derselben Sekunde ändert). Offline geht sie mit der nächsten Nachricht raus oder wenn die App wieder in den Vordergrund kommt.
  • name und email ersetzen die des Gesprächs (null löscht sie auf dem Gerät); userId = null lässt sie unverändert.
  • Die userId wird zusammen mit dem Gesprächs-Token gespeichert. Ist die userId eine andere, ist das ein anderes Konto auf demselben Gerät: Das Gespräch wird vergessen, wie bei reset(). Der neue Nutzer sieht den Verlauf des vorherigen nicht.
  • Hooky.reset() schließt das Gespräch und vergisst Verlauf, Identität und Daten. Ruf es bei der Abmeldung auf.

Regeln für die Daten

FeldRegel
Freie DatenEine flache Map: höchstens 50 Schlüssel, insgesamt 8 KB. Neue Schlüssel ersetzen die alten, die übrigen bleiben.
Schlüssel1 bis 64 Zeichen: Buchstaben, Ziffern, Leerzeichen, _, . und -. Sonst wird er ignoriert.
WertEin Text (nach 500 Zeichen abgeschnitten), eine Zahl oder ein Boolean. null löscht den Schlüssel. Listen und Maps werden ignoriert, andere Objekte als Text gesendet.
userIdHöchstens 200 Zeichen.
name, emailDer Name: höchstens 100 Zeichen. Die E-Mail: eine gültige Adresse, sonst wird sie ignoriert.

Hooky räumt auf, was darüber hinausgeht, und lehnt die Nachricht deines Nutzers deswegen nie ab.

Diese Daten sind Angaben, keine Beweise: Der Schlüssel der Website ist öffentlich, und jeder kann ihn nutzen, um einen beliebigen Namen, eine E-Mail oder eine userId seiner Wahl zu senden. Sie helfen deinem Team, die Person einzuordnen, beweisen aber nicht, wer sie ist. Leg dort nie etwas Geheimes ab (Passwort, Token, Kartennummer) und nutze sie nie, um Zugriff zu gewähren oder an einem Konto etwas zu ändern: Prüf immer auf deiner Seite nach.

Stats

Zähl, was in deiner App passiert, wie mit Hooky.track im Web: Die Kacheln erscheinen unter Statistiken, zusammen mit denen der Website. Ruf es direkt auf, nachdem die Aktion geklappt hat.

Hooky.track("anmeldung")
Hooky.track("bestellung", 49.9, mapOf("tarif" to "pro", "plaetze" to 3, "testphase" to false))
  • Der zweite Parameter, value: ein Betrag oder eine Menge, in der Kachel summiert. Die Properties sind flach (Texte, Zahlen, Booleans).
  • Ruf zuerst configure auf: Vorher tut track nichts. Fehler bleiben stumm; offline geht das Event verloren.
  • Dieselben Regeln wie im Web (Name, höchstens 20 Properties, Limit): siehe Stats in der Seite.

Schick nie personenbezogene Daten (E-Mail, Name, Telefon) in einem Event. Zähl Aktionen, keine Personen.

Was das Gespräch kann

  • Die erste Nachricht, mit optionalem Vornamen und optionaler E-Mail (oder denen aus identify), dann ein Verlauf wie in der Web-Bubble: Tagestrenner, Namen des Teams, Links, große Emojis, „Gelesen“, „… schreibt…“, ältere Nachrichten auf Abruf.
  • Bis zu 4 Anhänge pro Nachricht, je 10 MB, mit Fortschrittsbalken: Fotos aus der Auswahl des Systems (keine Berechtigung nötig), von der Kamera (über die Kamera-App des Handys) und PDFs aus Dateien.
  • Empfangene Fotos öffnen sich im Vollbild (zum Zoomen auseinanderziehen oder doppelt tippen); Dateien in der App, die sie lesen kann, oder sie werden geteilt.
  • Der Entwurf übersteht das Schließen des Bildschirms und der App. Die Tastatur verdeckt nie das Eingabefeld.
  • Verständliche Fehlermeldungen: offline, zu viele Nachrichten auf einmal, Datei zu groß oder abgelehnt.

Was das SDK deiner App hinzufügt

  • Die Berechtigung INTERNET, und keine andere.
  • HookyActivity und einen FileProvider (Authority <dein.package>.hooky.fichiers, beschränkt auf den Ordner hooky/ im Cache deiner App) für Kamerafotos und empfangene Dateien.
  • <queries> für Apps, die Fotos aufnehmen oder PDFs öffnen (ab Android 11).

Wenn deine App selbst die Berechtigung CAMERA deklariert, verlangt Android sie auch zum Öffnen der Kamera-App: Das SDK fragt dann vorher danach.

Nachrichten übersetzen

Dein Nutzer und dein Team schreiben jeweils in ihrer Sprache, wie mit der Web-Bubble (siehe Nachrichten übersetzen).

  • Unter einer Nachricht des Teams in einer anderen Sprache als der des Gesprächs (der des Geräts) steht ein dezenter Link Übersetzen. Übersetzt erscheint die Nachricht in der Sprache des Nutzers, mit „Übersetzt aus dem Englischen · Original anzeigen“, um zum ursprünglichen Text zurückzukehren.
  • Der Übersetzungsknopf in der Kopfzeile bietet Immer übersetzen an: Die Nachrichten des Teams kommen dann schon übersetzt an. Die Wahl wird auf dem Gerät gespeichert, pro Website-Schlüssel.
  • Schlägt eine Übersetzung fehl, erscheint ein kurzer Fehler mit Erneut versuchen. Die Nachrichten des Nutzers werden bei ihm nie übersetzt; dein Team hat seinen eigenen Knopf Übersetzen, im Dashboard und in der Hooky-App.
  • Nur Text wird übersetzt, keine Anhänge. Die Übersetzung ist standardmäßig aktiv; um sie aus deiner App zu entfernen: Hooky.configure(context, "DEIN_OEFFENTLICHER_SCHLUESSEL", translation = false).

Was das SDK sendet

Was die Web-Bubble sendet, und sonst nichts. Ohne Werbe-ID, ohne Geräte-ID, ohne Analyse-Bibliothek. Zum Ausfüllen des Abschnitts Datensicherheit deines Google Play-Eintrags:

DatenWannDatensicherheit
NachrichtenWenn der Nutzer schreibtNachrichten: Andere In-App-Nachrichten
Fotos und PDFsWenn er sie anhängtFotos und Videos: Fotos; Dateien und Dokumente
Vorname und E-MailWenn er sie angibt oder du sie an identify übergibstPersonenbezogene Daten: Name, E-Mail-Adresse
Deine userIdWenn du sie an identify übergibstPersonenbezogene Daten: Nutzer-IDs
Freie DatenWas du an identify und setAttributes übergibstJe nachdem, was du hineinschreibst
track-EventsWenn du sie sendest, ohne PersonenkennungApp-Aktivitäten: Andere Aktionen (Analysen)
Gerät, Sprache, Zeitzone, AppMit dem GesprächTechnische Angaben für dein Team (siehe unten)
  • Diese Daten dienen der App-Funktionalität (dem Kundensupport), die Stats deinen Analysen. Sie werden bei der Übertragung verschlüsselt (HTTPS und sicherer WebSocket). Hooky verarbeitet sie in deinem Auftrag: Im Sinne von Google Play ist das keine „Weitergabe“ an Dritte.
  • Das Gerät ist lesbar: „Samsung SM-S918B · Android 15“ (Hersteller, Modell und Android-Version). Die Seite des Gesprächs ist android-app://<dein.package>. Jede Anfrage trägt X-Hooky-Client: hooky-android/1.0.0 und X-Hooky-App: <package> <versionName> (<versionCode>): Dein Team sieht, dass das Gespräch von „Android · com.example.app 2.3.1 (231)“ kommt.
  • Auf dem Gerät: das Gesprächs-Token (der einzige Schlüssel des Nutzers), die userId und der Entwurf, pro Website-Schlüssel, in einer privaten Datei deiner App unter noBackupFilesDir: nie in der Cloud gesichert und nie auf einem anderen Gerät wiederhergestellt. Eine Übersetzung sendet nur die ID der Nachricht und die Sprache des Gesprächs.
  • Hooky ist Auftragsverarbeiter im Sinne der DSGVO: siehe die Datenschutzerklärung. Für deinen Eintrag bleibst du verantwortlich: Gib an, was deine App wirklich sendet.

Sprache, Dark Mode, Barrierefreiheit

Sprache
Die Oberfläche gibt es auf Französisch, Englisch, Spanisch, Portugiesisch, Italienisch und Deutsch. Sie folgt der Sprache des Geräts zum Zeitpunkt von configure, standardmäßig Englisch. Die Sprache wird mit der ersten Nachricht gesendet, damit dein Team in der richtigen Sprache und zur richtigen Uhrzeit antwortet (mit der Zeitzone).
Dark Mode
Gespräch und Bubble folgen dem hellen oder dunklen Design des Systems, mit der Farbe deiner Website. Nichts einzustellen.
Barrierefreiheit
TalkBack-Labels auf jedem Bedienelement (die Bubble: „Chat mit … öffnen“, und die Zahl der ungelesenen Nachrichten), die Schriftgröße des Systems wird berücksichtigt.

R8 und ProGuard

Nichts hinzuzufügen. Das SDK nutzt keine Reflection und bringt eigene Regeln mit: R8 kann in deinem Release-Build alles verkleinern.

Fehlerbehebung

Meine Website hat ihre Adressen eingetragen: Wird die App abgelehnt?
Nein. Eine App hat keine Adresse: Sie weist sich mit ihrem Header X-Hooky-Client aus und kommt auch dann durch, wenn die Website ihre Domains eingetragen hat. Trag unter Websites nichts ein.
Die Bubble erscheint nicht
Sie wartet auf das Aussehen der Website. Prüf, ob configure aufgerufen wird, mit dem öffentlichen Schlüssel der Website (nicht dem geheimen Schlüssel hks_… und keinem API-Schlüssel hka_…). Ohne Netz beim Start erscheint sie, sobald die App das nächste Mal in den Vordergrund kommt.
Absturz mit HookyBubbleView
Die Activity muss eine AppCompatActivity oder eine ComponentActivity sein.
Offline
Die Nachricht zeigt den Fehler und bleibt im Eingabefeld; identify und setAttributes werden später gesendet. Offline gesendete track-Events gehen verloren.
Ein anderer Nutzer sieht das alte Gespräch
Ruf Hooky.reset() bei der Abmeldung auf, und übergib eine userId an identify: Eine andere ID beginnt mit einem leeren Gespräch.

Versionsverlauf

1.0.0
Erste Version: configure, die Bubble für Compose und XML, open und close, identify (mit userId und freien Daten), setAttributes, track, reset, Ungelesene live (StateFlow und Java-Listener), Fotos und PDFs, die Übersetzung der Nachrichten des Teams, sechs Sprachen, Dark Mode und TalkBack.