javauis/coreui_akn/src/startupscreen/startscreen.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 14 May 2010 15:47:24 +0300
changeset 23 98ccebc37403
parent 21 2a9601315dfc
permissions -rw-r--r--
Revision: v2.1.24 Kit: 201019

/*
* 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: A user start screen impl.
*
*/

#include <iostream>
#include <fstream>
#include <AknsBasicBackgroundControlContext.h>
#include <AknsDrawUtils.h>
#include <AknUtils.h>
#include <layoutmetadata.cdl.h>
#include <imageconversion.h>

#include "startscreen.h"
#include "logger.h"
#include "javasymbianoslayer.h"
#include "coreuiavkonimpl.h"

_LIT(KUserFolder, "startupimages\\");
_LIT(KUserDefaultImage, "startupscreen.img");
_LIT(KUserLandscapeImage, "startupscreen_landscape.img");
_LIT(KUserPortraitImage, "startupscreen_portrait.img");

_LIT(KAutoFolder, "autostartupimages\\");
_LIT(KAutoLandscapeImage, "landscape.png"); // Gets prefixed by TUid
_LIT(KAutoPortraitImage, "portrait.png"); // Gets prefixed by TUid


CStartScreen* CStartScreen::NewL(CStartScreen::TStartScreenType aType,
                                 const java::ui::CoreUiParams& aParams)
{
    JELOG2(EJavaUI);
    CStartScreen* self = new(ELeave) CStartScreen(aType, aParams);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
}

CStartScreen::~CStartScreen()
{
    JELOG2(EJavaUI);
    Cancel();
    delete mPortraitBmp;
    delete mPortraitMask;
    if (mPortraitBmp != mLandscapeBmp)
    {
        delete mLandscapeBmp;
        delete mLandscapeMask;
    }
    delete mAsyncSaveBmp;
    delete mDecoder;
    delete mEncoder;
    mPath.Close();
    mAsyncSavePath.Close();
}

void CStartScreen::Draw(CWindowGc& aGc,
                        const TRect& aRect) const
{
    JELOG2(EJavaUI);

    CFbsBitmap* bmp = 0;
    CFbsBitmap* mask = 0;
    if (Layout_Meta_Data::IsLandscapeOrientation())
    {
        bmp = mLandscapeBmp;
        mask = mLandscapeMask;
    }
    else
    {
        bmp = mPortraitBmp;
        mask = mPortraitMask;
    }

    if (!bmp)
    {
        return;
    }

    TPoint topLeft(aRect.iTl);
    if (mType == EStartScreenAutomatic)
    {
        aGc.BitBlt(topLeft, bmp);
    }
    else
    {
        const TSize bmpSizeOrg(bmp->SizeInPixels());
        const TSize rectSize(aRect.Size());
        TSize bmpSize(bmpSizeOrg);

        // Scale the image if needed
        TBool scaled(EFalse);
        if (bmpSize.iWidth > rectSize.iWidth || bmpSize.iHeight > rectSize.iHeight)
        {
            scaled = ETrue;
            bmpSize = GetAspectRatioScaledBitmapSize(bmpSize, rectSize);
        }

        // Center the image
        if (bmpSize.iWidth < rectSize.iWidth)
        {
            topLeft.iX = (rectSize.iWidth - bmpSize.iWidth) / 2;
        }
        if (bmpSize.iHeight < rectSize.iHeight)
        {
            topLeft.iY = (rectSize.iHeight - bmpSize.iHeight) / 2;
        }

        if (scaled)
        {
            TRect destRect(topLeft, bmpSize);
            if (!mask)
            {
                aGc.DrawBitmap(destRect, bmp, TRect(bmpSizeOrg));
            }
            else
            {
                aGc.DrawBitmapMasked(destRect, bmp, TRect(bmpSizeOrg), mask, EFalse);
            }
        }
        else
        {
            // Faster drawing
            if (!mask)
            {
                aGc.BitBlt(topLeft, bmp);
            }
            else
            {
                aGc.BitBltMasked(topLeft, bmp, TRect(bmpSize), mask, EFalse);
            }
        }
    }
}

void CStartScreen::HandleResolutionChange()
{
    if (mLandscapeBmp && mPortraitBmp)
    {
        return;
    }

    if (Layout_Meta_Data::IsLandscapeOrientation())
    {
        if (!mLandscapeBmp)
        {
            TRAP_IGNORE(SyncLoadImageL(EImageLandscape));
        }
    }
    else
    {
        if (!mPortraitBmp)
        {
            TRAP_IGNORE(SyncLoadImageL(EImagePortrait));
        }
    }
}

TBool CStartScreen::IsEmpty() const
{
    if (Layout_Meta_Data::IsLandscapeOrientation())
    {
        return (mLandscapeBmp == 0);
    }
    else
    {
        return (mPortraitBmp == 0);
    }
}

TSize CStartScreen::Size() const
{
    if (Layout_Meta_Data::IsLandscapeOrientation())
    {
        if (mLandscapeBmp)
            return mLandscapeBmp->SizeInPixels();
    }
    else
    {
        if (mPortraitBmp)
            return mPortraitBmp->SizeInPixels();
    }

    return TSize(0, 0);
}

void CStartScreen::AsyncSaveScreenL(TCallBack aCallBack)
{
    // Expecting this is done once and only once.
    ASSERT(!mAsyncSaveBmp);
    ASSERT(!mEncoder);

    mCallBack = aCallBack;
    mAsyncSaveBmp = TakeScreenShotL();
    if (mAsyncSaveBmp)
    {
        if (Layout_Meta_Data::IsLandscapeOrientation())
        {
            AsyncSaveImageL(EImageLandscape, *mAsyncSaveBmp);
        }
        else
        {
            AsyncSaveImageL(EImagePortrait, *mAsyncSaveBmp);
        }
    }
}

CStartScreen::TStartScreenType CStartScreen::Type() const
{
    return mType;
}

TInt64 CStartScreen::LoadStartupTime()
{
    TInt64 res = 0;
    std::ifstream file;
    file.open(StartupTimeFileName().c_str(), std::ifstream::in);
    file >> res;
    file.close();
    return res;
}

void CStartScreen::SaveStartupTime(TInt64 aPeriod)
{
    std::ofstream file;
    file.open(StartupTimeFileName().c_str());
    file << aPeriod;
    file.close();
}

CStartScreen::CStartScreen(TStartScreenType aType,
                           const java::ui::CoreUiParams& aParams)
        : CActive(EPriorityStandard), mType(aType), mParams(aParams)
{
    CActiveScheduler::Add(this);
}

void CStartScreen::ConstructL()
{
    JELOG2(EJavaUI);

    InitPathL();
    ScanFolderL();

    if (mImageCount > 0)
    {
        if (mType == EStartScreenAutomatic || mImageCount >= 2)
        {
            if (Layout_Meta_Data::IsLandscapeOrientation())
            {
                TRAP_IGNORE(SyncLoadImageL(EImageLandscape));
            }
            else
            {
                TRAP_IGNORE(SyncLoadImageL(EImagePortrait));
            }
        }
        else
        {
            TRAP_IGNORE(SyncLoadImageL(EImageDefault));
        }
    }
}

void CStartScreen::InitPathL()
{
    HBufC* root(wstringToBuf(mParams.getImagePath()));
    if (!root || root->Length() == 0)
    {
        delete root;
        User::Leave(KErrPathNotFound);
    }
    CleanupStack::PushL(root);
    const TDesC& folder = FolderName();
    mPath.CreateL(root->Length() + folder.Length());
    mPath.Append(root->Des());
    CleanupStack::PopAndDestroy(root);
    mPath.Append(folder);
}

