Commit 0bab5941 authored by Ghislain MARY's avatar Ghislain MARY

Handle is-composing notification coming from several participants.

parent 8924272d
......@@ -144,9 +144,10 @@ typedef void (*LinphoneChatMessageCbsFileTransferProgressIndicationCb)(LinphoneC
/**
* Is composing notification callback prototype.
* @param[in] cr #LinphoneChatRoom involved in the conversation
* @param[in] participant The #LinphoneParticipant that has sent the is-composing notification
* @param[in] remoteAddr The address that has sent the is-composing notification
* @param[in] isComposing A boolean value telling whether the remote is composing or not
*/
typedef void (*LinphoneChatRoomCbsIsComposingReceivedCb) (LinphoneChatRoom *cr, const LinphoneParticipant *participant);
typedef void (*LinphoneChatRoomCbsIsComposingReceivedCb) (LinphoneChatRoom *cr, const LinphoneAddress *remoteAddr, bool_t isComposing);
/**
* Callback used to notify a chat room that a message has been received.
......
......@@ -20,6 +20,8 @@
#ifndef _CHAT_ROOM_P_H_
#define _CHAT_ROOM_P_H_
#include <unordered_set>
#include "chat/notification/is-composing.h"
#include "chat-room.h"
#include "object/object-p.h"
......@@ -74,17 +76,18 @@ public:
protected:
void chatMessageReceived (const std::shared_ptr<ChatMessage> &msg);
void imdnReceived (const std::string &text);
void isComposingReceived (const std::string &text);
void isComposingReceived (const Address &remoteAddr, const std::string &text);
private:
void notifyChatMessageReceived (const std::shared_ptr<ChatMessage> &msg);
void notifyIsComposingReceived (const Address &remoteAddr, bool isComposing);
void notifyStateChanged ();
void notifyUndecryptableMessageReceived (const std::shared_ptr<ChatMessage> &msg);
private:
/* IsComposingListener */
void onIsComposingStateChanged (bool isComposing) override;
void onIsRemoteComposingStateChanged (bool isComposing) override;
void onIsRemoteComposingStateChanged (const Address &remoteAddr, bool isComposing) override;
void onIsComposingRefreshNeeded () override;
public:
......@@ -94,7 +97,7 @@ public:
Address peerAddress;
int unreadCount = -1;
bool isComposing = false;
bool remoteIsComposing = false;
std::unordered_set<std::string> remoteIsComposing;
std::list<std::shared_ptr<ChatMessage>> messages;
std::list<std::shared_ptr<ChatMessage>> transientMessages;
std::list<std::weak_ptr<ChatMessage>> weakMessages;
......
......@@ -400,7 +400,7 @@ LinphoneReason ChatRoomPrivate::messageReceived (SalOp *op, const SalMessage *sa
}
if (msg->getPrivate()->getContentType() == ContentType::ImIsComposing) {
isComposingReceived(msg->getPrivate()->getText());
isComposingReceived(msg->getFromAddress(), msg->getPrivate()->getText());
increaseMsgCount = FALSE;
if (lp_config_get_int(core->config, "sip", "deliver_imdn", 0) != 1) {
goto end;
......@@ -435,11 +435,11 @@ end:
// -----------------------------------------------------------------------------
void ChatRoomPrivate::chatMessageReceived (const shared_ptr<ChatMessage> &msg) {
L_Q();
if ((msg->getPrivate()->getContentType() != ContentType::Imdn) && (msg->getPrivate()->getContentType() != ContentType::ImIsComposing)) {
notifyChatMessageReceived(msg);
remoteIsComposing = false;
linphone_core_notify_is_composing_received(core, L_GET_C_BACK_PTR(q));
remoteIsComposing.erase(msg->getFromAddress().asStringUriOnly());
isComposingHandler.stopRemoteRefreshTimer(msg->getFromAddress().asStringUriOnly());
notifyIsComposingReceived(msg->getFromAddress(), false);
msg->sendDeliveryNotification(LinphoneReasonNone);
}
}
......@@ -449,8 +449,8 @@ void ChatRoomPrivate::imdnReceived (const string &text) {
Imdn::parse(*q, text);
}
void ChatRoomPrivate::isComposingReceived (const string &text) {
isComposingHandler.parse(text);
void ChatRoomPrivate::isComposingReceived (const Address &remoteAddr, const string &text) {
isComposingHandler.parse(remoteAddr, text);
}
// -----------------------------------------------------------------------------
......@@ -469,6 +469,20 @@ void ChatRoomPrivate::notifyChatMessageReceived (const shared_ptr<ChatMessage> &
linphone_core_notify_message_received(core, cr, L_GET_C_BACK_PTR(msg));
}
void ChatRoomPrivate::notifyIsComposingReceived (const Address &remoteAddr, bool isComposing) {
L_Q();
LinphoneChatRoom *cr = L_GET_C_BACK_PTR(q);
LinphoneChatRoomCbs *cbs = linphone_chat_room_get_callbacks(cr);
LinphoneChatRoomCbsIsComposingReceivedCb cb = linphone_chat_room_cbs_get_is_composing_received(cbs);
if (cb) {
LinphoneAddress *lAddr = linphone_address_new(remoteAddr.asString().c_str());
cb(cr, lAddr, !!isComposing);
linphone_address_unref(lAddr);
}
// Legacy notification
linphone_core_notify_is_composing_received(core, cr);
}
void ChatRoomPrivate::notifyStateChanged () {
L_Q();
LinphoneChatRoom *cr = L_GET_C_BACK_PTR(q);
......@@ -495,10 +509,12 @@ void ChatRoomPrivate::onIsComposingStateChanged (bool isComposing) {
sendIsComposingNotification();
}
void ChatRoomPrivate::onIsRemoteComposingStateChanged (bool isComposing) {
L_Q();
remoteIsComposing = isComposing;
linphone_core_notify_is_composing_received(core, L_GET_C_BACK_PTR(q));
void ChatRoomPrivate::onIsRemoteComposingStateChanged (const Address &remoteAddr, bool isComposing) {
if (isComposing)
remoteIsComposing.insert(remoteAddr.asStringUriOnly());
else
remoteIsComposing.erase(remoteAddr.asStringUriOnly());
notifyIsComposingReceived(remoteAddr, isComposing);
}
void ChatRoomPrivate::onIsComposingRefreshNeeded () {
......@@ -670,7 +686,7 @@ int ChatRoom::getUnreadMessagesCount () {
bool ChatRoom::isRemoteComposing () const {
L_D();
return d->remoteIsComposing;
return d->remoteIsComposing.size() > 0;
}
void ChatRoom::markAsRead () {
......
......@@ -59,7 +59,7 @@ void RealTimeTextChatRoomPrivate::realtimeTextReceived (uint32_t character, Linp
cmc->has_been_read = FALSE;
receivedRttCharacters.push_back(cmc);
remoteIsComposing = true;
remoteIsComposing.insert(peerAddress.asStringUriOnly());
linphone_core_notify_is_composing_received(core, L_GET_C_BACK_PTR(q));
if ((character == new_line) || (character == crlf) || (character == lf)) {
......
......@@ -26,12 +26,14 @@
LINPHONE_BEGIN_NAMESPACE
class Address;
class IsComposingListener {
public:
virtual ~IsComposingListener() = default;
virtual void onIsComposingStateChanged (bool isComposing) = 0;
virtual void onIsRemoteComposingStateChanged (bool isComposing) = 0;
virtual void onIsRemoteComposingStateChanged (const Address &remoteAddr, bool isComposing) = 0;
virtual void onIsComposingRefreshNeeded () = 0;
};
......
......@@ -17,6 +17,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <utility>
#include "linphone/utils/utils.h"
#include "chat/chat-room/chat-room-p.h"
......@@ -30,6 +32,16 @@ using namespace std;
LINPHONE_BEGIN_NAMESPACE
struct IsRemoteComposingData {
IsRemoteComposingData (IsComposing *isComposingHandler, string uri)
: isComposingHandler(isComposingHandler), uri(uri) {}
IsComposing *isComposingHandler;
string uri;
};
// -----------------------------------------------------------------------------
const string IsComposing::isComposingPrefix = "/xsi:isComposing";
// -----------------------------------------------------------------------------
......@@ -94,12 +106,12 @@ string IsComposing::marshal (bool isComposing) {
return content;
}
void IsComposing::parse (const string &text) {
void IsComposing::parse (const Address &remoteAddr, const string &text) {
xmlparsing_context_t *xmlCtx = linphone_xmlparsing_context_new();
xmlSetGenericErrorFunc(xmlCtx, linphone_xmlparsing_genericxml_error);
xmlCtx->doc = xmlReadDoc((const unsigned char *)text.c_str(), 0, nullptr, 0);
if (xmlCtx->doc)
parse(xmlCtx);
parse(xmlCtx, remoteAddr);
else
lWarning() << "Wrongly formatted presence XML: " << xmlCtx->errorBuffer;
linphone_xmlparsing_context_destroy(xmlCtx);
......@@ -125,29 +137,10 @@ void IsComposing::startRefreshTimer () {
}
}
void IsComposing::startRemoteRefreshTimer (const char *refreshStr) {
unsigned int duration = getRemoteRefreshTimerDuration();
if (refreshStr)
duration = static_cast<unsigned int>(Utils::stoi(refreshStr));
if (!remoteRefreshTimer) {
remoteRefreshTimer = core->sal->create_timer(remoteRefreshTimerExpired, this,
duration * 1000, "composing remote refresh timeout");
} else {
belle_sip_source_set_timeout(remoteRefreshTimer, duration * 1000);
}
}
#if 0
void IsComposing::idleTimerExpired () {
stopRefreshTimer();
stopIdleTimer();
}
#endif
void IsComposing::stopTimers () {
stopIdleTimer();
stopRefreshTimer();
stopRemoteRefreshTimer();
stopAllRemoteRefreshTimers();
}
// -----------------------------------------------------------------------------
......@@ -170,13 +163,10 @@ void IsComposing::stopRefreshTimer () {
}
}
void IsComposing::stopRemoteRefreshTimer () {
if (remoteRefreshTimer) {
if (core && core->sal)
core->sal->cancel_timer(remoteRefreshTimer);
belle_sip_object_unref(remoteRefreshTimer);
remoteRefreshTimer = nullptr;
}
void IsComposing::stopRemoteRefreshTimer (const string &uri) {
auto it = remoteRefreshTimers.find(uri);
if (it != remoteRefreshTimers.end())
stopRemoteRefreshTimer(it);
}
// -----------------------------------------------------------------------------
......@@ -196,7 +186,7 @@ unsigned int IsComposing::getRemoteRefreshTimerDuration () {
return remoteRefreshTimerDuration < 0 ? 0 : static_cast<unsigned int>(remoteRefreshTimerDuration);
}
void IsComposing::parse (xmlparsing_context_t *xmlCtx) {
void IsComposing::parse (xmlparsing_context_t *xmlCtx, const Address &remoteAddr) {
char xpathStr[MAX_XPATH_LENGTH];
char *stateStr = nullptr;
char *refreshStr = nullptr;
......@@ -225,47 +215,78 @@ void IsComposing::parse (xmlparsing_context_t *xmlCtx) {
if (stateStr) {
if (strcmp(stateStr, "active") == 0) {
state = true;
startRemoteRefreshTimer(refreshStr);
startRemoteRefreshTimer(remoteAddr.asStringUriOnly(), refreshStr);
} else {
stopRemoteRefreshTimer();
stopRemoteRefreshTimer(remoteAddr.asStringUriOnly());
}
listener->onIsRemoteComposingStateChanged(state);
listener->onIsRemoteComposingStateChanged(remoteAddr, state);
linphone_free_xml_text_content(stateStr);
}
if (refreshStr)
linphone_free_xml_text_content(refreshStr);
}
int IsComposing::idleTimerExpired (unsigned int revents) {
int IsComposing::idleTimerExpired () {
stopRefreshTimer();
stopIdleTimer();
listener->onIsComposingStateChanged(false);
return BELLE_SIP_STOP;
}
int IsComposing::refreshTimerExpired (unsigned int revents) {
int IsComposing::refreshTimerExpired () {
listener->onIsComposingRefreshNeeded();
return BELLE_SIP_CONTINUE;
}
int IsComposing::remoteRefreshTimerExpired (unsigned int revents) {
stopRemoteRefreshTimer();
listener->onIsRemoteComposingStateChanged(false);
int IsComposing::remoteRefreshTimerExpired (const string &uri) {
stopRemoteRefreshTimer(uri);
listener->onIsRemoteComposingStateChanged(Address(uri), false);
return BELLE_SIP_STOP;
}
void IsComposing::startRemoteRefreshTimer (const string &uri, const char *refreshStr) {
unsigned int duration = getRemoteRefreshTimerDuration();
if (refreshStr)
duration = static_cast<unsigned int>(Utils::stoi(refreshStr));
auto it = remoteRefreshTimers.find(uri);
if (it == remoteRefreshTimers.end()) {
IsRemoteComposingData *data = new IsRemoteComposingData(this, uri);
belle_sip_source_t *timer = core->sal->create_timer(remoteRefreshTimerExpired, data,
duration * 1000, "composing remote refresh timeout");
pair<string, belle_sip_source_t *> p(uri, timer);
remoteRefreshTimers.insert(p);
} else
belle_sip_source_set_timeout(it->second, duration * 1000);
}
void IsComposing::stopAllRemoteRefreshTimers () {
for (auto it = remoteRefreshTimers.begin(); it != remoteRefreshTimers.end();)
it = stopRemoteRefreshTimer(it);
}
unordered_map<string, belle_sip_source_t *>::iterator IsComposing::stopRemoteRefreshTimer (const unordered_map<string, belle_sip_source_t *>::const_iterator it) {
if (core && core->sal)
core->sal->cancel_timer(it->second);
belle_sip_object_unref(it->second);
return remoteRefreshTimers.erase(it);
}
int IsComposing::idleTimerExpired (void *data, unsigned int revents) {
IsComposing *d = reinterpret_cast<IsComposing *>(data);
return d->idleTimerExpired(revents);
return d->idleTimerExpired();
}
int IsComposing::refreshTimerExpired (void *data, unsigned int revents) {
IsComposing *d = reinterpret_cast<IsComposing *>(data);
return d->refreshTimerExpired(revents);
return d->refreshTimerExpired();
}
int IsComposing::remoteRefreshTimerExpired (void *data, unsigned int revents) {
IsComposing *d = reinterpret_cast<IsComposing *>(data);
return d->remoteRefreshTimerExpired(revents);
IsRemoteComposingData *d = reinterpret_cast<IsRemoteComposingData *>(data);
int result = d->isComposingHandler->remoteRefreshTimerExpired(d->uri);
delete d;
return result;
}
LINPHONE_END_NAMESPACE
......@@ -20,6 +20,8 @@
#ifndef _IS_COMPOSING_H_
#define _IS_COMPOSING_H_
#include <unordered_map>
#include "linphone/utils/general.h"
#include "chat/notification/is-composing-listener.h"
......@@ -36,24 +38,25 @@ public:
~IsComposing ();
std::string marshal (bool isComposing);
void parse (const std::string &content);
void parse (const Address &remoteAddr, const std::string &content);
void startIdleTimer ();
void startRefreshTimer ();
void startRemoteRefreshTimer (const char *refreshStr);
void stopComposing ();
void stopIdleTimer ();
void stopRefreshTimer ();
void stopRemoteRefreshTimer ();
void stopRemoteRefreshTimer (const std::string &uri);
void stopTimers ();
private:
unsigned int getIdleTimerDuration ();
unsigned int getRefreshTimerDuration ();
unsigned int getRemoteRefreshTimerDuration ();
void parse (xmlparsing_context_t *xmlCtx);
int idleTimerExpired (unsigned int revents);
int refreshTimerExpired (unsigned int revents);
int remoteRefreshTimerExpired (unsigned int revents);
void parse (xmlparsing_context_t *xmlCtx, const Address &remoteAddr);
int idleTimerExpired ();
int refreshTimerExpired ();
int remoteRefreshTimerExpired (const std::string &uri);
void startRemoteRefreshTimer (const std::string &uri, const char *refreshStr);
void stopAllRemoteRefreshTimers ();
std::unordered_map<std::string, belle_sip_source_t *>::iterator stopRemoteRefreshTimer (const std::unordered_map<std::string, belle_sip_source_t *>::const_iterator it);
static int idleTimerExpired (void *data, unsigned int revents);
static int refreshTimerExpired (void *data, unsigned int revents);
......@@ -67,7 +70,7 @@ private:
LinphoneCore *core = nullptr;
IsComposingListener *listener = nullptr;
belle_sip_source_t *remoteRefreshTimer = nullptr;
std::unordered_map<std::string, belle_sip_source_t *>remoteRefreshTimers;
belle_sip_source_t *idleTimer = nullptr;
belle_sip_source_t *refreshTimer = nullptr;
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment