musicwidgetplugin/src/musicwidget.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 04 Oct 2010 00:14:19 +0300
changeset 61 3cd404d31176
parent 58 ed94e1e8390e
permissions -rw-r--r--
Revision: 201037 Kit: 201039

/*
* 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:  Music player home screen widget
*
*/

// System includes
#include <qgraphicslinearlayout.h>
#include <hbpushbutton.h>
#include <hblabel.h>
#include <hbicon.h>
#include <hbmarqueeitem.h>
#include <hbanchorlayout.h>
#include <hbwidget.h>
#include <hbframeitem.h>
#include <hbframedrawer.h>
#include <hbtranslator.h>

// User includes
#include "musicwidget.h"
#include "musicwidgetdocumentloader.h"
#include "mptrace.h"
#include "mpenginefactory.h"
#include "mpalbumcoverwidget.h"
#include "mpplaybackdata.h"
#include "mpapplicationmonitor.h"

// Constants
/** Docml */
const QString MUSIC_WIDGET_DOCML = ":/docml/music_widget.docml";

/**  Play and Pause icons */
const QLatin1String ICON_PLAY ( "qtg_mono_play" );
const QLatin1String ICON_PAUSE ( "qtg_mono_pause" );

/** Album art icon */
const QLatin1String ICON_DEFAULT_ART ( "qtg_large_music_empty" );

/**  HsWidget normal background */
const QLatin1String WIDGET_BG_NORMAL ( "qtg_fr_hswidget_normal" );
/**  Text background */
const QLatin1String TEXT_BG_NORMAL ( "qtg_fr_multimedia_trans" );
/**  Separator : character and space between artist and song name */
const QLatin1String SEPARATOR_TEXT ( ": " );

/**  Infinite loop value for marquee */
const int MARQUEE_LOOP_FOREVER = -1;

/**  Now playing view with random play */
const QLatin1String SERVICEREQ_NOW_PLAYING_VIEW_SHUFFLE ( "appto://10207C62?activityname=MusicNowPlayingView&launchtype=standalone&shuffle=yes" );
/**  Main view */
const QLatin1String SERVICEREQ_MAIN_VIEW ( "appto://10207C62?activityname=MusicMainView&launchtype=standalone" );
/**  Now playing view */
const QLatin1String SERVICEREQ_NOW_PLAYING_VIEW ( "appto://10207C62?activityname=MusicNowPlayingView&launchtype=standalone" );

/**  DOCML object names */
const QLatin1String DOCML_CONTAINER_WIDGET ( "containerWidget" );
const QLatin1String DOCML_BG_ICON ( "widget_background_icon" );
const QLatin1String DOCML_ALBUM_ART ( "album_art_image" );
const QLatin1String DOCML_TEXT_BG ( "text_bg" );
const QLatin1String DOCML_SCROLLING_TEXT ( "scrolling_text" );
const QLatin1String DOCML_BUTTON_GROUP_CONTAINER ( "button_group_container" );
const QLatin1String DOCML_PREV_BUTTON ( "btn_previous" );
const QLatin1String DOCML_PLAY_BUTTON ( "btn_play" );
const QLatin1String DOCML_NEXT_BUTTON ( "btn_next" );

/*!
    \class MusicWidget
    \brief Provides simple Music Player playback controls from the Home Screen.
 */

/*!
 Constructs the Music Player home screen widget.
 */
MusicWidget::MusicWidget( QGraphicsItem* parent, Qt::WindowFlags flags ):
    HbWidget( parent, flags ),
    mSongDataBackground( 0 ),
    mMarqueeText( 0 ),    
    mPreviousPushButton( 0 ),
    mPlayPushButton( 0 ),    
    mNextPushButton( 0 ),
    mMusicPlayerUpdating( false ),
    mMusicPlayerBlocked( false ),
    mMusicPlayerRunning( false ),
    mAlbumArt( 0 ),
    mMpEngine( 0 ),
    mMpPlaybackData( 0 ),
    mMpApplicationMonitor( 0 )
{
    TX_LOG
    HbTranslator translator( QString( "musicwidgetplugin" ) );
}

/*!
 Destructor
 */
