Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
linphone-desktop
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
linphone-desktop
Commits
530c3129
Commit
530c3129
authored
Jan 16, 2017
by
Ronan Abhamon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(ui/modules/Linphone/Chat/Chat): supports file upload
parent
581eb69c
Changes
27
Hide whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
577 additions
and
99 deletions
+577
-99
CMakeLists.txt
tests/CMakeLists.txt
+2
-0
attachment_disabled.svg
tests/assets/images/attachment_disabled.svg
+12
-0
en.ts
tests/assets/languages/en.ts
+5
-0
fr.ts
tests/assets/languages/fr.ts
+5
-0
resources.qrc
tests/resources.qrc
+2
-0
App.cpp
tests/src/app/App.cpp
+2
-1
App.hpp
tests/src/app/App.hpp
+2
-0
AvatarProvider.cpp
tests/src/app/AvatarProvider.cpp
+2
-7
AvatarProvider.hpp
tests/src/app/AvatarProvider.hpp
+1
-5
Paths.cpp
tests/src/app/Paths.cpp
+15
-10
Paths.hpp
tests/src/app/Paths.hpp
+1
-0
ThumbnailProvider.cpp
tests/src/app/ThumbnailProvider.cpp
+19
-0
ThumbnailProvider.hpp
tests/src/app/ThumbnailProvider.hpp
+21
-0
ChatModel.cpp
tests/src/components/chat/ChatModel.cpp
+148
-38
ChatModel.hpp
tests/src/components/chat/ChatModel.hpp
+7
-16
ChatProxyModel.cpp
tests/src/components/chat/ChatProxyModel.cpp
+8
-0
ChatProxyModel.hpp
tests/src/components/chat/ChatProxyModel.hpp
+3
-6
SettingsModel.cpp
tests/src/components/settings/SettingsModel.cpp
+14
-1
SettingsModel.hpp
tests/src/components/settings/SettingsModel.hpp
+6
-3
Colors.qml
tests/ui/modules/Common/Colors.qml
+2
-0
DroppableTextArea.qml
tests/ui/modules/Common/DroppableTextArea.qml
+10
-1
ExclusiveButtons.spec.qml
tests/ui/modules/Common/Form/ExclusiveButtons.spec.qml
+1
-7
Chat.qml
tests/ui/modules/Linphone/Chat/Chat.qml
+23
-3
FileMessage.qml
tests/ui/modules/Linphone/Chat/FileMessage.qml
+225
-0
OutgoingMessage.qml
tests/ui/modules/Linphone/Chat/OutgoingMessage.qml
+1
-1
ChatStyle.qml
tests/ui/modules/Linphone/Styles/ChatStyle.qml
+21
-0
utils.js
tests/ui/scripts/Utils/utils.js
+19
-0
No files found.
tests/CMakeLists.txt
View file @
530c3129
...
...
@@ -56,6 +56,7 @@ set(SOURCES
src/app/DefaultTranslator.cpp
src/app/Logger.cpp
src/app/Paths.cpp
src/app/ThumbnailProvider.cpp
src/components/camera/Camera.cpp
src/components/chat/ChatModel.cpp
src/components/chat/ChatProxyModel.cpp
...
...
@@ -81,6 +82,7 @@ set(HEADERS
src/app/DefaultTranslator.hpp
src/app/Logger.hpp
src/app/Paths.hpp
src/app/ThumbnailProvider.hpp
src/components/camera/Camera.hpp
src/components/chat/ChatModel.hpp
src/components/chat/ChatProxyModel.hpp
...
...
tests/assets/images/attachment_disabled.svg
0 → 100644
View file @
530c3129
<?xml version="1.0" encoding="UTF-8"?>
<svg
width=
"10px"
height=
"20px"
viewBox=
"0 0 10 20"
version=
"1.1"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<!-- Generator: Sketch 40.3 (33839) - http://www.bohemiancoding.com/sketch -->
<title>
attachment_clic
</title>
<desc>
Created with Sketch.
</desc>
<defs></defs>
<g
id=
"Symbols"
stroke=
"none"
stroke-width=
"1"
fill=
"none"
fill-rule=
"evenodd"
>
<g
id=
"attachment_clic"
fill=
"#D0D8DE"
>
<path
d=
"M8.76252348,14.9551548 C8.76095805,17.0249882 7.07341891,18.7247345 4.99686913,18.7489385 C2.91953663,18.7692387 1.23747652,17.1061888 1.23982467,15.0371361 L1.23982467,3.7807088 C1.23982467,2.39873777 2.36693801,1.26505306 3.75234815,1.25099912 C5.13619286,1.23538363 6.25469631,2.342522 6.25469631,3.7252738 L6.25469631,14.9824819 C6.25469631,15.6734674 5.6895742,16.2379674 4.99765185,16.2465559 C4.30572949,16.2543637 3.74843456,15.7007945 3.74686913,15.0082474 L3.74686913,5.00418259 L2.49295554,5.01667498 L2.49295554,15.0215206 C2.49295554,16.4034916 3.61380714,17.5114108 5,17.4973568 C6.38306199,17.4817414 7.50704446,16.350399 7.50860989,14.9699895 L7.50860989,3.71200064 C7.51095805,1.64216719 5.82889793,-0.0208826913 3.75078272,0.000198222717 C1.67501565,0.0236214605 0.00313087038,1.72102542 0,3.79163964 L0,15.6742482 C0.304477145,18.137592 2.43973075,20.025505 4.99452098,19.9997394 C7.55244208,19.9708508 9.69082655,18.0392144 10,15.567282 L10,2.4440227 L8.76252348,2.4440227 L8.76252348,14.9551548 Z"
></path>
</g>
</g>
</svg>
tests/assets/languages/en.ts
View file @
530c3129
...
...
@@ -41,6 +41,11 @@
<
source
>
newMessagePlaceholder
<
/source
>
<
translation
>
Enter
your
message
<
/translation
>
<
/message
>
<
message
>
<
source
>
noFileTransferUrl
<
/source
>
<
translation
>
Unable
to
send
file
.
Server
url
not
configured
.
<
/translation
>
<
/message
>
<
/context
>
<
context
>
<
name
>
ConfirmDialog
<
/name
>
...
...
tests/assets/languages/fr.ts
View file @
530c3129
...
...
@@ -33,6 +33,11 @@
<
source
>
newMessagePlaceholder
<
/source
>
<
translation
>
Entrer
votre
message
.
<
/translation
>
<
/message
>
<
message
>
<
source
>
noFileTransferUrl
<
/source
>
<
translation
>
Impossible
d
&
apos
;
envoyer
un
fichier
.
Url
du
serveur
non
configur
é
e
.
<
/translation
>
<
/message
>
<
/context
>
<
context
>
<
name
>
ConfirmDialog
<
/name
>
...
...
tests/resources.qrc
View file @
530c3129
...
...
@@ -4,6 +4,7 @@
<file>
assets/images/add_hovered.svg
</file>
<file>
assets/images/add_normal.svg
</file>
<file>
assets/images/add_pressed.svg
</file>
<file>
assets/images/attachment_disabled.svg
</file>
<file>
assets/images/attachment_hovered.svg
</file>
<file>
assets/images/attachment_normal.svg
</file>
<file>
assets/images/attachment_pressed.svg
</file>
...
...
@@ -204,6 +205,7 @@
<file>
ui/modules/Linphone/Call/PausedCallControls.qml
</file>
<file>
ui/modules/Linphone/Chat/Chat.qml
</file>
<file>
ui/modules/Linphone/Chat/Event.qml
</file>
<file>
ui/modules/Linphone/Chat/FileMessage.qml
</file>
<file>
ui/modules/Linphone/Chat/IncomingMessage.qml
</file>
<file>
ui/modules/Linphone/Chat/Message.qml
</file>
<file>
ui/modules/Linphone/Chat/OutgoingMessage.qml
</file>
...
...
tests/src/app/App.cpp
View file @
530c3129
...
...
@@ -43,8 +43,9 @@ App::App (int &argc, char **argv) : QApplication(argc, argv) {
.
arg
(
current_locale
.
name
());
}
// Provide avatars
loader
.
// Provide avatars
/thumbnails providers
.
m_engine
.
addImageProvider
(
AvatarProvider
::
PROVIDER_ID
,
&
m_avatar_provider
);
m_engine
.
addImageProvider
(
ThumbnailProvider
::
PROVIDER_ID
,
&
m_thumbnail_provider
);
setWindowIcon
(
QIcon
(
WINDOW_ICON_PATH
));
...
...
tests/src/app/App.hpp
View file @
530c3129
...
...
@@ -9,6 +9,7 @@
#include "../components/notifier/Notifier.hpp"
#include "AvatarProvider.hpp"
#include "DefaultTranslator.hpp"
#include "ThumbnailProvider.hpp"
// =============================================================================
...
...
@@ -57,6 +58,7 @@ private:
QSystemTrayIcon
*
m_system_tray_icon
=
nullptr
;
AvatarProvider
m_avatar_provider
;
ThumbnailProvider
m_thumbnail_provider
;
DefaultTranslator
m_default_translator
;
QTranslator
m_english_translator
;
...
...
tests/src/app/AvatarProvider.cpp
View file @
530c3129
...
...
@@ -7,18 +7,13 @@
const
QString
AvatarProvider
::
PROVIDER_ID
=
"avatar"
;
AvatarProvider
::
AvatarProvider
()
:
QQuickImageProvider
(
AvatarProvider
::
AvatarProvider
()
:
QQuickImageProvider
(
QQmlImageProviderBase
::
Image
,
QQmlImageProviderBase
::
ForceAsynchronousImageLoading
)
{
m_avatars_path
=
Utils
::
linphoneStringToQString
(
Paths
::
getAvatarsDirpath
());
}
QImage
AvatarProvider
::
requestImage
(
const
QString
&
id
,
QSize
*
,
const
QSize
&
)
{
QImage
AvatarProvider
::
requestImage
(
const
QString
&
id
,
QSize
*
,
const
QSize
&
)
{
return
QImage
(
m_avatars_path
+
id
);
}
tests/src/app/AvatarProvider.hpp
View file @
530c3129
...
...
@@ -10,11 +10,7 @@ public:
AvatarProvider
();
~
AvatarProvider
()
=
default
;
QImage
requestImage
(
const
QString
&
id
,
QSize
*
size
,
const
QSize
&
requested_size
)
override
;
QImage
requestImage
(
const
QString
&
id
,
QSize
*
size
,
const
QSize
&
requested_size
)
override
;
static
const
QString
PROVIDER_ID
;
...
...
tests/src/app/Paths.cpp
View file @
530c3129
...
...
@@ -11,7 +11,7 @@
#ifdef _WIN32
#define MAIN_PATH \
QStandardPaths::writableLocation(QStandardPaths::DataLocation
)
(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/"
)
#define PATH_CONFIG "linphonerc"
#define LINPHONE_FOLDER "linphone/"
...
...
@@ -19,15 +19,16 @@
#else
#define MAIN_PATH \
QStandardPaths::writableLocation(QStandardPaths::HomeLocation
)
(QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/"
)
#define PATH_CONFIG ".linphonerc"
#define LINPHONE_FOLDER ".linphone/"
#endif // ifdef _WIN32
#define PATH_AVATARS LINPHONE_FOLDER "avatars/"
#define PATH_LOGS LINPHONE_FOLDER "logs/"
#define PATH_AVATARS (LINPHONE_FOLDER "avatars/")
#define PATH_LOGS (LINPHONE_FOLDER "logs/")
#define PATH_THUMBNAILS (LINPHONE_FOLDER "thumbnails/")
#define PATH_CALL_HISTORY_LIST ".linphone-call-history.db"
#define PATH_FRIENDS_LIST ".linphone-friends.db"
...
...
@@ -65,25 +66,29 @@ inline string getFilePath (const QString &filename) {
// -----------------------------------------------------------------------------
string
Paths
::
getAvatarsDirpath
()
{
return
getDirectoryPath
(
MAIN_PATH
+
"/"
PATH_AVATARS
);
return
getDirectoryPath
(
MAIN_PATH
+
PATH_AVATARS
);
}
string
Paths
::
getCallHistoryFilepath
()
{
return
getFilePath
(
MAIN_PATH
+
"/"
+
PATH_CALL_HISTORY_LIST
);
return
getFilePath
(
MAIN_PATH
+
PATH_CALL_HISTORY_LIST
);
}
string
Paths
::
getConfigFilepath
()
{
return
getFilePath
(
MAIN_PATH
+
"/"
+
PATH_CONFIG
);
return
getFilePath
(
MAIN_PATH
+
PATH_CONFIG
);
}
string
Paths
::
getFriendsListFilepath
()
{
return
getFilePath
(
MAIN_PATH
+
"/"
+
PATH_FRIENDS_LIST
);
return
getFilePath
(
MAIN_PATH
+
PATH_FRIENDS_LIST
);
}
string
Paths
::
getLogsDirpath
()
{
return
getDirectoryPath
(
MAIN_PATH
+
"/"
PATH_LOGS
);
return
getDirectoryPath
(
MAIN_PATH
+
PATH_LOGS
);
}
string
Paths
::
getMessageHistoryFilepath
()
{
return
getFilePath
(
MAIN_PATH
+
"/"
+
PATH_MESSAGE_HISTORY_LIST
);
return
getFilePath
(
MAIN_PATH
+
PATH_MESSAGE_HISTORY_LIST
);
}
string
Paths
::
getThumbnailsDirPath
()
{
return
getDirectoryPath
(
MAIN_PATH
+
PATH_THUMBNAILS
);
}
tests/src/app/Paths.hpp
View file @
530c3129
...
...
@@ -12,6 +12,7 @@ namespace Paths {
std
::
string
getFriendsListFilepath
();
std
::
string
getLogsDirpath
();
std
::
string
getMessageHistoryFilepath
();
std
::
string
getThumbnailsDirPath
();
}
#endif // PATHS_H_
tests/src/app/ThumbnailProvider.cpp
0 → 100644
View file @
530c3129
#include "Paths.hpp"
#include "../utils.hpp"
#include "ThumbnailProvider.hpp"
// =============================================================================
const
QString
ThumbnailProvider
::
PROVIDER_ID
=
"thumbnail"
;
ThumbnailProvider
::
ThumbnailProvider
()
:
QQuickImageProvider
(
QQmlImageProviderBase
::
Image
,
QQmlImageProviderBase
::
ForceAsynchronousImageLoading
)
{
m_thumbnails_path
=
Utils
::
linphoneStringToQString
(
Paths
::
getThumbnailsDirPath
());
}
QImage
ThumbnailProvider
::
requestImage
(
const
QString
&
id
,
QSize
*
,
const
QSize
&
)
{
return
QImage
(
m_thumbnails_path
+
id
);
}
tests/src/app/ThumbnailProvider.hpp
0 → 100644
View file @
530c3129
#ifndef THUMBNAIL_PROVIDER_H_
#define THUMBNAIL_PROVIDER_H_
#include <QQuickImageProvider>
// =============================================================================
class
ThumbnailProvider
:
public
QQuickImageProvider
{
public:
ThumbnailProvider
();
~
ThumbnailProvider
()
=
default
;
QImage
requestImage
(
const
QString
&
id
,
QSize
*
size
,
const
QSize
&
requested_size
)
override
;
static
const
QString
PROVIDER_ID
;
private:
QString
m_thumbnails_path
;
};
#endif // THUMBNAIL_PROVIDER_H_
tests/src/components/chat/ChatModel.cpp
View file @
530c3129
#include <algorithm>
#include <QDateTime>
#include <QTimer>
#include <QFileInfo>
#include <QImage>
#include <QtDebug>
#include <QTimer>
#include <QUuid>
#include "../../app/Paths.hpp"
#include "../../app/ThumbnailProvider.hpp"
#include "../../utils.hpp"
#include "../core/CoreManager.hpp"
#include "ChatModel.hpp"
#define THUMBNAIL_IMAGE_FILE_HEIGHT 100
#define THUMBNAIL_IMAGE_FILE_WIDTH 100
using
namespace
std
;
// =============================================================================
inline
void
fillThumbnailProperty
(
QVariantMap
&
dest
,
const
shared_ptr
<
linphone
::
ChatMessage
>
&
message
)
{
string
data
=
message
->
getAppdata
();
if
(
!
data
.
empty
())
dest
[
"thumbnail"
]
=
QStringLiteral
(
"image://%1/%2"
)
.
arg
(
ThumbnailProvider
::
PROVIDER_ID
).
arg
(
::
Utils
::
linphoneStringToQString
(
data
));
}
inline
void
removeFileMessageThumbnail
(
const
shared_ptr
<
linphone
::
ChatMessage
>
&
message
)
{
if
(
message
&&
message
->
getFileTransferInformation
())
{
message
->
cancelFileTransfer
();
string
file_id
=
message
->
getAppdata
();
if
(
!
file_id
.
empty
())
{
QString
thumbnail_path
=
::
Utils
::
linphoneStringToQString
(
Paths
::
getThumbnailsDirPath
()
+
file_id
);
if
(
!
QFile
::
remove
(
thumbnail_path
))
qWarning
()
<<
QStringLiteral
(
"Unable to remove `%1`."
).
arg
(
thumbnail_path
);
}
}
}
// -----------------------------------------------------------------------------
class
ChatModel
::
MessageHandlers
:
public
linphone
::
ChatMessageListener
{
friend
class
ChatModel
;
...
...
@@ -22,48 +52,93 @@ public:
~
MessageHandlers
()
=
default
;
private:
QList
<
ChatEntryData
>::
iterator
findMessageEntry
(
const
shared_ptr
<
linphone
::
ChatMessage
>
&
message
)
{
return
find_if
(
m_chat_model
->
m_entries
.
begin
(),
m_chat_model
->
m_entries
.
end
(),
[
&
message
](
const
ChatEntryData
&
pair
)
{
return
pair
.
second
==
message
;
}
);
}
void
signalDataChanged
(
const
QList
<
ChatEntryData
>::
iterator
&
it
)
{
int
row
=
static_cast
<
int
>
(
distance
(
m_chat_model
->
m_entries
.
begin
(),
it
));
emit
m_chat_model
->
dataChanged
(
m_chat_model
->
index
(
row
,
0
),
m_chat_model
->
index
(
row
,
0
));
}
void
onFileTransferRecv
(
const
shared_ptr
<
linphone
::
ChatMessage
>
&
message
,
const
shared_ptr
<
linphone
::
Content
>
&
content
,
const
shared_ptr
<
linphone
::
Buffer
>
&
buffer
const
shared_ptr
<
linphone
::
ChatMessage
>
&
,
const
shared_ptr
<
linphone
::
Content
>
&
,
const
shared_ptr
<
linphone
::
Buffer
>
&
)
override
{
q
Debug
()
<<
"Not yet implemented
"
;
q
Warning
()
<<
"`onFileTransferRecv` called.
"
;
}
shared_ptr
<
linphone
::
Buffer
>
onFileTransferSend
(
const
shared_ptr
<
linphone
::
ChatMessage
>
&
message
,
const
shared_ptr
<
linphone
::
Content
>
&
content
,
size_t
offset
,
size_t
size
const
shared_ptr
<
linphone
::
ChatMessage
>
&
,
const
shared_ptr
<
linphone
::
Content
>
&
,
size_t
,
size_t
)
override
{
qDebug
()
<<
"Not yet implemented"
;
qWarning
()
<<
"`onFileTransferSend` called."
;
return
nullptr
;
}
void
onFileTransferProgressIndication
(
const
shared_ptr
<
linphone
::
ChatMessage
>
&
message
,
const
shared_ptr
<
linphone
::
Content
>
&
content
,
const
shared_ptr
<
linphone
::
Content
>
&
,
size_t
offset
,
size_t
total
size_t
)
override
{
qDebug
()
<<
"Not yet implemented"
;
if
(
!
m_chat_model
)
return
;
auto
it
=
findMessageEntry
(
message
);
if
(
it
==
m_chat_model
->
m_entries
.
end
())
return
;
(
*
it
).
first
[
"fileOffset"
]
=
static_cast
<
quint64
>
(
offset
);
signalDataChanged
(
it
);
}
void
onMsgStateChanged
(
const
shared_ptr
<
linphone
::
ChatMessage
>
&
message
,
linphone
::
ChatMessageState
state
)
override
{
if
(
!
m_chat_model
)
return
;
ChatModel
&
chat
=
*
m_chat_model
;
auto
it
=
find_if
(
chat
.
m_entries
.
begin
(),
chat
.
m_entries
.
end
(),
[
&
message
](
const
ChatEntryData
&
pair
)
{
return
pair
.
second
==
message
;
});
if
(
it
==
chat
.
m_entries
.
end
())
auto
it
=
findMessageEntry
(
message
);
if
(
it
==
m_chat_model
->
m_entries
.
end
())
return
;
if
(
state
==
linphone
::
ChatMessageStateFileTransferError
)
state
=
linphone
::
ChatMessageStateNotDelivered
;
else
if
(
state
==
linphone
::
ChatMessageStateFileTransferDone
)
{
QString
thumbnail_path
=
::
Utils
::
linphoneStringToQString
(
message
->
getFileTransferFilepath
());
QImage
image
(
thumbnail_path
);
if
(
!
image
.
isNull
())
{
QImage
thumbnail
=
image
.
scaled
(
THUMBNAIL_IMAGE_FILE_WIDTH
,
THUMBNAIL_IMAGE_FILE_HEIGHT
,
Qt
::
KeepAspectRatio
,
Qt
::
SmoothTransformation
);
QString
uuid
=
QUuid
::
createUuid
().
toString
();
QString
file_id
=
QStringLiteral
(
"%1.jpg"
).
arg
(
uuid
.
mid
(
1
,
uuid
.
length
()
-
2
));
if
(
!
thumbnail
.
save
(
::
Utils
::
linphoneStringToQString
(
Paths
::
getThumbnailsDirPath
())
+
file_id
,
"jpg"
,
100
))
{
qWarning
()
<<
QStringLiteral
(
"Unable to create thumbnail of: `%1`."
).
arg
(
thumbnail_path
);
return
;
}
message
->
setAppdata
(
::
Utils
::
qStringToLinphoneString
(
file_id
));
fillThumbnailProperty
((
*
it
).
first
,
message
);
}
state
=
linphone
::
ChatMessageStateDelivered
;
}
(
*
it
).
first
[
"status"
]
=
state
;
int
row
=
distance
(
chat
.
m_entries
.
begin
(),
it
);
emit
chat
.
dataChanged
(
chat
.
index
(
row
,
0
),
chat
.
index
(
row
,
0
)
);
signalDataChanged
(
it
);
}
ChatModel
*
m_chat_model
;
...
...
@@ -245,15 +320,22 @@ void ChatModel::removeAllEntries () {
}
void
ChatModel
::
sendMessage
(
const
QString
&
message
)
{
if
(
!
m_chat_room
)
return
;
shared_ptr
<
linphone
::
ChatMessage
>
_message
=
m_chat_room
->
createMessage
(
::
Utils
::
qStringToLinphoneString
(
message
));
_message
->
setListener
(
m_message_handlers
);
m_chat_room
->
sendMessage
(
_message
);
insertMessageAtEnd
(
_message
);
m_chat_room
->
sendMessage
(
_message
);
emit
messageSent
(
_message
);
}
void
ChatModel
::
resendMessage
(
int
id
)
{
if
(
!
m_chat_room
)
return
;
if
(
id
<
0
||
id
>
m_entries
.
count
())
{
qWarning
()
<<
QStringLiteral
(
"Entry %1 not exists."
).
arg
(
id
);
return
;
...
...
@@ -275,23 +357,48 @@ void ChatModel::resendMessage (int id) {
m_chat_room
->
sendMessage
(
message
);
}
void
ChatModel
::
sendFileMessage
(
const
QString
&
path
)
{
if
(
!
m_chat_room
)
return
;
QFile
file
(
path
);
if
(
!
file
.
exists
())
return
;
shared_ptr
<
linphone
::
Content
>
content
=
CoreManager
::
getInstance
()
->
getCore
()
->
createContent
();
content
->
setType
(
"application"
);
content
->
setSubtype
(
"octet-stream"
);
content
->
setSize
(
file
.
size
());
content
->
setName
(
::
Utils
::
qStringToLinphoneString
(
QFileInfo
(
file
).
fileName
()));
shared_ptr
<
linphone
::
ChatMessage
>
message
=
m_chat_room
->
createFileTransferMessage
(
content
);
message
->
setFileTransferFilepath
(
::
Utils
::
qStringToLinphoneString
(
path
));
message
->
setListener
(
m_message_handlers
);
insertMessageAtEnd
(
message
);
m_chat_room
->
sendMessage
(
message
);
emit
messageSent
(
message
);
}
// -----------------------------------------------------------------------------
void
ChatModel
::
fillMessageEntry
(
QVariantMap
&
dest
,
const
shared_ptr
<
linphone
::
ChatMessage
>
&
message
)
{
void
ChatModel
::
fillMessageEntry
(
QVariantMap
&
dest
,
const
shared_ptr
<
linphone
::
ChatMessage
>
&
message
)
{
dest
[
"type"
]
=
EntryType
::
MessageEntry
;
dest
[
"timestamp"
]
=
QDateTime
::
fromMSecsSinceEpoch
(
message
->
getTime
()
*
1000
);
dest
[
"content"
]
=
::
Utils
::
linphoneStringToQString
(
message
->
getText
());
dest
[
"isOutgoing"
]
=
message
->
isOutgoing
();
dest
[
"isOutgoing"
]
=
message
->
isOutgoing
()
||
message
->
getState
()
==
linphone
::
ChatMessageStateIdle
;
dest
[
"status"
]
=
message
->
getState
();
shared_ptr
<
linphone
::
Content
>
content
=
message
->
getFileTransferInformation
();
if
(
content
)
{
dest
[
"fileSize"
]
=
static_cast
<
quint64
>
(
content
->
getSize
());
dest
[
"fileName"
]
=
::
Utils
::
linphoneStringToQString
(
content
->
getName
());
fillThumbnailProperty
(
dest
,
message
);
}
}
void
ChatModel
::
fillCallStartEntry
(
QVariantMap
&
dest
,
const
shared_ptr
<
linphone
::
CallLog
>
&
call_log
)
{
void
ChatModel
::
fillCallStartEntry
(
QVariantMap
&
dest
,
const
shared_ptr
<
linphone
::
CallLog
>
&
call_log
)
{
QDateTime
timestamp
=
QDateTime
::
fromMSecsSinceEpoch
(
call_log
->
getStartDate
()
*
1000
);
dest
[
"type"
]
=
EntryType
::
CallEntry
;
...
...
@@ -301,10 +408,7 @@ void ChatModel::fillCallStartEntry (
dest
[
"isStart"
]
=
true
;
}
void
ChatModel
::
fillCallEndEntry
(
QVariantMap
&
dest
,
const
shared_ptr
<
linphone
::
CallLog
>
&
call_log
)
{
void
ChatModel
::
fillCallEndEntry
(
QVariantMap
&
dest
,
const
shared_ptr
<
linphone
::
CallLog
>
&
call_log
)
{
QDateTime
timestamp
=
QDateTime
::
fromMSecsSinceEpoch
((
call_log
->
getStartDate
()
+
call_log
->
getDuration
())
*
1000
);
dest
[
"type"
]
=
EntryType
::
CallEntry
;
...
...
@@ -320,10 +424,14 @@ void ChatModel::removeEntry (ChatEntryData &pair) {
int
type
=
pair
.
first
[
"type"
].
toInt
();
switch
(
type
)
{
case
ChatModel
:
:
MessageEntry
:
m_chat_room
->
deleteMessage
(
static_pointer_cast
<
linphone
::
ChatMessage
>
(
pair
.
second
));
case
ChatModel
:
:
MessageEntry
:
{
shared_ptr
<
linphone
::
ChatMessage
>
message
=
static_pointer_cast
<
linphone
::
ChatMessage
>
(
pair
.
second
);
removeFileMessageThumbnail
(
message
);
m_chat_room
->
deleteMessage
(
message
);
break
;
case
ChatModel
:
:
CallEntry
:
}
case
ChatModel
:
:
CallEntry
:
{
if
(
pair
.
first
[
"status"
].
toInt
()
==
linphone
::
CallStatusSuccess
)
{
// WARNING: Unable to remove symmetric call here. (start/end)
// We are between `beginRemoveRows` and `endRemoveRows`.
...
...
@@ -343,6 +451,8 @@ void ChatModel::removeEntry (ChatEntryData &pair) {
CoreManager
::
getInstance
()
->
getCore
()
->
removeCallLog
(
static_pointer_cast
<
linphone
::
CallLog
>
(
pair
.
second
));
break
;
}
default:
qWarning
()
<<
QStringLiteral
(
"Unknown chat entry type: %1."
).
arg
(
type
);
}
...
...
tests/src/components/chat/ChatModel.hpp
View file @
530c3129
...
...
@@ -18,8 +18,6 @@ class ChatModel : public QAbstractListModel {
Q_PROPERTY
(
QString
sipAddress
READ
getSipAddress
WRITE
setSipAddress
NOTIFY
sipAddressChanged
);
public:
typedef
QPair
<
QVariantMap
,
std
::
shared_ptr
<
void
>
>
ChatEntryData
;
enum
Roles
{
ChatEntry
=
Qt
::
DisplayRole
,
SectionDate
...
...
@@ -70,6 +68,8 @@ public:
void
resendMessage
(
int
id
);
void
sendFileMessage
(
const
QString
&
path
);
signals:
void
sipAddressChanged
(
const
QString
&
sip_address
);
void
allEntriesRemoved
();
...
...
@@ -80,20 +80,11 @@ signals:
void
messagesCountReset
();
private:
void
fillMessageEntry
(
QVariantMap
&
dest
,
const
std
::
shared_ptr
<
linphone
::
ChatMessage
>
&
message
);
void
fillCallStartEntry
(
QVariantMap
&
dest
,
const
std
::
shared_ptr
<
linphone
::
CallLog
>
&
call_log
);
void
fillCallEndEntry
(
QVariantMap
&
dest
,
const
std
::
shared_ptr
<
linphone
::
CallLog
>
&
call_log
);
typedef
QPair
<
QVariantMap
,
std
::
shared_ptr
<
void
>
>
ChatEntryData
;
void
fillMessageEntry
(
QVariantMap
&
dest
,
const
std
::
shared_ptr
<
linphone
::
ChatMessage
>
&
message
);
void
fillCallStartEntry
(
QVariantMap
&
dest
,
const
std
::
shared_ptr
<
linphone
::
CallLog
>
&
call_log
);
void
fillCallEndEntry
(
QVariantMap
&
dest
,
const
std
::
shared_ptr
<
linphone
::
CallLog
>
&
call_log
);
void
removeEntry
(
ChatEntryData
&
pair
);
...
...
tests/src/components/chat/ChatProxyModel.cpp
View file @
530c3129
...
...
@@ -105,10 +105,18 @@ void ChatProxyModel::resendMessage (int id) {
);
}
void
ChatProxyModel
::
sendFileMessage
(
const
QString
&
path
)
{
static_cast
<
ChatModel
*>
(
m_chat_model_filter
->
sourceModel
())
->
sendFileMessage
(
path
);
}
// -----------------------------------------------------------------------------
bool
ChatProxyModel
::
filterAcceptsRow
(
int
source_row
,
const
QModelIndex
&
)
const
{
return
m_chat_model_filter
->
rowCount
()
-
source_row
<=
m_n_max_displayed_entries
;
}
// -----------------------------------------------------------------------------
QString
ChatProxyModel
::
getSipAddress
()
const
{
return
static_cast
<
ChatModel
*>
(
m_chat_model_filter
->
sourceModel
())
->
getSipAddress
();
}
...
...
tests/src/components/chat/ChatProxyModel.hpp
View file @
530c3129
...
...
@@ -12,12 +12,7 @@ class ChatProxyModel : public QSortFilterProxyModel {
Q_OBJECT
;
Q_PROPERTY
(
QString
sipAddress
READ
getSipAddress
WRITE
setSipAddress
NOTIFY
sipAddressChanged
);
Q_PROPERTY
(
QString
sipAddress
READ
getSipAddress
WRITE
setSipAddress
NOTIFY
sipAddressChanged
);
public:
ChatProxyModel
(
QObject
*
parent
=
Q_NULLPTR
);
...
...
@@ -31,6 +26,8 @@ public:
Q_INVOKABLE
void
sendMessage
(
const
QString
&
message
);
Q_INVOKABLE
void
resendMessage
(
int
id
);
Q_INVOKABLE
void
sendFileMessage
(
const
QString
&
path
);
signals:
void
sipAddressChanged
(
const
QString
&
sip_address
);
void
moreEntriesLoaded
(
int
n
);
...
...
tests/src/components/settings/SettingsModel.cpp
View file @
530c3129
#include "../../utils.hpp"
#include "../core/CoreManager.hpp"
#include "SettingsModel.hpp"
...
...
@@ -18,7 +19,19 @@ bool SettingsModel::getAutoAnswerStatus () const {
return
!!
m_config
->
getInt
(
UI_SECTION
,
"auto_answer"
,
0
);
}
bool
SettingsModel
::
setAutoAnswerStatus
(
bool
status
)
{
void
SettingsModel
::
setAutoAnswerStatus
(
bool
status
)
{
m_config
->
setInt
(
UI_SECTION
,
"auto_answer"
,
status
);
emit
autoAnswerStatusChanged
(
status
);
}
QString
SettingsModel
::
getFileTransferUrl
()
const
{
return
::
Utils
::
linphoneStringToQString
(
CoreManager
::
getInstance
()
->
getCore
()
->
getFileTransferServer
()
);
}
void
SettingsModel
::
setFileTransferUrl
(
const
QString
&
url
)
{
CoreManager
::
getInstance
()
->
getCore
()
->
setFileTransferServer
(
::
Utils
::
qStringToLinphoneString
(
url
)
);
}
tests/src/components/settings/SettingsModel.hpp
View file @
530c3129
...
...
@@ -4,24 +4,27 @@
#include <linphone++/linphone.hh>
#include <QObject>
#include "AccountSettingsModel.hpp"
// =============================================================================
class
SettingsModel
:
public
QObject
{
Q_OBJECT
;
Q_PROPERTY
(
bool
autoAnswerStatus
READ
getAutoAnswerStatus
WRITE
setAutoAnswerStatus
NOTIFY
autoAnswerStatusChanged
);
Q_PROPERTY
(
QString
fileTransferUrl
READ
getFileTransferUrl
WRITE
setFileTransferUrl
NOTIFY
fileTransferUrlChanged
);
public:
SettingsModel
(
QObject
*
parent
=
Q_NULLPTR
);
signals:
void
autoAnswerStatusChanged
(
bool
status
);
void
fileTransferUrlChanged
(
const
QString
&
url
);
private:
bool
getAutoAnswerStatus
()
const
;
bool
setAutoAnswerStatus
(
bool
status
);
void
setAutoAnswerStatus
(
bool
status
);
QString
getFileTransferUrl
()
const
;
void
setFileTransferUrl
(
const
QString
&
url
);
std
::
shared_ptr
<
linphone
::
Config
>
m_config
;
...
...
tests/ui/modules/Common/Colors.qml
View file @
530c3129
...
...
@@ -21,6 +21,7 @@ QtObject {
property
color
k
:
'
#FFFFFF
'
property
color
k50
:
'
#32FFFFFF
'
property
color
l
:
'
#000000
'
property
color
l50
:
'
#32000000
'
property
color
m
:
'
#D1D1D1
'
property
color
n
:
'
#C0C0C0
'
property
color
o
:
'
#232323
'
...
...
@@ -34,4 +35,5 @@ QtObject {
property
color
w
:
'
#A1A1A1
'
property
color
x
:
'
#96A5B1
'
property
color
y
:
'
#D0D8DE
'
property
color
z
:
'
#17A81A
'
}
tests/ui/modules/Common/DroppableTextArea.qml
View file @
530c3129
...
...
@@ -7,9 +7,14 @@ import Common.Styles 1.0
// =============================================================================
Item
{
id
:
droppableTextArea
property
alias
placeholderText
:
textArea
.
placeholderText
property
alias
text
:
textArea
.
text
property
bool
dropEnabled
:
true
property
string
dropDisabledReason
// ---------------------------------------------------------------------------
signal
dropped
(
var
files
)
...
...
@@ -73,6 +78,7 @@ Item {
DroppableTextAreaStyle
.
fileChooserButton
.
margins
verticalCenter
:
parent
.
verticalCenter
}
enabled
:
droppableTextArea
.
dropEnabled
icon
:
'
attachment
'
iconSize
:
DroppableTextAreaStyle
.
fileChooserButton
.
size
...
...
@@ -88,7 +94,9 @@ Item {
}
TooltipArea
{
text
:
qsTr
(
'
attachmentTooltip
'
)
text
:
droppableTextArea
.
dropEnabled
?
qsTr
(
'
attachmentTooltip
'
)
:
droppableTextArea
.
dropDisabledReason
}
}
...
...
@@ -111,6 +119,7 @@ Item {
DropArea
{
anchors.fill
:
parent
keys
:
[
'
text/uri-list
'
]
visible
:
droppableTextArea
.
dropEnabled
onDropped
:
{
state
=
''
...
...
tests/ui/modules/Common/Form/ExclusiveButtons.spec.qml
View file @
530c3129
...
...
@@ -25,13 +25,7 @@ Item {
ExclusiveButtons
{
id
:
exclusiveButtons
texts
:
[
qsTr
(
'
A
'
),
qsTr
(
'
B
'
),
qsTr
(
'
C
'
),
qsTr
(
'
D
'
),
qsTr
(
'
E
'
)
]
texts
:
[
'
A
'
,
'
B
'
,
'
C
'
,
'
D
'
,
'
E
'
]
}
SignalSpy
{
...
...
tests/ui/modules/Linphone/Chat/Chat.qml
View file @
530c3129
...
...
@@ -191,6 +191,11 @@ ColumnLayout {
OutgoingMessage
{}
}
Component
{
id
:
fileMessage
FileMessage
{}
}
// -----------------------------------------------------------------------
MouseArea
{
...
...
@@ -223,9 +228,17 @@ ColumnLayout {
// Display content.
Loader
{
Layout.fillWidth
:
true
sourceComponent
:
$chatEntry
.
type
===
ChatModel
.
MessageEntry
?
(
$chatEntry
.
isOutgoing
?
outgoingMessage
:
incomingMessage
)
:
event
sourceComponent
:
{
if
(
$chatEntry
.
fileName
)
{
return
fileMessage
}
if
(
$chatEntry
.
type
===
ChatModel
.
CallEntry
)
{
return
event
}
return
$chatEntry
.
isOutgoing
?
outgoingMessage
:
incomingMessage
}
}
}
}
...
...
@@ -245,8 +258,15 @@ ColumnLayout {
DroppableTextArea
{
anchors.fill
:
parent
dropEnabled
:
SettingsModel
.
fileTransferUrl
.
length
>
0
dropDisabledReason
:
qsTr
(
'
noFileTransferUrl
'
)
placeholderText
:
qsTr
(
'
newMessagePlaceholder
'
)
onDropped
:
{
_bindToEnd
=
true
files
.
forEach
(
proxyModel
.
sendFileMessage
)
}
onValidText
:
{
this
.
text
=
''
_bindToEnd
=
true
...
...
tests/ui/modules/Linphone/Chat/FileMessage.qml
0 → 100644
View file @
530c3129
import
QtQuick
2.7
import
QtQuick
.
Controls
2.0
import
QtQuick
.
Layouts
1.3
import
Common
1.0
import
Linphone
1.0
import
LinphoneUtils
1.0
import
Linphone
.
Styles
1.0
import
Utils
1.0
// =============================================================================
Row
{
// ---------------------------------------------------------------------------
// Avatar if it's an incoming message.
// ---------------------------------------------------------------------------
Item
{
height
:
ChatStyle
.
entry
.
lineHeight
width
:
ChatStyle
.
entry
.
metaWidth
Component
{
id
:
avatar
Avatar
{
height
:
ChatStyle
.
entry
.
message
.
incoming
.
avatarSize
width
:
ChatStyle
.
entry
.
message
.
incoming
.
avatarSize
image
:
_contactObserver
.
contact
?
_contactObserver
.
contact
.
avatar
:
''
username
:
LinphoneUtils
.
getContactUsername
(
_contactObserver
.
contact
||
proxyModel
.
sipAddress
)
}
}
Loader
{
anchors.centerIn
:
parent
sourceComponent
:
!
$chatEntry
.
isOutgoing
?
avatar
:
undefined
}
}
// ---------------------------------------------------------------------------
// File message.
// ---------------------------------------------------------------------------
Row
{
spacing
:
ChatStyle
.
entry
.
message
.
extraContent
.
leftMargin
Rectangle
{
id
:
rectangle
color
:
$chatEntry
.
isOutgoing
?
ChatStyle
.
entry
.
message
.
outgoing
.
backgroundColor
:
ChatStyle
.
entry
.
message
.
incoming
.
backgroundColor
height
:
ChatStyle
.
entry
.
message
.
file
.
height
width
:
ChatStyle
.
entry
.
message
.
file
.
width
radius
:
ChatStyle
.
entry
.
message
.
radius
RowLayout
{
anchors
{
fill
:
parent
margins
:
ChatStyle
.
entry
.
message
.
file
.
margins
}
spacing
:
ChatStyle
.
entry
.
message
.
file
.
spacing
// ---------------------------------------------------------------------
// Thumbnail or extension.
// ---------------------------------------------------------------------
Component
{
id
:
thumbnail
Image
{
source
:
$chatEntry
.
thumbnail
}
}
Component
{
id
:
extension
Rectangle
{
color
:
Colors
.
l50
Text
{
anchors.fill
:
parent
color
:
Colors
.
k
font.bold
:
true
elide
:
Text
.
ElideRight
text
:
Utils
.
getExtension
(
$chatEntry
.
fileName
).
toUpperCase
()
horizontalAlignment
:
Text
.
AlignHCenter
verticalAlignment
:
Text
.
AlignVCenter
}
}
}
Loader
{
Layout.preferredHeight
:
ChatStyle
.
entry
.
message
.
file
.
thumbnail
.
height
Layout.preferredWidth
:
ChatStyle
.
entry
.
message
.
file
.
thumbnail
.
width
sourceComponent
:
$chatEntry
.
thumbnail
?
thumbnail
:
extension
}
// ---------------------------------------------------------------------
// Upload or file status.
// ---------------------------------------------------------------------
Column
{
Layout.fillWidth
:
true
Layout.fillHeight
:
true
spacing
:
ChatStyle
.
entry
.
message
.
file
.
status
.
spacing
Text
{
id
:
fileName
color
:
$chatEntry
.
isOutgoing
?
ChatStyle
.
entry
.
message
.
outgoing
.
text
.
color
:
ChatStyle
.
entry
.
message
.
incoming
.
text
.
color
elide
:
Text
.
ElideRight
font
{
bold
:
true
pointSize
:
$chatEntry
.
isOutgoing
?
ChatStyle
.
entry
.
message
.
outgoing
.
text
.
fontSize
:
ChatStyle
.
entry
.
message
.
incoming
.
text
.
fontSize
}
text
:
$chatEntry
.
fileName
width
:
parent
.
width
}
ProgressBar
{
id
:
progressBar
height
:
ChatStyle
.
entry
.
message
.
file
.
status
.
bar
.
height
width
:
parent
.
width
to
:
$chatEntry
.
fileSize
value
:
$chatEntry
.
fileOffset
||
0
visible
:
$chatEntry
.
status
===
ChatModel
.
MessageStatusInProgress
background
:
Rectangle
{
color
:
Colors
.
f
radius
:
ChatStyle
.
entry
.
message
.
file
.
status
.
bar
.
radius
}
contentItem
:
Item
{
Rectangle
{
color
:
Colors
.
z
height
:
parent
.
height
width
:
progressBar
.
visualPosition
*
parent
.
width
}
}
}
Text
{
color
:
fileName
.
color
elide
:
Text
.
ElideRight
font.pointSize
:
fileName
.
font
.
pointSize
text
:
{
var
fileSize
=
Utils
.
formatSize
(
$chatEntry
.
fileSize
)
return
progressBar
.
visible
?
Utils
.
formatSize
(
$chatEntry
.
fileOffset
)
+
'
/
'
+
fileSize
:
fileSize
}
}
}
}
}
// -------------------------------------------------------------------------
// Resend/Remove file message.
// -------------------------------------------------------------------------
Row
{
spacing
:
ChatStyle
.
entry
.
message
.
extraContent
.
spacing
Component
{
id
:
icon
Icon
{
readonly
property
bool
isNotDelivered
:
$chatEntry
.
status
===
ChatModel
.
MessageStatusNotDelivered
icon
:
isNotDelivered
?
'
chat_error
'
:
'
chat_send
'
iconSize
:
ChatStyle
.
entry
.
message
.
outgoing
.
sendIconSize
MouseArea
{
anchors.fill
:
parent
onClicked
:
isNotDelivered
&&
proxyModel
.
resendMessage
(
index
)
}
}
}
Component
{
id
:
indicator
BusyIndicator
{
width
:
ChatStyle
.
entry
.
message
.
outgoing
.
sendIconSize
}
}
Loader
{
height
:
ChatStyle
.
entry
.
lineHeight
sourceComponent
:
$chatEntry
.
isOutgoing
?
(
$chatEntry
.
status
===
ChatModel
.
MessageStatusInProgress
?
indicator
:
icon
)
:
undefined
}
ActionButton
{
height
:
ChatStyle
.
entry
.
lineHeight
icon
:
'
delete
'
iconSize
:
ChatStyle
.
entry
.
deleteIconSize
visible
:
isHoverEntry
()
onClicked
:
removeEntry
()
}
}
}
}
tests/ui/modules/Linphone/Chat/OutgoingMessage.qml
View file @
530c3129
import
QtQuick
2.7
import
QtQuick
.
Controls
1.4
import
QtQuick
.
Controls
2.0
import
QtQuick
.
Layouts
1.3
import
Common
1.0
...
...
tests/ui/modules/Linphone/Styles/ChatStyle.qml
View file @
530c3129
...
...
@@ -56,6 +56,27 @@ QtObject {
property
int
rightMargin
:
5
}
property
QtObject
file
:
QtObject
{
property
int
height
:
64
property
int
margins
:
8
property
int
spacing
:
8
property
int
width
:
250
property
QtObject
status
:
QtObject
{
property
int
spacing
:
4
property
QtObject
bar
:
QtObject
{
property
int
height
:
6
property
int
radius
:
3
}
}
property
QtObject
thumbnail
:
QtObject
{
property
int
height
:
50
property
int
width
:
50
}
}
property
QtObject
images
:
QtObject
{
property
int
height
:
48
// `width` can be used.
...
...
tests/ui/scripts/Utils/utils.js
View file @
530c3129
...
...
@@ -314,6 +314,25 @@ function find (obj, cb, context) {
// -----------------------------------------------------------------------------
function
formatSize
(
size
)
{
var
units
=
[
'
KB
'
,
'
MB
'
,
'
GB
'
,
'
TB
'
]
var
unit
=
'
B
'
if
(
size
==
null
)
{
size
=
0
}
var
length
=
units
.
length
for
(
var
i
=
0
;
size
>=
1024
&&
i
<
length
;
i
++
)
{
unit
=
units
[
i
]
size
/=
1024
}
return
parseFloat
(
size
.
toFixed
(
2
))
+
unit
}
// -----------------------------------------------------------------------------
// Generate a random number in the [min, max[ interval.
// Uniform distrib.
function
genRandomNumber
(
min
,
max
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment