emailuis/emailui/src/FreestyleEmailDownloadInformationMediator.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 09 Jun 2010 09:22:57 +0300
branchRCL_3
changeset 29 6b8f3b30d0ec
parent 26 67369d1b217f
permissions -rw-r--r--
Revision: 201021 Kit: 2010123

/*
* Copyright (c) 2005 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:  Freestyle Email application attachment download info mediator
*
*/


//  INCLUDE FILES
#include "emailtrace.h"
#include <e32svr.h>
//<cmail>
#include "cfsmailclient.h"
//</cmail>
#include <StringLoader.h>
#include <FreestyleEmailUi.rsg>

#include "FreestyleEmailUiConstants.h"
#include "FreestyleEmailUiLiterals.h"
#include "FreestyleEmailUiUtilities.h"
#include "FreestyleEmailDownloadInformationMediator.h"
#include "freestyleemailcenrephandler.h"
#include "FreestyleEmailUiAppui.h"
#include "FSEmail.pan"


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

// ----------------------------------------------------
// CFSEmailDownloadInfoMediator::CFSEmailDownloadInfoMediator

// Default class constructor.
// ----------------------------------------------------
//
CFSEmailDownloadInfoMediator::CFSEmailDownloadInfoMediator( CFSMailClient& aMailClient )
    : CActive( EPriorityNormal ), iMailClient(aMailClient),
    iDownloadArray( KArrayGranularity, _FOFF(TDownload, iRequestId) ), iDownloadsStarted ( EFalse ),
    iNotificationsInProgress( 0 ), iObserverDeleted( EFalse )
	{
    FUNC_LOG;
	CActiveScheduler::Add( this );
	}

// ----------------------------------------------------
// CFSEmailDownloadInfoMediator::ConstructL
// Second phase class constructor.
// ----------------------------------------------------
//
void CFSEmailDownloadInfoMediator::ConstructL()
	{
    FUNC_LOG;
	}

// ----------------------------------------------------
// CFSEmailDownloadInfoMediator::NewL
// Two-phased class constructor.
// Singleton
// ----------------------------------------------------
//
CFSEmailDownloadInfoMediator* CFSEmailDownloadInfoMediator::NewL( CFSMailClient& aMailClient )
	{
    FUNC_LOG;

	CFSEmailDownloadInfoMediator* singleton = NULL;

	// Check Thread Local Storage for instance pointer
	singleton = static_cast<CFSEmailDownloadInfoMediator*>( UserSvr::DllTls( KTlsHandleDownloadInfo ) );
	if ( !singleton )
		{
		singleton = new ( ELeave ) CFSEmailDownloadInfoMediator( aMailClient );
		CleanupStack::PushL( singleton );
		singleton->ConstructL();
		CleanupStack::Pop( singleton );
		
		// Store a pointer of a new instance in Thread Local Storage
		TInt err = UserSvr::DllSetTls( KTlsHandleDownloadInfo, singleton );
		if ( err )
			{
			delete singleton;
			singleton = NULL;
			User::Leave( err );
			}
		}

	return singleton;
	}


void CFSEmailDownloadInfoMediator::AddObserver( MFSEmailDownloadInformationObserver* aObserver, TFSMailMsgId aMessageId )
	{
    FUNC_LOG;
	TRequestObserver newObserver = { aObserver, aMessageId };
	//To avoid adding duplicate observers 
	for ( TInt i=0; i<iObserverArray.Count(); i++ )
	    {
	    if ( newObserver.iObserver == iObserverArray[i].iObserver && newObserver.iMessageId == iObserverArray[i].iMessageId )
	        {
	        return;
	        }
	    }
	iObserverArray.Append( newObserver );
	}

void CFSEmailDownloadInfoMediator::AddObserver( MFSEmailDownloadInformationObserver* aObserver )
	{
    //To avoid adding duplicate observers 
    for ( TInt i=0; i<iAllObserverArray.Count(); i++ )
        {
        if ( aObserver == iAllObserverArray[i] )
            {
            return;
            }
        }
	iAllObserverArray.Append(aObserver);
	}