MusicWidget::~MusicWidget()
{
    TX_ENTRY
    if ( mMpEngine ) {
        MpEngineFactory::close();
    }

    delete mMpApplicationMonitor;
    TX_EXIT
}

/*!
 Creates UI.
 */
void MusicWidget::setupUi()
{
    TX_ENTRY
    
    // Use document loader to load the contents
    MusicWidgetDocumentLoader loader;
    bool ok = false;
    loader.load( MUSIC_WIDGET_DOCML, &ok );
    Q_ASSERT_X( ok, "music_widget", "invalid title docml file" );

    TX_LOG_ARGS( "loaded docml" )
    
    QGraphicsLinearLayout* mainLayout = new QGraphicsLinearLayout( Qt::Vertical, this );
    QGraphicsWidget* container = loader.findWidget( DOCML_CONTAINER_WIDGET );
    mainLayout->addItem( container );
    
    //Sets parent
    setLayout( mainLayout );

    // Load and set widget background
    HbLabel *bgWidget = qobject_cast<HbLabel*> ( loader.findWidget( DOCML_BG_ICON ) );
    HbFrameItem *frameItemWidgetBg = new HbFrameItem();
    frameItemWidgetBg->frameDrawer().setFrameGraphicsName( WIDGET_BG_NORMAL );
    frameItemWidgetBg->frameDrawer().setFrameType( HbFrameDrawer::NinePieces );
    frameItemWidgetBg->frameDrawer().setFillWholeRect( true );
    bgWidget->setBackgroundItem( frameItemWidgetBg );

    // Setup album art area
    QGraphicsWidget *tmpWidgetPtr;
    tmpWidgetPtr = loader.findWidget( DOCML_ALBUM_ART );
    mAlbumArt = qobject_cast<MpAlbumCoverWidget*>( tmpWidgetPtr );
    mAlbumArt->setEnabled( false );
    mAlbumArt->setDefaultIcon( HbIcon( ICON_DEFAULT_ART ) );
        
    // Set song data background
    mSongDataBackground = qobject_cast<HbLabel*> ( loader.findWidget( DOCML_TEXT_BG ) );
    HbFrameItem *frameItemSongBg = new HbFrameItem();
    frameItemSongBg->frameDrawer().setFrameGraphicsName( TEXT_BG_NORMAL );
    frameItemSongBg->frameDrawer().setFrameType( HbFrameDrawer::NinePieces );
    frameItemSongBg->frameDrawer().setFillWholeRect( true );
    mSongDataBackground->setBackgroundItem( frameItemSongBg );
    mSongDataBackground->hide();

    // Set scrolling song data
    mMarqueeText = qobject_cast<HbMarqueeItem*> ( loader.findWidget( DOCML_SCROLLING_TEXT ) );
    mMarqueeText->setLoopCount( MARQUEE_LOOP_FOREVER );

    // Get buttons
    HbWidget *buttonGroupContainer = qobject_cast<HbWidget*> ( loader.findWidget(
            DOCML_BUTTON_GROUP_CONTAINER ) );
    if ( buttonGroupContainer ) {
        // Define push buttons
        mPreviousPushButton = qobject_cast<HbPushButton*> ( loader.findWidget( DOCML_PREV_BUTTON ) );
        mPlayPushButton = qobject_cast<HbPushButton*> ( loader.findWidget( DOCML_PLAY_BUTTON ) );
        mNextPushButton = qobject_cast<HbPushButton*> ( loader.findWidget( DOCML_NEXT_BUTTON ) );
    }

    TX_EXIT
}

/*!
 Manually update music widget state.
 */
void MusicWidget::refreshData()
{ 
    TX_ENTRY
    
    // Get current player state
    playbackStateChanged();
    
    // Get current song data, updates button states
    playbackInfoChanged();

    // Get current album art
    albumArtReady();
    
    TX_EXIT
}

/*!
 Launch music player.
 */
