emailservices/emailserver/cmailhandlerplugin/src/emailsoundstates.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 19 Feb 2010 22:37:30 +0200
branchRCL_3
changeset 8 e1b6206813b4
parent 0 8466d47a6819
permissions -rw-r--r--
Revision: 201003 Kit: 201007

/*
* Copyright (c) 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:  Class to handle email sound playing.
*
*/


#include <AudioPreference.h>
#include <MProfileEngine.h>
#include <MProfile.h>
#include <MProfileTones.h>
#include <MProfileExtraTones.h>
#include <TProfileToneSettings.h>
#include <CProfileChangeNotifyHandler.h>
#include <coreapplicationuisdomainpskeys.h>

#include "emailtrace.h"
#include "memailsoundstatecontext.h"
#include "emailsoundstates.h"
#include "fsnotificationhandlertimer.h"
#include "cmailhandlerpluginpanic.h"

// Local constants
static const TInt KToneMinVolume = 0;
static const TInt KToneMaxVolume = 10;
static const TInt KDelayTimerInterval = 60000000; // 60s

// ---------------------------------------------------------------------------
// Initializes the state machine by setting initial state and creating
// failure state that can be entered at any time in error cases.
// ---------------------------------------------------------------------------
//
void CEmailSoundState::InitializeL( MEmailSoundStateContext* aContext )
    {
    FUNC_LOG
    TAny* tls = Dll::Tls();
    if ( !tls )
        {
        // prepare failed state so its always available
        CEmailSoundState* failedState = new ( ELeave ) CEmailSoundFailed( *aContext );
        Dll::SetTls( failedState );
        // now enter to initial state, use failed state for obtaining context
        CEmailInitilisingTone::EnterL( failedState );
        }
    }

// ---------------------------------------------------------------------------
// Exits the state machine and frees memory allocated for states
// ---------------------------------------------------------------------------
//
void CEmailSoundState::Uninitialize( CEmailSoundState* aCurrentState )
    {
    FUNC_LOG

    if ( aCurrentState )
        {
        // Exit must be called before deleting failedState because it may
        // also be current state.
        aCurrentState->Exit();
        }

    TAny* tls = Dll::Tls();
    if ( tls )
        {
        CEmailSoundState* failedState =
            reinterpret_cast<CEmailSoundState*>( tls );
        delete failedState;
        Dll::SetTls( NULL );
        }
    }

// ---------------------------------------------------------------------------
// No-op in base class, also enough for some derived classes.
// ---------------------------------------------------------------------------
//
void CEmailSoundState::ProfileChanged()
    {
    FUNC_LOG
    }

// ---------------------------------------------------------------------------
// Tone initialization failure always leads to failed state.
// ---------------------------------------------------------------------------
//
void CEmailSoundState::AudioInitFailed()
    {
    FUNC_LOG
    // enter to failed state.
    StateTransition( EFailed );
    }

// ---------------------------------------------------------------------------
// Derived classes implement
// ---------------------------------------------------------------------------
//
void CEmailSoundState::AudioInitCompleted()
    {
    FUNC_LOG
    }

// ---------------------------------------------------------------------------
// Derived classes implement
// ---------------------------------------------------------------------------
//
void CEmailSoundState::AudioPlayCompleted()
    {
    FUNC_LOG
    }

// ---------------------------------------------------------------------------
// Handled in CEmailReadyToPlay state, in most other states ignored.
// ---------------------------------------------------------------------------
//
void CEmailSoundState::PlayTone()
    {
    FUNC_LOG
    }

// ---------------------------------------------------------------------------
// Applicable only in playing state
// ---------------------------------------------------------------------------
//
void CEmailSoundState::StopTone()
    {
    FUNC_LOG
    }

// ---------------------------------------------------------------------------
// Tries to create audio player or activas failed state if error occurs
// ---------------------------------------------------------------------------
//
TBool CEmailSoundState::TryStartToneInitialization()
    {
    FUNC_LOG
    TRAPD( err, iContext.RecreateAudioPlayerL() );
    if ( err )
        {
        StateTransition( EFailed );
        }
    return ( err == KErrNone );
    }

// ---------------------------------------------------------------------------
// Stores as current state in owner of the state machine.
// ---------------------------------------------------------------------------
//
void CEmailSoundState::BaseEnter()
    {
    FUNC_LOG
    iContext.SetState( this );
    }

