Android SDK Integration Guide
1. Environment Requirements
| Item | Requirement |
|---|---|
| Minimum Android version | minSdk 27 (Android 8.1, recommended) |
| Supported CPU architectures | armeabi-v7a, arm64-v8a, x86, x86_64 |
To filter by architecture, configure abiFilters in the defaultConfig block of your app module's build.gradle file:
groovy
android {
defaultConfig {
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
}
}2. Import the AAR File
Copy webrtc-pull-stream-release.aar to the app/libs directory of your application module, then add the dependency in your build.gradle file:
groovy
dependencies {
implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
}3. Declare Permissions
Declare the permissions required by the SDK in your AndroidManifest.xml file:
xml
<!-- Network permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Camera permission -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- Microphone permission -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />TIP
CAMERA and RECORD_AUDIO are dangerous permissions. On Android 6.0 and above, they must be requested at runtime.
4. Add PullStreamView to Your Layout
Add the PullStreamView widget to your layout XML:
xml
<com.yjz.pullstream.webrtc.render.PullStreamView
android:id="@+id/pullStreamView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:maxStretchRatio="1.2"
app:stretchEnabled="false"
android:gravity="center"
/>5. Initialize WebrtcClient
Required Parameters
signalServerUrl: Signaling server URLroomId: Room IDToken: Business tokenturnServerUrl: TURN server URL
Initialization Example (Kotlin)
kotlin
val client = WebrtcClient.Builder(this)
.signalServerUrl("wss://your-signal-server/v1/signaling")
.roomId("room_id_from_business")
.token("token_from_business")
.identityType(IdentityType.Android)
.turnServerUrl("turn:xxx:3478")
.lifecycleOwner(this) // Automatically handles onPause/onResume/onDestroy once bound
.build()
client.setStateListener(stateListener)
client.setReportListener(reportListener)
client.connect(surfaceViewContainer)
// Streaming is paused automatically on onPause, resumed on onResume, and released on onDestroy6. Implement Callback Listeners
1) StateListener
TIP
The SDK guarantees that all callbacks are invoked on the main thread.
Callbacks fall into three categories:
- Connection state: Socket, WebRTC, ICE, ConnectSuccess
- Errors: Failure, CameraFail, Reconnect, KICK_OUT, CLOUD_NOT_AVAILABLE
- Business: CurrentClarity, CloudClipData, FIRST_FRAME
kotlin
override fun onStateChanged(state: PullMonitorState) {
when (state) {
is PullMonitorState.OnSocketStateChanged ->
Log.d(TAG, "Socket: ${state.socketState.getDescription()}")
is PullMonitorState.OnWebRTCStateChanged ->
handleStreamState(state.rtcState)
is PullMonitorState.Failure -> {
Toast.makeText(this, "Error: ${state.code.code} ${state.errorMessage}", Toast.LENGTH_SHORT).show()
Log.e(TAG, "Failure: ${state.code.code} ${state.errorMessage}")
}
is PullMonitorState.CameraFail -> {
Toast.makeText(this, "Camera: ${state.message}", Toast.LENGTH_SHORT).show()
Log.e(TAG, "CameraFail: ${state.message}")
loadingContainer?.visibility = View.GONE
}
is PullMonitorState.Reconnect -> {
val msg = when (state.wasEverConnected) {
true -> "Connection lost. Tap \"Start Streaming\" again"
false -> "Connection failed. Check the network and retry"
null -> "Connection error. Tap \"Start Streaming\" again"
}
Log.e(TAG, "$msg ==== ${state.wasEverConnected}")
Toast.makeText(this, "$msg ==== ${state.wasEverConnected}", Toast.LENGTH_SHORT).show()
loadingContainer?.visibility = View.VISIBLE
tip?.text = "Reconnecting..."
webrtcClient?.reconnect()
}
is PullMonitorState.OnIceStateChanged ->
Log.d(TAG, "ICE: ${state.iceStatusMsg}")
is PullMonitorState.OnConnectionChange ->
Log.d(TAG, "Connection: ${state.status}")
is PullMonitorState.ConnectSuccess ->
Log.d(TAG, "Connected")
is PullMonitorState.CurrentClarity ->
level = if (state.level == 4) 1 else level + 1
is PullMonitorState.CloudClipData ->
Toast.makeText(this, state.data, Toast.LENGTH_SHORT).show()
is PullMonitorState.ImeiStatus -> {
imeiMode = state.imeiMode
Log.e("-main-", "ImeiStatus====>" + state.imeiMode)
}
else -> {}
}
}
private fun handleStreamState(rtcState: WebRTCStateType) {
when (rtcState) {
WebRTCStateType.FIRST_FRAME ->
loadingContainer?.visibility = View.GONE
WebRTCStateType.KICK_OUT ->
Toast.makeText(this, "Kicked out of the room", Toast.LENGTH_SHORT).show()
WebRTCStateType.CLOUD_NOT_AVAILABLE ->
Toast.makeText(this, "The cloud phone is unavailable", Toast.LENGTH_SHORT).show()
WebRTCStateType.ROOM_UPPER_LIMIT ->
Toast.makeText(this, "The room connection limit has been reached", Toast.LENGTH_SHORT).show()
else -> {}
}
}2) ReportListener
kotlin
override fun onWebRTCReport(report: WebRTCReport) {
when (report) {
is WebRTCReport.OnCandidateTypeReport ->
pctText?.text = "Connection type: ${report.candidateType.getDescription()}"
is WebRTCReport.OnNetTypeReport -> when (report.key) {
RK_ROUND_TRIP_TIME -> rttText?.text = "Latency: ${report.value}s"
RK_LOSS_RATE -> lrText?.text = "Packet loss: ${report.value}%"
RK_JITTER -> jitText?.text = "Jitter: ${report.value}s"
else -> {}
}
}
}7. Cloud Phone Operations
| Feature | Call |
|---|---|
| Start streaming | webrtcClient.connect(streamView) |
| Stop streaming | webrtcClient.release() |
| Cloud phone Back key | webrtcClient.sendCommand(CommandCode.BACK) |
| Cloud phone Home key | webrtcClient.sendCommand(CommandCode.HOME) |
| Cloud phone Recents key | webrtcClient.sendCommand(CommandCode.TASK) |
| Copy content to the cloud phone | webrtcClient.clipDataToCloud("content") |
| Volume up | webrtcClient.sendCommand(CommandCode.VOICE_ADD) |
| Volume down | webrtcClient.sendCommand(CommandCode.VOICE_DOWN) |
| Mute | webrtcClient.switchImeiMode(ActionImeInputValue.DISABLE) |
| Unmute | webrtcClient.switchImeiMode(ActionImeInputValue.ENABLE) |