void CStartScreen::ScanFolderL()
{
    ASSERT(mPath.Length());

    TUint fileAttrMask = KEntryAttNormal | KEntryAttSystem | KEntryAttDir;
    CDir* dir = 0;
    RFs& fs = CCoeEnv::Static()->FsSession();
    TInt err = fs.GetDir(mPath, fileAttrMask, ESortByName, dir);

    if (KErrNone == err)
    {
        if (!dir)
        {
            err = KErrNotFound;
            ELOG1(EJavaUI, "CStartScreen::InitStateL, "
                  "RFs::GetDir failed: %d", err);
        }
        else
        {
            mImageCount = dir->Count();
        }
    }
    else
    {
        ELOG1(EJavaUI, "CStartScreen::InitStateL, "
              "RFs::GetDir failed: %d", err);
    }

    if (mType == EStartScreenAutomatic && KErrNone != err)
    {
        err = fs.MkDir(mPath);
    }

    User::LeaveIfError(err);
}

void CStartScreen::SyncLoadImageL(TImageId aId)
{
    RBuf fullPath;
    const TDesC& img = ImageName(aId);
    // Do prefixing here just to avoid dealing with another RBuf :)
    TUidName uid;
    if (mType == EStartScreenAutomatic)
        uid = java::ui::CoreUiAvkonImpl::getInstanceImpl().getMidletTUid().Name();
    fullPath.CreateL(mPath.Length() + img.Length() + uid.Length());
    fullPath.CleanupClosePushL();
    fullPath.Append(mPath);
    if (mType == EStartScreenAutomatic)
        fullPath.Append(uid);
    fullPath.Append(img);

    switch (aId)
    {
    case EImageDefault:
        ASSERT(!mPortraitBmp);
        ASSERT(!mLandscapeBmp);
        DoSyncLoadImageL(fullPath, mPortraitBmp, mPortraitMask);
        mLandscapeBmp = mPortraitBmp;
        mLandscapeMask = mPortraitMask;
        break;
    case EImagePortrait:
        ASSERT(!mPortraitBmp);
        DoSyncLoadImageL(fullPath, mPortraitBmp, mPortraitMask);
        break;
    case EImageLandscape:
        ASSERT(!mLandscapeBmp);
        DoSyncLoadImageL(fullPath, mLandscapeBmp, mLandscapeMask);
        break;
    default:
        break;
    }

    CleanupStack::PopAndDestroy(&fullPath);
}


void CStartScreen::DoSyncLoadImageL(const TDesC& aPath, CFbsBitmap*& aResBmp,
                                    CFbsBitmap*& aResMask)
{
    ASSERT(!aResBmp);
    ASSERT(!aResMask);

    if (IsActive())
    {
        Cancel();
    }

    if (mDecoder != NULL)
    {
        delete mDecoder;
        mDecoder = 0;
    }

    mDecoder = CImageDecoder::FileNewL(CCoeEnv::Static()->FsSession(), aPath);

    CFbsBitmap* bmp = new(ELeave) CFbsBitmap();
    CleanupStack::PushL(bmp);

    const TFrameInfo& frameInfo = mDecoder->FrameInfo();

    TInt err = bmp->Create(frameInfo.iOverallSizeInPixels,
                           frameInfo.iFrameDisplayMode);
    if (KErrNone != err)
    {
        CleanupStack::PopAndDestroy(bmp);
        ELOG1(EJavaUI, "CStartScreen::DoSyncLoadImageL, "
              "Bitmap creation failed: %d", err);
        User::Leave(err);
    }

    CFbsBitmap* mask = 0;

    if (!(frameInfo.iFlags & TFrameInfo::ETransparencyPossible))
    {
        mState = ESyncRead;
        mDecoder->Convert(&iStatus, *bmp);
    }
    else
    {
        mask = new(ELeave) CFbsBitmap();
        CleanupStack::PushL(mask);

        TDisplayMode mode(EGray2);
        if (frameInfo.iFlags & TFrameInfo::EAlphaChannel)
        {
            mode = EGray256;
        }

        err = mask->Create(frameInfo.iOverallSizeInPixels, mode);
        if (KErrNone != err)
        {
            CleanupStack::PopAndDestroy(mask);
            CleanupStack::PopAndDestroy(bmp);
            ELOG1(EJavaUI, "CImageReader::DoSyncLoadImageL, "
                  "Mask creation failed: %d", err);
            User::Leave(err);
        }

        mState = ESyncRead;
        mDecoder->Convert(&iStatus, *bmp, *mask);
    }

    SetActive();
    mWait.Start();

    err = iStatus.Int();
    if (KErrNone != err)
    {
        if (mask)
        {
            CleanupStack::PopAndDestroy(mask);
        }
        CleanupStack::PopAndDestroy(bmp);
        ELOG1(EJavaUI, "CImageReader::DoSyncLoadImageL, "
              "CImageDecoder::Convert failed: %d", err);
        User::Leave(err);
    }

    if (mask)
    {
        CleanupStack::Pop(mask);
    }
    CleanupStack::Pop(bmp);
    aResBmp = bmp;
    aResMask = mask;
}

