dlnasrv_exampleapp/src/exampleappui.cpp
author Sampo Huttunen <sampo.huttunen@nokia.com>
Wed, 03 Nov 2010 12:18:00 +0200
branchIOP_Improvements
changeset 41 b4d83ea1d6e2
parent 40 08b5eae9f9ff
child 43 9b689d9d3a1c
permissions -rw-r--r--
Added comments to the example application

/*
* Copyright (c) 2010 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 <qlabel.h>
#include <qboxlayout.h>
#include <qgroupbox.h>
#include <qpushbutton.h>
#include <qcombobox.h>
#include <qlayoutitem.h>
#include <qfiledialog.h>
#include <qaction.h>

#include "exampleappui.h"
#include "exampleappengine.h"
#include "trace.h"

/*!
    \class ExampleAppUi
    \brief Implements the application UI for Dlnasrv example application.

    The application provides a simple UI for selecting a renderer device
    (from a connected WLAN AP, if none is connected that needs to be done
    outside the application UI) and selecting a file to be rendered and
    control buttons for controlling the playback on the renderer device.

    So the first step is to have a WLAN connection active. Only then the
    renderer search and selection can be done.

    The next step is to select a rendering device. When rendering device
    search is initiated, the underlying UPnP AV control server is queried
    for existing renderers. Also all new renderers appearing in the AP
    get signalled with renderingDeviceFound()

    When a renderer is selected, the next step is to select a file to be
    pushed to the renderer. When selecting a file, it gets published in
    the push server as a DLNA item. When that is done, the controls for
    playback are enabled and the renderer can be controlled with those.
*/

/*!
    ExampleAppUi constructor.
 */
ExampleAppUi::ExampleAppUi():
    mEngine(0),
    mSelectedRenderer(0),
    mSelectedFileLabel(0),
    mPlaybackStatus(0),
    mRenderingDevices(0),
    mPauseButton(0),
    mStopButton(0),
    mRewButton(0),
    mFfButton(0),
    mSelectFileButton(0),
    mSearchDevicesButton(0),
    mSelectedFile("")
{
    FUNC_LOG
}

/*!
    ExampleAppUi destructor.
*/
ExampleAppUi::~ExampleAppUi()
{
    FUNC_LOG

    delete mEngine;
}

/*!
    Creates application engine and the UI. Also the state of the WLAN AP
    connection gets queried and updated on the UI.
 */
void ExampleAppUi::construct()
{
    FUNC_LOG

    createEngine();
    createUi();
}

/*!
    Creates the UI components.
 */
void ExampleAppUi::createUi()
{
    FUNC_LOG

    // create main view
    QWidget *view = new QWidget;
    setCentralWidget(view);
    
    // create main view layout
    QVBoxLayout *mainLayout = new QVBoxLayout;
    view->setLayout(mainLayout);
    
    // create accesspoint selection
    QLayout *layout = createApSelectionUi();
    mainLayout->addLayout(layout);
    mainLayout->addSpacing(40);
    
    // create renderer selection
    layout = createRendererSelectionUi();
    mainLayout->addLayout(layout);
    mainLayout->addSpacing(40);
    
    // create file selection
    layout = createFileSelectionUi();
    mainLayout->addLayout(layout);
    mainLayout->addSpacing(40);
    
    // create playback ui
    layout = createPlaybackUi();
    mainLayout->addLayout(layout);
    mainLayout->addSpacing(40);
}

/*!
    Creates the UI for showing and selecting WLAN AP connection status.
 */
QLayout *ExampleAppUi::createApSelectionUi()
{
    FUNC_LOG

    QVBoxLayout *layout = new QVBoxLayout;
    QHBoxLayout *topLayout = new QHBoxLayout;
    QLabel *titleLabel = new QLabel(tr("Access point:"));
    QLabel *statusLabel = new QLabel(tr("Not connected"));    

    // connect signals
    connect(mEngine, SIGNAL(iapUpdated(const QString &)), statusLabel, SLOT(setText(const QString &)));
    
    // add widgets to top layout
    topLayout->addWidget(titleLabel);
    topLayout->addWidget(statusLabel);
    
    // add sub layouts to main layout
    layout->addLayout(topLayout);
    
    // update iap
    QString iapName = mEngine->getIapName();
    if (iapName.length())
    {
        statusLabel->setText(iapName);
    }
    
    return layout;
}

