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 /*! |
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 /*! |
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 { |