Skip to content
This repository has been archived by the owner on Sep 4, 2021. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
[libcontacts] Move photo handling logic into libcontacts
  • Loading branch information
matthewvogt committed Sep 9, 2013
1 parent ddaa210 commit c98b29a
Show file tree
Hide file tree
Showing 3 changed files with 255 additions and 3 deletions.
169 changes: 169 additions & 0 deletions src/seasidephotohandler.cpp
@@ -0,0 +1,169 @@
/*
* Copyright (C) 2013 Jolla Ltd.
* Contact: Chris Adams <chris.adams@jollamobile.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* "Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor Jolla Ltd nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
*/

#include "seasidephotohandler.h"

#ifndef QT_VERSION_5
#include <QDesktopServices>
#include <QContactThumbnail>
#endif
#include <QContactAvatar>
#include <QCryptographicHash>
#include <QDir>
#include <QImage>

SeasidePhotoHandler::SeasidePhotoHandler()
{
}

SeasidePhotoHandler::~SeasidePhotoHandler()
{
}

void SeasidePhotoHandler::documentProcessed(const QVersitDocument &, QContact *)
{
// do nothing, have no state to clean.
}

void SeasidePhotoHandler::propertyProcessed(const QVersitDocument &, const QVersitProperty &property, const QContact &, bool *alreadyProcessed, QList<QContactDetail> * updatedDetails)
{
// if the property is a PHOTO property, store the data to disk
// and then create an avatar detail which points to it.
if (property.name().toLower() != QLatin1String("photo"))
return;

#ifndef QT_VERSION_5
// The Qt4 / QtMobility version has QContactThumbnail support.
// We need to remove any such thumbnail detail from the output,
// as some backends (such as qtcontacts-sqlite) do not support
// that detail type.
for (int i = 0; i < updatedDetails->size(); ++i) {
if (updatedDetails->at(i).definitionName() == QContactThumbnail::DefinitionName) {
updatedDetails->removeAt(i);
--i;
}
}
#endif

// The data might be either a URL, a file path, or encoded image data
bool encodedData = false;
foreach (const QString &parameter, property.parameters().keys()) {
// If there is an 'encoding=' or 'type=' parameter, assume encoded data
if ((parameter.compare(QString::fromLatin1("encoding"), Qt::CaseInsensitive) == 0) ||
(parameter.compare(QString::fromLatin1("type"), Qt::CaseInsensitive) == 0)) {
encodedData = true;
break;
}
}

QUrl url;

if (!encodedData) {
// Assume the data is a URL
QString path(property.variantValue().toString());
url = QUrl(path);

// Treat remote URL as a true URL; local file should be copied into our cache
if (url.isValid() && !url.scheme().isEmpty() && !url.isLocalFile()) {
QContactAvatar newAvatar;
newAvatar.setImageUrl(url);
updatedDetails->append(newAvatar);

// we have successfully processed this PHOTO property.
*alreadyProcessed = true;
return;
} else {
// See if we can resolve the data as a local file
url = QUrl::fromLocalFile(path);
}
}

QByteArray photoData;

if (!encodedData) {
QFile file(url.toLocalFile());
if (!file.open(QIODevice::ReadOnly)) {
qWarning() << "Unable to process photo data as file:" << property.variantValue().toString();
return;
} else {
photoData = file.readAll();
}
} else {
photoData = property.variantValue().toByteArray();
}

if (photoData.isEmpty()) {
qWarning() << "Failed to extract avatar data from vCard PHOTO property";
return;
}

QImage img;
bool loaded = img.loadFromData(photoData);
if (!loaded) {
qWarning() << "Failed to load avatar image from vCard PHOTO data";
return;
}

// We will save the avatar image to disk in the system's data location
// Since we're importing user data, it should not require privileged access
const QString subdirectory(QString::fromLatin1(".local/share/system/Contacts/avatars"));
const QString photoDirPath(QDir::home().filePath(subdirectory));

// create the photo file dir if it doesn't exist.
QDir photoDir;
if (!photoDir.mkpath(photoDirPath)) {
qWarning() << "Failed to create avatar image directory when loading avatar image from vCard PHOTO data";
return;
}

// construct the filename of the new avatar image.
QString photoFilePath = QString::fromLatin1(QCryptographicHash::hash(photoData, QCryptographicHash::Md5).toHex());
photoFilePath = photoDirPath + QDir::separator() + photoFilePath + QString::fromLatin1(".jpg");

// save the file to disk
bool saved = img.save(photoFilePath);
if (!saved) {
qWarning() << "Failed to save avatar image from vCard PHOTO data to" << photoFilePath;
return;
}

qWarning() << "Successfully saved avatar image from vCard PHOTO data to" << photoFilePath;

// save the avatar detail - TODO: mark the avatar as "owned by the contact" (remove on delete)
QContactAvatar newAvatar;
newAvatar.setImageUrl(QUrl::fromLocalFile(photoFilePath));
updatedDetails->append(newAvatar);

// we have successfully processed this PHOTO property.
*alreadyProcessed = true;
}