// ----------------------------------------------------
// CFSEmailDownloadInfoMediator::~CFSEmailDownloadInfoMediator
// Destructor of CFSEmailDownloadInfoMediator class.
// ----------------------------------------------------
//
CFSEmailDownloadInfoMediator::~CFSEmailDownloadInfoMediator()
	{
	FUNC_LOG;
	iAllObserverArray.Close();
	iObserverArray.Close();
	iDownloadArray.Close();
	iDownloadCountArray.Close();
	Cancel();
    }


void CFSEmailDownloadInfoMediator::RequestResponseL(
	TFSProgress aEvent, TInt aRequestId )
	{
	FUNC_LOG;
	// Find the correct download structure
	TDownload download( aRequestId, TPartData(), KNullDesC(), EFalse );
	TInt idx = iDownloadArray.Find( download );
	
    if ( idx >= 0 && idx < iDownloadArray.Count() )
    	{
    	// save the progress info from status events
    	if ( aEvent.iProgressStatus == TFSProgress::EFSStatus_Status )
    	    {
        	iDownloadArray[idx].iCounter = aEvent.iCounter;
        	iDownloadArray[idx].iMaxCount = aEvent.iMaxCount;
    	    }
    	
		// Make a local copy of the download object as the object may be
		// removed from the array before we are finished
		download = iDownloadArray[idx];
        
    	// In case of error, show a note.  Note that these notes should not
        // be displayed synchronously, since otherwise other events can
        // occur that delete objects that are still in use waiting for this
        // method to complete (see PDOO-7X93JP).
		if ( aEvent.iError && aEvent.iError != KErrCancel )
		    {
			// Download failed, show error note
			if ( aEvent.iError == KErrCouldNotConnect ||
				 aEvent.iError == KErrConnectionTerminated )
			    { // connection error
			    TFsEmailUiUtility::ShowErrorNoteL(
			    	R_FREESTYLE_EMAIL_ERROR_GENERAL_CONNECTION_ERROR,
			    	EFalse );
			    }
			else // other error
			    {
			    TFsEmailUiUtility::ShowErrorNoteL(
			    	R_FREESTYLE_EMAIL_ERROR_GENERAL_UNABLE_TO_COMPLETE,
			    	EFalse );
			    }
		    }
		    
		// Remove download from array if it has been cancelled or failed
		if ( aEvent.iProgressStatus ==
			 TFSProgress::EFSStatus_RequestCancelled || aEvent.iError )
			{
			iDownloadArray.Remove(idx);
			}
		// Mark download as completed if download just finished
		else if ( aEvent.iProgressStatus ==
				  TFSProgress::EFSStatus_RequestComplete )
		    {
		    if ( !iDownloadArray[idx].iMaxCount )
		        {
		        // Set max count to 1 if no single progress event has arrived
		        // before download is finished.
		        iDownloadArray[idx].iMaxCount = 1;
		        }
		    iDownloadArray[idx].iCounter = iDownloadArray[idx].iMaxCount;
		    }
		
		TInt completedDownloadsToNotify = 0;        

		// Find and update the count array entry
        TDownloadCount countObject( download.iPartData.iMessageId );
        GetAndUpdateDownloadCountL( countObject, aEvent );

		// Notification may be given if all ongoing downloads from message
		// have finished and there are some properly completed downloads
        if ( download.iNotifyComplete &&
             countObject.iDownloadsCompletedCount &&
             countObject.iDownloadsCompletedCount ==
             	countObject.iDownloadsStartedCount )
            {
			completedDownloadsToNotify = countObject.iDownloadsCompletedCount;
            }
					    
		// if download is complete
		if ( aEvent.iProgressStatus ==
			 TFSProgress::EFSStatus_RequestComplete && !aEvent.iError )
			{
            // if file was set to be saved after download
    		if ( download.iSaveFileName.CompareC( KNullDesC ) )
    			{
    			CFSMailMessage* mailMessage = iMailClient.GetMessageByUidL(
    				download.iPartData.iMailBoxId,
    				download.iPartData.iFolderId,
    				download.iPartData.iMessageId,
    				EFSMsgDataEnvelope );
        		CleanupStack::PushL( mailMessage );
        		CFSMailMessagePart* attachment = mailMessage->ChildPartL(
        			download.iPartData.iMessagePartId );
        		CleanupStack::PopAndDestroy( mailMessage );
        		CleanupStack::PushL( attachment );
        		if ( TFsEmailUiUtility::OkToSaveFileL( download.iSaveFileName, *attachment ) )
        			{
	        		attachment->CopyContentFileL( download.iSaveFileName );       			
        			}
        		CleanupStack::PopAndDestroy( attachment );
    			}
			}
			
		// relay the event to observers
		NotifyObserversL( aEvent, download.iPartData );

        // <cmail> moved completion note after notifying so that observers can 
        // clear menus etc. before note is displayed
        // if download is complete
		if ( aEvent.iProgressStatus ==
			 TFSProgress::EFSStatus_RequestComplete && !aEvent.iError )
			{
            // Notification of saved attachments may be given if all downloads of the given message has been completed.
            if ( download.iNotifyComplete && countObject.iDownloadsCompletedCount &&
                 countObject.iDownloadsCompletedCount == countObject.iDownloadsStartedCount &&
                 countObject.iSaveRequestedCount > 0 )
                 {
                 TFsEmailUiUtility::SetDownloadSave( ETrue );
                 TFsEmailUiUtility::ShowFilesSavedToFolderNoteL( countObject.iDownloadsCompletedCount /*iSaveRequestedCount*/ ); // Fix: ESLX-84ACJ9
                 }        
            // Show "Download completed" if necessary
            else if ( CompletionNotesInUseL() && completedDownloadsToNotify )
                {
                OpenAttachmentL( download.iPartData, completedDownloadsToNotify );            
                }
            else if ( download.iNotifyComplete &&
                      countObject.iSaveRequestedCount == 0 )
                {
                TFsEmailUiUtility::OpenAttachmentL( download.iPartData );
                }
			}
	    // </cmail>
        }
	}
	