/*!
    Creates the UI for showing and selecting renderer device.
 */
QLayout *ExampleAppUi::createRendererSelectionUi()
{
    FUNC_LOG

    QVBoxLayout *layout = new QVBoxLayout;
    QHBoxLayout *topLayout = new QHBoxLayout;
    QVBoxLayout *bottomLayout = new QVBoxLayout;
    QLabel *titleLabel = new QLabel(tr("Rendering device:"));
    QLabel *statusLabel = new QLabel(tr("Not connected"));
    QPushButton *searchButton = new QPushButton(tr("Search for new devices"));
    QComboBox *selectionComboBox = new QComboBox;
    
    // connect signals
    connect(searchButton, SIGNAL(clicked()), mEngine, SLOT(searchRenderingDevices()));
    connect(selectionComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(selectRenderingDevice(int)));
    connect(mEngine, SIGNAL(iapUpdated(int)), this, SLOT(enableRenderingDeviceSelection(int)));
    
    // add widgets to top layout
    topLayout->addWidget(titleLabel);
    topLayout->addWidget(statusLabel);
    
    // add widgets to bottom layout
    bottomLayout->addWidget(searchButton);
    bottomLayout->addWidget(selectionComboBox);
    
    // add sub layouts to main layout
    layout->addLayout(topLayout);
    layout->addLayout(bottomLayout);
    
    // store rendering devices
    mRenderingDevices = selectionComboBox;
    mSelectedRenderer = statusLabel;
    mSearchDevicesButton = searchButton;
    
    // disable search button if iap has not been defined
    if (!mEngine->getIap())
    {
        disableRenderingDeviceSelection();
    }
    
    return layout;
}

/*!
    Creates the UI for showing and selecting media file to be rendered.
 */
QLayout *ExampleAppUi::createFileSelectionUi()
{
    FUNC_LOG

    QVBoxLayout *layout = new QVBoxLayout;
    QHBoxLayout *topLayout = new QHBoxLayout;
    QVBoxLayout *bottomLayout = new QVBoxLayout;    
    QLabel *titleLabel = new QLabel(tr("Selected file:"));
    QLabel *statusLabel = new QLabel(tr("None"));
    QPushButton *selectButton = new QPushButton(tr("Select media file"));
    
    // connect signals
    connect(selectButton, SIGNAL(clicked()), this, SLOT(selectFile()));

    // add widgets to top layout
    topLayout->addWidget(titleLabel);
    topLayout->addWidget(statusLabel);
    
    // add widgets to bottom layout
    bottomLayout->addWidget(selectButton);
    
    // add sub layouts to main layout
    layout->addLayout(topLayout);
    layout->addLayout(bottomLayout);
    
    // store status label and select file button
    mSelectedFileLabel = statusLabel;
    mSelectFileButton = selectButton;
    
    // disable select file button by default
    disableFileSelection();

    return layout;
}

/*!
    Creates the push buttons for controlling the playback.
 */
QLayout *ExampleAppUi::createPlaybackUi()
{
    FUNC_LOG

    QVBoxLayout *layout = new QVBoxLayout;
    QHBoxLayout *topLayout = new QHBoxLayout;
    QHBoxLayout *bottomLayout = new QHBoxLayout;
    QLabel *titleLabel = new QLabel(tr("Playback status:"));
    QLabel *statusLabel = new QLabel(tr("Stopped"));
    QPushButton *rewButton = new QPushButton(tr("Rew"));    
    QPushButton *playButton = new QPushButton(tr("Play"));    
    QPushButton *pauseButton = new QPushButton(tr("Pause"));    
    QPushButton *stopButton = new QPushButton(tr("Stop"));    
    QPushButton *ffButton = new QPushButton(tr("Ff"));    
    
    // connect signals
    connect(rewButton, SIGNAL(clicked()), mEngine, SLOT(rew()));
    connect(playButton, SIGNAL(clicked()), mEngine, SLOT(play()));
    connect(pauseButton, SIGNAL(clicked()), mEngine, SLOT(pause()));
    connect(stopButton, SIGNAL(clicked()), mEngine, SLOT(stop()));
    connect(ffButton, SIGNAL(clicked()), mEngine, SLOT(ff()));

    // add widgets to top layout
    topLayout->addWidget(titleLabel);
    topLayout->addWidget(statusLabel);
    
    // add widgets to bottom layout
    bottomLayout->addWidget(rewButton);
    bottomLayout->addWidget(playButton);
    bottomLayout->addWidget(pauseButton);
    bottomLayout->addWidget(stopButton);
    bottomLayout->addWidget(ffButton);
    
    // add sub layouts to main layout
    layout->addLayout(topLayout);
    layout->addLayout(bottomLayout);
    
    // store status label
    mPlaybackStatus = statusLabel;
    mPlayButton = playButton;
    mPauseButton = pauseButton;
    mStopButton = stopButton;
    mRewButton = rewButton;
    mFfButton = ffButton;
    
    // disable all buttons by default
    disablePlayback();

    return layout;
}

