qtmobility/examples/flickrdemo/flickrdemo.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 23 Jun 2010 19:08:38 +0300
changeset 14 6fbed849b4f4
parent 11 06b8e2af4411
permissions -rw-r--r--
Revision: 201023 Kit: 2010125

/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:BSD$
** 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 Nokia Corporation and its Subsidiary(-ies) 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."
** $QT_END_LICENSE$
**
****************************************************************************/

#include "flickrdemo.h"
#include <QDomDocument>

#include <qnmeapositioninfosource.h>
#include <qgeopositioninfosource.h>
#include <qgeosatelliteinfosource.h>
#include <qgeopositioninfo.h>
#include <qgeosatelliteinfo.h>
#include <qnetworkconfigmanager.h>
#include <qnetworksession.h>

#include <QAction>
#include <QApplication>
#include <QDialogButtonBox>
#include <QDir>
#include <QFile>
#include <QLabel>
#include <QMenuBar>
#include <QMessageBox>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QProgressDialog>
#include <QPushButton>
#include <QTimer>
#include <QVBoxLayout>
#include <QWidget>

// static constant intialization

const QSize FlickrDemo::gridSize = QSize(52, 52);
const QSize FlickrDemo::thumbnailSize = QSize(50, 50);
const QSize FlickrDemo::imageSize = QSize(150, 150);
const QString FlickrDemo::apikey = QString("e36784df8a03fea04c22ed93318b291c");

FlickrDemo::FlickrDemo(QWidget* parent) :
        QMainWindow(parent),
        m_logfileInUse(false),
        m_session(0),
        m_pictureListReply(0),
        m_thumbnailReply(0),
        m_pictureReply(0),
        m_pages(0),
        m_page(1),
        m_satellitesInView(0),
        m_satellitesUsed(0),
        m_latitude(-1000),
        m_longitude(-1000),
        m_downloadPictureList(true),
        m_shuttingDown(false)
{
    resize(252, 344);

    locationLabel = new QLabel(tr("Lat: Long:"));
    satellitesLabel = new QLabel(tr("Using 0 of 0 satellites"));
    listWidget = new XQListWidget();
    downloadButton = new QPushButton(tr("Download Picture List"));

    QVBoxLayout *verticalLayout = new QVBoxLayout();
    verticalLayout->addWidget(locationLabel);
    verticalLayout->addWidget(satellitesLabel);
    verticalLayout->addWidget(listWidget);
    verticalLayout->addWidget(downloadButton);

    QWidget *centralWidget = new QWidget;
    centralWidget->setLayout(verticalLayout);
    setCentralWidget(centralWidget);

    createMenus();
    listWidget->setGridSize(gridSize);
    listWidget->setIconSize(thumbnailSize);
    m_progressDialog = new QProgressDialog(this);
    m_progressDialog->setModal(true);
    connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(cancelDownload()));

    setWindowTitle(tr("Flickr Demo"));

    connect(downloadButton, SIGNAL(clicked()), this, SLOT(downloadButtonClicked()));

    // QGeoPositionInfoSource
    m_location = QGeoPositionInfoSource::createDefaultSource(this);

    if (m_location == 0) {
        QNmeaPositionInfoSource *nmeaLocation = new QNmeaPositionInfoSource(QNmeaPositionInfoSource::SimulationMode, this);
        QFile *logFile = new QFile(QApplication::applicationDirPath()
                                   + QDir::separator() + "nmealog.txt", this);
        nmeaLocation->setDevice(logFile);
        m_location = nmeaLocation;
        m_logfileInUse = true;
    }

    // Listen gps position changes
    connect(m_location, SIGNAL(positionUpdated(QGeoPositionInfo)),
            this, SLOT(positionUpdated(QGeoPositionInfo)));

    // QGeoSatelliteInfoSource
    m_satellite = QGeoSatelliteInfoSource::createDefaultSource(this);
    // Listen satellite status changes
    if (m_satellite != 0) {
        connect(m_satellite, SIGNAL(satellitesInViewUpdated(const QList<QGeoSatelliteInfo>&)),
                this, SLOT(satellitesInViewUpdated(const QList<QGeoSatelliteInfo>&)));
        connect(m_satellite, SIGNAL(satellitesInUseUpdated(const QList<QGeoSatelliteInfo>&)),
                this, SLOT(satellitesInUseUpdated(const QList<QGeoSatelliteInfo>&)));
    }

    m_nam = new QNetworkAccessManager(this);

    QTimer::singleShot(0, this, SLOT(delayedInit()));
}