TBool CFSEmailDownloadInfoMediator::IsAnyAttachmentDownloads()
	{
	FUNC_LOG;
	return iDownloadsStarted;
	}


TBool CFSEmailDownloadInfoMediator::IsAnyAttachmentDownloads( TFSMailMsgId aMessageId )
	{
	FUNC_LOG;
	for ( TInt i=0; i<iDownloadArray.Count(); i++ )
		{
		if ( iDownloadArray[i].iPartData.iMessageId == aMessageId )
			{
			return ETrue;
			}
		}
	return EFalse;
	}

TInt CFSEmailDownloadInfoMediator::GetDownloadPercentageL( TFSMailMsgId aMessageId )
    {
	FUNC_LOG;
    TInt count = 0;
    TInt maxCount = 0;
    
    for ( TInt i=0; i<iDownloadArray.Count(); i++ )
        {
        if ( iDownloadArray[i].iPartData.iMessageId == aMessageId )
            {
            // In case we find an attachment for which maxCount is still missing,
            // update all the missing maxCounts to match the file size of
            // the attachment.
            if ( iDownloadArray[i].iMaxCount <= 0 )
                {
                // This should happen at most once per call to GetDownloadPercentageL().
                UpdateAttachmentSizesL( iDownloadArray[i].iPartData );
                }

            count += iDownloadArray[i].iCounter;
            maxCount += iDownloadArray[i].iMaxCount;
            }
        }
    
    if ( maxCount <= 0 )
        {
        return KErrNotFound;
        }
    else
        {
        return 100*count/maxCount;
        }
    }

void CFSEmailDownloadInfoMediator::UpdateAttachmentSizesL( const TPartData& aMessageData )
    {
	FUNC_LOG;
    CFSMailMessage* mailMessage = iMailClient.GetMessageByUidL( aMessageData.iMailBoxId, 
                                                                aMessageData.iFolderId, 
                                                                aMessageData.iMessageId, 
                                                                EFSMsgDataEnvelope );
    CleanupStack::PushL( mailMessage );
    RPointerArray<CFSMailMessagePart> attachments;
    CleanupResetAndDestroyClosePushL( attachments );
    mailMessage->AttachmentListL( attachments );
    
    for ( TInt i=0; i<iDownloadArray.Count(); i++ )
        {
        if ( iDownloadArray[i].iPartData.iMessageId == aMessageData.iMessageId )
            {
            if ( iDownloadArray[i].iMaxCount <= 0 )
                {
                // Find the matching attachment object and retrieve the attachment size
                for ( TInt j=0 ; j<attachments.Count() ; ++j )
                    {
                    if ( attachments[j]->GetPartId() == iDownloadArray[i].iPartData.iMessagePartId )
                        {
                        iDownloadArray[i].iMaxCount = attachments[j]->ContentSize();
                        break;
                        }
                    }
                }
            }
        }
    
    CleanupStack::PopAndDestroy( &attachments );
    CleanupStack::PopAndDestroy( mailMessage );
    }

