mpviewplugins/mpdetailsviewplugin/src/mpquerymanager.cpp
branchRCL_3
changeset 25 14979e23cb5e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mpviewplugins/mpdetailsviewplugin/src/mpquerymanager.cpp	Tue Aug 31 15:12:29 2010 +0300
@@ -0,0 +1,471 @@
+/*
+* 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: Music Player Query Manager.
+*
+*/
+
+#include "mpquerymanager.h"
+#include <QObject>
+#include <QNetworkAccessManager>
+#include <QNetworkDiskCache>
+#include <QNetworkProxyFactory>
+#include <qmobilityglobal.h>
+#include <QNetworkSession>
+#include <QDomElement>
+#include <QList>
+#include <QUrl>
+#include <QSslError>
+#include <XQSysInfo>
+#include <QSignalMapper>
+#include <QSettings>
+
+#include "mptrace.h"
+
+const int KRecommendationCount = 2;
+
+MpQueryManager::MpQueryManager()
+    : mManager(0),
+      mAlbumArtDownloader(0),
+      mDefaultRecommendationAlbumArt("qtg_large_album_art"),
+      mRequestType(NoRequest),
+      mRecommendationCount(0)                               
+{
+    TX_ENTRY
+    mManager = new QNetworkAccessManager( this );      
+    // A second intance is necessary to reduce complexity.
+    // Otherwise, we would have to shoot async events when we want to receive inspire me items' album art
+    // and that may not always work.
+    mAlbumArtDownloader = new QNetworkAccessManager( this ); 
+
+    mDownloadSignalMapper = new QSignalMapper(this);
+    TX_EXIT
+}
+
+MpQueryManager::~MpQueryManager()
+{
+    TX_ENTRY
+    reset();
+    if ( mManager ) {
+       mManager->deleteLater();
+    }
+    if ( mAlbumArtDownloader ) {
+       mAlbumArtDownloader->deleteLater();
+    }
+    delete mDownloadSignalMapper;
+    TX_EXIT
+}
+
+
+void MpQueryManager::clearNetworkReplies()
+{
+    mRequestType = NoRequest; 
+    disconnect( mManager, SIGNAL( finished( QNetworkReply * ) ), this, SLOT( retrieveInformationFinished( QNetworkReply * ) ) );     
+    TX_ENTRY_ARGS( "Reply count = " << mReplys.count() );    
+    for ( int i = 0; i < mReplys.count(); ++i ) {
+        QNetworkReply *reply = mReplys.at( i );
+        disconnect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( retrieveInformationNetworkError( QNetworkReply::NetworkError ) ) );        
+        if ( reply != NULL ) {
+            reply->close();
+            reply->deleteLater();
+            reply = NULL;
+        }
+    }
+    mReplys.clear();
+    TX_EXIT
+}
+
+    
+void MpQueryManager::queryInspireMeItems(QString artist,QString album,QString title)
+{
+    TX_ENTRY    
+    mArtist=artist;
+    mAlbum=album;
+    mTitle=title;
+    // start querying inspire me items
+    QString queryRecommendation("http://api.music.ovi.com/1.0/" + mMusicStore + "/releases/recommend/?");
+    constructRequest( queryRecommendation );
+    // TODO: Tokens change per new ovi api release. 
+	//       Need to figure out a way to get them updated on the fly
+    queryRecommendation.append("&Token=03574704-e3d1-4466-9691-e0b34c7abfff");
+
+    TX_LOG_ARGS( queryRecommendation );
+    retrieveInformation( queryRecommendation );
+    mRequestType = InspireMeItemsMetadataRequest;
+    TX_EXIT
+}
+
+void MpQueryManager::clearRecommendations()
+{
+    TX_ENTRY
+    mRecommendationCount = 0;
+    mDownloadedAlbumArts = 0;
+    mAlbumArtsReadyCount = 0;
+    mRecommendationSongs.clear();
+    mRecommendationArtists.clear();
+    mRecommendationAlbumArtsLink.clear();
+    mRecommendationAlbumArtsMap.clear();
+    TX_EXIT    
+}
+
+
+
+/*!
+ Returns the Local Music Store(if available) to be used while querying "Inspire Me" Items
+ */
+void MpQueryManager::queryLocalMusicStore()
+{
+    TX_ENTRY
+    QString imsi,mcc;
+
+    XQSysInfo sysInfo( this );
+    imsi = sysInfo.imsi();
+    mcc= imsi.left(3);
+    TX_LOG_ARGS( "mcc : " << mcc );
+    
+    QString queryLocalMusicStore("http://api.music.cq1.brislabs.com/1.0/?mcc=" + mcc + "&token=06543e34-0261-40a4-a03a-9e09fe110c1f");
+    TX_LOG_ARGS( "queryLocalMusicStore : " << queryLocalMusicStore );
+    retrieveInformation( queryLocalMusicStore );
+    mRequestType = LocalStoreRequest;
+    TX_EXIT
+}
+
+
+int MpQueryManager::recommendationsCount() const
+{
+    TX_LOG_ARGS ("count: " << mRecommendationSongs.count());
+    return mRecommendationSongs.count();
+}
+
+QString MpQueryManager::recommendedSong(int index) const
+{
+    QString result;
+    if( (0 <= index) && (index < mRecommendationSongs.count())) {
+        result = mRecommendationSongs.at(index);
+    }
+    TX_LOG_ARGS ("recommendedSong: " << result);    
+    return result; 
+}
+
+/*!
+ Return recommendation artists
+ */
+QString MpQueryManager::recommendedArtist(int index) const
+{
+    QString result;
+    if( (0 <= index) && (index < mRecommendationArtists.count())) {
+        result = mRecommendationArtists.at(index);
+    }
+    TX_LOG_ARGS ("recommendedArtist: " << result);    
+    return result;
+}
+
+/*!
+ Slot to call when getting response
+ */
+void MpQueryManager::retrieveInformationFinished( QNetworkReply* reply )
+{
+    TX_ENTRY
+    QString errorStr;
+    int errorLine;
+    int errorColumn;
+    bool parsingSuccess;
+        
+    if ( reply->error() != QNetworkReply::NoError ) {
+		TX_LOG_ARGS("reply->error(): " << reply->error());
+        signalError();
+        return;
+    }
+        
+    parsingSuccess = mDomDocument.setContent( reply, true, &errorStr, &errorLine, &errorColumn );
+    if ( !parsingSuccess ) {
+		TX_LOG_ARGS("Parsing Received Content Failed");
+        signalError();
+        return;
+    }
+
+    handleParsedXML();  //CodeScanner throws a warning mis-interpreting the trailing 'L' to be a leaving function.
+    
+    mReplys.removeAll(reply); // remove it so that we wont process it again
+    reply->deleteLater(); // make sure reply is deleted, as we longer care about it
+    
+    TX_EXIT
+}
+
+void MpQueryManager::signalError()
+{
+    TX_ENTRY; 
+    switch(mRequestType) {
+        case InspireMeItemsMetadataRequest:
+            TX_LOG_ARGS("emit inspireMeItemsRetrievalError");
+            emit inspireMeItemsRetrievalError();
+            break;
+        case InspireMeItemsAlbumArtRequest:
+            TX_LOG_ARGS("Warning: InspireMeItemsAlbumArtRequestError, will keep using the default AA icon");            
+            break;    
+        case LocalStoreRequest:
+            TX_LOG_ARGS("emit localMusicStoreRetrievalError");            
+            emit localMusicStoreRetrievalError();
+            break;
+        case NoRequest:   
+        default:
+            TX_LOG_ARGS("Warning!! Possible uninitialized mRequestType");            
+            break;                                 
+    }
+    TX_EXIT    
+}
+
+/*!
+ Slot to call when there is network error
+ */
+void MpQueryManager::retrieveInformationNetworkError( QNetworkReply::NetworkError error )
+{
+    TX_ENTRY_ARGS( "Network error for retrieving Information" << error);    
+
+	Q_UNUSED(error)
+
+    disconnect( mManager, SIGNAL( finished( QNetworkReply * ) ), this, SLOT( retrieveInformationFinished( QNetworkReply * ) ) );     
+    signalError();
+    TX_EXIT
+}
+
+/*!
+ Slot to call when there is ssl error
+ */
+void MpQueryManager::retrieveInformationSslErrors( const QList<QSslError> &/*error*/ )
+{
+    TX_ENTRY_ARGS( "SSL error for retrieving Information" );
+    disconnect( mManager, SIGNAL( finished( QNetworkReply * ) ), this, SLOT( retrieveInformationFinished( QNetworkReply * ) ) );     
+    signalError();    
+    TX_EXIT
+}
+
+/*!
+ Slot to call when downloading finished
+ */
+void MpQueryManager::albumArtDownloaded( int index )
+{
+    TX_ENTRY_ARGS( "mDownloadedAlbumArts = " << mDownloadedAlbumArts << "index = " << index);
+    
+    QNetworkReply* reply = qobject_cast<QNetworkReply*> ( qobject_cast<QSignalMapper*>( sender() )->mapping( index ) );
+    // It seems we get several finished signals for the same reply obj
+    // do nothing if we get a second signal
+    if( mReplys.indexOf(reply) == -1 ) {
+        TX_LOG_ARGS("Warning: QNetworkReply AA request may have been processed in previous call: " << reply );
+        return;
+    }
+       
+    if ( reply->error() == QNetworkReply::NoError ) {
+        QPixmap albumart;
+        bool result = albumart.loadFromData( reply->readAll() );
+        if ( result ) {
+            mRecommendationAlbumArtsMap.insert( mRecommendationAlbumArtsLink.at( index ), HbIcon( QIcon( albumart ) ) );
+
+        } else {
+            mRecommendationAlbumArtsMap.insert( mRecommendationAlbumArtsLink.at( index ), mDefaultRecommendationAlbumArt );        
+        }
+
+        ++mDownloadedAlbumArts;
+        mReplys.removeAll(reply); // remove it so that we wont process it again
+        reply->deleteLater(); // make sure reply is deleted, as we longer care about it
+    }
+    else {
+        TX_LOG_ARGS( "Error: Downloading album art failed! Will keep using the default AA" );
+        mRecommendationAlbumArtsMap.insert( mRecommendationAlbumArtsLink.at( index ), mDefaultRecommendationAlbumArt );  
+    }
+    mDownloadSignalMapper->removeMappings( reply );
+    
+    if( mDownloadedAlbumArts == mRecommendationCount) {
+        // no need to be informed anymore
+        mDownloadSignalMapper->disconnect(this);
+        emit inspireMeItemAlbumArtReady();    
+    }
+    
+    TX_EXIT
+}
+
+
+/*!
+ Get Atom response from Ovi server based on query
+ */
+void MpQueryManager::retrieveInformation( const QString &urlEncoded )
+{
+    TX_ENTRY_ARGS( "urlEconded = " << urlEncoded)
+    connect( mManager, SIGNAL( finished( QNetworkReply * ) ), this, SLOT( retrieveInformationFinished( QNetworkReply * ) ), Qt::UniqueConnection );
+    QNetworkReply *reply = mManager->get( QNetworkRequest( QUrl( urlEncoded ) ) );
+    mReplys.append( reply );
+    
+    connect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( retrieveInformationNetworkError( QNetworkReply::NetworkError ) ) );
+    connect( reply, SIGNAL( sslErrors( QList<QSslError> ) ), this, SLOT( retrieveInformationSslErrors( QList<QSslError> ) ) );
+    TX_EXIT
+}
+
+
+/*!
+ Find the most suitable link based on Atom response from Ovi music server
+ */
+void MpQueryManager::handleParsedXML()
+{
+    TX_ENTRY
+    QDomElement rootElement = mDomDocument.documentElement();
+    
+    if ( rootElement.attribute( "type" ) == "recommendedTracks" ) {
+        TX_LOG_ARGS( "Recommendation response" )
+        QDomElement entry = rootElement.firstChildElement( "entry" );
+        mRecommendationCount = 0;
+        while ( !entry.isNull() && mRecommendationCount < KRecommendationCount ) {
+            if ( entry.attribute( "type" ) == "musictrack" ) {
+                QDomElement link = entry.firstChildElement( "link" );
+                while ( !link.isNull() ) {
+                    if ( link.attribute( "title" ) == "albumart100" ) {
+                        mRecommendationAlbumArtsLink.append( link.attribute( "href" ) );
+                        mRecommendationAlbumArtsMap.insert( link.attribute( "href" ), mDefaultRecommendationAlbumArt );                      
+                        break;
+                    }
+                    else {
+                        link = link.nextSiblingElement( "link" );
+                    }
+                }
+                QDomElement metadata = entry.firstChildElement( "metadata" );
+                mRecommendationSongs.append( metadata.firstChildElement( "name" ).text() );
+                mRecommendationArtists.append( metadata.firstChildElement( "primaryartist" ).text() );
+                ++mRecommendationCount;
+            }
+            entry = entry.nextSiblingElement( "entry" );
+        }
+ 
+        emit inspireMeItemsMetadataRetrieved();
+
+        QNetworkReply *reply = 0;
+        // we need to channel the retrieved album arts to albumArtDownloaded slot only
+        disconnect( mManager, SIGNAL( finished( QNetworkReply * ) ), this, SLOT( retrieveInformationFinished( QNetworkReply * ) ) );        
+        for (int i = 0; i < mRecommendationCount; i++ ) {
+            TX_LOG_ARGS( "song name: " << mRecommendationSongs.at(i) );
+            TX_LOG_ARGS( "Artist name: " << mRecommendationArtists.at(i) );
+            TX_LOG_ARGS( "Album art link: " << mRecommendationAlbumArtsLink.at(i) );
+            mRequestType = InspireMeItemsAlbumArtRequest;
+            if ( mRecommendationAlbumArtsLink.at( i ).contains( "http", Qt::CaseInsensitive ) ) {
+                reply = mAlbumArtDownloader->get( QNetworkRequest( QUrl( mRecommendationAlbumArtsLink.at(i) ) ) );
+                mReplys.append( reply );
+                connect( reply, SIGNAL( finished() ), mDownloadSignalMapper, SLOT( map() ) );
+                mDownloadSignalMapper->setMapping( reply, i );
+
+                connect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( retrieveInformationNetworkError( QNetworkReply::NetworkError ) ) );
+                connect( reply, SIGNAL( sslErrors( QList<QSslError> ) ), this, SLOT( retrieveInformationSslErrors( QList<QSslError> ) ) );
+            }
+        }
+       // we have queried for album arts for inspire me items. Now, time to wait for a response        
+        connect( mDownloadSignalMapper, SIGNAL( mapped( int ) ), this, SLOT( albumArtDownloaded( int ) ) );
+
+    }
+    else if ( rootElement.attribute( "type" ) == "storeList" ) {
+		TX_LOG_ARGS( "Music Store List" )
+        QDomElement entry = rootElement.firstChildElement( "workspace" );
+        QString previousMusicStore = mMusicStore;
+        mMusicStore = entry.attribute( "countryCode" );
+        if( !mMusicStore.isEmpty() ) {
+            bool musicStoreUpdated = ( previousMusicStore != mMusicStore );
+            TX_LOG_ARGS("Music Store" << mMusicStore );
+            emit localMusicStoreRetrieved( musicStoreUpdated );
+            if( musicStoreUpdated ) {
+                QSettings settings;
+                TX_LOG_ARGS( "Storing music store value: " << mMusicStore );
+                settings.setValue( "LocalMusicStore", QVariant( mMusicStore ) );
+            }    
+        }
+        else {
+            emit localMusicStoreRetrievalError();   
+        }		
+    }
+    else {
+        TX_LOG_ARGS( "Not supported response" )
+    }
+    TX_EXIT
+}
+
+
+void MpQueryManager::reset()
+{
+    TX_ENTRY
+    mManager->disconnect(this);
+    mAlbumArtDownloader->disconnect(this);
+    clearNetworkReplies();
+    clearRecommendations();
+    mRecommendationAlbumArtsMap.clear();
+    TX_EXIT
+}
+    
+/*!
+ Construct the query for fetching URI & recommendations
+ */
+void MpQueryManager::constructRequest( QString &uri )
+{
+    TX_ENTRY_ARGS( "uri =" << uri)
+    
+    QStringList keys;
+    keys << "artist" << "albumtitle" << "tracktitle" << "orderby";
+    
+    // "relevancy" is the selected sort order
+    // sort order types can be relevancy, alltimedownloads, streetreleasedate, sortname, recentdownloads
+    QStringList values;
+    values << mArtist << mAlbum << mTitle << QString("relevancy");
+    TX_LOG_ARGS( "Artist: " << mArtist ); 
+    TX_LOG_ARGS( "Album: " << mAlbum );
+    TX_LOG_ARGS( "Title: " << mTitle );
+    
+    uri += keyValues( keys, values );
+
+    QUrl url(uri);
+    uri = url.toEncoded();
+    TX_EXIT
+}
+
+/*!
+ Make a key & value pair string for querying
+ */
+QString MpQueryManager::keyValues( QStringList keys, QStringList values ) const
+{
+    TX_ENTRY
+    QString str;
+    if ( keys.length() != values.length() ) {
+        TX_LOG_ARGS( "Error: keys length is not equal to values length" ); 
+    }
+    else {
+        for ( int i = 0; i < keys.length(); i++ ) {
+            QString tValue = values.at( i );
+            if ( 0 != tValue.length() ) {
+                str += keys.at( i ) + "=" + values.at( i ) + "&";
+            }
+        }
+    }
+    TX_EXIT
+    return str.left( str.length() - 1 );
+}
+
+bool MpQueryManager::isLocalMusicStore()
+{
+    if( mMusicStore.isEmpty() ) {
+        QSettings settings;
+        QVariant settingsvariant = settings.value( "LocalMusicStore", "" );
+        mMusicStore = settingsvariant.toString(); 
+        TX_LOG_ARGS( "Got local music store from settings:" << mMusicStore );
+    }
+    TX_LOG_ARGS( "isLocalMusicStore = " << !mMusicStore.isEmpty() )
+    return !mMusicStore.isEmpty();
+}
+
+HbIcon MpQueryManager::recommendedAlbumArt(int index) const
+{
+    TX_LOG_ARGS( "index = " << index )
+    return mRecommendationAlbumArtsMap.value( mRecommendationAlbumArtsLink.at( index ) );
+}