qtinternetradio/ui/src/irviewmanager.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 06 Jul 2010 14:07:20 +0300
changeset 12 608f67c22514
parent 5 0930554dc389
child 14 896e9dbc5f19
permissions -rw-r--r--
Revision: 201025 Kit: 2010127

/*
* Copyright (c) 2009 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 <hbaction.h>
#include <QApplication>
#include <QTimer>
#include <HbApplication>
#include <HbActivityManager>
#include <xqserviceutil.h>


#include "irviewmanager.h"
#include "irapplication.h"
#include "irmainview.h"
#include "ircategoryview.h"
#include "irstationsview.h"
#include "irnowplayingview.h"
#include "irsearchchannelsview.h"
#include "irfavoritesview.h"
#include "irhistoryview.h"
#include "irsettingsview.h"
#include "iropenwebaddressview.h"
#include "irsonghistoryview.h"
#include "irtermsconsview.h"
#include "irplsview.h"
#include "irstationdetailsview.h"

const int KCrossLineWidth = 30; // pixel
const double KCrossLineMinLenth = 180.0; // pixel

const int KCrossLineTimeInterval = 1500; // ms
const int KExitTimeInterval = 800; // ms, used for showing cross Line

const int KCrossLineMinAngle = 15; // degree
const int KCrossLineMaxAngle = 75; // degree

static bool crossLineReady(const QLineF &aLine);
static bool crossLineIntersected(const QLineF &aLineA, const QLineF &aLineB);

static const QString KActivityMainView("InternetRadioMainView");
static const QString KActivityPlayingView("InternetRadioNowPlayingView");

enum CrossLineAngleType
{
    EPositiveAngle = 0,  // Line within 1,3 quadrant
    ENegativeAngle       // Line within 2,4 quadrant
};
static CrossLineAngleType crossLineAngleType(const QLineF &aLine);

/*
 * Description : constructor.
 *               add a softkey action to it in order to know that back buttion is touched.
 */
IRViewManager::IRViewManager() : iApplication(NULL),
                                 iCrossLineAReady(false),
                                 iCrossLineBReady(false),
                                 iCrossLineEnable(true),
                                 iCrossLineShowing(false),
                                 iCrossLineTimer(NULL),
                                 iExitTimer(NULL),
                                 iExiting(false)
{
    iBackAction = new HbAction(Hb::BackNaviAction, this);
    connect(iBackAction, SIGNAL(triggered()), this, SLOT(backToPreviousView()));
    
    iExitAction = new HbAction(Hb::QuitNaviAction, this);
    connect(iExitAction, SIGNAL(triggered()), this, SLOT(lower()));
    
    connect(this, SIGNAL(viewReady()), this, SLOT(handleViewReady()));
    connect(this, SIGNAL(currentViewChanged(HbView*)), this, SLOT(handleCurrentViewChanged(HbView*)));
    
    iCrossLineTimer = new QTimer(this);
    iExitTimer = new QTimer(this);
    iCrossLineTimer->setSingleShot(true);
    iExitTimer->setSingleShot(true);
    connect(iCrossLineTimer,SIGNAL(timeout()),this,SLOT(crossLineReset()));
    connect(iExitTimer,SIGNAL(timeout()),this,SLOT(exitTimeout()));     
}

/*
 * Description : destructor
 */
IRViewManager::~IRViewManager()
{
}

void IRViewManager::setApplication(IRApplication *aApplication)
{
    iApplication = aApplication;
}

/*
 * Description : get a pointer to a specified view. If the view is not created yet,
 *               view manager can create it and then return pointer to it.
 * Parameters  : aViewId : the view's id
 *               aCreateIfNotExist : whether or not create a view if it doesn't exist
 * Return      : pointer to the specified view.
 */
IRBaseView* IRViewManager::getView(TIRViewId aViewId, bool aCreateIfNotExist)
{
    int viewNumber = views().count();
    for (int i = 0; i < viewNumber; ++i)
    {
        IRBaseView* addedView = static_cast<IRBaseView*>(views().at(i));
        if (addedView && addedView->id() == aViewId)
        {
            return addedView;
        }
    }
    
    if (aCreateIfNotExist)
    {
        IRBaseView* newView = createView(iApplication, aViewId);
        addView(newView);
        return newView;
    }
    
    return NULL;
}