FlickrDemo::~FlickrDemo()
{
    m_shuttingDown = true;

    m_location->stopUpdates();
    if (m_satellite)
        m_satellite->stopUpdates();

    if (m_pictureListReply) {
        m_pictureListReply->abort();
        delete m_pictureListReply;
    }
    if (m_thumbnailReply) {
        m_thumbnailReply->abort();
        delete m_thumbnailReply;
    }
    if (m_pictureReply) {
        m_pictureReply->abort();
        delete m_pictureReply;
    }

    if (m_session)
        m_session->close();
}

void FlickrDemo::delayedInit()
{
    if (m_logfileInUse) {
        QMessageBox::information(this, tr("Flickr Demo"),
                                 tr("No GPS support detected, using GPS data from a sample log file instead."));
    }

    QTM_PREPEND_NAMESPACE(QNetworkConfigurationManager) manager;
    const bool canStartIAP = (manager.capabilities()
                              & QTM_PREPEND_NAMESPACE(QNetworkConfigurationManager)::CanStartAndStopInterfaces);
    QTM_PREPEND_NAMESPACE(QNetworkConfiguration) cfg = manager.defaultConfiguration();
    if (!cfg.isValid() || (!canStartIAP && cfg.state() != QTM_PREPEND_NAMESPACE(QNetworkConfiguration)::Active)) {
        QMessageBox::information(this, tr("Flickr Demo"), tr("Available Access Points not found."));
        return;
    }

    m_session = new QNetworkSession(cfg, this);
    m_connectivityHelper = new ConnectivityHelper(m_session, this);
    connect(m_session, SIGNAL(opened()), this, SLOT(networkSessionOpened()));
    connect(m_connectivityHelper, SIGNAL(networkingCancelled()), qApp, SLOT(quit()));

    m_session->open();
}

void FlickrDemo::networkSessionOpened()
{
    // Start listening GPS position updates
    m_location->startUpdates();

    // Start listening GPS satellite status changes
    if (m_satellite != 0) {
        m_satellite->startUpdates();
    } else {
        satellitesLabel->setText(tr("GPS not detected, replaying coordinates from sample log file."));
    }
}

void FlickrDemo::createMenus()
{
    m_downloadAct = new QAction(tr("Download Picture List"), this);
#if defined(Q_OS_SYMBIAN) || defined(Q_OS_WINCE)
    menuBar()->addAction(m_downloadAct);
#endif
    connect(m_downloadAct, SIGNAL(triggered()), this, SLOT(downloadButtonClicked()));

    QAction* exitAct = new QAction(tr("Exit"), this);
#if defined(Q_OS_SYMBIAN) || defined(Q_OS_WINCE)
    menuBar()->addAction(exitAct);
#endif
    connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
}

void FlickrDemo::positionUpdated(const QGeoPositionInfo &gpsPos)
{
    if (gpsPos.isValid()) {
        QGeoCoordinate coord = gpsPos.coordinate();
        m_latitude = coord.latitude();
        m_longitude = coord.longitude();

        locationLabel->setText(tr("Lat: %1 Long: %2").arg(QString::number(m_latitude),
                               QString::number(m_longitude)));
    }
}

void FlickrDemo::satellitesInViewUpdated(const QList<QGeoSatelliteInfo> &satellites)
{
    m_satellitesInView = satellites.count();
    viewSatelliteInfo();
}
void FlickrDemo::satellitesInUseUpdated(const QList<QGeoSatelliteInfo> &satellites)
{
    m_satellitesUsed = satellites.count();
    viewSatelliteInfo();
}