TBool CFSEmailDownloadInfoMediator::IsDownloadableL( TPartData aPart )
	{
	FUNC_LOG;
	TBool ret(EFalse);
	// if part is downloading, no more checking is needed
	if ( !IsDownloading( aPart.iMessagePartId ) )
		{
		CFSMailMessage* mailMessage = iMailClient.GetMessageByUidL(
			aPart.iMailBoxId, aPart.iFolderId,
			aPart.iMessageId, EFSMsgDataEnvelope );
		if ( mailMessage )
			{
			CleanupStack::PushL( mailMessage );
			CFSMailMessagePart* mailMessagePart =
				mailMessage->ChildPartL( aPart.iMessagePartId );
			// if the file is fetched full
			if ( mailMessagePart &&
			        mailMessagePart->FetchLoadState() == EFSFull )
				{
				ret = EFalse;
				}
			else
				{
				ret =  ETrue;
				}
			delete mailMessagePart;
			CleanupStack::PopAndDestroy( mailMessage );			
			}
		}
	return ret;
	}


void CFSEmailDownloadInfoMediator::Destroy()
	{
	FUNC_LOG;
    CFSEmailDownloadInfoMediator* self = 
        static_cast<CFSEmailDownloadInfoMediator*>( UserSvr::DllTls( KTlsHandleDownloadInfo ) );
	UserSvr::DllFreeTls( KTlsHandleDownloadInfo );
    delete self;
    }

void CFSEmailDownloadInfoMediator::StopObserving( MFSEmailDownloadInformationObserver* aObserver, TFSMailMsgId aMessageId )
	{
	FUNC_LOG;
    for (TInt i=iObserverArray.Count()-1; i>=0; i--)
    	{
   		if ( iObserverArray[i].iObserver == aObserver && iObserverArray[i].iMessageId == aMessageId )
   			{
			RemoveObserver(i);
   			}
    	}
	}

void CFSEmailDownloadInfoMediator::StopObserving( MFSEmailDownloadInformationObserver* aObserver )
	{
	FUNC_LOG;
    for ( TInt i=iObserverArray.Count()-1; i>=0; i-- )
    	{
   		if ( iObserverArray[i].iObserver == aObserver )
   			{
   			RemoveObserver(i);
   			}
    	}
    for ( TInt i=iAllObserverArray.Count()-1; i>=0; i-- )
    	{
		if ( iAllObserverArray[i] == aObserver )
   			{
   			iAllObserverArray.Remove(i);
   			}
    	}
	}

void CFSEmailDownloadInfoMediator::RemoveObserver( TInt aIdx )
    {
    if ( iNotificationsInProgress == 0 )
        {
        // If we're not currently iterating over the array, remove the entry
        // immediately.
        iObserverArray.Remove( aIdx );
        }
    else
        {
        // We're currently iterating over the array: mark the entry as
        // deleted and flag that we've done so.
        iObserverArray[aIdx].iDeleted = ETrue;
        iObserverDeleted = ETrue;
        }
    }

void CFSEmailDownloadInfoMediator::DownloadL( TPartData aPart, TBool aCompleteNote )
	{
	FUNC_LOG;
	TFileName emptyFileName;
	DownloadAndSaveL( aPart, emptyFileName, aCompleteNote );
	}

