Sets the volume using a linear scale [0,100].
diff --git a/bin/dbus/dbusclient.cpp b/bin/dbus/dbusclient.cpp
index f4970075e..a56f97494 100644
--- a/bin/dbus/dbusclient.cpp
+++ b/bin/dbus/dbusclient.cpp
@@ -177,6 +177,7 @@ DBusClient::initLibrary(int flags)
exportable_callback(bind(&DBusConfigurationManager::errorAlert, confM, _1)),
exportable_callback(bind(&DBusConfigurationManager::incomingAccountMessage, confM, _1, _2, _3 )),
exportable_callback(bind(&DBusConfigurationManager::accountMessageStatusChanged, confM, _1, _2, _3, _4 )),
+ exportable_callback(bind(&DBusConfigurationManager::composingStatusChanged, confM, _1, _2, _3 )),
exportable_callback(bind(&DBusConfigurationManager::incomingTrustRequest, confM, _1, _2, _3, _4 )),
exportable_callback(bind(&DBusConfigurationManager::contactAdded, confM, _1, _2, _3 )),
exportable_callback(bind(&DBusConfigurationManager::contactRemoved, confM, _1, _2, _3 )),
diff --git a/bin/dbus/dbusconfigurationmanager.cpp b/bin/dbus/dbusconfigurationmanager.cpp
index b71da1af5..068fcfa21 100644
--- a/bin/dbus/dbusconfigurationmanager.cpp
+++ b/bin/dbus/dbusconfigurationmanager.cpp
@@ -192,6 +192,12 @@ DBusConfigurationManager::cancelMessage(const std::string& accountID, const uint
return DRing::cancelMessage(accountID, id);
}
+void
+DBusConfigurationManager::setIsComposing(const std::string& accountID, const std::string& to, const bool& isWriting)
+{
+ DRing::setIsComposing(accountID, to, isWriting);
+}
+
auto
DBusConfigurationManager::getTlsDefaultSettings() -> decltype(DRing::getTlsDefaultSettings())
{
diff --git a/bin/dbus/dbusconfigurationmanager.h b/bin/dbus/dbusconfigurationmanager.h
index cad216eb0..18b72829b 100644
--- a/bin/dbus/dbusconfigurationmanager.h
+++ b/bin/dbus/dbusconfigurationmanager.h
@@ -88,6 +88,7 @@ class DRING_PUBLIC DBusConfigurationManager :
int getMessageStatus(const uint64_t& id);
int getMessageStatus(const std::string& accountID, const uint64_t& id);
bool cancelMessage(const std::string& accountID, const uint64_t& messageId);
+ void setIsComposing(const std::string& accountID, const std::string& to, const bool& isWriting);
std::map getTlsDefaultSettings();
std::vector getSupportedCiphers(const std::string& accountID);
std::vector getCodecList();
diff --git a/bin/jni/configurationmanager.i b/bin/jni/configurationmanager.i
index 03464636a..2c61adb02 100644
--- a/bin/jni/configurationmanager.i
+++ b/bin/jni/configurationmanager.i
@@ -35,6 +35,7 @@ public:
virtual void volatileAccountDetailsChanged(const std::string& account_id, const std::map& details){}
virtual void incomingAccountMessage(const std::string& /*account_id*/, const std::string& /*from*/, const std::map& /*payload*/){}
virtual void accountMessageStatusChanged(const std::string& /*account_id*/, uint64_t /*message_id*/, const std::string& /*to*/, int /*state*/){}
+ virtual void composingStatusChanged(const std::string& /*account_id*/, const std::string& /*from*/, int /*state*/){}
virtual void knownDevicesChanged(const std::string& /*account_id*/, const std::map& /*devices*/){}
virtual void exportOnRingEnded(const std::string& /*account_id*/, int /*state*/, const std::string& /*pin*/){}
@@ -92,6 +93,8 @@ std::vector getLastMessages(const std::string& accountID, uint64
int getMessageStatus(uint64_t id);
int getMessageStatus(const std::string& accountID, uint64_t id);
bool cancelMessage(const std::string& accountID, uint64_t id);
+void setIsComposing(const std::string& accountID, const std::string& to, bool isWriting);
+
bool changeAccountPassword(const std::string& accountID, const std::string& password_old, const std::string& password_new);
bool lookupName(const std::string& account, const std::string& nameserver, const std::string& name);
@@ -241,6 +244,7 @@ public:
virtual void volatileAccountDetailsChanged(const std::string& account_id, const std::map& details){}
virtual void incomingAccountMessage(const std::string& /*account_id*/, const std::string& /*from*/, const std::map& /*payload*/){}
virtual void accountMessageStatusChanged(const std::string& /*account_id*/, uint64_t /*message_id*/, const std::string& /*to*/, int /*state*/){}
+ virtual void composingStatusChanged(const std::string& /*account_id*/, const std::string& /*from*/, int /*state*/){}
virtual void knownDevicesChanged(const std::string& /*account_id*/, const std::map& /*devices*/){}
virtual void exportOnRingEnded(const std::string& /*account_id*/, int /*state*/, const std::string& /*pin*/){}
diff --git a/bin/jni/jni_interface.i b/bin/jni/jni_interface.i
index 1a9201cdc..304e208f1 100644
--- a/bin/jni/jni_interface.i
+++ b/bin/jni/jni_interface.i
@@ -263,6 +263,7 @@ void init(ConfigurationCallback* confM, Callback* callM, PresenceCallback* presM
exportable_callback(bind(&ConfigurationCallback::errorAlert, confM, _1)),
exportable_callback(bind(&ConfigurationCallback::incomingAccountMessage, confM, _1, _2, _3 )),
exportable_callback(bind(&ConfigurationCallback::accountMessageStatusChanged, confM, _1, _2, _3, _4 )),
+ exportable_callback(bind(&ConfigurationCallback::composingStatusChanged, confM, _1, _2, _3 )),
exportable_callback(bind(&ConfigurationCallback::incomingTrustRequest, confM, _1, _2, _3, _4 )),
exportable_callback(bind(&ConfigurationCallback::contactAdded, confM, _1, _2, _3 )),
exportable_callback(bind(&ConfigurationCallback::contactRemoved, confM, _1, _2, _3 )),
diff --git a/configure.ac b/configure.ac
index 333267ea0..69f688162 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@ dnl Jami - configure.ac for automake 1.9 and autoconf 2.59
dnl Process this file with autoconf to produce a configure script.
AC_PREREQ([2.65])
-AC_INIT([Jami Daemon],[7.8.0],[ring@gnu.org],[jami])
+AC_INIT([Jami Daemon],[7.9.0],[ring@gnu.org],[jami])
AC_COPYRIGHT([[Copyright (c) Savoir-faire Linux 2004-2019]])
AC_REVISION([$Revision$])
diff --git a/src/account.cpp b/src/account.cpp
index 0c8565401..86e92b4af 100644
--- a/src/account.cpp
+++ b/src/account.cpp
@@ -340,6 +340,12 @@ Account::getVolatileAccountDetails() const
};
}
+void
+Account::onIsComposing(const std::string& peer, bool isComposing)
+{
+ emitSignal(accountID_, peer, isComposing ? 1 : 0);
+}
+
bool
Account::hasActiveCodec(MediaType mediaType) const
{
diff --git a/src/account.h b/src/account.h
index e5271bab7..37ea96e97 100644
--- a/src/account.h
+++ b/src/account.h
@@ -153,8 +153,12 @@ class Account : public Serializable, public std::enable_shared_from_this& payloads UNUSED) { return 0; }
+ virtual uint64_t sendTextMessage(const std::string& /*to*/,
+ const std::map& /*payloads*/) { return 0; }
+
+ virtual void setIsComposing(const std::string& /*to*/, bool /*isWriting*/) {};
+
+ virtual void onIsComposing(const std::string& /*peer*/, bool /*isWriting*/);
virtual std::vector getLastMessages(const uint64_t& /*base_timestamp*/) {
return {};
diff --git a/src/client/configurationmanager.cpp b/src/client/configurationmanager.cpp
index 14b6767eb..102756fb7 100644
--- a/src/client/configurationmanager.cpp
+++ b/src/client/configurationmanager.cpp
@@ -298,6 +298,13 @@ cancelMessage(const std::string& accountID, uint64_t messageId)
return {};
}
+void
+setIsComposing(const std::string& accountID, const std::string& to, bool isWriting)
+{
+ if (const auto acc = jami::Manager::instance().getAccount(accountID))
+ return acc->setIsComposing(to, isWriting);
+}
+
bool
exportOnRing(const std::string& accountID, const std::string& password)
{
diff --git a/src/client/ring_signal.cpp b/src/client/ring_signal.cpp
index 2e42aed62..f61f1f44c 100644
--- a/src/client/ring_signal.cpp
+++ b/src/client/ring_signal.cpp
@@ -61,6 +61,7 @@ getSignalHandlers()
exported_callback(),
exported_callback(),
exported_callback(),
+ exported_callback(),
exported_callback(),
exported_callback(),
exported_callback(),
diff --git a/src/dring/configurationmanager_interface.h b/src/dring/configurationmanager_interface.h
index 18d694cac..6169cd0f9 100644
--- a/src/dring/configurationmanager_interface.h
+++ b/src/dring/configurationmanager_interface.h
@@ -81,6 +81,7 @@ DRING_PUBLIC std::vector getLastMessages(const std::string& accountID,
DRING_PUBLIC std::map getNearbyPeers(const std::string& accountID);
DRING_PUBLIC int getMessageStatus(uint64_t id);
DRING_PUBLIC int getMessageStatus(const std::string& accountID, uint64_t id);
+DRING_PUBLIC void setIsComposing(const std::string& accountID, const std::string& to, bool isWriting);
DRING_PUBLIC std::map getTlsDefaultSettings();
@@ -289,6 +290,10 @@ struct DRING_PUBLIC ConfigurationSignal {
constexpr static const char* name = "AccountMessageStatusChanged";
using cb_type = void(const std::string& /*account_id*/, uint64_t /*message_id*/, const std::string& /*to*/, int /*state*/);
};
+ struct DRING_PUBLIC ComposingStatusChanged {
+ constexpr static const char* name = "ComposingStatusChanged";
+ using cb_type = void(const std::string& /*account_id*/, const std::string& /*from*/, int /*status*/);
+ };
struct DRING_PUBLIC IncomingTrustRequest {
constexpr static const char* name = "IncomingTrustRequest";
using cb_type = void(const std::string& /*account_id*/, const std::string& /*from*/, const std::vector& payload, time_t received);
diff --git a/src/im/instant_messaging.cpp b/src/im/instant_messaging.cpp
index c6aebda12..12e271e7a 100644
--- a/src/im/instant_messaging.cpp
+++ b/src/im/instant_messaging.cpp
@@ -238,7 +238,7 @@ im::parseSipMessage(const pjsip_msg* msg)
}
// check if its a multipart message
- pj_str_t typeMultipart {const_cast("multipart"), 9};
+ constexpr pj_str_t typeMultipart {CONST_PJ_STR("multipart")};
if (pj_strcmp(&typeMultipart, &msg->body->content_type.type) != 0) {
// treat as single content type message
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index 004b06202..81fb11e13 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -2908,6 +2908,16 @@ JamiAccount::sendTextMessage(const std::string& to, const std::map& payload);
void sendTrustRequestConfirm(const std::string& to);
- virtual void sendTextMessage(const std::string& to, const std::map& payloads, uint64_t id, bool retryOnTimeout=true) override;
- virtual uint64_t sendTextMessage(const std::string& to, const std::map& payloads) override;
+ void sendTextMessage(const std::string& to, const std::map& payloads, uint64_t id, bool retryOnTimeout=true) override;
+ uint64_t sendTextMessage(const std::string& to, const std::map& payloads) override;
+ void onIsComposing(const std::string& peer, bool isWriting) override;
/* Devices */
void addDevice(const std::string& password);
diff --git a/src/sip/sipaccountbase.cpp b/src/sip/sipaccountbase.cpp
index 9b9bf8667..e848211e1 100644
--- a/src/sip/sipaccountbase.cpp
+++ b/src/sip/sipaccountbase.cpp
@@ -32,22 +32,27 @@
#include "config/yamlparser.h"
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#include
-#pragma GCC diagnostic pop
-
#include "client/ring_signal.h"
#include "string_utils.h"
#include "fileutils.h"
#include "sip_utils.h"
#include "utf8_utils.h"
-#include
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#include
+#pragma GCC diagnostic pop
+
#include
+#include
+
+#include
namespace jami {
+static constexpr const char MIME_TYPE_IM_COMPOSING[] {"application/im-iscomposing+xml"};
+static constexpr std::chrono::steady_clock::duration COMPOSING_TIMEOUT {std::chrono::seconds(12)};
+
SIPAccountBase::SIPAccountBase(const std::string& accountID)
: Account(accountID),
messageEngine_(*this, fileutils::get_cache_dir()+DIR_SEPARATOR_STR+getAccountID()+DIR_SEPARATOR_STR "messages"),
@@ -102,6 +107,53 @@ SIPAccountBase::flush()
fileutils::remove(fileutils::get_cache_dir() + DIR_SEPARATOR_STR + getAccountID() + DIR_SEPARATOR_STR "messages");
}
+std::string
+getIsComposing(bool isWriting)
+{
+ // implementing https://tools.ietf.org/rfc/rfc3994.txt
+ std::ostringstream ss;
+ ss << "" << std::endl
+ << "" << (isWriting ? "active" : "idle") << "";
+ return ss.str();
+}
+
+void
+SIPAccountBase::setIsComposing(const std::string& to, bool isWriting)
+{
+ if (not isWriting and to != composingUri_)
+ return;
+
+ if (composingTimeout_) {
+ composingTimeout_->cancel();
+ composingTimeout_.reset();
+ }
+ if (isWriting) {
+ if (not composingUri_.empty() and composingUri_ != to) {
+ sendTextMessage(composingUri_, {{MIME_TYPE_IM_COMPOSING, getIsComposing(false)}});
+ composingTime_ = std::chrono::steady_clock::time_point::min();
+ }
+ composingUri_.clear();
+ composingUri_.insert(composingUri_.end(), to.begin(), to.end());
+ auto now = std::chrono::steady_clock::now();
+ if (now >= composingTime_ + COMPOSING_TIMEOUT) {
+ sendTextMessage(composingUri_, {{MIME_TYPE_IM_COMPOSING, getIsComposing(true)}});
+ composingTime_ = now;
+ }
+ std::weak_ptr weak = std::static_pointer_cast(shared_from_this());
+ composingTimeout_ = Manager::instance().scheduleTask([weak, to](){
+ if (auto sthis = weak.lock()) {
+ sthis->sendTextMessage(to, {{MIME_TYPE_IM_COMPOSING, getIsComposing(false)}});
+ sthis->composingUri_.clear();
+ sthis->composingTime_ = std::chrono::steady_clock::time_point::min();
+ }
+ }, now + COMPOSING_TIMEOUT);
+ } else {
+ sendTextMessage(to, {{MIME_TYPE_IM_COMPOSING, getIsComposing(false)}});
+ composingUri_.clear();
+ composingTime_ = std::chrono::steady_clock::time_point::min();
+ }
+}
+
template
static void
validate(std::string &member, const std::string ¶m, const T& valid)
@@ -413,6 +465,22 @@ SIPAccountBase::onTextMessage(const std::string& from,
JAMI_WARN("Dropping invalid message with MIME type %s", m.first.c_str());
return;
}
+ if (m.first == MIME_TYPE_IM_COMPOSING) {
+ try {
+ static const std::regex COMPOSING_REGEX("\\s*(\\w+)\\s*<\\/state>");
+ std::smatch matched_pattern;
+ std::regex_search(m.second, matched_pattern, COMPOSING_REGEX);
+ bool isComposing {false};
+ if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
+ isComposing = matched_pattern[1] == "active";
+ }
+ onIsComposing(from, isComposing);
+ if (payloads.size() == 1)
+ return;
+ } catch (const std::exception& e) {
+ JAMI_WARN("Error parsing composing state: %s", e.what());
+ }
+ }
}
emitSignal(accountID_, from, payloads);
DRing::Message message;
diff --git a/src/sip/sipaccountbase.h b/src/sip/sipaccountbase.h
index 1ea26cca0..79421c2ee 100644
--- a/src/sip/sipaccountbase.h
+++ b/src/sip/sipaccountbase.h
@@ -31,8 +31,6 @@
#include "noncopyable.h"
#include "im/message_engine.h"
-#include
-
#include
#include
#include