|
1 /* |
|
2 * Copyright (c) 2006-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: |
|
15 * SMS Details Ecom Plugin implementation. |
|
16 * |
|
17 */ |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 // INCLUDE FILES |
|
23 |
|
24 #include <e32std.h> |
|
25 #include <implementationproxy.h> |
|
26 #include <data_caging_path_literals.hrh> |
|
27 |
|
28 #include <gsmumsg.h> |
|
29 #include <gsmuieoperations.h> |
|
30 #include <gsmubuf.h> |
|
31 |
|
32 #include <txtetext.h> // CEditableText |
|
33 |
|
34 #include <MVPbkContactStore.h> |
|
35 #include <MVPbkContactStoreProperties.h> |
|
36 #include <contactmatcher.h> |
|
37 #include <CVPbkContactStoreUriArray.h> |
|
38 #include <MVPbkContactLink.h> |
|
39 #include <CVPbkContactLinkArray.h> |
|
40 #include <MVPbkFieldType.h> |
|
41 #include <TVPbkFieldVersitProperty.h> |
|
42 #include <MVPbkStoreContact.h> |
|
43 #include <CVPbkPhoneNumberMatchStrategy.h> |
|
44 #include <VPbkContactStoreUris.h> |
|
45 #include <TVPbkContactStoreUriPtr.h> |
|
46 #include <CPbk2StoreConfiguration.h> // Contact store configuration |
|
47 |
|
48 #include <StringLoader.h> // StringLoader |
|
49 #include <stringresourcereader.h> |
|
50 #include <centralrepository.h> |
|
51 #include <telconfigcrkeys.h> // KCRUidTelephonyConfiguration |
|
52 #include <commonphoneparser.h> // Common phone number validity checker |
|
53 |
|
54 #include <smsdetailsplugindata.rsg> |
|
55 #include "smsdetailsplugin.h" |
|
56 |
|
57 //For logging |
|
58 #include "SmumLogging.h" |
|
59 |
|
60 // CONSTANTS |
|
61 |
|
62 const TInt KSmsDefaultGsmNumberMatchLength = 7; |
|
63 |
|
64 const TInt KErrMultipleMatchFound = KErrGeneral; |
|
65 |
|
66 _LIT( KSmsDetailsPluginResourceFile, "smsdetailsplugindata.rsc" ); |
|
67 |
|
68 |
|
69 struct TCntMatchRequestData |
|
70 { |
|
71 TPtrC iFromAddress; |
|
72 TDes* iOutput; |
|
73 TInt iMaxLength; |
|
74 TInt iMatchDigitCount; |
|
75 }; |
|
76 |
|
77 |
|
78 class CSmsDetailsPluginOneShotOperation : public CActive |
|
79 { |
|
80 public: |
|
81 CSmsDetailsPluginOneShotOperation( TCntMatchRequestData& aRequestData ); |
|
82 ~CSmsDetailsPluginOneShotOperation(); |
|
83 |
|
84 public: // API |
|
85 inline TInt CompletionCode() const { return iStatus.Int(); } |
|
86 |
|
87 private: // From CActive |
|
88 void RunL(); |
|
89 void DoCancel(); |
|
90 |
|
91 private: // Internal functions |
|
92 void MatchContactL(); |
|
93 HBufC* GetContactNameL(const MVPbkContactLink& aContactLink); |
|
94 HBufC* GetContactNameInLowerCaseL(const MVPbkContactLink& aContactLink ); |
|
95 TBool ShowContactNameL(CVPbkContactLinkArray* aLinkArray, TInt &nameIndex); |
|
96 TInt GetCurrentStoreIndexL( CVPbkContactLinkArray& aLinkArray ); |
|
97 |
|
98 private: // Data members |
|
99 TCntMatchRequestData& iRequestData; |
|
100 CContactMatcher* iContactMatcher; |
|
101 }; |
|
102 |
|
103 |
|
104 // ==================== LOCAL FUNCTIONS ==================== |
|
105 |
|
106 // ----------------------------------------------------------------------------- |
|
107 // Define the implementation table for Ecom |
|
108 // ----------------------------------------------------------------------------- |
|
109 // |
|
110 const TImplementationProxy ImplementationTable[] = |
|
111 { |
|
112 IMPLEMENTATION_PROXY_ENTRY(0x102828A8, CSmsDetailsPlugin::NewL) |
|
113 }; |
|
114 |
|
115 // ----------------------------------------------------------------------------- |
|
116 // Returns the implementation table |
|
117 // ----------------------------------------------------------------------------- |
|
118 // |
|
119 EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount) |
|
120 { |
|
121 aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy); |
|
122 |
|
123 return ImplementationTable; |
|
124 } |
|
125 |
|
126 |
|
127 // ============================ MEMBER FUNCTIONS =============================== |
|
128 |
|
129 // ----------------------------------------------------------------------------- |
|
130 // CSmsDetailsPlugin::NewL |
|
131 // ----------------------------------------------------------------------------- |
|
132 // |
|
133 CSmsDetailsPlugin* CSmsDetailsPlugin::NewL() |
|
134 { |
|
135 SMUMLOGGER_ENTERFN("CSmsDetailsPlugin::NewL"); |
|
136 CSmsDetailsPlugin* self = new (ELeave) CSmsDetailsPlugin(); |
|
137 SMUMLOGGER_LEAVEFN("CSmsDetailsPlugin::NewL"); |
|
138 return self; |
|
139 } |
|
140 |
|
141 // ----------------------------------------------------------------------------- |
|
142 // CSmsDetailsPlugin::CreateResourceReaderL |
|
143 // ----------------------------------------------------------------------------- |
|
144 // |
|
145 void CSmsDetailsPlugin::CreateResourceReaderL( RFs* aFs ) |
|
146 { |
|
147 SMUMLOGGER_ENTERFN("CSmsDetailsPlugin::CreateResourceReaderL"); |
|
148 if ( !iResourceReader ) |
|
149 { |
|
150 TParse fileParse; |
|
151 fileParse.Set( KSmsDetailsPluginResourceFile, &KDC_RESOURCE_FILES_DIR, NULL ); |
|
152 TFileName resourceFile( fileParse.FullName() ); |
|
153 if ( aFs ) |
|
154 { |
|
155 iResourceReader = CStringResourceReader::NewL( resourceFile, *aFs ); |
|
156 } |
|
157 else |
|
158 { |
|
159 iResourceReader = CStringResourceReader::NewL( resourceFile ); |
|
160 } |
|
161 } |
|
162 SMUMLOGGER_LEAVEFN("CSmsDetailsPlugin::CreateResourceReaderL"); |
|
163 } |
|
164 |
|
165 // ----------------------------------------------------------------------------- |
|
166 // CSmsDetailsPlugin::CSmsDetailsPlugin |
|
167 // ----------------------------------------------------------------------------- |
|
168 // |
|
169 CSmsDetailsPlugin::CSmsDetailsPlugin() |
|
170 { |
|
171 } |
|
172 |
|
173 // ----------------------------------------------------------------------------- |
|
174 // CSmsDetailsPlugin::~CSmsDetailsPlugin |
|
175 // ----------------------------------------------------------------------------- |
|
176 // |
|
177 CSmsDetailsPlugin::~CSmsDetailsPlugin() |
|
178 { |
|
179 SMUMLOGGER_ENTERFN("CSmsDetailsPlugin::~CSmsDetailsPlugin"); |
|
180 delete iResourceReader; |
|
181 SMUMLOGGER_LEAVEFN("CSmsDetailsPlugin::~CSmsDetailsPlugin"); |
|
182 } |
|
183 |
|
184 // ----------------------------------------------------------------------------- |
|
185 // CSmsDetailsPlugin::GetDetails |
|
186 // ----------------------------------------------------------------------------- |
|
187 // |
|
188 TInt CSmsDetailsPlugin::GetDetails(RFs& aFs, const CSmsMessage& aMessage, TDes& aDetails, TInt aMaxLength) |
|
189 { |
|
190 SMUMLOGGER_ENTERFN("CSmsDetailsPlugin::GetDetails(aFs, aMessage, aDetails, aMaxLength"); |
|
191 |
|
192 __ASSERT_DEBUG( aMaxLength <= aDetails.MaxLength(), User::Invariant() ); |
|
193 |
|
194 if (aMaxLength > aDetails.MaxLength()) |
|
195 { |
|
196 aMaxLength = aDetails.MaxLength(); |
|
197 } |
|
198 |
|
199 aDetails.Zero(); |
|
200 |
|
201 TPtrC fromAddress; |
|
202 |
|
203 switch (aMessage.SmsPDU().Type()) |
|
204 { |
|
205 case CSmsPDU::ESmsSubmit: |
|
206 case CSmsPDU::ESmsDeliver: |
|
207 case CSmsPDU::ESmsStatusReport: |
|
208 fromAddress.Set(aMessage.SmsPDU().ToFromAddress()); |
|
209 break; |
|
210 default: |
|
211 return KErrNotSupported; |
|
212 } |
|
213 |
|
214 SMUMLOGGER_LEAVEFN("CSmsDetailsPlugin::GetDetails(aFs, aMessage, aDetails, aMaxLength"); |
|
215 return GetDetails(aFs, fromAddress, aDetails, aMaxLength); |
|
216 } |
|
217 |
|
218 // ----------------------------------------------------------------------------- |
|
219 // CSmsDetailsPlugin::GetDetails |
|
220 // ----------------------------------------------------------------------------- |
|
221 // |
|
222 TInt CSmsDetailsPlugin::GetDetails(RFs& aFs, const TDesC& aFromAddress, TDes& aDetails, TInt aMaxLength) |
|
223 { |
|
224 SMUMLOGGER_ENTERFN("CSmsDetailsPlugin::GetDetails(aFs, aFromAddress, aDetails, aMaxLength"); |
|
225 __ASSERT_DEBUG( aMaxLength <= aDetails.MaxLength(), User::Invariant() ); |
|
226 |
|
227 if (aMaxLength > aDetails.MaxLength()) |
|
228 { |
|
229 aMaxLength = aDetails.MaxLength(); |
|
230 } |
|
231 |
|
232 TInt ret = KErrNone; |
|
233 TRAPD(err, DoGetDetailsL(aFs, aFromAddress, aDetails, aMaxLength)); |
|
234 |
|
235 if ( (err != KErrNone) || (aDetails.Length() == 0) ) |
|
236 { |
|
237 if (aFromAddress.Length() <= aMaxLength) |
|
238 { |
|
239 aDetails = aFromAddress; |
|
240 aDetails.Trim(); |
|
241 } |
|
242 else |
|
243 { |
|
244 // Truncate aFromAddress so that it fits into aDetails. |
|
245 aDetails = aFromAddress.Left(aMaxLength); |
|
246 aDetails.Trim(); |
|
247 } |
|
248 // Propagate KErrCancel forwards |
|
249 if ( err == KErrCancel ) |
|
250 { |
|
251 ret = err; |
|
252 } |
|
253 } |
|
254 SMUMLOGGER_LEAVEFN("CSmsDetailsPlugin::GetDetails(aFs, aFromAddress, aDetails, aMaxLength"); |
|
255 |
|
256 return ret; |
|
257 } |
|
258 |
|
259 // ----------------------------------------------------------------------------- |
|
260 // CSmsDetailsPlugin::GetDescription |
|
261 // ----------------------------------------------------------------------------- |
|
262 // |
|
263 TInt CSmsDetailsPlugin::GetDescription(const CSmsMessage& aMessage, TDes& aDescription, TInt aMaxLength) |
|
264 { |
|
265 SMUMLOGGER_ENTERFN("CSmsDetailsPlugin::GetDescription(aMessage, aDescription, aMaxLength"); |
|
266 |
|
267 __ASSERT_DEBUG( aMaxLength <= aDescription.MaxLength(), User::Invariant() ); |
|
268 |
|
269 if (aMaxLength > aDescription.MaxLength()) |
|
270 { |
|
271 aMaxLength = aDescription.MaxLength(); |
|
272 } |
|
273 |
|
274 aDescription.Zero(); |
|
275 |
|
276 TBool gotDescription = EFalse; |
|
277 TRAPD(err, gotDescription = DoGetDescriptionL(aMessage, aDescription, aMaxLength)); |
|
278 if(err != KErrNone || !gotDescription) |
|
279 { |
|
280 ExtractDescriptionFromMessage(aMessage, aDescription, aMaxLength); |
|
281 } |
|
282 SMUMLOGGER_LEAVEFN("CSmsDetailsPlugin::GetDescription(aMessage, aDescription, aMaxLength"); |
|
283 return KErrNone; |
|
284 } |
|
285 |
|
286 // ----------------------------------------------------------------------------- |
|
287 // CSmsDetailsPlugin::DoGetDetailsL |
|
288 // |
|
289 // For clarity the whole function is flagged |
|
290 // ----------------------------------------------------------------------------- |
|
291 // |
|
292 |
|
293 void CSmsDetailsPlugin::DoGetDetailsL(RFs& aFs, const TDesC& aFromAddress, TDes& aDetails, TInt aMaxLength) |
|
294 { |
|
295 __UHEAP_MARK; |
|
296 SMUMLOGGER_ENTERFN("CSmsDetailsPlugin::DoGetDetailsL(aFs, aFromAddress, aDetails, aMaxLength"); |
|
297 // Check that aFromAddress is a valid GSM telephone number |
|
298 if ( !CommonPhoneParser::IsValidPhoneNumber( aFromAddress, CommonPhoneParser::ESMSNumber ) ) |
|
299 { |
|
300 User::Leave( KErrArgument ); |
|
301 } |
|
302 |
|
303 aDetails.Zero(); |
|
304 |
|
305 TInt matchDigitCount = KSmsDefaultGsmNumberMatchLength; |
|
306 |
|
307 // Read the amount of digits to be used in contact matching |
|
308 // The key is owned by PhoneApp |
|
309 CRepository* repository = CRepository::NewLC(KCRUidTelConfiguration); |
|
310 if (KErrNone == repository->Get(KTelMatchDigits, matchDigitCount)) |
|
311 { |
|
312 // Min is 7 |
|
313 matchDigitCount = |
|
314 Max(matchDigitCount, KSmsDefaultGsmNumberMatchLength); |
|
315 } |
|
316 CleanupStack::PopAndDestroy(); // repository |
|
317 |
|
318 // Match contacts to the from address |
|
319 // Prepare worker thread arguments |
|
320 TCntMatchRequestData requestData; |
|
321 requestData.iFromAddress.Set( aFromAddress ); |
|
322 requestData.iOutput = &aDetails; |
|
323 requestData.iMaxLength = aMaxLength; |
|
324 requestData.iMatchDigitCount = matchDigitCount; |
|
325 |
|
326 // Prepare thread name |
|
327 _LIT( KSmsDetailsPluginCntMatcherWorkerThreadName, "S60SmsDetailsPluginWorkerThread_%08x" ); |
|
328 TName threadName; |
|
329 threadName.AppendFormat( KSmsDetailsPluginCntMatcherWorkerThreadName, &requestData ); |
|
330 |
|
331 // Create worker thread |
|
332 RThread workerThread; |
|
333 CleanupClosePushL( workerThread ); |
|
334 /* |
|
335 * LSAN-7HUB6B:: Contact matcher thread was using watcher process stack and hence running |
|
336 * out of memory as the inbox was growing. At some point, when inbox has arnd 1700 msgs, it fails to allocate memory |
|
337 * and hence there was no matching. |
|
338 * changed the thread creation to use seperate stack of 1MB max size. |
|
339 */ |
|
340 const TInt threadCreationError = workerThread.Create( threadName, CntMatchThreadFunction, 8 * 1024, 0x1000, 0x100000, &requestData ); |
|
341 User::LeaveIfError( threadCreationError ); |
|
342 |
|
343 // Wait for thread to finish work |
|
344 TRequestStatus workerThreadStatus; |
|
345 workerThread.Rendezvous( workerThreadStatus ); |
|
346 workerThread.Resume(); |
|
347 User::WaitForRequest( workerThreadStatus ); |
|
348 |
|
349 // Propagate error |
|
350 User::LeaveIfError( workerThreadStatus.Int() ); |
|
351 |
|
352 // Tidy up |
|
353 CleanupStack::PopAndDestroy( &workerThread ); |
|
354 SMUMLOGGER_LEAVEFN("CSmsDetailsPlugin::DoGetDetailsL(aFs, aFromAddress, aDetails, aMaxLength"); |
|
355 |
|
356 __UHEAP_MARKEND; |
|
357 } |
|
358 |
|
359 |
|
360 // ----------------------------------------------------------------------------- |
|
361 // CSmsDetailsPlugin::DoGetDescriptionL |
|
362 // ----------------------------------------------------------------------------- |
|
363 // |
|
364 TBool CSmsDetailsPlugin::DoGetDescriptionL(const CSmsMessage& aMessage, |
|
365 TDes& aDescription, TInt aMaxLength) |
|
366 // this function returns EFalse if aMessage has no special message indication data and is not an SMS_STATUS_REPORT, |
|
367 // i.e. more needs to be done to extract the description from the message |
|
368 // otherwise returns ETrue |
|
369 { |
|
370 SMUMLOGGER_ENTERFN("CSmsDetailsPlugin::DoGetDescriptionL(aMessage, aDescription, aMaxLength"); |
|
371 |
|
372 TInt resourceId = 0; |
|
373 TBuf<KSmsDescriptionLength> format; |
|
374 TSmsMessageIndicationType messageIndicationType; |
|
375 TExtendedSmsIndicationType extendedType; |
|
376 TSmsMessageProfileType messageProfileType; |
|
377 TBool toStore=EFalse; |
|
378 TUint totalIndicationCount=0; |
|
379 TUint totalMessageCount=0; |
|
380 |
|
381 //check if the messae contains an enhanced voice mail indication |
|
382 CSmsEnhancedVoiceMailOperations& enhancedVoiceMailOperations = STATIC_CAST(CSmsEnhancedVoiceMailOperations&,aMessage.GetOperationsForIEL(CSmsInformationElement::ESmsEnhanceVoiceMailInformation)); |
|
383 |
|
384 if(enhancedVoiceMailOperations.ContainsEnhancedVoiceMailIEL()) |
|
385 { |
|
386 //get a copy of the indication |
|
387 CEnhancedVoiceMailBoxInformation* retrievedNotification=enhancedVoiceMailOperations.CopyEnhancedVoiceMailIEL(); |
|
388 TVoiceMailInfoType typeInfo=retrievedNotification->Type(); |
|
389 //check its type |
|
390 if(typeInfo==EGsmSmsVoiceMailNotification) |
|
391 { |
|
392 //increment the indication count |
|
393 ++totalIndicationCount; |
|
394 resourceId = R_MSG_INDICATION_ENHANCED_VOICEMAIL_ONE; |
|
395 } |
|
396 |
|
397 TUint8 messageCount=retrievedNotification->NumberOfVoiceMessages(); |
|
398 //add the message count to the running total |
|
399 totalMessageCount+=messageCount; |
|
400 //if there is more that one message of this type then set the resouce id to 'many' |
|
401 if(messageCount!=1) |
|
402 { |
|
403 ++resourceId; |
|
404 } |
|
405 |
|
406 delete retrievedNotification; |
|
407 } |
|
408 |
|
409 //check for special message waiting indications |
|
410 CSmsSpecialSMSMessageOperations& operations = STATIC_CAST(CSmsSpecialSMSMessageOperations&,aMessage.GetOperationsForIEL(CSmsInformationElement::ESmsIEISpecialSMSMessageIndication)); |
|
411 TUint specialMessageIndicationCount=operations.GetCountOfSpecialMessageIndicationsL(); |
|
412 |
|
413 if(specialMessageIndicationCount!=0) |
|
414 { |
|
415 //add special message indications to out indication count |
|
416 totalIndicationCount+=specialMessageIndicationCount; |
|
417 |
|
418 if(totalIndicationCount>1) |
|
419 { |
|
420 //set the resource id to R_MSG_INDICATION_OTHER_ONE |
|
421 resourceId = R_MSG_INDICATION_OTHER_ONE; |
|
422 //get the total number of messages from the indicatations |
|
423 TUint8 messageCount=0; |
|
424 for(TInt loopCount=0;loopCount<specialMessageIndicationCount;loopCount++) |
|
425 { |
|
426 operations.GetMessageIndicationIEL(loopCount,toStore,messageIndicationType,extendedType,messageProfileType,messageCount); |
|
427 totalMessageCount+=messageCount; |
|
428 } |
|
429 } |
|
430 else |
|
431 { |
|
432 //there is only one indication, get it's type and the number of messages it holds. |
|
433 TUint8 messageCount=0; |
|
434 operations.GetMessageIndicationIEL(0,toStore,messageIndicationType, |
|
435 extendedType,messageProfileType,messageCount); |
|
436 |
|
437 //add the message count to the running total |
|
438 totalMessageCount+=messageCount; |
|
439 |
|
440 switch (messageIndicationType) |
|
441 { |
|
442 case EGsmSmsVoiceMessageWaiting: |
|
443 resourceId = R_MSG_INDICATION_VOICEMAIL_ONE; |
|
444 break; |
|
445 |
|
446 case EGsmSmsFaxMessageWaiting: |
|
447 resourceId = R_MSG_INDICATION_FAX_ONE; |
|
448 break; |
|
449 |
|
450 case EGsmSmsElectronicMailMessageWaiting: |
|
451 resourceId = R_MSG_INDICATION_EMAIL_ONE; |
|
452 break; |
|
453 |
|
454 case EGsmSmsExtendedMessageTypeWaiting: |
|
455 //get the extended indications type |
|
456 if(extendedType==EGsmSmsVideoMessageWaiting) |
|
457 { |
|
458 resourceId = R_MSG_INDICATION_VIDEOMESSAGE_ONE; |
|
459 } |
|
460 else |
|
461 { |
|
462 resourceId = R_MSG_INDICATION_OTHER_ONE; |
|
463 } |
|
464 break; |
|
465 |
|
466 default: |
|
467 resourceId = R_MSG_INDICATION_OTHER_ONE; |
|
468 break; |
|
469 } |
|
470 } |
|
471 //if there is more that one message waiting append 'many' to the id. |
|
472 if(totalMessageCount!=1) |
|
473 { |
|
474 resourceId++; |
|
475 } |
|
476 } |
|
477 |
|
478 const CSmsPDU& smsPDU= aMessage.SmsPDU(); |
|
479 // If no Special Msg Indication found in the User Data, |
|
480 // then check the DataCodingScheme. |
|
481 if (totalIndicationCount==0 && smsPDU.DataCodingSchemePresent()) |
|
482 { |
|
483 TInt bits7to4 = smsPDU.Bits7To4(); |
|
484 |
|
485 switch (bits7to4) |
|
486 { |
|
487 case TSmsDataCodingScheme::ESmsDCSMessageWaitingIndication7Bit: |
|
488 case TSmsDataCodingScheme::ESmsDCSMessageWaitingIndicationUCS2: |
|
489 { |
|
490 if (smsPDU.IndicationState() == TSmsDataCodingScheme::ESmsIndicationActive) |
|
491 { |
|
492 totalIndicationCount = 1; |
|
493 |
|
494 switch (smsPDU.IndicationType()) |
|
495 { |
|
496 case TSmsDataCodingScheme::ESmsVoicemailMessageWaiting: |
|
497 resourceId = R_MSG_INDICATION_VOICEMAIL_ONE; |
|
498 break; |
|
499 case TSmsDataCodingScheme::ESmsFaxMessageWaiting: |
|
500 resourceId = R_MSG_INDICATION_FAX_ONE; |
|
501 break; |
|
502 case TSmsDataCodingScheme::ESmsElectronicMailMessageWaiting: |
|
503 resourceId = R_MSG_INDICATION_EMAIL_ONE; |
|
504 break; |
|
505 case TSmsDataCodingScheme::ESmsFaxOtherMessageWaiting: |
|
506 default: |
|
507 resourceId = R_MSG_INDICATION_OTHER_ONE; |
|
508 break; |
|
509 } //end switch |
|
510 } //end if |
|
511 } //end case |
|
512 default: |
|
513 { |
|
514 break; //do nothing |
|
515 } |
|
516 } |
|
517 } |
|
518 |
|
519 if (totalIndicationCount!=0) |
|
520 { |
|
521 //Special message found. |
|
522 CreateResourceReaderL( NULL ); |
|
523 format = iResourceReader->ReadResourceString( resourceId ); |
|
524 |
|
525 if (totalMessageCount == 1) |
|
526 { |
|
527 if (format.Length() <= aMaxLength) |
|
528 { |
|
529 aDescription = format; |
|
530 } |
|
531 else |
|
532 { |
|
533 // Truncate format so that it fits into aDescription. |
|
534 aDescription = format.Left(aMaxLength); |
|
535 } |
|
536 } |
|
537 else if (format.Length() < aMaxLength) |
|
538 { |
|
539 StringLoader::Format( aDescription, format, -1, totalMessageCount ); |
|
540 } |
|
541 |
|
542 SMUMLOGGER_LEAVEFN("CSmsDetailsPlugin::DoGetDescriptionL(aMessage, aDescription, aMaxLength) - 1- true"); |
|
543 return ETrue; |
|
544 } |
|
545 else |
|
546 { |
|
547 if(aMessage.Type() == CSmsPDU::ESmsStatusReport) |
|
548 { |
|
549 // for SMS_STATUS_REPORT messages, if we cannot read the string in, then |
|
550 // we do not attempt to extract the description from the message: return EFalse |
|
551 CreateResourceReaderL( NULL ); |
|
552 aDescription.Copy( iResourceReader->ReadResourceString( R_MSG_TYPE_STATUS_REPORT ) ); |
|
553 SMUMLOGGER_LEAVEFN("CSmsDetailsPlugin::DoGetDescriptionL(aMessage, aDescription, aMaxLength) - 2- true"); |
|
554 return ETrue; |
|
555 } |
|
556 else |
|
557 { |
|
558 SMUMLOGGER_LEAVEFN("CSmsDetailsPlugin::DoGetDescriptionL(aMessage, aDescription, aMaxLength) - 3- false"); |
|
559 return EFalse; |
|
560 } |
|
561 } |
|
562 } |
|
563 |
|
564 // ----------------------------------------------------------------------------- |
|
565 // CSmsDetailsPlugin::ExtractDescriptionFromMessage |
|
566 // ----------------------------------------------------------------------------- |
|
567 // |
|
568 void CSmsDetailsPlugin::ExtractDescriptionFromMessage(const CSmsMessage& aMessage, TDes& aDescription, TInt aMaxLength) |
|
569 { |
|
570 SMUMLOGGER_ENTERFN("CSmsDetailsPlugin::ExtractDescriptionFromMessage(aMessage, aDescription, aMaxLength)"); |
|
571 if(aMessage.Type() != CSmsPDU::ESmsStatusReport) |
|
572 { |
|
573 aMessage.Buffer().Extract(aDescription, 0, Min(aMaxLength, aMessage.Buffer().Length())); |
|
574 |
|
575 TInt length = aDescription.Length(); |
|
576 |
|
577 //replace paragraphs with spaces. |
|
578 while(length--) |
|
579 { |
|
580 TText& text = aDescription[length]; |
|
581 const TChar ch(text); |
|
582 if (ch.IsSpace() || text == CEditableText::EParagraphDelimiter) |
|
583 text = ' '; |
|
584 } |
|
585 |
|
586 aDescription.TrimAll(); //removes leading trailing and multiple internal whitespace (spaces, line feeds etc) |
|
587 } |
|
588 SMUMLOGGER_LEAVEFN("CSmsDetailsPlugin::ExtractDescriptionFromMessage(aMessage, aDescription, aMaxLength)"); |
|
589 } |
|
590 |
|
591 TInt CSmsDetailsPlugin::CntMatchThreadFunction( TAny* aRequestData ) |
|
592 { |
|
593 SMUMLOGGER_ENTERFN("CSmsDetailsPlugin::CntMatchThreadFunction"); |
|
594 TInt error = KErrNone; |
|
595 // |
|
596 CTrapCleanup* cleanupStack = CTrapCleanup::New(); |
|
597 if ( !cleanupStack ) |
|
598 { |
|
599 error = KErrNoMemory; |
|
600 } |
|
601 else |
|
602 { |
|
603 // Carry out operation in this thread, but via an active scheduler |
|
604 TRAP( error, CntMatchThreadFunctionL( aRequestData ) ); |
|
605 delete cleanupStack; |
|
606 } |
|
607 |
|
608 // Rendezvous with MsvServer thread to report completion |
|
609 RThread::Rendezvous( error ); |
|
610 SMUMLOGGER_LEAVEFN("CSmsDetailsPlugin::CntMatchThreadFunction"); |
|
611 |
|
612 return error; |
|
613 } |
|
614 |
|
615 |
|
616 void CSmsDetailsPlugin::CntMatchThreadFunctionL( TAny* aRequestData ) |
|
617 { |
|
618 SMUMLOGGER_ENTERFN("CSmsDetailsPlugin::CntMatchThreadFunctionL"); |
|
619 TCntMatchRequestData* requestData = reinterpret_cast< TCntMatchRequestData* >( aRequestData ); |
|
620 |
|
621 CActiveScheduler* scheduler = new(ELeave) CActiveScheduler(); |
|
622 CleanupStack::PushL( scheduler ); |
|
623 CActiveScheduler::Install( scheduler ); |
|
624 |
|
625 // Create one-shot object to do the match operation |
|
626 CSmsDetailsPluginOneShotOperation* operation = new(ELeave) CSmsDetailsPluginOneShotOperation( *requestData ); |
|
627 CleanupStack::PushL( operation ); |
|
628 |
|
629 // Now start the scheduler to carry out the op |
|
630 CActiveScheduler::Start(); |
|
631 |
|
632 // Control returns here after the operation is complete. Propagate error codes to |
|
633 // thread function. |
|
634 User::LeaveIfError( operation->CompletionCode() ); |
|
635 |
|
636 CleanupStack::PopAndDestroy( 2, scheduler ); // operation, scheduler |
|
637 SMUMLOGGER_LEAVEFN("CSmsDetailsPlugin::CntMatchThreadFunctionL"); |
|
638 } |
|
639 |
|
640 |
|
641 |
|
642 |
|
643 |
|
644 CSmsDetailsPluginOneShotOperation::CSmsDetailsPluginOneShotOperation( TCntMatchRequestData& aRequestData ) |
|
645 : CActive( EPriorityNormal ), iRequestData( aRequestData ) |
|
646 { |
|
647 SMUMLOGGER_ENTERFN("CSmsDetailsPluginOneShotOperation::CSmsDetailsPluginOneShotOperation"); |
|
648 CActiveScheduler::Add( this ); |
|
649 // |
|
650 TRequestStatus* status = &iStatus; |
|
651 User::RequestComplete( status, KErrNone ); |
|
652 SetActive(); |
|
653 SMUMLOGGER_LEAVEFN("CSmsDetailsPluginOneShotOperation::CSmsDetailsPluginOneShotOperation"); |
|
654 } |
|
655 |
|
656 |
|
657 CSmsDetailsPluginOneShotOperation::~CSmsDetailsPluginOneShotOperation() |
|
658 { |
|
659 SMUMLOGGER_ENTERFN("CSmsDetailsPluginOneShotOperation::~CSmsDetailsPluginOneShotOperation"); |
|
660 if( iContactMatcher ) |
|
661 delete iContactMatcher; |
|
662 Cancel(); |
|
663 SMUMLOGGER_LEAVEFN("CSmsDetailsPluginOneShotOperation::~CSmsDetailsPluginOneShotOperation"); |
|
664 } |
|
665 |
|
666 |
|
667 void CSmsDetailsPluginOneShotOperation::RunL() |
|
668 { |
|
669 SMUMLOGGER_ENTERFN("CSmsDetailsPluginOneShotOperation::RunL"); |
|
670 TRAPD( err, MatchContactL() ); |
|
671 |
|
672 // Preserver error code so that the thread function can report it to the main MsvServer thread. |
|
673 iStatus = err; |
|
674 |
|
675 // Stop scheduler which will return control to the thread function |
|
676 CActiveScheduler::Stop(); |
|
677 SMUMLOGGER_LEAVEFN("CSmsDetailsPluginOneShotOperation::RunL"); |
|
678 } |
|
679 |
|
680 |
|
681 void CSmsDetailsPluginOneShotOperation::DoCancel() |
|
682 { |
|
683 // Nothing to do here - we already completed our request in the ctor |
|
684 } |
|
685 |
|
686 |
|
687 void CSmsDetailsPluginOneShotOperation::MatchContactL() |
|
688 { |
|
689 SMUMLOGGER_ENTERFN("CSmsDetailsPluginOneShotOperation::MatchContactL"); |
|
690 RFs fsSession; |
|
691 User::LeaveIfError( fsSession.Connect() ); |
|
692 CleanupClosePushL( fsSession ); |
|
693 |
|
694 if( !iContactMatcher ) |
|
695 { |
|
696 iContactMatcher = CContactMatcher::NewL( &fsSession ); |
|
697 iContactMatcher->OpenAllStoresL(); |
|
698 } |
|
699 |
|
700 CVPbkContactLinkArray* linkArray = CVPbkContactLinkArray::NewLC(); |
|
701 |
|
702 iContactMatcher->MatchPhoneNumberL( |
|
703 iRequestData.iFromAddress, |
|
704 iRequestData.iMatchDigitCount, |
|
705 CVPbkPhoneNumberMatchStrategy::EVPbkMatchFlagsNone, |
|
706 *linkArray ); |
|
707 |
|
708 TInt nameIndex = 0; //correct index if only one match is found |
|
709 if( linkArray->Count() == 0 ) |
|
710 { |
|
711 SMUMLOGGER_WRITE("No match found"); |
|
712 User::Leave( KErrNotFound ); |
|
713 } |
|
714 else if( linkArray->Count() > 1 ) |
|
715 { |
|
716 //Multiple matches found. Get the current store single match index if any. |
|
717 nameIndex = GetCurrentStoreIndexL( *linkArray ); |
|
718 if( nameIndex == KErrMultipleMatchFound ) |
|
719 { |
|
720 /* No unique match in current store, Hence show the name only if all the matches have |
|
721 * identical names |
|
722 */ |
|
723 if( ShowContactNameL( linkArray, nameIndex) == EFalse) |
|
724 { |
|
725 SMUMLOGGER_WRITE("No (Perfect) match found"); |
|
726 User::Leave( KErrMultipleMatchFound ); |
|
727 } |
|
728 } |
|
729 } |
|
730 |
|
731 // There should be only one match or multiple identical matches in this case. |
|
732 // Use the same and read contact from the store. |
|
733 |
|
734 HBufC* alias = GetContactNameL( linkArray->At(nameIndex) ); |
|
735 if ( alias ) |
|
736 { |
|
737 iRequestData.iOutput->Copy( alias->Left( iRequestData.iMaxLength ) ); |
|
738 delete alias; |
|
739 alias = NULL; |
|
740 } |
|
741 |
|
742 CleanupStack::PopAndDestroy( 2, &fsSession ); // linkArray, fsSession |
|
743 SMUMLOGGER_LEAVEFN("CSmsDetailsPluginOneShotOperation::MatchContactL"); |
|
744 } |
|
745 |
|
746 |
|
747 // ----------------------------------------------------------------------------- |
|
748 // CSmsDetailsPluginOneShotOperation::GetContactNameL |
|
749 // ----------------------------------------------------------------------------- |
|
750 // |
|
751 HBufC* CSmsDetailsPluginOneShotOperation::GetContactNameL(const MVPbkContactLink& aContactLink ) |
|
752 { |
|
753 //SMUMLOGGER_ENTERFN("CSmsDetailsPluginOneShotOperation::GetContactNameL"); |
|
754 MVPbkStoreContact* tempContact; |
|
755 iContactMatcher->GetStoreContactL(aContactLink, &tempContact); |
|
756 tempContact->PushL(); |
|
757 |
|
758 MVPbkStoreContactFieldCollection& coll = tempContact->Fields(); |
|
759 HBufC* nameBuff = iContactMatcher->GetNameL( coll ); |
|
760 |
|
761 CleanupStack::PopAndDestroy(tempContact); // tempContact |
|
762 //SMUMLOGGER_LEAVEFN("CSmsDetailsPluginOneShotOperation::GetContactNameL"); |
|
763 return nameBuff; |
|
764 } |
|
765 |
|
766 // ----------------------------------------------------------------------------- |
|
767 // CSmsDetailsPluginOneShotOperation::GetContactNameInLowerCaseL |
|
768 // ----------------------------------------------------------------------------- |
|
769 // |
|
770 HBufC* CSmsDetailsPluginOneShotOperation::GetContactNameInLowerCaseL(const MVPbkContactLink& aContactLink ) |
|
771 { |
|
772 //SMUMLOGGER_ENTERFN("CSmsDetailsPluginOneShotOperation::GetContactNameInLowerCaseL"); |
|
773 //get the name |
|
774 HBufC* nameBuff = GetContactNameL(aContactLink); |
|
775 CleanupStack::PushL( nameBuff ); |
|
776 |
|
777 //SMUMLOGGER_WRITE_FORMAT( "length: %d", nameBuff->Length() ); |
|
778 //SMUMLOGGER_WRITE_FORMAT( "size: %d", nameBuff->Size() ); |
|
779 |
|
780 //Convert to lower case , since this name buffer is used to compare names. |
|
781 HBufC* nameInLowerCase = HBufC::NewL( nameBuff->Length() + 2 ); |
|
782 nameInLowerCase->Des().CopyLC( *nameBuff ); |
|
783 |
|
784 CleanupStack::PopAndDestroy( nameBuff ); // nameBuff |
|
785 //SMUMLOGGER_LEAVEFN("CSmsDetailsPluginOneShotOperation::GetContactNameInLowerCaseL"); |
|
786 return nameInLowerCase; |
|
787 } |
|
788 |
|
789 // ----------------------------------------------------------------------------- |
|
790 // CSmsDetailsPluginOneShotOperation::ShowContactNameL |
|
791 // ----------------------------------------------------------------------------- |
|
792 // |
|
793 TBool CSmsDetailsPluginOneShotOperation::ShowContactNameL(CVPbkContactLinkArray* aLinkArray, TInt &nameIndex) |
|
794 { |
|
795 SMUMLOGGER_ENTERFN("CSmsDetailsPluginOneShotOperation::ShowContactName"); |
|
796 /* TODO:: see the NOTE: below |
|
797 * compare the names upto standard |
|
798 * 1. if all the names are same - display the name |
|
799 * eg: "abcdef xyz" && "abcdef xyz" |
|
800 * 2. find min name legth among all, |
|
801 * if this length is > standard length and matches upto standard length - display the larger name. |
|
802 * eg: abcdef xyz123, abcdef xyz12, abcdef xyz and std length is 10, |
|
803 * since match upto 10 chars is fine, display abcdef xyz123 |
|
804 * 3. in any other case do not show name |
|
805 * eg: abcdef xyz , abcde xyz |
|
806 * abcdef xyz , abcdef xy |
|
807 * abcdef xyz , abcde |
|
808 */ |
|
809 TInt i, minLength = 999, maxLength = 0, length = 0, maxLengthIndex = 0; |
|
810 TBool retVal = ETrue ; |
|
811 SMUMLOGGER_WRITE( "Contact Match statistics to follow..." ); |
|
812 SMUMLOGGER_WRITE_FORMAT( "CSmsDetailsPluginOneShotOperation::MatchContactL Match count: %d", aLinkArray->Count() ); |
|
813 |
|
814 for( i = 0 ; i < aLinkArray->Count(); i++ ) |
|
815 { |
|
816 HBufC* alias = GetContactNameL( aLinkArray->At(i) ); |
|
817 SMUMLOGGER_WRITE_FORMAT( ":-> %s", alias->Des().PtrZ() ); |
|
818 length = alias->Des().Length(); |
|
819 if(minLength > length) |
|
820 { |
|
821 minLength = length; |
|
822 } |
|
823 if(maxLength < length) |
|
824 { |
|
825 maxLength = length; |
|
826 maxLengthIndex = i; |
|
827 } |
|
828 delete alias; |
|
829 alias = NULL; |
|
830 } |
|
831 SMUMLOGGER_WRITE_FORMAT( " MinLength : %d", minLength); |
|
832 SMUMLOGGER_WRITE_FORMAT( " MaxLength : %d", maxLength); |
|
833 SMUMLOGGER_WRITE_FORMAT( " MaxLen index: %d", maxLengthIndex); |
|
834 |
|
835 if(minLength != maxLength) |
|
836 { |
|
837 //complete length match not possible |
|
838 retVal = EFalse; |
|
839 |
|
840 /* NOTE: |
|
841 * Uncomment below code if partial length(upto stdLength) match is sufficient, |
|
842 * ensure stdLength is correct |
|
843 */ |
|
844 /* |
|
845 if(minLength < stdLength) |
|
846 { |
|
847 SMUMLOGGER_WRITE( "minLength < stdLength" ); |
|
848 retVal = EFalse; |
|
849 } |
|
850 */ |
|
851 } |
|
852 |
|
853 if( retVal ) |
|
854 { |
|
855 TInt ret; |
|
856 HBufC* longestName = GetContactNameInLowerCaseL( aLinkArray->At(maxLengthIndex) ); |
|
857 SMUMLOGGER_WRITE_FORMAT( "Longest name:-> %s", longestName->Des().PtrZ() ); |
|
858 for ( i = 0; i < aLinkArray->Count() && retVal; i++ ) |
|
859 { |
|
860 HBufC* nameI = GetContactNameInLowerCaseL( aLinkArray->At(i) ); |
|
861 SMUMLOGGER_WRITE_FORMAT( "compared with -> %s", nameI->Des().PtrZ() ); |
|
862 ret = longestName->Find(nameI->Des()); |
|
863 if(ret == KErrNotFound || ret != 0) |
|
864 { |
|
865 SMUMLOGGER_WRITE_FORMAT( "Part/Full Match error/offset: %d", ret ); |
|
866 retVal = EFalse; |
|
867 } |
|
868 delete nameI; |
|
869 nameI = NULL; |
|
870 } |
|
871 delete longestName; |
|
872 longestName = NULL; |
|
873 } |
|
874 |
|
875 //If no match OR more than one match, then to avoid ambiguity, display the number |
|
876 SMUMLOGGER_WRITE_FORMAT( "Final Match result : %d", retVal ); |
|
877 SMUMLOGGER_WRITE_FORMAT( "Final Match index : %d", maxLengthIndex ); |
|
878 SMUMLOGGER_WRITE( "Contact Match statistics End here..." ); |
|
879 |
|
880 SMUMLOGGER_LEAVEFN("CSmsDetailsPluginOneShotOperation::ShowContactName"); |
|
881 nameIndex = maxLengthIndex; |
|
882 return retVal; |
|
883 } |
|
884 |
|
885 // ----------------------------------------------------------------------------- |
|
886 // CSmsDetailsPluginOneShotOperation::GetCurrentStoreIndexL |
|
887 // ----------------------------------------------------------------------------- |
|
888 // |
|
889 TInt CSmsDetailsPluginOneShotOperation::GetCurrentStoreIndexL( CVPbkContactLinkArray& aLinkArray ) |
|
890 { |
|
891 TInt curStoreIndex( KErrMultipleMatchFound ); |
|
892 TInt curStoreMatchCount = 0; |
|
893 RArray<TInt> otherStoreMatchIndices; |
|
894 CleanupClosePushL( otherStoreMatchIndices ); |
|
895 |
|
896 //Get the current configured contact store array(s) |
|
897 CPbk2StoreConfiguration* storeConfiguration = CPbk2StoreConfiguration::NewL(); |
|
898 CleanupStack::PushL( storeConfiguration ); |
|
899 CVPbkContactStoreUriArray* currStoreArray = storeConfiguration->CurrentConfigurationL(); |
|
900 CleanupStack::PopAndDestroy(storeConfiguration); |
|
901 |
|
902 if ( currStoreArray ) |
|
903 { |
|
904 /* Contact's store is compared against user selected stores. |
|
905 * If contact is from such store, found index is incremented |
|
906 * else, other store contact indices are populated into array for further use |
|
907 */ |
|
908 for ( TInt i = 0; i < aLinkArray.Count(); i++ ) |
|
909 { |
|
910 TVPbkContactStoreUriPtr uri = aLinkArray.At(i).ContactStore().StoreProperties().Uri(); |
|
911 if ( currStoreArray->IsIncluded( uri ) ) |
|
912 { |
|
913 // Set index to found contact and increment the count. |
|
914 curStoreIndex = i; |
|
915 curStoreMatchCount++; |
|
916 } |
|
917 else |
|
918 { |
|
919 otherStoreMatchIndices.AppendL(i); |
|
920 } |
|
921 } |
|
922 |
|
923 delete currStoreArray; |
|
924 if ( curStoreMatchCount > 1) |
|
925 { |
|
926 /* Multiple matches found from current user selected store(s) |
|
927 * Delete match from other stores in aLinkArray. New aLinkArray should only contain |
|
928 * current store contact matches, so that next level pruning can be done(e.g, names can be |
|
929 * compared and displayed if they are identical). |
|
930 */ |
|
931 for(TInt i = otherStoreMatchIndices.Count() - 1; i >= 0; i--) |
|
932 { |
|
933 aLinkArray.Delete( otherStoreMatchIndices[i] ); |
|
934 } |
|
935 curStoreIndex = KErrMultipleMatchFound; |
|
936 } |
|
937 } |
|
938 CleanupStack::PopAndDestroy( &otherStoreMatchIndices ); |
|
939 return curStoreIndex; |
|
940 } |
|
941 |
|
942 // End of File |