TIRViewId IRViewManager::getExitingView()
{
    if(0 == views().count())
    {
        return EIRView_InvalidId;
    }
    else
    {
        IRBaseView* lastView = static_cast<IRBaseView*>(currentView());
        if(lastView->testFlag(IRBaseView::EViewFlag_StickyViewEnabled))
        {
            return lastView->id();
        }
        
        if(iViewStack.empty())
        {
            return EIRView_InvalidId;
        }
        
        for(int i = iViewStack.size()-1; i >= 0 ; i--)
        {
            IRBaseView* lastView = iViewStack.at(i);
            if(lastView->testFlag(IRBaseView::EViewFlag_StickyViewEnabled))
            {
                return lastView->id();
            }
        }    
 
        return EIRView_InvalidId;
    }
}

/*
 * Description : Judge if a view is in the view stack.
 * Parameters  : aViewId : the view's id
 * Return      : true  : the view is in view stack
 *               false : the view is not in view stack
 */
bool IRViewManager::isViewInStack(TIRViewId aViewId) const
{
    int numberOfViewsInStack = iViewStack.count();

    for (int i = numberOfViewsInStack-1; i >=0 ; i--)
    {
        IRBaseView* view = iViewStack[i];
        if (view)
        {
            if (view->id() == aViewId)
            {
               return true;
            }
        }
    }
    
    return false;
}

/*
 * Description : Activate a view specified aViewId. The new view will be current view.
 *               Old current view will be deactivated and pushed into view stack if 
 *               aStackCurrent is true.
 * Parameters  : aViewId : the view's id.
 *               aStackCurrent : whether or not push current view into view stack
 * Return      : None
 */
void IRViewManager::activateView(TIRViewId aViewId, bool aPushCurrentView)
{    
    if (isViewInStack(aViewId))
    {
        backToView(aViewId);
        return;
    }
    
    IRBaseView *baseView = static_cast<IRBaseView*>(currentView());
    if (baseView && baseView->id() == aViewId)
    {
        baseView->updateView();
        return;
    }
    
    IRBaseView *view = getView(aViewId, true);
    
    if (view)
    {
        if (view->testFlag(IRBaseView::EViewFlag_ClearStackWhenActivate))
        {
            clearStack();
        }
        else
        {    if (aPushCurrentView)
             {
                 if (baseView && !baseView->testFlag(IRBaseView::EViewFlag_UnStackable))
                 {
                     iViewStack.push(baseView);
                 }
             }
        
             //deactivate current view
             if (baseView)
             {
                 baseView->handleCommand(EIR_ViewCommand_DEACTIVATE, EIR_ViewCommandReason_Hide);
             }
        }      
        switchToNextView(view);        
    }
}

/*
 * Description : Activate a view specified by aView. The new view will be current view.
 *               Old current view will be deactivated and pushed into view stack if 
 *               aStackCurrent is true.
 * Parameters  : aView : pointer to the view to be activated.
 *               aStackCurrent : whether or not push current view into view stack
 */
void IRViewManager::activateView(IRBaseView *aView, bool aPushCurrentView)
{
    if (aView == NULL)
    {
        return;
    }
    
    activateView(aView->id(), aPushCurrentView);
}

/*
 * Description : back view stack until a view whose id is aViewId.
 *               The view will become current view.
 * Parameters  : aViewId : the view's id
 */
void IRViewManager::backToView(TIRViewId aViewId)
{
    if (aViewId == currentViewId())
    {
        return;
    }
    
    //step 1 : back current view
    IRBaseView *topView = static_cast<IRBaseView*>(currentView());
    if (topView)
    {
        topView->handleCommand(EIR_ViewCommand_DEACTIVATE, EIR_ViewCommandReason_Back);
    }
    
    //step 2 : back the views in view stack
    while (!iViewStack.isEmpty())
    {
        topView = iViewStack.top();
        if (topView->id() == aViewId)
        {
            break;
        }
        
        topView->handleCommand(EIR_ViewCommand_DEACTIVATE, EIR_ViewCommandReason_Back);
        iViewStack.pop();
    }
    
    //step 3 : we back to the view or the view is not in view stack. Activate it.
    if (!iViewStack.isEmpty())
    {
        topView = iViewStack.pop();
        Q_ASSERT(topView->id() == aViewId);   
        switchToNextView(topView);
    }
    else
    {
        //backToView(id) is called when view is in stack, it's impossible to get here
        Q_ASSERT(false);
    }
}

