Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
136 changes: 37 additions & 99 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -2999,103 +2999,46 @@ html.dark .chat-landing {
}

.chat-hero-visual {
min-height: 400px;
}

.visual-window {
position: absolute;
top: 34px;
right: 0;
left: 0;
overflow: hidden;
border: 1px solid color-mix(in srgb, var(--accent) 22%, var(--border));
border-radius: 19px;
background: color-mix(in srgb, var(--surface) 96%, transparent);
box-shadow: var(--shadow-lg);
transform: perspective(1000px) rotateY(-6deg) rotateX(2deg);
}

.visual-window-bar {
display: flex;
min-height: 430px;
align-items: center;
gap: 6px;
height: 39px;
padding: 0 14px;
border-bottom: 1px solid var(--border);
background: var(--surface-soft);
}

.visual-window-bar i {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--border-strong);
}

.visual-layout {
display: grid;
grid-template-columns: 112px 1fr;
min-height: 315px;
}

.visual-sidebar {
display: grid;
align-content: start;
gap: 11px;
padding: 24px 16px;
border-right: 1px solid var(--border);
background: var(--surface-soft);
}

.visual-sidebar span {
height: 8px;
border-radius: 4px;
background: var(--border-strong);
}

.visual-sidebar span:nth-child(2) {
width: 70%;
}

.visual-sidebar span:nth-child(3) {
width: 84%;
background: color-mix(in srgb, var(--accent) 45%, var(--border));
justify-content: center;
}

