This project is a Compose-first Agora API example app. Each sample is a standalone Composable screen that creates its own Agora SDK client, joins or controls a channel/session, and releases resources when the screen leaves composition.
app/src/main/java/io/agora/api/example/compose/
├── APIExampleApp.kt
├── MainActivity.kt
├── NavGraph.kt
├── data/
│ └── SettingPreferences.kt
├── model/
│ ├── Components.kt
│ └── Examples.kt
├── samples/
│ ├── JoinChannelVideo.kt
│ ├── JoinChannelAudio.kt
│ └── ...
├── ui/
│ ├── common/
│ ├── example/
│ ├── home/
│ ├── settings/
│ └── theme/
└── utils/
├── AgoraConfig.java
├── TokenUtils.java
└── media/render helpers
MainActivityhosts the Compose app.APIExampleAppapplies the app theme and callsNavGraph.NavGraphstarts on the home route.Homerenders the registered components fromComponents.- Selecting an example navigates by component ID and example index.
Examplerenders the selected sample Composable.
Examples are registered manually in app/src/main/java/io/agora/api/example/compose/model/Examples.kt.
val BasicExampleList = listOf(
Example(R.string.example_join_channel_video_token) { JoinChannelVideoToken() },
Example(R.string.example_join_channel_video) { JoinChannelVideo() },
Example(R.string.example_join_channel_audio) { JoinChannelAudio() }
)The display name comes from app/src/main/res/values/strings.xml. The list position is the display order on the home screen.
Components are grouped in Components.kt:
val Components = listOf(
basicComponent,
advanceComponent,
chatComponent
)Most sample screens follow this structure:
- Read
LocalContextandLocalLifecycleOwner. - Hold UI/session state with
rememberSaveable. - Create
RtcEngineinsideremember. - Request runtime permissions with
rememberLauncherForActivityResult. - Join or control the channel after permissions are granted.
- Release the SDK in
DisposableEffect(lifecycleOwner).
The important lifecycle pattern is:
DisposableEffect(lifecycleOwner) {
onDispose {
rtcEngine.leaveChannel()
RtcEngine.destroy()
}
}Always leave the channel before destroying the engine.
Chat samples follow the same lifecycle idea with ChatClient:
- Initialize
ChatClientonce withChatOptions. - Log in with a Chat user/password or a Chat user token from your backend.
- Use
ChatMessage.ChatType.Chatfor direct 1:1 messages. - Use
ChatMessage.ChatType.GroupChatfor group messages. - Use
ChatMessage.createGifImageMessagefor GIF image messages. - Add message, connection, and presence listeners while the screen is active.
- Remove listeners and log out when leaving the screen.
Use rememberSaveable for primitive UI and session state that should survive rotation:
- Channel name.
- Join state.
- UID values.
- Selected options.
- Visible local/remote user lists.
Use remember for SDK objects and helpers that are not serializable:
RtcEngine.ChatClient.IRtcEngineEventHandler.MessageListener,ConnectionListener, andPresenceListener.- Media player/source helper classes.
- File readers and render helpers.
The Settings screen writes values into SettingPreferences, an in-memory object used by the samples:
- Video dimensions.
- Frame rate.
- Orientation mode.
- Agora area code.
Samples read these values when configuring RtcEngineConfig and VideoEncoderConfiguration.
Permissions are declared in AndroidManifest.xml and requested at runtime by sample screens as needed.
Common permissions include:
CAMERARECORD_AUDIOINTERNETMODIFY_AUDIO_SETTINGSBLUETOOTH_CONNECTFOREGROUND_SERVICE
Video samples request camera and microphone permissions. Audio-only samples request microphone permission.
AgoraConfig.java exposes BuildConfig.AGORA_APP_ID, BuildConfig.AGORA_APP_CERT, BuildConfig.AGORA_TOKEN_SERVER_URL, and BuildConfig.AGORA_CHAT_APP_KEY.
TokenUtils.java supports RTC token requests. It prefers a configured token server and falls back to the demo toolbox flow when only an App Certificate is configured. The helper is convenient for local examples, but production apps should generate tokens on a backend service.
Chat SDK user tokens are separate from RTC media tokens. The Chat samples can log in with demo passwords for local testing or accept a Chat token pasted into the login field.
For local token testing without a full backend, scripts/chat-test-tokens.mjs reads local.properties, creates a1 and a2 through Agora Chat REST if needed, and prints Chat user tokens. This helper is for demos only; production token issuance belongs on a trusted backend.
ChatLab.kt powers the Chat group. It demonstrates:
- Direct 1:1 chat.
- Group chat.
- GIF image messages with local and downloaded previews.
- Typing indicators through online-only command messages.
- Presence state.
- Message delivery/read callbacks.
- Message reactions.
- Local conversation history reload.
The Chat SDK is the correct Agora product layer for WhatsApp-like messaging. It handles user identity, direct messages, groups, offline sync, delivery/read callbacks, presence, and reactions. Production apps should create users and issue Chat tokens from a backend instead of registering demo users on device.
app/build.gradle.kts checks for ../../sdk. If the folder exists, local .jar and .aar files are used. Otherwise, the project downloads Agora SDK artifacts from Maven.
Debug builds use Android's default debug signing. Release builds do not define a repository-owned signing config. Add release signing through local Gradle properties or your CI/CD secret store when publishing an APK or AAB.