Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
BC
public
liblinphone
Commits
0bab5941
Commit
0bab5941
authored
Oct 16, 2017
by
Ghislain MARY
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
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