homescreenapp/stateplugins/hshomescreenstateplugin/src/hsselectbackgroundstate.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 14 May 2010 15:43:04 +0300
changeset 46 23b5d6a29cce
parent 39 4e8ebe173323
child 51 4785f57bf3d4
permissions -rw-r--r--
Revision: 201017 Kit: 201019

/*
* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  
*
*/

#include <QApplication>
#include <QDir>
#include <QFileInfo>
#include <QImageReader>
#include <QThread>
#include <QTimer>

#include <HbMainWindow>
#include <HbProgressDialog>
#include <HbView>

#include "hsselectbackgroundstate.h"
#include "hsscene.h"
#include "hsdomainmodeldatastructures.h"
#include "hswallpaper.h"
#include "hsdatabase.h"
#include "hshomescreenstatecommon.h"

#ifdef Q_OS_SYMBIAN
#include "hsimagefetcherclient.h"
#else
#include "xqaiwgetimageclient.h"
#include "xqaiwcommon.h"
#endif

const char hsLocTextId_ProgressDialog_WallpaperLoading[] = "txt_homescreen_dpopinfo_loading_wallpaper";

/*! 
    \class HsSelectBackgroundState
    \ingroup group_hshomescreenstateprovider
    \brief Implements imagefetcher event listening and handles those events.

    \sa StateMachine
*/

/*!
    Constructor.         
    \a parent Owner.
*/
HsSelectBackgroundState::HsSelectBackgroundState(QState *parent):
    QState(parent),
    mImageFetcher(0),
    mSourceView(0),
    mWallpaperImageReaderThread(0),
    mWallpaperImageReader(0),
    mProgressDialog(0),
    mImageProcessingState(NotRunning),
    mShowAnimation(false)
{
#ifdef Q_OS_SYMBIAN
    mImageFetcher = new HsImageFetcherClient(this);
#else    
    mImageFetcher = new XQAIWGetImageClient;
#endif

    connect(this, SIGNAL(entered()), SLOT(action_selectWallpaper()));
    connect(this, SIGNAL(exited()), SLOT(action_disconnectImageFetcher()));
    connect(this, SIGNAL(handleError()), SLOT(onHandleError()));
}

/*!
    Destructor.
    
*/
HsSelectBackgroundState::~HsSelectBackgroundState()
{
    delete mImageFetcher;
    delete mWallpaperImageReaderThread;
    delete mWallpaperImageReader;
}

/*!
    \internal
    Connects to image fetcher and launches "remote" ui from photos
    from which user can select background image
*/
void HsSelectBackgroundState::action_selectWallpaper()
{
    mSourceView = HsScene::mainWindow()->currentView();

    mImageProcessingState = NotRunning;
    mShowAnimation = false;

#ifdef Q_OS_SYMBIAN
    connect(mImageFetcher, SIGNAL(fetchCompleted(const QString&)),
            this, SLOT(fetchCompleted(const QString&)));
    connect(mImageFetcher, SIGNAL(fetchFailed(int, const QString&)),
            this, SLOT(fetchFailed(int, const QString&)));
    mImageFetcher->fetch();
#else
    connect(mImageFetcher, SIGNAL(fetchComplete(QStringList)),
            SLOT(onFetchComplete(QStringList)));
    connect(mImageFetcher, SIGNAL(fetchFailed(int)),
            SLOT(onFetchFailed(int)));
    mImageFetcher->fetch(QVariantMap(), SelectionSingle);
#endif
}

/*!
    \internal
    disconnects photos image fetcher services slots.
*/
void HsSelectBackgroundState::action_disconnectImageFetcher()
{
    HsScene::mainWindow()->setCurrentView(mSourceView);
    mImageFetcher->disconnect(this);
}