// ---------------------------------------------------------------------------
// State transition to requested state or failed state if attempt doesn't
// succeed.
// ---------------------------------------------------------------------------
//
void CEmailSoundState::StateTransition( const TSoundState aState )
    {
    FUNC_LOG
    TRAPD( err,
        {
        switch ( aState )
            {
            case EInit:
                CEmailInitilisingTone::EnterL( this );
                break;
            case EReady:
                CEmailReadyToPlay::EnterL( this );
                break;
            case EPlaying:
                CEmailPlayingTone::EnterL( this );
                break;
            case EPendingPlay:
                CEmailPendingPlay::EnterL( this );
                break;
            case EPendingInit:
                CEmailPendingInit::EnterL( this );
                break;
            case EDelay:
                CEmailTimerDelay::EnterL( this );
                break;
            case EFailed:
            default:
                // to failed state
                /* CodeScanner warning ignored because CS does not regognize
                 * TRAP being used */
                User::Leave( KErrArgument ); // codescanner::leave
                break;
            }
        } );
    if ( err != KErrNone )
        {
        CEmailSoundFailed::Enter( this );
        }
    // exit old state
    Exit();
    }

// ---------------------------------------------------------------------------
// For most states destructor call is fine.
// See also CEmailSoundFailed::Exit()
// ---------------------------------------------------------------------------
//
void CEmailSoundState::Exit()
    {
    FUNC_LOG
    delete this;
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
MEmailSoundStateContext& CEmailSoundState::Context() const
    {
    FUNC_LOG
    return iContext;
    }

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
CEmailSoundState::~CEmailSoundState()
    {
    FUNC_LOG
    }

// ---------------------------------------------------------------------------
// c++ constructor
// ---------------------------------------------------------------------------
//
CEmailSoundState::CEmailSoundState(
    MEmailSoundStateContext& aContext ) :
    iContext( aContext )
    {
    FUNC_LOG
    }

// ---------------------------------------------------------------------------
// c++ constructor
// ---------------------------------------------------------------------------
//
CEmailInitilisingTone::CEmailInitilisingTone(
    MEmailSoundStateContext& aContext ) :
    CEmailSoundState( aContext )
    {
    FUNC_LOG
    }

// ---------------------------------------------------------------------------
// Need to cancel and restart tone initialization
// ---------------------------------------------------------------------------
//
void CEmailInitilisingTone::ProfileChanged()
    {
    FUNC_LOG
    TryStartToneInitialization();
    }

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CEmailInitilisingTone::AudioInitCompleted()
    {
    FUNC_LOG
    StateTransition( EReady );
    }


// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CEmailInitilisingTone::PlayTone()
    {
    FUNC_LOG
    // not ready yet, postpone playing
    StateTransition( EPendingPlay );
    }

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CEmailInitilisingTone::DoEnterL()
    {
    FUNC_LOG
    iContext.RecreateAudioPlayerL();
    CEmailSoundState::BaseEnter();
    }

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CEmailInitilisingTone::EnterL( CEmailSoundState* aPreviousState )
    {
    FUNC_LOG
    CEmailInitilisingTone* state = new ( ELeave )
        CEmailInitilisingTone( aPreviousState->Context() );
    CleanupStack::PushL( state );
    state->DoEnterL();
    CleanupStack::Pop();
    }

// ---------------------------------------------------------------------------
// c++ constructor
// ---------------------------------------------------------------------------
//
CEmailReadyToPlay::CEmailReadyToPlay( MEmailSoundStateContext& aContext )
 : CEmailSoundState( aContext )
    {
    FUNC_LOG
    }

// ---------------------------------------------------------------------------
// New profile may have different tone, initialize it.
// ---------------------------------------------------------------------------
//
void CEmailReadyToPlay::ProfileChanged()
    {
    FUNC_LOG
    StateTransition( EInit );
    }

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CEmailReadyToPlay::PlayTone()
    {
    FUNC_LOG
    StateTransition( EPlaying );
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
void CEmailReadyToPlay::DoEnterL()
    {
    FUNC_LOG
    CEmailSoundState::BaseEnter();
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
void CEmailReadyToPlay::EnterL(
    CEmailSoundState* aPreviousState )
    {
    FUNC_LOG
    CEmailReadyToPlay* state = new ( ELeave ) CEmailReadyToPlay(
        aPreviousState->Context() );
    CleanupStack::PushL( state );
    state->DoEnterL();
    CleanupStack::Pop();
    }

// ---------------------------------------------------------------------------
// c++ constructor
// ---------------------------------------------------------------------------
//
CEmailSoundFailed::CEmailSoundFailed( MEmailSoundStateContext& aContext )
 : CEmailSoundState( aContext )
    {
    FUNC_LOG
    }

// ---------------------------------------------------------------------------
// Something has gone wrong earlier, now try recovery (re-init).
// ---------------------------------------------------------------------------
//
void CEmailSoundFailed::ProfileChanged()
    {
    FUNC_LOG
    // try re-initialization
    StateTransition( EInit );
    }

// ---------------------------------------------------------------------------
// Something has gone wrong earlier, now try recovery (re-init + play ).
// ---------------------------------------------------------------------------
//
void CEmailSoundFailed::PlayTone()
    {
    FUNC_LOG
    // kick off tone re-creation again
    if ( TryStartToneInitialization() )
        {
        StateTransition( EPendingPlay );
        }
    }

// ---------------------------------------------------------------------------
// Set failed state as current state.
// ---------------------------------------------------------------------------
//
void CEmailSoundFailed::DoEnter()
    {
    FUNC_LOG
    CEmailSoundState::BaseEnter();
    }

// ---------------------------------------------------------------------------
// Failed state is preserved until plugin shutdown, therefore base class Exit()
// is overridden and explicit destruction for this class is done in
// CEmailSoundState::Uninitialize.
// ---------------------------------------------------------------------------
//
void CEmailSoundFailed::Exit()
    {
    FUNC_LOG
    }

// ---------------------------------------------------------------------------
// Obtain failed state from TLS and set it as current state.
// ---------------------------------------------------------------------------
//
void CEmailSoundFailed::Enter( CEmailSoundState* /*aPreviousState*/ )
    {
    FUNC_LOG
    TAny* tls = Dll::Tls();
    __ASSERT_ALWAYS( tls, Panic( ECmailHandlerPluginPanicNoFailedState ) );
    CEmailSoundFailed* failedState = reinterpret_cast<CEmailSoundFailed*>( tls );
    failedState->DoEnter();
    }

// ---------------------------------------------------------------------------
// c++ constructor
// ---------------------------------------------------------------------------
//
CEmailPlayingTone::CEmailPlayingTone( MEmailSoundStateContext& aContext )
 : CEmailSoundState( aContext )
    {
    FUNC_LOG
    }

// ---------------------------------------------------------------------------
// When playing completes, recreate tone because new profile may have
// different one.
// ---------------------------------------------------------------------------
//
void CEmailPlayingTone::ProfileChanged()
    {
    FUNC_LOG
    StateTransition( EPendingInit );
    }

// ---------------------------------------------------------------------------
// Proceed to delay state.
// Existing player instance owned by context is preserved.
// ---------------------------------------------------------------------------
//
void CEmailPlayingTone::AudioPlayCompleted()
    {
    FUNC_LOG

    // Tell sysap that tone playing has stopped
    RProperty::Set( KPSUidCoreApplicationUIs, KCoreAppUIsMessageToneQuit, ECoreAppUIsStopTonePlaying );

    StateTransition( EDelay );
    }

// ---------------------------------------------------------------------------
// Stop tone and proceed to delay state.
// ---------------------------------------------------------------------------
//
void CEmailPlayingTone::StopTone()
    {
    FUNC_LOG
    CMdaAudioPlayerUtility* player = iContext.AudioPlayer();
    // should never be null in this state
    __ASSERT_ALWAYS( player, Panic( ECmailHandlerPluginPanicNullAudioPlayer ) );
    player->Stop();

    // Tell sysap that tone playing has stopped
    RProperty::Set( KPSUidCoreApplicationUIs, KCoreAppUIsMessageToneQuit, ECoreAppUIsStopTonePlaying );

    StateTransition( EDelay );
    }

// ---------------------------------------------------------------------------
// Starts playing of already initialized tone.
// ---------------------------------------------------------------------------
//
void CEmailPlayingTone::EnterL( CEmailSoundState* aPreviousState )
    {
    FUNC_LOG
    CEmailPlayingTone* state = new ( ELeave ) CEmailPlayingTone(
        aPreviousState->Context() );
    CleanupStack::PushL( state );
    state->DoEnterL();
    CleanupStack::Pop();
    }

// ---------------------------------------------------------------------------
// Playes 'new email' tone.
// ---------------------------------------------------------------------------
//
void CEmailPlayingTone::DoEnterL()
    {
    FUNC_LOG
    CMdaAudioPlayerUtility* player = iContext.AudioPlayer();
    // should never be null in this state
    __ASSERT_ALWAYS( player, Panic( ECmailHandlerPluginPanicNullAudioPlayer ) );

    MProfile* profile = iContext.ProfileEngine().ActiveProfileL(); // owned

    const TInt profileVolume =
        profile->ProfileTones().ToneSettings().iRingingVolume;
    TInt volume = Max( KToneMinVolume, Min( profileVolume, KToneMaxVolume ) );
    profile->Release();
    profile = NULL;

    // scale with player but only if volume is greater than zero
    if ( volume > 0 )
        {
        volume = volume * player->MaxVolume() / KToneMaxVolume ;
        }
    INFO_1( "email tone volume", volume )
    player->SetVolume( volume );

    // Set time
    TTimeIntervalMicroSeconds time = TTimeIntervalMicroSeconds( 0 );

    // Tell sysap that there's a tone playing at the moment
    RProperty::Set( KPSUidCoreApplicationUIs, KCoreAppUIsMessageToneQuit, ECoreAppUIsTonePlaying );

    // Set repeats and start playing
    player->SetRepeats( 0, time );
    player->Play();
    CEmailSoundState::BaseEnter();
    };

// ---------------------------------------------------------------------------
// c++ constructor
// ---------------------------------------------------------------------------
//
CEmailPendingPlay::CEmailPendingPlay( MEmailSoundStateContext& aContext )
 : CEmailSoundState( aContext )
    {
    FUNC_LOG
    }

// ---------------------------------------------------------------------------
// Currently initializing tone is outdated, need to re-init and play
// immediately.
// ---------------------------------------------------------------------------
//
void CEmailPendingPlay::ProfileChanged()
    {
    FUNC_LOG
    TryStartToneInitialization();
    }

// ---------------------------------------------------------------------------
// Play was requested while tone initialization was ongoing, no its ready
// and tone can be played.
// ---------------------------------------------------------------------------
//
void CEmailPendingPlay::AudioInitCompleted()
    {
    FUNC_LOG
    StateTransition( EPlaying );
    }

// ---------------------------------------------------------------------------
// Activates this state.
// ---------------------------------------------------------------------------
//
void CEmailPendingPlay::DoEnterL()
    {
    FUNC_LOG
    CEmailSoundState::BaseEnter();
    }

// ---------------------------------------------------------------------------
// Playing tone was requested during initialization.
// ---------------------------------------------------------------------------
//
void CEmailPendingPlay::EnterL( CEmailSoundState* aPreviousState )
    {
    FUNC_LOG
    CEmailPendingPlay* state = new ( ELeave ) CEmailPendingPlay(
        aPreviousState->Context() );
    CleanupStack::PushL( state );
    state->DoEnterL();
    CleanupStack::Pop();
    }

// ---------------------------------------------------------------------------
// c++ constructor
// ---------------------------------------------------------------------------
//
CEmailPendingInit::CEmailPendingInit( MEmailSoundStateContext& aContext )
 : CEmailSoundState( aContext )
    {
    FUNC_LOG
    }

// ---------------------------------------------------------------------------
// Playback finished and tone can be initialized.
// ---------------------------------------------------------------------------
//
void CEmailPendingInit::AudioPlayCompleted()
    {
    FUNC_LOG
    // now we can re-init new tone
    StateTransition( EInit );
    }

// ---------------------------------------------------------------------------
// Activates this state.
// ---------------------------------------------------------------------------
//
void CEmailPendingInit::DoEnterL()
    {
    FUNC_LOG
    CEmailSoundState::BaseEnter();
    }

// ---------------------------------------------------------------------------
// Tone initialization was requested during playback.
// ---------------------------------------------------------------------------
//
void CEmailPendingInit::EnterL( CEmailSoundState* aPreviousState )
    {
    FUNC_LOG
    CEmailPendingInit* state = new ( ELeave ) CEmailPendingInit(
        aPreviousState->Context() );
    CleanupStack::PushL( state );
    state->DoEnterL();
    CleanupStack::Pop();
    }

// ---------------------------------------------------------------------------
// c++ constructor
// ---------------------------------------------------------------------------
//
CEmailTimerDelay::CEmailTimerDelay( MEmailSoundStateContext& aContext )
 : CEmailSoundState( aContext )
    {
    FUNC_LOG
    }

// ---------------------------------------------------------------------------
// New profile may have different tone, try initialize it.
// ---------------------------------------------------------------------------
//
void CEmailTimerDelay::ProfileChanged()
    {
    FUNC_LOG
    TryStartToneInitialization();
    }

// ---------------------------------------------------------------------------
// Timer expired. Ready to play again.
// ---------------------------------------------------------------------------
//
void CEmailTimerDelay::TimerCallBackL( TInt /*aError*/ )
    {
    FUNC_LOG
    StateTransition( EReady );
    }

// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CEmailTimerDelay::~CEmailTimerDelay()
    {
    FUNC_LOG
    delete iTimer;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CEmailTimerDelay::DoEnterL()
    {
    FUNC_LOG
    iTimer = CFSNotificationHandlerTimer::NewL( *this );
    iTimer->After( KDelayTimerInterval );
    CEmailSoundState::BaseEnter();
    }
// ---------------------------------------------------------------------------
// Starts delay timer
// ---------------------------------------------------------------------------
//
void CEmailTimerDelay::EnterL( CEmailSoundState* aPreviousState )
    {
    FUNC_LOG
    CEmailTimerDelay* state = new ( ELeave ) CEmailTimerDelay(
        aPreviousState->Context() );
    CleanupStack::PushL( state );
    state->DoEnterL();
    CleanupStack::Pop( state );
    }

// End of file