void MusicWidget::launchMusicPlayer( int launchMode )
{  
    TX_ENTRY
    
    QUrl url;
    
    switch ( launchMode ) {
        case ELaunchToMainView:
            TX_LOG_ARGS( "to main view" )
            // Launch to main view
            url.setUrl( SERVICEREQ_MAIN_VIEW );    
            break;
        case ELaunchToNowPlayingView:
            TX_LOG_ARGS( "to now playing view" )
            // Launch to now playing view
            url.setUrl( SERVICEREQ_NOW_PLAYING_VIEW );
            break;
        case ELaunchToNowPlayingViewWithShuffle:
            TX_LOG_ARGS( "to now playing view with shuffle" )
            // Launch to now playing view with shuffle
            url.setUrl( SERVICEREQ_NOW_PLAYING_VIEW_SHUFFLE );        
            break;
        default:
            //should never get here
            TX_LOG_ARGS( "Error: invalid launchMode." )
            return;
    }
    
    XQAiwRequest* req;
    req = mApplicationManager.create( url );    
    if ( req ) {
        req->setBackground( false );
        req->setSynchronous( false );
    
        // Set function parameters
        QList<QVariant> args;
        args << url.toString();
        req->setArguments( args );

        // Send the request
        bool res = req->send();
        if ( !res ) {
            // Request failed. 
            int error = req->lastError();
            // Handle error
            TX_LOG_ARGS( "req send error: " << error )
        }
        delete req;
    }
   
    TX_EXIT
}

/*!
 Initializes the widget.
 */
void MusicWidget::onInitialize()
{
    TX_ENTRY

    // Setup UI
    setupUi();
    
    // Start Monitoring Music Player running status.
    mMpApplicationMonitor = new MpApplicationMonitor();
    
    // Check if Music Player UI is up and running
    mMusicPlayerRunning = mMpApplicationMonitor->isApplicationRunning();
    
    // Connect to MP engine and playback data
    connectMusicPlayerEngine( mMusicPlayerRunning );

    // Connect albumart click signal
    QObject::connect( mAlbumArt, SIGNAL( clicked() ), this, SLOT( handleAlbumArtClicked() ) );    
    
    // Connect button clicked signals
    QObject::connect( mPreviousPushButton, SIGNAL( clicked() ), this, SLOT( handlePrevButtonClicked() ) );
    QObject::connect( mPlayPushButton, SIGNAL( clicked() ), this, SLOT( handlePlayButtonClicked() ) );   
    QObject::connect( mNextPushButton, SIGNAL( clicked() ), this, SLOT( handleNextButtonClicked() ) );

    // Connect Music Player observer signal
    QObject::connect( mMpApplicationMonitor, SIGNAL( applicationStatusChanged( bool ) ), 
                     this, SLOT ( applicationStatusChanged( bool ) ) );   
    
    TX_EXIT
}

/*!
 Shows the widget.
 */
void MusicWidget::onShow()
{
    TX_ENTRY
    refreshData();
    TX_EXIT
}

/*!
 Hides the widget.
 */
void MusicWidget::onHide()
{
    TX_LOG
    if ( mMarqueeText->isAnimating() ) {
        mMarqueeText->stopAnimation();
    }
}

/*!
 Uninitializes the widget.
 */
void MusicWidget::onUninitialize()
{
    TX_LOG
}

/*!
 Slot to handle when the album art area is clicked.
 */
void MusicWidget::handleAlbumArtClicked()
{
    TX_ENTRY
    if ( getPlaybackState() == MpPlaybackData::NotPlaying ) {
        launchMusicPlayer( ELaunchToMainView );
    }
    else {
        launchMusicPlayer( ELaunchToNowPlayingView );
    }
    TX_EXIT
}

/*!
 Slot for previous button clicked.
 */
void MusicWidget::handlePrevButtonClicked()
{ 
    TX_ENTRY
    if ( mMpEngine ) {
        mMpEngine->skipBackward();
    }
    TX_EXIT
}

/*!
 Slot for play button clicked. Handles both Play and Pause!
 */
void MusicWidget::handlePlayButtonClicked()
{
    TX_ENTRY
    // If nothing to play, start music player in playback view.
    if ( getPlaybackState() == MpPlaybackData::NotPlaying ) {
        //Signal music player to start playing all songs with random
        launchMusicPlayer( ELaunchToNowPlayingViewWithShuffle );
    }
    else {
        // send play/pause command.
        if ( mMpEngine ) {
            mMpEngine->playPause();
        }
    }
    TX_EXIT
}

/*!
 Slot for next button clicked.
 */