/*!
    Creates the application engine and resolves the current connection status.
 */
void ExampleAppUi::createEngine()
{
    FUNC_LOG

    mEngine = new ExampleAppEngine;
    mEngine->construct();
    
    // connect signals
    connect(mEngine, SIGNAL(stateChanged(int)), this, SLOT(updateState(int)));
    connect(mEngine, SIGNAL(renderingDeviceSearchStarted()), this, SLOT(deviceSearchStarted()));
    connect(mEngine, SIGNAL(renderingDeviceFound(const QString &, const QString &)), this, SLOT(addRenderingDevice(const QString &, const QString &)));
    connect(mEngine, SIGNAL(renderingDeviceDisappeared(const QString &, const QString &)), this, SLOT(removeRenderingDevice(const QString &, const QString &)));
    connect(mEngine, SIGNAL(initComplete(int)), this, SLOT(enablePlayback(int)));
}

/*!
    This function gets called by the engine, when the renderer changes its state.
 */
void ExampleAppUi::updateState(int newState)
{
    FUNC_LOG

    switch (newState)
    {
        case ExampleAppEngine::PlaybackStateBuffering:
        {
            mPlaybackStatus->setText(tr("Buffering"));
            break;
        }
        case ExampleAppEngine::PlaybackStatePlaying:
        {
            mPlaybackStatus->setText(tr("Playing"));
            break;
        }
        case ExampleAppEngine::PlaybackStatePaused:
        {
            mPlaybackStatus->setText(tr("Paused"));
            break;
        }
        case ExampleAppEngine::PlaybackStateStopped:
            // fall through
        default:
        {
            mPlaybackStatus->setText(tr("Stopped"));
            break;
        }
    }
    
    // update playback controls
    enablePlayback();
}

/*!
    Launches QFile dialog for selecting a media file from the local
    file system. Then the file is sent to the engine for sharing, i.e.
    it will get resolved as an URI on the UPnP push server.
 */
void ExampleAppUi::selectFile()
{
    FUNC_LOG
    
    mSelectedFile = QFileDialog::getOpenFileName(this,
        tr("Open file"),
        tr("e:\\"),
        tr("Media Files (*.mp3 *.jpg *.mp4)"));
    if (mSelectedFile.length())
    {
        // convert all '/' to '\' since symbian does not understand '/' 
        // characters in the file path
        mSelectedFile.replace("/", "\\");
        
        // update text to ui
        QFileInfo fileInfo(mSelectedFile);
        // use only 20 characters to fit text on screen 
        // since otherwise qt will panic with bad alloc
        mSelectedFileLabel->setText(fileInfo.fileName().left(20));
       
        // resolve and init file for rendering
        int err = mEngine->initFile(mSelectedFile);
        if (err != 0)
        {
            ERROR_1(err, "Failed to init file %s", mSelectedFile.utf16());
            
            mSelectedFile = "";
            
            mSelectedFileLabel->setText(tr("Could not init"));

            disablePlayback();
        }
    }
    else
    {
        mSelectedFileLabel->setText(tr("Not selected"));

        disablePlayback();
    }
}

/*!
    Shows searching string on UI during search if no renderers are found yet.
*/
void ExampleAppUi::deviceSearchStarted()
{
    FUNC_LOG
    
    if (!mRenderingDevices->count())
    {
        mSelectedRenderer->setText(tr("Searching"));
    }
}

