camerauis/cameraxui/cxui/src/cxuiapplicationframeworkmonitorprivate.cpp
branchRCL_3
changeset 53 61bc0f252b2b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/camerauis/cameraxui/cxui/src/cxuiapplicationframeworkmonitorprivate.cpp	Tue Aug 31 15:03:46 2010 +0300
@@ -0,0 +1,495 @@
+/*
+* 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:
+*
+*/
+
+#include <QObject>
+
+#ifdef Q_OS_SYMBIAN
+#include <e32cmn.h>
+#include <w32std.h>
+#include <apgwgnam.h>
+#include <apgtask.h>
+#include <eikenv.h>
+#include <avkondomainpskeys.h>  // keyguard state
+#include <hwrmpowerstatesdkpskeys.h> // battery status
+#include <UsbWatcherInternalPSKeys.h> // usb status
+#include <usbman.h>
+#include <usbpersonalityids.h>
+
+#include <QMetaEnum>
+#include <QString>
+#include <QVariant>
+#include <QTimer>
+#include <qsymbianevent.h>
+
+#endif // Q_OS_SYMBIAN
+
+#include "cxutils.h"
+#include "cxuieventlog.h"
+#include "cxuiapplication.h"
+#include "cxesettings.h"
+#include "cxuiapplicationframeworkmonitorprivate.h"
+
+namespace{
+    const int CXUI_USB_MODE_CHECK_TIMER_DELAY = 1000; // 1 second
+}
+
+#ifdef Q_OS_SYMBIAN
+namespace
+{
+    inline QString convertTDesC2QString(const TDesC& aDescriptor)
+    {
+        #ifdef QT_NO_UNICODE
+            return QString::fromLocal8Bit(aDescriptor.Ptr(), aDescriptor.Length());
+        #else
+            return QString::fromUtf16(aDescriptor.Ptr(), aDescriptor.Length());
+        #endif
+    }
+
+    inline QString windowGroupName(RWsSession& ws, int id)
+    {
+        TBuf<CApaWindowGroupName::EMaxLength> name;
+        ws.GetWindowGroupNameFromIdentifier(id, name);
+        // Window group name contains "null" characters,
+        // which are considered end-of-string if not replaced.
+        for (int i=0; i < name.Length(); i++) {
+            if (name[i] == NULL) {
+                name[i] = ' ';
+            }
+        }
+        return convertTDesC2QString(name);
+    }
+
+    inline QString bitString(int number, char fill = '0', int width = 32)
+    {
+        return QString::number(number, 2).rightJustified(width, fill);
+    }
+
+    //!@todo: Avkon UIDs not needed once device dialogs fully implemented in Orbit.
+
+    // AknCapServer
+    static const unsigned int UID_AKNCAPSERVER    = 0x10207218;
+
+    // Phone ui
+    static const unsigned int UID_PHONEUI         = 0x100058B3;
+    // Task switcher
+    static const unsigned int UID_TASKSWITCHER    = 0x2002677D;
+    // Dialog server
+    static const unsigned int UID_DIALOGAPPSERVER = 0x20022FC5;
+
+    // Log event types
+    static const char *EVENT_USB        = "usb";
+    static const char *EVENT_FOREGROUND = "foreground";
+}
+#endif // Q_OS_SYMBIAN
+
+
+/*!
+* Constructor
+*/
+CxuiApplicationFrameworkMonitorPrivate::CxuiApplicationFrameworkMonitorPrivate(CxuiApplicationFrameworkMonitor *parent,
+                                                                               CxuiApplication &application,
+                                                                               CxeSettings& settings)
+    :  q(parent),
+       mApplication(application),
+       mSettings(settings),
+#ifdef Q_OS_SYMBIAN
+       mWsSession(CCoeEnv::Static()->WsSession()),
+       mWindowGroup(CCoeEnv::Static()->RootWin()),
+       mWindowGroupId(mWindowGroup.Identifier()),
+       mWindowGroupName(),
+       mKeyLockState(EKeyguardNotActive),
+       mBatteryStatus(EBatteryStatusUnknown),
+       mUsbPersonality(0),
+       mUsbModeCheckTimer(this),
+       mEventLog(NULL),
+#endif // Q_OS_SYMBIAN
+       mState(CxuiApplicationFrameworkMonitor::ForegroundOwned)
+{
+    CX_DEBUG_ENTER_FUNCTION();
+#ifdef Q_OS_SYMBIAN
+    mWindowGroup.EnableFocusChangeEvents();
+    mWindowGroupName = windowGroupName(mWsSession, mWindowGroupId);
+    mEventLog = new CxuiEventLog("CxuiApplicationFrameworkMonitorPrivate");
+    init();
+    mUsbModeCheckTimer.setSingleShot(true);
+    mUsbModeCheckTimer.setInterval(CXUI_USB_MODE_CHECK_TIMER_DELAY);
+    connect(&mUsbModeCheckTimer, SIGNAL(timeout()),
+            this, SLOT(usbModeCheckTimerCallback()));
+#endif // Q_OS_SYMBIAN
+    CX_DEBUG_EXIT_FUNCTION();
+}
+
+/*!
+* Destructor
+*/
+CxuiApplicationFrameworkMonitorPrivate::~CxuiApplicationFrameworkMonitorPrivate()
+{
+    CX_DEBUG_ENTER_FUNCTION();
+#ifdef Q_OS_SYMBIAN
+    delete mEventLog;
+#endif // Q_OS_SYMBIAN
+    CX_DEBUG_EXIT_FUNCTION();
+}
+
+/*!
+* Current foreground owning state of this application.
+* @return Foreground owning state.
+*/
+CxuiApplicationFrameworkMonitor::ForegroundState CxuiApplicationFrameworkMonitorPrivate::foregroundState() const
+{
+    return mState;
+}
+
+/*!
+* Is USB connected in mass memory mode?
+* @return True if USB mass memory mode is active and connected, false otherwise.
+*/
+bool CxuiApplicationFrameworkMonitorPrivate::isUsbMassMemoryModeActive() const
+{
+    bool active(false);
+#ifdef Q_OS_SYMBIAN
+    // Mass memory mode activity can be seen from the KUsbWatcherSelectedPersonality property.
+    // When USB is connected in Mass Memory Mode, we get KUsbPersonalityIdMS as personality id.
+    // If USB is not connected, personality id is KUsbWatcherSelectedPersonalityNone.
+    active = (mUsbPersonality == KUsbPersonalityIdMS);
+#endif // Q_OS_SYMBIAN
+    return active;
+}
+
+
+
+#ifdef Q_OS_SYMBIAN
+/*!
+* Slot to handle Symbian event.
+* @param event Symbian event to be handled. (Ownership not taken.)
+*/
+void CxuiApplicationFrameworkMonitorPrivate::handleEvent(const QSymbianEvent *event)
+{
+    // We receive tons of these events, so function start and end traces
+    // are intentionally left out.
+
+    if (event) {
+        switch (event->type()) {
+        case QSymbianEvent::WindowServerEvent:
+            handleWindowServerEvent(event);
+            break;
+        }
+    }
+}
+
+/*!
+* Handle changes in RProperty values of keylock state and battery status.
+* @param uid Category uid of the changed property.
+* @param key Integer key of the changed property.
+* @param value New value of the changed property.
+*/
+void CxuiApplicationFrameworkMonitorPrivate::handlePropertyEvent(long int uid, unsigned long int key, QVariant value)
+{
+    CX_DEBUG_ENTER_FUNCTION();
+
+    if (uid == KPSUidAvkonDomain.iUid && key == KAknKeyguardStatus) {
+        CX_DEBUG(("CxuiApplicationFrameworkMonitor - keylock status changed: %d -> %d", mKeyLockState, value.toInt()));
+
+        // Check if the keylock value has actually changed
+        const int newKeyLockState = value.toInt();
+        if (newKeyLockState != mKeyLockState) {
+            mKeyLockState = newKeyLockState;
+            // Set foreground state based on keylock status and focused application info.
+            setState(getCurrentState());
+        }
+    } else if (uid == KPSUidHWRMPowerState.iUid && key == KHWRMBatteryStatus ) {
+        CX_DEBUG(("CxuiApplicationFrameworkMonitor - battery status changed: %d -> %d", mBatteryStatus, value.toInt() ));
+
+        // If status changed, check if battery is going empty.
+        const int newBatteryStatus = value.toInt();
+        if (newBatteryStatus != mBatteryStatus) {
+            mBatteryStatus = newBatteryStatus;
+
+            // Notify that battery is almost empty,
+            // need to stop any recordings etc.
+            if(mBatteryStatus == EBatteryStatusEmpty) {
+                emit q->batteryEmpty();
+            }
+        }
+    } else if (uid == KPSUidUsbWatcher.iUid && key == KUsbWatcherSelectedPersonality) {
+        CX_DEBUG(("CxuiApplicationFrameworkMonitor - usb personality changed: %d -> %d", mUsbPersonality, value.toInt()));
+
+        const int newUsbPersonality(value.toInt());
+        if (newUsbPersonality != mUsbPersonality) {
+            // Check before saving the new state if mass memory mode was active,
+            // so we know when to emit the unactivated signal.
+            const bool wasUsbMassMemoryModeActive(isUsbMassMemoryModeActive());
+            // Store new state.
+            mUsbPersonality = newUsbPersonality;
+
+            // Save state to log.
+            if (mEventLog) {
+                mEventLog->append(EVENT_USB, QString::number(mUsbPersonality));
+            }
+
+            // Check if mass memory mode activity changed.
+            if (wasUsbMassMemoryModeActive != isUsbMassMemoryModeActive()) {
+
+                // If the massmemory mode switched from on to off,
+                // the signal is emitted immediately.
+                // If the switch is from off to on, we need to use a timer
+                // as a workaround because  plugging in the USB charger
+                // sends a mass memory mode change event.
+                if (wasUsbMassMemoryModeActive) {
+                    emit q->usbMassMemoryModeToggled(isUsbMassMemoryModeActive());
+                } else {
+                    // (Re)starting the timer
+                    mUsbModeCheckTimer.stop();
+                    mUsbModeCheckTimer.start();
+                }
+
+            }
+        }
+    }
+
+    CX_DEBUG_EXIT_FUNCTION();
+}
+
+/*!
+*  Callback function for the timer used to seperate USB charging
+*  from USB mass memory mode
+*/
+void CxuiApplicationFrameworkMonitorPrivate::usbModeCheckTimerCallback()
+{
+    CX_DEBUG_ENTER_FUNCTION();
+
+    // if the device is still in mass memory mode after the timer has finished,
+    // the device really is in massmemory mode and not plugged into the charger
+    if (isUsbMassMemoryModeActive()){
+        emit q->usbMassMemoryModeToggled(isUsbMassMemoryModeActive());
+    }
+
+    CX_DEBUG_EXIT_FUNCTION();
+}
+
+/*!
+* Set initial values.
+*/
+void CxuiApplicationFrameworkMonitorPrivate::init()
+{
+    CX_DEBUG_ENTER_FUNCTION();
+
+    // Connect to application (window server) events.
+    connect(&mApplication, SIGNAL(symbianEvent(const QSymbianEvent *)), this, SLOT(handleEvent(const QSymbianEvent *)));
+
+    QVariant value;
+
+    // Get initial battery status.
+    mSettings.get(KPSUidHWRMPowerState.iUid, KHWRMBatteryStatus, Cxe::PublishAndSubscribe, value);
+    mBatteryStatus = value.toInt();
+
+    // Get initial keylock status.
+    mSettings.get(KPSUidAvkonDomain.iUid, KAknKeyguardStatus, Cxe::PublishAndSubscribe, value);
+    mKeyLockState = value.toInt();
+
+    // Get current USB personality
+    mSettings.get(KPSUidUsbWatcher.iUid, KUsbWatcherSelectedPersonality, Cxe::PublishAndSubscribe, value);
+    mUsbPersonality = value.toInt();
+
+    bool ok = connect(&mSettings, SIGNAL(settingValueChanged(long int, unsigned long int, QVariant)),
+                      this, SLOT(handlePropertyEvent(long int, unsigned long int, QVariant)));
+    CX_DEBUG_ASSERT(ok);
+
+    // Get foreground state. Depends on keyguard status, so that needs to be read first.
+    mState = getCurrentState();
+
+    CX_DEBUG_EXIT_FUNCTION();
+}
+
+/*!
+* Helper method to handle Symbian event that specificly is of type QSymbianEvent::WindowServerEvent.
+* @param event Symbian event to be handled. (Ownership not taken.)
+*/
+void CxuiApplicationFrameworkMonitorPrivate::handleWindowServerEvent(const QSymbianEvent *event)
+    {
+    // We receive tons of these events, so function start and end traces
+    // are intentionally left out.
+
+    const TWsEvent *wsEvent = event->windowServerEvent();
+    if (wsEvent) {
+        switch (wsEvent->Type()) {
+        case EEventFocusGroupChanged: {
+            CX_DEBUG(("CxuiApplicationFrameworkMonitor - EEventFocusGroupChanged event"));
+            setState(getCurrentState());
+            break;
+        }
+        case EEventFocusGained: {
+            CX_DEBUG(("CxuiApplicationFrameworkMonitor - EEventFocusGained event"));
+            setState(getCurrentState());
+            break;
+        }
+        case EEventFocusLost: {
+            CX_DEBUG(("CxuiApplicationFrameworkMonitor - EEventFocusLost event"));
+            setState(getCurrentState());
+            break;
+        }
+        case EEventWindowVisibilityChanged: {
+            const TWsVisibilityChangedEvent *visibilityEvent = wsEvent->VisibilityChanged();
+            if (visibilityEvent) {
+                CX_DEBUG(("CxuiApplicationFrameworkMonitor - EFullyVisible: bits[%s]",
+                    bitString(TWsVisibilityChangedEvent::EFullyVisible).toAscii().constData() ));
+                CX_DEBUG(("CxuiApplicationFrameworkMonitor - EPartiallyVisible: bits[%s]",
+                    bitString(TWsVisibilityChangedEvent::EPartiallyVisible).toAscii().constData() ));
+                CX_DEBUG(("CxuiApplicationFrameworkMonitor - ENotVisible: bits[%s]",
+                    bitString(TWsVisibilityChangedEvent::ENotVisible).toAscii().constData() ));
+                CX_DEBUG(("CxuiApplicationFrameworkMonitor - event:       bits[%s]",
+                    bitString(visibilityEvent->iFlags).toAscii().constData() ));
+            }
+            break;
+        }
+        default:
+            break;
+        }
+    }
+}
+
+/*!
+* Set state and emit signal if state really changes.
+* @param state New state.
+*/
+void CxuiApplicationFrameworkMonitorPrivate::setState(CxuiApplicationFrameworkMonitor::ForegroundState state)
+{
+    if (mState != state) {
+        const CxuiApplicationFrameworkMonitor::ForegroundState original(mState);
+
+        // Check if state transition is acceptable in current state.
+        switch (mState) {
+        case CxuiApplicationFrameworkMonitor::ForegroundOwned:
+        case CxuiApplicationFrameworkMonitor::ForegroundPartiallyLost:
+            // All changes accepted.
+            mState = state;
+            break;
+        case CxuiApplicationFrameworkMonitor::ForegroundFullyLost:
+            // If foreground application is changed to note when we are already
+            // fully in background, cannot accept state change to "partial foreground".
+            if (state != CxuiApplicationFrameworkMonitor::ForegroundPartiallyLost) {
+                mState = state;
+            } else {
+                CX_DEBUG(("CxuiApplicationFrameworkMonitor - state change full bg -> partial bg ignored"));
+            }
+        }
+
+        if (mState != original) {
+            // Print the event log with this foreground event included.
+            if (mEventLog) {
+                mEventLog->append(
+                    EVENT_FOREGROUND,
+                    CxuiApplicationFrameworkMonitor::staticMetaObject.enumerator(
+                        CxuiApplicationFrameworkMonitor::staticMetaObject.indexOfEnumerator("ForegroundState")).valueToKey(mState));
+                mEventLog->print();
+            }
+
+            // If state was changed, signal it to listeners.
+            emit q->foregroundStateChanged(mState);
+        }
+    }
+}
+
+/*!
+* Get the current foreground state.
+* @return Current state for foreground ownership.
+*/
+CxuiApplicationFrameworkMonitor::ForegroundState CxuiApplicationFrameworkMonitorPrivate::getCurrentState()
+{
+    CX_DEBUG_ENTER_FUNCTION();
+
+    CxuiApplicationFrameworkMonitor::ForegroundState state(CxuiApplicationFrameworkMonitor::ForegroundOwned);
+    int focusWindowGroupId(mWsSession.GetFocusWindowGroup());
+
+    if (mKeyLockState != EKeyguardNotActive) {
+        // Keylock enabled is the same as if other application is in foreground.
+        CX_DEBUG(("CxuiApplicationFrameworkMonitor - key lock on"));
+        state = CxuiApplicationFrameworkMonitor::ForegroundFullyLost;
+    } else if (focusWindowGroupId == mWindowGroupId) {
+        // If our window group has focus, we clearly are the foreground owning application.
+        CX_DEBUG(("CxuiApplicationFrameworkMonitor - Foreground window group matches ours."));
+        state = CxuiApplicationFrameworkMonitor::ForegroundOwned;
+
+    } else {
+        // Need to check if foreground is owned by known apps.
+        unsigned int uid(focusedApplicationUid());
+
+        // Check the app uid.
+        switch (uid) {
+        case UID_AKNCAPSERVER:
+        case UID_TASKSWITCHER:
+        case UID_DIALOGAPPSERVER:
+            // Note or task switcher in foreground.
+            state = CxuiApplicationFrameworkMonitor::ForegroundPartiallyLost;
+            break;
+        case UID_PHONEUI:
+        default:
+            // Foreground owned by other app.
+            state = CxuiApplicationFrameworkMonitor::ForegroundFullyLost;
+            break;
+        }
+    }
+
+    CX_DEBUG_EXIT_FUNCTION();
+    return state;
+}
+
+/*!
+* Get the uid of application in foreground.
+* @return Application uid for the foreground application.
+*/
+unsigned int CxuiApplicationFrameworkMonitorPrivate::focusedApplicationUid()
+{
+    unsigned int uid(0);
+    int focusWgId(mWsSession.GetFocusWindowGroup());
+
+    TRAP_IGNORE({
+        CApaWindowGroupName* wgn = CApaWindowGroupName::NewLC(mWsSession, focusWgId);
+        uid = wgn->AppUid().iUid;
+        CleanupStack::PopAndDestroy(wgn);
+    });
+
+    // If the window group identifier does not have the application uid set,
+    // get it via thread secure id.
+    if (uid == 0) {
+        TApaTask task(mWsSession);
+        task.SetWgId(focusWgId);
+
+        RThread t;
+        int err = t.Open(task.ThreadId());
+        if (err == KErrNone) {
+            uid = t.SecureId().iId;
+            CX_DEBUG(("CxuiApplicationFrameworkMonitor - uid resolved from thread"));
+        }
+        t.Close();
+    }
+
+#ifdef CX_DEBUG
+    QString name(windowGroupName(mWsSession, focusWgId));
+    CX_DEBUG(("CxuiApplicationFrameworkMonitor - Own window group id:     0x%08x", mWindowGroupId));
+    CX_DEBUG(("CxuiApplicationFrameworkMonitor - Focused window group id: 0x%08x", focusWgId));
+    CX_DEBUG(("CxuiApplicationFrameworkMonitor - Own window group name:        [%s]", mWindowGroupName.toAscii().constData()));
+    CX_DEBUG(("CxuiApplicationFrameworkMonitor - Focused window group name:    [%s]", name.toAscii().constData()));
+    CX_DEBUG(("CxuiApplicationFrameworkMonitor - Focused application uid: 0x%08x", uid));
+#endif
+
+    return uid;
+}
+
+#endif // Q_OS_SYMBIAN
+
+// end of file