/*!
    \internal
    Called when user has selected an image 
*/
void HsSelectBackgroundState::onFetchComplete(QStringList imageStringList)
{
    if (mImageProcessingState == NotRunning) {
        // TODO: temporarily show animation immediately (~0.5 sec delay)
        onShowAnimation();
        // start counting time for possible animation
        // TODO: from UX the real response time
        // TODO: cannot use timer since UI does not respond during hardcore image processing
        //QTimer::singleShot(1000, this, SLOT(onShowAnimation()));
    }

    HsDatabase *db = HsDatabase::instance();
    Q_ASSERT(db);

    HsSceneData sceneData;
    if (!db->scene(sceneData)) {
        emit handleError();
        return;
    }

    // clean thread instances
    delete mWallpaperImageReader;
    delete mWallpaperImageReaderThread;
    mWallpaperImageReader = NULL;
    mWallpaperImageReaderThread = NULL;

    mWallpaperImageReaderThread = new QThread();
    mWallpaperImageReader = new HsWallpaperImageReader();

    // setup processing when image is fetched at first time
    if (mImageProcessingState == NotRunning) {
        // delete old wallpapers
        QFile::remove(sceneData.portraitWallpaper);
        QFile::remove(sceneData.landscapeWallpaper);
        
        QString wallpaperDir = HsWallpaper::wallpaperDirectory();            
        QDir dir(wallpaperDir);
        if (!dir.exists()) {
            dir.mkpath(wallpaperDir);
        }
        HsScene *scene = HsScene::instance();
        Qt::Orientation orientation = scene->orientation();
        // based on screen orientation select first image to process
        if (orientation == Qt::Vertical) {
            mImageProcessingState = ProcessPortraitAsFirst;
        } else {
            mImageProcessingState = ProcessLandscapeAsFirst;
        }
    }

    QRect targetRect;

    switch (mImageProcessingState) {
    case ProcessPortraitAsFirst:
    case ProcessPortraitAsSecond:
        targetRect = QRect(0, 0, (2 * 360) + HSBOUNDARYEFFECT, 640);
        break;
    case ProcessLandscapeAsFirst:
    case ProcessLandscapeAsSecond:
        targetRect = QRect(0, 0, (2 * 640) + HSBOUNDARYEFFECT, 360);
        break;
    default:
        emit handleError();
        return;        
    }

    // left empty to signal we want to use full size image as source
    QRect sourceRect;
    mWallpaperImageReader->setSourcePath(imageStringList.first());
    mWallpaperImageReader->setSourceRect(sourceRect);
    mWallpaperImageReader->setTargetRect(targetRect);
    mWallpaperImageReader->setCenterTarget(true);
    mWallpaperImageReader->moveToThread(mWallpaperImageReaderThread);

    mWallpaperImageReader->connect(mWallpaperImageReaderThread,
                      SIGNAL(started()),
                      SLOT(processImage()));
    
    connect(mWallpaperImageReader,
            SIGNAL(processingFinished()),
            SLOT(onImageProcessed()));
  
    // start image processing in thread
    mWallpaperImageReaderThread->start(QThread::IdlePriority);
}

/*!
    \internal
    Called when selection of background image fails  
*/
void HsSelectBackgroundState::onFetchFailed(int error)
{
    Q_UNUSED(error)
    emit handleError();
}

