contextutility/src/hgcontextutilityimpl.cpp
changeset 0 79c6a41cd166
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contextutility/src/hgcontextutilityimpl.cpp	Thu Dec 17 08:54:17 2009 +0200
@@ -0,0 +1,972 @@
+/*
+* Copyright (c) 2008 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:  Context publishing helper dll
+*
+*/
+
+
+#include <cfcontextobject.h>
+#include <cfclient.h>
+#include <mdesession.h>
+#include <mdeobject.h>
+#include <s32mem.h>
+#include <MVPbkContactLink.h>
+#include <MVPbkStoreContact.h>
+#include <MVPbkStreamable.h>
+#include <CVPbkContactLinkArray.h>
+#include <e32debug.h>
+#include <w32std.h>
+#include "hgcontextutilityimpl.h"
+#include "hgcontexttypes.h"
+
+// max number of entries processed when aContextData is an array in PublishContextL
+const TInt KMaxEntriesInMulti = 20;
+
+// separator character in combined string for multiple entries
+const TInt KMultiSepChar = 0x0001;
+
+// granularity for string array
+const TInt KArrayGranularity = 4;
+
+// argument for CBufFlat ctor when serializing contact links
+const TInt KBufGranularity = 64;
+
+// default security policy (use LocalServices cap) for contexts
+_LIT_SECURITY_POLICY_C1( KContextSecurity, ECapabilityLocalServices );
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::NewL
+// -----------------------------------------------------------------------------
+//
+CHgContextUtilityImpl* CHgContextUtilityImpl::NewL()
+    {
+    CHgContextUtilityImpl* self = NewLC();
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::NewLC
+// -----------------------------------------------------------------------------
+//
+CHgContextUtilityImpl* CHgContextUtilityImpl::NewLC()
+    {
+    CHgContextUtilityImpl* self = new ( ELeave ) CHgContextUtilityImpl;
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::CHgContextUtilityImpl
+// -----------------------------------------------------------------------------
+//
+CHgContextUtilityImpl::CHgContextUtilityImpl()
+        : CTimer( CActive::EPriorityStandard )
+    {
+    CActiveScheduler::Add( this );
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::ConstructL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::ConstructL()
+    {
+    CTimer::ConstructL();
+
+    iEnv = CCoeEnv::Static(); // may be NULL
+
+    // Do not create iCFClient here as cf server may not be available yet
+    // if we are early in the boot phase.
+    
+    // set defaults
+    RePublishWhenFgL( EFalse );
+    AllowPublishFromBackground( EFalse );
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::~CHgContextUtilityImpl
+// -----------------------------------------------------------------------------
+//
+CHgContextUtilityImpl::~CHgContextUtilityImpl()
+    {
+    Cancel();
+    delete iPendingContextType;
+    delete iPendingContextData;
+    delete iPendingContextDataArray;
+    delete iCFClient;
+    delete iLastContextType;
+    delete iLastContextData;
+    if ( iFgWatchEnabled && iEnv )
+        {
+        iEnv->RemoveForegroundObserver( *this );
+        }
+    
+    iMusicContextInfo.ResetAndDestroy();
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::CFReady
+// -----------------------------------------------------------------------------
+//
+TBool CHgContextUtilityImpl::CFReady()
+    {
+    if ( !iCFClient )
+        {
+        TRAPD( err, iCFClient = CCFClient::NewL( *this ) );
+        if ( err != KErrNone )
+            {
+            RDebug::Printf( "[hgctxutil] cfw not ready" );
+            return EFalse;
+            }
+        }
+    return ETrue;
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishContextL
+// All other non-static versions of this function will fall back to this one.
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishContextL( const TDesC& aContextType,
+        const TDesC& aContextData )
+    {
+    RDebug::Print( _L("[hgctxutil] PublishContextL [%S] [%S]"),
+        &aContextType, &aContextData );
+    // create cf client instance if not yet done
+    // and check foreground status if needed
+    if ( CFReady() && AllowedToPublish() )
+        {
+        // call static version with our cf client instance
+        PublishContextL( *iCFClient, aContextType, aContextData );
+        }
+    // store type and value for later use
+    // (even when cfserver is not available yet, the data may still be
+    //  used later when the app comes to foreground, for example)
+    if ( iLastContextType != &aContextType )
+        {
+        delete iLastContextType; iLastContextType = 0;
+        iLastContextType = aContextType.AllocL();
+        }
+    if ( iLastContextData != &aContextData )
+        {
+        delete iLastContextData; iLastContextData = 0;
+        iLastContextData = aContextData.AllocL();
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// AppendCharL
+// Appends a char to aDst, calls ReAllocL when needed, assumes that aDst
+// is also on cleanupstack (at top position) so it updates that pointer too.
+// -----------------------------------------------------------------------------
+//
+LOCAL_C void AppendCharL( HBufC*& aDst, TChar aChar )
+    {
+    TPtr des( aDst->Des() );
+    if ( des.Length() == des.MaxLength() )
+        {
+        HBufC* oldDst = aDst;
+        aDst = aDst->ReAllocL( des.MaxLength() * 2 );
+        CleanupStack::Pop( oldDst ); // pop the old pointer
+        CleanupStack::PushL( aDst ); // and push the new (possibly different) one
+        des.Set( aDst->Des() );
+        }
+    des.Append( aChar );
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::BuildCombinedStringL
+// -----------------------------------------------------------------------------
+//
+HBufC* CHgContextUtilityImpl::BuildCombinedStringL(
+        const MDesCArray& aArray )
+    {
+    TInt arrayCount = aArray.MdcaCount();
+    if ( arrayCount >= 2 )
+        {
+        // Rules:
+        // 1. escape all separator chars in the input with a preceeding \
+        // 2. escape all \ chars in the input with \\
+        // 3. take only the first KMaxEntriesInMulti elements from the array
+        // 4. append a separator also after last entry
+        // 5. prepend two separators to the combined string
+        TInt processedEntryCount = Min( arrayCount, KMaxEntriesInMulti );
+        // calculate a big enough size so we can avoid ReAllocL calls later
+        TInt sz = 0;
+        for ( TInt i = 0; i < processedEntryCount; ++i )
+            {
+            sz += aArray.MdcaPoint( i ).Length() + 1;
+            }
+        sz += 2; // for the magic prefix
+        HBufC* value = HBufC::NewLC( sz );
+        AppendCharL( value, KMultiSepChar );
+        AppendCharL( value, KMultiSepChar );
+        for ( TInt i = 0; i < processedEntryCount; ++i )
+            {
+            TPtrC entry( aArray.MdcaPoint( i ) );
+            // append, also do the escaping
+            for ( TInt j = 0, je = entry.Length(); j != je; ++j )
+                {
+                TChar c = entry[j];
+                if ( c == KMultiSepChar || c == '\\' )
+                    {
+                    AppendCharL( value, '\\' );
+                    }
+                AppendCharL( value, c );
+                }
+            AppendCharL( value, KMultiSepChar );
+            }
+        CleanupStack::Pop( value );
+        return value;
+        }
+    return 0;
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::SplitCombinedStringL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::SplitCombinedStringL(
+        const TDesC& aString, CDesCArray& aArray )
+    {
+    TInt inputLength = aString.Length();
+    TBool isMulti = inputLength > 2
+        && aString[0] == KMultiSepChar && aString[1] == KMultiSepChar;
+    if ( isMulti )
+        {
+        // allocate a work buffer that is big enough for sure
+        HBufC* buf = HBufC::NewLC( inputLength );
+        TPtr des( buf->Des() );
+        TBool esc = EFalse;
+        // go through the string, find entries, and add them to output array
+        for ( TInt i = 2; i < inputLength; ++i ) // start from 2 because of the magic prefix
+            {
+            TChar c = aString[i];
+            if ( c == '\\' && !esc )
+                {
+                esc = ETrue;
+                }
+            else if ( c == KMultiSepChar && !esc )
+                {
+                // found separator: append to output array, clear buffer, and continue
+                aArray.AppendL( des );
+                des.Zero();
+                }
+            else
+                {
+                esc = EFalse;
+                des.Append( c );
+                }
+            }
+        CleanupStack::PopAndDestroy( buf );
+        }
+    else
+        {
+        // not a combined string: append to array as it is
+        aArray.AppendL( aString );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishContextL( const TDesC& aContextType,
+        const MDesCArray& aContextData )
+    {
+    TInt entryCount = aContextData.MdcaCount();
+    // do nothing if array is empty
+    if ( !entryCount )
+        {
+        return;
+        }
+    // nothing special when having only 1 item
+    if ( entryCount == 1 )
+        {
+        PublishContextL( aContextType, aContextData.MdcaPoint( 0 ) );
+        return;
+        }
+    // at least two items: create the special combined string
+    HBufC* value = BuildCombinedStringL( aContextData );
+    CleanupStack::PushL( value );
+    // publish the combined string
+    PublishContextL( aContextType, *value );
+    CleanupStack::PopAndDestroy( value );
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishContextL
+// This is the version of the function where the real work is performed.
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishContextL( CCFClient& aCFClient,
+        const TDesC& aContextType, const TDesC& aContextData )
+    {
+    CCFContextObject* context = CCFContextObject::NewLC();
+    context->SetSourceL( KHgCFSource );
+    context->SetTypeL( aContextType );
+    context->SetValueL( aContextData );
+    TInt err = aCFClient.PublishContext( *context );
+    if ( err == KErrNotFound )
+        {
+        User::LeaveIfError( aCFClient.DefineContext( KHgCFSource,
+            aContextType, KContextSecurity ) );
+        err = aCFClient.PublishContext( *context );
+        if ( err != KErrArgument ) // ignore -6 which comes e.g. when trying to publish an empty value
+            {
+            User::LeaveIfError( err );
+            }
+        }
+    else if ( err != KErrArgument )
+        {
+        User::LeaveIfError( err );
+        }
+    CleanupStack::PopAndDestroy( context );
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::GetContextL
+// -----------------------------------------------------------------------------
+//
+HBufC* CHgContextUtilityImpl::GetContextL( const TDesC& aContextSource,
+        const TDesC& aContextType )
+    {
+    HBufC* ret = 0;
+    if ( CFReady() )
+        {
+        CCFContextQuery* query = CCFContextQuery::NewLC();
+        query->SetSourceL( aContextSource );
+        query->SetTypeL( aContextType );
+        RContextObjectArray result;
+        TInt err = iCFClient->RequestContext( *query, result );
+        if ( err == KErrNone && result.Count() )
+            {
+            ret = result[0]->Value().Alloc();
+            }
+        result.ResetAndDestroy();
+        CleanupStack::PopAndDestroy( query );
+        }
+    return ret;
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::GetContextL
+// -----------------------------------------------------------------------------
+//
+HBufC* CHgContextUtilityImpl::GetContextL( const TDesC& aContextType )
+    {
+    return GetContextL( KHgCFSource, aContextType );
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishContextDelayedL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishContextDelayedL( const TDesC& aContextType,
+        const TDesC& aContextData, TTimeIntervalMicroSeconds32 aDelay )
+    {
+    Cancel();
+    delete iPendingContextType; iPendingContextType = 0;
+    iPendingContextType = aContextType.AllocL();
+    delete iPendingContextData; iPendingContextData = 0;
+    iPendingContextData = aContextData.AllocL();
+    delete iPendingContextDataArray; iPendingContextDataArray = 0;
+    After( aDelay );
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishContextDelayedL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishContextDelayedL( const TDesC& aContextType,
+        const MDesCArray& aContextData, TTimeIntervalMicroSeconds32 aDelay )
+    {
+    Cancel();
+    delete iPendingContextType; iPendingContextType = 0;
+    iPendingContextType = aContextType.AllocL();
+    delete iPendingContextData; iPendingContextData = 0;
+    if ( iPendingContextDataArray )
+        {
+        iPendingContextDataArray->Reset();
+        }
+    else
+        {
+        iPendingContextDataArray = new ( ELeave ) CDesC16ArrayFlat( KArrayGranularity );
+        }
+    for ( TInt i = 0, ie = aContextData.MdcaCount(); i != ie; ++i )
+        {
+        iPendingContextDataArray->AppendL( aContextData.MdcaPoint( i ) );
+        }
+    After( aDelay );
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::RunL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::RunL()
+    {
+    if ( iPendingContextType )
+        {
+        if ( iPendingContextData )
+            {
+            PublishContextL( *iPendingContextType, *iPendingContextData );
+            }
+        else if ( iPendingContextDataArray )
+            {
+            PublishContextL( *iPendingContextType, *iPendingContextDataArray );
+            }
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::RunError
+// -----------------------------------------------------------------------------
+//
+TInt CHgContextUtilityImpl::RunError( TInt /*aError*/ )
+    {
+    return KErrNone;
+    }
+
+// empty implementations for cfw
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::ContextIndicationL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::ContextIndicationL(
+		const CCFContextIndication& /*aChangedContext*/ )
+	{
+	// empty
+	}
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::ActionIndicationL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::ActionIndicationL(
+		const CCFActionIndication& /*aActionToExecute*/ )
+	{
+	// empty
+	}
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::HandleContextFrameworkError
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::HandleContextFrameworkError( TCFError /*aError*/,
+	    const TDesC& /*aSource*/,
+	    const TDesC& /*aType*/ )
+	{
+	// empty
+	}
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::Extension
+// -----------------------------------------------------------------------------
+//
+TAny* CHgContextUtilityImpl::Extension( const TUid& /*aExtensionUid*/ ) const
+	{
+	return 0;
+	}
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::MakeLinkPublishableLC
+// -----------------------------------------------------------------------------
+//
+HBufC* CHgContextUtilityImpl::MakeLinkPublishableLC(
+        const MVPbkContactLink& aLink )
+    {
+    HBufC* ret = 0;
+    // serialize the link and place it into a 16-bit descriptor
+    // prefixed with one special mark character
+    const MVPbkStreamable* strm = aLink.Streamable();
+    User::LeaveIfNull(strm);
+    CBufFlat* buf = CBufFlat::NewL( KBufGranularity );
+    CleanupStack::PushL( buf );
+    RBufWriteStream ws;
+    CleanupClosePushL( ws );
+    ws.Open( *buf );
+    strm->ExternalizeL( ws );
+    CleanupStack::PopAndDestroy( &ws );
+    TPtr8 p( buf->Ptr( 0 ) );
+    ret = HBufC::NewLC( p.Length() + 1 );
+    TPtr des( ret->Des() );
+    des.Copy( p );
+    _LIT( KTemp, " " );
+    des.Insert( 0, KTemp );
+    des[0] = KHgCFValueLinkMarker; // codescanner::accessArrayElementWithoutCheck2
+    CleanupStack::Pop( ret );
+    CleanupStack::PopAndDestroy( buf );
+    CleanupStack::PushL( ret );
+    return ret;
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishContactContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishContactContextL(
+        const MVPbkStoreContact& aContact,
+        const TTimeIntervalMicroSeconds32& aDelay )
+    {
+    MVPbkContactLink* link = aContact.CreateLinkLC();
+    if ( link )
+        {
+        HBufC* pubstr = MakeLinkPublishableLC( *link );
+        PublishContactContextL( *pubstr, aDelay );
+        CleanupStack::PopAndDestroy( pubstr );
+        }
+    CleanupStack::PopAndDestroy( );//link
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishContactContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishContactContextL(
+        const MVPbkContactLink& aContactLink,
+        const TTimeIntervalMicroSeconds32& aDelay )
+    {
+    HBufC* pubstr = MakeLinkPublishableLC( aContactLink );
+    PublishContactContextL( *pubstr, aDelay );
+    CleanupStack::PopAndDestroy( pubstr );
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishContactContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishContactContextL(
+        const TDesC& aContactName,
+        const TTimeIntervalMicroSeconds32& aDelay )
+    {
+    if ( !aDelay.Int() )
+        {
+        PublishContextL( KHgCFTypeContact, aContactName );
+        }
+    else
+        {
+        PublishContextDelayedL( KHgCFTypeContact, aContactName, aDelay );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishContactContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishContactContextL(
+        const RPointerArray<MVPbkStoreContact>& aContacts,
+        const TTimeIntervalMicroSeconds32& aDelay )
+    {
+    CDesCArray* arr = new ( ELeave ) CDesCArrayFlat( KArrayGranularity );
+    CleanupStack::PushL( arr );
+    for ( TInt i = 0, ie = aContacts.Count(); i != ie; ++i )
+        {
+        MVPbkContactLink* link = aContacts[i]->CreateLinkLC();
+        if ( link )
+            {
+            HBufC* pubstr = MakeLinkPublishableLC( *link );
+            arr->AppendL( *pubstr );
+            CleanupStack::PopAndDestroy( pubstr );
+            }
+        CleanupStack::PopAndDestroy( );//link
+        }
+    PublishContactContextL( *arr, aDelay );
+    CleanupStack::PopAndDestroy( arr );
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishContactContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishContactContextL(
+        const CVPbkContactLinkArray& aContactLinks,
+        const TTimeIntervalMicroSeconds32& aDelay )
+    {
+    CDesCArray* arr = new ( ELeave ) CDesCArrayFlat( KArrayGranularity );
+    CleanupStack::PushL( arr );
+    for ( TInt i = 0, ie = aContactLinks.Count(); i != ie; ++i )
+        {
+        HBufC* pubstr = MakeLinkPublishableLC( aContactLinks.At( i ) );
+        arr->AppendL( *pubstr );
+        CleanupStack::PopAndDestroy( pubstr );
+        }
+    PublishContactContextL( *arr, aDelay );
+    CleanupStack::PopAndDestroy( arr );
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishContactContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishContactContextL(
+        const MDesCArray& aContactNames,
+        const TTimeIntervalMicroSeconds32& aDelay )
+    {
+    if ( !aDelay.Int() )
+        {
+        PublishContextL( KHgCFTypeContact, aContactNames );
+        }
+    else
+        {
+        PublishContextDelayedL( KHgCFTypeContact, aContactNames, aDelay );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishTextContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishTextContextL( const TDesC& aText,
+        const TTimeIntervalMicroSeconds32& aDelay )
+    {
+    if ( !aDelay.Int() )
+        {
+        PublishContextL( KHgCFTypeText, aText );
+        }
+    else
+        {
+        PublishContextDelayedL( KHgCFTypeText, aText, aDelay );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishUrlContextL
+// -----------------------------------------------------------------------------
+//   
+void CHgContextUtilityImpl::PublishUrlContextL( const TDesC& aUrl,
+        const TTimeIntervalMicroSeconds32& aDelay )
+    {
+    if ( !aDelay.Int() )
+        {
+        PublishContextL( KHgCFTypeUrl, aUrl );
+        }
+    else
+        {
+        PublishContextDelayedL( KHgCFTypeUrl, aUrl, aDelay );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishTimeContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishTimeContextL( const TTime& aTime,
+        const TTimeIntervalMicroSeconds32& aDelay )
+    {
+    // YYYYMMDD:HHMMSS.MMMMMM
+    const TInt KDateTimeLength = 22;
+    const TInt KYearLength = 4;
+    const TInt KMonthLength = 2;
+    const TInt KDayLength = 2;
+    _LIT( KTimeZero, ":010101.000000");
+
+    TDateTime dt = aTime.DateTime();
+    TBuf<KDateTimeLength> buf;
+    buf.AppendNumFixedWidth( dt.Year(), EDecimal, KYearLength );
+    buf.AppendNumFixedWidth( dt.Month(), EDecimal, KMonthLength );
+    buf.AppendNumFixedWidth( dt.Day(), EDecimal, KDayLength );
+    buf.Append( KTimeZero );
+    if ( !aDelay.Int() )
+        {
+        PublishContextL( KHgCFTypeActiveDate, buf );
+        }
+    else
+        {
+        PublishContextDelayedL( KHgCFTypeActiveDate, buf, aDelay );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishPhotoContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishPhotoContextL(
+        const TDesC& aFilename,
+        const TTimeIntervalMicroSeconds32& aDelay )
+    {
+    if ( !aDelay.Int() )
+        {
+        PublishContextL( KHgCFTypePhoto, aFilename );
+        }
+    else
+        {
+        PublishContextDelayedL( KHgCFTypePhoto, aFilename, aDelay );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishPhotoContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishPhotoContextL(
+        TItemId aMdeItemId,
+        CMdESession& aMdeSession,
+        const TTimeIntervalMicroSeconds32& aDelay )
+    {
+    CMdEObject* obj = aMdeSession.GetObjectL( aMdeItemId );
+    if ( obj )
+        {
+        CleanupStack::PushL( obj );
+        PublishPhotoContextL( obj->Uri(), aDelay );
+        CleanupStack::PopAndDestroy( obj );
+        }
+    else
+        {
+        User::Leave( KErrNotFound );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishTvContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishTvContextL( const TDesC& aChannelName,
+        const TDesC& aProgramName, const TDesC& aProgramDescription,
+        const TDesC& aGenre )
+    {
+    TPtrC channelName( aChannelName.Length() ? aChannelName
+        : KHgCFValueUnknownInfo );
+    TPtrC programName( aProgramName.Length() ? aProgramName
+        : KHgCFValueUnknownInfo );
+    TPtrC programDesc( aProgramDescription.Length() ? aProgramDescription
+        : KHgCFValueUnknownInfo );
+    TPtrC programGenre( aGenre.Length() ? aGenre : KHgCFValueUnknownInfo );
+
+    // Publish description/genre first because it is unlikely to have those
+    // in rules so their content will be available for sure when an action
+    // is triggered.
+    PublishContextL( KHgCFTypeTvProgramDesc, programDesc );
+    PublishContextL( KHgCFTypeTvProgramGenre, programGenre );
+    PublishContextL( KHgCFTypeTvChannelName, channelName );
+    PublishContextL( KHgCFTypeTvProgramName, programName );
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishServiceIdL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishServiceIdL( const TDesC& aServiceId,
+        const TDesC& aAccountId,
+        const TTimeIntervalMicroSeconds32& aDelay )
+    {
+    HBufC* combinedIdBuf = HBufC::NewLC( aServiceId.Length()
+        + aAccountId.Length() + 1 );
+    TPtr combinedId( combinedIdBuf->Des() );
+    _LIT( KCombinedFormat, "%S:%S" );
+    combinedId.Format( KCombinedFormat, &aServiceId, &aAccountId );
+    PublishContactContextL( combinedId, aDelay );
+    CleanupStack::PopAndDestroy( combinedIdBuf );
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::RePublishWhenFgL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::RePublishWhenFgL( TBool aEnable )
+    {
+    if ( iEnv )
+        {
+        if ( iFgWatchEnabled )
+            {
+            iEnv->RemoveForegroundObserver( *this );
+            }
+        iFgWatchEnabled = aEnable;
+        if ( iFgWatchEnabled )
+            {
+            iEnv->AddForegroundObserverL( *this );
+            }
+        }
+    }
+
+// callbacks from CCoeEnv
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::HandleGainingForeground
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::HandleGainingForeground()
+    {
+    if ( iLastContextType && iLastContextData )
+        {
+        TRAP_IGNORE( PublishContextL( *iLastContextType, *iLastContextData ) );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::HandleLosingForeground
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::HandleLosingForeground()
+    {
+    // nothing to do here
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::IsForeground
+// -----------------------------------------------------------------------------
+//
+TBool CHgContextUtilityImpl::IsForeground()
+    {
+    if ( iEnv )
+        {
+        TInt rootWgId = iEnv->RootWin().WindowGroupId();
+        TInt focusWgId = iEnv->WsSession().GetFocusWindowGroup();
+        return rootWgId == focusWgId;
+        }
+    else
+        {
+        return ETrue;
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::AllowedToPublish
+// -----------------------------------------------------------------------------
+//
+TBool CHgContextUtilityImpl::AllowedToPublish()
+    {
+    TBool result = !iEnv || iAllowPublishFromBackground || IsForeground();
+    RDebug::Printf( "[hgctxutil] AllowedToPublish = %d", result );
+    return result;
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::AllowPublishFromBackground
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::AllowPublishFromBackground( TBool aAllow )
+    {
+    iAllowPublishFromBackground = aAllow;
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::AddMusicContextInfoL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::AddMusicContextInfoL( 
+    const TDesC& aKey, const TDesC& aData )
+    {   
+    // Key needs to be provided and also it shouldn't exist in the table.
+    // Latter case is simple safe measure, as RPtrHasMap won't delete existing
+    // objects in InsertL, so adding same key twice would cause memory leak.
+    // The use case of adding same key twice is not 'real world' case, so 
+    // this method can simply leave, when same key is offered again.
+    __ASSERT_ALWAYS( aKey.Length(), User::Leave( KErrNotFound ) );
+    __ASSERT_ALWAYS( 
+        !iMusicContextInfo.Find( aKey ), User::Leave( KErrAlreadyExists ) );
+    
+    // Hash table needs pointers and it should own the pointers, so allocate
+    // key and data, and add them to table. In case the data is empty, add
+    // unknown information, since some data needs to be in the action field.
+    HBufC* key = aKey.AllocLC();
+    HBufC* data = aData.Length() ? 
+        aData.AllocLC() : KHgCFValueUnknownInfo().AllocLC();
+    iMusicContextInfo.InsertL( key, data );
+    CleanupStack::Pop( 2, key );
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishMusicContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishMusicContextL( 
+    const TTimeIntervalMicroSeconds32& aDelay )
+    {
+    // If nothing has been done, just leave. No point of publishing entirely
+    // empty music context.
+    __ASSERT_ALWAYS( iMusicContextInfo.Count(), User::Leave( KErrNotReady ) );    
+    
+    // Before publishing anything, make sure all keys contain at least some
+    // data.
+    VerifyAndPublishMusicContextL( KHgCFTypeMusicState, aDelay );
+    VerifyAndPublishMusicContextL( KHgCFTypeMusicArtist, aDelay );
+    VerifyAndPublishMusicContextL( KHgCFTypeMusicTitle, aDelay );
+    VerifyAndPublishMusicContextL( KHgCFTypeMusicAlbum, aDelay );
+    VerifyAndPublishMusicContextL( KHgCFTypeMusicAlbumArt, aDelay );
+    VerifyAndPublishMusicContextL( KHgCFTypeMusicUri, aDelay );
+    VerifyAndPublishMusicContextL( KHgCFTypeMusicGenre, aDelay );
+    VerifyAndPublishMusicContextL( KHgCFTypeMusicType, aDelay );
+    
+    // Clear all data from hash table, so new music context can be published.
+    iMusicContextInfo.ResetAndDestroy();
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::VerifyAndPublishMusicContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::VerifyAndPublishMusicContextL( 
+    const TDesC& aKey,
+    const TTimeIntervalMicroSeconds32& aDelay )
+    {
+    TDesC* data = iMusicContextInfo.Find( aKey );
+    if ( !data )
+        {
+        // Key didn't contain any data, just create the key with empty info.
+        AddMusicContextInfoL( aKey, KNullDesC );
+        data = iMusicContextInfo.Find( aKey );
+        }
+    
+    PublishContextL( aKey, *data, aDelay );
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishContextL(
+    const TDesC & aContextType, 
+    const TDesC & aContextData, 
+    const TTimeIntervalMicroSeconds32& aDelay )
+    {
+    if ( !aDelay.Int() )
+        {
+        PublishContextL( aContextType, aContextData );
+        }
+    else
+        {
+        PublishContextDelayedL( aContextType, aContextData, aDelay );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHgContextUtilityImpl::PublishRadioContextL
+// -----------------------------------------------------------------------------
+//
+void CHgContextUtilityImpl::PublishRadioContextL( 
+        const TDesC& aRadioName,
+        const TDesC& aRadioUrl,
+        const TDesC& aRadioFrequency,
+        const TDesC& aRadioRDSPI )
+    {
+    TPtrC radioName( aRadioName.Length() ? aRadioName
+        : KHgCFValueUnknownInfo );
+    TPtrC radioUrl( aRadioUrl.Length() ? aRadioUrl
+        : KHgCFValueUnknownInfo );
+    TPtrC radioFrequency( aRadioFrequency.Length() ? aRadioFrequency
+        : KHgCFValueUnknownInfo );
+    TPtrC radioRDSPI( aRadioRDSPI.Length() ? aRadioRDSPI
+        : KHgCFValueUnknownInfo );
+
+    PublishContextL( KHgCFTypeMusicRadioRDSPI, radioRDSPI );
+    PublishContextL( KHgCFTypeMusicRadioFrequency, radioFrequency );
+    PublishContextL( KHgCFTypeMusicRadioUrl, radioUrl );
+    PublishContextL( KHgCFTypeMusicRadioName, radioName );
+    }
+
+// end of file