Skip to content

Latest commit

 

History

History
176 lines (130 loc) · 6.05 KB

File metadata and controls

176 lines (130 loc) · 6.05 KB

Architecture

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.

Directory Layout

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

App Flow

  1. MainActivity hosts the Compose app.
  2. APIExampleApp applies the app theme and calls NavGraph.
  3. NavGraph starts on the home route.
  4. Home renders the registered components from Components.
  5. Selecting an example navigates by component ID and example index.
  6. Example renders the selected sample Composable.

Example Registration

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
)

Sample Screen Pattern

Most sample screens follow this structure:

  1. Read LocalContext and LocalLifecycleOwner.
  2. Hold UI/session state with rememberSaveable.
  3. Create RtcEngine inside remember.
  4. Request runtime permissions with rememberLauncherForActivityResult.
  5. Join or control the channel after permissions are granted.
  6. 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:

  1. Initialize ChatClient once with ChatOptions.
  2. Log in with a Chat user/password or a Chat user token from your backend.
  3. Use ChatMessage.ChatType.Chat for direct 1:1 messages.
  4. Use ChatMessage.ChatType.GroupChat for group messages.
  5. Use ChatMessage.createGifImageMessage for GIF image messages.
  6. Add message, connection, and presence listeners while the screen is active.
  7. Remove listeners and log out when leaving the screen.

State Guidelines

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, and PresenceListener.
  • Media player/source helper classes.
  • File readers and render helpers.

Settings

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

Permissions are declared in AndroidManifest.xml and requested at runtime by sample screens as needed.

Common permissions include:

  • CAMERA
  • RECORD_AUDIO
  • INTERNET
  • MODIFY_AUDIO_SETTINGS
  • BLUETOOTH_CONNECT
  • FOREGROUND_SERVICE

Video samples request camera and microphone permissions. Audio-only samples request microphone permission.

Token Flow

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.

Chat SDK Behavior

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.

Local SDK Override

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.

Signing

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.