void CStartScreen::AsyncSaveImageL(TImageId aId, const CFbsBitmap& aBmp)
{
    const TDesC& img = ImageName(aId);
    // Do prefixing here just to avoid dealing with another RBuf :)
    TUidName uid;
    if (mType == EStartScreenAutomatic)
        uid = java::ui::CoreUiAvkonImpl::getInstanceImpl().getMidletTUid().Name();
    mAsyncSavePath.CreateL(mPath.Length() + img.Length() + uid.Length());
    mAsyncSavePath.Append(mPath);
    if (mType == EStartScreenAutomatic)
        mAsyncSavePath.Append(uid);
    mAsyncSavePath.Append(img);

    DoAsyncSaveImageL(mAsyncSavePath, aBmp);
}

void CStartScreen::DoAsyncSaveImageL(const TDesC& aPath, const CFbsBitmap& aBmp)
{
    if (IsActive())
    {
        Cancel();
    }

    mEncoder = CImageEncoder::FileNewL(CCoeEnv::Static()->FsSession(), aPath,
                                       CImageEncoder::EOptionNone, KImageTypePNGUid);

    mState = EAsyncWrite;
    mEncoder->Convert(&iStatus, aBmp);
    SetActive();
}

TSize CStartScreen::GetAspectRatioScaledBitmapSize(
    const TSize& aSourceSize, const TSize& aMaxDestSize) const
{
    JELOG2(EJavaUI);
    TSize imageSize = aSourceSize;
    TInt yDiff = 0;
    TInt xDiff = 0;
    TBool scalingNeeded = EFalse;

    if (aSourceSize.iWidth == 0 || aSourceSize.iHeight == 0)
    {
        return imageSize;
    }

    // Figure out if any scaling is needed
    if (aSourceSize.iHeight > aMaxDestSize.iHeight)
    {
        yDiff = aSourceSize.iHeight - aMaxDestSize.iHeight;
        scalingNeeded = ETrue;
    }
    if (aSourceSize.iWidth > aMaxDestSize.iWidth)
    {
        xDiff = aSourceSize.iWidth - aMaxDestSize.iWidth;
        scalingNeeded = ETrue;
    }

    if (scalingNeeded)
    {
        if (xDiff > yDiff)
        {
            imageSize.iWidth = aMaxDestSize.iWidth;
            imageSize.iHeight = (aSourceSize.iHeight * aMaxDestSize.iWidth)
                                / aSourceSize.iWidth;
        }
        else if (yDiff > xDiff)
        {
            imageSize.iHeight = aMaxDestSize.iHeight;
            imageSize.iWidth = (aSourceSize.iWidth * aMaxDestSize.iHeight)
                               / aSourceSize.iHeight;
        }
        else
        {
            // Aspect ratios are the same
            imageSize = aMaxDestSize;
        }
    }

    return imageSize;
}