void FlickrDemo::viewSatelliteInfo()
{
    if (m_satellite != 0) {
        satellitesLabel->setText(tr("Using %1 of %2 satellites").arg(QString::number(m_satellitesUsed), QString::number(m_satellitesInView)));
    } else {
        satellitesLabel->setText(tr("GPS not detected, replaying coordinates from sample log file."));
    }
}

void FlickrDemo::downloadButtonClicked()
{
    if (m_downloadPictureList) {
        downloadFlickerPictureList();
    } else {
        downloadPictureFromFlickr();
    }
}

void FlickrDemo::downloadFlickerPictureList()
{
    if (m_latitude == -1000 || m_longitude == -1000) {
        if (QMessageBox::question(this,
                                  tr("Flickr Demo"),
                                  tr("No satellite connection.\n""Use hard coded coordinates?"),
                                  QMessageBox::Ok | QMessageBox::Cancel,
                                  QMessageBox::Cancel)
                == QMessageBox::Cancel) {
            return;
        }
        // If GPS signal is not detected, Tampere area is used as default location
        m_latitude = 61.4500;
        m_longitude = 23.8502;
    }

    // Set IAP name
    satellitesLabel->setText(tr("Access Point: %1").arg(m_session->configuration().name()));
    locationLabel->setText(tr("Lat: %1 Long: %2").arg(QString::number(m_latitude),
                           QString::number(m_longitude)));

    QString
    urlstring =
        "http://api.flickr.com/services/rest/?min_taken_date=2000-01-01+0:00:00&extras=date_taken&method=flickr.photos.search&per_page=30&sort=date-taken-desc";
    urlstring.append("&api_key=");
    urlstring.append(apikey);
    urlstring.append("&lat=");
    urlstring.append(QString::number(m_latitude));
    urlstring.append("&lon=");
    urlstring.append(QString::number(m_longitude));
    urlstring.append("&page=");
    urlstring.append(QString::number(m_page));

    QUrl url(urlstring);

    QNetworkRequest req(url);
    m_pictureListReply = m_nam->get(req);
    connect(m_pictureListReply,
            SIGNAL(downloadProgress(qint64, qint64)),
            this,
            SLOT(pictureListDownloadProgress(qint64, qint64)));
    connect(m_pictureListReply,
            SIGNAL(finished()),
            this,
            SLOT(pictureListFinished()));
    connect(m_pictureListReply,
            SIGNAL(error(QNetworkReply::NetworkError)),
            this,
            SLOT(pictureListError(QNetworkReply::NetworkError)));

    m_progressDialog->setWindowTitle(tr("FlickrDemo"));
    m_progressDialog->setLabelText(tr("Downloading\nPicture List."));
    m_progressDialog->setMaximum(10);
    m_progressDialog->setValue(0);
    m_progressDialog->show();
}

bool FlickrDemo::parsePictureList(QString xmlString)
{
    QString errorStr;
    int errorLine;
    int errorColumn;

    QDomDocument domDocument;
    if (!domDocument.setContent(xmlString, true, &errorStr, &errorLine, &errorColumn)) {
        QMessageBox::information(window(),
                                 tr("Flickr Demo"),
                                 tr("XML Parse error at line %1, column %2:\n%3").arg(errorLine).arg(errorColumn).arg(errorStr));
        return false;
    }
    QDomElement root = domDocument.documentElement();

    if (root.tagName() != "rsp") {
        QMessageBox::information(window(), tr("Flickr Demo"), tr("Invalid XML format."));
        return false;
    }
    if (root.attribute("stat") != "ok") {
        QMessageBox::information(window(), tr("Flickr Demo"), tr("Query failed."));
        return false;
    }

    listWidget->clear();
    QListWidgetItem* listItem;
    QDomElement photosElement = root.firstChildElement("photos");
    if (!photosElement.isNull()) {
        m_pages = (photosElement.attribute("pages")).toInt();
        m_page = (photosElement.attribute("page")).toInt();

        QDomElement child = photosElement.firstChildElement("photo");
        while (!child.isNull()) {
            QString url = "http://farm";
            url.append(child.attribute("farm"));
            url.append(".static.flickr.com/");
            url.append(child.attribute("server"));
            url.append("/");
            url.append(child.attribute("id"));
            url.append("_");
            url.append(child.attribute("secret"));

            m_names << url;

            listItem = new QListWidgetItem(child.attribute("title"));
            QImage image(thumbnailSize, QImage::Format_Mono);
            image.fill(1);
            listItem->setIcon(QPixmap::fromImage(image));
            listItem->setData(Qt::UserRole, url);
            listWidget->addItem(listItem);

            child = child.nextSiblingElement("photo");
        }
    } else {
        return false;
    }
    m_nameCounter = -1;
    downloadNextThumbnail();

    return true;
}

