Tizen 2.0 Release
[apps/osp/Phone.git] / src / PhnRetrieveSuggestionsTasks.cpp
1 //
2 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.0 (the License);
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://floralicense.org/license/
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an AS IS BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 /**
18  * @file    PhnRetrieveSuggestionsTasks.cpp
19  * @brief   RetrieveContactsListTask class
20  */
21 #include "PhnAppUtility.h"
22 #include "PhnCalllogManager.h"
23 #include "PhnCommonUtils.h"
24 #include "PhnDialContactInfo.h"
25 #include "PhnRetrieveSuggestionsTasks.h"
26 #include "PhnSettingsManager.h"
27 #include "PhnTypes.h"
28
29 using namespace Tizen::Base;
30 using namespace Tizen::Base::Collection;
31 using namespace Tizen::Graphics;
32 using namespace Tizen::Social;
33
34
35 RetrieveContactsListTask::RetrieveContactsListTask(ISuggestionsRetrievedEventListener& pEventListener)
36                 : __pEventListener(pEventListener), __pContactsList(null), __pNumberStr(null)
37 {
38 }
39
40 RetrieveContactsListTask::~RetrieveContactsListTask()
41 {
42         if(__pNumberStr)
43         {
44                 delete __pNumberStr;
45         }
46         if (__pAddressBook != null)
47         {
48                 delete __pAddressBook;
49         }
50 }
51
52 result
53 RetrieveContactsListTask::Construct(String& searchString,const IList* oldSearchResults)
54 {
55         result r = E_SUCCESS;
56
57         __pAddressBook = new (std::nothrow) Addressbook();
58         r = __pAddressBook->Construct();
59         TryCatch (r == E_SUCCESS,, "AddressBook.Contruct() failed");
60         //new search string and result list
61         __pNumberStr = new (std::nothrow) String(searchString);
62         __pContactsList = new (std::nothrow) ArrayList(SingleObjectDeleter);
63         r = __pContactsList->Construct();
64         TryCatch (r == E_SUCCESS,,"ArrayList.Contruct() failed");
65
66         //Copy old search result, if available
67         if(oldSearchResults != null)
68         {
69                 IEnumerator* pOldResultsEnum = oldSearchResults->GetEnumeratorN();
70                 while(pOldResultsEnum->MoveNext() == E_SUCCESS)
71                 {
72                         DialContactInfo* pContactInfo = (static_cast<DialContactInfo*>(pOldResultsEnum->GetCurrent()))->CloneN();
73                         if(pContactInfo != null)
74                         {
75                                 __pContactsList->Add(pContactInfo);
76                         }
77                 }
78                 delete pOldResultsEnum;
79         }
80
81         return r;
82
83 CATCH:
84         delete __pAddressBook;
85         delete __pNumberStr;
86         delete __pContactsList;
87         return r;
88 }
89
90 Object*
91 RetrieveContactsListTask::Run(void)
92 {
93         //check if this is a new search
94         if(__pContactsList != null && __pContactsList->GetCount() > 0)
95         {
96                 //filter out suggestion list
97                 UpdateSuggestionList();
98         }
99         else
100         {
101                 //search for Speed dial number -> 1st Priority in Result List.
102                 SearchSpeedDialForNumberN();
103
104                 if(__pNumberStr->GetLength() > 0)
105                 {
106                         //searches contacts by name -> 2nd Priority in result list
107                         SearchContactsForName();
108                         //Searches contacts by number -> 3rd Priority in result list
109                         SearchContactsForNumber();
110                 }
111                 //TODO: Search logs by number - Last Priority in the list
112                 //Only Unknown Numbers are added, since known numbers will be added from above searches
113                 /*SearchCallLogsForNumber();*/
114         }
115
116         //Inform listener about search completion.
117         __pEventListener.HandleContactsRetrievalCompleted(__pContactsList);
118         __pContactsList = null;
119         return null;
120 }
121
122 void
123 RetrieveContactsListTask::UpdateSuggestionList(void)
124 {
125         //search through each list to filter out suggestions not matching new search string.
126         for (int index = 0; index < __pContactsList->GetCount(); index++)
127         {
128                 DialContactInfo* pContact = static_cast<DialContactInfo*>(__pContactsList->GetAt(index));
129                 if(pContact == null)
130                 {//goto next record
131                         continue;
132                 }
133                 //Fetch search key list
134                 IList* pKeyList = pContact->GetSearchKeyList();
135                 int nextKey = 0;
136                 while(nextKey < pKeyList->GetCount())
137                 {
138                         bool isRemoveKey = false;
139                         //search for Speed dial
140                         String* keyWord = static_cast<String*>(pKeyList->GetAt(nextKey));
141                         if(keyWord->Equals(String(IDS_SPEED_DIAL_SEARCH_KEY)) == true && __pNumberStr->GetLength() > 1)
142                         {
143                                 isRemoveKey = true;
144                         }
145                         else if ((__pNumberStr->GetLength() > 0)
146                                         && ((DialUtil::IsNumericString(*keyWord) == true)
147                                         || keyWord->Contains("+") == true || keyWord->Contains("#") == true
148                                         || keyWord->Contains(",") == true || keyWord->Contains(";") == true))
149                         {
150                                 //search for number - update list
151                                 if (pContact->GetPhoneNumber().Contains(*__pNumberStr) == false)
152                                 {
153                                         isRemoveKey = true;
154                                 }
155                                 else
156                                 {
157                                         //update search keys.
158                                         pContact->ReplaceSearchKey(nextKey, *__pNumberStr);
159                                 }
160                         }
161                         else
162                         {
163                                 //search For name
164                                 //substring of contact name to match
165                                 String subName;
166                                 //numeric keys typed, equivalent to above 'subName'.
167                                 String numericKey;
168                                 String contactName = pContact->GetDisplayName();
169                                 contactName.SubString(0,__pNumberStr->GetLength(),subName);
170                                 DialUtil::ConvertNameToNumericKeys(subName,numericKey);
171                                 if(numericKey.Equals(*__pNumberStr) == true)
172                                 {
173                                         //update search keys.
174                                         pContact->ReplaceSearchKey(nextKey, subName);
175                                 }
176                                 else
177                                 {
178                                         isRemoveKey = true;
179                                 }
180                         }
181
182                         //remove previous search key, as updated key did'nt match.
183                         if(isRemoveKey == true)
184                         {
185                                 pContact->RemoveSearchKey(nextKey);
186                         }
187                         else
188                         {
189                                 nextKey++;
190                         }
191                 }
192
193                 //Add to contacts list to be removed
194                 if(pKeyList->GetCount() <= 0)
195                 {
196                         __pContactsList->RemoveAt(index);
197                         index--;
198                 }
199         }
200
201         //Need to sort arraylist according to latest search keys.
202         if(__pContactsList->GetCount() > 0)
203         {
204                 SuggestionListComparer* pComparer = new  (std::nothrow) SuggestionListComparer();
205                 __pContactsList->Sort(*pComparer);
206                 delete pComparer;
207         }
208         else
209         {
210                 //remove old search key
211                 delete __pNumberStr;
212                 __pNumberStr = null;
213         }
214 }
215
216 void
217 RetrieveContactsListTask::SearchContactsForNumber(void)
218 {
219         //Searches contacts by (part of)phone number.
220         IList* pResultList = __pAddressBook->SearchContactsByPhoneNumberN(*__pNumberStr);
221         if(pResultList != null && pResultList->GetCount() > 0)
222         {
223                 SaveContactsToListByNumber(*pResultList, *__pNumberStr);
224                 pResultList->RemoveAll(true);
225         }
226         delete pResultList;
227 }
228
229 void
230 RetrieveContactsListTask::SearchContactsForName(void)
231 {
232         // If phoneNumber: "23" -> Possible string matches -> 9.
233         // It is much better to do first 3(2=A,B,C) search in 1000 fields(in database), and
234         // subsequent 9 searches in locally stored array of fewer records.
235         // then to 9 search searches in 1000 fields(in database).
236
237         //Fetch matching names for mapping char for first digit, e.g. 2->ABC, 3->DEF.
238         int digit = 0;
239         String strDigit;
240         __pNumberStr->SubString(0,1,strDigit);
241         Integer::Parse(strDigit,digit);
242         IList* pNamesList = DialUtil::FetchPossibleNamesListN(L"",digit);
243         if (pNamesList != null)
244         {
245                 int totalMatches = pNamesList->GetCount();
246                 for (int index = 0; index < totalMatches; index++)
247                 {
248                         //Search All names that start with single character
249                         String* searchKey = static_cast<String*>(pNamesList->GetAt(index));
250                         SearchContactsWithInitialChar(*searchKey);
251                 }
252                 pNamesList->RemoveAll();
253                 delete pNamesList;
254         }
255 }
256
257 void
258 RetrieveContactsListTask::SearchContactsWithInitialChar(String& searchStr)
259 {
260         //search for contacts which contains 'searchStr' anywhere in displayName.
261         IList* pResultList = __pAddressBook->SearchContactsByNameN(searchStr);
262         //Check if empty list.
263         if(pResultList == null || pResultList->GetCount() <= 0)
264         {
265                 delete pResultList;
266                 return;
267         }
268
269         //further filter out result list - save contacts starting with 'searchStr'.
270         ArrayList* pDialInfoList = new (std::nothrow) ArrayList();
271         pDialInfoList->Construct();
272
273         for(int index = 0; index < pResultList->GetCount(); index++)
274         {
275                 Contact* pContact = static_cast<Contact*>(pResultList->GetAt(index));
276                 //Check if display name starts with search string
277                 String contactName;
278                 result r = pContact->GetValue(CONTACT_PROPERTY_ID_DISPLAY_NAME, contactName);
279                 if ((r == E_SUCCESS) && (contactName.StartsWith(searchStr, 0) == true))
280                 {
281                         //substring of contact name to match
282                         String subName;
283                         //numeric keys typed, equivalent to above 'subName'.
284                         String numericKey;
285                         contactName.SubString(0,__pNumberStr->GetLength(),subName);
286                         DialUtil::ConvertNameToNumericKeys(subName,numericKey);
287                         if(numericKey.Equals(*__pNumberStr) == true)
288                         {
289                                 DialContactInfo* pDialInfo = ConvertToDialContactN(*pContact,subName);
290                                 if(pDialInfo != null)
291                                 {
292                                         pDialInfoList->Add(*pDialInfo);
293                                 }
294                         }
295                 }
296         }
297         pResultList->RemoveAll(true);
298         delete pResultList;
299
300         //save list
301         if(pDialInfoList->GetCount() > 0 && __pContactsList != null)
302         {
303                 //remove duplicate items
304                 pDialInfoList->RemoveItems(*__pContactsList,true);
305                 //Add all distinct item to list
306                 __pContactsList->AddItems(*pDialInfoList);
307                 //remove ownership for all items.
308                 pDialInfoList->RemoveAll();
309         }
310         delete pDialInfoList;
311 }
312
313 void
314 RetrieveContactsListTask::SearchSpeedDialForNumberN(void)
315 {
316         //Check if only 1 digit number
317         if(__pNumberStr->GetLength() != 1)
318         {
319                 return;
320         }
321         //convert to speed dial rowId
322         int rowId;
323         Integer::Parse(*__pNumberStr, rowId);
324         if (rowId < 2 || rowId > 9)
325         {
326                 return;
327         }
328
329         //Fetch matched speed dial contact number
330         SettingsManager* pSettingsManager = SettingsManager::GetInstance();
331         String* contactNo = pSettingsManager->GetMappedSpeedDialNumberN(rowId);
332         if (contactNo == null)
333         {
334                 return;
335         }
336
337         //Fetch Contact for phoneNumber
338         IList* pContactList = __pAddressBook->SearchContactsByPhoneNumberN(*contactNo);
339         if (pContactList != null)
340         {
341                 IEnumerator* pContactsEnum = pContactList->GetEnumeratorN();
342                 while (pContactsEnum->MoveNext() == E_SUCCESS)
343                 {
344                         Contact* pContact = static_cast<Contact*>(pContactsEnum->GetCurrent());
345                         if(pContact != null)
346                         {
347                                 //save to HashMap - ownership transferred of key-value pair.
348                                 DialContactInfo* pDialContact = ConvertToDialContactN(*pContact,*contactNo);
349                                 if(pDialContact != null)
350                                 {
351                                         IList* keyList = pDialContact->GetSearchKeyList();
352                                         keyList->SetAt(*(new String(IDS_SPEED_DIAL_SEARCH_KEY)),(keyList->GetCount()-1), true);
353                                         __pContactsList->Add(pDialContact);
354                                 }
355                         }
356                 }
357
358                 //free resources
359                 delete pContactsEnum;
360                 pContactList->RemoveAll(true);
361                 delete pContactList;
362         }
363         delete contactNo;
364 }
365
366 /*void
367 RetrieveContactsListTask::SearchCallLogsForNumber(void)
368 {
369         //conversion "contactNumber" to char*
370         const wchar_t* pContact = __pNumberStr->GetPointer();
371         int len = __pNumberStr->GetLength()+1;
372         char* pNumber = new (std::nothrow) char[len];
373         wcstombs(pNumber, pContact, len);
374
375         //fetch Unknown numbers from Call logs Manager containing search string.
376         HashMapT<int, CallLogDetails>* pLogsByNumber = CallLogManager::GetInstance()->GetCallogListByUnknownNumberFromDatabaseN(pNumber);
377         if(pLogsByNumber != null)
378         {
379                 IListT<CallLogDetails>* logDetailsList = pLogsByNumber->GetValuesN();
380                 if(logDetailsList != null)
381                 {
382                         //Convert to DialContacts
383                         for(int index=0;index<logDetailsList->GetCount();index++)
384                         {
385                                 CallLogDetails logDetails;
386                                 result r = logDetailsList->GetAt(index,logDetails);
387                                 if(r == E_SUCCESS)
388                                 {
389                                         DialContactInfo* pDialContact = ConvertLogDetailsToDialContactN(logDetails);
390                                         if(pDialContact != null)
391                                         {
392                                                 if((__pContactsList->GetCount() <= 0) || (__pContactsList->Contains(*pDialContact) == false))
393                                                 {
394                                                         //Add only 'unknown' number to Map
395                                                         __pContactsList->Add(pDialContact);
396                                                 }
397                                                 else
398                                                 {
399                                                         delete pDialContact;
400                                                         pDialContact = null;
401                                                 }
402                                         }
403                                 }
404                         }
405                         delete logDetailsList;
406                 }
407                 delete pLogsByNumber;
408         }
409 }*/
410
411 void
412 RetrieveContactsListTask::SaveContactsToListByNumber(IList& saveList, String& matchedString)
413 {
414         if(__pContactsList != null)
415         {
416                 //List of contacts to be saved
417                 for (int index = 0; index < saveList.GetCount(); index++)
418                 {
419                         Contact* pContact = static_cast<Contact*>(saveList.GetAt(index));
420                         DialContactInfo* pDialContact = ConvertToDialContactN(*pContact, matchedString);
421                         if (pDialContact != null)
422                         {
423                                 //check if already present
424                                 int foundItemIndex;
425                                 if (__pContactsList->IndexOf(*pDialContact, foundItemIndex) != E_SUCCESS)
426                                 {
427                                         //save new mapping
428                                         __pContactsList->Add(pDialContact);
429                                 }
430                                 else
431                                 {
432                                         //Add search key to existing list
433                                         DialContactInfo* pExistingInfo = static_cast<DialContactInfo*>(__pContactsList->GetAt( foundItemIndex));
434                                         if (pExistingInfo != null)
435                                         {
436                                                 pExistingInfo->AddSearchKey(*(pDialContact->GetSearchKey()));
437                                         }
438                                         delete pDialContact;
439                                 }
440                         }
441                 }
442         }
443 }
444
445 DialContactInfo*
446 RetrieveContactsListTask::ConvertToDialContactN(Contact& contact, String& matchedString)
447 {
448         result r = E_FAILURE;
449
450         //display name
451         String displayName;
452         r = contact.GetValue(CONTACT_PROPERTY_ID_DISPLAY_NAME, displayName);
453         TryReturn(r == E_SUCCESS,null,"Display name not available");
454
455         //phone number
456         String phoneNumber(L"");
457         IList* pNumberList = contact.GetValuesN(CONTACT_MPROPERTY_ID_PHONE_NUMBERS);
458         if(pNumberList == null || pNumberList->GetCount() <= 0)
459         {
460                 delete pNumberList;
461                 return null;
462         }
463
464         if(DialUtil::IsNumericString(matchedString) == true)
465         {
466                 //this contact searched by phone number
467                 IEnumerator* pPhoneEnum = pNumberList->GetEnumeratorN();
468                 while (E_SUCCESS == pPhoneEnum->MoveNext())
469                 {
470                         PhoneNumber* pPhoneNumber = (PhoneNumber*) pPhoneEnum->GetCurrent();
471                         //Check if this is the correct contact
472                         if (pPhoneNumber->GetPhoneNumber().Contains(matchedString) == true)
473                         {
474                                 phoneNumber.Append(pPhoneNumber->GetPhoneNumber());
475                                 break;
476                         }
477                 }
478                 delete pPhoneEnum;
479         }
480         else
481         {
482                 //this contact searched by name
483                 PhoneNumber* pContactNo = static_cast<PhoneNumber*>(pNumberList->GetAt(0));
484                 phoneNumber.Append(pContactNo->GetPhoneNumber());
485         }
486         pNumberList->RemoveAll(true);
487         delete pNumberList;
488         pNumberList = null;
489         TryReturn((phoneNumber.IsEmpty() == false), null, "No matching phone number found");
490
491         DialContactInfo* pDialContact = new (std::nothrow) DialContactInfo();
492         pDialContact->AddSearchKey(matchedString);
493         pDialContact->SetPhoneNumber(phoneNumber);
494         pDialContact->SetDisplayName(displayName);
495
496         //Check if Thumbnail is present
497         Bitmap* pThumbnail = contact.GetThumbnailN();
498         if(pThumbnail != null)
499         {
500                 pDialContact->SetThumbnail(pThumbnail);
501         }
502         return pDialContact;
503 }
504
505 /*DialContactInfo*
506 RetrieveContactsListTask::ConvertLogDetailsToDialContactN(CallLogDetails& logDetails)
507 {
508         DialContactInfo* pDialContact = new (std::nothrow) DialContactInfo();
509         String emptyDisplayName(L"");
510         pDialContact->SetDisplayName(emptyDisplayName);
511         //call log db id
512         pDialContact->SetCallLogDbId(logDetails.GetCalllogDbId());
513
514         String phoneNumber(logDetails.GetPhoneNumber());
515         pDialContact->SetPhoneNumber(phoneNumber);
516         pDialContact->AddSearchKey(*__pNumberStr);
517         return pDialContact;
518 }*/
519
520 ///////////////////////////////////////////////
521 // SuggestionListComparer Implementation //////
522 ///////////////////////////////////////////////
523 result
524 SuggestionListComparer::Compare(const Object& objRef1, const Object& objRef2, int& cmpResult) const
525 {
526         result r = E_INVALID_ARG;
527
528         //Check if both object are of type 'DialContactInfo'
529         DialContactInfo* pDialInfo1 = dynamic_cast<DialContactInfo*>(const_cast<Object*>(&objRef1));
530         DialContactInfo* pDialInfo2 = dynamic_cast<DialContactInfo*>(const_cast<Object*>(&objRef2));
531         if((pDialInfo1 == null) || (pDialInfo2 == null))
532         {
533                 return r;
534         }
535         r = E_SUCCESS;
536
537         //Rules for comparison:-
538         //Priority 1 - show contacts for Speed dial.
539         //Priority 2 - show contacts searched by Name from Contacts.
540         //Priority 3 - show contacts searched by Number from Contacts.
541         //Priority 4 - show contacts searched by Number from Logs.
542
543         //Fetch initial search keys for both objects
544         String displayName1 = pDialInfo1->GetDisplayName();
545         String* searchKey1 = pDialInfo1->GetSearchKey();
546         bool isNumericKey1 = DialUtil::IsNumericString(*searchKey1);
547
548         String displayName2 = pDialInfo2->GetDisplayName();
549         String* searchKey2 = pDialInfo2->GetSearchKey();
550         bool isNumericKey2 = DialUtil::IsNumericString(*searchKey2);
551
552         //via Speed Dial
553         if(searchKey1->Equals(String(IDS_SPEED_DIAL_SEARCH_KEY)))
554         {
555                 cmpResult = 1;//objRef1 is searched via Speed Dial
556                 return r;
557         }
558         else if (searchKey2->Equals(String(IDS_SPEED_DIAL_SEARCH_KEY)))
559         {
560                 cmpResult = -1;//objRef2 via Speed Dial
561                 return r;
562         }
563
564         //via Logs
565         if((displayName1.IsEmpty() == false) && (displayName2.IsEmpty() == true))
566         {
567                 cmpResult = -1;//objRef1 NOT via Logs, objRef2 via Logs
568                 return r;
569         }
570         else if((displayName1.IsEmpty() == true) && (displayName2.IsEmpty() == false))
571         {
572                 cmpResult = 1;//objRef2 NOT via Logs, objRef1 via Logs
573                 return r;
574         }
575         else if((displayName1.IsEmpty() == true) && (displayName2.IsEmpty() == true))
576         {
577                 //Both via Logs - keep the same ordering as shown in logs.
578                 cmpResult = (pDialInfo2->GetCallLogDbId() - pDialInfo1->GetCallLogDbId());
579                 return r;
580         }
581
582         //If control comes here, it means both Objects are fetched from Contacts.
583         //Here, priority is first Names than Numbers
584         if((isNumericKey1 == false) && (isNumericKey2 == true))
585         {
586                 cmpResult = -1;//objRef1 via name and objRef2 via number
587                 return r;
588         }
589         else if((isNumericKey2 == false) && (isNumericKey1 == true))
590         {
591                 cmpResult = 1;//objRef2 via name and objRef1 via number
592                 return r;
593         }
594         else if((isNumericKey1 == false) && (isNumericKey2 == false))
595         {
596                 //Both searched via name - sort by alphabetic ordering of search key.
597                 cmpResult = searchKey1->CompareTo(*searchKey2);
598                 return r;
599         }
600         else
601         {
602                 //Both searched via number - keep current ordering as fetched from contacts.
603                 cmpResult = pDialInfo1->GetPhoneNumber().CompareTo(pDialInfo2->GetPhoneNumber());
604                 return r;
605         }
606
607         return E_SUCCESS;
608 }