void CFSEmailDownloadInfoMediator::DownloadAndSaveL( TPartData aPart, const TDesC& aSaveFileName, TBool aCompleteNote )
	{
	FUNC_LOG;
	// now there is at least one download started 
	iDownloadsStarted = ETrue;
	TFsEmailUiUtility::SetSaveSelect( ETrue );  
	// fetch message part
	CFSMailMessage* mailMessage = iMailClient.GetMessageByUidL( aPart.iMailBoxId, aPart.iFolderId, aPart.iMessageId, EFSMsgDataEnvelope );
	CleanupStack::PushL( mailMessage );
	CFSMailMessagePart* messagePart = mailMessage->ChildPartL( aPart.iMessagePartId );
	CleanupStack::PopAndDestroy( mailMessage );
	
	// Panic in udeb builds, the condition !messagePart should not be possible
    ASSERT( messagePart );	

	// to be on the safer side only leave in urel builds
    if ( !messagePart )
	    {
	    User::Leave( KErrNotFound );
	    }	
	
	CleanupStack::PushL( messagePart );
	// start download and get request id
	TInt requestId = messagePart->FetchMessagePartL( aPart.iMessagePartId, *this, 0 );
	
	// store download information
	TDownload newDownload( requestId, aPart, aSaveFileName, aCompleteNote );
	AppendDownloadToInternalArraysL( newDownload );
    CleanupStack::PopAndDestroy( messagePart );
		
	// It may take some time before the first progress event is received from 
	// the protocol plug-in. This is true at least with the current Intellisync
	// plug-in implementation. Send artificial "0% event" to the UI to ensure that
	// the UI shows that the file is being downloaded.
	TFSProgress zeroEvent = { TFSProgress::EFSStatus_Waiting, 0, 0, KErrNone };
	NotifyObserversL( zeroEvent, aPart );
	}

void CFSEmailDownloadInfoMediator::CancelDownloadL(
	TFSMailMsgId aMessagePartId )
	{
	FUNC_LOG;
	// Find the corresponding download object
	TDownload downloadToCancel;
	downloadToCancel.iPartData.iMessagePartId = aMessagePartId;
    TInt idx = iDownloadArray.Find( downloadToCancel, 
                                    TIdentityRelation<TDownload>(EqualMessagePartId) );
    // Cancel if download object was found
    if ( idx >= 0 && idx < iDownloadArray.Count() )
        {
        iMailClient.CancelL( iDownloadArray[idx].iRequestId );
        }
	}

void CFSEmailDownloadInfoMediator::CancelAllDownloadsL( TFSMailMsgId aMailBoxId )
    {
    // Cancel all downloads within mailbox indicated with aMailBoxId
    for (TInt i=0 ; i < iDownloadArray.Count() ; i ++ )
        {
        if ( iDownloadArray[i].iPartData.iMailBoxId == aMailBoxId )
            {
            iMailClient.CancelL( iDownloadArray[i].iRequestId );
            }
        }    
    }

TBool CFSEmailDownloadInfoMediator::IsDownloading(
	TFSMailMsgId aMessagePartId )
	{
	FUNC_LOG;
	TInt dlArrayCount = iDownloadArray.Count();
	for ( TInt i = 0; i < dlArrayCount; i++ )
    	{
    	const TDownload& downloadObject = iDownloadArray[i];
   		if ( downloadObject.iPartData.iMessagePartId == aMessagePartId )
   			{
   			if ( downloadObject.iCounter < downloadObject.iMaxCount ||
   			     downloadObject.iMaxCount <= 0 )
   			    {
   			    // Only return ETrue if the corresponding download entry is
   			    // found and it's not yet finished.
   	            return ETrue;
   			    }
   			}
    	}
	return EFalse;
	}

void CFSEmailDownloadInfoMediator::NotifyObserversIfAttachmentsDownloadedL(
	TPartData aMessageId )
    {
	FUNC_LOG;
	CFSMailMessage* mailMessage = iMailClient.GetMessageByUidL(
		aMessageId.iMailBoxId,
		aMessageId.iFolderId,
		aMessageId.iMessageId,
		EFSMsgDataEnvelope );
	CleanupStack::PushL( mailMessage );

	RPointerArray<CFSMailMessagePart> attachments;
	CleanupResetAndDestroyClosePushL( attachments );
	mailMessage->AttachmentListL( attachments );
	
	for ( TInt i=0 ; i<attachments.Count() ; ++i )
	    {
	    TInt fetched = attachments[i]->FetchedContentSize();
	    TInt totalSize = attachments[i]->ContentSize();
	    
	    if ( fetched == totalSize )
	        {
	        // download has happened => Download Manager should be available in menus
	        iDownloadsStarted = ETrue;
	        
	        // notify observers about completed attachment download
    	    TFSProgress completeEvent = 
    	        {
    	        TFSProgress::EFSStatus_RequestComplete, 
    	        fetched,
    	        totalSize,
    	        KErrNone
    	        };
    	    aMessageId.iMessagePartId = attachments[i]->GetPartId();
    	    NotifyObserversL( completeEvent, aMessageId );
	        }
	    }
	
	CleanupStack::PopAndDestroy( &attachments );
	CleanupStack::PopAndDestroy( mailMessage );
    }