void IRViewManager::switchToNextView(IRBaseView *aView)
{
    aView->handleCommand(EIR_ViewCommand_TOBEACTIVATED, EIR_ViewCommandReason_Show);
    IRBaseView *viewToHide = static_cast<IRBaseView*>(currentView());
    if( viewToHide )
    {
        if( (EIRView_StationDetailsView == aView->id() && EIRView_PlayingView == viewToHide->id())
          ||(EIRView_StationDetailsView == viewToHide->id() && EIRView_PlayingView == aView->id())
          )
        {
            setCurrentView(aView,true,Hb::ViewSwitchUseAltEvent); 
        }
        else
        {
            setCurrentView(aView,true,Hb::ViewSwitchUseNormalAnim);
        }        
    }
    else
    {
        setCurrentView(aView,true,Hb::ViewSwitchUseNormalAnim);
    }        
}

/*
 * Description : return the current view's id.
 * Return      : current view's id. 
 */
TIRViewId IRViewManager::currentViewId() const
{
    IRBaseView *topView = static_cast<IRBaseView*>(currentView());
    if (topView)
    {
        return topView->id();
    }
    
    return EIRView_InvalidId;
}

/*
 * Description : push a view into the view stack by id *               
 * Parameters  : aEvent : see the definition of TIRSystemEventType
 * Return      : void :  there is no return value for the function
 *                
 */
void IRViewManager::pushViewById(TIRViewId aViewId)
{
    if (isViewInStack(aViewId))
    {     
        return;
    }
    
    IRBaseView *curView = getView(aViewId, true);
    Q_ASSERT(curView);
    iViewStack.push(curView);
}

void IRViewManager::saveScreenShot()
{
    //if capture screen shot immediately for first view, can't get correct result
    //use timer as a temp solution, will raise error to Orbit
    QTimer::singleShot(200, this, SLOT(handleSaveScreenShot()));
}

void IRViewManager::saveActivity()
{
    HbActivityManager *activityManager = qobject_cast<HbApplication*>(qApp)->activityManager();
    
    //for embedded applications, don't publish activity. If backup activity is available, need to restore
    if (XQServiceUtil::isEmbedded())
    {
        if (!iActivityBackup.activityId.isEmpty())
        {
            QVariantHash metadata;
            metadata.insert("screenshot", iActivityBackup.screenShot);
            
            QByteArray serializedActivity;
            QDataStream stream(&serializedActivity, QIODevice::WriteOnly | QIODevice::Append);
            stream<<iActivityBackup.viewId;
            activityManager->addActivity(iActivityBackup.activityId, serializedActivity, metadata);
        }
        return;
    }
    
    removeActivity();
    
    
    QVariantHash metadata;
        
    QByteArray serializedActivity;
    QDataStream stream(&serializedActivity, QIODevice::WriteOnly | QIODevice::Append);
    
    TIRViewId viewId = getExitingView();
    switch (viewId)
    {
    case EIRView_MainView:
    case EIRView_FavoritesView:
    case EIRView_CategoryView:
        stream<<viewId;
        metadata.insert("screenshot", iScreenShots[viewId]);
        activityManager->addActivity(KActivityMainView, serializedActivity, metadata);
        break;
        
    case EIRView_PlayingView:
        stream<<viewId;
        metadata.insert("screenshot", iScreenShots[viewId]);
        activityManager->addActivity(KActivityPlayingView, serializedActivity, metadata);
        break;
        
    default:
        break;
    }
}

void IRViewManager::removeActivity()
{
    if (XQServiceUtil::isEmbedded())
    {
        backupActivity();
    }
    
    HbActivityManager *activityManager = qobject_cast<HbApplication*>(qApp)->activityManager();
    activityManager->removeActivity(KActivityMainView);
    activityManager->removeActivity(KActivityPlayingView);
}