void FlickrDemo::downloadPictureFromFlickr()
{
    QListWidgetItem* item = listWidget->currentItem();
    if (!item) {
        return;
    }
    QString pictureUrl = item->data(Qt::UserRole).toString();
    pictureUrl.append("_m.jpg");

    QUrl url(pictureUrl);

    QNetworkRequest req(url);
    m_pictureReply = m_nam->get(req);
    connect(m_pictureReply,
            SIGNAL(downloadProgress(qint64, qint64)),
            this,
            SLOT(pictureDownloadProgress(qint64, qint64)));
    connect(m_pictureReply,
            SIGNAL(finished()),
            this,
            SLOT(pictureFinished()));
    connect(m_pictureReply,
            SIGNAL(error(QNetworkReply::NetworkError)),
            this,
            SLOT(pictureError(QNetworkReply::NetworkError)));

    m_progressDialog->setWindowTitle(tr("Flickr Demo"));
    m_progressDialog->setLabelText(tr("Downloading:\n%1").arg(pictureUrl));
    m_progressDialog->setMaximum(10);
    m_progressDialog->setValue(0);
    m_progressDialog->show();

    downloadButton->setEnabled(false);
}

void FlickrDemo::cancelDownload()
{
    if (m_pictureListReply) {
        m_pictureListReply->abort();
        delete m_pictureListReply;
        m_pictureListReply = 0;
    }

    if (m_pictureReply) {
        m_pictureReply->abort();
        delete m_pictureReply;
        m_pictureReply = 0;
    }

    downloadButton->setEnabled(true);
}

void FlickrDemo::pictureListDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
    m_progressDialog->setMaximum(bytesTotal);
    m_progressDialog->setValue(bytesReceived);
}

void FlickrDemo::pictureListFinished()
{
    m_progressDialog->hide();
    if (parsePictureList(QString::fromUtf8(m_pictureListReply->readAll()))) {
        m_downloadPictureList = false;
        downloadButton->setText(tr("Download Selected Picture"));
        m_downloadAct->setText(tr("Download Selected Picture"));
    }

    downloadButton->setEnabled(true);

    QTimer::singleShot(0, this, SLOT(clearPictureListRequest()));
}

void FlickrDemo::pictureListError(QNetworkReply::NetworkError /*code*/)
{
    if (m_shuttingDown)
        return;

    m_progressDialog->hide();
    QMessageBox::information(this,
                             tr("Flickr Demo"),
                             tr("Error downloading picture list: %1.").arg(m_pictureListReply->errorString()));

    QTimer::singleShot(0, this, SLOT(clearPictureListRequest()));
}

void FlickrDemo::clearPictureListRequest()
{
    delete m_pictureListReply;
    m_pictureListReply = 0;
}

void FlickrDemo::thumbnailFinished()
{
    QByteArray picture = m_thumbnailReply->readAll();
    if (!picture.isNull() && picture.size() > 0) {
        QListWidgetItem* item = listWidget->item(m_nameCounter);
        QImage image;
        if (image.loadFromData(picture, "jpg")) {
            item->setIcon(QPixmap::fromImage(
                              image.scaled(thumbnailSize,
                                           Qt::KeepAspectRatio,
                                           Qt::SmoothTransformation)));
            listWidget->update();
        }
    }
    downloadNextThumbnail();
}