80 changes: 80 additions & 0 deletions src/seasidephotohandler.h
@@ -0,0 +1,80 @@
/*
* Copyright (C) 2013 Jolla Ltd.
* Contact: Chris Adams <chris.adams@jollamobile.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* "Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor Jolla Ltd nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
*/

#ifndef PHOTOHANDLER_H
#define PHOTOHANDLER_H

#include "contactcacheexport.h"

#include <QString>
#include <QByteArray>

#include <QContact>

#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
# include <QVersitContactImporterPropertyHandlerV2>
#else
# include <QVersitContactImporterPropertyHandler>
#endif

#include <QVersitResourceHandler>
#include <QVersitDocument>
#include <QVersitProperty>

#ifdef USING_QTPIM
QTCONTACTS_USE_NAMESPACE
QTVERSIT_USE_NAMESPACE
#else
QTM_USE_NAMESPACE
#endif

/*
SeasidePhotoHandler
Some backends don't support saving PHOTO data directly.
Instead, the PHOTO data needs to be extracted, saved to
a file, and then the path to the file needs to be saved
to the backend as a contact avatar url detail.
*/
class CONTACTCACHE_EXPORT SeasidePhotoHandler : public QVersitContactImporterPropertyHandlerV2
{
public:
SeasidePhotoHandler();
~SeasidePhotoHandler();

// QVersitContactImporterPropertyHandlerV2
void documentProcessed(const QVersitDocument &, QContact *);
void propertyProcessed(const QVersitDocument &, const QVersitProperty &property,
const QContact &, bool *alreadyProcessed, QList<QContactDetail> * updatedDetails);
};

#endif // PHOTOHANDLER_H
9 changes: 6 additions & 3 deletions src/src.pro
Expand Up @@ -37,18 +37,21 @@ equals(QT_MAJOR_VERSION, 5) {
DEFINES += CONTACTCACHE_BUILD

SOURCES += \
$$PWD/seasidecache.cpp
$$PWD/seasidecache.cpp \
$$PWD/seasidephotohandler.cpp

HEADERS += \
$$PWD/contactcacheexport.h \
$$PWD/seasidecache.h \
$$PWD/synchronizelists.h \
$$PWD/seasidenamegrouper.h
$$PWD/seasidenamegrouper.h \
$$PWD/seasidephotohandler.h

headers.files = \
$$PWD/contactcacheexport.h \
$$PWD/seasidecache.h \
$$PWD/synchronizelists.h \
$$PWD/seasidenamegrouper.h
$$PWD/seasidenamegrouper.h \
$$PWD/seasidephotohandler.h
headers.path = $$PREFIX/include/$$TARGET
INSTALLS += headers

0 comments on commit c98b29a

Please sign in to comment.