contextframework/cfwplugins/ApplicationStateSourcePlugIn/src/ApplicationStateSourcePlugin.cpp
author William Roberts <williamr@symbian.org>
Fri, 23 Apr 2010 14:37:17 +0100
branchRCL_3
changeset 22 c82a39b81a38
parent 13 096dad6e50a9
child 57 5e7d68cc22e0
permissions -rw-r--r--
Rework addition of Symbian splash screen to reduce the source impact (uses SVG from Bug 2414) Notes: by using the OPTION SOURCEDIR parameter in the mifconv extension instructions, I can arrange to use the same source file name in sfimage, without having to export over the original Nokia file. This means that the name inside splashscreen.mbg is the same, which removes the need for the conditional compilation in SplashScreen.cpp, and gets rid of sf_splashscreen.mmp.

/*
* Copyright (c) 2002-2009 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:  ?Description
*
*/


// INCLUDE FILES
#include <cfcontextinterface.h>
#include <cfcontextsourcesetting.h>
#include <cfcontextsourcesettingarray.h>
#include <cfcontextsourcesettingparameter.h>
#include <cfkeyvaluepair.h>
#include <centralrepository.h>
#include <apgwgnam.h>
#include <applicationorientation.h>
#include <aknappui.h>
#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include <vwsdefpartner.h>
#endif
#include "ApplicationStateSourcePlugin.h"
#include "ApplicationStateSourceContextDef.h"
#include "ContextFrameworkPrivateCRKeys.h"
#include "trace.h"

// CONSTANTS

#ifdef _DEBUG
// Panic codes
enum TPanicCode
    {
    EInvalidUid,
    EInvalidAttribute
    };
#endif

_LIT( KDefaultApplicationName,  "DefaultAppName" );
_LIT( KDefaultViewName,         "DefaultViewName" );
_LIT( KAttributeAppUid,         "AppUid" );
_LIT( KAttributeAppName,        "AppName" );
_LIT( KTagView,                 "View" );
_LIT( KAttributeUid,            "Uid" );
_LIT( KAttributeName,           "Name" );