CFbsBitmap* CStartScreen::TakeScreenShotL()
{
    CEikonEnv* env = CEikonEnv::Static();
    CFbsBitmap* bmp = new(ELeave) CFbsBitmap();
    CleanupStack::PushL(bmp);
    CWsScreenDevice* screenDevice = env->ScreenDevice();
    TInt err = bmp->Create(screenDevice->SizeInPixels(), screenDevice->DisplayMode());
    if (KErrNone != err)
    {
        ELOG1(EJavaUI, "CStartScreen::TakeScreenShotL, "
              "CFbsBitmap::Create failed: %d", err);
    }
    err = screenDevice->CopyScreenToBitmap(bmp);
    if (KErrNone != err)
    {
        ELOG1(EJavaUI, "CStartScreen::TakeScreenShotL, "
              "CWsScreenDevice::CopyScreenToBitmap failed: %d", err);
    }
    User::LeaveIfError(err);
    CleanupStack::Pop(bmp);
    return bmp;
}

const TDesC& CStartScreen::FolderName() const
{
    if (mParams.getScreenMode() == java::ui::USER_DEFINED_SCREEN)
    {
        return KNullDesC;
    }
    if (mType == EStartScreenAutomatic)
    {
        return KAutoFolder;
    }
    else
    {
        return KUserFolder;
    }
}

const TDesC& CStartScreen::ImageName(CStartScreen::TImageId aId) const
{
    if (mParams.getScreenMode() == java::ui::USER_DEFINED_SCREEN)
    {
        return KNullDesC;
    }
    if (mType == EStartScreenAutomatic)
    {
        switch (aId)
        {
        case EImagePortrait:
            return KAutoPortraitImage;
        case EImageLandscape:
            return KAutoLandscapeImage;
        default:
            return KNullDesC;
        }
    }
    else
    {
        switch (aId)
        {
        case EImagePortrait:
            return KUserPortraitImage;
        case EImageLandscape:
            return KUserLandscapeImage;
        case EImageDefault:
        default:
            return KUserDefaultImage;
        }
    }
}

std::string CStartScreen::StartupTimeFileName() const
{
    std::wstring temp(mParams.getImagePath());
    temp.append(L"autostartupimages\\");
    temp.append(java::ui::CoreUiAvkonImpl::getInstanceImpl().getMidletUid().toString());
    temp.append(L"time.txt");
    // This is ok as we know for sure we have only ascii in the path.
    std::string path(temp.begin(), temp.end());
    return path;
}

void CStartScreen::RunL()
{
    if (mState == ESyncRead)
    {
        if (mWait.IsStarted())
        {
            mWait.AsyncStop();
        }

        // Release the lock on the file.
        ASSERT(mDecoder);
        delete mDecoder;
        mDecoder = NULL;

        // Reset state
        mState = EIdle;
    }
    else if (mState == EAsyncWrite)
    {
        // Release the lock on the file.
        ASSERT(mEncoder);
        delete mEncoder;
        mEncoder = 0;

        // Reset state
        mState = EIdle;

        if (iStatus == KErrNone)
        {
            // The callback will delete the object in the current setup.
            mCallBack.CallBack();
        }
        else
        {
            TInt err = iStatus.Int();
            ELOG1(EJavaUI, "CStartScreen::RunL, "
                  "CImageEncoder::Convert failed: %d", err);
        }
    }
}

void CStartScreen::DoCancel()
{
    if (mState == ESyncRead)
    {
        if (mDecoder)
        {
            mDecoder->Cancel();
        }
        if (mWait.IsStarted())
        {
            mWait.AsyncStop();
        }
    }
    else if (mState == EAsyncWrite)
    {
        if (mEncoder)
        {
            mEncoder->Cancel();
        }
    }

    mState = EIdle;
}