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) && (DialUtil::IsNumericString(*keyWord) == true))
146                         {
147                                 //search for number - update list
148                                 if (pContact->GetPhoneNumber().Contains(*__pNumberStr) == false)
149                                 {
150                                         isRemoveKey = true;
151                                 }
152                                 else
153                                 {
154                                         //update search keys.
155                                         pContact->ReplaceSearchKey(nextKey, *__pNumberStr);
156                                 }
157                         }
158                         else
159                         {
160                                 //search For name
161                                 //substring of contact name to match
162                                 String subName;
163                                 //numeric keys typed, equivalent to above 'subName'.
164                                 String numericKey;
165                                 String contactName = pContact->GetDisplayName();
166                                 contactName.SubString(0,__pNumberStr->GetLength(),subName);
167                                 DialUtil::ConvertNameToNumericKeys(subName,numericKey);
168                                 if(numericKey.Equals(*__pNumberStr) == true)
169                                 {
170                                         //update search keys.
171                                         pContact->ReplaceSearchKey(nextKey, subName);
172                                 }
173                                 else
174                                 {
175                                         isRemoveKey = true;
176                                 }
177                         }
178
179                         //remove previous search key, as updated key did'nt match.
180                         if(isRemoveKey == true)
181                         {
182                                 pContact->RemoveSearchKey(nextKey);
183                         }
184                         else
185                         {
186                                 nextKey++;
187                         }
188                 }
189
190                 //Add to contacts list to be removed
191                 if(pKeyList->GetCount() <= 0)
192                 {
193                         __pContactsList->RemoveAt(index);
194                         index--;
195                 }
196         }
197
198         //Need to sort arraylist according to latest search keys.
199         if(__pContactsList->GetCount() > 0)
200         {
201                 SuggestionListComparer* pComparer = new  (std::nothrow) SuggestionListComparer();
202                 __pContactsList->Sort(*pComparer);
203                 delete pComparer;
204         }
205         else
206         {
207                 //remove old search key
208                 delete __pNumberStr;
209                 __pNumberStr = null;
210         }
211 }
212
213 void
214 RetrieveContactsListTask::SearchContactsForNumber(void)
215 {
216         //Searches contacts by (part of)phone number.
217         IList* pResultList = __pAddressBook->SearchContactsByPhoneNumberN(*__pNumberStr);
218         if(pResultList != null && pResultList->GetCount() > 0)
219         {
220                 SaveContactsToListByNumber(*pResultList, *__pNumberStr);
221                 pResultList->RemoveAll(true);
222         }
223         delete pResultList;
224 }
225
226 void
227 RetrieveContactsListTask::SearchContactsForName(void)
228 {
229         // If phoneNumber: "23" -> Possible string matches -> 9.
230         // It is much better to do first 3(2=A,B,C) search in 1000 fields(in database), and
231         // subsequent 9 searches in locally stored array of fewer records.
232         // then to 9 search searches in 1000 fields(in database).
233
234         //Fetch matching names for mapping char for first digit, e.g. 2->ABC, 3->DEF.
235         int digit = 0;
236         String strDigit;
237         __pNumberStr->SubString(0,1,strDigit);
238         Integer::Parse(strDigit,digit);
239         IList* pNamesList = DialUtil::FetchPossibleNamesListN(L"",digit);
240         if (pNamesList != null)
241         {
242                 int totalMatches = pNamesList->GetCount();
243                 for (int index = 0; index < totalMatches; index++)
244                 {
245                         //Search All names that start with single character
246                         String* searchKey = static_cast<String*>(pNamesList->GetAt(index));
247                         SearchContactsWithInitialChar(*searchKey);
248                 }
249                 pNamesList->RemoveAll();
250                 delete pNamesList;
251         }
252 }
253
254 void
255 RetrieveContactsListTask::SearchContactsWithInitialChar(String& searchStr)
256 {
257         //search for contacts which contains 'searchStr' anywhere in displayName.
258         IList* pResultList = __pAddressBook->SearchContactsByNameN(searchStr);
259         //Check if empty list.
260         if(pResultList == null || pResultList->GetCount() <= 0)
261         {
262                 delete pResultList;
263                 return;
264         }
265
266         //further filter out result list - save contacts starting with 'searchStr'.
267         ArrayList* pDialInfoList = new (std::nothrow) ArrayList();
268         pDialInfoList->Construct();
269
270         for(int index = 0; index < pResultList->GetCount(); index++)
271         {
272                 Contact* pContact = static_cast<Contact*>(pResultList->GetAt(index));
273                 //Check if display name starts with search string
274                 String contactName;
275                 result r = pContact->GetValue(CONTACT_PROPERTY_ID_DISPLAY_NAME, contactName);
276                 if ((r == E_SUCCESS) && (contactName.StartsWith(searchStr, 0) == true))
277                 {
278                         //substring of contact name to match
279                         String subName;
280                         //numeric keys typed, equivalent to above 'subName'.
281                         String numericKey;
282                         contactName.SubString(0,__pNumberStr->GetLength(),subName);
283                         DialUtil::ConvertNameToNumericKeys(subName,numericKey);
284                         if(numericKey.Equals(*__pNumberStr) == true)
285                         {
286                                 DialContactInfo* pDialInfo = ConvertToDialContactN(*pContact,subName);
287                                 if(pDialInfo != null)
288                                 {
289                                         pDialInfoList->Add(*pDialInfo);
290                                 }
291                         }
292                 }
293         }
294         pResultList->RemoveAll(true);
295         delete pResultList;
296
297         //save list
298         if(pDialInfoList->GetCount() > 0 && __pContactsList != null)
299         {
300                 //remove duplicate items
301                 pDialInfoList->RemoveItems(*__pContactsList,true);
302                 //Add all distinct item to list
303                 __pContactsList->AddItems(*pDialInfoList);
304                 //remove ownership for all items.
305                 pDialInfoList->RemoveAll();
306         }
307         delete pDialInfoList;
308 }
309
310 void
311 RetrieveContactsListTask::SearchSpeedDialForNumberN(void)
312 {
313         //Check if only 1 digit number
314         if(__pNumberStr->GetLength() != 1)
315         {
316                 return;
317         }
318         //convert to speed dial rowId
319         int rowId;
320         Integer::Parse(*__pNumberStr, rowId);
321         if (rowId < 2 || rowId > 9)
322         {
323                 return;
324         }
325
326         //Fetch matched speed dial contact number
327         SettingsManager* pSettingsManager = SettingsManager::GetInstance();
328         String* contactNo = pSettingsManager->GetMappedSpeedDialNumberN(rowId);
329         if (contactNo == null)
330         {
331                 return;
332         }
333
334         //Fetch Contact for phoneNumber
335         IList* pContactList = __pAddressBook->SearchContactsByPhoneNumberN(*contactNo);
336         if (pContactList != null)
337         {
338                 IEnumerator* pContactsEnum = pContactList->GetEnumeratorN();
339                 while (pContactsEnum->MoveNext() == E_SUCCESS)
340                 {
341                         Contact* pContact = static_cast<Contact*>(pContactsEnum->GetCurrent());
342                         if(pContact != null)
343                         {
344                                 //save to HashMap - ownership transferred of key-value pair.
345                                 DialContactInfo* pDialContact = ConvertToDialContactN(*pContact,*contactNo);
346                                 if(pDialContact != null)
347                                 {
348                                         IList* keyList = pDialContact->GetSearchKeyList();
349                                         keyList->SetAt(*(new String(IDS_SPEED_DIAL_SEARCH_KEY)),(keyList->GetCount()-1), true);
350                                         __pContactsList->Add(pDialContact);
351                                 }
352                         }
353                 }
354
355                 //free resources
356                 delete pContactsEnum;
357                 pContactList->RemoveAll(true);
358                 delete pContactList;
359         }
360         delete contactNo;
361 }
362
363 /*void
364 RetrieveContactsListTask::SearchCallLogsForNumber(void)
365 {
366         //conversion "contactNumber" to char*
367         const wchar_t* pContact = __pNumberStr->GetPointer();
368         int len = __pNumberStr->GetLength()+1;
369         char* pNumber = new (std::nothrow) char[len];
370         wcstombs(pNumber, pContact, len);
371
372         //fetch Unknown numbers from Call logs Manager containing search string.
373         HashMapT<int, CallLogDetails>* pLogsByNumber = CallLogManager::GetInstance()->GetCallogListByUnknownNumberFromDatabaseN(pNumber);
374         if(pLogsByNumber != null)
375         {
376                 IListT<CallLogDetails>* logDetailsList = pLogsByNumber->GetValuesN();
377                 if(logDetailsList != null)
378                 {
379                         //Convert to DialContacts
380                         for(int index=0;index<logDetailsList->GetCount();index++)
381                         {
382                                 CallLogDetails logDetails;
383                                 result r = logDetailsList->GetAt(index,logDetails);
384                                 if(r == E_SUCCESS)
385                                 {
386                                         DialContactInfo* pDialContact = ConvertLogDetailsToDialContactN(logDetails);
387                                         if(pDialContact != null)
388                                         {
389                                                 if((__pContactsList->GetCount() <= 0) || (__pContactsList->Contains(*pDialContact) == false))
390                                                 {
391                                                         //Add only 'unknown' number to Map
392                                                         __pContactsList->Add(pDialContact);
393                                                 }
394                                                 else
395                                                 {
396                                                         delete pDialContact;
397                                                         pDialContact = null;
398                                                 }
399                                         }
400                                 }
401                         }
402                         delete logDetailsList;
403                 }
404                 delete pLogsByNumber;
405         }
406 }*/
407
408 void
409 RetrieveContactsListTask::SaveContactsToListByNumber(IList& saveList, String& matchedString)
410 {
411         if(__pContactsList != null)
412         {
413                 //List of contacts to be saved
414                 for (int index = 0; index < saveList.GetCount(); index++)
415                 {
416                         Contact* pContact = static_cast<Contact*>(saveList.GetAt(index));
417                         DialContactInfo* pDialContact = ConvertToDialContactN(*pContact, matchedString);
418                         if (pDialContact != null)
419                         {
420                                 //check if already present
421                                 int foundItemIndex;
422                                 if (__pContactsList->IndexOf(*pDialContact, foundItemIndex) != E_SUCCESS)
423                                 {
424                                         //save new mapping
425                                         __pContactsList->Add(pDialContact);
426                                 }
427                                 else
428                                 {
429                                         //Add search key to existing list
430                                         DialContactInfo* pExistingInfo = static_cast<DialContactInfo*>(__pContactsList->GetAt( foundItemIndex));
431                                         if (pExistingInfo != null)
432                                         {
433                                                 pExistingInfo->AddSearchKey(*(pDialContact->GetSearchKey()));
434                                         }
435                                         delete pDialContact;
436                                 }
437                         }
438                 }
439         }
440 }
441
442 DialContactInfo*
443 RetrieveContactsListTask::ConvertToDialContactN(Contact& contact, String& matchedString)
444 {
445         result r = E_FAILURE;
446
447         //display name
448         String displayName;
449         r = contact.GetValue(CONTACT_PROPERTY_ID_DISPLAY_NAME, displayName);
450         TryReturn(r == E_SUCCESS,null,"Display name not available");
451
452         //phone number
453         String phoneNumber(L"");
454         IList* pNumberList = contact.GetValuesN(CONTACT_MPROPERTY_ID_PHONE_NUMBERS);
455         if(pNumberList == null || pNumberList->GetCount() <= 0)
456         {
457                 delete pNumberList;
458                 return null;
459         }
460
461         if(DialUtil::IsNumericString(matchedString) == true)
462         {
463                 //this contact searched by phone number
464                 IEnumerator* pPhoneEnum = pNumberList->GetEnumeratorN();
465                 while (E_SUCCESS == pPhoneEnum->MoveNext())
466                 {
467                         PhoneNumber* pPhoneNumber = (PhoneNumber*) pPhoneEnum->GetCurrent();
468                         //Check if this is the correct contact
469                         if (pPhoneNumber->GetPhoneNumber().Contains(matchedString) == true)
470                         {
471                                 phoneNumber.Append(pPhoneNumber->GetPhoneNumber());
472                                 break;
473                         }
474                 }
475                 delete pPhoneEnum;
476         }
477         else
478         {
479                 //this contact searched by name
480                 PhoneNumber* pContactNo = static_cast<PhoneNumber*>(pNumberList->GetAt(0));
481                 phoneNumber.Append(pContactNo->GetPhoneNumber());
482         }
483         pNumberList->RemoveAll(true);
484         delete pNumberList;
485         pNumberList = null;
486         TryReturn((phoneNumber.IsEmpty() == false), null, "No matching phone number found");
487
488         DialContactInfo* pDialContact = new (std::nothrow) DialContactInfo();
489         pDialContact->AddSearchKey(matchedString);
490         pDialContact->SetPhoneNumber(phoneNumber);
491         pDialContact->SetDisplayName(displayName);
492
493         //Check if Thumbnail is present
494         Bitmap* pThumbnail = contact.GetThumbnailN();
495         if(pThumbnail != null)
496         {
497                 pDialContact->SetThumbnail(pThumbnail);
498         }
499         return pDialContact;
500 }
501
502 /*DialContactInfo*
503 RetrieveContactsListTask::ConvertLogDetailsToDialContactN(CallLogDetails& logDetails)
504 {
505         DialContactInfo* pDialContact = new (std::nothrow) DialContactInfo();
506         String emptyDisplayName(L"");
507         pDialContact->SetDisplayName(emptyDisplayName);
508         //call log db id
509         pDialContact->SetCallLogDbId(logDetails.GetCalllogDbId());
510
511         String phoneNumber(logDetails.GetPhoneNumber());
512         pDialContact->SetPhoneNumber(phoneNumber);
513         pDialContact->AddSearchKey(*__pNumberStr);
514         return pDialContact;
515 }*/
516
517 ///////////////////////////////////////////////
518 // SuggestionListComparer Implementation //////
519 ///////////////////////////////////////////////
520 result
521 SuggestionListComparer::Compare(const Object& objRef1, const Object& objRef2, int& cmpResult) const
522 {
523         result r = E_INVALID_ARG;
524
525         //Check if both object are of type 'DialContactInfo'
526         DialContactInfo* pDialInfo1 = dynamic_cast<DialContactInfo*>(const_cast<Object*>(&objRef1));
527         DialContactInfo* pDialInfo2 = dynamic_cast<DialContactInfo*>(const_cast<Object*>(&objRef2));
528         if((pDialInfo1 == null) || (pDialInfo2 == null))
529         {
530                 return r;
531         }
532         r = E_SUCCESS;
533
534         //Rules for comparison:-
535         //Priority 1 - show contacts for Speed dial.
536         //Priority 2 - show contacts searched by Name from Contacts.
537         //Priority 3 - show contacts searched by Number from Contacts.
538         //Priority 4 - show contacts searched by Number from Logs.
539
540         //Fetch initial search keys for both objects
541         String displayName1 = pDialInfo1->GetDisplayName();
542         String* searchKey1 = pDialInfo1->GetSearchKey();
543         bool isNumericKey1 = DialUtil::IsNumericString(*searchKey1);
544
545         String displayName2 = pDialInfo2->GetDisplayName();
546         String* searchKey2 = pDialInfo2->GetSearchKey();
547         bool isNumericKey2 = DialUtil::IsNumericString(*searchKey2);
548
549         //via Speed Dial
550         if(searchKey1->Equals(String(IDS_SPEED_DIAL_SEARCH_KEY)))
551         {
552                 cmpResult = 1;//objRef1 is searched via Speed Dial
553                 return r;
554         }
555         else if (searchKey2->Equals(String(IDS_SPEED_DIAL_SEARCH_KEY)))
556         {
557                 cmpResult = -1;//objRef2 via Speed Dial
558                 return r;
559         }
560
561         //via Logs
562         if((displayName1.IsEmpty() == false) && (displayName2.IsEmpty() == true))
563         {
564                 cmpResult = -1;//objRef1 NOT via Logs, objRef2 via Logs
565                 return r;
566         }
567         else if((displayName1.IsEmpty() == true) && (displayName2.IsEmpty() == false))
568         {
569                 cmpResult = 1;//objRef2 NOT via Logs, objRef1 via Logs
570                 return r;
571         }
572         else if((displayName1.IsEmpty() == true) && (displayName2.IsEmpty() == true))
573         {
574                 //Both via Logs - keep the same ordering as shown in logs.
575                 cmpResult = (pDialInfo2->GetCallLogDbId() - pDialInfo1->GetCallLogDbId());
576                 return r;
577         }
578
579         //If control comes here, it means both Objects are fetched from Contacts.
580         //Here, priority is first Names than Numbers
581         if((isNumericKey1 == false) && (isNumericKey2 == true))
582         {
583                 cmpResult = -1;//objRef1 via name and objRef2 via number
584                 return r;
585         }
586         else if((isNumericKey2 == false) && (isNumericKey1 == true))
587         {
588                 cmpResult = 1;//objRef2 via name and objRef1 via number
589                 return r;
590         }
591         else if((isNumericKey1 == false) && (isNumericKey2 == false))
592         {
593                 //Both searched via name - sort by alphabetic ordering of search key.
594                 cmpResult = searchKey1->CompareTo(*searchKey2);
595                 return r;
596         }
597         else
598         {
599                 //Both searched via number - keep current ordering as fetched from contacts.
600                 cmpResult = pDialInfo1->GetPhoneNumber().CompareTo(pDialInfo2->GetPhoneNumber());
601                 return r;
602         }
603
604         return E_SUCCESS;
605 }