call history

new item: popup button
This commit is contained in:
Gaelle Braud 2023-12-15 09:50:39 +01:00
parent 4ed07c9104
commit 97c01e66f8
28 changed files with 1379 additions and 234 deletions

View file

@ -34,6 +34,7 @@
#include "core/account/AccountCore.hpp"
#include "core/account/AccountProxy.hpp"
#include "core/call-history/CallHistoryProxy.hpp"
#include "core/call/CallCore.hpp"
#include "core/call/CallGui.hpp"
#include "core/call/CallList.hpp"
@ -157,6 +158,7 @@ void App::initCppInterfaces() {
qmlRegisterUncreatableType<AccountCore>(Constants::MainQmlUri, 1, 0, "AccountCore", QLatin1String("Uncreatable"));
qmlRegisterUncreatableType<CallCore>(Constants::MainQmlUri, 1, 0, "CallCore", QLatin1String("Uncreatable"));
qmlRegisterType<CallProxy>(Constants::MainQmlUri, 1, 0, "CallProxy");
qmlRegisterType<CallHistoryProxy>(Constants::MainQmlUri, 1, 0, "CallHistoryProxy");
qmlRegisterType<CallGui>(Constants::MainQmlUri, 1, 0, "CallGui");
qmlRegisterType<FriendGui>(Constants::MainQmlUri, 1, 0, "FriendGui");
qmlRegisterUncreatableType<FriendCore>(Constants::MainQmlUri, 1, 0, "FriendCore", QLatin1String("Uncreatable"));

View file

@ -8,6 +8,10 @@ list(APPEND _LINPHONEAPP_SOURCES
core/call/CallGui.cpp
core/call/CallList.cpp
core/call/CallProxy.cpp
core/call-history/CallHistoryCore.cpp
core/call-history/CallHistoryGui.cpp
core/call-history/CallHistoryList.cpp
core/call-history/CallHistoryProxy.cpp
core/camera/CameraGui.cpp
core/camera/CameraDummy.cpp
core/friend/FriendCore.cpp

View file

@ -23,11 +23,11 @@
#include "AccountList.hpp"
AccountProxy::AccountProxy(QObject *parent) : SortFilterProxy(parent) {
mList = AccountList::create();
connect(mList.get(), &AccountList::countChanged, this, &AccountProxy::resetDefaultAccount);
connect(mList.get(), &AccountList::defaultAccountChanged, this, &AccountProxy::resetDefaultAccount);
connect(mList.get(), &AccountList::haveAccountChanged, this, &AccountProxy::haveAccountChanged);
setSourceModel(mList.get());
mAccountList = AccountList::create();
connect(mAccountList.get(), &AccountList::countChanged, this, &AccountProxy::resetDefaultAccount);
connect(mAccountList.get(), &AccountList::defaultAccountChanged, this, &AccountProxy::resetDefaultAccount);
connect(mAccountList.get(), &AccountList::haveAccountChanged, this, &AccountProxy::haveAccountChanged);
setSourceModel(mAccountList.get());
sort(0);
}

View file

@ -58,7 +58,7 @@ protected:
QString mFilterText;
AccountGui *mDefaultAccount = nullptr; // When null, a new UI object is build from List
QSharedPointer<AccountList> mList;
QSharedPointer<AccountList> mAccountList;
};
#endif

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CallHistoryCore.hpp"
#include "core/App.hpp"
#include "model/call-history/CallHistoryModel.hpp"
#include "model/object/VariantObject.hpp"
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QDateTime>
DEFINE_ABSTRACT_OBJECT(CallHistoryCore)
QSharedPointer<CallHistoryCore> CallHistoryCore::create(const std::shared_ptr<linphone::CallLog> &callLog) {
auto sharedPointer = QSharedPointer<CallHistoryCore>(new CallHistoryCore(callLog), &QObject::deleteLater);
sharedPointer->setSelf(sharedPointer);
sharedPointer->moveToThread(App::getInstance()->thread());
return sharedPointer;
}
CallHistoryCore::CallHistoryCore(const std::shared_ptr<linphone::CallLog> &callLog) : QObject(nullptr) {
// qDebug() << "[CallHistoryCore] new" << this;
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
// Should be call from model Thread
mustBeInLinphoneThread(getClassName());
auto addr = callLog->getRemoteAddress()->clone();
addr->clean();
mRemoteAddress = Utils::coreStringToAppString(addr->asStringUriOnly());
// mRemoteAddress->clean();
mStatus = LinphoneEnums::fromLinphone(callLog->getStatus());
mDate = QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000);
mCallHistoryModel = std::make_shared<CallHistoryModel>(callLog);
mIsOutgoing = callLog->getDir() == linphone::Call::Dir::Outgoing;
mDuration = QString::number(callLog->getDuration());
}
CallHistoryCore::~CallHistoryCore() {
qDebug() << "[CallHistoryCore] delete" << this;
mustBeInMainThread("~" + getClassName());
}
void CallHistoryCore::setSelf(QSharedPointer<CallHistoryCore> me) {
mHistoryModelConnection = QSharedPointer<SafeConnection<CallHistoryCore, CallHistoryModel>>(
new SafeConnection<CallHistoryCore, CallHistoryModel>(me, mCallHistoryModel), &QObject::deleteLater);
}
QString CallHistoryCore::getDuration() const {
return mDuration;
}
void CallHistoryCore::setDuration(const QString &duration) {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (mDuration != duration) {
mDuration = duration;
emit durationChanged(mDuration);
}
}
void CallHistoryCore::remove() {
mHistoryModelConnection->invokeToModel([this]() { mCallHistoryModel->removeCallHistory(); });
}

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALL_HISTORY_CORE_H_
#define CALL_HISTORY_CORE_H_
#include "tool/LinphoneEnums.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QDateTime>
#include <QObject>
#include <QSharedPointer>
#include <linphone++/linphone.hh>
class CallHistoryModel;
class CallHistoryCore : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(QString remoteAddress MEMBER mRemoteAddress CONSTANT)
Q_PROPERTY(bool isOutgoing MEMBER mIsOutgoing CONSTANT)
Q_PROPERTY(QDateTime date MEMBER mDate CONSTANT)
Q_PROPERTY(LinphoneEnums::CallStatus status MEMBER mStatus CONSTANT)
Q_PROPERTY(QString duration READ getDuration WRITE setDuration NOTIFY durationChanged)
public:
static QSharedPointer<CallHistoryCore> create(const std::shared_ptr<linphone::CallLog> &callLogs);
CallHistoryCore(const std::shared_ptr<linphone::CallLog> &callLog);
~CallHistoryCore();
void setSelf(QSharedPointer<CallHistoryCore> me);
QString getDuration() const;
void setDuration(const QString &duration);
Q_INVOKABLE void remove();
QString mRemoteAddress;
QDateTime mDate;
bool mIsOutgoing;
LinphoneEnums::CallStatus mStatus;
signals:
void durationChanged(QString duration);
private:
QString mDuration;
std::shared_ptr<CallHistoryModel> mCallHistoryModel;
QSharedPointer<SafeConnection<CallHistoryCore, CallHistoryModel>> mHistoryModelConnection;
DECLARE_ABSTRACT_OBJECT
};
Q_DECLARE_METATYPE(CallHistoryCore *)
#endif

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CallHistoryGui.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(CallHistoryGui)
CallHistoryGui::CallHistoryGui(QSharedPointer<CallHistoryCore> core) {
// qDebug() << "[CallHistoryGui] new" << this;
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
mCore = core;
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
}
CallHistoryGui::~CallHistoryGui() {
mustBeInMainThread("~" + getClassName());
// qDebug() << "[CallHistoryGui] delete" << this;
}
CallHistoryCore *CallHistoryGui::getCore() const {
return mCore.get();
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALL_HISTORY_GUI_H_
#define CALL_HISTORY_GUI_H_
#include "CallHistoryCore.hpp"
#include <QObject>
#include <QSharedPointer>
class CallHistoryGui : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(CallHistoryCore *core READ getCore CONSTANT)
public:
CallHistoryGui(QSharedPointer<CallHistoryCore> core);
~CallHistoryGui();
CallHistoryCore *getCore() const;
QSharedPointer<CallHistoryCore> mCore;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,107 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CallHistoryList.hpp"
#include "CallHistoryGui.hpp"
#include "core/App.hpp"
#include "model/object/VariantObject.hpp"
#include "tool/Utils.hpp"
#include <QSharedPointer>
#include <linphone++/linphone.hh>
// =============================================================================
DEFINE_ABSTRACT_OBJECT(CallHistoryList)
QSharedPointer<CallHistoryList> CallHistoryList::create() {
auto model = QSharedPointer<CallHistoryList>(new CallHistoryList(), &QObject::deleteLater);
model->moveToThread(App::getInstance()->thread());
model->setSelf(model);
return model;
}
QSharedPointer<CallHistoryCore>
CallHistoryList::createCallHistoryCore(const std::shared_ptr<linphone::CallLog> &callLog) {
auto callHistoryCore = CallHistoryCore::create(callLog);
return callHistoryCore;
}
CallHistoryList::CallHistoryList(QObject *parent) : ListProxy(parent) {
mustBeInMainThread(getClassName());
}
CallHistoryList::~CallHistoryList() {
mustBeInMainThread("~" + getClassName());
mModelConnection = nullptr;
}
void CallHistoryList::setSelf(QSharedPointer<CallHistoryList> me) {
mModelConnection = QSharedPointer<SafeConnection<CallHistoryList, CoreModel>>(
new SafeConnection<CallHistoryList, CoreModel>(me, CoreModel::getInstance()), &QObject::deleteLater);
mModelConnection->makeConnectToCore(&CallHistoryList::lUpdate, [this]() {
mModelConnection->invokeToModel([this]() {
// Avoid copy to lambdas
QList<QSharedPointer<CallHistoryCore>> *callLogs = new QList<QSharedPointer<CallHistoryCore>>();
mustBeInLinphoneThread(getClassName());
auto linphoneCallLogs = CoreModel::getInstance()->getCore()->getCallLogs();
for (auto it : linphoneCallLogs) {
auto model = createCallHistoryCore(it);
callLogs->push_back(model);
}
mModelConnection->invokeToCore([this, callLogs]() {
mustBeInMainThread(getClassName());
resetData();
add(*callLogs);
delete callLogs;
});
});
});
mModelConnection->makeConnectToModel(&CoreModel::callLogUpdated,
[this]() { mModelConnection->invokeToCore([this]() { lUpdate(); }); });
lUpdate();
}
void CallHistoryList::removeAllEntries() {
beginResetModel();
for (auto it = mList.rbegin(); it != mList.rend(); ++it) {
auto callHistory = it->objectCast<CallHistoryCore>();
callHistory->remove();
}
mList.clear();
endResetModel();
}
void CallHistoryList::remove(const int &row) {
beginRemoveRows(QModelIndex(), row, row);
auto item = mList[row].objectCast<CallHistoryCore>();
item->remove();
endRemoveRows();
}
QVariant CallHistoryList::data(const QModelIndex &index, int role) const {
int row = index.row();
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();
if (role == Qt::DisplayRole) {
return QVariant::fromValue(new CallHistoryGui(mList[row].objectCast<CallHistoryCore>()));
}
return QVariant();
}

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALL_HISTORY_LIST_H_
#define CALL_HISTORY_LIST_H_
#include "../proxy/ListProxy.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QLocale>
class CallGui;
class CallHistoryCore;
class CoreModel;
// =============================================================================
class CallHistoryList : public ListProxy, public AbstractObject {
Q_OBJECT
public:
static QSharedPointer<CallHistoryList> create();
// Create a CallHistoryCore and make connections to List.
QSharedPointer<CallHistoryCore> createCallHistoryCore(const std::shared_ptr<linphone::CallLog> &callLog);
CallHistoryList(QObject *parent = Q_NULLPTR);
~CallHistoryList();
void setSelf(QSharedPointer<CallHistoryList> me);
void removeAllEntries();
void remove(const int &row);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
// virtual QHash<int, QByteArray> roleNames() const override {
// QHash<int, QByteArray> roles;
// roles[Qt::DisplayRole] = "gui";
// roles[Qt::DisplayRole + 1] = "name";
// roles[Qt::DisplayRole + 2] = "date";
// return roles;
// }
signals:
void lUpdate();
private:
// Check the state from CallHistoryCore: sender() must be a CallHistoryCore.
void onStatusChanged();
bool mHaveCallHistory = false;
QSharedPointer<SafeConnection<CallHistoryList, CoreModel>> mModelConnection;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CallHistoryProxy.hpp"
#include "CallHistoryGui.hpp"
#include "CallHistoryList.hpp"
#include "tool/Utils.hpp"
DEFINE_ABSTRACT_OBJECT(CallHistoryProxy)
CallHistoryProxy::CallHistoryProxy(QObject *parent) : SortFilterProxy(parent) {
mHistoryList = CallHistoryList::create();
setSourceModel(mHistoryList.get());
// sort(0);
}
CallHistoryProxy::~CallHistoryProxy() {
setSourceModel(nullptr);
}
QString CallHistoryProxy::getFilterText() const {
return mFilterText;
}
void CallHistoryProxy::setFilterText(const QString &filter) {
if (mFilterText != filter) {
mFilterText = filter;
invalidate();
emit filterTextChanged();
}
}
void CallHistoryProxy::removeAllEntries() {
static_cast<CallHistoryList *>(sourceModel())->removeAllEntries();
}
void CallHistoryProxy::removeEntriesWithFilter() {
std::list<QSharedPointer<CallHistoryCore>> itemList(rowCount());
for (auto i = rowCount() - 1; i >= 0; --i) {
auto item = getItemAt<CallHistoryList, CallHistoryCore>(i);
itemList.emplace_back(item);
}
for (auto item : itemList) {
mHistoryList->ListProxy::remove(item.get());
if (item) item->remove();
}
}
void CallHistoryProxy::updateView() {
mHistoryList->lUpdate();
}
bool CallHistoryProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
bool show = (mFilterText.isEmpty() || mFilterText == "*");
if (!show) {
QRegularExpression search(QRegularExpression::escape(mFilterText),
QRegularExpression::CaseInsensitiveOption |
QRegularExpression::UseUnicodePropertiesOption);
auto callLog = qobject_cast<CallHistoryList *>(sourceModel())->getAt<CallHistoryCore>(sourceRow);
show = callLog->mRemoteAddress.contains(search);
}
return show;
}
bool CallHistoryProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const {
auto l = getItemAt<CallHistoryList, CallHistoryCore>(left.row());
auto r = getItemAt<CallHistoryList, CallHistoryCore>(right.row());
return l->mDate < r->mDate;
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALL_HISTORY_PROXY_H_
#define CALL_HISTORY_PROXY_H_
#include "../proxy/SortFilterProxy.hpp"
#include "CallHistoryGui.hpp"
#include "CallHistoryList.hpp"
#include "tool/AbstractObject.hpp"
// =============================================================================
class CallHistoryProxy : public SortFilterProxy, public AbstractObject {
Q_OBJECT
Q_PROPERTY(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged)
public:
CallHistoryProxy(QObject *parent = Q_NULLPTR);
~CallHistoryProxy();
QString getFilterText() const;
void setFilterText(const QString &filter);
Q_INVOKABLE void removeAllEntries();
Q_INVOKABLE void removeEntriesWithFilter();
Q_INVOKABLE void updateView();
signals:
void filterTextChanged();
protected:
virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
QString mFilterText;
QSharedPointer<CallHistoryList> mHistoryList;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -36,6 +36,11 @@ public:
virtual int getCount() const;
virtual int getFilterType() const;
Q_INVOKABLE QVariant getAt(const int &index) const;
template <class A, class B>
QSharedPointer<B> getItemAt(const int &atIndex) const {
auto modelIndex = index(atIndex, 0);
return qobject_cast<A *>(sourceModel())->template getAt<B>(mapToSource(modelIndex).row());
}
Q_INVOKABLE void setSortOrder(const Qt::SortOrder &order);
virtual void setFilterType(int filterType);

View file

@ -58,6 +58,9 @@ list(APPEND _LINPHONEAPP_RC_FILES data/assistant/use-app-sip-account.rc
"data/image/randomAvatar.png"
"data/image/pause.svg"
"data/image/smiley.svg"
"data/image/trash-simple.svg"
"data/image/copy.svg"
"data/image/empty.svg"
data/shaders/roundEffect.vert.qsb
data/shaders/roundEffect.frag.qsb

View file

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.8285 12.6229L17.4503 15.9998L20.8285 19.3768C20.9238 19.472 20.9994 19.5852 21.051 19.7097C21.1026 19.8342 21.1291 19.9676 21.1291 20.1024C21.1291 20.2372 21.1026 20.3706 21.051 20.4951C20.9994 20.6196 20.9238 20.7327 20.8285 20.828C20.7332 20.9233 20.6201 20.9989 20.4956 21.0505C20.3711 21.1021 20.2377 21.1286 20.1029 21.1286C19.9681 21.1286 19.8347 21.1021 19.7102 21.0505C19.5857 20.9989 19.4725 20.9233 19.3773 20.828L16.0003 17.4498L12.6234 20.828C12.5281 20.9233 12.415 20.9989 12.2905 21.0505C12.166 21.1021 12.0325 21.1286 11.8978 21.1286C11.763 21.1286 11.6296 21.1021 11.5051 21.0505C11.3805 20.9989 11.2674 20.9233 11.1721 20.828C11.0768 20.7327 11.0012 20.6196 10.9497 20.4951C10.8981 20.3706 10.8716 20.2372 10.8716 20.1024C10.8716 19.9676 10.8981 19.8342 10.9497 19.7097C11.0012 19.5852 11.0768 19.472 11.1721 19.3768L14.5503 15.9998L11.1721 12.6229C10.9797 12.4305 10.8716 12.1694 10.8716 11.8973C10.8716 11.6251 10.9797 11.3641 11.1721 11.1716C11.3646 10.9792 11.6256 10.8711 11.8978 10.8711C12.1699 10.8711 12.431 10.9792 12.6234 11.1716L16.0003 14.5498L19.3773 11.1716C19.4725 11.0763 19.5857 11.0007 19.7102 10.9492C19.8347 10.8976 19.9681 10.8711 20.1029 10.8711C20.2377 10.8711 20.3711 10.8976 20.4956 10.9492C20.6201 11.0007 20.7332 11.0763 20.8285 11.1716C20.9238 11.2669 20.9994 11.3801 21.051 11.5046C21.1026 11.6291 21.1291 11.7625 21.1291 11.8973C21.1291 12.032 21.1026 12.1655 21.051 12.29C20.9994 12.4145 20.9238 12.5276 20.8285 12.6229ZM29.3337 15.9998C29.3337 18.6369 28.5517 21.2148 27.0866 23.4074C25.6215 25.6001 23.5391 27.3091 21.1028 28.3182C18.6664 29.3274 15.9855 29.5914 13.3991 29.077C10.8127 28.5625 8.43694 27.2926 6.57224 25.4279C4.70754 23.5632 3.43766 21.1875 2.92319 18.601C2.40872 16.0146 2.67277 13.3337 3.68194 10.8974C4.69111 8.46104 6.40007 6.37866 8.59273 4.91358C10.7854 3.44849 13.3632 2.6665 16.0003 2.6665C19.5354 2.67024 22.9246 4.07619 25.4243 6.57587C27.924 9.07554 29.3299 12.4648 29.3337 15.9998ZM27.2824 15.9998C27.2824 13.7685 26.6207 11.5872 25.381 9.73186C24.1413 7.87654 22.3793 6.43049 20.3178 5.57658C18.2563 4.72267 15.9878 4.49925 13.7993 4.93457C11.6108 5.36989 9.60054 6.4444 8.02272 8.02222C6.44489 9.60004 5.37038 11.6103 4.93506 13.7988C4.49974 15.9873 4.72316 18.2558 5.57707 20.3173C6.43099 22.3788 7.87703 24.1408 9.73236 25.3805C11.5877 26.6202 13.769 27.2819 16.0003 27.2819C18.9915 27.2785 21.8591 26.0888 23.9742 23.9737C26.0893 21.8586 27.279 18.991 27.2824 15.9998Z" fill="#6C7A87"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -4,6 +4,8 @@ list(APPEND _LINPHONEAPP_SOURCES
model/call/CallModel.cpp
model/call-history/CallHistoryModel.cpp
model/core/CoreModel.cpp
model/friend/FriendModel.cpp

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CallHistoryModel.hpp"
#include <QDebug>
#include "model/core/CoreModel.hpp"
#include "tool/Utils.hpp"
DEFINE_ABSTRACT_OBJECT(CallHistoryModel)
CallHistoryModel::CallHistoryModel(const std::shared_ptr<linphone::CallLog> &callLog, QObject *parent)
: callLog(callLog) {
mustBeInLinphoneThread(getClassName());
}
CallHistoryModel::~CallHistoryModel() {
qDebug() << "[CallHistoryModel] delete" << this;
mustBeInLinphoneThread("~" + getClassName());
}
void CallHistoryModel::removeCallHistory() {
CoreModel::getInstance()->getCore()->removeCallLog(callLog);
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALL_HISTORY_MODEL_H_
#define CALL_HISTORY_MODEL_H_
#include "model/listener/Listener.hpp"
#include "tool/AbstractObject.hpp"
#include <QObject>
#include <QTimer>
#include <linphone++/linphone.hh>
class CallHistoryModel : public QObject, public AbstractObject {
Q_OBJECT
public:
CallHistoryModel(const std::shared_ptr<linphone::CallLog> &callLog, QObject *parent = nullptr);
~CallHistoryModel();
void removeCallHistory();
private:
std::shared_ptr<linphone::CallLog> callLog;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -89,6 +89,7 @@ VariantObject *Utils::createCall(const QString &sipAddress,
auto app = App::getInstance();
auto window = app->getCallsWindow(callGui);
smartShowWindow(window);
// callGui.value<CallGui *>()->getCore()->lSetCameraEnabled(true);
});
return callGui;
} else return QVariant();
@ -174,7 +175,7 @@ QString Utils::createAvatar(const QUrl &fileUrl) {
return fileUri;
}
QString Utils::formatElapsedTime(int seconds) {
QString Utils::formatElapsedTime(int seconds, bool dotsSeparator) {
// s, m, h, d, W, M, Y
// 1, 60, 3600, 86400, 604800, 2592000, 31104000
auto y = floor(seconds / 31104000);
@ -192,14 +193,50 @@ QString Utils::formatElapsedTime(int seconds) {
QString hours, min, sec;
if (h < 10 && h > 0) hours = "0";
if (dotsSeparator && h < 10 && h > 0) hours = "0";
hours.append(QString::number(h));
if (m < 10) min = "0";
if (dotsSeparator && m < 10) min = "0";
min.append(QString::number(m));
if (s < 10) sec = "0";
if (dotsSeparator && s < 10) sec = "0";
sec.append(QString::number(s));
return (h == 0 ? "" : hours + ":") + min + ":" + sec;
if (dotsSeparator) return (h == 0 ? "" : hours + ":") + min + ":" + sec;
else return (h == 0 ? "" : hours + "h ") + (m == 0 ? "" : min + "min ") + sec + "s";
}
QString Utils::formatDate(const QDateTime &date, bool includeTime) {
QString format = date.date().year() == QDateTime::currentDateTime().date().year() ? "dd MMMM" : "dd MMMM yyyy";
auto dateDay = tr(date.date().toString(format).toLocal8Bit().data());
if (!includeTime) return dateDay;
auto time = date.time().toString("hh:mm");
return dateDay + " | " + time;
}
QString Utils::formatDateElapsedTime(const QDateTime &date) {
// auto y = floor(seconds / 31104000);
// if (y > 0) return QString::number(y) + " years";
// auto M = floor(seconds / 2592000);
// if (M > 0) return QString::number(M) + " months";
// auto w = floor(seconds / 604800);
// if (w > 0) return QString::number(w) + " week";
auto dateSec = date.secsTo(QDateTime::currentDateTime());
auto d = floor(dateSec / 86400);
if (d > 7) {
return formatDate(date, false);
} else if (d > 0) {
return tr(date.date().toString("dddd").toLocal8Bit().data());
}
auto h = floor(dateSec / 3600);
if (h > 0) return QString::number(h) + " h";
auto m = floor((dateSec - h * 3600) / 60);
if (m > 0) return QString::number(m) + " m";
auto s = dateSec - h * 3600 - m * 60;
return QString::number(s) + " s";
}

View file

@ -64,7 +64,10 @@ public:
Q_INVOKABLE static VariantObject *haveAccount();
Q_INVOKABLE static void smartShowWindow(QQuickWindow *window);
Q_INVOKABLE static QString createAvatar(const QUrl &fileUrl); // Return the avatar path
Q_INVOKABLE static QString formatElapsedTime(int seconds); // Return the elapsed time formated
Q_INVOKABLE static QString formatElapsedTime(int seconds,
bool dotsSeparator = true); // Return the elapsed time formated
Q_INVOKABLE static QString formatDate(const QDateTime &date, bool includeTime = true); // Return the date formated
Q_INVOKABLE static QString formatDateElapsedTime(const QDateTime &date); // Return the date formated
static inline QString coreStringToAppString(const std::string &str) {
if (Constants::LinphoneLocaleEncoding == QString("UTF-8")) return QString::fromStdString(str);

View file

@ -121,9 +121,9 @@ Window {
component BottomButton : Button {
id: bottomButton
required property string enabledIcon
property string disabledIcon
id: bottomButton
enabled: call != undefined
padding: 18 * DefaultStyle.dp
checkable: true
@ -367,7 +367,8 @@ Window {
Avatar {
Layout.alignment: Qt.AlignCenter
// TODO : remove username when friend list ready
address: mainWindow.peerNameText
call: mainWindow.call
// address: mainWindow.peerNameText
Layout.preferredWidth: 120 * DefaultStyle.dp
Layout.preferredHeight: 120 * DefaultStyle.dp
}
@ -414,6 +415,7 @@ Window {
}
Text {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.leftMargin: 10 * DefaultStyle.dp
anchors.bottomMargin: 10 * DefaultStyle.dp
@ -528,15 +530,13 @@ Window {
id: callList
model: callsModel
height: contentHeight
onHeightChanged: console.log("height changzed lustviexw", height, contentHeight)
spacing: 15 * DefaultStyle.dp
onCountChanged: forceLayout()
delegate: Item {
anchors.left: parent.left
anchors.right: parent.right
id: callDelegate
width: callList.width
height: 45 * DefaultStyle.dp
RowLayout {
@ -567,118 +567,62 @@ Window {
|| modelData.core.state === LinphoneEnums.CallState.PausedByRemote
? qsTr("Appel en pause") : qsTr("Appel en cours")
}
Button {
PopupButton {
id: listCallOptionsButton
checked: listCallOptionsMenu.visible
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
Layout.alignment: Qt.AlignRight
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
background: Rectangle {
anchors.fill: listCallOptionsButton
opacity: listCallOptionsButton.checked ? 1 : 0
color: DefaultStyle.main2_300
radius: 40 * DefaultStyle.dp
}
contentItem: Image {
source: AppIcons.verticalDots
sourceSize.width: 24 * DefaultStyle.dp
sourceSize.height: 24 * DefaultStyle.dp
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
}
onPressed: {
console.log("listCallOptionsMenu visible", listCallOptionsMenu.visible, "opened", listCallOptionsMenu.opened)
if (listCallOptionsMenu.visible){
console.log("close popup")
listCallOptionsMenu.close()
}
else {
console.log("open popup")
listCallOptionsMenu.open()
}
}
Control.Popup {
id: listCallOptionsMenu
x: - width
y: listCallOptionsButton.height
onVisibleChanged: console.log("popup visible", visible)
closePolicy: Popup.CloseOnPressOutsideParent | Popup.CloseOnEscape
padding: 20 * DefaultStyle.dp
background: Item {
anchors.fill: parent
Rectangle {
id: callOptionsMenuPopup
anchors.fill: parent
color: DefaultStyle.grey_0
radius: 16 * DefaultStyle.dp
}
MultiEffect {
source: callOptionsMenuPopup
anchors.fill: callOptionsMenuPopup
shadowEnabled: true
shadowBlur: 1
shadowColor: DefaultStyle.grey_900
shadowOpacity: 0.4
}
}
contentItem: ColumnLayout {
spacing: 0
Control.Button {
background: Item {}
contentItem: RowLayout {
Image {
source: modelData.core.state === LinphoneEnums.CallState.Paused
|| modelData.core.state === LinphoneEnums.CallState.PausedByRemote
? AppIcons.phone : AppIcons.pause
sourceSize.width: 32 * DefaultStyle.dp
sourceSize.height: 32 * DefaultStyle.dp
Layout.preferredWidth: 32 * DefaultStyle.dp
Layout.preferredHeight: 32 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
}
Text {
text: modelData.core.state === LinphoneEnums.CallState.Paused
|| modelData.core.state === LinphoneEnums.CallState.PausedByRemote
? qsTr("Reprendre l'appel") : qsTr("Mettre en pause")
color: DefaultStyle.main2_500main
Layout.preferredWidth: metrics.width
}
TextMetrics {
id: metrics
text: qsTr("Reprendre l'appel")
}
Item {
Layout.fillWidth: true
}
popup.contentItem: ColumnLayout {
spacing: 0
Control.Button {
background: Item {}
contentItem: RowLayout {
Image {
source: modelData.core.state === LinphoneEnums.CallState.Paused
|| modelData.core.state === LinphoneEnums.CallState.PausedByRemote
? AppIcons.phone : AppIcons.pause
sourceSize.width: 32 * DefaultStyle.dp
sourceSize.height: 32 * DefaultStyle.dp
Layout.preferredWidth: 32 * DefaultStyle.dp
Layout.preferredHeight: 32 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
}
onClicked: modelData.core.lSetPaused(!modelData.core.paused)
}
Control.Button {
background: Item {}
contentItem: RowLayout {
EffectImage {
image.source: AppIcons.endCall
colorizationColor: DefaultStyle.danger_500main
width: 32 * DefaultStyle.dp
height: 32 * DefaultStyle.dp
}
Text {
color: DefaultStyle.danger_500main
text: qsTr("Terminer l'appel")
}
Item {
Layout.fillWidth: true
}
Text {
text: modelData.core.state === LinphoneEnums.CallState.Paused
|| modelData.core.state === LinphoneEnums.CallState.PausedByRemote
? qsTr("Reprendre l'appel") : qsTr("Mettre en pause")
color: DefaultStyle.main2_500main
Layout.preferredWidth: metrics.width
}
TextMetrics {
id: metrics
text: qsTr("Reprendre l'appel")
}
Item {
Layout.fillWidth: true
}
onClicked: mainWindow.endCall(modelData)
}
onClicked: modelData.core.lSetPaused(!modelData.core.paused)
}
Control.Button {
background: Item {}
contentItem: RowLayout {
EffectImage {
image.source: AppIcons.endCall
colorizationColor: DefaultStyle.danger_500main
width: 32 * DefaultStyle.dp
height: 32 * DefaultStyle.dp
}
Text {
color: DefaultStyle.danger_500main
text: qsTr("Terminer l'appel")
}
Item {
Layout.fillWidth: true
}
}
onClicked: mainWindow.endCall(modelData)
}
}
}

View file

@ -15,18 +15,16 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Item/Prototype/CanvasCircle.qml
view/Item/BusyIndicator.qml
view/Item/Button.qml
view/Item/Carousel.qml
view/Item/CheckBox.qml
view/Item/ComboBox.qml
view/Item/Contact/Avatar.qml
view/Item/Contact/Contact.qml
view/Item/Contact/ContactDescription.qml
view/Item/Contact/Sticker.qml
view/Item/BusyIndicator.qml
view/Item/Button.qml
view/Item/Carousel.qml
view/Item/CheckBox.qml
view/Item/ComboBox.qml
view/Item/DesktopPopup.qml
view/Item/DigitInput.qml
view/Item/EffectImage.qml
@ -35,6 +33,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Item/PhoneNumberComboBox.qml
view/Item/PhoneNumberInput.qml
view/Item/Popup.qml
view/Item/PopupButton.qml
view/Item/RadioButton.qml
view/Item/RectangleTest.qml
view/Item/SearchBar.qml

View file

@ -104,7 +104,7 @@ Rectangle{
width: 22 * DefaultStyle.dp
height: 22 * DefaultStyle.dp
radius: width/2
color: DefaultStyle.danger_500
color: DefaultStyle.danger_500main
border.color: DefaultStyle.grey_0
border.width: 2 * DefaultStyle.dp
Text{

View file

@ -12,6 +12,7 @@ Notification {
readonly property var call: notificationData && notificationData.call
property var state: call.core.state
onStateChanged:{
console.log("state notif", state)
if(state != LinphoneEnums.CallState.IncomingReceived){
close()
}

View file

@ -0,0 +1,62 @@
import QtQuick 2.7
import QtQuick.Controls 2.2 as Control
import QtQuick.Effects
import Linphone
Button {
id: mainItem
property alias popup: popup
checked: popup.visible
implicitWidth: 24 * DefaultStyle.dp
implicitHeight: 24 * DefaultStyle.dp
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
function closePopup() {
popup.close()
}
background: Rectangle {
anchors.fill: mainItem
visible: mainItem.checked
color: DefaultStyle.main2_300
radius: 40 * DefaultStyle.dp
}
contentItem: Image {
source: AppIcons.verticalDots
sourceSize.width: 24 * DefaultStyle.dp
sourceSize.height: 24 * DefaultStyle.dp
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
}
onPressed: {
if (popup.visible) popup.close()
else popup.open()
}
Control.Popup {
id: popup
x: - width
y: mainItem.height
closePolicy: Popup.CloseOnPressOutsideParent | Popup.CloseOnEscape
padding: 20 * DefaultStyle.dp
background: Item {
anchors.fill: parent
Rectangle {
id: callOptionsMenuPopup
anchors.fill: parent
color: DefaultStyle.grey_0
radius: 16 * DefaultStyle.dp
}
MultiEffect {
source: callOptionsMenuPopup
anchors.fill: callOptionsMenuPopup
shadowEnabled: true
shadowBlur: 1
shadowColor: DefaultStyle.grey_900
shadowOpacity: 0.4
}
}
}
}

View file

@ -15,9 +15,8 @@ Item {
property string newItemIconSource
property string emptyListText
property alias leftPanelContent: leftPanel.children
property var rightPanelContent: rightPanelItem.children
property alias rightPanelContent: rightPanelItem.children
property bool showDefaultItem: true
// onShowDefaultItemChanged: stackView.replace(showDefaultItem ? defaultItem : rightPanelItem)
signal noItemButtonPressed()
Control.SplitView {
@ -31,7 +30,7 @@ Item {
ColumnLayout {
id: leftPanel
Control.SplitView.preferredWidth: 280 * DefaultStyle.dp
Control.SplitView.preferredWidth: 350 * DefaultStyle.dp
}
Rectangle {
id: rightPanel
@ -44,6 +43,7 @@ Item {
id: defaultItem
Layout.fillWidth: true
Layout.fillHeight: true
RowLayout {
Layout.fillHeight: true
Layout.fillWidth: true
@ -105,7 +105,7 @@ Item {
}
}
Item {
ColumnLayout {
id: rightPanelItem
Layout.fillWidth: true
Layout.fillHeight: true
@ -114,4 +114,3 @@ Item {
}
}
}

View file

@ -1,4 +1,5 @@
import QtQuick 2.15
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls as Control
import Linphone
@ -10,26 +11,33 @@ AbstractMainPage {
emptyListText: qsTr("Historique d'appel vide")
newItemIconSource: AppIcons.newCall
property var selectedRowHistoryGui
signal listViewUpdated()
onNoItemButtonPressed: goToNewCall()
showDefaultItem: listStackView.currentItem.listView ? listStackView.currentItem.listView.count === 0 : true
function goToNewCall() {
listStackView.push(newCallItem)
}
leftPanelContent: Item {
id: leftPanel
Layout.fillWidth: true
Layout.fillHeight: true
Control.StackView {
id: listStackView
clip: true
initialItem: listItem
initialItem: historyListItem
anchors.fill: parent
property int sideMargin: 25 * DefaultStyle.dp
// anchors.leftMargin: 25
// anchors.rightMargin: 25
}
Component {
id: listItem
id: historyListItem
ColumnLayout {
RowLayout {
@ -45,12 +53,35 @@ AbstractMainPage {
Item {
Layout.fillWidth: true
}
Control.Button {
enabled: false
background: Item {
}
contentItem: Image {
source: AppIcons.verticalDots
PopupButton {
id: removeHistory
popup.x: 0
popup.padding: 10 * DefaultStyle.dp
popup.contentItem: Button {
background: Item{}
contentItem: RowLayout {
EffectImage {
image.source: AppIcons.trashCan
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
image.fillMode: Image.PreserveAspectFit
colorizationColor: DefaultStyle.danger_500main
}
Text {
text: qsTr("Supprimmer lhistorique")
color: DefaultStyle.danger_500main
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
}
onClicked: {
historyListView.model.removeAllEntries()
removeHistory.closePopup()
}
}
}
Control.Button {
@ -65,112 +96,200 @@ AbstractMainPage {
fillMode: Image.PreserveAspectFit
}
onClicked: {
console.log("[CallPage]User: create new call")
console.debug("[CallPage]User: create new call")
listStackView.push(newCallItem)
}
}
}
Control.Control {
id: listLayout
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: listStackView.sideMargin
Layout.rightMargin: listStackView.sideMargin
RowLayout {
Control.Control {
id: listLayout
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: listStackView.sideMargin
Layout.rightMargin: listStackView.sideMargin
background: Rectangle {
anchors.fill: parent
}
ColumnLayout {
anchors.fill: parent
SearchBar {
id: searchBar
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
placeholderText: qsTr("Rechercher un appel")
background: Rectangle {
anchors.fill: parent
}
ColumnLayout {
Text {
text: qsTr("Aucun appel")
font {
pixelSize: 16 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
}
visible: listView.count === 0
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 30 * DefaultStyle.dp
}
ListView {
id: listView
clip: true
anchors.fill: parent
SearchBar {
id: searchBar
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
Layout.fillHeight: true
model: 0
currentIndex: 0
delegate: Item {
required property int index
width:listView.width
height: 30 * DefaultStyle.dp
// RectangleTest{}
RowLayout {
anchors.fill: parent
Image {
source: AppIcons.info
}
ColumnLayout {
Text {
text: "John Doe"
}
// RowLayout {
// Image {
// source: AppIcons.incomingCall
// }
// Text {
// text: "info sur l'appel"
// }
// }
}
Item {
Layout.fillWidth: true
}
Control.Button {
implicitWidth: 30 * DefaultStyle.dp
implicitHeight: 30 * DefaultStyle.dp
background: Item {
visible: false
}
contentItem: Image {
source: AppIcons.phone
width: 20 * DefaultStyle.dp
sourceSize.width: 20 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
}
}
placeholderText: qsTr("Rechercher un appel")
}
ColumnLayout {
Text {
text: qsTr("Aucun appel")
font {
pixelSize: 16 * DefaultStyle.dp
weight: 800 * DefaultStyle.dp
}
MouseArea {
hoverEnabled: true
Rectangle {
visible: historyListView.count === 0
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 30 * DefaultStyle.dp
}
ListView {
id: historyListView
clip: true
Layout.fillWidth: true
Layout.fillHeight: true
Layout.topMargin: 30 * DefaultStyle.dp
model: CallHistoryProxy{
filterText: searchBar.text
}
currentIndex: -1
onCurrentIndexChanged: {
mainItem.selectedRowHistoryGui = model.getAt(currentIndex)
}
spacing: 10 * DefaultStyle.dp
highlightMoveDuration: 10
highlightMoveVelocity: -1
// highlightFollowsCurrentItem: true
highlight: Rectangle {
x: historyListView.x
width: historyListView.width
height: historyListView.height
color: DefaultStyle.main2_100
y: historyListView.currentItem? historyListView.currentItem.y : 0
}
delegate: Item {
width:historyListView.width
height: 56 * DefaultStyle.dp
anchors.topMargin: 5 * DefaultStyle.dp
anchors.bottomMargin: 5 * DefaultStyle.dp
RowLayout {
z: 1
anchors.fill: parent
opacity: 0.1
radius: 15 * DefaultStyle.dp
color: DefaultStyle.main2_500main
visible: parent.containsMouse
Item {
Layout.preferredWidth: historyAvatar.width
Layout.preferredHeight: historyAvatar.height
Layout.leftMargin: 5 * DefaultStyle.dp
MultiEffect {
source: historyAvatar
anchors.fill: historyAvatar
shadowEnabled: true
shadowBlur: 1
shadowColor: DefaultStyle.grey_900
shadowOpacity: 0.1
}
Avatar {
id: historyAvatar
address: modelData.core.remoteAddress
width: 45 * DefaultStyle.dp
height: 45 * DefaultStyle.dp
}
}
ColumnLayout {
Layout.alignment: Qt.AlignVCenter
Text {
property var remoteAddress: modelData ? UtilsCpp.getDisplayName(modelData.core.remoteAddress) : undefined
text: remoteAddress ? remoteAddress.value : ""
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
RowLayout {
Image {
source: modelData.core.status === LinphoneEnums.CallStatus.Declined
|| modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere
|| modelData.core.status === LinphoneEnums.CallStatus.Aborted
|| modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted
? modelData.core.isOutgoing
? AppIcons.outgoingCallRejected
: AppIcons.incomingCallRejected
: modelData.core.status === LinphoneEnums.CallStatus.Missed
? modelData.core.isOutgoing
? AppIcons.outgoingCallMissed
: AppIcons.incomingCallMissed
: modelData.core.isOutgoing
? AppIcons.outgoingCall
: AppIcons.incomingCall
Layout.preferredWidth: 5 * DefaultStyle.dp
Layout.preferredHeight: 5 * DefaultStyle.dp
sourceSize.width: 5 * DefaultStyle.dp
sourceSize.height: 5 * DefaultStyle.dp
}
Text {
// text: modelData.core.date
text: UtilsCpp.formatDateElapsedTime(modelData.core.date)
font {
pixelSize: 12 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
}
}
}
Item {
Layout.fillWidth: true
}
Control.Button {
implicitWidth: 24 * DefaultStyle.dp
implicitHeight: 24 * DefaultStyle.dp
Layout.rightMargin: 5 * DefaultStyle.dp
padding: 0
background: Item {
visible: false
}
contentItem: Image {
source: AppIcons.phone
width: 24 * DefaultStyle.dp
sourceSize.width: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
}
onClicked: {
var addr = modelData.core.remoteAddress
var addressEnd = "@sip.linphone.org"
if (!addr.endsWith(addressEnd)) addr += addressEnd
var callVarObject = UtilsCpp.createCall(addr)
}
}
}
MouseArea {
hoverEnabled: true
anchors.fill: parent
Rectangle {
anchors.fill: parent
opacity: 0.1
color: DefaultStyle.main2_500main
visible: parent.containsMouse
}
onPressed: {
historyListView.currentIndex = model.index
}
}
onPressed: listView.currentIndex = parent.index
}
onCountChanged: {
mainItem.showDefaultItem = historyListView.count === 0 && historyListView.visible
}
onVisibleChanged: {
mainItem.showDefaultItem = historyListView.count === 0 && historyListView.visible
if (!visible) currentIndex = -1
}
Connections {
target: mainItem
onShowDefaultItemChanged: mainItem.showDefaultItem = mainItem.showDefaultItem && historyListView.count === 0 && historyListView.visible
onListViewUpdated: {
historyListView.model.updateView()
}
}
Control.ScrollBar.vertical: scrollbar
}
onCountChanged: mainItem.showDefaultItem = listView.count === 0
Connections {
target: mainItem
onShowDefaultItemChanged: mainItem.showDefaultItem = mainItem.showDefaultItem && listView.count === 0
}
Control.ScrollIndicator.vertical: Control.ScrollIndicator { }
}
}
}
Control.ScrollBar {
id: scrollbar
active: true
Layout.fillHeight: true
}
}
}
}
@ -223,7 +342,321 @@ AbstractMainPage {
}
}
rightPanelContent: ColumnLayout {
rightPanelContent: RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
visible: mainItem.selectedRowHistoryGui != undefined
Layout.topMargin: 45 * DefaultStyle.dp
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
ColumnLayout {
spacing: 30 * DefaultStyle.dp
Layout.fillWidth: true
Layout.fillHeight: true
Item {
Layout.preferredHeight: detailAvatar.height
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
Avatar {
id: detailAvatar
anchors.centerIn: parent
width: 100 * DefaultStyle.dp
height: 100 * DefaultStyle.dp
address: mainItem.selectedRowHistoryGui ? mainItem.selectedRowHistoryGui.core.remoteAddress : ""
}
PopupButton {
id: detailOptions
anchors.left: detailAvatar.right
anchors.leftMargin: 30 * DefaultStyle.dp
anchors.verticalCenter: parent.verticalCenter
popup.x: width
popup.contentItem: ColumnLayout {
Button {
background: Item {}
contentItem: IconLabel {
text: qsTr("Ajouter aux contacts")
iconSource: AppIcons.plusCircle
}
onClicked: console.debug("[CallPage.qml] TODO : add to contact")
}
Button {
background: Item {}
contentItem: IconLabel {
text: qsTr("Copier l'adresse SIP")
iconSource: AppIcons.copy
}
onClicked: console.debug("[CallPage.qml] TODO : copy SIP address")
}
Button {
background: Item {}
contentItem: IconLabel {
text: qsTr("Bloquer")
iconSource: AppIcons.empty
}
onClicked: console.debug("[CallPage.qml] TODO : block user")
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 2 * DefaultStyle.dp
color: DefaultStyle.main2_400
}
Button {
background: Item {}
contentItem: IconLabel {
text: qsTr("Supprimer l'historique")
iconSource: AppIcons.trashCan
colorizationColor: DefaultStyle.danger_500main
}
onClicked: {
detailListView.model.removeEntriesWithFilter()
detailListView.currentIndex = -1 // reset index for ui
detailOptions.closePopup()
}
}
}
}
}
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Text {
property var remoteAddress: mainItem.selectedRowHistoryGui ? UtilsCpp.getDisplayName(mainItem.selectedRowHistoryGui.core.remoteAddress) : undefined
Layout.alignment: Qt.AlignHCenter
text: remoteAddress ? remoteAddress.value : ""
horizontalAlignment: Text.AlignHCenter
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
Text {
text: mainItem.selectedRowHistoryGui ? mainItem.selectedRowHistoryGui.core.remoteAddress : ""
horizontalAlignment: Text.AlignHCenter
font {
pixelSize: 12 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
}
Text {
// connection status
}
}
RowLayout {
spacing: 40 * DefaultStyle.dp
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
// Layout.fillHeight: true
Item {Layout.fillWidth: true}
LabelButton {
Layout.preferredWidth: 24 * DefaultStyle.dp//image.width
Layout.preferredHeight: image.height
image.source: AppIcons.phone
label: qsTr("Appel")
button.onClicked: {
var addr = mainItem.selectedRowHistoryGui.core.remoteAddress
var addressEnd = "@sip.linphone.org"
if (!addr.endsWith(addressEnd)) addr += addressEnd
UtilsCpp.createCall(addr)
}
}
LabelButton {
Layout.preferredWidth: image.width
Layout.preferredHeight: image.height
image.source: AppIcons.chatTeardropText
label: qsTr("Message")
button.onClicked: console.debug("[CallPage.qml] TODO : open conversation")
}
LabelButton {
Layout.preferredWidth: image.width
Layout.preferredHeight: image.height
image.source: AppIcons.videoCamera
label: qsTr("Appel Video")
button.onClicked: {
var addr = mainItem.selectedRowHistoryGui.core.remoteAddress
var addressEnd = "@sip.linphone.org"
if(!addr.endsWith(addressEnd)) addr += addressEnd
UtilsCpp.createCall(addr)
console.log("[CallPage.qml] TODO : enable video")
}
}
Item {Layout.fillWidth: true}
}
Control.Control {
id: detailControl
Layout.preferredWidth: detailListView.width + leftPadding + rightPadding
implicitHeight: 430 * DefaultStyle.dp + topPadding + bottomPadding
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 30 * DefaultStyle.dp
topPadding: 16 * DefaultStyle.dp
bottomPadding: 21 * DefaultStyle.dp
leftPadding: 17 * DefaultStyle.dp
rightPadding: 17 * DefaultStyle.dp
background: Rectangle {
id: detailListBackground
width: parent.width
height: detailListView.height
color: DefaultStyle.grey_0
radius: 15 * DefaultStyle.dp
}
ListView {
id: detailListView
width: 360 * DefaultStyle.dp
height: Math.min(detailControl.implicitHeight, contentHeight)
anchors.centerIn: detailListBackground
anchors.bottomMargin: 21 * DefaultStyle.dp
spacing: 10 * DefaultStyle.dp
clip: true
onCountChanged: {
mainItem.listViewUpdated()
}
model: CallHistoryProxy {
filterText: mainItem.selectedRowHistoryGui ? mainItem.selectedRowHistoryGui.core.remoteAddress : ""
}
delegate: Item {
width:detailListView.width
height: 56 * DefaultStyle.dp
// anchors.topMargin: 5 * DefaultStyle.dp
// anchors.bottomMargin: 5 * DefaultStyle.dp
RowLayout {
anchors.fill: parent
ColumnLayout {
Layout.alignment: Qt.AlignVCenter
RowLayout {
Image {
source: modelData.core.status === LinphoneEnums.CallStatus.Declined
|| modelData.core.status === LinphoneEnums.CallStatus.DeclinedElsewhere
|| modelData.core.status === LinphoneEnums.CallStatus.Aborted
|| modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted
? modelData.core.isOutgoing
? AppIcons.outgoingCallRejected
: AppIcons.incomingCallRejected
: modelData.core.status === LinphoneEnums.CallStatus.Missed
? modelData.core.isOutgoing
? AppIcons.outgoingCallMissed
: AppIcons.incomingCallMissed
: modelData.core.isOutgoing
? AppIcons.outgoingCall
: AppIcons.incomingCall
Layout.preferredWidth: 6.67 * DefaultStyle.dp
Layout.preferredHeight: 6.67 * DefaultStyle.dp
sourceSize.width: 6.67 * DefaultStyle.dp
sourceSize.height: 6.67 * DefaultStyle.dp
}
Text {
Component.onCompleted: console.log("status", modelData.core.status)
text: modelData.core.status === LinphoneEnums.CallStatus.Missed
? qsTr("Appel manqué")
: modelData.core.isOutgoing
? qsTr("Appel sortant")
: qsTr("Appel entrant")
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
}
Text {
text: UtilsCpp.formatDate(modelData.core.date)
color: modelData.core.status === LinphoneEnums.CallStatus.Missed? DefaultStyle.danger_500main : DefaultStyle.main2_500main
font {
pixelSize: 12 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
}
}
Item {
Layout.fillWidth: true
}
Text {
text: UtilsCpp.formatElapsedTime(modelData.core.duration, false)
font {
pixelSize: 12 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
}
}
}
}
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
component LabelButton: ColumnLayout {
id: labelButton
property alias image: buttonImg
property alias button: button
property string label
spacing: 8 * DefaultStyle.dp
Button {
id: button
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: 56 * DefaultStyle.dp
Layout.preferredHeight: 56 * DefaultStyle.dp
topPadding: 16 * DefaultStyle.dp
bottomPadding: 16 * DefaultStyle.dp
leftPadding: 16 * DefaultStyle.dp
rightPadding: 16 * DefaultStyle.dp
background: Rectangle {
anchors.fill: parent
radius: 40 * DefaultStyle.dp
color: DefaultStyle.main2_200
}
contentItem: Image {
id: buttonImg
source: labelButton.source
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
sourceSize.width: 24 * DefaultStyle.dp
sourceSize.height: 24 * DefaultStyle.dp
}
}
Text {
Layout.alignment: Qt.AlignHCenter
text: labelButton.label
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
}
component IconLabel: RowLayout {
id: iconLabel
property string text
property string iconSource
property color colorizationColor: DefaultStyle.main2_500main
EffectImage {
image.source: iconLabel.iconSource
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
image.fillMode: Image.PreserveAspectFit
colorizationColor: iconLabel.colorizationColor
}
Text {
text: iconLabel.text
color: iconLabel.colorizationColor
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
}
}

View file

@ -46,6 +46,7 @@ QtObject {
property string incomingCallRejected: "image://internal/incoming_call_rejected.svg"
property string outgoingCall: "image://internal/outgoing_call.svg"
property string outgoingCallMissed: "image://internal/outgoing_call_missed.svg"
property string outgoingCallRejected: "image://internal/outgoing_call_rejected.svg"
property string microphone: "image://internal/microphone.svg"
property string microphoneSlash: "image://internal/microphone-slash.svg"
property string videoCamera: "image://internal/video-camera.svg"
@ -57,4 +58,7 @@ QtObject {
property string pause: "image://internal/pause.svg"
property string play: "image://internal/play.svg"
property string smiley: "image://internal/smiley.svg"
property string trashCan: "image://internal/trash-simple.svg"
property string copy: "image://internal/copy.svg"
property string empty: "image://internal/empty.svg"
}