void FlickrDemo::thumbnailError(QNetworkReply::NetworkError /*code*/)
{
    if (m_shuttingDown)
        return;

    QMessageBox::information(this,
                             tr("Flickr Demo"),
                             tr("Error downloading thumbnails: %1.").arg(m_thumbnailReply->errorString()));

    QTimer::singleShot(0, this, SLOT(clearThumbnailRequest()));
}

void FlickrDemo::clearThumbnailRequest()
{
    delete m_thumbnailReply;
    m_thumbnailReply = 0;
}

void FlickrDemo::pictureDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
    m_progressDialog->setMaximum(bytesTotal);
    m_progressDialog->setValue(bytesReceived);
}

void FlickrDemo::pictureFinished()
{
    m_progressDialog->hide();
    downloadButton->setEnabled(true);

    QByteArray picture = m_pictureReply->readAll();

    if (picture.isNull() || picture.size() <= 0)
        return;

    QImage image;
    if (!image.loadFromData(picture, "jpg"))
        return;

    QPixmap pixmap = QPixmap::fromImage(
                         image.scaled(imageSize,
                                      Qt::KeepAspectRatio,
                                      Qt::SmoothTransformation));

    displayImage(pixmap);

    QTimer::singleShot(0, this, SLOT(clearPictureRequest()));
}

void FlickrDemo::pictureError(QNetworkReply::NetworkError /*code*/)
{
    if (m_shuttingDown)
        return;

    m_progressDialog->hide();
    QMessageBox::information(this,
                             tr("Flickr Demo"),
                             tr("Error downloading picture: %1.").arg(m_pictureReply->errorString()));

    downloadButton->setEnabled(true);

    QTimer::singleShot(0, this, SLOT(clearPictureRequest()));
}

void FlickrDemo::clearPictureRequest()
{
    delete m_pictureReply;
    m_pictureReply = 0;
}

void FlickrDemo::displayImage(const QPixmap &pixmap)
{
    PictureDialog dialog(pixmap, listWidget->currentItem()->text(), this);
#if defined(Q_OS_SYMBIAN) || defined (Q_OS_WINCE)
    dialog.showMaximized();
#endif
    dialog.exec();
}

void FlickrDemo::downloadNextThumbnail()
{
    m_nameCounter++;
    if (m_nameCounter < m_names.count()) {
        QString pictureUrl = m_names[m_nameCounter];
        pictureUrl.append("_s.jpg");
        QUrl url(pictureUrl);

        QNetworkRequest req(url);
        m_thumbnailReply = m_nam->get(req);
        connect(m_thumbnailReply,
                SIGNAL(finished()),
                this,
                SLOT(thumbnailFinished()));
        connect(m_thumbnailReply,
                SIGNAL(error(QNetworkReply::NetworkError)),
                this,
                SLOT(thumbnailError(QNetworkReply::NetworkError)));
    } else {
        QTimer::singleShot(0, this, SLOT(clearThumbnailRequest()));
    }
}

PictureDialog::PictureDialog(const QPixmap& pixmap, const QString& pictureName, QWidget* parent) :
        QDialog(parent)
{
    resize(252, 361);
    QVBoxLayout *verticalLayout = new QVBoxLayout();
    verticalLayout->setSpacing(6);
    verticalLayout->setContentsMargins(11, 11, 11, 11);

    label = new QLabel();
    label->setText(tr("Downloaded:\n%1").arg(pictureName));

    QSizePolicy sizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
    sizePolicy.setHorizontalStretch(0);
    sizePolicy.setVerticalStretch(0);
    sizePolicy.setHeightForWidth(label->sizePolicy().hasHeightForWidth());
    label->setSizePolicy(sizePolicy);

    verticalLayout->addWidget(label);

    imageLabel = new QLabel();
    imageLabel->setPixmap(pixmap);

    verticalLayout->addWidget(imageLabel);

    buttonBox = new QDialogButtonBox();
    buttonBox->setStandardButtons(QDialogButtonBox::Close);
    connect(buttonBox, SIGNAL(rejected()), this, SLOT(accept()));

    verticalLayout->addWidget(buttonBox);

    setLayout(verticalLayout);

    setWindowTitle(tr("Flickr Demo"));
}