phonebookengines/cntlistmodel/src/cntcache_p.cpp
changeset 72 6abfb1094884
parent 66 554fe4dbbb59
equal deleted inserted replaced
67:59984e68247d 72:6abfb1094884
    22 #include <qcontactmanager.h>
    22 #include <qcontactmanager.h>
    23 #include <hbapplication.h>
    23 #include <hbapplication.h>
    24 #include <thumbnailmanager_qt.h>
    24 #include <thumbnailmanager_qt.h>
    25 #include <hbicon.h>
    25 #include <hbicon.h>
    26 #include <QTimer>
    26 #include <QTimer>
       
    27 
    27 #include "cntcache.h"
    28 #include "cntcache.h"
    28 #include "cntcache_p.h"
    29 #include "cntcache_p.h"
    29 #include <cntinfoproviderfactory.h>
    30 #include <cntinfoproviderfactory.h>
    30 #include <cntinfoprovider.h>
    31 #include <cntinfoprovider.h>
    31 #include "cntdefaultinfoprovider.h"
    32 #include "cntdefaultinfoprovider.h"
    35 // maximum amount of info and icon jobs respectively -- if there are more jobs,
    36 // maximum amount of info and icon jobs respectively -- if there are more jobs,
    36 // then the oldest job is skipped and the client informed that this happened
    37 // then the oldest job is skipped and the client informed that this happened
    37 // in this way the client can request the job again if wanted
    38 // in this way the client can request the job again if wanted
    38 static const int CntMaxInfoJobs = 20;
    39 static const int CntMaxInfoJobs = 20;
    39 static const int CntMaxIconJobs = 20;
    40 static const int CntMaxIconJobs = 20;
    40 // amount of milliseconds to postpone the jobs if the UI is very active
       
    41 static const int PostponeJobsMilliSeconds = 300;
       
    42 // the event for starting to process all outstanding jobs
    41 // the event for starting to process all outstanding jobs
    43 static const QEvent::Type ProcessJobsEvent = QEvent::User;
    42 static const QEvent::Type ProcessJobsEvent = QEvent::User;
    44 // the id that states that no icon is currently pending from thumbnail manager
    43 // the id that states that no icon is currently pending from thumbnail manager
    45 static const int NoIconRequest = -1;
    44 static const int NoIconRequest = -1;
    46 // the id that states that there is no job with that key
    45 // the id that states that there is no job with that key
    47 static const int NoSuchJob = -1;
    46 static const int NoSuchJob = -1;
       
    47 // different states of postponement 
       
    48 static const int JobsNotPostponed = 0;
       
    49 static const int JobsPostponedForDuration = 1;
       
    50 static const int JobsPostponedUntilResume = 2;
    48 
    51 
    49 const char *CNT_INFO_PROVIDER_EXTENSION_PLUGIN_DIRECTORY = "/resource/qt/plugins/contacts/infoproviders/";
    52 const char *CNT_INFO_PROVIDER_EXTENSION_PLUGIN_DIRECTORY = "/resource/qt/plugins/contacts/infoproviders/";
    50     
    53     
    51 // TODO: Provide a way (cenrep keys?) for UI to set which provider to use for
    54 // TODO: Provide a way (cenrep keys?) for UI to set which provider to use for
    52 //       what info field (and what info fields are indeed even in use).
    55 //       what info field (and what info fields are indeed even in use).
    54 /*!
    57 /*!
    55     Creates a new thread for fetching contact info and icons in the background.
    58     Creates a new thread for fetching contact info and icons in the background.
    56  */
    59  */
    57 CntCacheThread::CntCacheThread()
    60 CntCacheThread::CntCacheThread()
    58     : mContactManager(new QContactManager()),
    61     : mContactManager(new QContactManager()),
    59       mStarted(false),
       
    60       mProcessingJobs(false),
    62       mProcessingJobs(false),
    61       mPostponeJobs(false),
    63       mJobsPostponed(JobsNotPostponed),
    62       mIconRequestId(NoIconRequest)
    64       mIconRequestId(NoIconRequest),
       
    65       mTimer(new QTimer())
    63 {
    66 {
    64     CNT_ENTRY
    67     CNT_ENTRY
    65 
    68 
    66     // create static provider plugins
    69     // create static provider plugins
    67     mInfoProviders.insert(new CntDefaultInfoProvider(), ContactInfoAllFields);
    70     mInfoProviders.insert(new CntDefaultInfoProvider(), ContactInfoAllFields);
    68     mInfoProviders.insert(new CntPresenceInfoProvider(), ContactInfoIcon2Field);
    71     mInfoProviders.insert(new CntPresenceInfoProvider(), ContactInfoIcon2Field);
    69 
    72 
    70     // load dynamic provider plugins
    73     // load dynamic provider plugins
    71     QDir pluginsDir(CNT_INFO_PROVIDER_EXTENSION_PLUGIN_DIRECTORY);
    74     QDir pluginsDir(CNT_INFO_PROVIDER_EXTENSION_PLUGIN_DIRECTORY);
    72     foreach (QString fileName, pluginsDir.entryList(QDir::Files))
    75     foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
    73     {
       
    74         // Create plugin loader
    76         // Create plugin loader
    75         QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
    77         QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
    76         if ( pluginLoader.load() )
    78         if (pluginLoader.load()) {
    77         {
       
    78             CntInfoProviderFactory *factory = qobject_cast<CntInfoProviderFactory*>(pluginLoader.instance());
    79             CntInfoProviderFactory *factory = qobject_cast<CntInfoProviderFactory*>(pluginLoader.instance());
    79             
    80             
    80             if (factory)
    81             if (factory) {
    81             {
       
    82                 CntInfoProvider *provider = factory->infoProvider();
    82                 CntInfoProvider *provider = factory->infoProvider();
    83                 mInfoProviders.insert(provider, provider->supportedFields());
    83                 mInfoProviders.insert(provider, provider->supportedFields());
    84             }
    84             }
    85         }
    85         }
    86     }
    86     }
    92         connect(static_cast<CntInfoProvider*>(i.key()),
    92         connect(static_cast<CntInfoProvider*>(i.key()),
    93                 SIGNAL(infoFieldReady(CntInfoProvider*, int, ContactInfoField, const QString&)),
    93                 SIGNAL(infoFieldReady(CntInfoProvider*, int, ContactInfoField, const QString&)),
    94                 this,
    94                 this,
    95                 SLOT(onInfoFieldReady(CntInfoProvider*, int, ContactInfoField, const QString&)));
    95                 SLOT(onInfoFieldReady(CntInfoProvider*, int, ContactInfoField, const QString&)));
    96     }
    96     }
    97 
    97     
    98     // create & connect the thumbnail manager
    98     // create & connect the thumbnail manager
    99     mThumbnailManager = new ThumbnailManager(this);
    99     mThumbnailManager = new ThumbnailManager(this);
   100     mThumbnailManager->setMode(ThumbnailManager::Default);
   100     mThumbnailManager->setMode(ThumbnailManager::Default);
   101     mThumbnailManager->setQualityPreference(ThumbnailManager::OptimizeForPerformance);
   101     mThumbnailManager->setQualityPreference(ThumbnailManager::OptimizeForPerformance);
   102     mThumbnailManager->setThumbnailSize(ThumbnailManager::ThumbnailSmall);
   102     mThumbnailManager->setThumbnailSize(ThumbnailManager::ThumbnailSmall);
   103     connect(mThumbnailManager, SIGNAL(thumbnailReady(QPixmap, void *, int, int)),
   103     connect(mThumbnailManager, SIGNAL(thumbnailReady(QPixmap, void *, int, int)),
   104              this, SLOT(onIconReady(QPixmap, void *, int, int)));
   104              this, SLOT(onIconReady(QPixmap, void *, int, int)));
       
   105     
       
   106     mTimer->setSingleShot(true);
       
   107     connect(mTimer, SIGNAL(timeout()), this, SLOT(resumeJobs()));
   105 
   108 
   106     CNT_EXIT
   109     CNT_EXIT
   107 }
   110 }
   108 
   111 
   109 /*!
   112 /*!
   114     CNT_ENTRY
   117     CNT_ENTRY
   115     
   118     
   116     delete mContactManager;
   119     delete mContactManager;
   117     disconnect(this);
   120     disconnect(this);
   118 
   121 
   119     mJobMutex.lock();
       
   120     mInfoJobs.clear();
   122     mInfoJobs.clear();
   121     mCancelledInfoJobs.clear();
   123     mCancelledInfoJobs.clear();
   122     mIconJobs.clear();
   124     mIconJobs.clear();
   123     mCancelledIconJobs.clear();
   125     mCancelledIconJobs.clear();
   124 
   126 
   131     mThumbnailManager = NULL;
   133     mThumbnailManager = NULL;
   132 
   134 
   133     qDeleteAll(mInfoProviders.keys());
   135     qDeleteAll(mInfoProviders.keys());
   134     mInfoProviders.clear();
   136     mInfoProviders.clear();
   135 
   137 
   136     mJobMutex.unlock();
       
   137 
       
   138     exit();
       
   139     wait();
       
   140 
       
   141     CNT_EXIT
       
   142 }
       
   143 
       
   144 /*!
       
   145     Starts the event loop for this thread.
       
   146  */
       
   147 void CntCacheThread::run()
       
   148 {
       
   149     CNT_ENTRY
       
   150 
       
   151     exec();
       
   152 
       
   153     CNT_EXIT
   138     CNT_EXIT
   154 }
   139 }
   155 
   140 
   156 /*!
   141 /*!
   157     Schedules a info to be fetched for a contact. When info has been fetched
   142     Schedules a info to be fetched for a contact. When info has been fetched
   163 {
   148 {
   164     CNT_ENTRY_ARGS( contactId )
   149     CNT_ENTRY_ARGS( contactId )
   165 
   150 
   166     if (contactId <= 0)
   151     if (contactId <= 0)
   167         return;
   152         return;
   168 
       
   169     mJobMutex.lock();
       
   170 
   153 
   171     int index = infoJobIndex(contactId);
   154     int index = infoJobIndex(contactId);
   172     if (index != NoSuchJob) {
   155     if (index != NoSuchJob) {
   173         // if the job already exists, update the priority
   156         // if the job already exists, update the priority
   174         if (priority < mInfoJobs.at(index).second) {
   157         if (priority < mInfoJobs.at(index).second) {
   175             mInfoJobs[index] = QPair<int,int>(contactId,priority);
   158             mInfoJobs[index] = QPair<int,int>(contactId,priority);
   176         }
   159         }
   177         mJobMutex.unlock();
       
   178         return;
   160         return;
   179     }
       
   180 
       
   181     if (!mStarted) {
       
   182         // starting the event loop; minimum priority is used as this thread
       
   183         // should interfere as little as possible with more time-critical tasks,
       
   184         // like updating the UI during scrolling
       
   185         start(QThread::IdlePriority);
       
   186         mStarted = true;
       
   187     }
   161     }
   188 
   162 
   189     if (!mProcessingJobs) {
   163     if (!mProcessingJobs) {
   190         // new job => start processing jobs
   164         // new job => start processing jobs
   191         mProcessingJobs = true;
   165         mProcessingJobs = true;
   203 
   177 
   204     // since this job has now been scheduled, remove it from the list of
   178     // since this job has now been scheduled, remove it from the list of
   205     // cancelled jobs in case it is there
   179     // cancelled jobs in case it is there
   206     mCancelledInfoJobs.removeOne(contactId);
   180     mCancelledInfoJobs.removeOne(contactId);
   207 
   181 
   208     mJobMutex.unlock();
       
   209 
       
   210     CNT_EXIT
   182     CNT_EXIT
   211 }
   183 }
   212 
   184 
   213 /*!
   185 /*!
   214     Schedules an icon to be fetched. An iconUpdated() signal will be emitted when the icon
   186     Schedules an icon to be fetched. An iconUpdated() signal will be emitted when the icon
   220 {
   192 {
   221     CNT_ENTRY_ARGS( iconName )
   193     CNT_ENTRY_ARGS( iconName )
   222 
   194 
   223     if (iconName.isEmpty())
   195     if (iconName.isEmpty())
   224         return;
   196         return;
   225 
       
   226     mJobMutex.lock();
       
   227 
   197 
   228     int index = iconJobIndex(iconName);
   198     int index = iconJobIndex(iconName);
   229     if (index != NoSuchJob) {
   199     if (index != NoSuchJob) {
   230         // if the job already exists, update the priority
   200         // if the job already exists, update the priority
   231         if (priority < mIconJobs.at(index).second) {
   201         if (priority < mIconJobs.at(index).second) {
   232             mIconJobs[index] = QPair<QString,int>(iconName,priority);
   202             mIconJobs[index] = QPair<QString,int>(iconName,priority);
   233         }
   203         }
   234         mJobMutex.unlock();
       
   235         return;
   204         return;
   236     }
   205     }
   237 
   206 
   238     if (!mProcessingJobs) {
   207     if (!mProcessingJobs) {
   239         // new job, so restart job loop
   208         // new job, so restart job loop
   252 
   221 
   253     // since this job has now been rescheduled, remove it from the list of
   222     // since this job has now been rescheduled, remove it from the list of
   254     // cancelled jobs in case it is there
   223     // cancelled jobs in case it is there
   255     mCancelledIconJobs.removeOne(iconName);
   224     mCancelledIconJobs.removeOne(iconName);
   256 
   225 
   257     mJobMutex.unlock();
   226     CNT_EXIT
   258 
   227 }
   259     CNT_EXIT
   228 
   260 }
   229 /*!
   261 
   230     Postpones outstanding jobs until milliseconds ms has passed or resumeJobs() is called.
   262 /*!
   231     This should be called if the client wants to reserve more CPU time for some urgent tasks.
   263     Postpones outstanding jobs for a few tenths of a second. This should be called if
   232     
   264     the client wants to reserve more CPU time for some urgent task.
   233     \param milliseconds The duration of the delay; 0, which is the default, means to delay
   265  */
   234                         until resumeJobs() is called
   266 void CntCacheThread::postponeJobs()
   235  */
   267 {
   236 void CntCacheThread::postponeJobs(int milliseconds)
   268     CNT_ENTRY
   237 {
   269     
   238     CNT_ENTRY_ARGS("ms =" << milliseconds << "  type =" << mJobsPostponed)
   270     mPostponeJobs = true;
   239 
       
   240     Q_ASSERT(milliseconds >= 0);
       
   241 
       
   242     if (milliseconds == 0) {
       
   243         mTimer->stop();
       
   244         mJobsPostponed = JobsPostponedUntilResume;
       
   245     } else if (mJobsPostponed != JobsPostponedUntilResume) {
       
   246         mTimer->stop();
       
   247         mJobsPostponed = JobsPostponedForDuration;
       
   248         mTimer->start(milliseconds);
       
   249     }
       
   250 
       
   251     CNT_EXIT
       
   252 }
       
   253 
       
   254 /*!
       
   255     Postpones outstanding jobs until resumeJobs() is called. This must always be called after
       
   256     postponeJobs.
       
   257  */
       
   258 void CntCacheThread::resumeJobs()
       
   259 {
       
   260     CNT_ENTRY
       
   261     
       
   262     mTimer->stop();
       
   263     mJobsPostponed = JobsNotPostponed;
       
   264     HbApplication::instance()->postEvent(this, new QEvent(ProcessJobsEvent));
   271     
   265     
   272     CNT_EXIT
   266     CNT_EXIT
   273 }
   267 }
   274 
   268 
   275 /*!
   269 /*!
   281     if (event->type() == ProcessJobsEvent) {
   275     if (event->type() == ProcessJobsEvent) {
   282         processJobs();
   276         processJobs();
   283         return true;
   277         return true;
   284     }
   278     }
   285 
   279 
   286     return QThread::event(event);
   280     return QObject::event(event);
   287 }
   281 }
   288 
   282 
   289 /*!
   283 /*!
   290     Processes all scheduled jobs. The loop runs until all jobs are done.
   284     Processes all scheduled jobs. The loop runs until all jobs are done.
   291     It pauses for a while if new info jobs appear -- this means that the
   285     It pauses for a while if new info jobs appear -- this means that the
   294  */
   288  */
   295 void CntCacheThread::processJobs()
   289 void CntCacheThread::processJobs()
   296 {
   290 {
   297     CNT_ENTRY
   291     CNT_ENTRY
   298 
   292 
       
   293     bool hasDoneJobs = false;
       
   294 
   299     forever {
   295     forever {
   300         mJobMutex.lock();
       
   301         int infoJobs = mInfoJobs.count();
   296         int infoJobs = mInfoJobs.count();
   302         int iconJobs = mIconJobs.count();
   297         int iconJobs = mIconJobs.count();
   303         int totalJobs = infoJobs + iconJobs + mCancelledInfoJobs.count() + mCancelledIconJobs.count();
   298         int totalJobs = infoJobs + iconJobs + mCancelledInfoJobs.count() + mCancelledIconJobs.count();
   304 
   299         
   305         if (totalJobs == 0 || totalJobs == iconJobs && mIconRequestId != NoIconRequest || mPostponeJobs) {
   300         if (totalJobs == 0 || totalJobs == iconJobs && mIconRequestId != NoIconRequest || mJobsPostponed != JobsNotPostponed) {
   306             if (mPostponeJobs) {
   301             if (mJobsPostponed == JobsNotPostponed || totalJobs == 0) {
   307                 // client has requested a pause in activies (e.g. due to high UI activity)
       
   308                 mPostponeJobs = false;
       
   309                 if (totalJobs > 0) {
       
   310                     QTimer::singleShot(PostponeJobsMilliSeconds, this, SLOT(processJobs()));
       
   311                 }
       
   312                 else {
       
   313                     mProcessingJobs = false;
       
   314                 }
       
   315             }
       
   316             else {
       
   317                 mProcessingJobs = false;
   302                 mProcessingJobs = false;
   318             }
   303             }
   319 
   304             
   320             mJobMutex.unlock();
   305             if (totalJobs == 0 && hasDoneJobs) {
   321 
       
   322             if (totalJobs == 0) {
       
   323                 emit allJobsDone();
   306                 emit allJobsDone();
   324             }
   307             }
   325 
   308             
   326             break;
   309             break;
   327         }
   310         }
   328 
       
   329         bool doInfoJob = infoJobs > 0; // && (iconJobs == 0 || mIconRequestId != NoIconRequest || qrand() % (infoJobs + iconJobs) < infoJobs);
       
   330         
   311         
   331         if (doInfoJob) {
   312         if (infoJobs > 0) {
   332             // get next job
   313             // get next job
   333             int contactId = takeNextInfoJob();
   314             int contactId = takeNextInfoJob();
   334             mJobMutex.unlock();
   315             
   335     
       
   336             // fetch qcontact
   316             // fetch qcontact
   337             QContactFetchHint restrictions;
   317             QContactFetchHint restrictions;
   338             restrictions.setOptimizationHints(QContactFetchHint::NoRelationships);
   318             restrictions.setOptimizationHints(QContactFetchHint::NoRelationships);
   339 			QContact contact = mContactManager->contact(contactId, restrictions);
   319 			QContact contact = mContactManager->contact(contactId, restrictions);
   340 
   320             
   341             // request contact info from providers
   321             // request contact info from providers
   342             QMapIterator<CntInfoProvider*, ContactInfoFields> i(mInfoProviders);
   322             QMapIterator<CntInfoProvider*, ContactInfoFields> i(mInfoProviders);
   343             while (i.hasNext()) {
   323             while (i.hasNext()) {
   344                 i.next();
   324                 i.next();
   345                 if (i.value() != 0) {
   325                 if (i.value() != 0) {
   350         else if (iconJobs > 0 && mIconRequestId == NoIconRequest) {
   330         else if (iconJobs > 0 && mIconRequestId == NoIconRequest) {
   351             // request icon from thumbnail manager
   331             // request icon from thumbnail manager
   352             QString iconName  = takeNextIconJob();
   332             QString iconName  = takeNextIconJob();
   353             mIconRequestId = mThumbnailManager->getThumbnail(iconName, NULL, 0);
   333             mIconRequestId = mThumbnailManager->getThumbnail(iconName, NULL, 0);
   354             mIconRequestName = iconName;
   334             mIconRequestName = iconName;
   355             mJobMutex.unlock();
       
   356         }
   335         }
   357         else {
   336         else {
   358             if (mCancelledInfoJobs.count() > 0) {
   337             if (mCancelledInfoJobs.count() > 0) {
   359                 int contactId = mCancelledInfoJobs.takeLast();
   338                 int contactId = mCancelledInfoJobs.takeLast();
   360                 mJobMutex.unlock();
       
   361                 emit infoCancelled(contactId);
   339                 emit infoCancelled(contactId);
   362             }
   340             }
   363             else if (mCancelledIconJobs.count() > 0) {
   341             else if (mCancelledIconJobs.count() > 0) {
   364                 QString iconName = mCancelledIconJobs.takeFirst();
   342                 QString iconName = mCancelledIconJobs.takeFirst();
   365                 mJobMutex.unlock();
       
   366                 emit iconCancelled(iconName);
   343                 emit iconCancelled(iconName);
   367             }
   344             }
   368         }
   345         }
   369     
   346         
       
   347         hasDoneJobs = true;
       
   348 
   370         // allow signals to be passed from providers and from the client
   349         // allow signals to be passed from providers and from the client
   371         HbApplication::processEvents();
   350         HbApplication::processEvents();
   372     }
   351     }
   373 
   352 
   374     CNT_EXIT
   353     CNT_EXIT
   406     CNT_ENTRY
   385     CNT_ENTRY
   407 
   386 
   408     Q_UNUSED(id);
   387     Q_UNUSED(id);
   409     Q_UNUSED(data);
   388     Q_UNUSED(data);
   410 
   389 
   411     mJobMutex.lock();
       
   412     Q_ASSERT(id == mIconRequestId && !mIconRequestName.isEmpty());
   390     Q_ASSERT(id == mIconRequestId && !mIconRequestName.isEmpty());
   413     if (!mProcessingJobs) {
   391     if (!mProcessingJobs) {
   414         // job loop quit while waiting for this icon, so restart it
   392         // job loop quit while waiting for this icon, so restart it
   415         mProcessingJobs = true;
   393         mProcessingJobs = true;
   416         HbApplication::instance()->postEvent(this, new QEvent(ProcessJobsEvent));
   394         HbApplication::instance()->postEvent(this, new QEvent(ProcessJobsEvent));
   417     }
   395     }
   418     mIconRequestId = NoIconRequest;
   396     mIconRequestId = NoIconRequest;
   419     mJobMutex.unlock();
       
   420 
   397 
   421     if (error == 0) {
   398     if (error == 0) {
   422         emit iconUpdated(mIconRequestName, HbIcon(pixmap));
   399         emit iconUpdated(mIconRequestName, HbIcon(pixmap));
   423     }
   400     }
   424 
   401 
   425     CNT_EXIT
   402     CNT_EXIT
   426 }
   403 }
   427 
   404 
   428 /*!
   405 /*!
   429     Finds out the index of an info job in the job list. Job mutex must be held when calling this function.
   406     Finds out the index of an info job in the job list.
   430 
   407 
   431     \return index of the contact in the job list, or NoSuchJob if no job is scheduled for the contact
   408     \return index of the contact in the job list, or NoSuchJob if no job is scheduled for the contact
   432  */
   409  */
   433 int CntCacheThread::infoJobIndex(int contactId)
   410 int CntCacheThread::infoJobIndex(int contactId)
   434 {
   411 {
   493     
   470     
   494     return mIconJobs.takeAt(selectionIndex).first;
   471     return mIconJobs.takeAt(selectionIndex).first;
   495 }
   472 }
   496 
   473 
   497 /*!
   474 /*!
   498     Finds out the index of an icon job in the job list. Job mutex must be held when calling this function.
   475     Finds out the index of an icon job in the job list.
   499 
   476 
   500     \return index of the icon in the job list, or NoSuchJob if a job for the icon is not scheduled.
   477     \return index of the icon in the job list, or NoSuchJob if a job for the icon is not scheduled.
   501  */
   478  */
   502 int CntCacheThread::iconJobIndex(QString iconName)
   479 int CntCacheThread::iconJobIndex(QString iconName)
   503 {
   480 {
   508         }
   485         }
   509     }
   486     }
   510     
   487     
   511     return NoSuchJob;
   488     return NoSuchJob;
   512 }
   489 }
       
   490 
       
   491