void MusicWidget::handleNextButtonClicked()
{
    TX_ENTRY
    if ( mMpEngine ) {
        mMpEngine->skipForward();
    }
    TX_EXIT
}

/*!
 Slot to handle when the music player running status changed.
 */
void MusicWidget::applicationStatusChanged( bool isRunning )
{
    TX_ENTRY_ARGS( "running " << isRunning );

    // Connect/disconnect engine base on music player running state.
    if ( mMusicPlayerRunning != isRunning ) {
        TX_LOG_ARGS("running state changed");
        mMusicPlayerRunning = isRunning;
        connectMusicPlayerEngine( isRunning );
        // refresh data if musicplayer is on, otherwise clear data
        if ( isRunning ) {
            refreshData();
        }
        else {
            clearData();
        }
    }
    
    TX_EXIT
}

/*!
 Library started refreshing.
 */
void MusicWidget::libraryUpdateStarted()
{
    TX_ENTRY
    //Refresh Library start
    mMusicPlayerUpdating = true;
    
    // Update button enabled/disabled state
    updateButtons();
    TX_EXIT
}

/*!
 Library is finished refreshing.
 */
void MusicWidget::libraryUpdated()
{
    TX_ENTRY
    //Refresh Library done
    mMusicPlayerUpdating = false;
    
    // Update button enabled/disabled state
    updateButtons();
    TX_EXIT
}

/*!
 USB blocking state has changed.
 */
void MusicWidget::usbBlocked( bool blocked )
{
    TX_ENTRY_ARGS( "blocked: " << blocked )
    mMusicPlayerBlocked = blocked;
    
    // Update button enabled/disabled state
    updateButtons();
    TX_EXIT
}

/*!
 The album art has changed.
 */
void MusicWidget::albumArtReady()
{   
    TX_ENTRY
    HbIcon icon;
    
    if ( getPlaybackState() == MpPlaybackData::NotPlaying ) {
        TX_LOG_ARGS( "default album art" )
        icon = HbIcon( ICON_DEFAULT_ART );    
    }
    else {
        TX_LOG_ARGS( "playback data album art" )
        if ( mMpPlaybackData ) {
            mMpPlaybackData->albumArt( icon );
        }
    }
    
    mAlbumArt->setIcon( icon );
    mAlbumArt->setEnabled( true );
    TX_EXIT
}

/*!
 Playback state has changed.
 */
void MusicWidget::playbackStateChanged()
{
    TX_ENTRY
    
    if ( getPlaybackState() == MpPlaybackData::Playing ) {
        TX_LOG_ARGS( "pause icon" )
        mPlayPushButton->setIcon( HbIcon( ICON_PAUSE ) );
    }
    else {
        TX_LOG_ARGS( "play icon" )
        mPlayPushButton->setIcon( HbIcon( ICON_PLAY ) );
    }

    // Update button enabled/disabled state
    updateButtons();
    
    TX_EXIT
}

/*!
 Song data has changed.
 */
void MusicWidget::playbackInfoChanged()
{ 
    TX_ENTRY
    
    QString songData;
    QString artist;
    QString title;
    
    if ( mMpPlaybackData ) {
        artist = mMpPlaybackData->artist();
        title = mMpPlaybackData->title();
    }
    
    if ( !title.length() ) {
        artist.clear();
        title.clear();
        // Initialize marquee with something (space), to ensure text updates to display as well.
        songData = " ";
        TX_LOG_ARGS( "no song data" )
    }
    else {
        if ( !artist.length() ) {
            //Show localized "Unknown" if there is no artist name
            songData.append( hbTrId( "txt_muwidget_other_unknown_1" ).arg( title ) );
        }
        else {
            songData.append( artist );
            songData.append( SEPARATOR_TEXT );
            songData.append( title );
        }
        TX_LOG_ARGS( "song data received" )
    }    
    
    mMarqueeText->setText( songData );
    TX_LOG_ARGS( "marquee text: " << songData )

    // Display song data if it exist.
    if ( title.length() ) {
        if ( !mSongDataBackground->isVisible() ) {
            mSongDataBackground->show();
        }
        if ( !mMarqueeText->isAnimating() ) {
            mMarqueeText->startAnimation();
        }
    }
    else {
        if ( mSongDataBackground->isVisible() ) {
            mSongDataBackground->hide();
        }
        if ( mMarqueeText->isAnimating() ) {
            mMarqueeText->stopAnimation();
        }
    }
    
    TX_EXIT
}

