Record call + fixes (

remove friend listener
fix call history layout (sometimes the details exceeded row bottom)
do not open terminate calls popup when all call ended already
change '.' with ' ' only if display name is the address
initials headers in contact list delegate
confirmation dialog delete history
use intermediate variable
try to fix variant object crash
)

recordable option
fix effect image not colorized on visible change
This commit is contained in:
Gaelle Braud 2024-01-05 17:03:22 +01:00
parent 4782bd2990
commit f9abfb9fbc
32 changed files with 583 additions and 328 deletions

View file

@ -58,6 +58,9 @@ CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullpt
encryption == LinphoneEnums::MediaEncryption::Dtls;
mPaused = mState == LinphoneEnums::CallState::Pausing || mState == LinphoneEnums::CallState::Paused ||
mState == LinphoneEnums::CallState::PausedByRemote;
mRecording = call->getParams() && call->getParams()->isRecording();
mRemoteRecording = call->getRemoteParams() && call->getRemoteParams()->isRecording();
mRecordable = mState == LinphoneEnums::CallState::StreamsRunning;
}
CallCore::~CallCore() {
@ -87,6 +90,19 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
mAccountModelConnection->makeConnectToCore(&CallCore::lSetCameraEnabled, [this](bool enabled) {
mAccountModelConnection->invokeToModel([this, enabled]() { mCallModel->setCameraEnabled(enabled); });
});
mAccountModelConnection->makeConnectToCore(&CallCore::lStartRecording, [this]() {
mAccountModelConnection->invokeToModel([this]() { mCallModel->startRecording(); });
});
mAccountModelConnection->makeConnectToCore(&CallCore::lStopRecording, [this]() {
mAccountModelConnection->invokeToModel([this]() { mCallModel->stopRecording(); });
});
mAccountModelConnection->makeConnectToModel(&CallModel::recordingChanged, [this](bool recording) {
mAccountModelConnection->invokeToCore([this, recording]() { setRecording(recording); });
});
mAccountModelConnection->makeConnectToModel(
&CallModel::remoteRecording, [this](const std::shared_ptr<linphone::Call> &call, bool recording) {
mAccountModelConnection->invokeToCore([this, recording]() { setRemoteRecording(recording); });
});
mAccountModelConnection->makeConnectToModel(&CallModel::cameraEnabledChanged, [this](bool enabled) {
mAccountModelConnection->invokeToCore([this, enabled]() { setCameraEnabled(enabled); });
});
@ -102,6 +118,12 @@ void CallCore::setSelf(QSharedPointer<CallCore> me) {
mAccountModelConnection->makeConnectToModel(&CallModel::statusChanged, [this](linphone::Call::Status status) {
mAccountModelConnection->invokeToCore([this, status]() { setStatus(LinphoneEnums::fromLinphone(status)); });
});
mAccountModelConnection->makeConnectToModel(&CallModel::stateChanged,
[this](linphone::Call::State state, const std::string &message) {
mAccountModelConnection->invokeToCore([this, state]() {
setRecordable(state == linphone::Call::State::StreamsRunning);
});
});
mAccountModelConnection->makeConnectToCore(&CallCore::lSetPaused, [this](bool paused) {
mAccountModelConnection->invokeToModel([this, paused]() { mCallModel->setPaused(paused); });
});
@ -275,6 +297,36 @@ void CallCore::setRemoteVideoEnabled(bool enabled) {
}
}
bool CallCore::getRecording() const {
return mRecording;
}
void CallCore::setRecording(bool recording) {
if (mRecording != recording) {
mRecording = recording;
emit recordingChanged();
}
}
bool CallCore::getRemoteRecording() const {
return mRemoteRecording;
}
void CallCore::setRemoteRecording(bool recording) {
if (mRemoteRecording != recording) {
mRemoteRecording = recording;
emit remoteRecordingChanged();
}
}
bool CallCore::getRecordable() const {
return mRecordable;
}
void CallCore::setRecordable(bool recordable) {
if (mRecordable != recordable) {
mRecordable = recordable;
emit recordableChanged();
}
}
LinphoneEnums::CallState CallCore::getTransferState() const {
return mTransferState;
}

View file

@ -45,6 +45,9 @@ class CallCore : public QObject, public AbstractObject {
Q_PROPERTY(bool peerSecured READ getPeerSecured WRITE setPeerSecured NOTIFY peerSecuredChanged)
Q_PROPERTY(
bool remoteVideoEnabled READ getRemoteVideoEnabled WRITE setRemoteVideoEnabled NOTIFY remoteVideoEnabledChanged)
Q_PROPERTY(bool recording READ getRecording WRITE setRecording NOTIFY recordingChanged)
Q_PROPERTY(bool remoteRecording READ getRemoteRecording WRITE setRemoteRecording NOTIFY remoteRecordingChanged)
Q_PROPERTY(bool recordable READ getRecordable WRITE setRecordable NOTIFY recordableChanged)
Q_PROPERTY(LinphoneEnums::CallState transferState READ getTransferState NOTIFY transferStateChanged)
public:
@ -89,6 +92,15 @@ public:
bool getRemoteVideoEnabled() const;
void setRemoteVideoEnabled(bool enabled);
bool getRecording() const;
void setRecording(bool recording);
bool getRemoteRecording() const;
void setRemoteRecording(bool recording);
bool getRecordable() const;
void setRecordable(bool recordable);
LinphoneEnums::CallState getTransferState() const;
void setTransferState(LinphoneEnums::CallState state, const QString &message);
@ -108,6 +120,9 @@ signals:
void transferStateChanged();
void peerSecuredChanged();
void remoteVideoEnabledChanged(bool remoteVideoEnabled);
void recordingChanged();
void remoteRecordingChanged();
void recordableChanged();
// Linphone commands
void lAccept(bool withVideo); // Accept an incoming call
@ -119,6 +134,8 @@ signals:
void lSetCameraEnabled(bool enabled);
void lSetPaused(bool paused);
void lTransferCall(const QString &dest);
void lStartRecording();
void lStopRecording();
/* TODO
Q_INVOKABLE void acceptWithVideo();
@ -133,13 +150,10 @@ signals:
Q_INVOKABLE void rejectVideoRequest();
Q_INVOKABLE void takeSnapshot();
Q_INVOKABLE void startRecording();
Q_INVOKABLE void stopRecording();
Q_INVOKABLE void sendDtmf(const QString &dtmf);
Q_INVOKABLE void verifyAuthenticationToken(bool verify);
Q_INVOKABLE void updateStreams();
Q_INVOKABLE void toggleSpeakerMute();
*/
private:
std::shared_ptr<CallModel> mCallModel;
@ -156,6 +170,9 @@ private:
bool mCameraEnabled;
bool mPaused = false;
bool mRemoteVideoEnabled = false;
bool mRecording = false;
bool mRemoteRecording = false;
bool mRecordable = false;
QSharedPointer<SafeConnection<CallCore, CallModel>> mAccountModelConnection;
DECLARE_ABSTRACT_OBJECT

View file

@ -50,7 +50,10 @@ FriendCore::FriendCore(const std::shared_ptr<linphone::Friend> &contact) : QObje
name.empty() ? Utils::getDisplayName(mAddress)->getValue().toString() : Utils::coreStringToAppString(name);
mStarred = contact->getStarred();
mIsSaved = true;
} else mIsSaved = false;
} else {
mIsSaved = false;
mStarred = false;
}
}
FriendCore::FriendCore(const FriendCore &friendCore) {
@ -60,6 +63,8 @@ FriendCore::FriendCore(const FriendCore &friendCore) {
}
FriendCore::~FriendCore() {
mustBeInMainThread("~" + getClassName());
emit mFriendModel->removeListener();
}
void FriendCore::setSelf(SafeSharedPointer<FriendCore> me) {
@ -217,7 +222,6 @@ void FriendCore::remove() {
void FriendCore::save() { // Save Values to model
FriendCore *thisCopy = new FriendCore(*this); // Pointer to avoid multiple copies in lambdas
auto linphoneAddr = ToolModel::interpretUrl(mAddress);
if (mFriendModel) {
mFriendModelConnection->invokeToModel([this, thisCopy]() { // Copy values to avoid concurrency
@ -227,7 +231,8 @@ void FriendCore::save() { // Save Values to model
mFriendModelConnection->invokeToCore([this]() { saved(); });
});
} else {
mCoreModelConnection->invokeToModel([this, thisCopy, linphoneAddr]() {
mCoreModelConnection->invokeToModel([this, thisCopy]() {
auto linphoneAddr = ToolModel::interpretUrl(mAddress);
auto core = CoreModel::getInstance()->getCore();
auto contact = core->findFriend(linphoneAddr);
auto friendExists = contact != nullptr;

View file

@ -58,7 +58,6 @@ bool FriendInitialProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sour
QRegularExpression search(mFilterText, QRegularExpression::CaseInsensitiveOption |
QRegularExpression::UseUnicodePropertiesOption);
auto friendData = sourceModel()->data(sourceModel()->index(sourceRow, 0, sourceParent)).value<FriendGui *>();
auto name = friendData->getCore()->getName();
show = friendData->getCore()->getName().indexOf(search) == 0;
}

View file

@ -35,7 +35,6 @@ class FriendInitialProxy : public SortFilterProxy, public AbstractObject {
Q_OBJECT
Q_PROPERTY(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged)
// Q_PROPERTY(QAbstractItemModel *sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged)
public:
FriendInitialProxy(QObject *parent = Q_NULLPTR);
@ -52,7 +51,6 @@ signals:
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<MagicSearchProxy> mSource;

View file

@ -36,6 +36,7 @@ list(APPEND _LINPHONEAPP_RC_FILES data/assistant/use-app-sip-account.rc
"data/image/users-three-selected.svg"
"data/image/noItemImage.svg"
"data/image/dots-three-vertical.svg"
"data/image/more.svg"
"data/image/plus-circle.svg"
"data/image/microphone-stage.svg"
"data/image/group-call.svg"
@ -64,6 +65,7 @@ list(APPEND _LINPHONEAPP_RC_FILES data/assistant/use-app-sip-account.rc
"data/image/empty.svg"
"data/image/heart.svg"
"data/image/heart-fill.svg"
"data/image/record-fill.svg"
data/shaders/roundEffect.vert.qsb
data/shaders/roundEffect.frag.qsb

View file

@ -1 +0,0 @@

View file

@ -33,7 +33,6 @@ CallHistoryModel::CallHistoryModel(const std::shared_ptr<linphone::CallLog> &cal
}
CallHistoryModel::~CallHistoryModel() {
// qDebug() << "[CallHistoryModel] delete" << this;
// mustBeInLinphoneThread("~" + getClassName());
}

View file

@ -22,6 +22,7 @@
#include <QDebug>
#include "core/path/Paths.hpp"
#include "model/core/CoreModel.hpp"
#include "tool/Utils.hpp"
@ -49,6 +50,11 @@ void CallModel::accept(bool withVideo) {
auto core = CoreModel::getInstance()->getCore();
auto params = core->createCallParams(mMonitor);
params->enableVideo(withVideo);
params->setRecordFile(
Paths::getCapturesDirPath()
.append(Utils::generateSavedFilename(QString::fromStdString(mMonitor->getToAddress()->getUsername()), ""))
.append(".mkv")
.toStdString());
mMonitor->enableCamera(withVideo);
// Answer with local call address.
auto localAddress = mMonitor->getCallLog()->getLocalAddress();
@ -116,6 +122,30 @@ void CallModel::setCameraEnabled(bool enabled) {
emit cameraEnabledChanged(enabled);
}
void CallModel::startRecording() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mMonitor->startRecording();
emit recordingChanged(mMonitor->getParams()->isRecording());
}
void CallModel::stopRecording() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mMonitor->stopRecording();
emit recordingChanged(mMonitor->getParams()->isRecording());
// TODO : display notification
}
void CallModel::setRecordFile(const std::string &path) {
auto core = CoreModel::getInstance()->getCore();
auto params = core->createCallParams(mMonitor);
params->setRecordFile(path);
mMonitor->update(params);
}
std::string CallModel::getRecordFile() const {
return mMonitor->getParams()->getRecordFile();
}
std::shared_ptr<const linphone::Address> CallModel::getRemoteAddress() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
return mMonitor->getRemoteAddress();

View file

@ -33,7 +33,7 @@ class CallModel : public ::Listener<linphone::Call, linphone::CallListener>,
public AbstractObject {
Q_OBJECT
public:
CallModel(const std::shared_ptr<linphone::Call> &account, QObject *parent = nullptr);
CallModel(const std::shared_ptr<linphone::Call> &call, QObject *parent = nullptr);
~CallModel();
void accept(bool withVideo);
@ -43,10 +43,15 @@ public:
void setMicrophoneMuted(bool isMuted);
void setSpeakerMuted(bool isMuted);
void setCameraEnabled(bool enabled);
void startRecording();
void stopRecording();
void setRecordFile(const std::string &path);
void setPaused(bool paused);
void transferTo(const std::shared_ptr<linphone::Address> &address);
void terminateAllCalls();
std::string getRecordFile() const;
std::shared_ptr<const linphone::Address> getRemoteAddress();
bool getAuthenticationTokenVerified();
@ -57,6 +62,7 @@ signals:
void durationChanged(int);
void pausedChanged(bool paused);
void remoteVideoEnabledChanged(bool remoteVideoEnabled);
void recordingChanged(bool recording);
private:
QTimer mDurationTimer;

View file

@ -20,6 +20,7 @@
#include "ToolModel.hpp"
#include "core/App.hpp"
#include "core/path/Paths.hpp"
#include "model/core/CoreModel.hpp"
#include "tool/Utils.hpp"
#include <QDebug>
@ -51,7 +52,10 @@ QString ToolModel::getDisplayName(const std::shared_ptr<const linphone::Address>
QString displayName;
if (address) {
displayName = Utils::coreStringToAppString(address->getDisplayName());
if (displayName.isEmpty()) displayName = Utils::coreStringToAppString(address->getUsername());
if (displayName.isEmpty()) {
displayName = Utils::coreStringToAppString(address->getUsername());
displayName.replace('.', ' ');
}
// TODO
// std::shared_ptr<linphone::Address> cleanAddress = address->clone();
// cleanAddress->clean();
@ -59,7 +63,6 @@ QString ToolModel::getDisplayName(const std::shared_ptr<const linphone::Address>
// auto sipAddressEntry = getSipAddressEntry(qtAddress, cleanAddress);
// displayName = sipAddressEntry->displayNames.get();
}
displayName.replace('.', ' ');
return displayName;
}
@ -85,6 +88,14 @@ QSharedPointer<CallCore> ToolModel::createCall(const QString &sipAddress,
std::shared_ptr<linphone::CallParams> params = core->createCallParams(nullptr);
params->enableVideo(false);
if (Utils::coreStringToAppString(params->getRecordFile()).isEmpty()) {
params->setRecordFile(
Paths::getCapturesDirPath()
.append(Utils::generateSavedFilename(QString::fromStdString(address->getUsername()), ""))
.append(".mkv")
.toStdString());
}
QHashIterator<QString, QString> iterator(headers);
while (iterator.hasNext()) {
@ -92,8 +103,8 @@ QSharedPointer<CallCore> ToolModel::createCall(const QString &sipAddress,
params->addCustomHeader(Utils::appStringToCoreString(iterator.key()),
Utils::appStringToCoreString(iterator.value()));
}
if (core->getDefaultAccount()) params->setAccount(core->getDefaultAccount());
// CallModel::setRecordFile(params, Utils::coreStringToAppString(address->getUsername()));
auto call = core->inviteAddressWithParams(address, params);
return call ? CallCore::create(call) : nullptr;

View file

@ -239,4 +239,16 @@ QString Utils::formatDateElapsedTime(const QDateTime &date) {
auto s = dateSec - h * 3600 - m * 60;
return QString::number(s) + " s";
}
QString Utils::generateSavedFilename(const QString &from, const QString &to) {
auto escape = [](const QString &str) {
constexpr char ReservedCharacters[] = "[<|>|:|\"|/|\\\\|\\?|\\*|\\+|\\||_|-]+";
static QRegularExpression regexp(ReservedCharacters);
return QString(str).replace(regexp, "");
};
return QStringLiteral("%1_%2_%3")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss"))
.arg(escape(from))
.arg(escape(to));
}

View file

@ -68,13 +68,14 @@ public:
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 QString generateSavedFilename(const QString &from, const QString &to);
static inline QString coreStringToAppString(const std::string &str) {
if (Constants::LinphoneLocaleEncoding == QString("UTF-8")) return QString::fromStdString(str);
else
return QString::fromLocal8Bit(str.c_str(),
int(str.size())); // When using Locale. Be careful about conversion bijection
// with UTF-8, you may loss characters
int(str.size())); // When using Locale. Be careful about conversion
// bijection with UTF-8, you may loss characters
}
static inline std::string appStringToCoreString(const QString &str) {

View file

@ -48,7 +48,8 @@ Window {
}
onClosing: (close) => {
close.accepted = false
terminateAllCallsDialog.open()
if (callsModel.haveCall)
terminateAllCallsDialog.open()
}
Timer {
@ -69,41 +70,11 @@ Window {
}
}
Popup {
Dialog {
id: terminateAllCallsDialog
modal: true
anchors.centerIn: parent
closePolicy: Control.Popup.NoAutoClose
padding: 10 * DefaultStyle.dp
contentItem: ColumnLayout {
height: terminateAllCallsDialog.height
width: 278 * DefaultStyle.dp
spacing: 8 * DefaultStyle.dp
Text {
text: qsTr("La fenêtre est sur le point d'être fermée. Cela terminera tous les appels en cours. Souhaitez vous continuer ?")
Layout.preferredWidth: parent.width
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
Button {
text: qsTr("Oui")
onClicked: {
call.core.lTerminateAllCalls()
terminateAllCallsDialog.close()
}
}
Button {
text: qsTr("Non")
onClicked: terminateAllCallsDialog.close()
}
}
}
onAccepted: call.core.lTerminateAllCalls()
width: 278 * DefaultStyle.dp
text: qsTr("La fenêtre est sur le point d'être fermée. Cela terminera tous les appels en cours. Souhaitez vous continuer ?")
}
CallProxy{
@ -127,11 +98,13 @@ Window {
required property string enabledIcon
property string disabledIcon
enabled: call != undefined
padding: 18 * DefaultStyle.dp
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
checkable: true
background: Rectangle {
anchors.fill: parent
RectangleTest{}
color: bottomButton.enabled
? disabledIcon
? DefaultStyle.grey_500
@ -142,10 +115,10 @@ Window {
radius: 71 * DefaultStyle.dp
}
contentItem: EffectImage {
image.source: disabledIcon && bottomButton.checked ? disabledIcon : enabledIcon
anchors.fill: parent
image.width: 32 * DefaultStyle.dp
image.height: 32 * DefaultStyle.dp
source: disabledIcon && bottomButton.checked ? disabledIcon : enabledIcon
imageWidth: 32 * DefaultStyle.dp
imageHeight: 32 * DefaultStyle.dp
anchors.centerIn: parent
colorizationColor: disabledIcon && bottomButton.checked ? DefaultStyle.main2_0 : DefaultStyle.grey_0
}
}
@ -211,20 +184,20 @@ Window {
Layout.fillWidth: true
Layout.minimumHeight: 25 * DefaultStyle.dp
RowLayout {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 10 * DefaultStyle.dp
EffectImage {
id: callStatusIcon
image.fillMode: Image.PreserveAspectFit
image.width: 15 * DefaultStyle.dp
image.height: 15 * DefaultStyle.dp
image.sourceSize.width: 15 * DefaultStyle.dp
image.sourceSize.height: 15 * DefaultStyle.dp
image.source: mainWindow.call.core.paused
? AppIcons.pause
: (mainWindow.call.core.state === LinphoneEnums.CallState.End
fillMode: Image.PreserveAspectFit
width: 15 * DefaultStyle.dp
height: 15 * DefaultStyle.dp
source:(mainWindow.call.core.state === LinphoneEnums.CallState.End
|| mainWindow.call.core.state === LinphoneEnums.CallState.Released)
? AppIcons.endCall
? AppIcons.endCall
: mainWindow.call.core.paused
? AppIcons.pause
: mainWindow.call.core.dir === LinphoneEnums.CallDir.Outgoing
? AppIcons.outgoingCall
: AppIcons.incomingCall
@ -261,6 +234,23 @@ Window {
visible: mainWindow.call.core.state === LinphoneEnums.CallState.Connected
|| mainWindow.call.core.state === LinphoneEnums.CallState.StreamsRunning
}
Item {
Layout.fillWidth: true
}
RowLayout {
visible: mainWindow.call.core.recording || mainWindow.call.core.remoteRecording
Text {
color: DefaultStyle.danger_500main
font.pixelSize: 14 * DefaultStyle.dp
text: mainWindow.call.core.recording ? qsTr("Vous enregistrez l'appel") : qsTr("Votre correspondant enregistre l'appel")
}
EffectImage {
source: AppIcons.recordFill
colorizationColor: DefaultStyle.danger_500main
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
}
}
}
Control.Control {
@ -481,7 +471,7 @@ Window {
visible: parent.visible
closeButtonVisible: false
onLaunchCall: {
var callVarObject = UtilsCpp.createCall(dialerTextInput.text + "@sip.linphone.org")
UtilsCpp.createCall(dialerTextInput.text + "@sip.linphone.org")
}
}
}
@ -595,7 +585,7 @@ Window {
background: Item {}
contentItem: RowLayout {
EffectImage {
image.source: AppIcons.endCall
source: AppIcons.endCall
colorizationColor: DefaultStyle.danger_500main
width: 32 * DefaultStyle.dp
height: 32 * DefaultStyle.dp
@ -745,7 +735,6 @@ Window {
Layout.preferredWidth: 55 * DefaultStyle.dp
Layout.preferredHeight: 55 * DefaultStyle.dp
onClicked: mainWindow.call.core.lSetCameraEnabled(!mainWindow.call.core.cameraEnabled)
}
BottomButton {
enabledIcon: AppIcons.microphone
@ -755,25 +744,27 @@ Window {
Layout.preferredHeight: 55 * DefaultStyle.dp
onClicked: mainWindow.call.core.lSetMicrophoneMuted(!mainWindow.call.core.microphoneMuted)
}
BottomButton {
PopupButton {
id: moreOptionsButton
checkable: true
enabledIcon: AppIcons.verticalDots
Layout.preferredWidth: 55 * DefaultStyle.dp
Layout.preferredHeight: 55 * DefaultStyle.dp
onPressed: {
moreOptionsMenu.visible = !moreOptionsMenu.visible
background: Rectangle {
anchors.fill: moreOptionsButton
color: moreOptionsButton.checked ? DefaultStyle.grey_0 : DefaultStyle.grey_500
radius: 40 * DefaultStyle.dp
}
}
Popup {
id: moreOptionsMenu
x: moreOptionsButton.x
y: moreOptionsButton.y - height
padding: 20 * DefaultStyle.dp
// closePolicy: Control.Popup.CloseOnEscape
onAboutToHide: moreOptionsButton.checked = false
contentItem: ColumnLayout {
contentItem: Item {
EffectImage {
source: AppIcons.more
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
anchors.centerIn: parent
colorizationColor: moreOptionsButton.checked ? DefaultStyle.grey_500 : DefaultStyle.grey_0
}
}
popup.x: width/2
popup.y: y - popup.height + height/4
popup.contentItem: ColumnLayout {
id: optionsList
spacing: 10 * DefaultStyle.dp
@ -785,8 +776,9 @@ Window {
}
contentItem: RowLayout {
Image {
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
source: AppIcons.callList
}
Text {
@ -807,8 +799,9 @@ Window {
}
contentItem: RowLayout {
Image {
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
source: AppIcons.dialer
}
Text {
@ -829,13 +822,16 @@ Window {
visible: false
}
contentItem: RowLayout {
Image {
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
EffectImage {
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
source: mainWindow.call.core.speakerMuted ? AppIcons.speakerSlash : AppIcons.speaker
colorizationColor: mainWindow.call.core.speakerMuted ? DefaultStyle.danger_500main : undefined
}
Text {
text: mainWindow.call.core.speakerMuted ? qsTr("Activer le son") : qsTr("Désactiver le son")
color: mainWindow.call.core.speakerMuted ? DefaultStyle.danger_500main : DefaultStyle.main2_600
}
}
@ -843,6 +839,32 @@ Window {
mainWindow.call.core.lSetSpeakerMuted(!mainWindow.call.core.speakerMuted)
}
}
Control.Button {
id: recordButton
Layout.fillWidth: true
enabled: mainWindow.call.core.recordable
checkable: true
background: Item {
visible: false
}
contentItem: RowLayout {
EffectImage {
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
source: AppIcons.recordFill
colorizationColor: mainWindow.call.core.recording ? DefaultStyle.danger_500main : undefined
}
Text {
color: mainWindow.call.core.recording ? DefaultStyle.danger_500main : DefaultStyle.main2_600
text: mainWindow.call.core.recording ? qsTr("Terminer l'enregistrement") : qsTr("Enregistrer l'appel")
}
}
onClicked: {
mainWindow.call.core.recording ? mainWindow.call.core.lStopRecording() : mainWindow.call.core.lStartRecording()
}
}
}
}
}

View file

@ -46,7 +46,7 @@ Item {
contentItem: RowLayout {
spacing: 15 * DefaultStyle.dp
EffectImage {
image.source: AppIcons.smiley
source: AppIcons.smiley
colorizationColor: DefaultStyle.success_500main
Layout.preferredWidth: 32 * DefaultStyle.dp
Layout.preferredHeight: 32 * DefaultStyle.dp
@ -136,7 +136,7 @@ Item {
background: Item {
}
contentItem: Image {
source: AppIcons.verticalDots
source: AppIcons.more
}
Popup{
id: accountList

View file

@ -27,6 +27,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Item/ComboBox.qml
view/Item/ContactsList.qml
view/Item/DesktopPopup.qml
view/Item/Dialog.qml
view/Item/DigitInput.qml
view/Item/EffectImage.qml
view/Item/ErrorText.qml

View file

@ -68,11 +68,11 @@ Item {
spacing: 5 * DefaultStyle.dp
EffectImage {
id: newAccount
image.source: AppIcons.plusCircle
source: AppIcons.plusCircle
Layout.fillHeight: true
Layout.preferredWidth: height
Layout.alignment: Qt.AlignHCenter
image.fillMode: Image.PreserveAspectFit
fillMode: Image.PreserveAspectFit
colorizationColor: DefaultStyle.main2_500main
}
Text{

View file

@ -231,31 +231,10 @@ Item {
weight: 800 * DefaultStyle.dp
}
}
Repeater {
model: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\\d"]
RowLayout {
visible: contactList.count > 0
spacing: 8 * DefaultStyle.dp
Layout.fillWidth: true
Text {
Layout.preferredWidth: 20 * DefaultStyle.dp
Layout.alignment: Qt.AlignTop
Layout.topMargin: 15 * DefaultStyle.dp // Align center with the first row
text: modelData == "\\d" ? " " : modelData
color: DefaultStyle.main2_400
font {
pixelSize: 20 * DefaultStyle.dp
weight: 500 * DefaultStyle.dp
}
}
ContactsList{
Layout.fillWidth: true
id: contactList
initialProxyModel: modelData
searchBarText: searchBar.text
// contactMenuVisible: false
}
}
ContactsList{
Layout.fillWidth: true
id: contactList
searchBarText: searchBar.text
}
}
ColumnLayout {
@ -269,16 +248,11 @@ Item {
ContactsList{
contactMenuVisible: false
Layout.fillHeight: true
model: FriendInitialProxy {
filterText: ""
property int sourceFlags: LinphoneEnums.MagicSearchSource.FavoriteFriends//mainItem.magicSearchSourceFlags
sourceModel: MagicSearchProxy {
id: search
searchText: searchBar.text.length === 0 ? "*" : searchBar.text
aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend
sourceFlags: LinphoneEnums.MagicSearchSource.FavoriteFriends
}
initialHeadersVisible: false
model: MagicSearchProxy {
searchText: searchBar.text.length === 0 ? "*" : searchBar.text
sourceFlags: LinphoneEnums.MagicSearchSource.FavoriteFriends
aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend
}
}
}
@ -299,7 +273,7 @@ Item {
id: numPad
width: parent.width
onLaunchCall: {
var callVarObject = UtilsCpp.createCall(searchBar.text + "@sip.linphone.org")
UtilsCpp.createCall(searchBar.text + "@sip.linphone.org")
// TODO : auto completion instead of sip linphone
}
}

View file

@ -16,7 +16,7 @@ Control.CheckBox {
// color: mainItem.checked ? DefaultStyle.main1_500_main : "transparent"
EffectImage {
visible: mainItem.checked
image.source: AppIcons.check
source: AppIcons.check
colorizationColor: DefaultStyle.main1_500_main
anchors.fill: parent
}

View file

@ -33,7 +33,7 @@ StackView{
id: initials
Rectangle {
id: initialItem
property string initials: UtilsCpp.getInitials(mainItem.displayNameObj.value)
property string initials: displayNameObj ? UtilsCpp.getInitials(mainItem.displayNameObj.value) : ""
radius: width / 2
color: DefaultStyle.main2_200
height: mainItem.height

View file

@ -123,12 +123,12 @@ Rectangle{
}
EffectImage {
id: manageAccount
image.source: AppIcons.manageProfile
source: AppIcons.manageProfile
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
Layout.alignment: Qt.AlignHCenter
image.sourceSize.width: 24 * DefaultStyle.dp
image.fillMode: Image.PreserveAspectFit
width: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
colorizationColor: DefaultStyle.main2_500main
MouseArea{ // TODO
anchors.fill: parent

View file

@ -11,118 +11,128 @@ ListView {
height: contentHeight
visible: count > 0
property string initialProxyModel
property bool favorite: true
// Component.onCompleted: model.sourceModel.sourceFlags = magicSearchSourceFlags
property string searchBarText
property bool contactMenuVisible: true
property bool initialHeadersVisible: true
model: FriendInitialProxy {
filterText: initialProxyModel
property int sourceFlags: LinphoneEnums.MagicSearchSource.FavoriteFriends//mainItem.magicSearchSourceFlags
sourceModel: MagicSearchProxy {
id: search
searchText: searchBarText.length === 0 ? "*" : searchBarText
}
model: MagicSearchProxy {
searchText: searchBarText.length === 0 ? "*" : searchBarText
}
delegate: Item {
width: mainItem.width
height: 56 * DefaultStyle.dp
RowLayout {
anchors.fill: parent
z: 1
Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
contact: modelData
delegate: RowLayout {
id: itemDelegate
property var previousItem : mainItem.model.count > 0 && index > 0 ? mainItem.model.getAt(index-1) : null
property var previousDisplayName: previousItem ? UtilsCpp.getDisplayName(previousItem.core.address) : null
property string previousDisplayNameText: previousDisplayName ? previousDisplayName.value : ""
property var displayName: UtilsCpp.getDisplayName(modelData.core.address)
property string displayNameText: displayName ? displayName.value : ""
Text {
Layout.preferredWidth: 20 * DefaultStyle.dp
opacity: (!previousItem || !previousDisplayNameText.startsWith(displayNameText[0])) ? 1 : 0
text: displayNameText[0]
color: DefaultStyle.main2_400
font {
pixelSize: 20 * DefaultStyle.dp
weight: 500 * DefaultStyle.dp
capitalization: Font.AllUppercase
}
Text {
text: UtilsCpp.getDisplayName(modelData.core.address).value
font.pixelSize: 14 * DefaultStyle.dp
font.capitalization: Font.Capitalize
}
Item {
Layout.fillWidth: true
}
PopupButton {
id: friendPopup
hoverEnabled: true
visible: mainItem.contactMenuVisible && (contactArea.containsMouse || hovered || popup.opened)
popup.x: 0
popup.padding: 10 * DefaultStyle.dp
popup.contentItem: ColumnLayout {
Button {
background: Item{}
contentItem: RowLayout {
Image {
source: modelData.core.starred ? AppIcons.heartFill : AppIcons.heart
fillMode: Image.PreserveAspectFit
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
}
Text {
text: modelData.core.starred ? qsTr("Enlever des favoris") : qsTr("Mettre en favori")
color: DefaultStyle.main2_500main
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
Item {
width: mainItem.width
height: 56 * DefaultStyle.dp
RowLayout {
anchors.fill: parent
z: 1
Avatar {
Layout.preferredWidth: 45 * DefaultStyle.dp
Layout.preferredHeight: 45 * DefaultStyle.dp
contact: modelData
}
Text {
text: itemDelegate.displayNameText
font.pixelSize: 14 * DefaultStyle.dp
font.capitalization: Font.Capitalize
}
Item {
Layout.fillWidth: true
}
PopupButton {
id: friendPopup
hoverEnabled: true
visible: mainItem.contactMenuVisible && (contactArea.containsMouse || hovered || popup.opened)
popup.x: 0
popup.padding: 10 * DefaultStyle.dp
popup.contentItem: ColumnLayout {
Button {
background: Item{}
contentItem: RowLayout {
Image {
source: modelData.core.starred ? AppIcons.heartFill : AppIcons.heart
fillMode: Image.PreserveAspectFit
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
}
Text {
text: modelData.core.starred ? qsTr("Enlever des favoris") : qsTr("Mettre en favori")
color: DefaultStyle.main2_500main
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
}
}
onClicked: {
modelData.core.lSetStarred(!modelData.core.starred)
friendPopup.close()
}
}
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
onClicked: {
modelData.core.lSetStarred(!modelData.core.starred)
friendPopup.close()
}
Text {
text: qsTr("Supprimmer")
color: DefaultStyle.danger_500main
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
Button {
background: Item{}
contentItem: RowLayout {
EffectImage {
source: AppIcons.trashCan
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
colorizationColor: DefaultStyle.danger_500main
}
Text {
text: qsTr("Supprimmer")
color: DefaultStyle.danger_500main
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
}
}
onClicked: {
modelData.core.remove()
friendPopup.close()
onClicked: {
modelData.core.remove()
friendPopup.close()
}
}
}
}
}
}
MouseArea {
id: contactArea
hoverEnabled: true
anchors.fill: parent
Rectangle {
anchors.fill: contactArea
opacity: 0.1
color: DefaultStyle.main2_500main
visible: contactArea.containsMouse || friendPopup.hovered
}
onClicked: {
startCallPopup.contact = modelData
startCallPopup.open()
// mainItem.callButtonPressed(modelData.core.address)
MouseArea {
id: contactArea
hoverEnabled: true
anchors.fill: parent
Rectangle {
anchors.fill: contactArea
opacity: 0.1
color: DefaultStyle.main2_500main
visible: contactArea.containsMouse || friendPopup.hovered
}
onClicked: {
startCallPopup.contact = modelData
startCallPopup.open()
// mainItem.callButtonPressed(modelData.core.address)
}
}
}
}

View file

@ -0,0 +1,81 @@
import QtQuick 2.7
import QtQuick.Controls 2.2 as Control
import QtQuick.Effects
import QtQuick.Layouts
import Linphone
Popup {
id: mainItem
modal: true
anchors.centerIn: parent
closePolicy: Control.Popup.NoAutoClose
rightPadding: 10 * DefaultStyle.dp
leftPadding: 10 * DefaultStyle.dp
topPadding: 10 * DefaultStyle.dp
bottomPadding: 10 * DefaultStyle.dp + buttonsLayout.height
property int radius: 16 * DefaultStyle.dp
property color underlineColor: DefaultStyle.main1_500_main
property string text
signal accepted()
signal rejected()
background: Item {
anchors.fill: parent
Rectangle {
visible: mainItem.underlineColor != undefined
width: mainItem.width
height: mainItem.height + 2 * DefaultStyle.dp
color: mainItem.underlineColor
radius: mainItem.radius
}
Rectangle{
id: backgroundItem
width: mainItem.width
height: mainItem.height
radius: mainItem.radius
color: DefaultStyle.grey_0
border.color: DefaultStyle.grey_0
}
MultiEffect {
anchors.fill: backgroundItem
source: backgroundItem
shadowEnabled: true
shadowColor: DefaultStyle.grey_900
shadowBlur: 1.0
shadowOpacity: 0.1
}
RowLayout {
id: buttonsLayout
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: 10 * DefaultStyle.dp
Button {
text: qsTr("Oui")
onClicked: {
mainItem.accepted()
mainItem.close()
}
}
Button {
text: qsTr("Non")
onClicked: {
mainItem.rejected()
mainItem.close()
}
}
}
}
contentItem: Text {
width: parent.width
Layout.preferredWidth: 278 * DefaultStyle.dp
text: mainItem.text
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
}
}

View file

@ -5,41 +5,46 @@ import QtQuick.Effects
import Linphone
Item {
// The loader is needed here to refresh the colorization effect (effect2) which is not refreshed when visibility change
// and causes colorization issue when effect image is inside a popup
Loader {
id: mainItem
property alias image: image
property alias effect: effect
property alias effect2: effect2
active: visible
property var source
property var fillMode: Image.PreserveAspectFit
property var colorizationColor
readonly property bool useColor: colorizationColor != undefined
width: image.width
height: image.height
property int imageWidth: width
property int imageHeight: height
property bool useColor: colorizationColor != undefined
sourceComponent: Item {
Image {
id: image
visible: !effect2.enabled
source: mainItem.source
fillMode: mainItem.fillMode
sourceSize.width: width
sourceSize.height: height
width: mainItem.imageWidth
height: mainItem.imageHeight
anchors.centerIn: parent
}
MultiEffect {
id: effect
visible: false
anchors.fill: image
source: image
maskSource: image
brightness: effect2.enabled ? 1.0 : 0.0
}
Image {
id: image
visible: !effect2.enabled
sourceSize.width: parent.width
sourceSize.height: parent.height
width: parent.width
height: parent.height
fillMode: Image.PreserveAspectFit
anchors.centerIn: parent
}
MultiEffect {
id: effect
visible: !effect2.enabled
anchors.fill: image
source: image
maskSource: image
brightness: effect2.enabled ? 1.0 : 0.0
}
MultiEffect {
id: effect2
enabled: mainItem.useColor
anchors.fill: effect
source: effect
maskSource: effect
colorizationColor: effect2.enabled ? mainItem.colorizationColor : 'black'
colorization: effect2.enabled ? 1.0: 0.0
MultiEffect {
id: effect2
enabled: mainItem.useColor
anchors.fill: effect
source: effect
maskSource: effect
colorizationColor: effect2.enabled && mainItem.colorizationColor ? mainItem.colorizationColor : 'black'
colorization: effect2.enabled ? 1.0: 0.0
}
}
}

View file

@ -153,11 +153,11 @@ Control.Popup {
}
contentItem: EffectImage {
id: buttonIcon
image.source: AppIcons.phone
source: AppIcons.phone
anchors.centerIn: parent
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
image.fillMode: Image.PreserveAspectFit
fillMode: Image.PreserveAspectFit
colorizationColor: DefaultStyle.grey_0
}
onClicked: mainItem.launchCall()

View file

@ -21,8 +21,8 @@ Control.Popup{
width: mainItem.width
height: mainItem.height
radius: mainItem.radius
color: DefaultStyle.grey_0
border.color: DefaultStyle.grey_0
// border.width: 1
}
MultiEffect {
anchors.fill: backgroundItem

View file

@ -23,7 +23,7 @@ Button {
radius: 40 * DefaultStyle.dp
}
contentItem: Image {
source: AppIcons.verticalDots
source: AppIcons.more
sourceSize.width: 24 * DefaultStyle.dp
sourceSize.height: 24 * DefaultStyle.dp
width: 24 * DefaultStyle.dp
@ -37,7 +37,7 @@ Button {
id: popup
x: - width
y: mainItem.height
closePolicy: Popup.CloseOnPressOutsideParent | Popup.CloseOnEscape
closePolicy: Popup.CloseOnPressOutsideParent |Popup.CloseOnPressOutside
padding: 20 * DefaultStyle.dp

View file

@ -60,16 +60,18 @@ Control.TabBar {
width: mainItem.width
contentItem: ColumnLayout {
height: tabButton.height
width: tabButton.width
// height: tabButton.height
// width: tabButton.width
EffectImage {
id: buttonIcon
property int buttonSize: 24 * DefaultStyle.dp
image.source: mainItem.currentIndex === index ? modelData.selectedIcon : modelData.icon
source: mainItem.currentIndex === index ? modelData.selectedIcon : modelData.icon
Layout.preferredWidth: buttonSize
Layout.preferredHeight: buttonSize
width: buttonSize
height: buttonSize
Layout.alignment: Qt.AlignHCenter
image.fillMode: Image.PreserveAspectFit
fillMode: Image.PreserveAspectFit
colorizationColor: DefaultStyle.grey_0
}
Text {

View file

@ -77,10 +77,10 @@ Item {
Layout.alignment: Qt.AlignVCenter
EffectImage {
colorizationColor: DefaultStyle.grey_0
image.source: mainItem.newItemIconSource
image.width: 24 * DefaultStyle.dp
image.height: 24 * DefaultStyle.dp
image.fillMode: Image.PreserveAspectFit
source: mainItem.newItemIconSource
width: 24 * DefaultStyle.dp
height: 24 * DefaultStyle.dp
fillMode: Image.PreserveAspectFit
}
Text {
text: mainItem.noItemButtonText

View file

@ -23,6 +23,18 @@ AbstractMainPage {
listStackView.push(newCallItem)
}
Dialog {
id: deleteHistoryPopup
width: 278 * DefaultStyle.dp
text: qsTr("L'historique d'appel sera supprimé. Souhaitez-vous continuer ?")
}
Dialog {
id: deleteForUserPopup
width: 278 * DefaultStyle.dp
text: qsTr("L'historique d'appel de l'utilisateur sera supprimé. Souhaitez-vous continuer ?")
}
leftPanelContent: Item {
id: leftPanel
Layout.fillWidth: true
@ -61,12 +73,12 @@ AbstractMainPage {
background: Item{}
contentItem: RowLayout {
EffectImage {
image.source: AppIcons.trashCan
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
fillMode: Image.PreserveAspectFit
colorizationColor: DefaultStyle.danger_500main
}
Text {
@ -78,9 +90,13 @@ AbstractMainPage {
}
}
}
Connections {
target: deleteHistoryPopup
onAccepted: historyListView.model.removeAllEntries()
}
onClicked: {
historyListView.model.removeAllEntries()
removeHistory.close()
deleteHistoryPopup.open()
}
}
}
@ -182,50 +198,58 @@ AbstractMainPage {
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
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout {
spacing: 5 * DefaultStyle.dp
anchors.verticalCenter: parent.verticalCenter
Text {
// text: modelData.core.date
text: UtilsCpp.formatDateElapsedTime(modelData.core.date)
id: friendAddress
property var remoteAddress: modelData ? UtilsCpp.getDisplayName(modelData.core.remoteAddress) : undefined
text: remoteAddress ? remoteAddress.value : ""
font {
pixelSize: 12 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
pixelSize: 14 * DefaultStyle.dp
weight: 400 * DefaultStyle.dp
}
}
RowLayout {
spacing: 3 * DefaultStyle.dp
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
RectangleTest{anchors.fill: parent}
}
Text {
// text: modelData.core.date
text: UtilsCpp.formatDateElapsedTime(modelData.core.date)
font {
pixelSize: 12 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
}
}
}
}
Item {
Layout.fillWidth: true
}
// Item {
// Layout.fillWidth: true
// }
Control.Button {
implicitWidth: 24 * DefaultStyle.dp
implicitHeight: 24 * DefaultStyle.dp
@ -244,7 +268,7 @@ AbstractMainPage {
var addr = modelData.core.remoteAddress
var addressEnd = "@sip.linphone.org"
if (!addr.endsWith(addressEnd)) addr += addressEnd
var callVarObject = UtilsCpp.createCall(addr)
UtilsCpp.createCall(addr)
}
}
}
@ -342,7 +366,7 @@ AbstractMainPage {
onCallButtonPressed: (address) => {
var addressEnd = "@sip.linphone.org"
if (!address.endsWith(addressEnd)) address += addressEnd
var callVarObject = UtilsCpp.createCall(address)
UtilsCpp.createCall(address)
// var window = UtilsCpp.getCallsWindow()
}
}
@ -427,10 +451,13 @@ AbstractMainPage {
iconSource: AppIcons.trashCan
colorizationColor: DefaultStyle.danger_500main
}
Connections {
target: deleteForUserPopup
onAccepted: detailListView.model.removeEntriesWithFilter()
}
onClicked: {
detailListView.model.removeEntriesWithFilter()
// detailListView.currentIndex = -1 // reset index for ui
detailOptions.close()
deleteForUserPopup.open()
}
}
}
@ -659,12 +686,12 @@ AbstractMainPage {
property string iconSource
property color colorizationColor: DefaultStyle.main2_500main
EffectImage {
image.source: iconLabel.iconSource
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
fillMode: Image.PreserveAspectFit
colorizationColor: iconLabel.colorizationColor
}
Text {

View file

@ -36,6 +36,7 @@ QtObject {
property string usersThreeSelected: "image://internal/users-three-selected.svg"
property string noItemImage: "image://internal/noItemImage.svg"
property string verticalDots: "image://internal/dots-three-vertical.svg"
property string more: "image://internal/more.svg"
property string plusCircle: "image://internal/plus-circle.svg"
property string micro: "image://internal/microphone-stage.svg"
property string groupCall: "image://internal/group-call.svg"
@ -64,4 +65,5 @@ QtObject {
property string empty: "image://internal/empty.svg"
property string heart: "image://internal/heart.svg"
property string heartFill: "image://internal/heart-fill.svg"
property string recordFill: "image://internal/record-fill.svg"
}

View file

@ -31,7 +31,7 @@ QtObject {
property double dp: 0.8 //1
property double dp: 1
property string emojiFont: "Noto Color Emoji"
property string defaultFont: "Noto Sans"