fix: wake app for call pushes

This commit is contained in:
richjohnrambo
2026-09-18 13:51:22 +08:00
parent 153f541078
commit 9482bb19f6
3 changed files with 25 additions and 12 deletions
+3 -2
View File
@@ -585,6 +585,7 @@ X-MeetingIM-APIKey: <预置的普通 API Key>
"sound": "default",
"badge": "+1",
"category": "im_call",
"content-available": 1,
"extras": {
"type": "call_invite",
"tenant_code": "default",
@@ -606,7 +607,7 @@ X-MeetingIM-APIKey: <预置的普通 API Key>
}
```
来电推送必须走普通 APNs/极光 alert 通知,不使用 PushKit VoIP topic;iOS `notification.ios.category` 固定为 `im_call`,用于客户端通知按钮。`time_to_live` 和 `expires_at` 以 30 秒左右短有效期为准,过期后服务端不再投递。
来电推送必须走普通 APNs/极光 alert 通知,不使用 PushKit VoIP topic;iOS `notification.ios.category` 固定为 `im_call`,用于客户端通知按钮,同时带 `content-available: 1` 唤醒后台 App 拉取 `topic/seq` 并启动 Live Activity。`time_to_live` 和 `expires_at` 以 30 秒左右短有效期为准,过期后服务端不再投递。
#### 0.5.5 Long Polling 与文件基础示例
@@ -2414,7 +2415,7 @@ GET /v1/im/topics/{topic}/message-mutations?since_version={localMutationVersion}
- 来电是 Topic 下行的初始 `{data}`:`head.webrtc="started"`、`head.x-im-type="call"`,`content` 至少含 `callId,callType,audio,video`。客户端保存该消息 `seq` 作为后续所有 call note 的关联 ID。
- 来电端开始响铃后发送 `{note:{topic,what:"call",event:"ringing",seq,payload:{"callId":"..."}}}`;接听发送 `event:"accept"`;拒绝发送 `event:"hang-up"`。
- 后台 Push 只携带 `callId,topic,seq,caller_id,caller_display,callType,expires_at`,不含 SDP、ICE 或 Token;iOS 来电 Push 使用普通 alert,`category=im_call`,短 TTL 约 30 秒,不使用 PushKit VoIP。
- 后台 Push 只携带 `callId,topic,seq,caller_id,caller_display,callType,expires_at`,不含 SDP、ICE 或 Token;iOS 来电 Push 使用普通 alert,`category=im_call`,`content-available=1`,短 TTL 约 30 秒,不使用 PushKit VoIP。
- 被叫在未发送 `ringing/accept` 前也可以直接发送 `hang-up`,服务端按 `topic+seq+callId` 校验后立即结束主叫侧振铃,并把原通话消息替换为 `declined`;主叫主动取消则替换为 `missed`。
### 8.3 语音通话-对方待接受状态.png
+19 -10
View File
@@ -79,11 +79,12 @@ type androidPayload struct {
}
type iosPayload struct {
Alert iosAlert `json:"alert"`
Sound string `json:"sound,omitempty"`
Badge string `json:"badge,omitempty"`
Category string `json:"category,omitempty"`
Extras map[string]string `json:"extras,omitempty"`
Alert iosAlert `json:"alert"`
Sound string `json:"sound,omitempty"`
Badge string `json:"badge,omitempty"`
Category string `json:"category,omitempty"`
ContentAvailable int `json:"content-available,omitempty"`
Extras map[string]string `json:"extras,omitempty"`
}
type iosAlert struct {
@@ -501,11 +502,12 @@ func buildPayload(audience map[string][]string, topic string, data map[string]st
Priority: 1,
},
IOS: &iosPayload{
Alert: iosAlert{Title: title, Body: alert},
Sound: "default",
Badge: badge,
Category: iosCategory(data),
Extras: data,
Alert: iosAlert{Title: title, Body: alert},
Sound: "default",
Badge: badge,
Category: iosCategory(data),
ContentAvailable: iosContentAvailable(data),
Extras: data,
},
HMOS: &hmosPayload{
Title: title,
@@ -548,6 +550,13 @@ func iosCategory(data map[string]string) string {
return ""
}
func iosContentAvailable(data map[string]string) int {
if data["type"] == "call_invite" {
return 1
}
return 0
}
func hydrateCallInviteData(tenantID t.TenantID, data map[string]string) {
if data["type"] != "call_invite" || strings.TrimSpace(data["caller_display"]) != "" {
return
+3
View File
@@ -190,6 +190,9 @@ func TestBuildPayloadUsesShortTTLForCalls(t *testing.T) {
if payload.Notification.IOS.Category != callCategory {
t.Fatalf("unexpected iOS category: %s", payload.Notification.IOS.Category)
}
if payload.Notification.IOS.ContentAvailable != 1 {
t.Fatalf("unexpected iOS content-available: %d", payload.Notification.IOS.ContentAvailable)
}
if payload.Notification.IOS.Alert.Body != "张三邀请你语音通话" {
t.Fatalf("unexpected body: %s", payload.Notification.IOS.Alert.Body)
}