mpviewplugins/mpdetailsviewplugin/src/mpquerymanager.cpp
branchRCL_3
changeset 25 14979e23cb5e
equal deleted inserted replaced
24:26a1709b9fec 25:14979e23cb5e
       
     1 /*
       
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available 
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: Music Player Query Manager.
       
    15 *
       
    16 */
       
    17 
       
    18 #include "mpquerymanager.h"
       
    19 #include <QObject>
       
    20 #include <QNetworkAccessManager>
       
    21 #include <QNetworkDiskCache>
       
    22 #include <QNetworkProxyFactory>
       
    23 #include <qmobilityglobal.h>
       
    24 #include <QNetworkSession>
       
    25 #include <QDomElement>
       
    26 #include <QList>
       
    27 #include <QUrl>
       
    28 #include <QSslError>
       
    29 #include <XQSysInfo>
       
    30 #include <QSignalMapper>
       
    31 #include <QSettings>
       
    32 
       
    33 #include "mptrace.h"
       
    34 
       
    35 const int KRecommendationCount = 2;
       
    36 
       
    37 MpQueryManager::MpQueryManager()
       
    38     : mManager(0),
       
    39       mAlbumArtDownloader(0),
       
    40       mDefaultRecommendationAlbumArt("qtg_large_album_art"),
       
    41       mRequestType(NoRequest),
       
    42       mRecommendationCount(0)                               
       
    43 {
       
    44     TX_ENTRY
       
    45     mManager = new QNetworkAccessManager( this );      
       
    46     // A second intance is necessary to reduce complexity.
       
    47     // Otherwise, we would have to shoot async events when we want to receive inspire me items' album art
       
    48     // and that may not always work.
       
    49     mAlbumArtDownloader = new QNetworkAccessManager( this ); 
       
    50 
       
    51     mDownloadSignalMapper = new QSignalMapper(this);
       
    52     TX_EXIT
       
    53 }
       
    54 
       
    55 MpQueryManager::~MpQueryManager()
       
    56 {
       
    57     TX_ENTRY
       
    58     reset();
       
    59     if ( mManager ) {
       
    60        mManager->deleteLater();
       
    61     }
       
    62     if ( mAlbumArtDownloader ) {
       
    63        mAlbumArtDownloader->deleteLater();
       
    64     }
       
    65     delete mDownloadSignalMapper;
       
    66     TX_EXIT
       
    67 }
       
    68 
       
    69 
       
    70 void MpQueryManager::clearNetworkReplies()
       
    71 {
       
    72     mRequestType = NoRequest; 
       
    73     disconnect( mManager, SIGNAL( finished( QNetworkReply * ) ), this, SLOT( retrieveInformationFinished( QNetworkReply * ) ) );     
       
    74     TX_ENTRY_ARGS( "Reply count = " << mReplys.count() );    
       
    75     for ( int i = 0; i < mReplys.count(); ++i ) {
       
    76         QNetworkReply *reply = mReplys.at( i );
       
    77         disconnect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( retrieveInformationNetworkError( QNetworkReply::NetworkError ) ) );        
       
    78         if ( reply != NULL ) {
       
    79             reply->close();
       
    80             reply->deleteLater();
       
    81             reply = NULL;
       
    82         }
       
    83     }
       
    84     mReplys.clear();
       
    85     TX_EXIT
       
    86 }
       
    87 
       
    88     
       
    89 void MpQueryManager::queryInspireMeItems(QString artist,QString album,QString title)
       
    90 {
       
    91     TX_ENTRY    
       
    92     mArtist=artist;
       
    93     mAlbum=album;
       
    94     mTitle=title;
       
    95     // start querying inspire me items
       
    96     QString queryRecommendation("http://api.music.ovi.com/1.0/" + mMusicStore + "/releases/recommend/?");
       
    97     constructRequest( queryRecommendation );
       
    98     // TODO: Tokens change per new ovi api release. 
       
    99 	//       Need to figure out a way to get them updated on the fly
       
   100     queryRecommendation.append("&Token=03574704-e3d1-4466-9691-e0b34c7abfff");
       
   101 
       
   102     TX_LOG_ARGS( queryRecommendation );
       
   103     retrieveInformation( queryRecommendation );
       
   104     mRequestType = InspireMeItemsMetadataRequest;
       
   105     TX_EXIT
       
   106 }
       
   107 
       
   108 void MpQueryManager::clearRecommendations()
       
   109 {
       
   110     TX_ENTRY
       
   111     mRecommendationCount = 0;
       
   112     mDownloadedAlbumArts = 0;
       
   113     mAlbumArtsReadyCount = 0;
       
   114     mRecommendationSongs.clear();
       
   115     mRecommendationArtists.clear();
       
   116     mRecommendationAlbumArtsLink.clear();
       
   117     mRecommendationAlbumArtsMap.clear();
       
   118     TX_EXIT    
       
   119 }
       
   120 
       
   121 
       
   122 
       
   123 /*!
       
   124  Returns the Local Music Store(if available) to be used while querying "Inspire Me" Items
       
   125  */
       
   126 void MpQueryManager::queryLocalMusicStore()
       
   127 {
       
   128     TX_ENTRY
       
   129     QString imsi,mcc;
       
   130 
       
   131     XQSysInfo sysInfo( this );
       
   132     imsi = sysInfo.imsi();
       
   133     mcc= imsi.left(3);
       
   134     TX_LOG_ARGS( "mcc : " << mcc );
       
   135     
       
   136     QString queryLocalMusicStore("http://api.music.cq1.brislabs.com/1.0/?mcc=" + mcc + "&token=06543e34-0261-40a4-a03a-9e09fe110c1f");
       
   137     TX_LOG_ARGS( "queryLocalMusicStore : " << queryLocalMusicStore );
       
   138     retrieveInformation( queryLocalMusicStore );
       
   139     mRequestType = LocalStoreRequest;
       
   140     TX_EXIT
       
   141 }
       
   142 
       
   143 
       
   144 int MpQueryManager::recommendationsCount() const
       
   145 {
       
   146     TX_LOG_ARGS ("count: " << mRecommendationSongs.count());
       
   147     return mRecommendationSongs.count();
       
   148 }
       
   149 
       
   150 QString MpQueryManager::recommendedSong(int index) const
       
   151 {
       
   152     QString result;
       
   153     if( (0 <= index) && (index < mRecommendationSongs.count())) {
       
   154         result = mRecommendationSongs.at(index);
       
   155     }
       
   156     TX_LOG_ARGS ("recommendedSong: " << result);    
       
   157     return result; 
       
   158 }
       
   159 
       
   160 /*!
       
   161  Return recommendation artists
       
   162  */
       
   163 QString MpQueryManager::recommendedArtist(int index) const
       
   164 {
       
   165     QString result;
       
   166     if( (0 <= index) && (index < mRecommendationArtists.count())) {
       
   167         result = mRecommendationArtists.at(index);
       
   168     }
       
   169     TX_LOG_ARGS ("recommendedArtist: " << result);    
       
   170     return result;
       
   171 }
       
   172 
       
   173 /*!
       
   174  Slot to call when getting response
       
   175  */
       
   176 void MpQueryManager::retrieveInformationFinished( QNetworkReply* reply )
       
   177 {
       
   178     TX_ENTRY
       
   179     QString errorStr;
       
   180     int errorLine;
       
   181     int errorColumn;
       
   182     bool parsingSuccess;
       
   183         
       
   184     if ( reply->error() != QNetworkReply::NoError ) {
       
   185 		TX_LOG_ARGS("reply->error(): " << reply->error());
       
   186         signalError();
       
   187         return;
       
   188     }
       
   189         
       
   190     parsingSuccess = mDomDocument.setContent( reply, true, &errorStr, &errorLine, &errorColumn );
       
   191     if ( !parsingSuccess ) {
       
   192 		TX_LOG_ARGS("Parsing Received Content Failed");
       
   193         signalError();
       
   194         return;
       
   195     }
       
   196 
       
   197     handleParsedXML();  //CodeScanner throws a warning mis-interpreting the trailing 'L' to be a leaving function.
       
   198     
       
   199     mReplys.removeAll(reply); // remove it so that we wont process it again
       
   200     reply->deleteLater(); // make sure reply is deleted, as we longer care about it
       
   201     
       
   202     TX_EXIT
       
   203 }
       
   204 
       
   205 void MpQueryManager::signalError()
       
   206 {
       
   207     TX_ENTRY; 
       
   208     switch(mRequestType) {
       
   209         case InspireMeItemsMetadataRequest:
       
   210             TX_LOG_ARGS("emit inspireMeItemsRetrievalError");
       
   211             emit inspireMeItemsRetrievalError();
       
   212             break;
       
   213         case InspireMeItemsAlbumArtRequest:
       
   214             TX_LOG_ARGS("Warning: InspireMeItemsAlbumArtRequestError, will keep using the default AA icon");            
       
   215             break;    
       
   216         case LocalStoreRequest:
       
   217             TX_LOG_ARGS("emit localMusicStoreRetrievalError");            
       
   218             emit localMusicStoreRetrievalError();
       
   219             break;
       
   220         case NoRequest:   
       
   221         default:
       
   222             TX_LOG_ARGS("Warning!! Possible uninitialized mRequestType");            
       
   223             break;                                 
       
   224     }
       
   225     TX_EXIT    
       
   226 }
       
   227 
       
   228 /*!
       
   229  Slot to call when there is network error
       
   230  */
       
   231 void MpQueryManager::retrieveInformationNetworkError( QNetworkReply::NetworkError error )
       
   232 {
       
   233     TX_ENTRY_ARGS( "Network error for retrieving Information" << error);    
       
   234 
       
   235 	Q_UNUSED(error)
       
   236 
       
   237     disconnect( mManager, SIGNAL( finished( QNetworkReply * ) ), this, SLOT( retrieveInformationFinished( QNetworkReply * ) ) );     
       
   238     signalError();
       
   239     TX_EXIT
       
   240 }
       
   241 
       
   242 /*!
       
   243  Slot to call when there is ssl error
       
   244  */
       
   245 void MpQueryManager::retrieveInformationSslErrors( const QList<QSslError> &/*error*/ )
       
   246 {
       
   247     TX_ENTRY_ARGS( "SSL error for retrieving Information" );
       
   248     disconnect( mManager, SIGNAL( finished( QNetworkReply * ) ), this, SLOT( retrieveInformationFinished( QNetworkReply * ) ) );     
       
   249     signalError();    
       
   250     TX_EXIT
       
   251 }
       
   252 
       
   253 /*!
       
   254  Slot to call when downloading finished
       
   255  */
       
   256 void MpQueryManager::albumArtDownloaded( int index )
       
   257 {
       
   258     TX_ENTRY_ARGS( "mDownloadedAlbumArts = " << mDownloadedAlbumArts << "index = " << index);
       
   259     
       
   260     QNetworkReply* reply = qobject_cast<QNetworkReply*> ( qobject_cast<QSignalMapper*>( sender() )->mapping( index ) );
       
   261     // It seems we get several finished signals for the same reply obj
       
   262     // do nothing if we get a second signal
       
   263     if( mReplys.indexOf(reply) == -1 ) {
       
   264         TX_LOG_ARGS("Warning: QNetworkReply AA request may have been processed in previous call: " << reply );
       
   265         return;
       
   266     }
       
   267        
       
   268     if ( reply->error() == QNetworkReply::NoError ) {
       
   269         QPixmap albumart;
       
   270         bool result = albumart.loadFromData( reply->readAll() );
       
   271         if ( result ) {
       
   272             mRecommendationAlbumArtsMap.insert( mRecommendationAlbumArtsLink.at( index ), HbIcon( QIcon( albumart ) ) );
       
   273 
       
   274         } else {
       
   275             mRecommendationAlbumArtsMap.insert( mRecommendationAlbumArtsLink.at( index ), mDefaultRecommendationAlbumArt );        
       
   276         }
       
   277 
       
   278         ++mDownloadedAlbumArts;
       
   279         mReplys.removeAll(reply); // remove it so that we wont process it again
       
   280         reply->deleteLater(); // make sure reply is deleted, as we longer care about it
       
   281     }
       
   282     else {
       
   283         TX_LOG_ARGS( "Error: Downloading album art failed! Will keep using the default AA" );
       
   284         mRecommendationAlbumArtsMap.insert( mRecommendationAlbumArtsLink.at( index ), mDefaultRecommendationAlbumArt );  
       
   285     }
       
   286     mDownloadSignalMapper->removeMappings( reply );
       
   287     
       
   288     if( mDownloadedAlbumArts == mRecommendationCount) {
       
   289         // no need to be informed anymore
       
   290         mDownloadSignalMapper->disconnect(this);
       
   291         emit inspireMeItemAlbumArtReady();    
       
   292     }
       
   293     
       
   294     TX_EXIT
       
   295 }
       
   296 
       
   297 
       
   298 /*!
       
   299  Get Atom response from Ovi server based on query
       
   300  */
       
   301 void MpQueryManager::retrieveInformation( const QString &urlEncoded )
       
   302 {
       
   303     TX_ENTRY_ARGS( "urlEconded = " << urlEncoded)
       
   304     connect( mManager, SIGNAL( finished( QNetworkReply * ) ), this, SLOT( retrieveInformationFinished( QNetworkReply * ) ), Qt::UniqueConnection );
       
   305     QNetworkReply *reply = mManager->get( QNetworkRequest( QUrl( urlEncoded ) ) );
       
   306     mReplys.append( reply );
       
   307     
       
   308     connect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( retrieveInformationNetworkError( QNetworkReply::NetworkError ) ) );
       
   309     connect( reply, SIGNAL( sslErrors( QList<QSslError> ) ), this, SLOT( retrieveInformationSslErrors( QList<QSslError> ) ) );
       
   310     TX_EXIT
       
   311 }
       
   312 
       
   313 
       
   314 /*!
       
   315  Find the most suitable link based on Atom response from Ovi music server
       
   316  */
       
   317 void MpQueryManager::handleParsedXML()
       
   318 {
       
   319     TX_ENTRY
       
   320     QDomElement rootElement = mDomDocument.documentElement();
       
   321     
       
   322     if ( rootElement.attribute( "type" ) == "recommendedTracks" ) {
       
   323         TX_LOG_ARGS( "Recommendation response" )
       
   324         QDomElement entry = rootElement.firstChildElement( "entry" );
       
   325         mRecommendationCount = 0;
       
   326         while ( !entry.isNull() && mRecommendationCount < KRecommendationCount ) {
       
   327             if ( entry.attribute( "type" ) == "musictrack" ) {
       
   328                 QDomElement link = entry.firstChildElement( "link" );
       
   329                 while ( !link.isNull() ) {
       
   330                     if ( link.attribute( "title" ) == "albumart100" ) {
       
   331                         mRecommendationAlbumArtsLink.append( link.attribute( "href" ) );
       
   332                         mRecommendationAlbumArtsMap.insert( link.attribute( "href" ), mDefaultRecommendationAlbumArt );                      
       
   333                         break;
       
   334                     }
       
   335                     else {
       
   336                         link = link.nextSiblingElement( "link" );
       
   337                     }
       
   338                 }
       
   339                 QDomElement metadata = entry.firstChildElement( "metadata" );
       
   340                 mRecommendationSongs.append( metadata.firstChildElement( "name" ).text() );
       
   341                 mRecommendationArtists.append( metadata.firstChildElement( "primaryartist" ).text() );
       
   342                 ++mRecommendationCount;
       
   343             }
       
   344             entry = entry.nextSiblingElement( "entry" );
       
   345         }
       
   346  
       
   347         emit inspireMeItemsMetadataRetrieved();
       
   348 
       
   349         QNetworkReply *reply = 0;
       
   350         // we need to channel the retrieved album arts to albumArtDownloaded slot only
       
   351         disconnect( mManager, SIGNAL( finished( QNetworkReply * ) ), this, SLOT( retrieveInformationFinished( QNetworkReply * ) ) );        
       
   352         for (int i = 0; i < mRecommendationCount; i++ ) {
       
   353             TX_LOG_ARGS( "song name: " << mRecommendationSongs.at(i) );
       
   354             TX_LOG_ARGS( "Artist name: " << mRecommendationArtists.at(i) );
       
   355             TX_LOG_ARGS( "Album art link: " << mRecommendationAlbumArtsLink.at(i) );
       
   356             mRequestType = InspireMeItemsAlbumArtRequest;
       
   357             if ( mRecommendationAlbumArtsLink.at( i ).contains( "http", Qt::CaseInsensitive ) ) {
       
   358                 reply = mAlbumArtDownloader->get( QNetworkRequest( QUrl( mRecommendationAlbumArtsLink.at(i) ) ) );
       
   359                 mReplys.append( reply );
       
   360                 connect( reply, SIGNAL( finished() ), mDownloadSignalMapper, SLOT( map() ) );
       
   361                 mDownloadSignalMapper->setMapping( reply, i );
       
   362 
       
   363                 connect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( retrieveInformationNetworkError( QNetworkReply::NetworkError ) ) );
       
   364                 connect( reply, SIGNAL( sslErrors( QList<QSslError> ) ), this, SLOT( retrieveInformationSslErrors( QList<QSslError> ) ) );
       
   365             }
       
   366         }
       
   367        // we have queried for album arts for inspire me items. Now, time to wait for a response        
       
   368         connect( mDownloadSignalMapper, SIGNAL( mapped( int ) ), this, SLOT( albumArtDownloaded( int ) ) );
       
   369 
       
   370     }
       
   371     else if ( rootElement.attribute( "type" ) == "storeList" ) {
       
   372 		TX_LOG_ARGS( "Music Store List" )
       
   373         QDomElement entry = rootElement.firstChildElement( "workspace" );
       
   374         QString previousMusicStore = mMusicStore;
       
   375         mMusicStore = entry.attribute( "countryCode" );
       
   376         if( !mMusicStore.isEmpty() ) {
       
   377             bool musicStoreUpdated = ( previousMusicStore != mMusicStore );
       
   378             TX_LOG_ARGS("Music Store" << mMusicStore );
       
   379             emit localMusicStoreRetrieved( musicStoreUpdated );
       
   380             if( musicStoreUpdated ) {
       
   381                 QSettings settings;
       
   382                 TX_LOG_ARGS( "Storing music store value: " << mMusicStore );
       
   383                 settings.setValue( "LocalMusicStore", QVariant( mMusicStore ) );
       
   384             }    
       
   385         }
       
   386         else {
       
   387             emit localMusicStoreRetrievalError();   
       
   388         }		
       
   389     }
       
   390     else {
       
   391         TX_LOG_ARGS( "Not supported response" )
       
   392     }
       
   393     TX_EXIT
       
   394 }
       
   395 
       
   396 
       
   397 void MpQueryManager::reset()
       
   398 {
       
   399     TX_ENTRY
       
   400     mManager->disconnect(this);
       
   401     mAlbumArtDownloader->disconnect(this);
       
   402     clearNetworkReplies();
       
   403     clearRecommendations();
       
   404     mRecommendationAlbumArtsMap.clear();
       
   405     TX_EXIT
       
   406 }
       
   407     
       
   408 /*!
       
   409  Construct the query for fetching URI & recommendations
       
   410  */
       
   411 void MpQueryManager::constructRequest( QString &uri )
       
   412 {
       
   413     TX_ENTRY_ARGS( "uri =" << uri)
       
   414     
       
   415     QStringList keys;
       
   416     keys << "artist" << "albumtitle" << "tracktitle" << "orderby";
       
   417     
       
   418     // "relevancy" is the selected sort order
       
   419     // sort order types can be relevancy, alltimedownloads, streetreleasedate, sortname, recentdownloads
       
   420     QStringList values;
       
   421     values << mArtist << mAlbum << mTitle << QString("relevancy");
       
   422     TX_LOG_ARGS( "Artist: " << mArtist ); 
       
   423     TX_LOG_ARGS( "Album: " << mAlbum );
       
   424     TX_LOG_ARGS( "Title: " << mTitle );
       
   425     
       
   426     uri += keyValues( keys, values );
       
   427 
       
   428     QUrl url(uri);
       
   429     uri = url.toEncoded();
       
   430     TX_EXIT
       
   431 }
       
   432 
       
   433 /*!
       
   434  Make a key & value pair string for querying
       
   435  */
       
   436 QString MpQueryManager::keyValues( QStringList keys, QStringList values ) const
       
   437 {
       
   438     TX_ENTRY
       
   439     QString str;
       
   440     if ( keys.length() != values.length() ) {
       
   441         TX_LOG_ARGS( "Error: keys length is not equal to values length" ); 
       
   442     }
       
   443     else {
       
   444         for ( int i = 0; i < keys.length(); i++ ) {
       
   445             QString tValue = values.at( i );
       
   446             if ( 0 != tValue.length() ) {
       
   447                 str += keys.at( i ) + "=" + values.at( i ) + "&";
       
   448             }
       
   449         }
       
   450     }
       
   451     TX_EXIT
       
   452     return str.left( str.length() - 1 );
       
   453 }
       
   454 
       
   455 bool MpQueryManager::isLocalMusicStore()
       
   456 {
       
   457     if( mMusicStore.isEmpty() ) {
       
   458         QSettings settings;
       
   459         QVariant settingsvariant = settings.value( "LocalMusicStore", "" );
       
   460         mMusicStore = settingsvariant.toString(); 
       
   461         TX_LOG_ARGS( "Got local music store from settings:" << mMusicStore );
       
   462     }
       
   463     TX_LOG_ARGS( "isLocalMusicStore = " << !mMusicStore.isEmpty() )
       
   464     return !mMusicStore.isEmpty();
       
   465 }
       
   466 
       
   467 HbIcon MpQueryManager::recommendedAlbumArt(int index) const
       
   468 {
       
   469     TX_LOG_ARGS( "index = " << index )
       
   470     return mRecommendationAlbumArtsMap.value( mRecommendationAlbumArtsLink.at( index ) );
       
   471 }