---
sidebar_position: 1
sidebar_label: Getting started
---

# Getting started with Android

Live updates, chat, who is online and calls in an Android app written in Kotlin, through WordPlus Cloud. The app joins the same rooms as your web pages, so a customer in the app and an agent in a browser see each other, and a phone and a browser call each other. Your server stays the one your website has ([Next.js](../nextjs/getting-started.md), [Laravel](../laravel/getting-started.md), [Django](../django/getting-started.md), [Express](../express/getting-started.md) …): it declares the rooms, says who may join and call, and sends.

The SDK comes as three libraries, and an app takes only what it uses:

| Library | What it gives | Needs |
|---|---|---|
| `cloud.wordplus:realtime` | rooms, events with acks, presence, counts, history, who is online, what your server sends the user, the directory | a plain Kotlin library: Android 7 (API 24) and later, or any JVM |
| `cloud.wordplus:realtime-calls` | calls between two people, group calls, ringing someone into a call | WebRTC and LiveKit, which it brings |
| `cloud.wordplus:realtime-ui` | The Better Messages call screen: the ring, the call, the group call grid and the controls | Android |

The names are those of the browser: `join`, `on`, `emit`, `emitWithAck`, `volatile`, `history`, `members`, `count`, `online`, `calls.start`, `calls.group`. Events also come as flows. The [example app](example.md) is the shop of every other guide, in Jetpack Compose.

## 1. Your app in WordPlus Cloud

Use the app you made for your website (**Apps** in your WordPlus Cloud account), so the app and the site share rooms and users. A native app has no address to list: it is let in by itself.

## 2. Install

```kotlin
// settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven("https://jitpack.io")   // for calls: the LiveKit Android SDK takes a few artifacts from JitPack
    }
}

// app/build.gradle.kts
dependencies {
    implementation("cloud.wordplus:realtime:1.0.0")
    implementation("cloud.wordplus:realtime-calls:1.0.0")   // for calls
    implementation("cloud.wordplus:realtime-ui:1.0.0")      // for the Better Messages call screen
}
```

The libraries are built for Java 17, which the Android Gradle plugin 8 takes as it is.

## 3. Your server knows the app user

An app has no cookie of your site, so it sends its own sign-in with every request, such as a bearer token, and your server routes read the user from it. When the app signs in, send it the user realtime settings too, so it connects with no request first:

**Node**

```ts
// Express: the user of the session in a browser, that of the token in an app.
app.use( '/api/realtime', realtime.node( ( req ) => req.session.userId ?? userFromToken( req.get( 'authorization' ) ) ) );

app.post( '/api/token', express.json(), async ( req, res ) => {
    const user = await checkSignIn( req.body );
    res.json( { token: tokenFor( user ), realtime: await realtime.config( user.id ) } );
} );
```

**PHP**

```php
// Laravel: the package routes take $request->user(). Add the middleware that signs in the tokens of your app to
// 'routes' => [ 'middleware' => [ … ] ] in config/wordplus-realtime.php. Then, at sign-in:
return [ 'token' => $user->createToken( 'app' )->plainTextToken, 'realtime' => Realtime::config( $user->id ) ];

// Plain PHP: the user of the token to respond()
$realtime->respond( $action, user_from_token( $_SERVER['HTTP_AUTHORIZATION'] ?? '' ) );
```

**Python**

```python
# Django: who the user is, by your own function
path("api/realtime/", include(wr.urls(realtime, user=lambda request: user_from_token(request.headers.get("Authorization")))))

# FastAPI: current_user reads the token
app.include_router(wr.router(realtime, current_user), prefix="/api/realtime")

# At sign-in, in any of them
return {"token": token_for(user), "realtime": realtime.config(user.id)}
```

## 4. Connect

```kotlin
import cloud.wordplus.realtime.Realtime
import cloud.wordplus.realtime.RealtimeOptions
import cloud.wordplus.realtime.TextNodeStore

val server = "https://shop.example.com"
val (token, config) = signIn()   // your own sign-in, answering { token, realtime }
val prefs = context.getSharedPreferences("realtime", Context.MODE_PRIVATE)

val rt = Realtime(
    RealtimeOptions.fromConfig(
        config, server,
        headers = { mapOf("Authorization" to "Bearer $token") },
        callbackExecutor = ContextCompat.getMainExecutor(context),    // handlers on the main thread
        nodeStore = TextNodeStore({ prefs.getString(it, null) }, { key, value -> prefs.edit().putString(key, value).apply() }),
    ),
)
rt.connect()

val order = rt.join("order:1042")
order.on("updated") { data, _ -> status = data.jsonObject["status"]?.jsonPrimitive?.content ?: status }
order.on("error") { error, _ -> Log.w("shop", error.toString()) }   // forbidden, no_rule …
```

- **`RealtimeOptions.fromConfig`** takes your server `config()` as it came (a `JsonElement`), its routes resolved against your server address. Without one, give `RealtimeOptions(app = …, endpoint = "https://shop.example.com/api/realtime", headers = { … })`, and the app asks your keys route first.
- **Handlers run on `callbackExecutor`**, a thread of the SDK unless you give one: give the executor of the main thread to update your views from them.
- **`nodeStore`** keeps the realtime servers the app reached between launches, so it goes straight back to one.
- **One `Realtime` an app**, kept while the user is signed in (in a ViewModel or an object of your app). `rt.close()` at sign-out.
- **The connection keeps itself:** through a drop, from one realtime server to another without one, and back after the app was in the background, every room joined again with what it missed from its history.

## 5. Send

Your server sends to the room, and the app handler runs:

**Node**

```ts
await realtime.to( 'order:1042' ).emit( 'updated', { status: 'shipped' } );
```

**PHP**

```php
$realtime->to( 'order:1042' )->emit( 'updated', [ 'status' => 'shipped' ] );   // Laravel: broadcast( new OrderUpdated( $order ) )
```

**Python**

```python
realtime.to("order:1042").emit("updated", {"status": "shipped"})
```

## Next

- [Rooms](rooms.md), [presence and online status](presence.md), [sending from your server](sending.md) and [events from the app](events.md)
- [Calls](calls.md), with the Better Messages call screen
- [Encryption and your users](encryption.md)
- [Customizing the screens](customizing.md): the call screen look, a piece of your own, or its whole code
- [Shipping](shipping.md), and [the example app](example.md)
- The API reference: [realtime](pathname:///reference/android/realtime/), [realtime-calls](pathname:///reference/android/realtime-calls/) and [realtime-ui](pathname:///reference/android/realtime-ui/)
- On your server side: [rooms in depth](../apps/rooms.md), [errors](../apps/errors.md) and [limits](../apps/limits.md)