void CFSEmailDownloadInfoMediator::AppendDownloadToInternalArraysL( const TDownload& aNewDownload )
    {
	FUNC_LOG;
    // In case of some unexpected errors, it's possible that we have an existing download object
    // with the same request ID as the new download. In that case, the original download has failed
    // and needs to be removed.
    TInt foundIdx = iDownloadArray.Find( aNewDownload );
    if ( foundIdx >= 0 )
        {
        TFSProgress cancelEvent = { TFSProgress::EFSStatus_RequestCancelled, 0, 0, KErrUnknown };
        TDownload obsoleteDownload = iDownloadArray[foundIdx];
        TDownloadCount countObject( obsoleteDownload.iPartData.iMessageId );

        iDownloadArray.Remove( foundIdx );
        GetAndUpdateDownloadCountL( countObject, cancelEvent );
        NotifyObserversL( cancelEvent, obsoleteDownload.iPartData );     
        }
    
    // Add the download object to array now when we know that there's no duplicate IDs.
    iDownloadArray.Append( aNewDownload );

    // Add the download to the download count array
    TDownloadCount newCountObject( aNewDownload.iPartData.iMessageId );
    TInt idx = iDownloadCountArray.Find( newCountObject, 
                                         TIdentityRelation<TDownloadCount>(EqualMessageId) );
    if ( idx >= 0 && idx < iDownloadCountArray.Count() )
        {
        // if there are already downloads going on on the same message
        iDownloadCountArray[idx].iDownloadsStartedCount++;
        if ( aNewDownload.iSaveFileName.CompareC( KNullDesC ) )
            {
            iDownloadCountArray[idx].iSaveRequestedCount++;
            }      
        }
    else
        {
        newCountObject.iDownloadsStartedCount = 1;
        if ( aNewDownload.iSaveFileName.CompareC( KNullDesC ) )
            {
            newCountObject.iSaveRequestedCount=1;
            } 
        iDownloadCountArray.Append( newCountObject );
        }
    }

TBool CFSEmailDownloadInfoMediator::CompletionNotesInUseL() const
	{
	FUNC_LOG;
	// singleton, do not destroy
	CFSEmailCRHandler* cenRepHandler = CFSEmailCRHandler::InstanceL();
	
	return cenRepHandler->DownloadNotifications();
	}

void CFSEmailDownloadInfoMediator::OpenAttachmentL( const TPartData& aPart, 
        const TInt aCompletedCount )
    {
    iPopupLaunchData = aPart;
    if (aCompletedCount > 1)
        {
        iPopupLaunchData.iMessagePartId.SetNullId();    
        }
    TRequestStatus* status = &iStatus;
    User::RequestComplete(status, KErrNone);
    SetActive();
    }