void IRViewManager::backupActivity()
{
    HbApplication *hbApp = qobject_cast<HbApplication*>(qApp);
    HbActivityManager *activityManager = hbApp->activityManager();
    QList<QVariantHash> params = activityManager->activities();
    TInt count = params.count();
    if (count > 0)
    {
        QVariantHash activity = params[0];        
        QString path = activity.value(activity.keys().at(2)).toString();
        iActivityBackup.screenShot = QPixmap(path);
        iActivityBackup.activityId = activity.value(activity.keys().at(1)).toString();
        
        QByteArray serializedModel = activityManager->activityData(iActivityBackup.activityId).toByteArray();
        QDataStream stream(&serializedModel, QIODevice::ReadOnly);
        int id = 0;
        stream>>id;
        iActivityBackup.viewId = TIRViewId(id);
    }
}

//                                     slot functions

/*
 * Description : slot function for softkey action.
 *               Bring user to a previous view. If view stack is empty, quit application.
 */
void IRViewManager::backToPreviousView()
{
    if(iViewStack.isEmpty())
    {
        return;
    }

    IRBaseView *topView = static_cast<IRBaseView*>(currentView());
    IRBaseView *viewToShow = iViewStack.pop();
    
    if(viewToShow)
    {
        if(topView)
        {
            topView->handleCommand(EIR_ViewCommand_DEACTIVATE, EIR_ViewCommandReason_Back);
        }
        switchToNextView(viewToShow);       
    }
}

void IRViewManager::handleViewReady()
{    
    IRBaseView *topView = static_cast<IRBaseView*>(currentView());
    if (topView)
    {
        topView->handleCommand(EIR_ViewCommand_ACTIVATED, EIR_ViewCommandReason_Show); 
    }
    
    updateSoftkey();
}

/*
 * this function is needed because signal viewReady() has a bug. will be deleted once the bug is fixed
 */
void IRViewManager::handleCurrentViewChanged(HbView * /*aView*/)
{
    updateSoftkey();
}

/*
 * Description : create a view specified by aViewId. A view is created only when it's 
 *               first time requested
 * Parameters  : aApplication : pointer to ir application object
 *               aViewId      : the view's id
 * Return      : pointer to the created view
 */
IRBaseView* IRViewManager::createView(IRApplication* aApplication, TIRViewId aViewId)
{
    switch (aViewId)
    {
        case EIRView_MainView:
            return new IRMainView(aApplication, aViewId);
        
        case EIRView_CategoryView:
            return new IRCategoryView(aApplication, aViewId);
        
        case EIRView_StationsView:        
            return new IRStationsView(aApplication, aViewId);
        
        case EIRView_PlayingView:
            return new IRNowPlayingView(aApplication, aViewId);
            
        case EIRView_StationDetailsView:
            return new IRStationDetailsView(aApplication, aViewId);      
			
		case EIRView_SearchView:
            return new IRSearchChannelsView(aApplication, aViewId);
        
        case EIRView_FavoritesView:
            return new IRFavoritesView(aApplication, aViewId);
            
        case EIRView_HistoryView:
            return new IRHistoryView(aApplication, aViewId);
            
        case EIRView_SettingsView:
            return new IRSettingsView(aApplication, aViewId);
            
        case EIRView_OpenWebAddressView:
            return new IROpenWebAddressView(aApplication, aViewId);
        
        case EIRView_SongHistoryView:                        
            return new IRSongHistoryView(aApplication, aViewId);
            
        case EIRView_TermsConsView:                        
            return new IRTermsConsView(aApplication, aViewId);            
            
        case EIRView_PlsView:
            return new IRPlsView(aApplication, aViewId);
            
        default:
            break;
    }
    
    return NULL;
}

void IRViewManager::clearStack()
{
    IRBaseView *topView = NULL;
    
    //deactivate and back current view if it exists
    topView = static_cast<IRBaseView*>(currentView());
    if (topView)
    {
        topView->handleCommand(EIR_ViewCommand_DEACTIVATE, EIR_ViewCommandReason_Back);
    }
    
    while (!iViewStack.isEmpty())
    {
        topView = iViewStack.top();
        if (topView)
        {
            topView->handleCommand(EIR_ViewCommand_DEACTIVATE, EIR_ViewCommandReason_Back);
        }
        iViewStack.pop();
    }
}