const TUint KLimit = 0xFFFFFFFF;

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::CApplicationStateSourcePlugIn
// C++ default constructor can NOT contain any code, that might leave.
// -----------------------------------------------------------------------------
//
CApplicationStateSourcePlugIn::CApplicationStateSourcePlugIn( TContextSourceInitParams* aParams ):
    CCFContextSourcePlugIn( aParams ),
    iDefStateSetManyTimes( 0 )
    {
    FUNC_LOG;
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::ConstructL
// Symbian second-phase constructor that may leave.
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::ConstructL()
    {
    FUNC_LOG;

    iDefaultAppName = KApplicationStateDefaultValue().AllocL();
    iDefaultViewName = KApplicationStateDefaultValue().AllocL();
    iContext = CCFContextObject::NewL();
    
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CApplicationStateSourcePlugIn* CApplicationStateSourcePlugIn::NewL(
    TContextSourceInitParams* aParams )
    {
    FUNC_LOG;

    CApplicationStateSourcePlugIn* self = CApplicationStateSourcePlugIn::NewLC(
        aParams );
    CleanupStack::Pop( self );
    return self;
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::NewLC
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CApplicationStateSourcePlugIn* CApplicationStateSourcePlugIn::NewLC(
    TContextSourceInitParams* aParams )
    {
    FUNC_LOG;

    CApplicationStateSourcePlugIn* self = new( ELeave ) CApplicationStateSourcePlugIn(
        aParams );
    CleanupStack::PushL( self );
    self->ConstructL();
    return self;
    }

// Destructor
CApplicationStateSourcePlugIn::~CApplicationStateSourcePlugIn()
    {
    FUNC_LOG;

    if( iCRAppOrientationListener )
        {
        iCRAppOrientationListener->StopListening();
        delete iCRAppOrientationListener;
        }
    iUidOrientationList.Close();    
    delete iCRAppOrientation;
    iApplicationSettings.ResetAndDestroy();
    delete iSettings;
    delete iVws;
    delete iDefaultViewName;
    delete iDefaultAppName;
    delete iApplDefaultState;
    delete iContext;
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::HandleSettingL
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::HandleSettingL(
    CCFContextSourceSettingArray* aSettingList )
    {
    FUNC_LOG;

    // Take ownership from settings
    iSettings = aSettingList;
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::DefineContextsL
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::DefineContextsL()
    {
    FUNC_LOG;

    // Define contexts here
    _LIT_SECURITY_POLICY_C2(
        appStatePolicy, ECapabilitySwEvent, ECapabilityUserEnvironment );

    TInt err = iCF.DefineContext( KApplicationStateSource,
       KApplicationStateForegroundApplicationType,
       appStatePolicy );
    User::LeaveIfError( err );
    
    err = iCF.DefineContext( KApplicationStateSource,
       KApplicationStateForegroundApplicationViewType,
       appStatePolicy );
    User::LeaveIfError( err );

    err = iCF.DefineContext( KApplicationStateSource,
       KApplicationStateBackgroundApplicationType,
       appStatePolicy );
    User::LeaveIfError( err );
    
    err = iCF.DefineContext( KApplicationStateSource,
        KFgApplicationOrientation,
        KFgApplicationOrientationSec );
    User::LeaveIfError( err );
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::InitializeL
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::InitializeL()
    {
    FUNC_LOG;

    iContext->SetSourceL( KApplicationStateSource );

    // Get default app orientation
    CRepository* cenRep = CRepository::NewL( KCRUidCFApplDefaultState );
    TInt err = cenRep->Get( KApplDefaultState, iDefaultFgAppOrientation );
    if( err != KErrNone )
        {
        ERROR( err, "Failed to get default fg app orientation" );
        iDefaultFgAppOrientation = 0;
        }
    delete cenRep;
    cenRep = NULL;

    // Create KCRUidDefaultAppOrientation repository for default app orientation
    iCRAppOrientation = CRepository::NewL( KCRUidDefaultAppOrientation ); 
    
    // Create KCRUidDefaultAppOrientation repository listener
    iCRAppOrientationListener = CCenRepNotifyHandler::NewL(
        *this, *iCRAppOrientation );
    iCRAppOrientationListener->StartListeningL();
        
    if( iSettings )
        {
        InitializeApplicationsFromXMLL();
        InitializeFgApplicationL();

        // Free memory allocated for settings since we don't need them anymore
        delete iSettings;
        iSettings = NULL;
        }

    iVws = CVwsSessionWrapper::NewL( *this );
    iVws->NotifyNextActivation();
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::InitializeApplicationsFromXMLL
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::InitializeApplicationsFromXMLL()
    {
    FUNC_LOG;

     for( TInt i = 0; i < iSettings->Count(); ++i )
        {
        // Check attributes
        TBool defaultValueDefinitions = EFalse;
        TPtrC appUid( KNullDesC );
        TPtrC appName( KNullDesC );
        const RKeyValueArray& attributes = iSettings->Item( i ).Attributes();

        // Check application and view names (also default values)
        for( TInt j = 0; j < attributes.Count(); ++j )
            {
            TPtrC key( attributes[ j ]->Key() );
            TPtrC value( attributes[ j ]->Value() );

            if( key.CompareF( KAttributeAppUid ) == KErrNone )
                {
                appUid.Set( value );
                }
            else if( key.CompareF( KAttributeAppName ) == KErrNone )
                {
                appName.Set( value );
                }
            else if( key.CompareF( KDefaultApplicationName ) == KErrNone )
                {
                delete iDefaultAppName;
                iDefaultAppName = NULL;
                iDefaultAppName = value.AllocL();
                defaultValueDefinitions = ETrue;
                }
            else if( key.CompareF( KDefaultViewName ) == KErrNone )
                {
                delete iDefaultViewName;
                iDefaultViewName = NULL;
                iDefaultViewName = value.AllocL();
                defaultValueDefinitions = ETrue;
                }
            }

        if ( defaultValueDefinitions )
            {
            continue; // Default value definitions contain only attributes.
            }

        if ( appUid.Length() == 0 || appName.Length() == 0 )
            {
            ERROR_GEN( "Either AppUid or AppName tag (or both) not found, skipping" );
            continue; // Skip this definition.
            }

        TUint32 uid = 0;
        TInt err = ConvertToHex( appUid, uid );
        if( err != KErrNone )
            {
            ERROR_1( err, "Invalid uid [%S] for an application, skipping", &appUid );
            continue; // Skip this definition.
            }

        // below:                                  CLEANUP<< appStateSettings
        CCFApplicationStateSettings* appStateSettings
            = CCFApplicationStateSettings::NewLC( TUid::Uid( uid ), appName );

        // Check parameters
        const RSettingParameterArray& parameters = iSettings->Item( i ).Parameters();
        for( TInt j = 0; j < parameters.Count(); ++j )
            {
            if ( parameters[ j ]->Name() != KTagView )
                {
                continue; // At the moment, handle only view tags
                }

            TPtrC viewUid( KNullDesC );
            TPtrC viewName( KNullDesC );
            const RKeyValueArray& paramAttr = parameters[ j ]->Attributes();
            for( TInt k = 0; k < paramAttr.Count(); ++k )
                {
                TPtrC key( paramAttr[ k ]->Key() );
                TPtrC value( paramAttr[ k ]->Value() );

                // Check view uid
                if( key.CompareF( KAttributeUid ) == KErrNone )
                    {
                    viewUid.Set( value );
                    }
                // Check view name
                else if( key.CompareF( KAttributeName ) == KErrNone )
                    {
                    viewName.Set( value );
                    }
                }

            if ( viewUid.Length() == 0 || viewName.Length() == 0 )
                {
                ERROR_GEN( "Either View's Uid or Name tag (or both) not found, skipping" );
                continue; // Skip this definition.
                }

            uid = 0;
            TInt err = ConvertToHex( viewUid, uid );
            if( err != KErrNone )
                {
                ERROR_1( err, "Invalid view uid [%S], skipping", &viewUid );
                continue; // Skip this definition.
                }

            // below:                              CLEANUP<< viewUidName
            CCFUidNamePair* viewUidName
                = CCFUidNamePair::NewLC( TUid::Uid( uid ), viewName );
            appStateSettings->AddViewSettingsL( viewUidName );
            CleanupStack::Pop( viewUidName );   // CLEANUP>> viewUidName
            }

        iApplicationSettings.AppendL( appStateSettings );
        CleanupStack::Pop( appStateSettings );  // CLEANUP>> appStateSettings
        }
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::HandleViewEventL
// Maps view activations to contexts.
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::HandleViewEventL(
    const TVwsViewEvent& aEvent )
    {
    FUNC_LOG;

    // Renew listening
    iVws->NotifyNextActivation();

    // Handle the view server event
    DoHandleViewEventL( aEvent );

    // Log event members!
    INFO_1( "HandleViewEventL: eventType: [%d]", aEvent.iEventType );
    
    INFO_2( "HandleViewEventL: viewOne [appUid:%S] [viewUid%S]",
        &(const TDesC&)aEvent.iViewOneId.iAppUid.Name(),
        &(const TDesC&)aEvent.iViewOneId.iViewUid.Name() );
    
    INFO_2( "HandleViewEventL: viewTwo [appUid:%S] [viewUid%S]",
        &(const TDesC&)aEvent.iViewTwoId.iAppUid.Name(),
        &(const TDesC&)aEvent.iViewTwoId.iViewUid.Name() );
    }

//------------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::HandleNotifyGeneric
//------------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::HandleNotifyGeneric( TUint32 aId )
    {
    FUNC_LOG;

    // Get the new value from cenrep
    TInt val = 0;
    TInt err = iCRAppOrientation->Get( aId, val );
    if( err == KErrNone )
        {
        // New value found, check if the element is found from cache
        TInt index = FindByUid( TUid::Uid( aId ) );
        if( index != KErrNotFound )
            {
            // Update cache element
            TUidOrientationPair& pair = iUidOrientationList[index];
            pair.iOrientation = val;
            INFO_2( "Updated cache uid: [0x%x] to orientation: [%d]", pair.iUid.iUid, val );
            }
        else
            {
            // Add new cache element
            TUidOrientationPair pair( TUid::Uid( aId ), val );
            err = iUidOrientationList.Append( pair );
            if( err == KErrNone )
                {
                INFO_2( "Added cache uid: [0x%x], orientation: [%d]", pair.iUid.iUid, val );
                }
            else
                {
                ERROR_1( err, "Failed to add cache uid: [0x%x]", pair.iUid.iUid );
                }
            }
        }
    else
        {
        ERROR_1( err, "Failed to get new value from CR key: [0x%x]", aId );
        }
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::DoHandleViewEventL
// Maps view activations to contexts.
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::DoHandleViewEventL(
    const TVwsViewEvent& aEvent )
    {
    FUNC_LOG;

    TPtrC bgApp( *iDefaultAppName );
    TPtrC fgApp( *iDefaultAppName );
    TPtrC fgView( *iDefaultViewName );
    TBool bgFound( EFalse );
    TBool fgFound( EFalse );

    if ( aEvent.iViewTwoId.iAppUid == TUid::Null() )
        {
        // Previous foreground application was closed, get the stored name for
        // background application's name.
        bgFound = ETrue;
        bgApp.Set( iPreviousForegroundApplication );
        }

    for ( TInt i = 0; i < iApplicationSettings.Count(); ++i )
        {
        CCFApplicationStateSettings* appSettings = iApplicationSettings[ i ];
        if ( !fgFound && aEvent.iViewOneId.iAppUid == appSettings->Uid() )
            {
            fgApp.Set( appSettings->Name() );
            appSettings->GetViewName( aEvent.iViewOneId.iViewUid, fgView );
            fgFound = ETrue;
            }
        if ( !bgFound && aEvent.iViewTwoId.iAppUid == appSettings->Uid() )
            {
            bgApp.Set( appSettings->Name() );
            bgFound = ETrue;
            }
        if ( bgFound && fgFound )
            {
            break;
            }
        }

    TBool publishFgApp( EFalse );
    TBool publishFgView( EFalse );
    TBool publishBgApp( EFalse );

    if ( iPreviousForegroundApplication.Compare( fgApp ) != 0 )
        {
        publishFgApp = ETrue;
        }
    else if ( iPreviousForegroundView.Compare( fgView ) != 0 )
        {
        publishFgView = ETrue;
        }

    if ( bgApp != fgApp )
        {
        publishBgApp = ETrue;
        }

    iPreviousForegroundApplication.Set( fgApp ); // Store for next round
    iPreviousForegroundView.Set( fgView );

    // Publish foreground application and view contexts.
    RThread thread;

    if ( publishFgApp )
        {
        iContext->SetTypeL( KApplicationStateForegroundApplicationType );
        iContext->SetValueL( fgApp );
        iCF.PublishContext( *iContext, thread );
        }

    if ( publishFgApp || publishFgView )
        {
        iContext->SetTypeL( KApplicationStateForegroundApplicationViewType );
        iContext->SetValueL( fgView );
        iCF.PublishContext( *iContext, thread );
        }

    if ( publishBgApp )
        {
        iContext->SetTypeL( KApplicationStateBackgroundApplicationType );
        iContext->SetValueL( bgApp );
        iCF.PublishContext( *iContext, thread );
        }
    
    // Publish foreground application orientation context
    TPtrC fgAppOrientation( ResolveFgAppOrientation( aEvent.iViewOneId.iAppUid ) );
    if( fgAppOrientation.Length() )
        {
        // Fg app found
        if( fgAppOrientation.Compare( iPreviousOrientation ) != KErrNone )
            {
            // Update for next round
            iPreviousOrientation.Set( fgAppOrientation );
            
            // Fg app orientation changed, publish new context
            iContext->SetTypeL( KFgApplicationOrientation );
            iContext->SetValueL( fgAppOrientation );
            iCF.PublishContext( *iContext, thread );
            }
        }

    thread.Close();
    }

//------------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::ResolveFgAppOrientation
//------------------------------------------------------------------------------
//
TPtrC CApplicationStateSourcePlugIn::ResolveFgAppOrientation(
    const TUid& aUid )
    {
    FUNC_LOG;
    
    TPtrC fgAppOrientation( KNullDesC );
    TInt val = 0;
    
    // Check cache first
    TInt index = FindByUid( aUid );
    if( index != KErrNotFound )
        {
        // App found from cache
        val = iUidOrientationList[index].iOrientation;
        }
    else
        {
        // Get the SID from cenrep and update cache
        TInt err = iCRAppOrientation->Get( aUid.iUid, val );
        if( err != KErrNone )
            {
            INFO_1( "Application uid: [0x%x] not found, using default orientation",
                aUid.iUid );
            val = iDefaultFgAppOrientation;
            }
        
        // Update cache
        TUidOrientationPair pair( aUid, val );
        err = iUidOrientationList.Append( pair );
        if( err != KErrNone )
            {
            ERROR_2( err, "Failed to add uid: [0x%x], orientation: [%d] pair in cache",
                aUid.iUid, val );
            }
        }
    
    // Resolve context value
    if( val == CAknAppUiBase::EAppOrientationPortrait )
        {
        // Portrait
        fgAppOrientation.Set( TPtrC(
            KFgApplicationOrientationVal[EFgApplicationOrientationPortrait] ) );
        }
    else if( val == CAknAppUiBase::EAppOrientationLandscape )
        {
        // Landscape
        fgAppOrientation.Set( TPtrC(
            KFgApplicationOrientationVal[EFgApplicationOrientationLandscape] ) );
        }
    else
        {
        // Automatic
        fgAppOrientation.Set( TPtrC(
            KFgApplicationOrientationVal[EFgApplicationOrientationAutomatic] ) );
        }
    
    return fgAppOrientation;
    }

// -----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::InitializeFgApplicationL
// -----------------------------------------------------------------------------
//
void CApplicationStateSourcePlugIn::InitializeFgApplicationL()
    {
    FUNC_LOG;

    RWsSession wsSession;
    User::LeaveIfError( wsSession.Connect() );
    CleanupClosePushL( wsSession );
    TUid appUid( TUid::Null() );

    TInt focScr( wsSession.GetFocusScreen() );
    TInt focWndGrp( wsSession.GetFocusWindowGroup() );
    TInt wndGrpPrio( wsSession.GetWindowGroupOrdinalPriority( focWndGrp ) );
    
    INFO_1( "CApplicationStateSourcePlugIn::focScr     = [%d]", focScr );
    INFO_1( "CApplicationStateSourcePlugIn::focWndGrp  = [%d]", focWndGrp );
    INFO_1( "CApplicationStateSourcePlugIn::wndGrpPrio = [%d]", wndGrpPrio );

    CApaWindowGroupName* wndGrpName = CApaWindowGroupName::NewL( wsSession );
    CleanupStack::PushL( wndGrpName );

    wndGrpName->ConstructFromWgIdL( focWndGrp );

    TPtrC appCaption = wndGrpName->Caption();
    TPtrC appName = wndGrpName->AppUid().Name();

    // if focScr == 0 it's not CoverUi
    // if focScr != 0 it's CoverUi

    // if wndGrpPrio == 0 it's S60 application

    if ( ( focScr != 0 ) || ( ( focScr == 0 ) && ( wndGrpPrio == 0 ) ) )
        {
        INFO( "CApplicationStateSourcePlugIn::It's CoverUi or S60 application" );
        // It is CoverUi
        // or
        // It is Not CoverUi and it is S60 application

        appUid = wndGrpName->AppUid();
        INFO_1( "appUid = [0x%x]", appUid.iUid );
        }
    else
        {
        // Default values will still be published
        TUid tmpAppUid( TUid::Null() );
        tmpAppUid = wndGrpName->AppUid();

        INFO( "CApplicationStateSourcePlugIn::It's neiher CoverUi nor S60 application" );
        INFO_1( "appUid = [0x%x]", tmpAppUid.iUid );
        }

    INFO_1( "appCaption = [%S]", &appCaption );
    INFO_1( "appName    = [%S]", &appName );

    CleanupStack::PopAndDestroy( wndGrpName );
    CleanupStack::PopAndDestroy( &wsSession );

    TPtrC bgApp( *iDefaultAppName );
    TPtrC fgApp( *iDefaultAppName );
    TPtrC fgView( *iDefaultViewName );

    if ( appUid != TUid::Null() )
        {
        for ( TInt i = 0; i < iApplicationSettings.Count(); ++i )
            {
            CCFApplicationStateSettings* appSettings = iApplicationSettings[ i ];
            if ( appUid == appSettings->Uid() )
                {
                fgApp.Set( appSettings->Name() );
                break;
                }
            }
        }

    iPreviousForegroundApplication.Set( fgApp ); // Store for next round
    iPreviousForegroundView.Set( fgView );

    RThread thread;

    // Publish foreground application
    iContext->SetTypeL( KApplicationStateForegroundApplicationType );
    iContext->SetValueL( fgApp );
    iCF.PublishContext( *iContext, thread );

    // Publish  foreground view
    iContext->SetTypeL( KApplicationStateForegroundApplicationViewType );
    iContext->SetValueL( fgView );
    iCF.PublishContext( *iContext, thread );

    // Publish background application
    iContext->SetTypeL( KApplicationStateBackgroundApplicationType );
    iContext->SetValueL( bgApp );
    iCF.PublishContext( *iContext, thread );
    
    // Publish foreground application orientation
    TPtrC fgAppOrientation( ResolveFgAppOrientation( appUid ) );
    iContext->SetTypeL( KFgApplicationOrientation );
    iContext->SetValueL( fgAppOrientation );
    iCF.PublishContext( *iContext, thread );
    
    // Store for next round
    iPreviousOrientation.Set( fgAppOrientation );

    thread.Close();
    }


//-----------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::ConvertToHex
//-----------------------------------------------------------------------------
//
TInt CApplicationStateSourcePlugIn::ConvertToHex( const TDesC& aString,
    TUint32& aValue )
    {
    FUNC_LOG;

    TInt err = KErrNone;
    TLex lex( aString );

    // Get rid of '0x'
    const TInt skip = 2;
    lex.Inc( skip );
    if( lex.BoundedVal( aValue, EHex, KLimit ) != KErrNone )
        {
        err = KErrNotFound;
        }
    return err;
    }

//------------------------------------------------------------------------------
// CApplicationStateSourcePlugIn::FindByUid
//------------------------------------------------------------------------------
//
TInt CApplicationStateSourcePlugIn::FindByUid( const TUid& aUid ) const
    {
    FUNC_LOG;
    
    TInt index = KErrNotFound;
    for( TInt i = 0; i < iUidOrientationList.Count(); i++ )
        {
        if( iUidOrientationList[i].iUid == aUid )
            {
            index = i;
            break;
            }
        }
    return index;
    }