void CFSEmailDownloadInfoMediator::LaunchDownloadCompleteNoteL(
        const TPartData& aPart, TInt aCompletedCount )
    {
	FUNC_LOG;

    Cancel(); // deletes previous iGlobalMsgQuery

    iPopupLaunchData = aPart;

    // fetch message part
	CFSMailMessage* mailMessage = iMailClient.GetMessageByUidL(
	    aPart.iMailBoxId,
	    aPart.iFolderId,
	    aPart.iMessageId,
	    EFSMsgDataEnvelope );

	CleanupStack::PushL( mailMessage );

    // From:
	HBufC* from = StringLoader::LoadLC(
	    R_FSE_VIEWER_ATTACHMENTS_TEXT_FROM );

	CFSMailAddress* senderData = mailMessage->GetSender(); // NOT OWNED

	TDesC& mailSender = senderData->GetDisplayName(); 

	// Subject:
	HBufC* mailSubject =
	    TFsEmailUiUtility::CreateSubjectTextLC( mailMessage );

	HBufC* subj = StringLoader::LoadLC(
	    R_FSE_VIEWER_ATTACHMENTS_TEXT_SUBJECT );

    // Attachment name or count
    HBufC* attachmentText = NULL;

	CFSMailMessagePart* messagePart =
	    mailMessage->ChildPartL( aPart.iMessagePartId );

    if ( messagePart )
        {
    	// create popup text

    	if ( aCompletedCount == 1 )
    		{
        	CleanupStack::PushL( messagePart );

    		attachmentText = messagePart->AttachmentNameL().AllocL(); 

        	CleanupStack::PopAndDestroy( messagePart );
        	
        	CleanupStack::PushL( attachmentText );
    		}
    	else if ( aCompletedCount > 1 )
    		{
    		attachmentText = StringLoader::LoadLC(
    		    R_FSE_VIEWER_ATTACHMENTS_DOWNLOADED, aCompletedCount );
    		iPopupLaunchData.iMessagePartId.SetNullId();
    		}
    	else
    	    {
    	    __ASSERT_DEBUG( EFalse, Panic(EFSEmailUiUnexpectedValue) );
    	    attachmentText = KNullDesC().AllocLC();
    	    }

        }
    else
        {
		attachmentText = StringLoader::LoadLC(
		    R_FSE_VIEWER_ATTACHMENTS_DOWNLOADED, aCompletedCount );
		iPopupLaunchData.iMessagePartId.SetNullId();
        }

    // Combine the text parts into one descriptor
    TInt textLength = from->Length() +
                      KSpace().Length() + 
                      mailSender.Length() + 
                      KLineFeed().Length() + 
                      subj->Length() + 
                      KSpace().Length() + 
                      mailSubject->Length() + 
                      KLineFeed().Length() +
                      attachmentText->Length();

    HBufC* popupText = HBufC::NewL( textLength );
    TPtr textPtr = popupText->Des();

	textPtr.Append( *from );
	textPtr.Append( KSpace );					
	textPtr.Append( mailSender );
	textPtr.Append( KLineFeed );
	textPtr.Append( *subj );
	textPtr.Append( KSpace );					
	textPtr.Append( *mailSubject );
	textPtr.Append( KLineFeed );
	textPtr.Append( *attachmentText );
	
	CleanupStack::PopAndDestroy( attachmentText );
	CleanupStack::PopAndDestroy( subj );
	CleanupStack::PopAndDestroy( mailSubject );
	CleanupStack::PopAndDestroy( from );

	CleanupStack::PopAndDestroy( mailMessage );
    
    CleanupStack::PushL( popupText );

	HBufC* popupHeader = StringLoader::LoadLC(
	    R_FSE_VIEWER_NOTE_ATTACHMENTS_DOWNLOAD_COMPLETE );

	iGlobalMsgQuery = CAknGlobalMsgQuery::NewL();

    iGlobalMsgQuery->ShowMsgQueryL(
        iStatus,
        *popupText,
        R_AVKON_SOFTKEYS_OPEN_CLOSE,
        *popupHeader,
        KNullDesC );
	    
	CleanupStack::PopAndDestroy( popupHeader );

	CleanupStack::PopAndDestroy( popupText );
		
	SetActive();
    }

void CFSEmailDownloadInfoMediator::NotifyObserversL( const TFSProgress& aEvent, const TPartData& aPart )
    {
	FUNC_LOG;

	// notify observers of this particular message part
    iNotificationsInProgress++;
    TRAPD( error, NotifyPartObserversL( aEvent, aPart ) );
    if ( --iNotificationsInProgress == 0 )
        {
        CleanUpObservers();
        }
    if ( error )
        {
        User::Leave( error );
        }

    // send response to every 'all observer'
    for ( TInt i=iAllObserverArray.Count()-1; i>=0; i-- )
        {
        iAllObserverArray[i]->RequestResponseL( aEvent, aPart );
        }
    }

void CFSEmailDownloadInfoMediator::NotifyPartObserversL( const TFSProgress& aEvent, const TPartData& aPart )
    {
	FUNC_LOG;
	// go through all observers
    for ( TInt j=iObserverArray.Count()-1; j>=0; j-- )
		{
		// if observer is observing this message
		if ( !iObserverArray[j].iDeleted && iObserverArray[j].iMessageId == aPart.iMessageId )
			{
			// send response to observer
			iObserverArray[j].iObserver->RequestResponseL( aEvent, aPart );
			}
		}
    }