void IRViewManager::updateSoftkey()
{
    IRBaseView *topView = static_cast<IRBaseView*>(currentView());
    if (topView)
    {
        if (topView->testFlag(IRBaseView::EViewFlag_ClearStackWhenActivate))
        {
            topView->setNavigationAction(iExitAction);
        }
        else
        {
            topView->setNavigationAction(iBackAction);
        }
    }
}

void IRViewManager::handleSaveScreenShot()
{
    if (!XQServiceUtil::isEmbedded())
    {
        TIRViewId id = currentViewId();
        iScreenShots[id] = QPixmap::grabWidget(this, rect());
    }
}

void IRViewManager::mousePressEvent(QMouseEvent *aEvent)
{
    if(iCrossLineEnable)
    {
        if(iCrossLineAReady)
        {
            iCrossLineB.setP1(aEvent->posF());
        }
        else
        {
            iCrossLineA.setP1(aEvent->posF());
        }
    }

    HbMainWindow::mousePressEvent(aEvent);
}

void IRViewManager::mouseReleaseEvent(QMouseEvent *aEvent)
{
    if(iCrossLineEnable)
    {    
        if(iCrossLineAReady)
        {
            iCrossLineTimer->stop();

            iCrossLineB.setP2(aEvent->posF());
            iCrossLineBReady = crossLineReady(iCrossLineB);
    
            if(iCrossLineBReady && readyToQuit())
            {   
                iCrossLineEnable = false;
                iCrossLineShowing = true;              
                viewport()->repaint(); 
                iExitTimer->start(KExitTimeInterval);       
            }
            else
            {
                crossLineReset();
            }
        }
        else
        {
            iCrossLineA.setP2(aEvent->posF());
            iCrossLineAReady = crossLineReady(iCrossLineA);
    
            if(iCrossLineAReady)
            {
                iCrossLineTimer->stop();
                iCrossLineTimer->start(KCrossLineTimeInterval);
            }
        }
    }
    
    HbMainWindow::mouseReleaseEvent(aEvent);
}

bool IRViewManager::readyToQuit()
{
    if(iCrossLineAReady && iCrossLineBReady)
    {
        return crossLineIntersected(iCrossLineA,iCrossLineB);
    }

    return false;
}

bool crossLineIntersected(const QLineF &aLineA, const QLineF &aLineB)
{
    if(crossLineAngleType(aLineA) != crossLineAngleType(aLineB))
    {
        return QLineF::BoundedIntersection == aLineA.intersect(aLineB,NULL);
    }

    return false;
}

void IRViewManager::paintEvent(QPaintEvent *aEvent)
{
    HbMainWindow::paintEvent(aEvent);

    if(iCrossLineShowing)
    {
        QPainter painter(viewport());
        painter.setPen(QPen(QColor(225,225,225,200),KCrossLineWidth));	
        painter.drawLine(iCrossLineA);
		painter.drawLine(iCrossLineB);
    }
}

void IRViewManager::crossLineReset()
{
    iCrossLineAReady = false;
    iCrossLineBReady = false;
    iCrossLineShowing = false;
}

void IRViewManager::exitTimeout()
{
    crossLineReset();
    viewport()->repaint();
#ifdef SUBTITLE_STR_BY_LOCID
    HbMessageBox::information(hbTrId("txt_common_info_exiting"), (QObject*)NULL, NULL);
#else
    HbMessageBox::information(hbTrId("Exiting..."), (QObject*)NULL, NULL);    
#endif
    qApp->quit();
    iExiting = true;
}

bool IRViewManager::isExiting() const
{
    return iExiting;
}



CrossLineAngleType crossLineAngleType(const QLineF &aLine)
{
    int linePos = aLine.angle() / 90;
    if( 0==linePos || 2==linePos )
    {
        return EPositiveAngle;
    }
    else
    {
        return ENegativeAngle;
    }
}

bool crossLineReady(const QLineF &aLine)
{
    if(aLine.length()> KCrossLineMinLenth)
    {
        int lineDegree = qRound(aLine.angle()) % 90;
        return (lineDegree<=KCrossLineMaxAngle) && (lineDegree>=KCrossLineMinAngle) ? true : false;
    }
    return false;
}