/*!
    \internal
    Called when image processing is finished in thread  
*/
void HsSelectBackgroundState::onImageProcessed()
{
    HsScene *scene = HsScene::instance();
    HsDatabase *db = HsDatabase::instance();
    Q_ASSERT(db);
    HsSceneData sceneData;
    if (!db->scene(sceneData)) {
        emit handleError();
        return;
    }
    QFileInfo fileInfo(mWallpaperImageReader->getSourcePath());
    QString fileExtension("");
    if (!fileInfo.suffix().isEmpty()) {
        fileExtension = fileInfo.suffix();
    }
    // set image path to sceneData
    QString path;
    if (mImageProcessingState == ProcessPortraitAsFirst ||
        mImageProcessingState == ProcessPortraitAsSecond) {
        path = HsWallpaper::wallpaperPath(Qt::Vertical, QString(), fileExtension);
        sceneData.portraitWallpaper = path;
    } else {
        path = HsWallpaper::wallpaperPath(Qt::Horizontal, QString(), fileExtension);
        sceneData.landscapeWallpaper = path;
    }
    // get image from renderer and save it
    QImage image = mWallpaperImageReader->getProcessedImage();
    image.save(path);
    if (!image.isNull()) {
        // update scenedata and set new image to background
        if (db->updateScene(sceneData)) {
            switch (mImageProcessingState) {
            case ProcessPortraitAsFirst:
                scene->wallpaper()->setPortraitImage(path, true);
                break;
            case ProcessPortraitAsSecond:
                // if orientation changed during first image settings
                if (HsScene::orientation() == Qt::Vertical) {
                    scene->wallpaper()->setPortraitImage(path, true);
                } else {
                    scene->wallpaper()->setPortraitImage(path);
                }
                break;
            case ProcessLandscapeAsFirst:
                scene->wallpaper()->setLandscapeImage(path, true);
                break;
            case ProcessLandscapeAsSecond:
                if (HsScene::orientation() == Qt::Horizontal) {
                    scene->wallpaper()->setLandscapeImage(path, true);
                } else {
                    scene->wallpaper()->setLandscapeImage(path);
                }
                break;
            default:
                emit handleError();
                break;
            }
        }
    } else {
        emit handleError();
        return;
    }

    switch (mImageProcessingState) {
    case ProcessPortraitAsFirst:
        mImageProcessingState = ProcessLandscapeAsSecond;
        if (mShowAnimation) {
            mProgressDialog->setProgressValue(2);
        }
        // process second orientation
        onFetchComplete(QStringList(mWallpaperImageReader->getSourcePath()));
        break;
    case ProcessLandscapeAsFirst:
        mImageProcessingState = ProcessPortraitAsSecond;
        if (mShowAnimation) {
            mProgressDialog->setProgressValue(2);
        }
        onFetchComplete(QStringList(mWallpaperImageReader->getSourcePath()));
        break;
    case ProcessPortraitAsSecond:
    case ProcessLandscapeAsSecond:
        mImageProcessingState = NotRunning;
        if (mShowAnimation) {
            mProgressDialog->setProgressValue(3);
        }
        // let user control again homescreen
        emit event_waitInput();
        break;
    default:
        emit handleError();
        break;
    }
}

/*!
    \internal
    Shows animation for longer processing
*/
void HsSelectBackgroundState::onShowAnimation()
{
    delete mProgressDialog;
    mProgressDialog = new HbProgressDialog(HbProgressDialog::ProgressDialog);
    // TODO: setPrimaryAction is deprecated, clearActions does the same but crashes when dialog closes, check orbit list
    mProgressDialog->setPrimaryAction(0);
    mProgressDialog->setIcon(HbIcon("note_info"));
    mProgressDialog->setText(hbTrId(hsLocTextId_ProgressDialog_WallpaperLoading));
    mProgressDialog->setMinimum(0);
    mProgressDialog->setMaximum(3);
    mProgressDialog->setBackgroundFaded(true);
    mProgressDialog->setAutoClose(true);
    mProgressDialog->setProgressValue(1);
    mProgressDialog->show();
    // TODO: temporary solution to minimize progress dialog resizing problem
    QApplication::processEvents();
    mShowAnimation = true;
}

/*!
    \internal
    Called when error occurs during image processing  
*/
void HsSelectBackgroundState::onHandleError()
{
    mImageProcessingState = Error;
    if (mShowAnimation) {
        mProgressDialog->close();
    }
    emit event_waitInput();
}

/*!
    \internal
    Called when user has selected an image on emulator or HW
*/
#ifdef Q_OS_SYMBIAN
void HsSelectBackgroundState::fetchCompleted(const QString& imageFileName)
{
    QStringList imageFileNameAsList(imageFileName);
    onFetchComplete(imageFileNameAsList);
}
#endif

/*!
    \internal
    Called when selection of background image fails on emulator or HW
*/
#ifdef Q_OS_SYMBIAN
void HsSelectBackgroundState::fetchFailed(int error, const QString& errorString)
{
    Q_UNUSED(errorString)
    onFetchFailed(error);        
}
#endif