/*!
 Update buttons to disabled/enabled as required.
 */
void MusicWidget::updateButtons()
{ 
    TX_ENTRY
    
    // All buttons disabled if updating or usb blocked
    if ( mMusicPlayerUpdating || mMusicPlayerBlocked ) {
        TX_LOG_ARGS( "refreshing" )
        mPreviousPushButton->setEnabled( false );
        mPlayPushButton->setEnabled( false );
        mNextPushButton->setEnabled( false );
    }
    // Only play button enabled if there is no song data
    else if ( getPlaybackState() == MpPlaybackData::NotPlaying ) {
        TX_LOG_ARGS( "no song data" )
        mPreviousPushButton->setEnabled( false );
        mPlayPushButton->setEnabled( true );
        mNextPushButton->setEnabled( false );
    }
    // Enable all buttons if there is song data
    else {
        TX_LOG_ARGS( "enable all buttons" )
        mPreviousPushButton->setEnabled( true );
        mPlayPushButton->setEnabled( true );
        mNextPushButton->setEnabled( true );
    }
    
    TX_EXIT
}

/*!
 Connect/disconnect Music Player engine and playback data
 */
void MusicWidget::connectMusicPlayerEngine( bool connect )
{
    TX_ENTRY

    if ( mMpEngine ) {
        MpEngineFactory::close();
        mMpEngine = 0;
        mMpPlaybackData = 0;
        mMusicPlayerUpdating = false;
        mMusicPlayerBlocked = false;
    }
    
    if ( connect ) {
        // Connect to MP engine and playback data
        mMpEngine = MpEngineFactory::createSharedEngine( MpCommon::KMusicPlayerUid, MpEngine::HomeScreen );
        Q_ASSERT_X( mMpEngine, "music_widget", "no music player engine" );
        TX_LOG_ARGS( "got mp engine" )
    
        mMpPlaybackData = mMpEngine->playbackData();
        Q_ASSERT_X( mMpPlaybackData, "music_widget", "no playback data" );
        TX_LOG_ARGS( "got playback data" )
    
        // MpEngine
        QObject::connect( mMpEngine, SIGNAL( libraryUpdateStarted() ), this, SLOT( libraryUpdateStarted() ) );
        QObject::connect( mMpEngine, SIGNAL( libraryUpdated() ), this, SLOT( libraryUpdated() ) );
        QObject::connect( mMpEngine, SIGNAL( usbBlocked( bool ) ), this, SLOT( usbBlocked( bool ) ) );

        //MpPlaybackData
        QObject::connect( mMpPlaybackData, SIGNAL( albumArtReady() ), this, SLOT( albumArtReady() ) );
        QObject::connect( mMpPlaybackData, SIGNAL( playbackStateChanged() ), this, SLOT( playbackStateChanged() ) );
        QObject::connect( mMpPlaybackData, SIGNAL( playbackInfoChanged() ), this, SLOT( playbackInfoChanged() ) );
    }
    
    TX_EXIT
}

/*!
 Get the playback state
 */
int MusicWidget::getPlaybackState()
{
    TX_ENTRY
    int state;
    if ( mMpPlaybackData ) {
        state = mMpPlaybackData->playbackState();
    }
    else {
        state = MpPlaybackData::NotPlaying;
    }
    TX_EXIT_ARGS( "playback state = " << state );
    return state;
}

/*!
 Clear the song data
 */
void MusicWidget::clearData()
{
    TX_ENTRY
    // set play button to PLAY icon.
    mPlayPushButton->setIcon( HbIcon( ICON_PLAY ) );
    updateButtons();
    
    // remove song data.
    mMarqueeText->setText( QString( " " ) );
    mMarqueeText->stopAnimation();
    mSongDataBackground->hide();
    
    // set default album art
    mAlbumArt->setIcon( HbIcon( ICON_DEFAULT_ART ) );
    mAlbumArt->setEnabled( true );
    TX_EXIT
}