diff -r 41a7f70b3818 -r 5266b1f337bd phoneuis/easydialing/src/easydialingplugin.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/phoneuis/easydialing/src/easydialingplugin.cpp Wed Sep 01 12:30:10 2010 +0100 @@ -0,0 +1,2359 @@ +/* +* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Easy dialing plugin. +* +*/ + +// INCLUDE FILES + +#include "easydialingplugin.h" +#include "easydialinglistbox.h" +#include "easydialinglogger.h" +#include "easydialingpanics.h" +#include "easydialingcenreplistener.h" +#include "easydialingcontactdatamanager.h" +#include "easydialingutils.h" +#include "easydialingcommands.hrh" +#include + +// AVKON and drawing header files +#include +#include +#include +#include +#include + +#include + +// Predictive search header files +#include +#include +#include +#include +#include +#include + +// Virtual phonebook header files +#include +#include // contains virtual phonebook data fields +#include + +// CCA Launcher header files. +#include +#include +#include +#include + +// Service provider settings api +#include + +#include + +// CCA contactor service. +#include "edcontactorservice.h" + +// Open system trace +#include "OstTraceDefinitions.h" +#ifdef OST_TRACE_COMPILER_IN_USE +#include "easydialingpluginTraces.h" +#endif + +#include +#include // for BaflUtils + +// Transition effects +#include + +// EXTERNAL DATA STRUCTURES + +// EXTERNAL FUNCTION PROTOTYPES + +// CONSTANTS +const TInt KEDMaximumMatchingContactsCount = 100; +const TInt KEasyDialingListBoxModelGranularity = 8; +const TInt KEasyDialingMaximumMatchingParts = 10; +const TInt KErrEasyDialingNoFirstNamePCSIndexing = -2000; +const TInt KErrEasyDialingNoLastNamePCSIndexing = -2001; + +const TInt KEDFlushContactDataManagerLimit = 20; + +const TText KHighlightSeparatorChar = 0x1F; +_LIT(KHighlightSeparatorCharAsLiteral,"\x1F"); +const TText KListFieldSeparatorChar = '\t'; + +const TText KArabicAndSouthEastAsianRangeStart = 0x0600; +const TText KArabicAndSouthEastAsianRangeEnd = 0x19FF; +const TText KArabicPresentationFormsARangenStart = 0xFB50; +const TText KArabicPresentationFormsARangeEnd = 0xFDFF; +const TText KArabicPresentationFormsBRangenStart = 0xFE70; +const TText KArabicPresentationFormsBRangeEnd = 0xFEFF; + +const TInt KMaxRunInfoArrayCount = 20; + +// Transition context_uid for dialer_list component effects +#define KGfxContactListBoxUid TUid::Uid( 0x2000B47B ) +const TInt KGfxContactListBoxOpenEffect = 3; +const TInt KGfxContactListBoxCloseEffect = 4; + +// MACROS + +// LOCAL CONSTANTS AND MACROS +_LIT(KResourceFile, "\\resource\\easydialingpluginresources.rsc"); + +// MODULE DATA STRUCTURES + +// LOCAL FUNCTION PROTOTYPES + +TInt CompareTPsMatchLocation( const TPsMatchLocation& a1, const TPsMatchLocation& a2 ); + +void AppendStringWithMatchDataL( + TDes& aBuffer, + const TDesC& aText, + CPSRequestHandler* aPSHandler, + const CPsQuery* aPSQuery ); + +TBool HighlightingSupportedForText( const TDesC& aText ); + +inline TBool HighlightingSupportedForScript( TText aChar ); + +TBool IsStrictlyBidirectional( const TDesC& aText ); + +static HBufC* AllocWithoutHighlightSeparatorsLC( const TDesC& aDesc ); + +static TBool IsItuTCharacter( TChar aChar ); + +static TInt Find( const MVPbkContactLink* aLink, const RPointerArray& aArray ); + +template +inline void CleanupResetAndDestroyPushL( T& aRef ); + + + +// ----------------------------------------------------------------------------- +// CEasyDialingPlugin +// The default c++ constructor +// ----------------------------------------------------------------------------- +// +CEasyDialingPlugin::CEasyDialingPlugin() + : iActionToBeLaunched( ENoActionDefined ) + { + } + +// ----------------------------------------------------------------------------- +// NewL +// Create instance of concrete ECOM interface implementation. +// ----------------------------------------------------------------------------- +// +CEasyDialingPlugin* CEasyDialingPlugin::NewL() + { + OstTrace0( TRACE_NORMAL, CEASYDIALINGPLUGIN_NEWL_LOAD_PLUGIN, "Loading plugin.." ); + LOGSTRING("EasyDialingPlugin: Loading plugin.."); + + CEasyDialingPlugin* self = new (ELeave) CEasyDialingPlugin; + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + + OstTrace0( TRACE_NORMAL, CEASYDIALINGPLUGIN_NEWL_LOAD_PLUGIN_OK, "Loading plugin completed succesfully" ); + LOGSTRING("EasyDialingPlugin: Loading plugin completed succesfully"); + return self; + } + +// ----------------------------------------------------------------------------- +// ConstructL +// Main construction handled here. Creates connection to the predictive search +// engine and initialises all member data. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::ConstructL() + { + iLongTapped = EFalse; + // Create contact data manager. + iContactDataManager = CEasyDialingContactDataManager::NewL(); + iContactDataManager->SetObserver( this ); + + // Find a handle to ca launcher extension MCCAConnectionExt. + // Easydialing has to use the extension API, because it needs function CloseAppL + // only found in extension. + // MCCAConnection extension has to be obtained through MCCAParameter extension, + // since MCCAConnection is not designed to be extensible API. + MCCAParameter* parameter = TCCAFactory::NewParameterL(); + TAny* any = parameter->CcaParameterExtension( KMCCAConnectionExtUid ); + + // Parameter can be deallocated since "any" containing pointer to contact launcher + // is not tied to parameter in any way. + parameter->Close(); + + // Cast the pointer into contact launcher object. + User::LeaveIfNull( any ); + iContactLauncher = static_cast( any ); + + iCenrepListener = CEasyDialingCenrepListener::NewL( this ); + + iContactorService = CEDContactorService::NewL( this ); + + // EasyDialing resource file is attempted to be read from the same directory where the + // executed binary is located + TFileName dllFileName; + Dll::FileName( dllFileName ); + + TParse parse; + User::LeaveIfError( parse.Set(KResourceFile, &dllFileName, NULL) ); + TFileName resourceFileName( parse.FullName() ); + + BaflUtils::NearestLanguageFile( iCoeEnv->FsSession(), resourceFileName ); + iResourceFileOffset = iCoeEnv->AddResourceFileL( resourceFileName ); + + SetComponentsToInheritVisibility( ETrue ); + } + +// ----------------------------------------------------------------------------- +// ~CEasyDialingPlugin +// The desctructor +// ----------------------------------------------------------------------------- +// +CEasyDialingPlugin::~CEasyDialingPlugin() + { + iObservers.Reset(); + + delete iCenrepListener; + delete iContactDataManager; + delete iPredictiveSearchQuery; + iContactDataStores.ResetAndDestroy(); + + if ( iPredictiveContactSearchHandler ) + { + iPredictiveContactSearchHandler->RemoveObserver(this); + } + delete iPredictiveContactSearchHandler; + + delete iListBoxModel; + + if ( iContactListBox ) + { + GfxTransEffect::Deregister( iContactListBox ); + } + + delete iInfoLabelLine1; + delete iInfoLabelLine2; + delete iInfoLabelTextLine1; + delete iInfoLabelTextLine2; + + delete iContactListBox; + + if ( iContactLauncher ) + { + iContactLauncher->Close(); + } + + delete iContactorService; + + iCoeEnv->DeleteResourceFile( iResourceFileOffset ); + + delete iInputBlocker; + + if ( iAsyncCallBack ) + { + iAsyncCallBack->Cancel(); + } + delete iAsyncCallBack; + + delete iContactToBeLaunched; + delete iContactToBeLaunchedName; + + OstTrace0( TRACE_NORMAL, CEASYDIALINGPLUGIN_UNLOAD_PLUGIN, "Plugin unloaded" ); + LOGSTRING("EasyDialingPlugin: Plugin unloaded"); + } + + +// ----------------------------------------------------------------------------- +// InitializeL +// Initialises easy dialing. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::InitializeL( CCoeControl& aParent ) + { + SetContainerWindowL( aParent ); + SetMopParent( &aParent ); + + TCallBack asyncCallBack( AsyncCallBackToLaunchAction, this ); + iAsyncCallBack = new ( ELeave ) CAsyncCallBack( asyncCallBack, + CActive::EPriorityStandard ); + + iContactListBox = new (ELeave) CEasyDialingListBox(); + iListBoxModel = new(ELeave) CDesCArrayFlat( KEasyDialingListBoxModelGranularity ); + + iContactListBox->ConstructL( CEikListBox::EPaintedSelection + | CEikListBox::ENoFirstLetterMatching + | CEikListBox::EDisableItemSpecificMenu, + iContactDataManager ); + iContactListBox->SetListBoxObserver(this); + + iContactListBox->SetMopParent(this); + + CTextListBoxModel* model = iContactListBox->Model(); + model->SetItemTextArray( iListBoxModel ); + model->SetOwnershipType( ELbmDoesNotOwnItemArray ); + + iContactListBox->MakeVisible( EFalse ); + + SetFocus( EFalse ); + iContactListBox->ActivateL(); + + _LIT( KEDNewLine, "\n" ); + HBufC* infoLabelText = StringLoader::LoadLC( R_QTN_EASYDIAL_ENTER_NUMBER, iCoeEnv ); + TInt newLine = infoLabelText->Find( KEDNewLine ); + if ( newLine == KErrNotFound ) + { + iInfoLabelTextLine1 = infoLabelText; + CleanupStack::Pop( infoLabelText ); + iInfoLabelTextLine2 = HBufC::NewL(0); + } + else + { + iInfoLabelTextLine1 = infoLabelText->Left( newLine ).AllocL(); + iInfoLabelTextLine2 = infoLabelText->Mid( newLine + 1 ).AllocL(); + CleanupStack::PopAndDestroy( infoLabelText ); + } + + iInfoLabelLine1 = new( ELeave ) CEikLabel; + iInfoLabelLine1->SetContainerWindowL( *this ); + iInfoLabelLine1->SetParent( this ); + iInfoLabelLine1->SetMopParent( this ); + iInfoLabelLine1->SetLabelAlignment( ELayoutAlignCenter ); + iInfoLabelLine1->SetTextL( *iInfoLabelTextLine1 ); + iInfoLabelLine1->ActivateL(); + + iInfoLabelLine2 = new( ELeave ) CEikLabel; + iInfoLabelLine2->SetContainerWindowL( *this ); + iInfoLabelLine2->SetParent( this ); + iInfoLabelLine2->SetMopParent( this ); + iInfoLabelLine2->SetLabelAlignment( ELayoutAlignCenter ); + iInfoLabelLine2->SetTextL( *iInfoLabelTextLine2 ); + iInfoLabelLine2->ActivateL(); + + SetInfoLabelColourL(); + + GfxTransEffect::Register( iContactListBox, + KGfxContactListBoxUid, EFalse ); + + iCoeEnv->AddForegroundObserverL( *this ); + + // Do delayed initialization of PCS. PCS constructions takes a long time. + // On the other hand, easy dialing initialization is done in phone application + // constructor, so it contributes the whole system boot time. These are good + // reasons not to construct PCS in easy dialing constructor, but do it later as + // an asynchronous operation. + AsyncActionLaunchL( EInitializePcs ); + } + + +// ----------------------------------------------------------------------------- +// Reset +// Resets easydialing plugin. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::Reset() + { + // PCS searches completing after the Reset() call must be discarded + iDiscardCompletingSearches = ETrue; + + iNewSearchNeeded = EFalse; + iSearchString.Zero(); + iListBoxModel->Reset(); + iNumberOfNames = 0; + iContactListBox->MakeVisible( EFalse ); + iContactDataManager->Reset(); + if ( IsFocused() ) + { + SetFocus( EFalse ); + } + } + + +// ----------------------------------------------------------------------------- +// InitPredictiveContactSearchL +// Initialises predictive contact search. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::InitPredictiveContactSearchL() + { + iPredictiveContactSearchHandler = CPSRequestHandler::NewL(); + iPredictiveContactSearchHandler->AddObserverL(this); + + // Check which relevant contact fields are indexed in PCS search. + iFirstNamePCSIndex = FindContactFieldPCSIndexL( R_VPBK_FIELD_TYPE_FIRSTNAME ); + iLastNamePCSIndex = FindContactFieldPCSIndexL( R_VPBK_FIELD_TYPE_LASTNAME ); + iCompanyNamePCSIndex = FindContactFieldPCSIndexL( R_VPBK_FIELD_TYPE_COMPANYNAME ); + + // If first name and last name are not indexed in PCS, easy dialing plugin + // can not function reasonably. The function leaves, which in turn causes + // the plugin not to be initialized. + // Company name not being indexed is not as severe case, and does not cause a leave. + // Fields used in PCS indexing are configured in cenrep 2000B5C6. + if ( iFirstNamePCSIndex == KErrNotFound ) + { + OstTrace0( TRACE_ERROR, CEASYDIALINGPLUGIN_INITPREDICTIVECONTACTSEARCHL_ERROR_NO_FIRST_NAME, "ERROR, PCS does not support first name indexing!" ); + LOGSTRING("EasyDialingPlugin: PCS does not support first name indexing => Leave"); + User::Leave( KErrEasyDialingNoFirstNamePCSIndexing ); + } + if ( iLastNamePCSIndex == KErrNotFound ) + { + OstTrace0( TRACE_ERROR, CEASYDIALINGPLUGIN_INITPREDICTIVECONTACTSEARCHL_ERROR_NO_LAST_NAME, "ERROR, PCS does not support last name indexing!" ); + LOGSTRING("EasyDialingPlugin: PCS does not support last name indexing => Leave"); + User::Leave( KErrEasyDialingNoLastNamePCSIndexing ); + } + + SetupPcsSettingsL(); + + iPredictiveSearchQuery = CPsQuery::NewL(); + } + +// ----------------------------------------------------------------------------- +// SetupPcsSettingsL +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::SetupPcsSettingsL() + { + // Get current store configuration from Phonebook settings + iContactDataStores.ResetAndDestroy(); + iContactDataManager->GetCurrentStoreUrisL( iContactDataStores ); + + // Put the searched contact fields into array. + RArray contact_fields; + CleanupClosePushL(contact_fields); + + // First name, last name and company name (if supported) are used in PCS search. + contact_fields.Append( R_VPBK_FIELD_TYPE_FIRSTNAME ); + contact_fields.Append( R_VPBK_FIELD_TYPE_LASTNAME ); + contact_fields.Append( R_VPBK_FIELD_TYPE_COMPANYNAME ); + + // Create and fill PS settings object. + CPsSettings* ps_settings = CPsSettings::NewL(); + CleanupStack::PushL( ps_settings ); + + ps_settings->SetSearchUrisL( iContactDataStores ); + ps_settings->SetMaxResults( KEDMaximumMatchingContactsCount ); + ps_settings->SetSortType( EAlphabetical ); + ps_settings->SetDisplayFieldsL( contact_fields ); + + // Set the PCS settings. + iPredictiveContactSearchHandler->SetSearchSettingsL( *ps_settings ); + + CleanupStack::PopAndDestroy( ps_settings ); + CleanupStack::PopAndDestroy( &contact_fields ); + + // Set the sort order. This must happen after the contact store settings + // are up-to-date. + SetSortOrderL( iContactDataManager->NameOrder() ); + } + +// ----------------------------------------------------------------------------- +// SetSortOrderL +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::SetSortOrderL( CEasyDialingContactDataManager::TNameOrder aNameOrder ) + { + RArray fields; + CleanupClosePushL( fields ); + if ( aNameOrder == CEasyDialingContactDataManager::EFirstnameLastname ) + { + fields.AppendL( R_VPBK_FIELD_TYPE_FIRSTNAME ); + fields.AppendL( R_VPBK_FIELD_TYPE_LASTNAME ); + } + else + { + fields.AppendL( R_VPBK_FIELD_TYPE_LASTNAME ); + fields.AppendL( R_VPBK_FIELD_TYPE_FIRSTNAME ); + } + fields.AppendL( R_VPBK_FIELD_TYPE_COMPANYNAME ); + + // Set the same order for each contact store. PCS automatically ignores + // fields not supported by the given store. + for ( TInt i = 0 ; i < iContactDataStores.Count() ; ++i ) + { + iPredictiveContactSearchHandler->ChangeSortOrderL( *iContactDataStores[i], fields ); + } + CleanupStack::PopAndDestroy( &fields ); + } + +// ----------------------------------------------------------------------------- +// OfferKeyEventL +// Check the received keypad event and performs user +// actions related to it. +// ----------------------------------------------------------------------------- +// +TKeyResponse CEasyDialingPlugin::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType) + { + TKeyResponse keyResponse = EKeyWasNotConsumed; + + if ( aType != EEventKey ) + { + return keyResponse; + } + + TInt keyCode = aKeyEvent.iCode; + + // Swap right and left key codes in mirrored layout. + if ( AknLayoutUtils::LayoutMirrored() ) + { + if ( keyCode == EKeyRightArrow ) keyCode = EKeyLeftArrow; + else if ( keyCode == EKeyLeftArrow ) keyCode = EKeyRightArrow; + } + + + if ( IsFocused() ) + { + if ( keyCode == EKeyUpArrow ) + { + // if the focus is on the top-most item + if ( iContactListBox->CurrentItemIndex() == 0) + { + // then focus jumps off the component. + SetFocus( EFalse ); + DrawDeferred(); + keyResponse = EKeyWasConsumed; + } + else + { + keyResponse = iContactListBox->OfferKeyEventL( aKeyEvent, aType ); + } + } + else if ( keyCode == EKeyDownArrow ) + { + // if the focus is on the bottom-most item + if ( iContactListBox->CurrentItemIndex() == (iNumberOfNames - 1) ) + { + // then focus jumps off the component. + SetFocus( EFalse ); + DrawDeferred(); + keyResponse = EKeyWasConsumed; + } + else + { + keyResponse = iContactListBox->OfferKeyEventL( aKeyEvent, aType ); + } + } + + else if ( keyCode == EKeyRightArrow ) + { + iRememberFocus = ETrue; + AsyncActionLaunchL( ELaunchCurrentContact ); + keyResponse = EKeyWasConsumed; + } + + else if ( keyCode == EKeyLeftArrow ) + { + // then focus jumps off the component. + SetFocus( EFalse ); + DrawDeferred(); + keyResponse = EKeyWasConsumed; + } + + else + { + // then focus jumps off the component. + SetFocus( EFalse ); + + // FEP hasn't had chance to handle this key event as focus was not in the editor field. + // Give that chance now by consuming original event and simulating a new one. + // The simulation must be asynchronous because FEP focusing state is updated + // with high priority active object. + // As an exception, dialer simulated keys are given directly to editor. They are + // not handled by FEP anyway, and asynchronous handling would be problematic for + // the * key multitapping logic of Phone. + TBool simulatedByDialer = (aKeyEvent.iModifiers&EModifierNumLock) && + (aKeyEvent.iModifiers&EModifierKeypad); + if ( !simulatedByDialer ) + { + keyResponse = EKeyWasConsumed; + AsyncSimulateKeyEvent( aKeyEvent ); + } + } + } + + else if ( iNumberOfNames > 0 ) // not in focus but there are contacts to show + { + if ( keyCode == EKeyUpArrow || keyCode == EKeyDownArrow ) + { + SetFocus( ETrue ); + keyResponse = iContactListBox->SetFocusedWithKeyEventL( aKeyEvent, aType ); + } + } + return keyResponse; + } + + +// ----------------------------------------------------------------------------- +// CountComponentControls +// +// ----------------------------------------------------------------------------- +// +TInt CEasyDialingPlugin::CountComponentControls() const + { + return iNumberOfNames > 0 ? 3 : 2; + } + + +// ----------------------------------------------------------------------------- +// ComponentControl +// +// ----------------------------------------------------------------------------- +// +CCoeControl* CEasyDialingPlugin::ComponentControl( TInt aIndex ) const + { + switch ( aIndex ) + { + case 0: + return iInfoLabelLine1; + case 1: + return iInfoLabelLine2; + case 2: + return iContactListBox; + default: + return NULL; + } + } + + +// ----------------------------------------------------------------------------- +// MakeVisible +// +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::MakeVisible( TBool aVisible ) + { + if ( aVisible != IsVisible() ) + { + CCoeControl::MakeVisible( aVisible ); + + if ( !aVisible && IsFocused() ) + { + SetFocus( EFalse ); + } + } + } + +// ----------------------------------------------------------------------------- +// HandleResourceChange +// +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::HandleResourceChange( TInt /*aType*/ ) + { + TRAP_IGNORE( SetInfoLabelColourL() ); + } + +// ----------------------------------------------------------------------------- +// SizeChanged +// +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::SizeChanged() + { + TRect rect = Rect(); + // convert rect to absolute coordinates + rect.SetRect( PositionRelativeToScreen(), rect.Size() ); + + iContactListBox->SetMaxRect( rect ); + iContactListBox->SetRectToNumberOfItems( iNumberOfNames ); + + TInt itemToMakeVisible = iContactListBox->CurrentItemIndex(); + if ( itemToMakeVisible == KErrNotFound && iNumberOfNames ) + { + // if no current item is set, make sure that the list is then + // scrolled to the bottom. + itemToMakeVisible = iNumberOfNames - 1; + } + iContactListBox->ScrollToMakeItemVisible( itemToMakeVisible ); + + // get info label locations and fonts from layout + TRect baseRect = Rect(); + TAknTextComponentLayout labelLayoutLine1 = + AknLayoutScalable_Avkon::main_pane_empty_t1( 0 ); + TAknTextComponentLayout labelLayoutLine2 = + AknLayoutScalable_Avkon::main_pane_empty_t2( 0 ); + AknLayoutUtils::LayoutLabel( iInfoLabelLine1, baseRect, labelLayoutLine1 ); + AknLayoutUtils::LayoutLabel( iInfoLabelLine2, baseRect, labelLayoutLine2 ); + + // the layouts used place the text too low, so center the labels vertically + TInt labelHeight = iInfoLabelLine2->Rect().iBr.iY - iInfoLabelLine1->Rect().iTl.iY; + TInt centeredTop = ( baseRect.Height() - labelHeight ) / 2; + TInt offset = centeredTop - iInfoLabelLine1->Rect().iTl.iY; + iInfoLabelLine1->SetPosition( TPoint( iInfoLabelLine1->Rect().iTl.iX, + iInfoLabelLine1->Rect().iTl.iY + offset ) ); + iInfoLabelLine2->SetPosition( TPoint( iInfoLabelLine2->Rect().iTl.iX, + iInfoLabelLine2->Rect().iTl.iY + offset ) ); + + TRAP_IGNORE( SetInfoLabelColourL() ); + } + + +// ----------------------------------------------------------------------------- +// FocusChanged +// +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::FocusChanged( TDrawNow aDrawNow ) + { + iContactListBox->SetFocus( IsFocused() ); + if ( !IsFocused() ) + { + // To be on the safe side, cancel async callback and reset input block. + CancelActionLaunchAndInputBlock(); + iContactListBox->View()->ItemDrawer()->ClearFlags( CListItemDrawer::ESingleClickDisabledHighlight ); + } + CCoeControl::FocusChanged( aDrawNow ); + InformObservers( MDialingExtensionObserver::EFocusChanged ); + } + + +// ----------------------------------------------------------------------------- +// SetInputL +// +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::SetInputL( const TDesC& aSearchString ) + { + if ( !IsEnabled() ) + { + return; + } + OstTraceExt1( TRACE_NORMAL, CEASYDIALINGPLUGIN_SET_INPUT, "SetInput: '%S' ", aSearchString ); + LOGSTRING1("EasyDialingPlugin: SetInput: '%S'", &aSearchString ); + + // Earlier here was a test if the search string is the same as the one used in previous search. + // If it was, no search was done but function returned immediately. + // This has now been removed. It is possible that contacts have changed, and a new search + // may be needed even if the search string is the same. + + // Check if search string is just empty space or 0-length. Decision was made that no + // matches are shown if search string is just space (would show all contacts) + // even if somebody might have unnamed contacts. Should be quite rare. + // It's assumed later on in the code that string is not just empty space. + TLex searchString( aSearchString ); + searchString.SkipSpace(); + if ( searchString.Eos() ) // the end of the string reached after skipping empty space + { + // It's assumed here that if we get here due to switching to call + // handling etc, then dialer and thus ed is already set invisible + // and effect will not be shown as listbox is already invisible. + // However if user empties number entry, then it's feasible to show + // effect. + HideContactListBoxWithEffect(); + SetInfoLabelVisibleL( ETrue ); + Reset(); + } + else // proper search string + { + SetInfoLabelVisibleL( EFalse ); + iSearchString.Copy( aSearchString.Left( iSearchString.MaxLength() ) ); + LaunchSearchL(); + } + } + + +// ----------------------------------------------------------------------------- +// MatchingContactCount +// +// ----------------------------------------------------------------------------- +// +TInt CEasyDialingPlugin::MatchingContactCount() + { + return iNumberOfNames; + } + + +// ----------------------------------------------------------------------------- +// EasyDialingSettingsChanged +// From MEasyDialingCenrepListenerObserver +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::EasyDialingSettingsChanged( TInt aValue ) + { + if ( aValue == 0 ) + { + Reset(); + InformObservers( MDialingExtensionObserver::EEasyDialingDisabled ); + TRAP_IGNORE( SetInfoLabelVisibleL( EFalse ) ); + } + else if ( aValue == 1 ) + { + InformObservers( MDialingExtensionObserver::EEasyDialingEnabled ); + if ( iSearchString.Length() == 0 ) + { + TRAP_IGNORE( SetInfoLabelVisibleL( ETrue ) ); + } + } + MakeVisible( aValue ); + } + +// ----------------------------------------------------------------------------- +// EasyDialingContactThumbnailsSettingsChanged +// From MEasyDialingCenrepListenerObserver +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::EasyDialingContactThumbnailsSettingsChanged( TInt aThumbnailSettingValue ) + { + iContactDataManager->SetContactThumbnailSetting( aThumbnailSettingValue ); + iContactDataManager->Reload(); + DrawDeferred(); + } + +// ----------------------------------------------------------------------------- +// AllContactDataLoaded +// From MContactDataManagerObserver +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::AllContactDataLoaded() + { + iContactListBox->DrawDeferred(); + } + +// ----------------------------------------------------------------------------- +// NameOrderChanged +// From MContactDataManagerObserver +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::NameOrderChanged() + { + if ( iPredictiveContactSearchHandler ) + { + TRAP_IGNORE( SetSortOrderL( iContactDataManager->NameOrder() ) ); + // Refresh current results if needed + TRAP_IGNORE( DoHandleContactsChangedL() ); + } + } + +// ----------------------------------------------------------------------------- +// FavouritesChanged +// From MContactDataManagerObserver +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::FavouritesChanged() + { + TRAP_IGNORE( DoHandleContactsChangedL() ); + } + +// ----------------------------------------------------------------------------- +// StoreConfigurationChanged +// From MContactDataManagerObserver +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::StoreConfigurationChanged() + { + TRAP_IGNORE( + // Reconfigure PCS to update its store settings + SetupPcsSettingsL(); + // Contacts available have changed and a new search is needed if we have + // previous results shown. + DoHandleContactsChangedL() + ); + } + +// ----------------------------------------------------------------------------- +// InformContactorEvent +// From MEDContactorObserver +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::InformContactorEvent( MEDContactorObserver::TEvent aEvent ) + { + // This callback function simply propagates the events to its own listener. + switch ( aEvent ) + { + case MEDContactorObserver::ECommunicationStarted: + InformObservers( MDialingExtensionObserver::ECommunicationStarted ); + break; + case MEDContactorObserver::ECommunicationCancelled: + InformObservers( MDialingExtensionObserver::ECommunicationCancelled ); + break; + default: + break; + } + + // Reset focus unless it is flagged to be remembered. + if ( !iRememberFocus ) + { + SetFocus( EFalse ); + DrawDeferred(); + } + + iRememberFocus = EFalse; + } + +// ----------------------------------------------------------------------------- +// Draw +// The actual UI drawing function. Draws the easy dialing popup bubble on the +// idle screen. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::Draw( const TRect& /* aRect */ ) const + { + } + + +// ----------------------------------------------------------------------------- +// LaunchSearchL +// Initiates predictive contact search. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::LaunchSearchL() + { + if ( iSearchString.Length() == 0 || !iPredictiveContactSearchHandler ) + { + return; + } + + iDiscardCompletingSearches = EFalse; + iNewSearchNeeded = EFalse; + + iPredictiveSearchQuery->Reset(); + + for ( TInt i = 0; i < iSearchString.Length(); i++ ) + { + // Add a query item + CPsQueryItem* item = CPsQueryItem::NewL(); + CleanupStack::PushL(item); + + item->SetCharacter( iSearchString[i] ); + + // The PCS mode used with character is based on character itself, + // not to the keyboard it is made with. While this is not strictly + // identical to checking the used keyboard, this behaves identically + // in most of the normal cases, and makes the logic simpler. + // In case of hybrid mode keyboard, use always predictive default + // keyboard. + if ( iKeyboardMode == EDefaultKeyboard && + IsItuTCharacter( iSearchString[i] ) ) + { + item->SetMode( EPredictiveItuT ); + } + else + { + item->SetMode( EPredictiveDefaultKeyboard ); + } + + iPredictiveSearchQuery->AppendL(*item); + + // Previous CPsQuery::AppendL takes the ownership of item. + // Do not delete item. + CleanupStack::Pop(item); + } + + // Issue the search. SearchL is asynchronous function => returns immediately. + iPredictiveContactSearchHandler->SearchL(*iPredictiveSearchQuery); + } + + +// ----------------------------------------------------------------------------- +// HandlePsResultsUpdate from MPsResultsObserver +// Called after predictive search has been completed. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::HandlePsResultsUpdate( RPointerArray& aResults, RPointerArray& aSeqs ) + { + if ( !IsEnabled() ) + { + // Easydialing is off. We should arrive here only if user turned it off while a search was happening. + Reset(); + return; + } + TRAPD( leaveError, HandlePsResultsUpdateL( aResults, aSeqs ) ); + + if ( leaveError ) + { + OstTrace1( TRACE_ERROR, CEASYDIALINGPLUGIN_HANDLEPSRESULTSUPDATE, "HandlePsResultsUpdate failed: %d", leaveError ); + LOGSTRING1("EasyDialingPlugin: HandlePsResultsUpdate failed: %d", leaveError ); + } + } + + +// ----------------------------------------------------------------------------- +// HandlePsError from MPsResultsObserver +// Called when search error happens. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::HandlePsError( TInt aErrorCode ) + { + OstTrace1( TRACE_ERROR, CEASYDIALINGPLUGIN_HANDLEPSERROR, "PCS Error: %d", aErrorCode ); + LOGSTRING1("EasyDialingPlugin: PCS Error: %d", aErrorCode ); + } + +// ----------------------------------------------------------------------------- +// CachingStatus from MPsResultsObserver +// Called to update caching status. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::CachingStatus( TCachingStatus& aStatus, TInt& aError ) + { + OstTraceExt2( TRACE_NORMAL, CEASYDIALINGPLUGIN_CACHINGSTATUS, "PCS CachingStatus: %d, error: %d", ( TUint )( aStatus ), aError ); + + switch ( aStatus ) + { + case ECacheUpdateContactRemoved: + case ECacheUpdateContactModified: + case ECacheUpdateContactAdded: + TRAP_IGNORE( DoHandleContactsChangedL() ); + break; + default: + break; + } + + LOGSTRING2("EasyDialingPlugin: PCS CachingStatus: %d, error: %d", aStatus, aError ); + } + + +// ----------------------------------------------------------------------------- +// FindContactFieldPCSIndex +// Searches the index that stores the given contact field. +// To see possible contact field numbering, see file "vpbkeng.rsg". +// ----------------------------------------------------------------------------- +// +TInt CEasyDialingPlugin::FindContactFieldPCSIndexL( TInt aIndex ) + { + RArray fieldOrder; + CleanupClosePushL( fieldOrder ); + + // PCS uses the same data plugin for all the Phonebook databases + // (i.e. phone contacts, SIM contacts, SDN contacts), and there can be only + // one data order per plugin. Thus, data order for all databases supported + // by us is the same, and it's enough to ask data order just for the default CDB. + const TDesC& defaultCdb = VPbkContactStoreUris::DefaultCntDbUri(); + + iPredictiveContactSearchHandler->GetDataOrderL( defaultCdb, fieldOrder ); + TInt pcsIndex = fieldOrder.Find( aIndex ); + + CleanupStack::PopAndDestroy( &fieldOrder ); + return pcsIndex; + } + + +// ----------------------------------------------------------------------------- +// HandlePsResultsUpdateL +// This function does the actual callback fork, and must be trapped inside +// HandlePsResultsUpdateL. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::HandlePsResultsUpdateL( RPointerArray& aResults, RPointerArray& /*aSeqs*/ ) + { + // If iDiscardCompletingSearches is ETrue, don't show search results but just return. + // If easydialing has been reset with reset function, no searches completed after that + // can be shown. + // This is done like this because there seems not to be a feasible way of cancelling all + // PCS search request in PCS API. CancelRequest does not work here. + if ( iDiscardCompletingSearches ) + { + return; + } + + iListBoxModel->Reset(); + iContactListBox->HandleItemRemovalL(); + + TInt numberOfPCSMatches( aResults.Count() ); + + // Resetting the contactdatamanager causes the thumbnails to flicker a bit, because + // we need to load the thumbnails again. But if there are lots of search results, or + // lots of loaded thumbnails in the manager, matching the search results to the + // thumbnail manager contents may take too much time. If there are lots of results, + // it is likely that the contents of the listbox change anyway, so the flickering + // doesn't matter too much. This also limits the memory usage of the manager. + // But if there are fewer search results, it is worth trying to match them + // with the contents of thumbnail manager. + + TBool matchThumbnails(ETrue); + if ( numberOfPCSMatches > KEDFlushContactDataManagerLimit ) + { + iContactDataManager->Reset(); + matchThumbnails = EFalse; + } + + OstTrace1( TRACE_NORMAL, CEASYDIALINGPLUGIN_HANDLEPSRESULTSUPDATEL_MATCHES, "Matching results from PCS: %d", numberOfPCSMatches ); + LOGSTRING1("EasyDialingPlugin: Matching results from PCS: %d", numberOfPCSMatches ); + + // retrieve the name order before adding + CEasyDialingContactDataManager::TNameOrder nameOrder = iContactDataManager->NameOrder(); + + RPointerArray favsFoundByPcs; + CleanupResetAndDestroyPushL( favsFoundByPcs ); + + // Update the model + // ---------------- + for ( TInt i = 0; i < numberOfPCSMatches; i++ ) + { + TInt indexFromEnd = numberOfPCSMatches - i - 1; + + MVPbkContactLink* link = iPredictiveContactSearchHandler->ConvertToVpbkLinkLC( + *(aResults[indexFromEnd]), iContactDataManager->ContactManager() ); + if ( !iContactDataManager->IsFavL( link ) ) + { + // handle favourites separately, in another loop + HBufC* contactString = CreateContactStringLC( aResults[ indexFromEnd ], nameOrder ); + CreateListBoxContactStringL( *contactString, link, matchThumbnails, EFalse ); + CleanupStack::PopAndDestroy( contactString ); + CleanupStack::PopAndDestroy( link ); + } + else + { + // favourites are handled later + favsFoundByPcs.AppendL( link ); + CleanupStack::Pop( link ); + } + + OstTraceExt2( TRACE_NORMAL, CEASYDIALINGPLUGIN_HANDLEPSRESULTSUPDATEL_SHOW_MATCH, "Contact #%d: '%S'", i+1, iContactStringCreationBuffer ); + LOGSTRING2( "EasyDialingPlugin: Contact #%d: '%S'", i+1, &iContactStringCreationBuffer ); + } + + // Search synchronously through all the favourite contacts to ensure + // that all favourite matches are added to bottom even when number of mathces + // exceeds the maximum number set to PCS + TInt numberOfFavs( iContactDataManager->NumberOfFavsL() ); + TBuf results; + for ( TInt i = numberOfFavs - 1; i >= 0; i-- ) + { + HBufC* favContactString = iContactDataManager->FavContactStringLC( i, nameOrder ); + MVPbkContactLink* link = iContactDataManager->FavLinkLC( i ); + + // Check if this fav contact was returned in aResults. + // It's at least theoretically possible that all matches in aResults + // are not matched when using LookupMatchL. PCS has completely separate logics + // for functions SearchL and LookupMatchL and especially with Chinese variant + // they may return different results. + TBool found = ( Find( link, favsFoundByPcs ) != KErrNotFound ); + + // If this fav contact was not in aResults, then use LookupMatchL + // to check if this contact is still a match and was excluded from aResults + // because maximum number of results was exceeded. + if ( !found ) + { + results = KNullDesC; + iPredictiveContactSearchHandler->LookupMatchL( + *iPredictiveSearchQuery, *favContactString, results ); + found = ( results.Length() > 0 ); + } + + if ( found ) + { + // matches, add this fav to listbox. + CreateListBoxContactStringL( *favContactString, link, matchThumbnails, ETrue ); + } + + CleanupStack::PopAndDestroy(); // link + CleanupStack::PopAndDestroy( favContactString ); + } + + CleanupStack::PopAndDestroy( &favsFoundByPcs ); // ResetAndDestroy + + // Update the view + // --------------- + iNumberOfNames = iListBoxModel->Count(); + if ( iNumberOfNames ) + { + TInt oldRectHeight = iContactListBox->Rect().Height(); + + iContactListBox->SetRectToNumberOfItems( iNumberOfNames ); + + // If the window is resized (->position changes too) while it's visible, + // it has to be redrawn immediately or otherwise listbox will flash + // in a wrong place on the screen during execution of this method + // HandlePsResultsUpdateL. In the worst case it'll flash on top of + // dialer's numeric keypad when listbox is made smaller. + if ( oldRectHeight != iContactListBox->Rect().Height() ) + { + iContactListBox->DrawNow(); + } + + iContactListBox->HandleItemAdditionL(); + + // Scroll the list to bottom + iContactListBox->ScrollToMakeItemVisible( iNumberOfNames-1 ); + + ShowContactListBoxWithEffect(); + iContactListBox->View()->ItemDrawer()->ClearFlags( CListItemDrawer::ESingleClickDisabledHighlight ); + static_cast( iContactListBox->View() )->SetCurrentItemIndexToNone(); + } + else + { + HideContactListBoxWithEffect(); + } + + if ( IsFocused() ) + { + SetFocus( EFalse ); + } + + DrawDeferred(); + + InformObservers( MDialingExtensionObserver::ESearchComplete ); + } + + +// ----------------------------------------------------------------------------- +// void CCASimpleNotifyL() +// Implements MCCAObserver notification interface. +// +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::CCASimpleNotifyL( TNotifyType aType, TInt aReason ) + { + OstTraceExt2( TRACE_NORMAL, CEASYDIALINGPLUGIN_CCASIMPLENOTIFYL, "CCASimpleNotifyL: type: %d, reason: %d", ( TUint )( aType ), aReason ); + LOGSTRING2("EasyDialingPlugin: CCASimpleNotifyL: type: %d, reason: %d", (TInt)aType, aReason ); + + iContactLauncherActive = EFalse; + + CAknAppUi* appUi = static_cast( iCoeEnv->AppUi() ); + appUi->HandleCommandL( EPhoneCmdBlockingDialogClosed ); + + // Give up focus, if iRememberFocus flag is not set. + if ( !iRememberFocus ) + { + SetFocus( EFalse ); + DrawDeferred(); + } + iRememberFocus = EFalse; + + // Inform observers. + InformObservers( MDialingExtensionObserver::ECCALauncherExit ); + } + + +// ----------------------------------------------------------------------------- +// void LaunchCurrentContactL() +// Implements MCCAObserver notification interface. +// +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::LaunchCurrentContactL() + { + __ASSERT_DEBUG( iNumberOfNames > 0, EasyDialingPanic( EEasyDialingPanicNoResults ) ); + __ASSERT_DEBUG( iContactToBeLaunched, EasyDialingPanic( EEasyDialingPanicNoContactSelected ) ); + + if (( iContactLauncherActive ) || ( iNumberOfNames == 0 ) || ( !iContactListBox ) || ( !iContactToBeLaunched )) + { + OstTrace0( TRACE_ERROR, CEASYDIALINGPLUGIN_LAUNCHCURRENTCONTACTL_ERROR, "LaunchCurrentContactL: Parameter error" ); + LOGSTRING("EasyDialingPlugin: LaunchCurrentContactL - Parameter error"); + return; + } + + MCCAParameter* launchParameters = TCCAFactory::NewParameterL(); + CleanupClosePushL( *launchParameters ); + + // ESoftExit flag causes that ca launcher will not close the + // client application in any circumstance. + // Application hosting easydialing is telephone application. + // It must never be closed, so we must use this flag. + launchParameters->SetConnectionFlag(MCCAParameter::ESoftExit); + + launchParameters->SetContactDataFlag(MCCAParameter::EContactLink); + + // Expand contact link into 16-bit descriptor because cca launcher api expects this. + HBufC16* contact16 = HBufC16::NewLC( iContactToBeLaunched->Length() ); + contact16->Des().Copy( *iContactToBeLaunched ); + + launchParameters->SetContactDataL( *contact16 ); + + CleanupStack::PopAndDestroy( contact16 ); + + OstTraceExt1( TRACE_NORMAL, CEASYDIALINGPLUGIN_LAUNCHCURRENTCONTACTL_LAUNCH_CCA, "Launch CL for contact: '%S'", *iContactToBeLaunchedName ); + LOGSTRING1("EasyDialingPlugin: Launch CL for contact: '%S'", iContactToBeLaunchedName ); + + delete iContactToBeLaunched; + iContactToBeLaunched = NULL; + delete iContactToBeLaunchedName; + iContactToBeLaunchedName = NULL; + + iContactLauncher->LaunchAppL( *launchParameters, this ); + + // Ownership of parameter transferred to CCA launcher => pop but do not destroy. + CleanupStack::Pop( launchParameters ); + + if ( !IsVisible() ) + { + // MCCAConnection::LaunchAppL uses CActiveSchedulerWait to hide asynchronous + // opening of CCA launcher. It is possible that during opening of CCA launcher + // phone has moved to in-call view (at least in case of an incoming call). + // In that case we need to close CCA launcher again. + iContactLauncher->CloseAppL(); + } + else + { + iContactLauncherActive = ETrue; + CAknAppUi* appUi = static_cast( iCoeEnv->AppUi() ); + appUi->HandleCommandL( EPhoneCmdBlockingDialogLaunched ); + } + } + + +// ----------------------------------------------------------------------------- +// CreateListBoxContactStringL +// +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::CreateListBoxContactStringL( + const TDesC& aContactString, + MVPbkContactLink *aLink, + TBool aMatchThumbnails, + TBool aFav ) + { + // Construct the contact string for the listbox model. + iContactStringCreationBuffer.Zero(); + + // Append contact thumbnail id. + HBufC* thumbnailId = iContactDataManager->GetThumbnailIdL( aLink, aMatchThumbnails, aFav ); + iContactStringCreationBuffer.Append( *thumbnailId ); + delete thumbnailId; + iContactStringCreationBuffer.Append( KListFieldSeparatorChar ); + + AppendStringWithMatchDataL( + iContactStringCreationBuffer, + aContactString, + iPredictiveContactSearchHandler, + iPredictiveSearchQuery ); + + // Append the whole string to listbox model. + iListBoxModel->AppendL( iContactStringCreationBuffer ); + } + +// ----------------------------------------------------------------------------- +// CreateContactStringLC +// +// ----------------------------------------------------------------------------- +// +HBufC* CEasyDialingPlugin::CreateContactStringLC( CPsClientData* aResult, + CEasyDialingContactDataManager::TNameOrder aNameOrder ) + { + TPtrC firstName = *aResult->Data( iFirstNamePCSIndex ); + TPtrC lastName = *aResult->Data( iLastNamePCSIndex ); + TPtrC companyName( KNullDesC ); + + if ( iCompanyNamePCSIndex != KErrNotFound ) + { + companyName.Set( *aResult->Data( iCompanyNamePCSIndex ) ); + } + return EasyDialingUtils::CreateContactStringLC( firstName, lastName, companyName, aNameOrder ); + } + + +// ----------------------------------------------------------------------------- +// AddObserverL +// +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::AddObserverL( MDialingExtensionObserver* aObserver ) + { + if ( !aObserver ) + { + return; + } + + iObservers.AppendL( aObserver ); + } + + +// ----------------------------------------------------------------------------- +// RemoveObserver +// +// Removes the parameter observer from observer list if found. If same observer +// has registered multiple times (which is unnecessary), removed just the first +// registration. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::RemoveObserver( MDialingExtensionObserver* aObserver ) + { + for ( TInt i = 0; i < iObservers.Count(); i++ ) + { + if ( iObservers[i] == aObserver) + { + iObservers.Remove( i ); + return; + } + } + } + + +// ----------------------------------------------------------------------------- +// InformObservers +// Informs all registered observers of easy dialing events. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::InformObservers( MDialingExtensionObserver::TEvent aEvent ) + { + for ( TInt i = 0; i < iObservers.Count(); i++ ) + { + iObservers[i]->HandleDialingExtensionEvent( aEvent ); + } + } + +// ----------------------------------------------------------------------------- +// CbaResourceId +// Returns EasyDialingPlugin specific CBA resource ID that +// Phone application can use when updating Soft Keys. +// ----------------------------------------------------------------------------- +// +TInt CEasyDialingPlugin::CbaResourceId() + { + return R_EASYDIALING_CBA; + } + + +// ----------------------------------------------------------------------------- +// MenuResourceId +// Returns EasyDialingPlugin specific menu resource ID that +// Phone application can use when easydialing is in focus. +// ----------------------------------------------------------------------------- +// +TInt CEasyDialingPlugin::MenuResourceId() + { + return R_EASYDIALING_MENUBAR; + } + + +// ----------------------------------------------------------------------------- +// InitializeMenuPaneL +// Initializes easy dialing menu pane. This function is meant to be called in +// DynInitMenuPaneL of the application, if the application wants to use easy +// dialing menu. +// ----------------------------------------------------------------------------- +// +TBool CEasyDialingPlugin::InitializeMenuPaneL( CEikMenuPane& aMenuPane, TInt aMenuResourceId ) + { + if ( aMenuResourceId == R_PHONEUIDIALER_OPTIONS_MENU ) + { + TInt index( KErrNotFound ); + // To be on the safe side - place holder was added into phoneui.rss. + if ( aMenuPane.MenuItemExists( EEasyDialingSettingsItemPlaceHolder, index ) ) + { + // Add first menu item (= on/off submenu) after placeHolder item and + // then delete that (empty) placeHolder. + aMenuPane.AddMenuItemsL( R_EASYDIALING_OPTIONS_ON_OFF_MENU_ITEM, + EEasyDialingSettingsItemPlaceHolder ); + aMenuPane.DeleteMenuItem( EEasyDialingSettingsItemPlaceHolder ); + + LOGSTRING("EasyDialingPlugin::InitializeMenuPaneL: Added on/off menu into dialer's menu" ); + } + + // Return EFalse as only one menu item was added here. + return EFalse; + } + + else if ( aMenuResourceId == R_EASYDIALING_MENU ) + { + TBool voiceCall = EFalse; + TBool videoCall = EFalse; + TBool message = EFalse; + + if ( iContactListBox->CurrentItemIndex() >= 0 ) + { + TInt index = iContactListBox->CurrentContactDataIndex(); + + voiceCall = iContactDataManager->VoiceCallAvailable( index ); + videoCall = iContactDataManager->VideoCallAvailable( index ); + message = iContactDataManager->UniEditorAvailable( index ); + } + + + aMenuPane.SetItemDimmed( EEasyDialingVoiceCall, !voiceCall ); + aMenuPane.SetItemDimmed( EEasyDialingVideoCall, !videoCall ); + aMenuPane.SetItemDimmed( EEasyDialingSendMessage, !message ); + + LOGSTRING("EasyDialingPlugin::InitializeMenuPaneL: InitializeMenuPaneL done" ); + + return ETrue; + } + + else if ( aMenuResourceId == R_EASYDIALING_OPTIONS_ON_OFF_CASCADE_MENU ) + { + if ( IsEnabled() ) + { + aMenuPane.SetItemButtonState( EEasyDialingOn, EEikMenuItemSymbolOn ); + } + else + { + aMenuPane.SetItemButtonState( EEasyDialingOff, EEikMenuItemSymbolOn ); + } + + LOGSTRING("EasyDialingPlugin::InitializeMenuPaneL: InitializeMenuPaneL for on/off submenu done" ); + + return ETrue; + } + + else + { + return EFalse; + } + } + +// ----------------------------------------------------------------------------- +// HandleCommandL +// Method for handling EasyDialingPlugin specific commands. +// ----------------------------------------------------------------------------- +// +TBool CEasyDialingPlugin::HandleCommandL( TInt aCommand ) + { + // Input blocker can't block controls higher on the control stack than + // ECoeStackPriorityDialog so we will get HandleCommandL calls from + // phoneappui (CBA) when input blocker is active (=not NULL). + + if ( iInputBlocker && aCommand != EEasyDialingCallHandlingActivated && + aCommand != EEasyDialingVkbOpened && aCommand != EEasyDialingVkbClosed ) + { + // Some action is already being launched since iInputBlocker exists. + // Only call activation command requires always action from this plugin. + // Vkb status flag should be updated always too (invokes no action). + return ETrue; + } + + + TBool ret(ETrue); + + switch ( aCommand ) + { + case EEasyDialingOpenContact: + + iRememberFocus = ETrue; + AsyncActionLaunchL( ELaunchCurrentContact ); + break; + + // EEasyDialingEnterKeyAction is sent when Enter key is pressed. + // EEasyDialingMakeCall is sent when send key or send toolbar button or send menu item is pressed. + case EEasyDialingVoiceCall: // fall through + case EEasyDialingEnterKeyAction: + // Make a call. + iRememberFocus = ETrue; + AsyncActionLaunchL( ECallCurrentContact ); + break; + + // Video call is selectable only through menu. + case EEasyDialingVideoCall: + // Make a video call. + iRememberFocus = ETrue; + AsyncActionLaunchL( EVideoCallCurrentContact ); + break; + + // Uni-editor message is selectable only through menu. + case EEasyDialingSendMessage: + // Create a message. + iRememberFocus = ETrue; + AsyncActionLaunchL( ESendMessageCurrentContact ); + break; + + // EEasyDialingCallHandlingActivated is sent when the in-call-ui of telephony gets activated + case EEasyDialingCallHandlingActivated: + + iRememberFocus = EFalse; + // Stop input block and async action launch in case they are active. + CancelActionLaunchAndInputBlock(); + // Close down number selection popup in case it happens to be open. + iContactorService->CancelService(); + // Close down communication launcher if it's active + if ( iContactLauncherActive ) + { + iNewSearchNeeded = EFalse; + iContactLauncher->CloseAppL(); + } + break; + + case EEasyDialingOn: + + iCenrepListener->SetEasyDialingSettingsValue( 1 ); + break; + + case EEasyDialingOff: + + iCenrepListener->SetEasyDialingSettingsValue( 0 ); + break; + + case EEasyDialingClosePopup: + + // Not only Number Entry is removed but also closes down number selection popup in case it happens to be open. + iContactorService->CancelService(); + break; + + case EEasyDialingVkbOpened: + + iVirtualKeyboardOpen = ETrue; + break; + + case EEasyDialingVkbClosed: + + iVirtualKeyboardOpen = EFalse; + HandleGainingForeground(); + break; + + default: + ret = EFalse; + break; + } + return ret; + } + +// ----------------------------------------------------------------------------- +// IsEnabled +// Check if Easy dialing is enabled in the settings +// ----------------------------------------------------------------------------- +// +TBool CEasyDialingPlugin::IsEnabled() const + { + return ( iCenrepListener && iCenrepListener->Value() != 0 ); + } + +// ----------------------------------------------------------------------------- +// SetKeyboardMode +// Sets keyboard mode +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::SetKeyboardMode( TKeyboardMode aMode ) + { + iKeyboardMode = aMode; + } + +// ----------------------------------------------------------------------------- +// AsyncSimulateKeyEvent +// +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::AsyncSimulateKeyEvent( const TKeyEvent& aKeyEvent ) + { + // Do the simulation only if input hasn't been blocked + if ( !iInputBlocker ) + { + iKeyEventToSimulate = aKeyEvent; + iActionToBeLaunched = ESimulateKeyEvent; + iAsyncCallBack->SetPriority( CActive::EPriorityStandard ); + iAsyncCallBack->CallBack(); // activates callback request + } + } + +// ----------------------------------------------------------------------------- +// AsyncActionLaunchL +// Use asynchronous callback to launch action. While action is being launched, +// input blocker is used to avoid OfferKeyEvent and HandlePointerEvent calls +// coming in. Both end key and application key work despite of input blocker. +// Input blocker can't block phoneappui's cba so extra check is needed in +// HandleCommandL method. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::AsyncActionLaunchL( TEasyDialingAction aAction ) + { + iActionToBeLaunched = aAction; + + delete iContactToBeLaunched; + iContactToBeLaunched = NULL; + delete iContactToBeLaunchedName; + iContactToBeLaunchedName = NULL; + + if ( aAction == ECallCurrentContact + || aAction == EVideoCallCurrentContact + || aAction == ESendMessageCurrentContact + || aAction == ELaunchCurrentContact ) + { + // Need to save current contact link and name. Listbox current item + // index might not be correct when callback is handled. + TRAPD( err, iContactToBeLaunched = iContactListBox->CurrentContactLinkL() ); + if ( err || !iContactToBeLaunched ) + { + // Can't find contact for the action - probably because listbox + // has lost the selection somehow. This shouldn't happen, but if it does + // return without trying to launch any action. + return; + } + + TPtrC contactString( iListBoxModel->MdcaPoint( iContactListBox->CurrentItemIndex() ) ); + TPtrC fullNameSeparators; + TInt error = TextUtils::ColumnText( fullNameSeparators, 1, &contactString ); + __ASSERT_DEBUG( error == KErrNone, EasyDialingPanic( EEasyDialingPanicInvalidListBoxModelString ) ); + + // Remove highlight separators. It is possible that some contactor API has problem with them. + iContactToBeLaunchedName = AllocWithoutHighlightSeparatorsLC( fullNameSeparators ); + CleanupStack::Pop( iContactToBeLaunchedName ); + } + + CancelActionLaunchAndInputBlock(); + + if ( aAction == ELaunchSearch ) + { + // Must be set as low as possible to enable PCS to update its store. + iAsyncCallBack->SetPriority( CActive::EPriorityIdle ); + } + else + { + iAsyncCallBack->SetPriority( CActive::EPriorityStandard ); + } + + iAsyncCallBack->CallBack(); // activates callback request + + // By defining cancel handler, we don't block all input but system is still + // able to deactivate the view etc. + iInputBlocker = CAknInputBlock::NewCancelHandlerLC( this ); + CleanupStack::Pop( iInputBlocker ); + + // This means that iInputBlocker is deleted by CAknInputBlock when + // it's cancelled ( we get a callback where iInputBlocker is set to NULL). + iInputBlocker->SetCancelDelete( iInputBlocker ); + } + +// ----------------------------------------------------------------------------- +// AsyncCallBackToLaunchAction +// Callback function for CAsyncCallBack class. +// ----------------------------------------------------------------------------- +// +TInt CEasyDialingPlugin::AsyncCallBackToLaunchAction( TAny* aPtr ) + { + CEasyDialingPlugin* plugin = static_cast( aPtr ); + + TRAP_IGNORE( plugin->DoLaunchActionL() ); + + // Stop input block if active (=not NULL). + if ( plugin->iInputBlocker ) + { + plugin->iInputBlocker->Cancel(); + } + + plugin->iActionToBeLaunched = ENoActionDefined; + + return KErrNone; + } + +// ----------------------------------------------------------------------------- +// DoLaunchActionL +// +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::DoLaunchActionL() + { + if ( iActionToBeLaunched == EInitializePcs ) + { + PERF_MEASURE_START + InitPredictiveContactSearchL(); + PERF_MEASURE_STOP + + return; + } + else if ( !IsVisible() ) + { + // If ED is not visible, don't launch the action. This can happen if + // we get incoming call in the middle of action launching. + return; + } + // If ELaunchCurrentContact, then we launch cca launcher. + else if ( iActionToBeLaunched == ELaunchCurrentContact ) + { + LaunchCurrentContactL(); + return; + } + else if ( iActionToBeLaunched == ELaunchSearch ) + { + LaunchSearchL(); + return; + } + else if ( iActionToBeLaunched == ESimulateKeyEvent ) + { + iEikonEnv->SimulateKeyEventL( iKeyEventToSimulate, EEventKey ); + return; + } + + // If not for launching current contact or performing search, + // the action is launching some communication method. + + if ( iContactorService->IsBusy() ) + { + LOGSTRING("EasyDialingPlugin: LaunchActionL - contactor service is busy!"); + return; + } + + VPbkFieldTypeSelectorFactory::TVPbkContactActionTypeSelector selector( + VPbkFieldTypeSelectorFactory::EEmptySelector ); + + switch ( iActionToBeLaunched ) + { + case ECallCurrentContact: + { + // Easy Dialing provides pretty much the same functionality for big Call icon + // and Send key than phonebook's contact list view by using aiw interface + // in edcontactor. + // At this point one must however check whether to use voip or voice call selector - + // otherwise correct logic is provided by aiw interface. + // Note that Easy Dialing ignores default numbers and addresses set for + // action types and shows always the number and address list if there are + // more than one possible number/address for some action. + + CSPSettingsVoIPUtils* sPSettings = CSPSettingsVoIPUtils::NewLC(); + + if ( sPSettings->IsPreferredTelephonyVoIP() ) + { + selector = VPbkFieldTypeSelectorFactory::EVOIPCallSelector; + } + else + { + selector = VPbkFieldTypeSelectorFactory::EVoiceCallSelector; + } + + CleanupStack::PopAndDestroy( sPSettings ); + } + break; + + case EVideoCallCurrentContact: + + selector = VPbkFieldTypeSelectorFactory::EVideoCallSelector; + break; + + case ESendMessageCurrentContact: + + selector = VPbkFieldTypeSelectorFactory::EUniEditorSelector; + break; + + default: + delete iContactToBeLaunched; + iContactToBeLaunched = NULL; + delete iContactToBeLaunchedName; + iContactToBeLaunchedName = NULL; + __ASSERT_DEBUG( EFalse, EasyDialingPanic( EEasyDialingActionNotSupported ) ); + return; + } + + CEDContactorService::TCSParameter param( selector, *iContactToBeLaunched, + CEDContactorService::TCSParameter::EEnableDefaults, + *iContactToBeLaunchedName ); + + iContactorService->ExecuteServiceL( param ); + + delete iContactToBeLaunched; + iContactToBeLaunched = NULL; + delete iContactToBeLaunchedName; + iContactToBeLaunchedName = NULL; + } + + +// ----------------------------------------------------------------------------- +// CancelActionLaunchAndInputBlock +// Should be called when focus is lost or when call ui is activated. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::CancelActionLaunchAndInputBlock() + { + iAsyncCallBack->Cancel(); + if ( iInputBlocker ) + { + iInputBlocker->Cancel(); + } + } + +// ----------------------------------------------------------------------------- +// HandleListBoxEventL +// EasyDialerListBox calls this function when listbox events are reported +// to observers. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::HandleListBoxEventL( CEikListBox* /*aListBox*/, TListBoxEvent aEventType ) + { + LOGSTRING1( "EasyDialingPlugin: HandleListBoxEventL( %d ) - Enter", aEventType ); + + switch( aEventType ) + { + case EEventItemSingleClicked: + // 9.2. Selecting contact initiates call + AsyncActionLaunchL( ECallCurrentContact ); + break; + + case KEasyDialingContactLongTapped: + iLongTapped = ETrue; + AsyncActionLaunchL( ELaunchCurrentContact ); + break; + + // Pause contact data manager when panning and flicking listbox. + // This ensures smooth and responsive listbox touch handling. + case EEventFlickStarted: + case EEventPanningStarted: + case KEasyDialingScrollingStarted: + iContactDataManager->Pause( ETrue ); + break; + + case EEventFlickStopped: + case EEventPanningStopped: + case KEasyDialingScrollingStopped: + iContactListBox->View()->ItemDrawer()->ClearFlags( CListItemDrawer::ESingleClickDisabledHighlight ); + static_cast( iContactListBox->View() )->SetCurrentItemIndexToNone(); + iContactDataManager->Pause( EFalse ); + + // Touching the listbox always removes the visual focus from any list item. + // Move the focus away from the listbox after panning has ended to + // align our internal state with the visual lack of focus. + if ( IsFocused() && !iLongTapped ) + { + SetFocus( EFalse ); + DrawDeferred(); + } + iLongTapped = EFalse; + break; + + // We are not interested about the other listbox events. + default: + break; + } + } + +// ----------------------------------------------------------------------------- +// From MAknInputBlockCancelHandler. +// Called when input block is cancelled. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::AknInputBlockCancel() + { + LOGSTRING("EasyDialingPlugin: AknInputBlockCancel"); + + // iInputBlocker will be deleted right after this callback by CAknInputBlock + // cause we are using CAknInputBlock::SetCancelDelete method. + iInputBlocker = NULL; + } + +// ----------------------------------------------------------------------------- +// HandleGainingForeground +// From MCoeForegroundObserver. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::HandleGainingForeground() + { + if ( iNewSearchNeeded && IsEnabled() ) + { + // The cached information in contact data manager may be outdated. Call to reload makes sure that when the + // search is made, all data is loaded again. + iContactDataManager->Reload(); + + TRAP_IGNORE( LaunchSearchL() ); + } + } + +// ----------------------------------------------------------------------------- +// HandleLosingForeground +// From MCoeForegroundObserver. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::HandleLosingForeground() + { + // Make sure contact data manager is not left in paused state when + // ED loses foreground while scrolling is active. + iContactDataManager->Pause( EFalse ); + + // Simulate an EButton1Up event for scrollbar so that it will be in correct + // state if e.g. some popup appears while scrollbar is dragged. + // No need to check if scrollbar has received button1Down event or + // is indeed dragged currently: no harm done if button1Up is simulated + // in vain. + CEikScrollBarFrame* scrollBarFrame = iContactListBox->ScrollBarFrame(); + if ( scrollBarFrame ) + { + CEikScrollBar* scrollBar = scrollBarFrame->VerticalScrollBar(); + if ( scrollBar && scrollBar->IsVisible() ) + { + TPointerEvent simulatedPointerEvent( TPointerEvent::EButton1Up, 0, + TPoint(), TPoint() ); + TRAP_IGNORE( scrollBar->HandlePointerEventL( simulatedPointerEvent ) ); + } + } + } + +// ----------------------------------------------------------------------------- +// CEasyDialingPlugin::DoHandleContactsChangedL +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::DoHandleContactsChangedL() + { + if ( iSearchString.Length() > 0 && IsEnabled() ) + { + CAknAppUi* appUi = static_cast( iCoeEnv->AppUi() ); + if ( appUi->IsForeground() && !iVirtualKeyboardOpen ) + { + // Do new search immediately, if contacts change while we are on the + // foreground. This can happen for example if view is switched to + // dialer while deletion of multiple contacts is still ongoing or if + // PCS takes so long to update the cache that view gets changed + // before the update is ready. + iContactDataManager->Reload(); // to update thumbnails + AsyncActionLaunchL( ELaunchSearch ); + } + else + { + // Set the flag to make a search when we come back to foreground. + // This way, we don't make unnecessary searches when several + // contacts are imported or deleted, for example. + iNewSearchNeeded = ETrue; + + // Hide previous results so that they won't show up before the + // the new search is ready. + iContactListBox->MakeVisible( EFalse ); + } + } + } + +// ----------------------------------------------------------------------------- +// CEasyDialingPlugin::ShowContactListBoxWithEffect +// Use appear/disappear effects when in foreground and when listbox truly +// changes visiblity. +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::ShowContactListBoxWithEffect() + { + if ( !IsVisible() ) + { + // never show listbox if easydialing is not visible + return; + } + // Show effect only if listbox is about to come visible. + else if ( !iContactListBox->IsVisible() && CanListBoxEffectBeUsed() ) + { + GfxTransEffect::Begin( iContactListBox, KGfxContactListBoxOpenEffect ); + iContactListBox->MakeVisible( ETrue ); + GfxTransEffect::SetDemarcation( iContactListBox, iContactListBox->Rect() ); + GfxTransEffect::End( iContactListBox ); + } + else + { + iContactListBox->MakeVisible( ETrue ); + } + } + +// ----------------------------------------------------------------------------- +// CEasyDialingPlugin::HideContactListBoxWithEffect +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::HideContactListBoxWithEffect() + { + // Show effect only if listbox is about to disappear from the screen. + if ( iContactListBox->IsVisible() && CanListBoxEffectBeUsed() ) + { + GfxTransEffect::Begin( iContactListBox, KGfxContactListBoxCloseEffect ); + iContactListBox->MakeVisible( EFalse ); + GfxTransEffect::SetDemarcation( iContactListBox, iContactListBox->Rect() ); + GfxTransEffect::End( iContactListBox ); + } + else + { + iContactListBox->MakeVisible( EFalse ); + } + } + +// ----------------------------------------------------------------------------- +// CEasyDialingPlugin::CanListBoxEffectBeUsed +// ----------------------------------------------------------------------------- +// +TBool CEasyDialingPlugin::CanListBoxEffectBeUsed() const + { + TBool canBeUsed( EFalse ); + + CAknAppUi* appUi = static_cast( iCoeEnv->AppUi() ); + + // Note that when vkb is open, phone still keeps foreground and focus so + // vkb status must be checked separately (vkb's window group has just higher + // priority than phone's window group). + if ( appUi && appUi->IsForeground() && + !iVirtualKeyboardOpen && + GfxTransEffect::IsEnabled() && + GfxTransEffect::IsRegistered( iContactListBox ) ) + { + canBeUsed = ETrue; + } + + return canBeUsed; + } + +// ----------------------------------------------------------------------------- +// CEasyDialingPlugin::SetInfoLabelColourL +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::SetInfoLabelColourL() + { + MAknsSkinInstance* skin = AknsUtils::SkinInstance(); + TRgb skinColor; + TInt error = AknsUtils::GetCachedColor( skin, skinColor, + KAknsIIDQsnTextColors, EAknsCIQsnTextColorsCG6 ); + + if ( error == KErrNone ) + { + iInfoLabelLine1->OverrideColorL( EColorLabelText, skinColor ); + iInfoLabelLine2->OverrideColorL( EColorLabelText, skinColor ); + } + } + +// ----------------------------------------------------------------------------- +// CEasyDialingPlugin::SetInfoLabelVisibleL +// ----------------------------------------------------------------------------- +// +void CEasyDialingPlugin::SetInfoLabelVisibleL( TBool aVisible ) + { + if ( aVisible ) + { + iInfoLabelLine1->SetTextL( *iInfoLabelTextLine1 ); + iInfoLabelLine2->SetTextL( *iInfoLabelTextLine2 ); + } + else + { + iInfoLabelLine1->SetTextL( KNullDesC ); + iInfoLabelLine2->SetTextL( KNullDesC ); + } + iInfoLabelLine1->DrawDeferred(); + iInfoLabelLine2->DrawDeferred(); + } + + +/* + * ============================================================================== + * + * Local functions + * + * ============================================================================== + */ + + + +// ----------------------------------------------------------------------------- +// CompareTPsMatchLocation +// Compares two TPsMatchLocation objects based on where the match +// is located in the search string. This used in ordering matches +// based on their location. +// ----------------------------------------------------------------------------- +// +TInt CompareTPsMatchLocation( const TPsMatchLocation& a1, const TPsMatchLocation& a2) + { + return a1.index - a2.index; + } + + +// ----------------------------------------------------------------------------- +// AppendStringWithMatchDataL +// Appends a string with match data into a TDes buffer. Match data is a string +// where matching and non-matching parts are separated by matching hightlight separator +// characters. +// ----------------------------------------------------------------------------- +// +void AppendStringWithMatchDataL( + TDes& aBuffer, + const TDesC& aText, + CPSRequestHandler* aPSHandler, + const CPsQuery* aPSQuery ) + { + if ( HighlightingSupportedForText( aText ) ) + { + CDesCArrayFlat* matchingParts = new (ELeave) CDesCArrayFlat(KEasyDialingMaximumMatchingParts); + CleanupStack::PushL( matchingParts ); + + RArray matchIndices; + CleanupClosePushL( matchIndices ); + + aPSHandler->LookupL( *aPSQuery , aText , *matchingParts, matchIndices ); + + // Sort matchIndices array. Later handling assumes it to be sorted according to index field. + TLinearOrder order( CompareTPsMatchLocation ); + matchIndices.Sort( order ); + + TInt numberOfIndices = matchIndices.Count(); + TInt textOffset = 0; + + for ( TInt i = 0; i < numberOfIndices ; i++ ) + { + TInt matchingPartStart = matchIndices[i].index; + TInt matchingPartLength = matchIndices[i].length; + + // Append the non-matching part (if exists) + if ( matchingPartStart > textOffset ) + { + TPtrC notMatchingPart = aText.Mid( textOffset, matchingPartStart - textOffset ); + aBuffer.Append( notMatchingPart ); + } + + // Append matching separator charactes. + aBuffer.Append( KHighlightSeparatorChar ); + + TPtrC matchingPart = aText.Mid( matchingPartStart, matchingPartLength ); + aBuffer.Append( matchingPart ); + + // Append matching separator charactes. + aBuffer.Append( KHighlightSeparatorChar ); + + textOffset = matchingPartStart + matchingPartLength; + } + + CleanupStack::PopAndDestroy( &matchIndices ); + CleanupStack::PopAndDestroy( matchingParts ); + + // Now there still may be one not matching part to be appended. + if ( textOffset < aText.Length() ) + { + TPtrC notMatchingPart = aText.Mid( textOffset, aText.Length() - textOffset ); + aBuffer.Append( notMatchingPart ); + } + } + else // HighlightingSupportedForText( aText ) + { + aBuffer.Append( aText ); + } + } + +// ----------------------------------------------------------------------------- +// HighlightingSupportedForText +// Checks if highlighting of matching text part is supported for the combination +// of scripts used in the given text +// ----------------------------------------------------------------------------- +// +TBool HighlightingSupportedForText( const TDesC& aText ) + { + // Highlighting for text is supported unless it contains one ore more + // characters written with unsupported script. + TBool supported = ETrue; + for ( TInt i = 0 ; i < aText.Length() && supported ; ++i ) + { + supported = HighlightingSupportedForScript( aText[i] ); + } + + // ... or it contains both LTR and RTL blocks + if ( supported ) + { + supported = !IsStrictlyBidirectional( aText ); + } + + return supported; + } + +// ----------------------------------------------------------------------------- +// HighlightingSupportedForScript +// Checks if highlighting of matching text part is supported for the script +// of given character +// ----------------------------------------------------------------------------- +// +TBool HighlightingSupportedForScript( TText aChar ) + { + // For now, we don't support highlighting for any Arabic or South East Asian + // script. This is because many of these scripts use rendering rules + // which cause problems for our simple highlighting logic designed for + // Latin script. + + TBool belongsToUnsupportedRange = + ( aChar >= KArabicAndSouthEastAsianRangeStart && aChar <= KArabicAndSouthEastAsianRangeEnd ) || + ( aChar >= KArabicPresentationFormsARangenStart && aChar <= KArabicPresentationFormsARangeEnd ) || + ( aChar >= KArabicPresentationFormsBRangenStart && aChar <= KArabicPresentationFormsBRangeEnd ); + return !belongsToUnsupportedRange; + } + +// ----------------------------------------------------------------------------- +// IsStrictlyBidirectional +// +// Returns true if argument descriptor contains both left-to-right and +// right-to-left blocks +// ----------------------------------------------------------------------------- +// +TBool IsStrictlyBidirectional( const TDesC& aText ) + { + TBool bothDirectionsFound = EFalse; + + // TRunInfoArray contains information of the directionalities of the different sections of the aText + TBidirectionalState::TRunInfo array[ KMaxRunInfoArrayCount ]; + + // Initialize the TBidiLogicalToVisual converter for making the conversion from logical to visual order + TBidiLogicalToVisual converter( aText, array, KMaxRunInfoArrayCount ); + + // Do the reordering. Amount of different directionality sections is returned. + TInt blockCount = converter.Reorder(); + if ( blockCount > KMaxRunInfoArrayCount ) + { + // If there are more directionality blocks than we are prepared to handle, then we don't + // know the directionality of them all. Report this as bidirectional to be on the safe side. + // This should be an extremely rare special case. + bothDirectionsFound = ETrue; + } + else if ( blockCount > 1 ) + { + // If there are more than one directionality blocks, go through all of them and + // check if the resolved direction changes in any of the blocks. + TUint8 firstDirection = array[0].iDirection; + for ( TInt i = 1 ; i < blockCount && !bothDirectionsFound ; i++ ) + { + if ( array[i].iDirection != firstDirection ) + { + bothDirectionsFound = ETrue; + } + } + } + + return bothDirectionsFound; + } + + +// ----------------------------------------------------------------------------- +// AllocWithoutHighlightSeparatorsLC +// +// Allocates a copy of the parameter descriptor with highlight separators +// removed. +// ----------------------------------------------------------------------------- +// +static HBufC* AllocWithoutHighlightSeparatorsLC( const TDesC& aDesc ) + { + HBufC* resultDesc = aDesc.AllocLC(); + TPtr ptr = resultDesc->Des(); + AknTextUtils::StripCharacters( ptr, KHighlightSeparatorCharAsLiteral ); + return resultDesc; + } + + +// ----------------------------------------------------------------------------- +// IsItuTCharacter +// +// Test whether parameter character is a number pad character "0123456789#*+" +// ----------------------------------------------------------------------------- +// +static TBool IsItuTCharacter( TChar aChar ) + { + return aChar.IsDigit() || + aChar.GetCategory() == TChar::EArabicNumber || + aChar == TChar('#') || + aChar == TChar('*') || + aChar == TChar('+'); + } + +// ----------------------------------------------------------------------------- +// Find contact link pointing to the same contact as given link +// ----------------------------------------------------------------------------- +// +static TInt Find( const MVPbkContactLink* aLink, const RPointerArray& aArray ) + { + TInt idx = KErrNotFound; + for ( TInt i = 0 ; i < aArray.Count() ; ++i ) + { + if ( aArray[i]->IsSame( *aLink ) ) + { + idx = i; + i = aArray.Count(); + } + } + return idx; + } + +// ----------------------------------------------------------------------------- +// CleanupStack helpers for item owning RPointerArrays (etc) +// ----------------------------------------------------------------------------- +// +template +class CleanupResetAndDestroy + { +public: + inline static void PushL( T& aRef ) + { CleanupStack::PushL( TCleanupItem(&ResetAndDestroy,&aRef) ); } +private: + inline static void ResetAndDestroy( TAny *aPtr ) + { static_cast(aPtr)->ResetAndDestroy(); } + }; + +template +inline void CleanupResetAndDestroyPushL( T& aRef ) + { CleanupResetAndDestroy::PushL(aRef); } + +// End of File +