.visual-thread {
display: flex;
flex-direction: column;
gap: 14px;
padding: 34px 31px 25px;
.chat-hero-product-image {
display: block;
width: min(100%, 590px);
height: auto;
border-radius: 26px;
box-shadow:
0 36px 90px rgb(52 57 125 / 0.18),
0 12px 34px rgb(65 53 126 / 0.1);
transform: translateY(2px);
animation: hero-product-enter 700ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

.visual-message {
width: 78%;
height: 52px;
border: 1px solid var(--border);
border-radius: 5px 15px 15px 15px;
background: var(--surface-soft);
html.dark .chat-hero-product-image {
box-shadow:
0 38px 100px rgb(0 0 0 / 0.36),
0 12px 36px rgb(31 27 74 / 0.28);
}

.visual-message.is-accent {
align-self: flex-end;
width: 65%;
border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
border-radius: 15px 5px 15px 15px;
background: var(--accent-soft);
}
@keyframes hero-product-enter {
from {
opacity: 0;
transform: translateY(20px) scale(0.985);
}

.visual-message.is-short {
width: 48%;
height: 43px;
to {
opacity: 1;
transform: translateY(2px) scale(1);
}
}

.visual-composer {
height: 43px;
margin-top: auto;
border: 1px solid var(--border);
border-radius: 10px;
background: var(--surface);
@media (prefers-reduced-motion: reduce) {
.chat-hero-product-image {
animation: none;
}
}

.landing-section {
Expand Down Expand Up @@ -4329,7 +4272,7 @@ html.dark .guide-markdown a {
}

.chat-hero-visual {
min-height: 350px;
min-height: 405px;
}

.docs-home-section {
Expand Down Expand Up @@ -4494,16 +4437,11 @@ html.dark .guide-markdown a {
}

.chat-hero-visual {
min-height: 280px;
}

.visual-layout {
grid-template-columns: 75px 1fr;
min-height: 245px;
min-height: 350px;
}

.visual-thread {
padding: 22px 17px 17px;
.chat-hero-product-image {
width: min(100%, 560px);
}

.docs-home-section {
Expand Down Expand Up @@ -4553,10 +4491,6 @@ html.dark .guide-markdown a {
text-align: left;
}

.visual-window {
top: 10px;
}

.landing-section {
width: min(100% - 36px, 1180px);
padding-top: 62px;
Expand All @@ -4571,6 +4505,10 @@ html.dark .guide-markdown a {
.primary-nav {
display: none;
}

.chat-hero-visual {
min-height: 300px;
}
}

@media (prefers-reduced-motion: reduce) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
title: 'Accept a call'
description: 'Accept a call invitation with the Android SDK and retrieve room credentials.'
product: 'sdk'
context: 'chat/sdk/android'
template: 'guide'
status: 'published'
lastUpdated: '2026-08-10'
version: 'v4'
platform: 'android'
sourcePath: '/sdk/android/calling/managing-calls/accept-call'
---

After receiving the original `SignalingInfo` from `onReceiveNewInvitation`, pass it to `signalingAccept()`:

```java
OpenIMClient.getInstance().signalingManager.signalingAccept(
new OnBase<SignalingCertificate>() {
@Override
public void onSuccess(SignalingCertificate credentials) {
joinMediaRoom(
credentials.getRoomID(),
credentials.getToken(),
credentials.getLiveURL()
);
}

@Override
public void onError(int code, String error) {
showAcceptError(code, error);
}
},
receivedInfo
);
```

Preserve the original `roomID`, inviter, invitees, and session type in `receivedInfo`. Do not rebuild it as an object that contains only `roomID`.

The success callback returns `SignalingCertificate`. Connect the media room only after receiving a valid `token` and `roomID`, then keep merging remote state through [Handle call events](/sdk/android/calling/managing-calls/handle-call-events).
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
title: 'Cancel a call invitation'
description: 'Cancel a call invitation before it is accepted with the Android SDK.'
product: 'sdk'
context: 'chat/sdk/android'
template: 'guide'
status: 'published'
lastUpdated: '2026-08-10'
version: 'v4'
platform: 'android'
sourcePath: '/sdk/android/calling/managing-calls/cancel-call'
---

Before an invitation is accepted, the inviter can call `signalingCancel()`:

```java
OpenIMClient.getInstance().signalingManager.signalingCancel(
new OnBase<String>() {
@Override
public void onSuccess(String result) {
stopWaitingForAnswer(activeInfo.getInvitation().getRoomID());
}

@Override
public void onError(int code, String error) {
showCancelError(code, error);
}
},
activeInfo
);
```

Pass the complete `SignalingInfo` saved when the call was started, not a new object containing only `roomID`. A successful callback means that the cancel request completed. End the local waiting state and release unused media resources; remote clients update through `onInvitationCancelled`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
title: 'Handle call events'
description: 'Register Android SDK invitation, participant, and media-stream callbacks in one call listener.'
product: 'sdk'
context: 'chat/sdk/android'
template: 'guide'
status: 'published'
lastUpdated: '2026-08-10'
version: 'v4'
platform: 'android'
sourcePath: '/sdk/android/calling/managing-calls/handle-call-events'
---

Register `OnSignalingListener` once in the call state layer. Merge invitation lifecycle, participant connection, and media stream changes by `roomID`.

```java
OpenIMClient.getInstance().signalingManager.setSignalingListener(
new OnSignalingListener() {
@Override
public void onReceiveNewInvitation(SignalingInfo info) {
upsertCall(info, "ringing");
}

@Override
public void onInviteeAccepted(SignalingInfo info) {
mergeCallEvent("accepted", info);
}

@Override
public void onInviteeRejected(SignalingInfo info) {
mergeCallEvent("rejected", info);
}

@Override
public void onInvitationCancelled(SignalingInfo info) {
mergeCallEvent("cancelled", info);
}

@Override
public void onInvitationTimeout(SignalingInfo info) {
mergeCallEvent("timeout", info);
}

@Override
public void onInviteeAcceptedByOtherDevice(SignalingInfo info) {
mergeCallEvent("accepted-by-other-device", info);
}

@Override
public void onInviteeRejectedByOtherDevice(SignalingInfo info) {
mergeCallEvent("rejected-by-other-device", info);
}

@Override
public void onHangup(SignalingInfo info) {
finishCall(info.getInvitation().getRoomID());
}

@Override
public void onRoomParticipantConnected(RoomCallingInfo room) {
mergeConnectedParticipants(room.getRoomID(), room.getParticipant());
}

@Override
public void onRoomParticipantDisconnected(RoomCallingInfo room) {
mergeDisconnectedParticipants(room.getRoomID(), room.getParticipant());
}

@Override
public void onStreamChange(String streamInfo) {
mergeStreamChange(streamInfo);
}
}
);
```

This page owns the complete handling contract for the 11 call callbacks exposed by the Android adapter other than `onReceiveCustomSignal`. Custom signaling belongs to [Send a custom signal](/sdk/android/calling/sending-custom-signals/send-a-custom-signal).

Merge invitation events with `SignalingInfo.invitation.roomID`. Merge participant state with `RoomCallingInfo.roomID` plus the participant user ID. Do not merge by event order, display name, or array position. `onStreamChange` currently carries a string payload; parse and validate it according to the actual server protocol used by your deployment.

`SignalingManager` retains one listener. Setting another replaces the previous instance, and there is no public remove method. Combine custom signaling with these callbacks in the same application-level listener. When signing out or switching accounts, stop writing into old account state and replace the listener with the complete listener for the new account.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
title: 'Hang up a call'
description: 'Hang up an established call with the Android SDK.'
product: 'sdk'
context: 'chat/sdk/android'
template: 'guide'
status: 'published'
lastUpdated: '2026-08-10'
version: 'v4'
platform: 'android'
sourcePath: '/sdk/android/calling/managing-calls/hang-up-call'
---

After a call is established, a participant can call `signalingHungUp()`. The supplied `SignalingInfo` should preserve the complete invitation, and its `roomID` must match the active media room.

```java
OpenIMClient.getInstance().signalingManager.signalingHungUp(
new OnBase<String>() {
@Override
public void onSuccess(String result) {
releaseCallResources(activeInfo.getInvitation().getRoomID());
}

@Override
public void onError(int code, String error) {
showHangUpError(code, error);
}
},
activeInfo
);
```

The success callback only means that the hang-up request completed. Stop local capture, disconnect the media room, and release the camera and microphone. Cancellation, rejection, timeout, and hang-up should all enter one idempotent cleanup flow keyed by `roomID`.
Loading
Loading