Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add call ringing API #6650

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
130 changes: 130 additions & 0 deletions deltachat-ffi/deltachat.h
Original file line number Diff line number Diff line change
Expand Up @@ -1222,6 +1222,85 @@ void dc_set_webxdc_integration (dc_context_t* context, const char* f
uint32_t dc_init_webxdc_integration (dc_context_t* context, uint32_t chat_id);


/**
* Start an outgoing call.
* This sends a message with all relevant information to the callee,
* who will get informed by an #DC_EVENT_INCOMING_CALL event and rings.
*
* Possible actions during ringing:
* - caller cancels the call using dc_end_call(), callee receives #DC_EVENT_CALL_ENDED.
* - callee accepts using dc_accept_incoming_call(), caller receives #DC_EVENT_OUTGOING_CALL_ACCEPTED,
* callee's devices receive #DC_EVENT_INCOMING_CALL_ACCEPTED, call starts
* - callee rejects using dc_end_call(), caller receives #DC_EVENT_CALL_ENDED,
* callee's other devices receive #DC_EVENT_CALL_ENDED.
* - callee is already in a call. in this case,
* UI may decide to show a notification instead of ringing.
* otherwise, this is same as timeout.
* - timeout: after 1 minute without action, caller and callee receive #DC_EVENT_CALL_ENDED
* to prevent endless ringing of callee
* in case caller got offline without being able to send cancellation message.
*
* Actions during the call:
* - callee ends the call using dc_end_call(), caller receives #DC_EVENT_CALL_ENDED
* - caller ends the call using dc_end_call(), callee receives #DC_EVENT_CALL_ENDED
*
* Note, that the events are for updating the call screen,
* possible status messages are added and updated as usual, including the known events.
* In the UI, the sorted chatlist is used as an overview about calls as well as messages.
* To place a call with a contact that has no chat yet, use dc_create_chat_by_contact_id() first.
*
* UI will usually allow only one call at the same time,
* this has to be tracked by UI across profile, the core does not track this.
*
* @memberof dc_context_t
* @param context The context object.
* @param chat_id The chat to place a call for.
* This needs to be a one-to-one chat.
* @return ID of the system message announcing the call.
*/
uint32_t dc_place_outgoing_call (dc_context_t* context, uint32_t chat_id);


/**
* Accept incoming call.
*
* This implicitly accepts the contact request, if not yet done.
* All affected devices will receive
* either #DC_EVENT_OUTGOING_CALL_ACCEPTED or #DC_EVENT_INCOMING_CALL_ACCEPTED.
*
* @memberof dc_context_t
* @param context The context object.
* @param msg_id The ID of the call to accept.
* This is the ID reported by #DC_EVENT_INCOMING_CALL
* and equal to the ID of the corresponding info message.
* @return 1=success, 0=error
*/
int dc_accept_incoming_call (dc_context_t* context, uint32_t msg_id);


/**
* End incoming or outgoing call.
*
* From the view of the caller, a "cancellation",
* from the view of callee, a "rejection".
* If the call was accepted, this is a "hangup".
*
* For accepted calls,
* all participant devices get informed about the ended call via #DC_EVENT_CALL_ENDED.
* For not accepted calls, only the caller will inform the callee.
*
* If the callee rejects, the caller will get an timeout or give up at some point -
* same as for all other reasons the call cannot be established: Device not in reach, device muted, connectivity etc.
* This is to protect privacy of the callee, avoiding to check if callee is online.
*
* @memberof dc_context_t
* @param context The context object.
* @param msg_id the ID of the call.
* @return 1=success, 0=error
*/
int dc_end_call (dc_context_t* context, uint32_t msg_id);


/**
* Save a draft for a chat in the database.
*
Expand Down Expand Up @@ -4489,6 +4568,8 @@ int dc_msg_is_info (const dc_msg_t* msg);
* the UI should change the corresponding string using #DC_STR_INVALID_UNENCRYPTED_MAIL
* and also offer a way to fix the encryption, eg. by a button offering a QR scan
* - DC_INFO_WEBXDC_INFO_MESSAGE (32) - Info-message created by webxdc app sending `update.info`
* - DC_INFO_OUTGOING_CALL (50) - Info-message refers to an outgoing call
* - DC_INFO_INCOMING_CALL (55) - Info-message refers to an incoming call
*
* Even when you display an icon,
* you should still display the text of the informational message using dc_msg_get_text()
Expand Down Expand Up @@ -4517,6 +4598,8 @@ int dc_msg_get_info_type (const dc_msg_t* msg);
#define DC_INFO_PROTECTION_DISABLED 12
#define DC_INFO_INVALID_UNENCRYPTED_MAIL 13
#define DC_INFO_WEBXDC_INFO_MESSAGE 32
#define DC_INFO_OUTGOING_CALL 50
#define DC_INFO_INCOMING_CALL 55


/**
Expand Down Expand Up @@ -6514,6 +6597,53 @@ void dc_event_unref(dc_event_t* event);
*/
#define DC_EVENT_CHANNEL_OVERFLOW 2400



/**
* Incoming call.
* UI will usually start ringing,
* or show a notification if there is already a call in some profile.
*
* Together with this event,
* an info-message is added to the corresponding chat.
* The info-message, however, is _not_ additionally notified using #DC_EVENT_INCOMING_MSG,
* if needed, this has to be done by the UI explicitly.
*
* If user takes action, dc_accept_incoming_call() or dc_end_call() should be called.
*
* Otherwise, ringing should end on #DC_EVENT_CALL_ENDED
* or #DC_EVENT_INCOMING_CALL_ACCEPTED
*
* @param data1 (int) msg_id ID of the info-message referring to the call,
*/
#define DC_EVENT_INCOMING_CALL 2550

/**
* The callee accepted an incoming call on another another device using dc_accept_incoming_call().
* The caller gets the event #DC_EVENT_OUTGOING_CALL_ACCEPTED at the same time.
*
* The event is sent unconditionally when the corresponding message is received.
* UI should only take action in case call UI was opened before, otherwise the event should be ignored.
*/
#define DC_EVENT_INCOMING_CALL_ACCEPTED 2560

/**
* A call placed using dc_place_outgoing_call() was accepted by the callee using dc_accept_incoming_call().
*
* The event is sent unconditionally when the corresponding message is received.
* UI should only take action in case call UI was opened before, otherwise the event should be ignored.
*/
#define DC_EVENT_OUTGOING_CALL_ACCEPTED 2570

/**
* An incoming or outgoing call was ended using dc_end_call().
*
* The event is sent unconditionally when the corresponding message is received.
* UI should only take action in case call UI was opened before, otherwise the event should be ignored.
*/
#define DC_EVENT_CALL_ENDED 2580


/**
* @}
*/
Expand Down
59 changes: 58 additions & 1 deletion deltachat-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,10 @@ pub unsafe extern "C" fn dc_event_get_id(event: *mut dc_event_t) -> libc::c_int
EventType::AccountsChanged => 2302,
EventType::AccountsItemChanged => 2303,
EventType::EventChannelOverflow { .. } => 2400,
EventType::IncomingCall { .. } => 2550,
EventType::IncomingCallAccepted { .. } => 2560,
EventType::OutgoingCallAccepted { .. } => 2570,
EventType::CallEnded { .. } => 2580,
#[allow(unreachable_patterns)]
#[cfg(test)]
_ => unreachable!("This is just to silence a rust_analyzer false-positive"),
Expand Down Expand Up @@ -628,7 +632,11 @@ pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc:
EventType::WebxdcRealtimeData { msg_id, .. }
| EventType::WebxdcStatusUpdate { msg_id, .. }
| EventType::WebxdcRealtimeAdvertisementReceived { msg_id }
| EventType::WebxdcInstanceDeleted { msg_id, .. } => msg_id.to_u32() as libc::c_int,
| EventType::WebxdcInstanceDeleted { msg_id, .. }
| EventType::IncomingCall { msg_id, .. }
| EventType::IncomingCallAccepted { msg_id, .. }
| EventType::OutgoingCallAccepted { msg_id, .. }
| EventType::CallEnded { msg_id, .. } => msg_id.to_u32() as libc::c_int,
EventType::ChatlistItemChanged { chat_id } => {
chat_id.unwrap_or_default().to_u32() as libc::c_int
}
Expand Down Expand Up @@ -680,6 +688,10 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc:
| EventType::ChatModified(_)
| EventType::ChatDeleted { .. }
| EventType::WebxdcRealtimeAdvertisementReceived { .. }
| EventType::IncomingCall { .. }
| EventType::IncomingCallAccepted { .. }
| EventType::OutgoingCallAccepted { .. }
| EventType::CallEnded { .. }
| EventType::EventChannelOverflow { .. } => 0,
EventType::MsgsChanged { msg_id, .. }
| EventType::ReactionsChanged { msg_id, .. }
Expand Down Expand Up @@ -777,6 +789,10 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
| EventType::AccountsChanged
| EventType::AccountsItemChanged
| EventType::WebxdcRealtimeAdvertisementReceived { .. }
| EventType::IncomingCall { .. }
| EventType::IncomingCallAccepted { .. }
| EventType::OutgoingCallAccepted { .. }
| EventType::CallEnded { .. }
| EventType::EventChannelOverflow { .. } => ptr::null_mut(),
EventType::ConfigureProgress { comment, .. } => {
if let Some(comment) = comment {
Expand Down Expand Up @@ -1176,6 +1192,47 @@ pub unsafe extern "C" fn dc_init_webxdc_integration(
.unwrap_or(0)
}

#[no_mangle]
pub unsafe extern "C" fn dc_place_outgoing_call(context: *mut dc_context_t, chat_id: u32) -> u32 {
if context.is_null() || chat_id == 0 {
eprintln!("ignoring careless call to dc_place_outgoing_call()");
return 0;
}
let ctx = &*context;
let chat_id = ChatId::new(chat_id);

block_on(ctx.place_outgoing_call(chat_id))
.map(|msg_id| msg_id.to_u32())
.unwrap_or_log_default(ctx, "Failed to place call")
}

#[no_mangle]
pub unsafe extern "C" fn dc_accept_incoming_call(
context: *mut dc_context_t,
msg_id: u32,
) -> libc::c_int {
if context.is_null() || msg_id == 0 {
eprintln!("ignoring careless call to dc_accept_incoming_call()");
return 0;
}
let ctx = &*context;
let msg_id = MsgId::new(msg_id);

block_on(ctx.accept_incoming_call(msg_id)).is_ok() as libc::c_int
}

#[no_mangle]
pub unsafe extern "C" fn dc_end_call(context: *mut dc_context_t, msg_id: u32) -> libc::c_int {
if context.is_null() || msg_id == 0 {
eprintln!("ignoring careless call to dc_end_call()");
return 0;
}
let ctx = &*context;
let msg_id = MsgId::new(msg_id);

block_on(ctx.end_call(msg_id)).is_ok() as libc::c_int
}

#[no_mangle]
pub unsafe extern "C" fn dc_set_draft(
context: *mut dc_context_t,
Expand Down
25 changes: 25 additions & 0 deletions deltachat-jsonrpc/src/api/types/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,19 @@ pub enum EventType {
/// Number of events skipped.
n: u64,
},

/// Incoming call.
IncomingCall { msg_id: u32 },

/// Incoming call accepted.
/// This is esp. interesting to stop ringing on other devices.
IncomingCallAccepted { msg_id: u32 },

/// Outgoing call accepted.
OutgoingCallAccepted { msg_id: u32 },

/// Call ended.
CallEnded { msg_id: u32 },
}

impl From<CoreEventType> for EventType {
Expand Down Expand Up @@ -567,6 +580,18 @@ impl From<CoreEventType> for EventType {
CoreEventType::EventChannelOverflow { n } => EventChannelOverflow { n },
CoreEventType::AccountsChanged => AccountsChanged,
CoreEventType::AccountsItemChanged => AccountsItemChanged,
CoreEventType::IncomingCall { msg_id } => IncomingCall {
msg_id: msg_id.to_u32(),
},
CoreEventType::IncomingCallAccepted { msg_id } => IncomingCallAccepted {
msg_id: msg_id.to_u32(),
},
CoreEventType::OutgoingCallAccepted { msg_id } => OutgoingCallAccepted {
msg_id: msg_id.to_u32(),
},
CoreEventType::CallEnded { msg_id } => CallEnded {
msg_id: msg_id.to_u32(),
},
#[allow(unreachable_patterns)]
#[cfg(test)]
_ => unreachable!("This is just to silence a rust_analyzer false-positive"),
Expand Down
9 changes: 9 additions & 0 deletions deltachat-jsonrpc/src/api/types/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,11 @@ pub enum SystemMessageType {

/// This message contains a users iroh node address.
IrohNodeAddr,

OutgoingCall,
IncomingCall,
CallAccepted,
CallEnded,
}

impl From<deltachat::mimeparser::SystemMessage> for SystemMessageType {
Expand All @@ -454,6 +459,10 @@ impl From<deltachat::mimeparser::SystemMessage> for SystemMessageType {
SystemMessage::IrohNodeAddr => SystemMessageType::IrohNodeAddr,
SystemMessage::SecurejoinWait => SystemMessageType::SecurejoinWait,
SystemMessage::SecurejoinWaitTimeout => SystemMessageType::SecurejoinWaitTimeout,
SystemMessage::OutgoingCall => SystemMessageType::OutgoingCall,
SystemMessage::IncomingCall => SystemMessageType::IncomingCall,
SystemMessage::CallAccepted => SystemMessageType::CallAccepted,
SystemMessage::CallEnded => SystemMessageType::CallEnded,
}
}
}
Expand Down
Loading
Loading