/*!
    Adds a renderer to the UI's model.
 */
void ExampleAppUi::addRenderingDevice(const QString &name, const QString &uuid)
{
    FUNC_LOG
    
    mRenderingDevices->addItem(name, QVariant(uuid));
}

/*!
    Removes a renderer from the UI's model.
 */
void ExampleAppUi::removeRenderingDevice(const QString &/*name*/, const QString &uuid)
{
    FUNC_LOG

    int count(mRenderingDevices->count());
    for (int i = 0; i < count; i++)
    {
        QVariant userData(mRenderingDevices->itemData(i));
        if (userData.toString() == uuid)
        {
            mRenderingDevices->removeItem(i);
            break;
        }
    }
}

/*!
    Set the renderer selected from the UI as the current renderer on the engine side.
*/
void ExampleAppUi::selectRenderingDevice(int index)
{
    FUNC_LOG
    
    // use only 16 characters to fit on screen otherwise qt will panic with bad alloc
    mSelectedRenderer->setText(mRenderingDevices->itemText(index).left(16));
    
    QVariant userData(mRenderingDevices->itemData(index));
    mEngine->prepareRenderingDevice(userData.toString());
    
    enablePlayback();
    enableFileSelection();
}

/*!
    Enables/disables the pushbuttons for renderer control according to the
    current state.
*/
void ExampleAppUi::enablePlayback(int result)
{
    FUNC_LOG
    
    if (result == 0 &&
        mSelectedFile.length())
    {
        switch (mEngine->getPlaybackState())
        {
            case ExampleAppEngine::PlaybackStateBuffering:
            {
                // disable all controls when buffering
                disablePlayback();
                break;
            }
            case ExampleAppEngine::PlaybackStatePlaying:
            {
                mRewButton->setEnabled(mEngine->isSeekSupported());
                mPlayButton->setDisabled(true);
                mPauseButton->setEnabled(mEngine->isPauseSupported());
                mStopButton->setEnabled(true);
                mRewButton->setEnabled(mEngine->isSeekSupported());
                break;
            }
            case ExampleAppEngine::PlaybackStatePaused:
            {
                mRewButton->setEnabled(mEngine->isSeekSupported());
                mPlayButton->setEnabled(true);
                mPauseButton->setDisabled(true);
                mStopButton->setEnabled(true);
                mRewButton->setEnabled(mEngine->isSeekSupported());
                break;
            }
            case ExampleAppEngine::PlaybackStateStopped:
            {
                mRewButton->setDisabled(true);
                mPlayButton->setEnabled(true);
                mPauseButton->setDisabled(true);
                mStopButton->setDisabled(true);
                mRewButton->setDisabled(true);
                break;
            }
            default:
            {
                // invalid state, disable all buttons
                disablePlayback();
                break;
            }
        }
    }
}

/*!
    Disables all renderer control pushbuttons.
*/
void ExampleAppUi::disablePlayback()
{
    FUNC_LOG

    mPlayButton->setDisabled(true);
    mPauseButton->setDisabled(true);
    mStopButton->setDisabled(true);
    mRewButton->setDisabled(true);
    mFfButton->setDisabled(true);
}

/*!
    Enables file selection button. Called when a renderer is selected.
*/
void ExampleAppUi::enableFileSelection()
{
    FUNC_LOG
    
    mSelectFileButton->setEnabled(true);
}

/*!
    Disables file selection.
*/
void ExampleAppUi::disableFileSelection()
{
    FUNC_LOG

    mSelectFileButton->setDisabled(true);
}

/*!
    Enables the rendering device selection pushbutton. Called when a WLAN AP is in use.
*/
void ExampleAppUi::enableRenderingDeviceSelection(int iap)
{
    FUNC_LOG
    
    if (iap)
    {
        mSearchDevicesButton->setEnabled(true);
        mRenderingDevices->setEnabled(true);
    }
}

/*!
    Disables the rendering device selection pushbutton.
*/
void ExampleAppUi::disableRenderingDeviceSelection()
{
    FUNC_LOG
    
    mSearchDevicesButton->setDisabled(true);
    mRenderingDevices->setDisabled(true);
}

// End of file