/*
* Copyright (c) 2002-2007 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: A class that processes sim contacts into appropritate form for
* the copying process.
* Handles errors related to SIM contact fields
*
*/
// INCLUDE FILES
#include "CPsu2SimContactProcessor.h"
// Phonebook 2
#include "Pbk2USimUI.hrh"
#include "CPsu2CopyToSimFieldInfoArray.h"
#include "CPsu2CharConv.h"
#include <MPbk2FieldProperty.h>
#include <MPbk2ContactNameFormatter.h>
#include <Pbk2ContactFieldCopy.h>
// Virtual Phonebook
#include <TVPbkFieldTypeMapping.h>
#include <CVPbkContactManager.h>
#include <MVPbkContactStore.h>
#include <MVPbkFieldType.h>
#include <MVPbkBaseContact.h>
#include <MVPbkStoreContact.h>
#include <MVPbkStoreContactField.h>
#include <MVPbkContactStoreProperties.h>
#include <MVPbkContactFieldTextData.h>
#include <CVPbkFieldTypeRefsList.h>
#include <CVPbkContactFieldIterator.h>
#include <CVPbkFieldTypeRefsList.h>
#include <VPbkEng.rsg>
// System includes
#include <gsmerror.h>
#include <exterror.h>
#include <barsread.h>
#include <coemain.h>
#include <featmgr.h>
// Debugging headers
#include <Pbk2Debug.h>
/// Unnamed namespace for local definitions
namespace {
#ifdef _DEBUG
enum TPanicCode
{
EPreCond_SplitToSimContactsL
};
void Panic(TInt aReason)
{
_LIT( KPanicText, "CPsu2SimContactProcessor");
User::Panic( KPanicText, aReason );
}
#endif // _DEBUG
/**
* A helper class to keep track of the number fields of certain type
*/
class TFieldTypeCounter
{
public: // Construction
/**
* Constructor.
*
* @param aType Field type.
*/
TFieldTypeCounter(
const MVPbkFieldType& aType ) :
iType( aType ), iCounter( 0 )
{}
public: // Data
// Ref: Field type
const MVPbkFieldType& iType;
// Own: Number of fields
TInt iCounter;
};
/**
* Returns matching field type counter object based on given field type.
*
* @param aArray Array of field type counters.
* @param aType Field type of interest.
* @return Matching field type counter.
*/
TFieldTypeCounter& FieldTypeCounterL(
RArray<TFieldTypeCounter>& aArray, const MVPbkFieldType& aType )
{
const TInt count = aArray.Count();
for (TInt i = 0; i < count; ++i)
{
if (aArray[i].iType.IsSame(aType))
{
return aArray[i];
}
}
aArray.AppendL(TFieldTypeCounter(aType));
return aArray[count];
}
/**
* Checks if the contact already has maximum amount fields
* of given field type.
*
* @param aMaxAmountFieldInContact Max number of allowed fields of type.
* @param aType Field type.
* @param aArray Array of field type counters.
* @return ETrue if contact already has max amount of fields of given type.
*/
TBool CheckNumberOfFieldsL(
TInt aMaxAmountFieldInContact, const MVPbkFieldType& aType,
RArray<TFieldTypeCounter>& aArray )
{
TBool ret( EFalse );
TFieldTypeCounter& counter = FieldTypeCounterL( aArray, aType );
// Compare the max amount fields to the current amount of the
// fields in the contact
if (counter.iCounter >= aMaxAmountFieldInContact)
{
ret = ETrue;
}
// Add one to counter of the given type
++counter.iCounter;
return ret;
}
/**
* Checks is given field type a number type.
*
* @param aSimType Field type.
* @return ETrue if the field is of number type.
*/
TBool IsNumberType( const MVPbkFieldType& aSimType )
{
TBool ret = EFalse;
// SIM number type is always Mobile phone (general) -> EVPbkVersitNameTEL,
// therefore a selector is not needed
TArray<TVPbkFieldVersitProperty> props = aSimType.VersitProperties();
if ( props.Count() > 0 && props[0].Name() == EVPbkVersitNameTEL )
{
ret = ETrue;
}
return ret;
}
/**
* Returns a valid number, removes e.g spaces, braces...
*
* @param aSource Source descriptor.
* @param aNumberKeyMap Number key mapping.
* @return Formatted valid number.
*/
HBufC* CreateValidNumberLC(
const TDesC& aSource, const TDesC& aNumberKeyMap )
{
const TInt length = aSource.Length();
HBufC* number = HBufC::NewLC( length );
TPtr ptr( number->Des() );
for ( TInt i = 0; i < length; ++i )
{
if ( aNumberKeyMap.Locate( aSource[i] ) != KErrNotFound )
{
ptr.Append( aSource[i] );
}
}
return number;
}
} /// namespace
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::CPsu2SimContactProcessor
// --------------------------------------------------------------------------
//
CPsu2SimContactProcessor::CPsu2SimContactProcessor(
MVPbkContactStore& aTargetStore,
CPsu2CopyToSimFieldInfoArray& aCopyToSimFieldInfoArray,
MPbk2ContactNameFormatter& aNameFormatter,
const MVPbkFieldTypeList& aMasterFieldTypeList )
: iTargetStore( aTargetStore ),
iCopyToSimFieldInfoArray( aCopyToSimFieldInfoArray ),
iNameFormatter( aNameFormatter ),
iMasterFieldTypeList( aMasterFieldTypeList ),
iSimMaxMatchPriority(
aTargetStore.StoreProperties().SupportedFields().
MaxMatchPriority() )
{
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::~CPsu2SimContactProcessor
// --------------------------------------------------------------------------
//
CPsu2SimContactProcessor::~CPsu2SimContactProcessor()
{
iNewSimContacts.ResetAndDestroy();
iIncludedTypes.Close();
delete iCharConvUcs2;
delete iCharConvSms7Bit;
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::NewL
// --------------------------------------------------------------------------
//
CPsu2SimContactProcessor* CPsu2SimContactProcessor::NewL(
MVPbkContactStore& aTargetStore,
CPsu2CopyToSimFieldInfoArray& aCopyToSimFieldInfoArray,
MPbk2ContactNameFormatter& aNameFormatter,
const MVPbkFieldTypeList& aMasterFieldTypeList,
RFs& aFs )
{
CPsu2SimContactProcessor* self = new( ELeave ) CPsu2SimContactProcessor
( aTargetStore, aCopyToSimFieldInfoArray, aNameFormatter,
aMasterFieldTypeList );
CleanupStack::PushL( self );
self->ConstructL( aFs );
CleanupStack::Pop( self );
return self;
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::ConstructL
// --------------------------------------------------------------------------
//
void CPsu2SimContactProcessor::ConstructL( RFs& aFs )
{
const TInt count = iCopyToSimFieldInfoArray.Count();
for ( TInt i = 0; i < count; ++i )
{
iIncludedTypes.AppendL(
&iCopyToSimFieldInfoArray[i].SourceType() );
}
iCharConvUcs2 = CPsu2CharConv::NewL( aFs, KCharacterSetIdentifierUcs2 );
// Symbian character converter uses CR/LF by default. (U)SIM uses CR.
// (JustLineFeed is ok here because the converter is used just to check
// SIM conversion lengths)
iCharConvUcs2->SetDownGradeLf( CCnvCharacterSetConverter::
EDowngradeExoticLineTerminatingCharactersToJustLineFeed );
iCharConvSms7Bit = CPsu2CharConv::NewL( aFs, KCharacterSetIdentifierSms7Bit );
iCharConvSms7Bit->SetDownGradeLf( CCnvCharacterSetConverter::
EDowngradeExoticLineTerminatingCharactersToJustLineFeed );
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::HandleSimError
// --------------------------------------------------------------------------
//
TBool CPsu2SimContactProcessor::HandleSimError( TInt aError )
{
TBool result = EFalse;
switch (aError)
{
case KErrGsmSimServAnrFull:
{
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::HandleSimError KErrGsmSimServAnrFull"));
// There might be several EF ANR files in USIM. Save the number
// of error messages and use it later to check how many numbers
// can be put to one contact
++iNumOfAdditionalNumberErrors;
result = ETrue;
break;
}
case KErrGsmSimServEmailFull:
{
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::HandleSimError KErrGsmSimServEmailFull"));
// SIM contact can not have emails anymore because EF(email) is full
iSimErrors |= KPsu2EMailFullError;
RemoveFieldTypesFromIncludedTypes( KPsu2EMailFullError );
result = ETrue;
break;
}
case KErrGsmSimServSneFull:
{
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::HandleSimError KErrGsmSimServSneFull"));
iSimErrors |= KPsu2SecondNameFullError;
RemoveFieldTypesFromIncludedTypes( KPsu2SecondNameFullError );
result = ETrue;
break;
}
default:
{
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::HandleSimError unhandled error code %d"),
aError );
break;
}
}
return result;
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::CreateSimContactsL
//
// This called to create SIM contacts from a source contact
// The copying logic:
// 1) Create a SIM contact and add all the fields from the source
// that possibly can be copied.
// 2) If the created SIM contact has too many fields for one SIM contact,
// split the SIM contact. If the splitted contact has still too many
// fields for one SIM contact split the splitted contact. This is
// continued until the splitted contact doesn't need to be splitted
// again.
// --------------------------------------------------------------------------
//
void CPsu2SimContactProcessor::CreateSimContactsL(
MVPbkStoreContact& aSourceContact,
RPointerArray<MVPbkStoreContact>& aSimContacts )
{
iNewSimContacts.ResetAndDestroy();
// Create a target contact
MVPbkStoreContact* contact = iTargetStore.CreateNewContactLC();
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateSimContactsL target contact created"));
// Add name to the new contact
AddNameFieldsL(aSourceContact, *contact);
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateSimContactsL name fields added"));
// Append fields that can possible be copied to the SIM
AppendSupportedFieldsL(aSourceContact, *contact);
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateSimContactsL supported fields added"));
// Split the contact
CleanupStack::Pop(); // contact
SplitToSimContactsL(contact);
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateSimContactsL contact splitted"));
// Contacts that have only name are not copied to SIM
RemoveContactsThatHaveOnlyNameL();
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateSimContactsL name only contacts removed"));
const TInt firstPos = 0;
const TInt count = iNewSimContacts.Count();
for ( TInt i = count - 1; i >= 0; --i )
{
aSimContacts.InsertL( iNewSimContacts[i], firstPos );
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateSimContactsL contact added to new SIM contacts"));
iNewSimContacts.Remove( i );
}
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::CreateFixedSimContactsL
//
// This is called when saving a SIM contact has failed and HandleSimError
// has handled the error. It means that the state of this processor has
// changed and the failed SIM contact can be splitted or there are fields
// that must be removed from the failed contact. After this the resulted
// SIM contacts will be copied again.
// --------------------------------------------------------------------------
//
void CPsu2SimContactProcessor::CreateFixedSimContactsL(
MVPbkStoreContact& aSimContact,
RPointerArray<MVPbkStoreContact>& aSimContacts )
{
iNewSimContacts.ResetAndDestroy();
// Create a target contact
MVPbkStoreContact* contact = iTargetStore.CreateNewContactLC();
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateFixedSimContactsL target contact created"));
// Add name to the new contact
AddNameFieldsL(aSimContact, *contact);
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateFixedSimContactsL name fields added"));
// Append fields that can possible be copied to the SIM
AppendSupportedFieldsL(aSimContact, *contact);
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateFixedSimContactsL supported fields added"));
const TInt firstPos = 0;
const TInt newCount = contact->Fields().FieldCount();
if (newCount > 0 && newCount != aSimContact.Fields().FieldCount())
{
// After appending supported fields the amount of field is different
// than in the original contact. This means that there has been
// an error that has changed the included types list.
if ( IsValidContactToSaveL( *contact ) )
{
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateFixedSimContactsL is valid contact to save"));
aSimContacts.InsertL(contact, firstPos);
CleanupStack::Pop(); // contact
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateFixedSimContactsL contact added to new SIM contacts"));
}
else
{
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateFixedSimContactsL is not valid contact to save"));
CleanupStack::PopAndDestroy(); // contact
}
}
else
{
// Split the contact
CleanupStack::Pop(); // contact
// Takes ownership of the contact
if ( SplitToSimContactsL( contact ) )
{
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateFixedSimContactsL contact splitted"));
// Contacts that have only name are not copied to SIM
RemoveContactsThatHaveOnlyNameL();
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateFixedSimContactsL name only contacts removed"));
const TInt count = iNewSimContacts.Count();
for (TInt i = count - 1; i >= 0; --i)
{
aSimContacts.InsertL(iNewSimContacts[i], firstPos);
iNewSimContacts.Remove(i);
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateFixedSimContactsL contact added to new SIM contacts"));
}
}
else
{
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::CreateFixedSimContactsL contact does not split"));
iNewSimContacts.ResetAndDestroy();
}
}
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::CreateFixedSimContactsL
// --------------------------------------------------------------------------
//
TBool CPsu2SimContactProcessor::DetailsDropped()
{
TBool ret( EFalse );
ret = iSimErrors & KPsu2EMailFullError;
if ( !ret )
{
ret = iSimErrors & KPsu2SecondNameFullError;
}
return ret;
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::RemoveFieldTypesFromIncludedTypes
//
// Removes error related field types from the included types so those
// types won't be copied to SIM contact anymore.
// --------------------------------------------------------------------------
//
void CPsu2SimContactProcessor::RemoveFieldTypesFromIncludedTypes(
TPsu2ErrorCode aBlockingError )
{
const TInt includedCount = iIncludedTypes.Count();
for ( TInt i = includedCount - 1; i >= 0; --i )
{
const TPsu2CopyToSimFieldInfo* info =
iCopyToSimFieldInfoArray.FindInfoForSourceType(
*iIncludedTypes[i] );
if ( info && info->BlockedByError( aBlockingError ) )
{
iIncludedTypes.Remove( i );
}
}
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::AddNameFieldsL
//
// Adds name fields from source contact to the target.
// --------------------------------------------------------------------------
//
void CPsu2SimContactProcessor::AddNameFieldsL(
MVPbkStoreContact& aSource, MVPbkStoreContact& aTarget )
{
// Copy formatted name always to SIM's name field
CopyTitleFieldDataL( aSource, aTarget,
iCopyToSimFieldInfoArray.SimNameType() );
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::CopyReadingFieldsL
//
// Copies reading fields in Japanese variants.
// --------------------------------------------------------------------------
//
void CPsu2SimContactProcessor::CopyReadingFieldsL(
MVPbkStoreContact& aSource, MVPbkStoreContact& aTarget )
{
// In case the name can be built without first name reading
// and last name reading, the last name reading and first name reading
// field data is combined according to name formatting rules and
// the formatted name Reading is copied to Second name field
// in Japanese variants.
const MVPbkFieldTypeList& supportedTypes =
iTargetStore.StoreProperties().SupportedFields();
if ( supportedTypes.ContainsSame(
iCopyToSimFieldInfoArray.LastNameReadingType() ) )
{
CVPbkFieldTypeRefsList* list = CVPbkFieldTypeRefsList::NewL();
CleanupStack::PushL( list );
// Get fields that actually a part of the formatted name
CVPbkBaseContactFieldTypeListIterator* itr =
iNameFormatter.ActualTitleFieldsLC( *list, aSource.Fields() );
// Check if the title has reading fields
TBool containsReading = EFalse;
while ( itr->HasNext() )
{
TInt typeId =
itr->Next()->BestMatchingFieldType()->FieldTypeResId();
if ( typeId == R_VPBK_FIELD_TYPE_LASTNAMEREADING ||
typeId == R_VPBK_FIELD_TYPE_FIRSTNAMEREADING )
{
containsReading = ETrue;
}
}
CleanupStack::PopAndDestroy(2, list);
// If title doesn't contain reading fields then copy formatted
// reading to SIM's reading (=second name) field
if ( !containsReading )
{
// Create a temp contact from source store for getting
// field collection of reading fields
MVPbkStoreContact* tmpCnt =
aSource.ParentStore().CreateNewContactLC();
MVPbkStoreContactFieldCollection& sourceFields =
aSource.Fields();
const TInt fieldCount = sourceFields.FieldCount();
for ( TInt i = 0; i < fieldCount; ++i )
{
const MVPbkFieldType* sourceType =
sourceFields.FieldAt( i ).BestMatchingFieldType();
if ( sourceType )
{
TInt typeId = sourceType->FieldTypeResId();
if ( typeId == R_VPBK_FIELD_TYPE_LASTNAMEREADING ||
typeId == R_VPBK_FIELD_TYPE_FIRSTNAMEREADING )
{
Pbk2ContactFieldCopy::CopyFieldL
( sourceFields.FieldAt( i ),
*sourceType, *tmpCnt );
}
}
}
if ( tmpCnt->Fields().FieldCount() > 0 )
{
// If there were reading fields in the source then copy
// the formatted reading to SIM's last name reading
CopyTitleFieldDataL( *tmpCnt, aTarget,
iCopyToSimFieldInfoArray.LastNameReadingType() );
}
CleanupStack::PopAndDestroy(); // tmpCnt
}
}
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::AppendSupportedFieldsL
//
// Appends fields that are in included properties and supported
// by the SIM store.
// --------------------------------------------------------------------------
//
void CPsu2SimContactProcessor::AppendSupportedFieldsL(
MVPbkBaseContact& aSource, MVPbkStoreContact& aTarget)
{
const MVPbkBaseContactFieldCollection& fields = aSource.Fields();
const TInt fieldCount = fields.FieldCount();
const TInt typeCount = iIncludedTypes.Count();
const TInt maxPriority = iMasterFieldTypeList.MaxMatchPriority();
const MVPbkFieldTypeList& supportedTypes =
iTargetStore.StoreProperties().SupportedFields();
TBool contactCopyFailed = EFalse;
for (TInt i = 0; i < fieldCount && !contactCopyFailed; ++i)
{
// Get the source field
const MVPbkBaseContactField& field = fields.FieldAt(i);
// Get the source field type
const MVPbkFieldType* type = field.BestMatchingFieldType();
TBool fieldCopied = EFalse;
for (TInt j = 0; j < typeCount && type && !fieldCopied
&& !contactCopyFailed; ++j)
{
// Check if the field is one of the fields that can be copied
// to the SIM
if (type->IsSame(*iIncludedTypes[j]))
{
// The field possible can be copied to the SIM
// Get the target(=SIM) field type for source type
const MVPbkFieldType* simType =
iCopyToSimFieldInfoArray.ConvertToSimType(*type);
// Check if the sim store supports the converted field
if (simType && supportedTypes.ContainsSame(*simType))
{
if ( !CopyFieldL(field, aTarget, *simType) )
{
// If copying of one field fails then copying
// the contact fails
contactCopyFailed = ETrue;
PBK2_DEBUG_PRINT(PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::AppendSupportedFieldsL contact copy failed"));
}
fieldCopied = ETrue;
}
}
}
}
if ( contactCopyFailed )
{
// Remove all fields from the target contact
aTarget.RemoveAllFields();
}
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::SplitToSimContactsL
//
// Tries to split the source contact, return ETrue if splitted.
// --------------------------------------------------------------------------
//
TBool CPsu2SimContactProcessor::SplitToSimContactsL(
MVPbkStoreContact* aSourceContact )
{
__ASSERT_DEBUG(iNewSimContacts.Count() == 0,
Panic(EPreCond_SplitToSimContactsL));
MVPbkStoreContact* contact = aSourceContact;
CleanupDeletePushL(contact);
TBool result = EFalse;
MVPbkStoreContact* splitted = SplitContactLC(*contact);
if (splitted)
{
// Contact is splitted at least into two contacts
iNewSimContacts.AppendL(contact);
CleanupStack::Pop(2); // contact, splitted
contact = splitted;
CleanupDeletePushL(contact);
while (contact)
{
// Split as long as must
splitted = SplitContactLC(*contact);
iNewSimContacts.AppendL(contact);
if (splitted)
{
CleanupStack::Pop(2); // contact, splitted
contact = splitted;
CleanupDeletePushL(contact);
}
else
{
CleanupStack::Pop(); // contact
contact = NULL;
}
}
result = ETrue;
}
// Contact can be empty if the CopyFieldL failed
else if ( contact->Fields().FieldCount() > 0 )
{
// Contact is not splitted
iNewSimContacts.AppendL(contact);
CleanupStack::Pop(); // contact
}
return result;
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::SplitContactLC
//
// Splits the master contact if there is too much fields to one
// SIM contact.
// --------------------------------------------------------------------------
//
MVPbkStoreContact* CPsu2SimContactProcessor::SplitContactLC(
MVPbkStoreContact& aSimContact )
{
MVPbkStoreContactFieldCollection& fields = aSimContact.Fields();
TInt count = fields.FieldCount();
RArray<TFieldTypeCounter> fieldTypeCounterArray;
CleanupClosePushL(fieldTypeCounterArray);
RArray<TInt> removedIndexes;
CleanupClosePushL(removedIndexes);
MVPbkStoreContact* splittedContact = NULL;
// Loop all fields of the source contact
for (TInt i = 0; i < count; ++i)
{
const MVPbkFieldType* type =
fields.FieldAt(i).BestMatchingFieldType();
// Check all the data fields
if (type && !iNameFormatter.IsTitleFieldType( *type ) )
{
TInt maxNumber = MaxNumberOfFieldL( aSimContact, *type );
// Compare the max amount fields to the current amount of the
// fields in the contact
if ( CheckNumberOfFieldsL
( maxNumber, *type, fieldTypeCounterArray ) )
{
// Create a new contact for the fields that can not fit to
// the source contact
if (!splittedContact)
{
splittedContact = iTargetStore.CreateNewContactLC();
AddNameFieldsL(aSimContact, *splittedContact);
}
// Copy field to the new contact
// and remove it from the source
CopyFieldL( fields.FieldAt(i), *splittedContact, *type );
removedIndexes.AppendL(i);
}
}
}
count = removedIndexes.Count();
for (TInt k = count - 1; k >= 0; --k)
{
aSimContact.RemoveField(removedIndexes[k]);
}
if (splittedContact)
{
CleanupStack::Pop(); // splittedContact
CleanupStack::PopAndDestroy(2); // removedIndexes,
// fieldTypeCounterArray
CleanupDeletePushL(splittedContact);
}
else
{
CleanupStack::PopAndDestroy(2); // removedIndexes,
// fieldTypeCounterArray
}
return splittedContact;
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::CopyFieldL
//
// Copies the source field to the target contact, EFalse if not copied.
// --------------------------------------------------------------------------
//
TBool CPsu2SimContactProcessor::CopyFieldL(
const MVPbkBaseContactField& aFieldToCopy,
MVPbkStoreContact& aTarget,
const MVPbkFieldType& aSimType )
{
TBool result = ETrue;
if ( aFieldToCopy.FieldData().DataType() == EVPbkFieldStorageTypeText )
{
// Get source data
const MVPbkContactFieldTextData& sourceData =
MVPbkContactFieldTextData::Cast( aFieldToCopy.FieldData() );
// Create target field
MVPbkStoreContactField* field = aTarget.CreateFieldLC( aSimType );
// Get target data
MVPbkContactFieldTextData& targetData =
MVPbkContactFieldTextData::Cast( field->FieldData() );
// Invalid characters must be removed before copying if the
// field is number
if ( IsNumberType( aSimType ) )
{
HBufC* number = CreateValidNumberLC( sourceData.Text(),
iCopyToSimFieldInfoArray.NumberKeyMap() );
result = CopyFieldDataL( *number, targetData, aSimType );
CleanupStack::PopAndDestroy( number );
}
else
{
result = CopyFieldDataL
( sourceData.Text(), targetData, aSimType );
}
if ( result )
{
aTarget.AddFieldL(field);
CleanupStack::Pop(); // field
}
else
{
CleanupStack::PopAndDestroy(); // field
}
}
return result;
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::CopyTitleFieldDataL
//
// Copies title field from source contact to given field type.
// --------------------------------------------------------------------------
//
void CPsu2SimContactProcessor::CopyTitleFieldDataL(
MVPbkStoreContact& aSource, MVPbkStoreContact& aTarget,
const MVPbkFieldType& aSimTitleFieldType )
{
HBufC* title = iNameFormatter.GetContactTitleOrNullL
( aSource.Fields(),
MPbk2ContactNameFormatter::EPreserveLeadingSpaces );
if (title)
{
CleanupStack::PushL( title );
MVPbkStoreContactField* field =
aTarget.CreateFieldLC( aSimTitleFieldType );
MVPbkContactFieldTextData& data =
MVPbkContactFieldTextData::Cast( field->FieldData() );
TruncateAndCopyFieldDataL( *title, data );
aTarget.AddFieldL(field);
CleanupStack::Pop(field);
CleanupStack::PopAndDestroy(title);
}
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::CopyFieldDataL
// --------------------------------------------------------------------------
//
TBool CPsu2SimContactProcessor::CopyFieldDataL(
const TDesC& aSource, MVPbkContactFieldTextData& aTarget,
const MVPbkFieldType& aSimType )
{
TBool result = ETrue;
// If truncation is allowed for the field type then truncate
// and copy
if ( iCopyToSimFieldInfoArray.TruncationAllowed( aSimType ) )
{
TruncateAndCopyFieldDataL( aSource, aTarget );
}
// otherwise check if there is enough space for data
else if ( aSource.Length() <=
aTarget.MaxLength() )
{
aTarget.SetTextL( aSource );
}
else
{
result = EFalse;
}
return result;
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::TruncateAndCopyFieldDataL
// --------------------------------------------------------------------------
//
void CPsu2SimContactProcessor::TruncateAndCopyFieldDataL(
const TDesC& aSource,
MVPbkContactFieldTextData& aTarget )
{
PBK2_DEBUG_PRINT( PBK2_DEBUG_STRING
("CPsu2SimContactProcessor::TruncateAndCopyFieldDataL max len: [%i]"),
aTarget.MaxLength() );
// Try first with SMS 7-bit encoding
TInt unconvertedCount(0);
TPtrC data = iCharConvSms7Bit->CheckFieldLengthL
( aSource, aTarget.MaxLength(), unconvertedCount );
PBK2_DEBUG_PRINT( PBK2_DEBUG_STRING
("CPsu2SimContactProcessor:: max converted len: [%i], unconv: [%i]"),
data.Length(), unconvertedCount );
// If any characters could not be converted with SMS 7-bit encoding we need
// to check length using UCS-2 encoding which requires more space per a
// character.
// With some character sets that contain less than 128 characters the
// resulting data is shorter than the maximum field length allowed by the
// (U)SIM. In other words we may truncate the text fields stored to the
// (U)SIM more than would be required by the (U)SIM. This is because
// there are actually three possible coding schemes for such character
// sets available. See Annex B in document 3GPP TS 11.11.
// The conversion is made by SIM ATK TSY so we would need to have some
// SAT server API available to be able to determine the actual maximum
// length for the data unambiguously.
if( unconvertedCount > 0 )
{
// Leave one character extra space in case of non-7-bit conversions
// This seems to be a feature of (U)SIM that it requires one extra byte
// for unicode contact name (see also the 3GPP reference mentioned in
// the comment above).
data.Set( iCharConvUcs2->CheckFieldLengthL
( aSource, aTarget.MaxLength() - 1, unconvertedCount ) );
PBK2_DEBUG_PRINT( PBK2_DEBUG_STRING
("CPsu2SimContactProcessor:: max converted len: [%i], unconv: [%i]"),
data.Length(), unconvertedCount );
}
aTarget.SetTextL( data );
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::MaxNumberOfFieldL
//
// Returns the maximum amount of field of given type that
// can be added to the contact.
// --------------------------------------------------------------------------
//
TInt CPsu2SimContactProcessor::MaxNumberOfFieldL(
MVPbkStoreContact& aContact, const MVPbkFieldType& aType )
{
TInt maxAmount( aContact.MaxNumberOfFieldL( aType ) );
// For numbers it must be checked that is ANR file(s) of USIM full.
if ( IsNumberType( aType ) )
{
// Reduce the max according to amount of ANR errors from TSY
maxAmount -= iNumOfAdditionalNumberErrors;
}
return maxAmount;
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::RemoveContactsThatHaveOnlyNameL
// --------------------------------------------------------------------------
//
void CPsu2SimContactProcessor::RemoveContactsThatHaveOnlyNameL()
{
const TInt cntCount = iNewSimContacts.Count();
for ( TInt i = cntCount - 1; i >= 0; --i )
{
if ( !IsValidContactToSaveL( *iNewSimContacts[i] ) )
{
delete iNewSimContacts[i];
iNewSimContacts.Remove( i );
}
}
}
// --------------------------------------------------------------------------
// CPsu2SimContactProcessor::IsValidContactToSaveL
// --------------------------------------------------------------------------
//
TBool CPsu2SimContactProcessor::IsValidContactToSaveL(
MVPbkStoreContact& aSimContact )
{
// Specification says that "If the contact does not contain any number
// it is not copied to SIM.". This is valid for 2G SIMs but it's assumed
// that in USIM this means that contact that don't have any data fields
// is not copied. In other words contact is valid if it has other than
// title fields.
MVPbkStoreContactFieldCollection& fields = aSimContact.Fields();
const TInt fieldCount = fields.FieldCount();
for ( TInt i = 0; i < fieldCount; ++i )
{
if ( !iNameFormatter.IsTitleField( fields.FieldAt( i ) ) )
{
return ETrue;
}
}
return EFalse;
}
// End of File