Verified Commit c2a206cc authored by Filipe Azevedo's avatar Filipe Azevedo Committed by Linus Jahn

Introduce MediaUtils helper

Enums explicit integral type was removed - Qml does not support them.

Task-Id: #287Signed-off-by: Linus Jahn's avatarLinus Jahn <lnj@kaidan.im>
parent 039a14a0
......@@ -55,7 +55,7 @@ include(FeatureSummary)
kde_enable_exceptions()
# Find packages
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Qml Quick Svg Sql QuickControls2 Xml Multimedia)
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Qml Quick Svg Sql QuickControls2 Xml Multimedia Positioning Location)
find_package(KF5Kirigami2 REQUIRED)
find_package(ZXing REQUIRED COMPONENTS Core)
find_package(QXmpp 1.0.0 REQUIRED)
......@@ -168,6 +168,8 @@ target_link_libraries(${PROJECT_NAME}
Qt5::Network
Qt5::Xml
Qt5::Multimedia
Qt5::Positioning
Qt5::Location
ZXing::Core
QXmpp::QXmpp
${__Qt5Widgets_LIBRARIES}
......
......@@ -39,7 +39,7 @@ how to do that:
### Dependencies
Here are the general dependencies of Kaidan listed:
* [Qt](https://doc.qt.io/qt-5/build-sources.html) (Core Qml Quick Svg Sql QuickControls2 Multimedia) (>= 5.10.0)
* [Qt](https://doc.qt.io/qt-5/build-sources.html) (Core Qml Quick Svg Sql QuickControls2 Xml Multimedia Positioning Location) (>= 5.10.0)
* [QXmpp][qxmpp] (>= 1.0.0)
* [Kirigami 2](https://phabricator.kde.org/source/kirigami/) (>= 5.42.0)
* [ECM (extra-cmake-modules)](https://api.kde.org/ecm/manual/ecm.7.html)
......
......@@ -31,6 +31,7 @@ set(KAIDAN_SOURCES
src/QrCodeDecoder
src/QrCodeScannerFilter.cpp
src/QrCodeVideoFrame.cpp
src/MediaUtils.cpp
# needed to trigger moc generation / to be displayed in IDEs
src/Enums.h
......
......@@ -41,7 +41,7 @@ namespace Enums {
/**
* Enumeration of possible connection states.
*/
enum class ConnectionState : quint8 {
enum class ConnectionState {
StateDisconnected = QXmppClient::DisconnectedState,
StateConnecting = QXmppClient::ConnectingState,
StateConnected = QXmppClient::ConnectedState
......@@ -51,7 +51,7 @@ namespace Enums {
/**
* Enumeration of possible disconnection reasons
*/
enum class DisconnectionReason : quint8 {
enum class DisconnectionReason {
ConnNoError,
ConnUserDisconnected,
ConnAuthenticationFailed,
......@@ -73,20 +73,22 @@ namespace Enums {
/**
* Enumeration of different media/message types
*/
enum class MessageType : quint8 {
enum class MessageType {
MessageUnknown = -1,
MessageText,
MessageFile,
MessageImage,
MessageVideo,
MessageAudio,
MessageDocument
MessageDocument,
MessageGeoLocation
};
Q_ENUM_NS(MessageType)
/**
* Enumeration of contact availability states
*/
enum class AvailabilityTypes : quint8 {
enum class AvailabilityTypes {
PresError,
PresUnavailable,
PresOnline,
......
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2019 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan 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.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan 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 Kaidan. If not, see <http://www.gnu.org/licenses/>.
*/
#include "MediaUtils.h"
#include <QFileInfo>
#include <QUrl>
#include <QRegularExpression>
#include <QTime>
static QList<QMimeType> mimeTypes(const QList<QMimeType> &mimeTypes, const QString &parent);
const QMimeDatabase MediaUtils::s_mimeDB;
const QList<QMimeType> MediaUtils::s_allMimeTypes(MediaUtils::s_mimeDB.allMimeTypes());
const QList<QMimeType> MediaUtils::s_imageTypes(::mimeTypes(MediaUtils::s_allMimeTypes, QStringLiteral("image")));
const QList<QMimeType> MediaUtils::s_audioTypes(::mimeTypes(MediaUtils::s_allMimeTypes, QStringLiteral("audio")));
const QList<QMimeType> MediaUtils::s_videoTypes(::mimeTypes(MediaUtils::s_allMimeTypes, QStringLiteral("video")));
const QList<QMimeType> MediaUtils::s_documentTypes {
s_mimeDB.mimeTypeForName(QStringLiteral("application/vnd.oasis.opendocument.presentation")),
s_mimeDB.mimeTypeForName(QStringLiteral("application/vnd.oasis.opendocument.spreadsheet")),
s_mimeDB.mimeTypeForName(QStringLiteral("application/vnd.oasis.opendocument.text")),
s_mimeDB.mimeTypeForName(QStringLiteral("application/vnd.openxmlformats-officedocument.presentationml.presentation")),
s_mimeDB.mimeTypeForName(QStringLiteral("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")),
s_mimeDB.mimeTypeForName(QStringLiteral("application/vnd.openxmlformats-officedocument.wordprocessingml.document")),
s_mimeDB.mimeTypeForName(QStringLiteral("application/pdf")),
s_mimeDB.mimeTypeForName(QStringLiteral("text/plain"))
};
const QList<QMimeType> MediaUtils::s_geoTypes {
s_mimeDB.mimeTypeForName(QStringLiteral("application/geo+json"))
};
const QRegularExpression MediaUtils::s_geoLocationRegExp(QStringLiteral("geo:([-+]?[0-9]*\\.?[0-9]+),([-+]?[0-9]*\\.?[0-9]+)"));
static QList<QMimeType> mimeTypes(const QList<QMimeType> &mimeTypes, const QString &parent) {
QList<QMimeType> mimes;
for (const QMimeType &mimeType: mimeTypes) {
if (mimeType.name().section(QLatin1Char('/'), 0, 0) == parent) {
mimes << mimeType;
}
}
return mimes;
}
static QString iconName(const QList<QMimeType> &mimeTypes) {
if (!mimeTypes.isEmpty()) {
for (const QMimeType &type: mimeTypes) {
const QString name = type.iconName();
if (!name.isEmpty()) {
return name;
}
}
}
return QStringLiteral("application-octet-stream");
}
static QString filter(const QList<QMimeType> &mimeTypes)
{
QStringList filters;
filters.reserve(mimeTypes.size() * 2);
for (const QMimeType &mimeType : mimeTypes) {
QString filter = mimeType.filterString();
if (filter.isEmpty()) {
filters << QStringLiteral("*");
continue;
}
const int start = filter.lastIndexOf(QLatin1Char('('));
const int end = filter.lastIndexOf(QLatin1Char(')'));
Q_ASSERT(start != -1);
Q_ASSERT(end != -1);
filters << filter.mid(start + 1, filter.size() - start - 1 - 1).split(QLatin1Char(' '));
}
if (filters.size() > 1) {
filters.removeDuplicates();
filters.removeAll(QStringLiteral("*"));
}
filters.reserve(filters.size());
return filters.join(QLatin1Char(' '));
}
static QString prettyFormat(int durationMsecs, QTime *outTime = nullptr) {
const QTime time(QTime(0, 0, 0).addMSecs(durationMsecs));
QString format;
if (time.hour() > 0) {
format.append(QStringLiteral("H"));
}
if (time.minute() > 0 || !format.isEmpty()) {
format.append(format.isEmpty() ? QStringLiteral("m") : QStringLiteral(":mm"));
}
if (time.second() > 0 || !format.isEmpty()) {
format.append(format.isEmpty() ? QStringLiteral("s") : QStringLiteral(":ss"));
}
if (outTime) {
*outTime = time;
}
return format;
}
QString MediaUtils::prettyDuration(int msecs)
{
QTime time;
const QString format = prettyFormat(msecs, &time);
return time.toString(format);
}
QString MediaUtils::prettyDuration(int msecs, int durationMsecs)
{
const QString format = prettyFormat(durationMsecs);
return QTime(0, 0, 0).addMSecs(msecs).toString(format);
}
bool MediaUtils::isHttp(const QString &content)
{
const QUrl url(content);
return url.isValid() && url.scheme().startsWith(QStringLiteral("http"));
}
bool MediaUtils::isGeoLocation(const QString &content)
{
return s_geoLocationRegExp.match(content).hasMatch();
}
QGeoCoordinate MediaUtils::locationCoordinate(const QString &content)
{
const QRegularExpressionMatch match = s_geoLocationRegExp.match(content);
return match.hasMatch()
? QGeoCoordinate(match.captured(1).toDouble(), match.captured(2).toDouble())
: QGeoCoordinate();
}
bool MediaUtils::localFileAvailable(const QString &filePath)
{
if (filePath.isEmpty()) {
return false;
}
const QUrl url(filePath);
return url.isValid() && url.isLocalFile()
? localFileAvailable(url)
: QFile::exists(filePath);
}
bool MediaUtils::localFileAvailable(const QUrl &url)
{
if (url.isValid() && url.isLocalFile()) {
return localFileAvailable(url.toLocalFile());
}
return false;
}
QUrl MediaUtils::fromLocalFile(const QString &filePath)
{
return QUrl::fromLocalFile(filePath);
}
QMimeType MediaUtils::mimeType(const QString &filePath)
{
if (filePath.isEmpty()) {
return {};
}
const QUrl url(filePath);
return url.isValid() && !url.scheme().isEmpty()
? mimeType(url)
: s_mimeDB.mimeTypeForFile(filePath);
}
QMimeType MediaUtils::mimeType(const QUrl &url)
{
if (!url.isValid()) {
return {};
}
if (url.isLocalFile()) {
return mimeType(url.toLocalFile());
}
if (url.scheme().compare(QStringLiteral("geo")) == 0) {
return s_geoTypes.first();
}
return mimeType(url.fileName());
}
QString MediaUtils::iconName(const QString &filePath)
{
if (filePath.isEmpty()) {
return {};
}
const QUrl url(filePath);
return url.isValid() && !url.scheme().isEmpty()
? iconName(url)
: ::iconName({ mimeType(filePath) });
}
QString MediaUtils::iconName(const QUrl &url)
{
if (!url.isValid()) {
return {};
}
if (url.isLocalFile()) {
return iconName(url.toLocalFile());
}
return iconName(url.fileName());
}
QString MediaUtils::newMediaLabel(Enums::MessageType hint)
{
switch (hint) {
case Enums::MessageType::MessageImage:
return tr("Take picture");
case Enums::MessageType::MessageVideo:
return tr("Record video");
case Enums::MessageType::MessageAudio:
return tr("Record voice");
case Enums::MessageType::MessageGeoLocation:
return tr("Send location");
case Enums::MessageType::MessageText:
case Enums::MessageType::MessageFile:
case Enums::MessageType::MessageDocument:
case Enums::MessageType::MessageUnknown:
break;
}
Q_UNREACHABLE();
}
QString MediaUtils::newMediaIconName(Enums::MessageType hint)
{
switch (hint) {
case Enums::MessageType::MessageImage:
return QStringLiteral("camera-photo-symbolic");
case Enums::MessageType::MessageVideo:
return QStringLiteral("camera-video-symbolic");
case Enums::MessageType::MessageAudio:
return QStringLiteral("microphone");
case Enums::MessageType::MessageGeoLocation:
return QStringLiteral("gps");
case Enums::MessageType::MessageText:
case Enums::MessageType::MessageFile:
// return QStringLiteral("file-search-symbolic");
case Enums::MessageType::MessageDocument:
// return QStringLiteral("document-open");
case Enums::MessageType::MessageUnknown:
break;
}
Q_UNREACHABLE();
}
QString MediaUtils::label(Enums::MessageType hint)
{
switch (hint) {
case Enums::MessageType::MessageFile:
return tr("Choose file");
case Enums::MessageType::MessageImage:
return tr("Choose image");
case Enums::MessageType::MessageVideo:
return tr("Choose video");
case Enums::MessageType::MessageAudio:
return tr("Choose audio file");
case Enums::MessageType::MessageDocument:
return tr("Choose document");
case Enums::MessageType::MessageGeoLocation:
case Enums::MessageType::MessageText:
case Enums::MessageType::MessageUnknown:
break;
}
Q_UNREACHABLE();
}
QString MediaUtils::iconName(Enums::MessageType hint)
{
return ::iconName(mimeTypes(hint));
}
QString MediaUtils::filterName(Enums::MessageType hint)
{
switch (hint) {
case Enums::MessageType::MessageText:
break;
case Enums::MessageType::MessageFile:
return tr("All files");
case Enums::MessageType::MessageImage:
return tr("Images");
case Enums::MessageType::MessageVideo:
return tr("Videos");
case Enums::MessageType::MessageAudio:
return tr("Audio files");
case Enums::MessageType::MessageDocument:
return tr("Documents");
case Enums::MessageType::MessageGeoLocation:
case Enums::MessageType::MessageUnknown:
break;
}
Q_UNREACHABLE();
}
QString MediaUtils::filter(Enums::MessageType hint)
{
return ::filter(mimeTypes(hint));
}
QString MediaUtils::namedFilter(Enums::MessageType hint)
{
switch (hint) {
case Enums::MessageType::MessageText:
break;
case Enums::MessageType::MessageFile:
case Enums::MessageType::MessageImage:
case Enums::MessageType::MessageVideo:
case Enums::MessageType::MessageAudio:
case Enums::MessageType::MessageDocument:
return tr("%1 (%2)").arg(filterName(hint), filter(hint));
case Enums::MessageType::MessageGeoLocation:
case Enums::MessageType::MessageUnknown:
break;
}
Q_UNREACHABLE();
}
QList<QMimeType> MediaUtils::mimeTypes(Enums::MessageType hint)
{
switch (hint) {
case Enums::MessageType::MessageImage:
return s_imageTypes;
case Enums::MessageType::MessageVideo:
return s_videoTypes;
case Enums::MessageType::MessageAudio:
return s_audioTypes;
case Enums::MessageType::MessageDocument:
return s_documentTypes;
case Enums::MessageType::MessageGeoLocation:
return s_geoTypes;
case Enums::MessageType::MessageText:
case Enums::MessageType::MessageUnknown:
break;
case Enums::MessageType::MessageFile:
return { s_mimeDB.mimeTypeForName(QStringLiteral("application/octet-stream")) };
}
Q_UNREACHABLE();
}
Enums::MessageType MediaUtils::messageType(const QString &filePath)
{
if (filePath.isEmpty()) {
return Enums::MessageType::MessageUnknown;
}
const QUrl url(filePath);
return url.isValid() && !url.scheme().isEmpty()
? messageType(url)
: messageType(s_mimeDB.mimeTypeForFile(filePath));
}
Enums::MessageType MediaUtils::messageType(const QUrl &url)
{
if (!url.isValid()) {
return Enums::MessageType::MessageUnknown;
}
if (url.isLocalFile()) {
return messageType(url.toLocalFile());
}
QList<QMimeType> mimeTypes;
if (url.scheme().compare(QStringLiteral("geo")) == 0) {
mimeTypes = s_geoTypes;
} else {
const QFileInfo fileInfo(url.fileName());
if (fileInfo.completeSuffix().isEmpty()) {
return Enums::MessageType::MessageUnknown;
}
mimeTypes = s_mimeDB.mimeTypesForFileName(fileInfo.fileName());
}
for (const QMimeType &mimeType: mimeTypes) {
const Enums::MessageType messageType = MediaUtils::messageType(mimeType);
if (messageType != Enums::MessageType::MessageUnknown) {
return messageType;
}
}
return Enums::MessageType::MessageUnknown;
}
Enums::MessageType MediaUtils::messageType(const QMimeType &mimeType)
{
if (!mimeType.isValid()) {
return Enums::MessageType::MessageUnknown;
}
if (mimeTypes(Enums::MessageType::MessageImage).contains(mimeType)) {
return Enums::MessageType::MessageImage;
} else if (mimeTypes(Enums::MessageType::MessageAudio).contains(mimeType)) {
return Enums::MessageType::MessageAudio;
} else if (mimeTypes(Enums::MessageType::MessageVideo).contains(mimeType)) {
return Enums::MessageType::MessageVideo;
} else if (mimeTypes(Enums::MessageType::MessageGeoLocation).contains(mimeType)) {
return Enums::MessageType::MessageGeoLocation;
} else if (mimeTypes(Enums::MessageType::MessageDocument).contains(mimeType)) {
return Enums::MessageType::MessageDocument;
}
return Enums::MessageType::MessageFile;
}
/*
* Kaidan - A user-friendly XMPP client for every device!
*
* Copyright (C) 2016-2019 Kaidan developers and contributors
* (see the LICENSE file for a full list of copyright authors)
*
* Kaidan 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.
*
* In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL
* project's "OpenSSL" library (or with modified versions of it that
* use the same license as the "OpenSSL" library), and distribute the
* linked executables. You must obey the GNU General Public License in
* all respects for all of the code used other than "OpenSSL". If you
* modify this file, you may extend this exception to your version of
* the file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
* Kaidan 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 Kaidan. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <QMimeDatabase>
#include <QObject>
#include <QGeoCoordinate>
#include "Enums.h"
class MediaUtils : public QObject
{
Q_OBJECT
public:
using QObject::QObject;
Q_INVOKABLE static QString prettyDuration(int msecs);
Q_INVOKABLE static QString prettyDuration(int msecs, int durationMsecs);
Q_INVOKABLE static bool isHttp(const QString &content);
Q_INVOKABLE static bool isGeoLocation(const QString &content);
Q_INVOKABLE static QGeoCoordinate locationCoordinate(const QString &content);
Q_INVOKABLE static bool localFileAvailable(const QString &filePath);
Q_INVOKABLE static bool localFileAvailable(const QUrl &url);
Q_INVOKABLE static QUrl fromLocalFile(const QString &filePath);
Q_INVOKABLE static QMimeType mimeType(const QString &filePath);
Q_INVOKABLE static QMimeType mimeType(const QUrl &url);
Q_INVOKABLE static QString iconName(const QString &filePath);
Q_INVOKABLE static QString iconName(const QUrl &url);
Q_INVOKABLE static QString newMediaLabel(Enums::MessageType hint);
Q_INVOKABLE static QString newMediaIconName(Enums::MessageType hint);
Q_INVOKABLE static QString label(Enums::MessageType hint);
Q_INVOKABLE static QString iconName(Enums::MessageType hint);
Q_INVOKABLE static QString filterName(Enums::MessageType hint);
Q_INVOKABLE static QString filter(Enums::MessageType hint);
Q_INVOKABLE static QString namedFilter(Enums::MessageType hint);
Q_INVOKABLE static QList<QMimeType> mimeTypes(Enums::MessageType hint);
Q_INVOKABLE static Enums::MessageType messageType(const QString &filePath);
Q_INVOKABLE static Enums::MessageType messageType(const QUrl &url);
Q_INVOKABLE static Enums::MessageType messageType(const QMimeType &mimeType);
// Qml sucks... :)
Q_INVOKABLE inline static QString mimeTypeName(const QString &filePath)
{ return mimeType(filePath).name(); }
Q_INVOKABLE inline static QString mimeTypeName(const QUrl &url)
{ return mimeType(url).name(); }
private:
static const QMimeDatabase s_mimeDB;
static const QList<QMimeType> s_allMimeTypes;