void CFSEmailDownloadInfoMediator::CleanUpObservers()
    {
    FUNC_LOG;
    // If one or more observers have been marked for deletion, go through
    // the observer array and remove them.
    if ( iObserverDeleted )
        {
        for ( TInt j=iObserverArray.Count()-1; j>=0; j-- )
            {
            if ( iObserverArray[j].iDeleted )
                {
                iObserverArray.Remove(j);
                }
            }
        iObserverDeleted = EFalse;
        }
    }

void CFSEmailDownloadInfoMediator::RunL()
	{
	FUNC_LOG;
    // no close button pressed
    if (iStatus.Int() != EAknSoftkeyClose )
        {
        // open attachment
        if ( !iPopupLaunchData.iMessagePartId.IsNullId() )
            {
            // Force FsEmailUI to foreground because global completion note may appear
            // while some other application is active and our local error notes are not shown
            // in that case. (This wouldn't be necessary in case of succesful file launching.)
            TFsEmailUiUtility::BringFsEmailToForeground();
            TFsEmailUiUtility::OpenAttachmentL( iPopupLaunchData );
            }
        }
    }

void CFSEmailDownloadInfoMediator::DoCancel()
    {
    }

void CFSEmailDownloadInfoMediator::Cancel()
    {
    FUNC_LOG;
    if ( iGlobalMsgQuery )
        {
        iGlobalMsgQuery->CancelMsgQuery();
        delete iGlobalMsgQuery;
        iGlobalMsgQuery = NULL;
        }
    if ( IsActive() )
        {
        CActive::Cancel();
        }
    }

TInt CFSEmailDownloadInfoMediator::RunError( TInt /*aError*/ )
    {
    return KErrNone;
    }

void CFSEmailDownloadInfoMediator::GetAndUpdateDownloadCountL( TDownloadCount& aCountObject, 
                                                               const TFSProgress& aEvent )
    {
    FUNC_LOG;
    TInt idx = iDownloadCountArray.Find( aCountObject, 
                                         TIdentityRelation<TDownloadCount>(EqualMessageId) );
    if ( idx >= 0 && idx < iDownloadCountArray.Count() )
        {
        // Set started save requests, it does not change depending on the event received 
        aCountObject.iDownloadsStartedCount = iDownloadCountArray[idx].iDownloadsStartedCount;
        
        // If download completed succesfully, increase the completed downlods count
        if ( aEvent.iProgressStatus == TFSProgress::EFSStatus_RequestComplete &&
             !aEvent.iError )
            {
            iDownloadCountArray[idx].iDownloadsCompletedCount++;
            }
        
        // If download was cancelled or failed, decrease started downloads count
        else if ( aEvent.iProgressStatus == TFSProgress::EFSStatus_RequestCancelled ||
                  aEvent.iError )
            {
            iDownloadCountArray[idx].iDownloadsStartedCount--;
            }
        else
            {
            // do nothing with other request status values
            }
        aCountObject = iDownloadCountArray[idx];
        
        if ( aCountObject.iDownloadsCompletedCount ==
             aCountObject.iDownloadsStartedCount )
            {
            // The count entry can be removed from the array when all ongoing
            // downloads have been completed or cancelled.
            iDownloadCountArray.Remove(idx);
            
            // Remove also all the connected download entries from the iDownloadArray.
            for ( TInt i = iDownloadArray.Count()-1 ; i >=0  ; --i )
                {
                if ( iDownloadArray[i].iPartData.iMessageId == aCountObject.iMessageId )
                    {
                    iDownloadArray.Remove(i);
                    }
                }
            }
        }
    }

TBool CFSEmailDownloadInfoMediator::EqualMessageId( const TDownloadCount& aFirst, const TDownloadCount& aSecond )
    {
    return (aFirst.iMessageId == aSecond.iMessageId);
    }

TBool CFSEmailDownloadInfoMediator::EqualMessagePartId( const TDownload& aFirst, const TDownload& aSecond )
    {
    return (aFirst.iPartData.iMessagePartId == aSecond.iPartData.iMessagePartId);
    }