Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
BC
public
liblinphone
Commits
0bab5941
Commit
0bab5941
authored
Oct 16, 2017
by
Ghislain MARY
Browse files
Handle is-composing notification coming from several participants.
parent
8924272d
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
115 additions
and
69 deletions
+115
-69
include/linphone/api/c-callbacks.h
include/linphone/api/c-callbacks.h
+3
-2
src/chat/chat-room/chat-room-p.h
src/chat/chat-room/chat-room-p.h
+6
-3
src/chat/chat-room/chat-room.cpp
src/chat/chat-room/chat-room.cpp
+27
-11
src/chat/chat-room/real-time-text-chat-room.cpp
src/chat/chat-room/real-time-text-chat-room.cpp
+1
-1
src/chat/notification/is-composing-listener.h
src/chat/notification/is-composing-listener.h
+3
-1
src/chat/notification/is-composing.cpp
src/chat/notification/is-composing.cpp
+63
-42
src/chat/notification/is-composing.h
src/chat/notification/is-composing.h
+12
-9
No files found.
include/linphone/api/c-callbacks.h
View file @
0bab5941
...
...
@@ -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
Linphone
Participant
*
participant
);
typedef
void
(
*
LinphoneChatRoomCbsIsComposingReceivedCb
)
(
LinphoneChatRoom
*
cr
,
const
Linphone
Address
*
remoteAddr
,
bool_t
isComposing
);
/**
* Callback used to notify a chat room that a message has been received.
...
...
src/chat/chat-room/chat-room-p.h
View file @
0bab5941
...
...
@@ -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
;
...
...
src/chat/chat-room/chat-room.cpp
View file @
0bab5941
...
...
@@ -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
()
{
...
...
src/chat/chat-room/real-time-text-chat-room.cpp
View file @
0bab5941
...
...
@@ -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
))
{
...
...
src/chat/notification/is-composing-listener.h
View file @
0bab5941
...
...
@@ -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
;
};
...
...
src/chat/notification/is-composing.cpp
View file @
0bab5941
...
...
@@ -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
();
stop
All
RemoteRefreshTimer
s
();
}
// -----------------------------------------------------------------------------
...
...
@@ -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
src/chat/notification/is-composing.h
View file @
0bab5941
...
...
@@ -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
*
>
remoteRefreshTimer
s
;
belle_sip_source_t
*
idleTimer
=
nullptr
;
belle_sip_source_t
*
refreshTimer
=
nullptr
;
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment