phonebookengines/mobcntmodel/src/cntcache_p.cpp
changeset 37 fd64c38c277d
equal deleted inserted replaced
31:2a11b5b00470 37:fd64c38c277d
       
     1 /*
       
     2 * Copyright (c) 2010 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: Private data and helper classes used by class CntCache.
       
    15 *
       
    16 */
       
    17 
       
    18 #include <qtcontacts.h>
       
    19 #include <qcontactmanager.h>
       
    20 #include <hbapplication.h>
       
    21 #include <thumbnailmanager_qt.h>
       
    22 #include <hbicon.h>
       
    23 #include <QTimer>
       
    24 #include "cntcache.h"
       
    25 #include "cntcache_p.h"
       
    26 #include "cntinfoprovider.h"
       
    27 #include "cntdefaultinfoprovider.h"
       
    28 
       
    29 // maximum amount of info and icon jobs respectively -- if there are more jobs,
       
    30 // then the oldest job is skipped and the client informed that this happened
       
    31 // in this way the client can request the job again if wanted
       
    32 static const int CntMaxInfoJobs = 20;
       
    33 static const int CntMaxIconJobs = 20;
       
    34 // amount of milliseconds to postpone the jobs if the UI is very active
       
    35 static const int PostponeJobsMilliSeconds = 300;
       
    36 // the event for starting to do all the outstanding jobs
       
    37 static const QEvent::Type DoAllJobsEvent = QEvent::User;
       
    38 // the id that states that no icon is currently pending from thumbnail manager
       
    39 static const int NoIconRequest = -1;
       
    40     
       
    41 // TODO: Provide a way (cenrep keys?) for UI to set which provider to use for
       
    42 //       what info field (and what info fields are indeed even in use).
       
    43 
       
    44 /*!
       
    45     Creates a new thread for fetching contact info and icons in the background.
       
    46  */
       
    47 CntCacheThread::CntCacheThread()
       
    48     : mContactManager(new QContactManager()),
       
    49       mJobLoopRunning(false),
       
    50       mPostponeJobs(false),
       
    51       mIconRequestId(NoIconRequest)
       
    52 {
       
    53     DP_IN("CntCacheThread::CntCacheThread()");
       
    54 
       
    55     // create static provider plugins
       
    56     mDataProviders.insert(new CntDefaultInfoProvider(), ContactInfoAllFields);
       
    57     // TODO: create more static provider plugins
       
    58 
       
    59     // TODO: load dynamic provider plugins using QPluginLoader
       
    60 
       
    61     // connect the providers
       
    62     QMapIterator<CntInfoProvider*, ContactInfoFields> i(mDataProviders);
       
    63     while (i.hasNext()) {
       
    64         i.next();
       
    65         connect(qobject_cast<CntInfoProvider*>(i.key()),
       
    66                 SIGNAL(infoFieldReady(CntInfoProvider*, int, ContactInfoField, const QString&)),
       
    67                 this,
       
    68                 SLOT(onInfoFieldReady(CntInfoProvider*, int, ContactInfoField, const QString&)));
       
    69     }
       
    70 
       
    71     // create & connect the thumbnail manager
       
    72     mThumbnailManager = new ThumbnailManager(this);
       
    73     mThumbnailManager->setMode(ThumbnailManager::Default);
       
    74     mThumbnailManager->setQualityPreference(ThumbnailManager::OptimizeForPerformance);
       
    75     mThumbnailManager->setThumbnailSize(ThumbnailManager::ThumbnailSmall);
       
    76     connect(mThumbnailManager, SIGNAL(thumbnailReady(QPixmap, void *, int, int)),
       
    77              this, SLOT(onIconReady(QPixmap, void *, int, int)));
       
    78 
       
    79     // this thread should interfere as little as possible with more time-critical tasks,
       
    80     // like updating the UI during scrolling
       
    81     start(QThread::IdlePriority);
       
    82 
       
    83     DP_OUT("CntCacheThread::CntCacheThread()");
       
    84 }
       
    85 
       
    86 /*!
       
    87     Cleans up and destructs the thread.
       
    88  */
       
    89 CntCacheThread::~CntCacheThread()
       
    90 {
       
    91     DP_IN("CntCacheThread::~CntCacheThread()");
       
    92 
       
    93     disconnect(this);
       
    94 
       
    95     mJobMutex.lock();
       
    96     mInfoJobs.clear();
       
    97     mCancelledInfoJobs.clear();
       
    98     mIconJobs.clear();
       
    99     mCancelledIconJobs.clear();
       
   100 
       
   101     if (mIconRequestId != NoIconRequest) {
       
   102         mThumbnailManager->cancelRequest(mIconRequestId);
       
   103         mIconRequestId = NoIconRequest;
       
   104     }
       
   105 
       
   106     QMapIterator<CntInfoProvider*, ContactInfoFields> i(mDataProviders);
       
   107     while (i.hasNext()) {
       
   108         i.next();
       
   109         delete i.key();
       
   110     }
       
   111     mDataProviders.clear();
       
   112 
       
   113     mJobMutex.unlock();
       
   114 
       
   115     exit();
       
   116     wait();
       
   117 
       
   118     DP_OUT("CntCacheThread::~CntCacheThread()");
       
   119 }
       
   120 
       
   121 /*!
       
   122     Starts the event loop for this thread.
       
   123  */
       
   124 void CntCacheThread::run()
       
   125 {
       
   126     DP_IN("CntCacheThread::run()");
       
   127     exec();
       
   128     DP_OUT("CntCacheThread::run()");
       
   129 }
       
   130 
       
   131 /*!
       
   132     Schedules a info to be fetched for a contact. When info has been fetched
       
   133     infoFieldUpdated() signals will be emitted, once for each field.
       
   134     
       
   135     /param contactId the contact for which the info is wanted
       
   136  */
       
   137 void CntCacheThread::scheduleInfoJob(int contactId)
       
   138 {
       
   139     DP_IN("CntCacheThread::scheduleInfoJob(" << contactId << ")");
       
   140 
       
   141     Q_ASSERT(contactId > 0 && !mInfoJobs.contains(contactId));
       
   142 
       
   143     mJobMutex.lock();
       
   144 
       
   145     if (!mJobLoopRunning) {
       
   146         // new job => restart job loop
       
   147         mJobLoopRunning = true;
       
   148         HbApplication::instance()->postEvent(this, new QEvent(DoAllJobsEvent));
       
   149     }
       
   150     
       
   151     if (mInfoJobs.count() >= CntMaxInfoJobs) {
       
   152         // the queue of jobs is full, so remove the oldest job
       
   153         mCancelledInfoJobs.append(mInfoJobs.takeFirst());
       
   154         DP("CntCacheThread::scheduleInfoJob() :" << mCancelledInfoJobs.last() << "removed from joblist");
       
   155     }
       
   156 
       
   157     mInfoJobs.append(contactId);
       
   158     DP("CntCacheThread::scheduleInfoJob() :" << contactId << "appended @" << mInfoJobs.indexOf(contactId));
       
   159 
       
   160     // since this job has now been scheduled, remove it from the list of
       
   161     // cancelled jobs in case it is there
       
   162     mCancelledInfoJobs.removeOne(contactId);
       
   163 
       
   164     mJobMutex.unlock();
       
   165 
       
   166     DP_OUT("CntCacheThread::scheduleInfoJob(" << contactId << ")");
       
   167 }
       
   168 
       
   169 /*!
       
   170     Schedules an icon to be fetched. An iconUpdated() signal will be emitted when the icon
       
   171     has been fetched.
       
   172     
       
   173     /param iconName the name of the icon to be fetched
       
   174  */
       
   175 void CntCacheThread::scheduleIconJob(const QString& iconName)
       
   176 {
       
   177     DP_IN("CntCacheThread::scheduleIconJob(" << iconName << ")");
       
   178 
       
   179     mJobMutex.lock();
       
   180 
       
   181     Q_ASSERT(!iconName.isEmpty() && !mIconJobs.contains(iconName));
       
   182 
       
   183     if (!mJobLoopRunning) {
       
   184         // new job, so restart job loop
       
   185         mJobLoopRunning = true;
       
   186         HbApplication::instance()->postEvent(this, new QEvent(DoAllJobsEvent));
       
   187     }
       
   188 
       
   189     if (mIconJobs.count() >= CntMaxIconJobs) {
       
   190         // the queue of jobs is full, so remove the oldest job
       
   191         mCancelledIconJobs.append(mIconJobs.takeLast());
       
   192         DP("CntCacheThread::scheduleIconJob() :" << mCancelledIconJobs.last() << "removed from joblist");
       
   193     }
       
   194 
       
   195     mIconJobs.append(iconName);
       
   196     DP("CntCacheThread::scheduleIconJob() :" << iconName << "appended @" << mIconJobs.indexOf(iconName));
       
   197 
       
   198     // since this job has now been rescheduled, remove it from the list of
       
   199     // cancelled jobs in case it is there
       
   200     mCancelledIconJobs.removeOne(iconName);
       
   201 
       
   202     mJobMutex.unlock();
       
   203 
       
   204     DP_OUT("CntCacheThread::scheduleIconJob(" << iconName << ")");
       
   205 }
       
   206 
       
   207 /*!
       
   208     Postpones outstanding jobs for a few tenths of a second. This should be called if
       
   209     the client wants to reserve more CPU time for some urgent task.
       
   210  */
       
   211 void CntCacheThread::postponeJobs()
       
   212 {
       
   213     DP_IN("CntCacheThread::postponeJobs()");
       
   214     
       
   215     mPostponeJobs = true;
       
   216     
       
   217     DP_OUT("CntCacheThread::postponeJobs()");
       
   218 }
       
   219 
       
   220 /*!
       
   221     Handles a class-specific event that is sent by the scheduleOrUpdate functions
       
   222     when there are jobs.
       
   223  */
       
   224 bool CntCacheThread::event(QEvent* event)
       
   225 {
       
   226     if (event->type() == DoAllJobsEvent) {
       
   227         doAllJobs();
       
   228         return true;
       
   229     }
       
   230 
       
   231     return QThread::event(event);
       
   232 }
       
   233 
       
   234 /*!
       
   235     Does the jobs. The loop runs until all jobs are done. It pauses
       
   236     for a while if new info jobs appear -- this means that the UI is
       
   237     updating and so the CPU is yielded to the UI. If there are again
       
   238     new jobs after the pause, then it pauses again, and so on.
       
   239  */
       
   240 void CntCacheThread::doAllJobs()
       
   241 {
       
   242     DP_IN("CntCacheThread::doAllJobs()");
       
   243 
       
   244     forever {
       
   245         mJobMutex.lock();
       
   246         int infoJobs = mInfoJobs.count();
       
   247         int iconJobs = mIconJobs.count();
       
   248         int totalJobs = infoJobs + iconJobs + mCancelledInfoJobs.count() + mCancelledIconJobs.count();
       
   249         DP_IN("CntCacheThread::doAllJobs() : infojobs=" << infoJobs << ", iconjobs=" << iconJobs << ",icon_request=" << mIconRequestId << ", cancelledinfojobs=" << mCancelledInfoJobs.count() << ", cancellediconjobs=" << mCancelledIconJobs.count());
       
   250 
       
   251         if (totalJobs == 0 || totalJobs == iconJobs && mIconRequestId != NoIconRequest || mPostponeJobs) {
       
   252             if (mPostponeJobs) {
       
   253                 // client has requested a pause in activies (e.g. due to high UI activity)
       
   254                 mPostponeJobs = false;
       
   255                 if (totalJobs > 0) {
       
   256                     QTimer::singleShot(PostponeJobsMilliSeconds, this, SLOT(doAllJobs()));
       
   257                     DP("CntCacheThread::doAllJobs() : postponing for" << PostponeJobsMilliSeconds << "ms");
       
   258                 }
       
   259                 else {
       
   260                     mJobLoopRunning = false;
       
   261                 }
       
   262             }
       
   263             else {
       
   264                 mJobLoopRunning = false;
       
   265             }
       
   266 
       
   267             mJobMutex.unlock();
       
   268 
       
   269             if (totalJobs == 0) {
       
   270                 DP("CntCacheThread::doAllJobs() : emitting all jobs done");
       
   271                 emit allJobsDone();
       
   272             }
       
   273 
       
   274             break;
       
   275         }
       
   276 
       
   277         bool doInfoJobs = infoJobs > 0 && (iconJobs == 0 || mIconRequestId != NoIconRequest || qrand() % (infoJobs + iconJobs) < infoJobs);
       
   278         
       
   279         if (doInfoJobs) {
       
   280             // get next job
       
   281             int contactId = mInfoJobs.takeLast();
       
   282             mJobMutex.unlock();
       
   283     
       
   284             // fetch qcontact
       
   285             QStringList definitionRestrictions;
       
   286             definitionRestrictions.append(QContactName::DefinitionName);
       
   287             definitionRestrictions.append(QContactAvatar::DefinitionName);
       
   288             definitionRestrictions.append(QContactPhoneNumber::DefinitionName);
       
   289             definitionRestrictions.append(QContactOrganization::DefinitionName);
       
   290             QContactFetchHint restrictions;
       
   291             restrictions.setDetailDefinitionsHint(definitionRestrictions);
       
   292             restrictions.setOptimizationHints(QContactFetchHint::NoRelationships);
       
   293 			QContact contact = mContactManager->contact(contactId, restrictions);
       
   294 
       
   295             // request contact info from providers
       
   296             DP("CntCacheThread::doAllJobs() : fetching info for" << contact.displayLabel() << " (id=" << contactId << ")");
       
   297             QMapIterator<CntInfoProvider*, ContactInfoFields> i(mDataProviders);
       
   298             while (i.hasNext()) {
       
   299                 i.next();
       
   300                 if (i.value() != 0) {
       
   301                     i.key()->requestInfo(contact, i.value());
       
   302                 }
       
   303             }
       
   304         }
       
   305         else if (iconJobs > 0 && mIconRequestId == NoIconRequest) {
       
   306             // request icon from thumbnail manager
       
   307             QString iconName = mIconJobs.takeFirst();
       
   308             DP("CntCacheThread::doAllJobs() : fetching icon" << iconName);
       
   309             mIconRequestId = mThumbnailManager->getThumbnail(iconName, NULL, 0);
       
   310             mIconRequestName = iconName;
       
   311             mJobMutex.unlock();
       
   312         }
       
   313         else {
       
   314             if (mCancelledInfoJobs.count() > 0) {
       
   315                 int contactId = mCancelledInfoJobs.takeLast();
       
   316                 mJobMutex.unlock();
       
   317                 DP("CntCacheThread::doAllJobs() : emitting cancelled info job" << contactId);
       
   318                 emit infoCancelled(contactId);
       
   319             }
       
   320             else if (mCancelledIconJobs.count() > 0) {
       
   321                 QString iconName = mCancelledIconJobs.takeFirst();
       
   322                 mJobMutex.unlock();
       
   323                 DP("CntCacheThread::doAllJobs() : emitting cancelled icon job" << iconName);
       
   324                 emit iconCancelled(iconName);
       
   325             }
       
   326         }
       
   327     
       
   328         // allow signals to be passed from providers and from the client
       
   329         HbApplication::processEvents();
       
   330     }
       
   331 
       
   332     DP_OUT("CntCacheThread::doAllJobs()");
       
   333 }
       
   334 
       
   335 /*!
       
   336     Passes an info field from a data provider up to the client via signals. The
       
   337     client is not in the same thread, so Qt passes the signal as an event.
       
   338  */
       
   339 void CntCacheThread::onInfoFieldReady(CntInfoProvider* sender, int contactId,
       
   340                                       ContactInfoField field, const QString& text)
       
   341 {
       
   342     DP_IN("CntCacheThread::onInfoFieldReady( CntInfoProvider*," << contactId << "," << field << "," << text << ")");
       
   343 
       
   344     // there can be 3rd party providers, so we cannot blindly trust them;
       
   345     // info is emitted only if:
       
   346     // 1) the sender is in the list of providers
       
   347     // 2) exactly one field bit is set in parameter 'field'
       
   348     // 3) the field bit has been assigned to this provider
       
   349     if (mDataProviders.contains(sender)
       
   350         && ((field & (field - 1)) == 0)
       
   351         && ((field & mDataProviders.value(sender)) != 0)) {
       
   352         DP("CntCacheThread::onInfoFieldReady(" << contactId << "," << field << "," << text << ") : emitting infoFieldUpdated()");
       
   353         emit infoFieldUpdated(contactId, field, text);
       
   354     }
       
   355 
       
   356     DP_OUT("CntCacheThread::onInfoFieldReady(" << contactId << "," << field << "," << text << ")");
       
   357 }
       
   358 
       
   359 /*!
       
   360     Passes an icon from thumbnail manager up to the client via a signal. The
       
   361     client is not in the same thread, so Qt passes the signal as an event.
       
   362  */
       
   363 void CntCacheThread::onIconReady(const QPixmap& pixmap, void *data, int id, int error)
       
   364 {
       
   365     DP_IN("CntCacheThread::onIconReady( QPixMap, void*, " << id << "," << error << ")");
       
   366     Q_UNUSED(data);
       
   367 
       
   368     mJobMutex.lock();
       
   369     Q_ASSERT(id == mIconRequestId && !mIconRequestName.isEmpty());
       
   370     if (!mJobLoopRunning) {
       
   371         // job loop quit while waiting for this icon, so restart it
       
   372         mJobLoopRunning = true;
       
   373         HbApplication::instance()->postEvent(this, new QEvent(DoAllJobsEvent));
       
   374     }
       
   375     mIconRequestId = NoIconRequest;
       
   376     mJobMutex.unlock();
       
   377 
       
   378     if (error == 0) {
       
   379         DP("CntCacheThread::onIconReady() : emitting iconUpdated(" << mIconRequestName << ")");
       
   380         emit iconUpdated(mIconRequestName, HbIcon(pixmap));
       
   381     }
       
   382 
       
   383     DP_OUT("CntCacheThread::onIconReady( QPixMap, void*, " << id << "," << error << ")");
       
   384 }