/*
* Copyright (c) 2002 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:
* Provides phonebook send contacts command object methods.
*
*/
// INCLUDE FILES
#include "CPbkSendContactCmd.h"
#include "CPbkvCardConverter.h"
#include "CPbkAppGlobals.h"
#include <BCardEng.h>
#include <CPbkFieldInfo.h>
#include <Phonebook.rsg>
#include <CPbkContactEngine.h>
#include <CPbkProgressNoteWrapper.h>
#include <CPbkConstants.h>
#include <CPbkContactItem.h>
#include <AknNoteWrappers.h>
#include <eikclb.h> // CEikListBox
#include <sendui.h> // Send UI API
#include <SendUiMtmUids.h> // Send UI MTM uid's
#include <MsgBioUids.h>
#include <txtrich.h> // CRichText
#include <barsread.h> // TResourceReader
#include <CMessageData.h>
#include <PbkUID.h>
#include <MenuFilteringFlags.h>
#include <SendUiConsts.h> // Postcard Uid
#include <MPbkCommandObserver.h>
#include <pbkdebug.h>
/// Unnamed namespace for local definitions
namespace {
const TUint KNoMenu = 0;
/**
* Represents the different listbox index selections.
*/
enum TPbkListBoxSelections
{
EFirstSelection = 0,
ESecondSelection,
EThirdSelection
};
// LOCAL DEBUG CODE
#ifdef _DEBUG
enum TPanicCode
{
EPanicPreCond_SendvCardsL = 1,
EPanicLogic_CmdSendContactDataL,
EPanicLogic_MapSelection
};
void Panic(TPanicCode aReason)
{
_LIT(KPanicText, "CPbkSendContactCmd");
User::Panic(KPanicText,aReason);
}
#endif // _DEBUG
// ==================== LOCAL FUNCTIONS ====================
/**
* Creates a rich text object and packages contents of a file to it. The file's
* data is not converted in any way except that characters are widened to 16
* bits.
*
* @param aEikEnv EIKON environment.
* @param aFileName name of the file to convert.
* @return a new rich text object with file's contents. The returned object is
* also left on top of the cleanup stack.
*/
CRichText* CreateRichTextFromFileLC
(CEikonEnv& aEikEnv, const TDesC& aFileName);
/**
* Helper class for sending the vCard(s) in async callback.
*/
class CVCardSender : public CIdle
{
public: // Constructors
/*
* Creates a new instance of this object.
* @param aPriority desired priority
*/
static CVCardSender* NewL(TInt aPriority);
private: // from CIdle
void RunL();
TInt RunError(TInt aError);
private:
/*
* Constructor.
* @param aPriority desired priority
*/
CVCardSender(TInt aPriority);
};
inline CVCardSender::CVCardSender(TInt aPriority)
: CIdle(aPriority)
{
CActiveScheduler::Add(this);
}
CVCardSender* CVCardSender::NewL(TInt aPriority)
{
return new(ELeave) CVCardSender(aPriority);
}
void CVCardSender::RunL()
{
CIdle::RunL();
// Destroy self.
// If RunL (the callback) leaves RunError will handle the deletion.
delete this;
}
TInt CVCardSender::RunError(TInt aError)
{
delete this;
// Forward all errors to the active scheduler
return aError;
}
CRichText* CreateRichTextFromFileLC
(CEikonEnv& aEikEnv, const TDesC& aFileName)
{
// Common allocation granularity and buffer size for rich text and
// file reading
const TInt KBufSize = CEditableText::EDefaultTextGranularity;
// Create a rich text object with default formatting
CRichText* richText = CRichText::NewL(
aEikEnv.SystemParaFormatLayerL(),
aEikEnv.SystemCharFormatLayerL(),
CEditableText::ESegmentedStorage,
KBufSize // Allocation granularity
);
CleanupStack::PushL(richText);
// Open the file for reading
RFile file;
User::LeaveIfError(file.Open
(aEikEnv.FsSession(), aFileName,
EFileRead|EFileStream|EFileShareReadersOnly));
CleanupClosePushL(file);
// Create two buffers: 8-bit for reading from file and 16-bit for
// converting to 16-bit format
HBufC8* buf8 = HBufC8::NewLC(KBufSize);
TPtr8 ptr8 = buf8->Des();
HBufC16* buf16 = HBufC16::NewLC(ptr8.MaxLength());
TPtr16 ptr16 = buf16->Des();
// Read, convert and append to rich text until the file ends
for (TInt err = file.Read(ptr8);
ptr8.Length() > 0;
err = file.Read(ptr8))
{
User::LeaveIfError(err);
ptr16.Copy(ptr8);
richText->InsertL(richText->DocumentLength(), ptr16);
}
// Cleanup and return
CleanupStack::PopAndDestroy(3); // buf16, buf8, file
return richText;
}
} // namespace
// ================= MEMBER FUNCTIONS =======================
inline CPbkSendContactCmd::CPbkSendContactCmd(
TPbkSendingParams aParams,
CPbkContactEngine& aEngine,
TContactItemId aContactId,
const CContactIdArray* aContacts,
TPbkContactItemField* aField
) :
iEngine( aEngine ),
iParams( aParams ),
iContactId( aContactId ),
iContacts( aContacts ),
iField( aField )
{
PBK_DEBUG_PRINT
(PBK_DEBUG_STRING("CPbkSendContactCmd::CPbkSendContactCmd(0x%x)"), this);
}
void CPbkSendContactCmd::ConstructL
(CBCardEngine& aBCardEng)
{
iEikEnv = CEikonEnv::Static();
iConverter = CPbkvCardConverter::NewL(iEikEnv->FsSession(),
iEngine, aBCardEng);
iVcardSender = CVCardSender::NewL(CActive::EPriorityIdle);
iWaitNoteWrapper = CPbkProgressNoteWrapper::NewL();
}
/**
* Static constructor.
*/
CPbkSendContactCmd* CPbkSendContactCmd::NewL
(TPbkSendingParams aParams,
CPbkContactEngine& aEngine, CBCardEngine& aBCardEng,
TContactItemId aContactId,
TPbkContactItemField* aField)
{
CPbkSendContactCmd* self = new(ELeave)
CPbkSendContactCmd(aParams,aEngine, aContactId, NULL, aField);
CleanupStack::PushL(self);
self->ConstructL(aBCardEng);
CleanupStack::Pop(); // self
return self;
}
/**
* Static constructor (variation for multiple contacts).
*/
CPbkSendContactCmd* CPbkSendContactCmd::NewL
(TPbkSendingParams aParams,
CPbkContactEngine& aEngine, CBCardEngine& aBCardEng,
const CContactIdArray& aContacts)
{
CPbkSendContactCmd* self = new(ELeave)
CPbkSendContactCmd(aParams, aEngine, KNullContactId, &aContacts, NULL);
CleanupStack::PushL(self);
self->ConstructL(aBCardEng);
CleanupStack::Pop(); // self
return self;
}
/**
* Destructor.
*/
CPbkSendContactCmd::~CPbkSendContactCmd()
{
PBK_DEBUG_PRINT
(PBK_DEBUG_STRING("CPbkSendContactCmd::~CPbkSendContactCmd(0x%x)"), this);
iUnderDestruction = ETrue;
delete iWaitNoteWrapper;
delete iConverter;
delete iVcardSender;
}
void CPbkSendContactCmd::ExecuteLD()
{
PBK_DEBUG_PRINT
(PBK_DEBUG_STRING("CPbkSendContactCmd::ExecuteLD(0x%x)"), this);
CleanupStack::PushL(this);
iMtmUid = ShowSendQueryL();
if ( ( iContactId == KNullContactId &&
( !iContacts || iContacts->Count()==0 ) ) ||
iMtmUid == KNullUid )
{
CleanupStack::PopAndDestroy();
return;
}
TInt selectionIndex(ESendAllData);
// Ask the user to select the contact data to be send, if needed
selectionIndex = SelectSentDataL();
if (selectionIndex != ECancel)
{
if (iContactId != KNullContactId)
{
iConverter->ConvertContactL(iContactId, iField, selectionIndex);
}
else
{
// iContacts validity is checked in function entry
iConverter->ConvertContactsL(*iContacts, selectionIndex);
}
// Then send contact(s)
CPbkWaitNoteWrapperBase::TNoteParams noteParams;
noteParams.iObserver = this;
// ProcessFinished will be called when execution is finished.
iWaitNoteWrapper->ExecuteL
(*iConverter, R_QTN_SM_WAIT_BUSINESS_CARD, noteParams);
if ( iObserver )
{
iObserver->CommandFinished( *this );
}
CleanupStack::Pop(); // this
// The promised self destruction will happen in SendvCardsL,
// which is called by ProcessFinished
}
else
{
// User canceled the sending, exit
CleanupStack::PopAndDestroy();
}
}
/**
* Send the vCards from an idle callback to get standard active scheduler
* error handling. This is especially important to handle special leave
* code KLeaveExit which is propagated by SendUi in case the 'Exit" is
* picked from the message editor's menu.
*/
void CPbkSendContactCmd::ProcessFinished(MPbkBackgroundProcess& /*aProcess*/)
{
PBK_DEBUG_PRINT
(PBK_DEBUG_STRING("CPbkSendContactCmd::ProcessFinished(0x%x)"), this);
// Cancel before usage of active object.
iVcardSender->Cancel();
iVcardSender->Start(TCallBack(&CPbkSendContactCmd::SendvCardsLD, this));
}
/**
* Sends prepared vCard files using send UI.
*/
void CPbkSendContactCmd::SendvCardsLD()
{
// Relinquish ownership, iVcardSender takes care of it self
iVcardSender = NULL;
CleanupStack::PushL(this);
CMessageData* messageData = CMessageData::NewL();
CleanupStack::PushL( messageData );
if (iConverter->FileNames().MdcaCount() > 0 &&
!iUnderDestruction)
{
// Get globals (does not take ownership)
CPbkAppGlobals* globals = CPbkAppGlobals::InstanceL();
if (iMtmUid == KSenduiMtmSmsUid)
{
// Sending through SMS -> there should be only one vCard
// attachment. Package the attachment to a rich text object and
// send it as the message body.
__ASSERT_DEBUG(iConverter->FileNames().MdcaCount()==1,
Panic(EPanicPreCond_SendvCardsL));
// Copy the one and only attachment into a rich text object
CRichText* msgBody = CreateRichTextFromFileLC
(*iEikEnv, iConverter->FileNames().MdcaPoint(0));
messageData->SetBodyTextL( msgBody );
// Send the message using Send Ui
globals->SendUiL()->CreateAndSendMessageL(
iMtmUid, messageData, KMsgBioUidVCard );
CleanupStack::PopAndDestroy(msgBody);
}
else
{
// Not sending through SMS, just pass the attachments
__ASSERT_DEBUG(iConverter->FileNames().MdcaCount()>=1,
Panic(EPanicPreCond_SendvCardsL));
//Fill message data
const TInt count( iConverter->FileNames().MdcaCount());
for( TInt i( 0 ); i < count; ++i )
{
messageData->AppendAttachmentL(
iConverter->FileNames().MdcaPoint( i ) );
}
// Send the message using Send Ui
globals->SendUiL()->CreateAndSendMessageL(
iMtmUid, messageData, KMsgBioUidVCard );
}
}
// Destroy itself as promised
CleanupStack::PopAndDestroy(2); //this, messageData
}
TInt CPbkSendContactCmd::SendvCardsLD(TAny* aThis)
{
CPbkSendContactCmd* self = static_cast<CPbkSendContactCmd*>(aThis);
self->SendvCardsLD();
return EFalse;
}
/**
* If necessary, shows a popup selection list from which the
* the user selects what details are sent in a vCard.
* @return user selection mapped into TPbkChoiceItemEnumerations
*/
TInt CPbkSendContactCmd::SelectSentDataL()
{
TInt selectionIndex(ESendAllData);
// Get the resource id of the menu to be shown
TInt resourceId = SelectionListL();
if (resourceId)
{
// Create a list box
CEikColumnListBox* listBox = static_cast<CEikColumnListBox*>
(EikControlFactory::CreateByTypeL
(EAknCtSinglePopupMenuListBox).iControl);
CleanupStack::PushL(listBox);
// Create a popup list
CAknPopupList* popupList = CAknPopupList::NewL
(listBox, R_AVKON_SOFTKEYS_OK_CANCEL,
AknPopupLayouts::EMenuGraphicWindow);
CleanupStack::PushL(popupList);
HBufC* headingText= CCoeEnv::Static()->AllocReadResourceLC
(R_PBK_BUSINESSCARD_SEND_HEADING);
popupList->SetTitleL(*headingText);
CleanupStack::PopAndDestroy(); // headingText
// Init list box
listBox->SetContainerWindowL(*popupList);
TResourceReader resReader;
CCoeEnv::Static()->CreateResourceReaderLC(resReader, resourceId);
listBox->ConstructFromResourceL(resReader);
CleanupStack::PopAndDestroy(); // resReader
listBox->CreateScrollBarFrameL(ETrue);
listBox->ScrollBarFrame()->SetScrollBarVisibilityL
(CEikScrollBarFrame::EOff, CEikScrollBarFrame::EAuto);
CleanupStack::Pop(); // popupList
// Show popuplist dialog
TInt res = popupList->ExecuteLD();
if (res)
{
selectionIndex = listBox->CurrentItemIndex();
// We have to remap the selection index since
// several different listbox configurations
MapSelection(selectionIndex, resourceId);
}
else
{
selectionIndex = ECancel;
}
CleanupStack::PopAndDestroy(); // listBox
}
return selectionIndex;
}
/**
* Decides what selection list to show the user.
* @return resource id of the menu to show
*/
TInt CPbkSendContactCmd::SelectionListL() const
{
TInt ret(KNoMenu);
TBool supportsFieldType(ETrue);
// Check is the 'send selected fields' feature enabled
TBool sendSelectedFeatureEnabled(iEngine.Constants()->
LocallyVariatedFeatureEnabled(EPbkLVSendSelectedContactFields));
// Check is the sending media SMS and does the contact
// have a thumbnail
TBool smsAndThumbnail(EFalse);
if ((AnyThumbnailsL()) && (IsSmsMtmL()))
{
smsAndThumbnail = ETrue;
}
// If focused field is supplied, the command object was
// launched from contact info view and that requires
// we have to check is the field supported by vCard spec
if (iField)
{
CBCardEngine& bcardEng = CPbkAppGlobals::InstanceL()->BCardEngL(iEngine);
if (!bcardEng.SupportsFieldType(iField->FieldInfo().FieldId()))
{
supportsFieldType = EFalse;
}
}
// Now check the cases when the menu needs to be shown
if (sendSelectedFeatureEnabled)
{
// There are two main branches depending on which view
// this command object was launched from
if (iField)
{
// Command object was launched from contact info view.
if (!smsAndThumbnail)
{
// When there is no thumbnail involved the selection
// menu is shown only if we are over vCard supported
// field
if (supportsFieldType)
{
ret = R_PBK_CONTACTINFO_SEND_OPTIONS;
}
}
else
{
// Thumbnail is involved, the selection menu depends
// on whether we are over vCard supported field or not
if (supportsFieldType)
{
ret = R_PBK_CONTACTINFO_SEND_OPTIONS_SMS_THUMBNAIL;
}
else
{
ret = R_PBK_CONTACTINFO_SEND_OPTIONS_SMS_THUMBNAIL_NO_FIELD;
}
}
}
else
{
// Command object was launched from contact list view,
// the menu is shown only in case there is thumbnail
// involded and the sending media is SMS
if (smsAndThumbnail)
{
ret = R_PHONEBOOK_SEND_OPTIONS;
}
}
}
else
{
// If the 'send selected fields' is disabled, the menu
// needs to be shown only if the user is in contact info
// view and over a vCard supported field
if ((iField) && (supportsFieldType))
{
ret = R_PBK_CONTACTINFO_SEND_OPTIONS;
}
}
return ret;
}
/**
* Maps selection index to choice item TPbkChoiceItemEnumerations.
* @param aSelection goes in as a selection index made by user in
* the selection list, when exiting contains the selection value
* mapped to TPbkChoiceItemEnumerations
* @param aShownMenu what menu was shown to the user (resource id)
*/
void CPbkSendContactCmd::MapSelection(TInt& aSelection,
TInt aShownMenu)
{
switch (aShownMenu)
{
case R_PBK_CONTACTINFO_SEND_OPTIONS:
//'send item data'
//'send all data'
{
switch (aSelection)
{
case EFirstSelection:
{
aSelection = ESendCurrentItem;
break;
}
case ESecondSelection:
{
aSelection = ESendAllData;
break;
}
default:
{
__ASSERT_DEBUG(EFalse, Panic(EPanicLogic_MapSelection));
break;
}
}
break;
}
case R_PBK_CONTACTINFO_SEND_OPTIONS_SMS_THUMBNAIL:
//'send item data'
//'send detail without image'
//'send detail with image'
{
switch (aSelection)
{
case EFirstSelection:
{
aSelection = ESendCurrentItem;
break;
}
case ESecondSelection:
{
aSelection = ESendAllDataWithoutPicture;
break;
}
case EThirdSelection:
{
aSelection = ESendAllData;
break;
}
default:
{
__ASSERT_DEBUG(EFalse, Panic(EPanicLogic_MapSelection));
break;
}
}
break;
}
case R_PBK_CONTACTINFO_SEND_OPTIONS_SMS_THUMBNAIL_NO_FIELD:
//'send detail without image'
//'send detail with image'
{
switch (aSelection)
{
case EFirstSelection:
{
aSelection = ESendAllDataWithoutPicture;
break;
}
case ESecondSelection:
{
aSelection = ESendAllData;
break;
}
default:
{
__ASSERT_DEBUG(EFalse, Panic(EPanicLogic_MapSelection));
break;
}
}
break;
}
case R_PHONEBOOK_SEND_OPTIONS:
//'send without image'
//'send with image'
{
switch (aSelection)
{
case EFirstSelection:
{
aSelection = ESendAllDataWithoutPicture;
break;
}
case ESecondSelection:
{
aSelection = ESendAllData;
break;
}
default:
{
__ASSERT_DEBUG(EFalse, Panic(EPanicLogic_MapSelection));
break;
}
}
break;
}
default:
{
__ASSERT_DEBUG(EFalse, Panic(EPanicLogic_MapSelection));
break;
}
}
}
/**
* Checks are there any thumbnails in the contact set.
* @return ETrue if there was even one thumbnail, EFalse otherwise
*/
TBool CPbkSendContactCmd::AnyThumbnailsL() const
{
TBool ret(EFalse);
if (iContactId != KNullContactId)
{
CPbkContactItem* contact = iEngine.ReadContactLC(iContactId);
ret = HasThumbnail(*contact);
CleanupStack::PopAndDestroy(contact);
}
else if (iContacts && iContacts->Count() > 0)
{
for (TInt i=0; i < iContacts->Count(); ++i)
{
CPbkContactItem* contact = iEngine.ReadContactLC((*iContacts)[i]);
ret = HasThumbnail(*contact);
CleanupStack::PopAndDestroy(contact);
if (ret)
{
// We can exit the loop as soon as a thumbnail
// is found
break;
}
}
}
return ret;
}
/**
* Checks does aItem have a thumbnail.
* @return ETrue if the contact has a thumbnail, EFalse otherwise
*/
TBool CPbkSendContactCmd::HasThumbnail(CPbkContactItem& aItem) const
{
const TPbkContactItemField* field =
aItem.FindField(EPbkFieldIdThumbnailImage);
return (field && !field->IsEmptyOrAllSpaces());
}
/**
* Checks is the selected sending media SMS.
* @return ETrue if SMS is the sending media, EFalse otherwise
*/
TBool CPbkSendContactCmd::IsSmsMtmL() const
{
TBool ret(EFalse);
if ( iMtmUid == KSenduiMtmSmsUid )
{
ret = ETrue;
}
return ret;
}
TUid CPbkSendContactCmd::ShowSendQueryL()
{
return CPbkAppGlobals::InstanceL()->SendUiL()
->ShowSendQueryL( NULL, iParams.iCapabilities, iParams.iMtmFilter );
}
void CPbkSendContactCmd::AddObserver( MPbkCommandObserver& aObserver )
{
iObserver = &aObserver;
}
// End of File