diff -r 000000000000 -r ba25891c3a9e appinstaller/AppMngr2/src/appmngr2infoarray.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/appinstaller/AppMngr2/src/appmngr2infoarray.cpp Thu Dec 17 08:51:10 2009 +0200 @@ -0,0 +1,392 @@ +/* +* Copyright (c) 2008 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: Base class for AppInfo and PackageInfo arrays +* +*/ + + +#include "appmngr2infoarray.h" // CAppMngr2InfoArray +#include // FLOG macros + +const TInt KGranularity = 32; + + +// ======== LOCAL FUNCTIONS ========= + +// --------------------------------------------------------------------------- +// CompareInfoNamesC +// --------------------------------------------------------------------------- +// Compares two InfoBase objects and defines the order how they are displayed +// in the UI (in installed applications list or in installation files list). +// Returns positive integer if aInfo1 > aInfo2 (in defined/alphabetical order), +// zero if aInfo == aInfo2, and negative integer if aInfo1 < aInfo2. +TInt CompareInfoNamesC( const CAppMngr2InfoBase& aInfo1, const CAppMngr2InfoBase& aInfo2 ) + { + // IsShowOnTop() must denote "less than" to get the item be displayed before others. + // If IsShowOnTop()==ETrue then item is less than item having IsShowOnTop()==EFalse. + TInt result = ( aInfo2.IsShowOnTop() - aInfo1.IsShowOnTop() ); + if( !result ) + { + result = aInfo1.Name().CompareC( aInfo2.Name() ); + if( !result ) + { + // Compare also locations if the names are the same, as otherwise the order of + // two items having the same name changes in MoveCachedItemsToArrayInOrderL(). + // Memory card location is "greater than" location in phone internal memory. + result = ( aInfo1.Location() - aInfo2.Location() ); + } + } + return result; + } + + +// ======== MEMBER FUNCTIONS ======== + +// --------------------------------------------------------------------------- +// CAppMngr2InfoArray::CAppMngr2InfoArray() +// --------------------------------------------------------------------------- +// +CAppMngr2InfoArray::CAppMngr2InfoArray( MAppMngr2InfoArrayObserver& aObserver ) : + iObserver( aObserver), iArray( KGranularity ), iCache( KGranularity ), + iAlphabeticalOrder( CompareInfoNamesC ), iQuickRefreshes( ETrue ) + { + } + +// --------------------------------------------------------------------------- +// CAppMngr2InfoArray::~CAppMngr2InfoArray() +// --------------------------------------------------------------------------- +// +CAppMngr2InfoArray::~CAppMngr2InfoArray() + { + iArray.ResetAndDestroy(); + iCache.ResetAndDestroy(); + } + +// --------------------------------------------------------------------------- +// CAppMngr2InfoArray::At() +// --------------------------------------------------------------------------- +// +CAppMngr2InfoBase* CAppMngr2InfoArray::At( TInt aIndex ) const + { + if( IsCacheUsed() ) + { + return iCache[ aIndex ]; + } + return iArray[ aIndex ]; + } + +// --------------------------------------------------------------------------- +// CAppMngr2InfoArray::Count() +// --------------------------------------------------------------------------- +// +TInt CAppMngr2InfoArray::Count() const + { + if( IsCacheUsed() ) + { + return iCache.Count(); + } + return iArray.Count(); + } + +// --------------------------------------------------------------------------- +// CAppMngr2InfoArray::IncrementCacheUseL() +// --------------------------------------------------------------------------- +// +void CAppMngr2InfoArray::IncrementCacheUseL() + { + if( !iUseCache ) + { + // First time - take the cache in use. This starts adding new + // items to the CAppMngr2InfoArray. New items are always added + // to iArray, and iCache is displayed during adding. + if( iForceCacheUse ) + { + // Cache already used - initialize iArray for new items. + iArray.ResetAndDestroy(); + + // Make sure that observer is notified even if no items + // are added to iArray (e.g. "last item deleted" -case). + iArrayChangedObserverNeedsNotification = ETrue; + } + else + { + // Cache not used - move items to cache and start using it. + MoveItemsToCacheMaintainingOrderL(); + + // Reset flag - need to know if cache is forced in use + // while it is still in use because of this increment. + iForceCacheUseWhenAddingComplete = EFalse; + } + } + iUseCache++; + } + +// --------------------------------------------------------------------------- +// CAppMngr2InfoArray::IncrementCacheUseStartingNewRoundL() +// --------------------------------------------------------------------------- +// +void CAppMngr2InfoArray::IncrementCacheUseStartingNewRoundL() + { + if( iUseCache || iForceCacheUse ) + { + // Cache already used - initialize iArray for new items. + iArray.ResetAndDestroy(); + iArrayChangedObserverNeedsNotification = ETrue; + + if( iQuickRefreshes ) + { + // Touch luck - cache is not really used (in order to + // show the first items quickly on startup) and hence + // it is now necessary to show empty list to the user. + // If iObserver would not be notified, there would be + // USER 130 panics when UI would try to access iArray + // items that are deleted. + iObserver.ArrayContentChanged( this, iUseCache + 1 ); + iArrayChangedObserverNeedsNotification = EFalse; + } + } + IncrementCacheUseL(); + } + +// --------------------------------------------------------------------------- +// CAppMngr2InfoArray::DecrementCacheUse() +// --------------------------------------------------------------------------- +// +void CAppMngr2InfoArray::DecrementCacheUse() + { + if( iUseCache > 0 ) + { + iUseCache--; + + if( !iUseCache ) + { + // Quick refreshes are enabled on startup, but they need to be + // turned off when the first item adding round is complete. + // After the first round the cache is used to handle array + // content changes. + if( iQuickRefreshes ) + { + iQuickRefreshes = EFalse; + } + + // If adding is now complete, check if there is a request to + // force cache on. Note that iForceInUse cannot be set when + // cache is already in use (because items are added to iArray), + // otherwise partial list would be in iCache and the rest in + // iArray. This is why force cache in use is enabled only after + // the addition is complete. + if( iForceCacheUseWhenAddingComplete ) + { + iForceCacheUse = ETrue; + iForceCacheUseWhenAddingComplete = EFalse; + } + } + + // Notify UI if cache is not used any more (or quickrefreshing is on) + if( !IsCacheUsed() ) + { + NotifyObserver(); + } + } + } + +// --------------------------------------------------------------------------- +// CAppMngr2InfoArray::DisableRefreshNotificationsL() +// --------------------------------------------------------------------------- +// +void CAppMngr2InfoArray::DisableRefreshNotificationsL() + { + // Refresh notifications are disabled by forcing cache on. All changes go + // to the iArray and current iCache content is displayed unitl notifications + // are enabled again. Observer is not called while refreshes are disabled. + if( !iForceCacheUse ) + { + if( iUseCache ) + { + // Cannot force cache in use now because items are still being added + // to iArray. This would result partial list to be in iCache and the + // rest of items in iArray. Later when deletions are enabled, only the + // items in iArray would be displayed. So, set a flag that forces cache + // on when item additions are complete, so that observer is notified + // only after deletions are enabled again. + iForceCacheUseWhenAddingComplete = ETrue; + } + else + { + // Set cache in use + MoveItemsToCacheMaintainingOrderL(); + iForceCacheUse = ETrue; + } + } + } + +// --------------------------------------------------------------------------- +// CAppMngr2InfoArray::EnableRefreshNotificationsL() +// --------------------------------------------------------------------------- +// +void CAppMngr2InfoArray::EnableRefreshNotificationsL() + { + // It is safe to call EnableRefreshNotificationsL() many times, even if + // notifications have not been disabled first. + iForceCacheUseWhenAddingComplete = EFalse; + if( iForceCacheUse ) + { + iForceCacheUse = EFalse; + if( !iUseCache ) + { + // Refresh notifications were disabled by moving items to iCache. If no + // new items were added to iArray while notifications were disabled, + // then those moved items must be moved back to iArray again. Otherwise + // disabling cache would show just an empty list. If new items were added + // while refresh notifications were disabled, then observer needs + // notification and cache content can be ignored, as new content is + // already available in iArray. + if( iArrayChangedObserverNeedsNotification ) + { + NotifyObserver(); + } + else + { + MoveCachedItemsToArrayInOrderL(); + } + } + } + } + +// --------------------------------------------------------------------------- +// CAppMngr2InfoArray::AddItemInOrderL() +// --------------------------------------------------------------------------- +// +void CAppMngr2InfoArray::AddItemInOrderL( CAppMngr2InfoBase* aInfo ) + { + if( aInfo ) + { + FLOG( "CAppMngr2InfoArray::AddItemInOrderL( %S )", &( aInfo->Name() ) ); + + // All items are added to iArray and iCache is used while the changes + // are not wanted to be seen via Count() and At() functions. Observer + // is notified when the iArray is taken in use. + iArray.InsertInOrderAllowRepeatsL( aInfo, iAlphabeticalOrder ); + if( iForceCacheUse || iForceCacheUseWhenAddingComplete ) + { + iArrayChangedObserverNeedsNotification = ETrue; + } + } + } + +// --------------------------------------------------------------------------- +// CAppMngr2InfoArray::ImmediateDelete() +// --------------------------------------------------------------------------- +// +void CAppMngr2InfoArray::ImmediateDelete( CAppMngr2InfoBase* aInfo ) + { + FLOG( "CAppMngr2InfoArray::ImmediateDelete( 0x%08x )", aInfo ); + + RPointerArray* array; + if( IsCacheUsed() ) + { + array = &iCache; + } + else + { + array = &iArray; + } + + TBool found = EFalse; + TInt count = array->Count(); + for( TInt index = 0; index < count && !found; index++ ) + { + if( ( *array )[ index ] == aInfo ) + { + array->Remove( index ); + delete aInfo; + found = ETrue; + } + } + + if( found ) + { + iObserver.ArrayContentChanged( this, iUseCache ); + } + } + +// --------------------------------------------------------------------------- +// CAppMngr2InfoArray::MoveItemsToCacheMaintainingOrderL() +// --------------------------------------------------------------------------- +// +void CAppMngr2InfoArray::MoveItemsToCacheMaintainingOrderL() + { + // Move items from iArray to iCache maintaining order. + iCache.ResetAndDestroy(); + TInt count = iArray.Count(); + for( TInt index = count - 1; index >= 0; index-- ) + { + iCache.InsertL( iArray[ index ], 0 ); + iArray.Remove( index ); + } + iArray.ResetAndDestroy(); + } + +// --------------------------------------------------------------------------- +// CAppMngr2InfoArray::MoveCachedItemsToArrayInOrderL() +// --------------------------------------------------------------------------- +// +void CAppMngr2InfoArray::MoveCachedItemsToArrayInOrderL() + { + // Move items from iCache to iArray using proper order. There may be + // new items in iArray hence it is not possible to simply move items + // as in MoveItemsToCacheMaintainingOrderL(). + TInt count = iCache.Count(); + for( TInt index = count - 1; index >= 0; index-- ) + { + iArray.InsertInOrderAllowRepeatsL( iCache[ index ], iAlphabeticalOrder ); + iCache.Remove( index ); + } + iCache.ResetAndDestroy(); + } + +// --------------------------------------------------------------------------- +// CAppMngr2InfoArray::NotifyObserver() +// --------------------------------------------------------------------------- +// +void CAppMngr2InfoArray::NotifyObserver() + { + iObserver.ArrayContentChanged( this, iUseCache ); + iArrayChangedObserverNeedsNotification = EFalse; + iCache.ResetAndDestroy(); + } + +// --------------------------------------------------------------------------- +// CAppMngr2InfoArray::IsCacheUsed() +// --------------------------------------------------------------------------- +// +TBool CAppMngr2InfoArray::IsCacheUsed() const + { + // New items are added to iArray and static old content may be displayed + // from iCache while iArray is being updated. Priorities: 1) cache forced + // in use, 2) quick refreshes, and 3) normal inc/dec cache usage. If cache + // is forced in use, it prevents also changes that quick refreshes might + // otherwise display. If quick refreshing is enabled, then item additions + // to iArray are visible to observer, although normally iCache would be used. + if( iForceCacheUse ) + { + return ETrue; + } + if( iQuickRefreshes ) + { + return EFalse; + } + return ( iUseCache > 0 ); + } +