mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-05-03 20:46:28 +00:00
Handle is-composing notification coming from several participants.
This commit is contained in:
parent
8924272d72
commit
0bab59418d
7 changed files with 115 additions and 69 deletions
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue