Skip to content

Commit 0bbd910

Browse files
r10slink2xt
andauthored
feat: add call ringing API (#6650)
this PR adds a "ringing" api that can be used for calls later. see deltachat.h for details about the API; jsonrpc is left out until things are settled for the needs of android/iOS UI using this PR already successfully are deltachat/deltachat-ios#2638 and deltachat/deltachat-android#3785 ; the "payload" passed forth and back is optimised for https://github.com/deltachat/calls-webapp --------- Co-authored-by: l <[email protected]>
1 parent 4258088 commit 0bbd910

File tree

16 files changed

+1129
-40
lines changed

16 files changed

+1129
-40
lines changed

deltachat-ffi/deltachat.h

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,103 @@ void dc_set_webxdc_integration (dc_context_t* context, const char* f
12151215
uint32_t dc_init_webxdc_integration (dc_context_t* context, uint32_t chat_id);
12161216

12171217

1218+
/**
1219+
* Start an outgoing call.
1220+
* This sends a message with all relevant information to the callee,
1221+
* who will get informed by an #DC_EVENT_INCOMING_CALL event and rings.
1222+
*
1223+
* Possible actions during ringing:
1224+
*
1225+
* - caller cancels the call using dc_end_call():
1226+
* callee receives #DC_EVENT_CALL_ENDED
1227+
*
1228+
* - callee accepts using dc_accept_incoming_call():
1229+
* caller receives #DC_EVENT_OUTGOING_CALL_ACCEPTED.
1230+
* callee's devices receive #DC_EVENT_INCOMING_CALL_ACCEPTED, call starts
1231+
*
1232+
* - callee rejects using dc_end_call():
1233+
* caller receives #DC_EVENT_CALL_ENDED after 1 minute timeout.
1234+
* callee's other devices receive #DC_EVENT_CALL_ENDED
1235+
*
1236+
* - callee is already in a call:
1237+
* in this case, UI may decide to show a notification instead of ringing.
1238+
* otherwise, this is same as timeout
1239+
*
1240+
* - timeout:
1241+
* after 1 minute without action,
1242+
* caller and callee receive #DC_EVENT_CALL_ENDED
1243+
* to prevent endless ringing of callee
1244+
* in case caller got offline without being able to send cancellation message
1245+
*
1246+
* Actions during the call:
1247+
*
1248+
* - caller ends the call using dc_end_call():
1249+
* callee receives #DC_EVENT_CALL_ENDED
1250+
*
1251+
* - callee ends the call using dc_end_call():
1252+
* caller receives #DC_EVENT_CALL_ENDED
1253+
*
1254+
* Note, that the events are for updating the call screen,
1255+
* possible status messages are added and updated as usual, including the known events.
1256+
* In the UI, the sorted chatlist is used as an overview about calls as well as messages.
1257+
* To place a call with a contact that has no chat yet, use dc_create_chat_by_contact_id() first.
1258+
*
1259+
* UI will usually allow only one call at the same time,
1260+
* this has to be tracked by UI across profile, the core does not track this.
1261+
*
1262+
* @memberof dc_context_t
1263+
* @param context The context object.
1264+
* @param chat_id The chat to place a call for.
1265+
* This needs to be a one-to-one chat.
1266+
* @param place_call_info any data that other devices receive
1267+
* in #DC_EVENT_INCOMING_CALL.
1268+
* @return ID of the system message announcing the call.
1269+
*/
1270+
uint32_t dc_place_outgoing_call (dc_context_t* context, uint32_t chat_id, const char* place_call_info);
1271+
1272+
1273+
/**
1274+
* Accept incoming call.
1275+
*
1276+
* This implicitly accepts the contact request, if not yet done.
1277+
* All affected devices will receive
1278+
* either #DC_EVENT_OUTGOING_CALL_ACCEPTED or #DC_EVENT_INCOMING_CALL_ACCEPTED.
1279+
*
1280+
* @memberof dc_context_t
1281+
* @param context The context object.
1282+
* @param msg_id The ID of the call to accept.
1283+
* This is the ID reported by #DC_EVENT_INCOMING_CALL
1284+
* and equals to the ID of the corresponding info message.
1285+
* @param accept_call_info any data that other devices receive
1286+
* in #DC_EVENT_OUTGOING_CALL_ACCEPTED or #DC_EVENT_INCOMING_CALL_ACCEPTED.
1287+
* @return 1=success, 0=error
1288+
*/
1289+
int dc_accept_incoming_call (dc_context_t* context, uint32_t msg_id, const char* accept_call_info);
1290+
1291+
1292+
/**
1293+
* End incoming or outgoing call.
1294+
*
1295+
* From the view of the caller, a "cancellation",
1296+
* from the view of callee, a "rejection".
1297+
* If the call was accepted, this is a "hangup".
1298+
*
1299+
* For accepted calls,
1300+
* all participant devices get informed about the ended call via #DC_EVENT_CALL_ENDED.
1301+
* For not accepted calls, only the caller will inform the callee.
1302+
*
1303+
* If the callee rejects, the caller will get a timeout or give up at some point -
1304+
* same as for all other reasons the call cannot be established: Device not in reach, device muted, connectivity etc.
1305+
* This is to protect privacy of the callee, avoiding to check if callee is online.
1306+
*
1307+
* @memberof dc_context_t
1308+
* @param context The context object.
1309+
* @param msg_id the ID of the call.
1310+
* @return 1=success, 0=error
1311+
*/
1312+
int dc_end_call (dc_context_t* context, uint32_t msg_id);
1313+
1314+
12181315
/**
12191316
* Save a draft for a chat in the database.
12201317
*
@@ -4546,6 +4643,8 @@ int dc_msg_is_info (const dc_msg_t* msg);
45464643
* and also offer a way to fix the encryption, eg. by a button offering a QR scan
45474644
* - DC_INFO_WEBXDC_INFO_MESSAGE (32) - Info-message created by webxdc app sending `update.info`
45484645
* - DC_INFO_CHAT_E2EE (50) - Info-message for "Chat is end-to-end-encrypted"
4646+
* - DC_INFO_OUTGOING_CALL (60) - Info-message refers to an outgoing call
4647+
* - DC_INFO_INCOMING_CALL (65) - Info-message refers to an incoming call
45494648
*
45504649
* For the messages that refer to a CONTACT,
45514650
* dc_msg_get_info_contact_id() returns the contact ID.
@@ -4602,6 +4701,8 @@ uint32_t dc_msg_get_info_contact_id (const dc_msg_t* msg);
46024701
#define DC_INFO_INVALID_UNENCRYPTED_MAIL 13
46034702
#define DC_INFO_WEBXDC_INFO_MESSAGE 32
46044703
#define DC_INFO_CHAT_E2EE 50
4704+
#define DC_INFO_OUTGOING_CALL 60
4705+
#define DC_INFO_INCOMING_CALL 65
46054706

46064707

46074708
/**
@@ -6636,6 +6737,63 @@ void dc_event_unref(dc_event_t* event);
66366737
*/
66376738
#define DC_EVENT_CHANNEL_OVERFLOW 2400
66386739

6740+
6741+
6742+
/**
6743+
* Incoming call.
6744+
* UI will usually start ringing,
6745+
* or show a notification if there is already a call in some profile.
6746+
*
6747+
* Together with this event,
6748+
* an info-message is added to the corresponding chat.
6749+
* The info-message, however, is _not_ additionally notified using #DC_EVENT_INCOMING_MSG,
6750+
* if needed, this has to be done by the UI explicitly.
6751+
*
6752+
* If user takes action, dc_accept_incoming_call() or dc_end_call() should be called.
6753+
*
6754+
* Otherwise, ringing should end on #DC_EVENT_CALL_ENDED
6755+
* or #DC_EVENT_INCOMING_CALL_ACCEPTED
6756+
*
6757+
* @param data1 (int) msg_id ID of the info-message referring to the call.
6758+
* @param data2 (char*) place_call_info, text passed to dc_place_outgoing_call()
6759+
*/
6760+
#define DC_EVENT_INCOMING_CALL 2550
6761+
6762+
/**
6763+
* The callee accepted an incoming call on another device using dc_accept_incoming_call().
6764+
* The caller gets the event #DC_EVENT_OUTGOING_CALL_ACCEPTED at the same time.
6765+
*
6766+
* The event is sent unconditionally when the corresponding message is received.
6767+
* UI should only take action in case call UI was opened before, otherwise the event should be ignored.
6768+
*
6769+
* @param data1 (int) msg_id ID of the info-message referring to the call
6770+
* @param data2 (char*) accept_call_info, text passed to dc_place_outgoing_call()
6771+
*/
6772+
#define DC_EVENT_INCOMING_CALL_ACCEPTED 2560
6773+
6774+
/**
6775+
* A call placed using dc_place_outgoing_call() was accepted by the callee using dc_accept_incoming_call().
6776+
*
6777+
* The event is sent unconditionally when the corresponding message is received.
6778+
* UI should only take action in case call UI was opened before, otherwise the event should be ignored.
6779+
*
6780+
* @param data1 (int) msg_id ID of the info-message referring to the call
6781+
* @param data2 (char*) accept_call_info, text passed to dc_accept_incoming_call()
6782+
*/
6783+
#define DC_EVENT_OUTGOING_CALL_ACCEPTED 2570
6784+
6785+
/**
6786+
* An incoming or outgoing call was ended using dc_end_call().
6787+
* Moreover, the event is sent when the call was not accepted within 1 minute timeout.
6788+
*
6789+
* The event is sent unconditionally when the corresponding message is received.
6790+
* UI should only take action in case call UI was opened before, otherwise the event should be ignored.
6791+
*
6792+
* @param data1 (int) msg_id ID of the info-message referring to the call
6793+
*/
6794+
#define DC_EVENT_CALL_ENDED 2580
6795+
6796+
66396797
/**
66406798
* @}
66416799
*/

deltachat-ffi/src/lib.rs

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,10 @@ pub unsafe extern "C" fn dc_event_get_id(event: *mut dc_event_t) -> libc::c_int
556556
EventType::AccountsChanged => 2302,
557557
EventType::AccountsItemChanged => 2303,
558558
EventType::EventChannelOverflow { .. } => 2400,
559+
EventType::IncomingCall { .. } => 2550,
560+
EventType::IncomingCallAccepted { .. } => 2560,
561+
EventType::OutgoingCallAccepted { .. } => 2570,
562+
EventType::CallEnded { .. } => 2580,
559563
#[allow(unreachable_patterns)]
560564
#[cfg(test)]
561565
_ => unreachable!("This is just to silence a rust_analyzer false-positive"),
@@ -619,7 +623,11 @@ pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc:
619623
EventType::WebxdcRealtimeData { msg_id, .. }
620624
| EventType::WebxdcStatusUpdate { msg_id, .. }
621625
| EventType::WebxdcRealtimeAdvertisementReceived { msg_id }
622-
| EventType::WebxdcInstanceDeleted { msg_id, .. } => msg_id.to_u32() as libc::c_int,
626+
| EventType::WebxdcInstanceDeleted { msg_id, .. }
627+
| EventType::IncomingCall { msg_id, .. }
628+
| EventType::IncomingCallAccepted { msg_id, .. }
629+
| EventType::OutgoingCallAccepted { msg_id, .. }
630+
| EventType::CallEnded { msg_id, .. } => msg_id.to_u32() as libc::c_int,
623631
EventType::ChatlistItemChanged { chat_id } => {
624632
chat_id.unwrap_or_default().to_u32() as libc::c_int
625633
}
@@ -671,6 +679,10 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc:
671679
| EventType::ChatModified(_)
672680
| EventType::ChatDeleted { .. }
673681
| EventType::WebxdcRealtimeAdvertisementReceived { .. }
682+
| EventType::IncomingCall { .. }
683+
| EventType::IncomingCallAccepted { .. }
684+
| EventType::OutgoingCallAccepted { .. }
685+
| EventType::CallEnded { .. }
674686
| EventType::EventChannelOverflow { .. } => 0,
675687
EventType::MsgsChanged { msg_id, .. }
676688
| EventType::ReactionsChanged { msg_id, .. }
@@ -767,8 +779,23 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
767779
| EventType::ChatlistChanged
768780
| EventType::AccountsChanged
769781
| EventType::AccountsItemChanged
770-
| EventType::WebxdcRealtimeAdvertisementReceived { .. }
771-
| EventType::EventChannelOverflow { .. } => ptr::null_mut(),
782+
| EventType::WebxdcRealtimeAdvertisementReceived { .. } => ptr::null_mut(),
783+
EventType::IncomingCall {
784+
place_call_info, ..
785+
} => {
786+
let data2 = place_call_info.to_c_string().unwrap_or_default();
787+
data2.into_raw()
788+
}
789+
EventType::IncomingCallAccepted {
790+
accept_call_info, ..
791+
}
792+
| EventType::OutgoingCallAccepted {
793+
accept_call_info, ..
794+
} => {
795+
let data2 = accept_call_info.to_c_string().unwrap_or_default();
796+
data2.into_raw()
797+
}
798+
EventType::CallEnded { .. } | EventType::EventChannelOverflow { .. } => ptr::null_mut(),
772799
EventType::ConfigureProgress { comment, .. } => {
773800
if let Some(comment) = comment {
774801
comment.to_c_string().unwrap_or_default().into_raw()
@@ -1167,6 +1194,61 @@ pub unsafe extern "C" fn dc_init_webxdc_integration(
11671194
.unwrap_or(0)
11681195
}
11691196

1197+
#[no_mangle]
1198+
pub unsafe extern "C" fn dc_place_outgoing_call(
1199+
context: *mut dc_context_t,
1200+
chat_id: u32,
1201+
place_call_info: *const libc::c_char,
1202+
) -> u32 {
1203+
if context.is_null() || chat_id == 0 {
1204+
eprintln!("ignoring careless call to dc_place_outgoing_call()");
1205+
return 0;
1206+
}
1207+
let ctx = &*context;
1208+
let chat_id = ChatId::new(chat_id);
1209+
let place_call_info = to_string_lossy(place_call_info);
1210+
1211+
block_on(ctx.place_outgoing_call(chat_id, place_call_info))
1212+
.context("Failed to place call")
1213+
.log_err(ctx)
1214+
.map(|msg_id| msg_id.to_u32())
1215+
.unwrap_or_log_default(ctx, "Failed to place call")
1216+
}
1217+
1218+
#[no_mangle]
1219+
pub unsafe extern "C" fn dc_accept_incoming_call(
1220+
context: *mut dc_context_t,
1221+
msg_id: u32,
1222+
accept_call_info: *const libc::c_char,
1223+
) -> libc::c_int {
1224+
if context.is_null() || msg_id == 0 {
1225+
eprintln!("ignoring careless call to dc_accept_incoming_call()");
1226+
return 0;
1227+
}
1228+
let ctx = &*context;
1229+
let msg_id = MsgId::new(msg_id);
1230+
let accept_call_info = to_string_lossy(accept_call_info);
1231+
1232+
block_on(ctx.accept_incoming_call(msg_id, accept_call_info))
1233+
.context("Failed to accept call")
1234+
.is_ok() as libc::c_int
1235+
}
1236+
1237+
#[no_mangle]
1238+
pub unsafe extern "C" fn dc_end_call(context: *mut dc_context_t, msg_id: u32) -> libc::c_int {
1239+
if context.is_null() || msg_id == 0 {
1240+
eprintln!("ignoring careless call to dc_end_call()");
1241+
return 0;
1242+
}
1243+
let ctx = &*context;
1244+
let msg_id = MsgId::new(msg_id);
1245+
1246+
block_on(ctx.end_call(msg_id))
1247+
.context("Failed to end call")
1248+
.log_err(ctx)
1249+
.is_ok() as libc::c_int
1250+
}
1251+
11701252
#[no_mangle]
11711253
pub unsafe extern "C" fn dc_set_draft(
11721254
context: *mut dc_context_t,

deltachat-jsonrpc/src/api/types/events.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,37 @@ pub enum EventType {
416416
/// Number of events skipped.
417417
n: u64,
418418
},
419+
420+
/// Incoming call.
421+
IncomingCall {
422+
/// ID of the info message referring to the call.
423+
msg_id: u32,
424+
/// User-defined info as passed to place_outgoing_call()
425+
place_call_info: String,
426+
},
427+
428+
/// Incoming call accepted.
429+
/// This is esp. interesting to stop ringing on other devices.
430+
IncomingCallAccepted {
431+
/// ID of the info message referring to the call.
432+
msg_id: u32,
433+
/// User-defined info passed to dc_accept_incoming_call()
434+
accept_call_info: String,
435+
},
436+
437+
/// Outgoing call accepted.
438+
OutgoingCallAccepted {
439+
/// ID of the info message referring to the call.
440+
msg_id: u32,
441+
/// User-defined info passed to dc_accept_incoming_call(
442+
accept_call_info: String,
443+
},
444+
445+
/// Call ended.
446+
CallEnded {
447+
/// ID of the info message referring to the call.
448+
msg_id: u32,
449+
},
419450
}
420451

421452
impl From<CoreEventType> for EventType {
@@ -566,6 +597,30 @@ impl From<CoreEventType> for EventType {
566597
CoreEventType::EventChannelOverflow { n } => EventChannelOverflow { n },
567598
CoreEventType::AccountsChanged => AccountsChanged,
568599
CoreEventType::AccountsItemChanged => AccountsItemChanged,
600+
CoreEventType::IncomingCall {
601+
msg_id,
602+
place_call_info,
603+
} => IncomingCall {
604+
msg_id: msg_id.to_u32(),
605+
place_call_info,
606+
},
607+
CoreEventType::IncomingCallAccepted {
608+
msg_id,
609+
accept_call_info,
610+
} => IncomingCallAccepted {
611+
msg_id: msg_id.to_u32(),
612+
accept_call_info,
613+
},
614+
CoreEventType::OutgoingCallAccepted {
615+
msg_id,
616+
accept_call_info,
617+
} => OutgoingCallAccepted {
618+
msg_id: msg_id.to_u32(),
619+
accept_call_info,
620+
},
621+
CoreEventType::CallEnded { msg_id } => CallEnded {
622+
msg_id: msg_id.to_u32(),
623+
},
569624
#[allow(unreachable_patterns)]
570625
#[cfg(test)]
571626
_ => unreachable!("This is just to silence a rust_analyzer false-positive"),

0 commit comments

Comments
 (0)