Active Frames

Active Frames are dynamic application covers that are displayed when your app is minimized on the home screen. Every app uses application covers by default. Just swipe up from the bottom bezel within an app and the application cover is displayed automatically. Tapping the application cover takes you back into the app.Diagram showing how to initiate Active Frames in an app.

The application cover is a noninteractive scene that the Cascades framework automatically updates whenever it changes. By default, the application cover displays a scaled-down version of the scene that's being displayed in the app. For many apps, using the default application cover might be the best option since it displays the current visual state of the app for the user.

However, there might be instances where you want to display specific information in the application cover. For example, in a weather app you could display the current temperature. In Cascades, you can create your own application covers using the SceneCover class.

For best practices for using application covers, see the Active Frames topic in the UI Guidelines.

Creating a cover

An application cover is similar to any other screen that you might display in your application. You can create the content for the cover using either QML or C++ and it can support whichever controls that you want to display.

Although the root node of the scene needs to be an AbstractPane subclass, the root of a cover can be a Control. The major difference is that users can't interact with a cover, and it doesn't support animations.

The Cascades Cookbook apps provide examples of how to create covers using both C++ and QML. You can download both the C++ and QML versions of the app from the Sample Apps page.

Since it doesn't have a custom cover, the application displays a scaled-down version of the current scene. Here's what the cover might look like when the home screen of the app is being displayed.

Screen showing the cover of the Cookbook sample.

Creating a cover in C++

To create the content for a cover in C++, you need to create the controls and add them to a root container. The custom cover for the Cascades Cookbook app contains a 334 x 396 pixel image for the background, a Label indicating the name of the cookbook, and various containers and layouts for positioning the controls.

The background image is designed specifically for the dimensions of the BlackBerry Z10. If your app also targets devices with a physical keyboard, you need another background image that is 310 x 211 pixels. Since the cover itself is small compared to a regular screen, the label uses a custom TextStyle with a Small FontSize.

Screen showing the Active Frame created from the sample code that follows.
// A small UI consisting of just an ImageView in a Container
// is set up and used as the cover for the application when running 
// in minimized mode.

Container *coverContainer = Container::create().background(
		Color::fromRGBA(0, 0, 0, 1));
coverContainer->setLayout(new DockLayout());

// A background image for the app cover.
ImageView *coverImage = ImageView::create(
		"asset:///images/application-cover.png").scalingMethod(
		ScalingMethod::AspectFit);

Container *titleContainer = Container::create().bottom(31);
titleContainer->setHorizontalAlignment(HorizontalAlignment::Center);
titleContainer->setVerticalAlignment(VerticalAlignment::Bottom);

Container *labelContainer =
		Container::create().preferredSize(84, 42).background(
				Color::fromARGB(0xff121212));
labelContainer->setLayout(new DockLayout());

// A title for the "book" cover so that one can see that it 
// is the C++ version of the cookbook that is running.
Label* title = Label::create("C++");
title->textStyle()->setColor(Color::fromARGB(0xffebebeb));
title->textStyle()->setFontSize(FontSize::PointValue);
title->textStyle()->setFontSizeValue(6);
title->setHorizontalAlignment(HorizontalAlignment::Center);
title->setVerticalAlignment(VerticalAlignment::Bottom);

// Setting up the title Container with a title
labelContainer->add(title);
titleContainer->add(labelContainer);

// Adding the background image and the title Container
coverContainer->add(coverImage);
coverContainer->add(titleContainer);

After you add the components to the root container, you create an instance of SceneCover using the root container, retrieve an instance of Application, and set the cover.

// Create a SceneCover and set the application cover
SceneCover *sceneCover = 
    SceneCover::create().content(coverContainer);
Application::instance()->setCover(sceneCover);

When the user minimizes the application, the custom application cover is displayed instead of the default cover.

Creating a cover in QML

The process for creating a cover in QML is basically the same as C++. The only difference is that the UI is defined in QML.

First, you create the QML document while specifying the QML file containing the UI for the cover.

After you verify that the QML document is free from errors, you retrieve the root container, set it as the content for a SceneCover, and then set the cover on your application instance.

Screen showing the Active Frame created from the sample code that follows.
QmlDocument *qmlCover = 
    QmlDocument::create("asset:///AppCover.qml").parent(this);

if (!qmlCover->hasErrors()) {
	// Create the QML Container from using the QMLDocument.
	Container *coverContainer = 
        qmlCover->createRootObject<Container>();

	// Create a SceneCover and set the application cover
	SceneCover *sceneCover = 
        SceneCover::create().content(coverContainer);
	Application::instance()->setCover(sceneCover);
}

Here's the QML that defines the components for the cover.

import bb.cascades 1.0

Container {
    Container {        
        layout: DockLayout {}
        background: Color.Black
        
        ImageView {
            imageSource: "asset:///images/application-cover.png"
            scalingMethod: ScalingMethod.AspectFit
        }
        
        Container {
            bottomPadding: 31
            horizontalAlignment: HorizontalAlignment.Center
            verticalAlignment: VerticalAlignment.Bottom
            
            Container {
                preferredWidth: 300
                preferredHeight: 42
                background: Color.create("#121212")
                layout: DockLayout {}
                
                Label {
                    objectName: "TheLabel"
                    horizontalAlignment: HorizontalAlignment.Center
                    verticalAlignment: VerticalAlignment.Center
                    text: "QML"
                    textStyle.color: Color.create("#ebebeb")
                    textStyle.fontSize: FontSize.PointValue
                    textStyle.fontSizeValue: 6
                }
            }
        }
    }
}

When the user minimizes the application, the custom application cover is displayed instead of the default cover.

Dynamic covers

The previous sections demonstrated how to create application covers using static images and controls. In the previous examples, the content of the covers doesn't change. This section shows how to create dynamic covers.

Dynamic covers are useful since they can provide the user with specific information regardless of the current application context. For example, a calendar application might show details about the next event.

The image on the right shows an application cover that contains dynamic content. The content of the cover contains information about recent updates from a user's friends.

Screen showing the Active Frame from BBM.

Creating a dynamic cover

Creating the content for a dynamic cover isn't different from creating content for a static cover. You create the scene as you normally would, using C++ or QML, but now you can update the content.

A typical way to create a dynamic cover is to create a class that inherits from SceneCover. In this example, the class is called ActiveFrameQML. The content for the cover is defined in a separate QML file. In the constructor, findChild() is called on the root container to retrieve the Label component that is updated. The class also contains a Q_INVOKABLE function called update(), which is called from main.qml whenever updates need to occur. The content for the cover is set using setContent().

#include "ActiveFrameQML.h"

#include <bb/cascades/SceneCover>
#include <bb/cascades/Container>
#include <bb/cascades/Application>
#include <bb/cascades/QmlDocument>

using namespace bb::cascades;

ActiveFrameQML::ActiveFrameQML(QObject *parent)
	: SceneCover(parent)
{
	QmlDocument *qml = QmlDocument::create("asset:///AppCover.qml")
            .parent(parent);
	Container *mainContainer = qml->createRootObject<Container>();
	setContent(mainContainer);

    // Retrieves the label from QML that we want to update
	m_coverLabel = mainContainer->findChild<Label*>("TheLabel");
	m_coverLabel->setParent(mainContainer);
}

void ActiveFrameQML::update(QString appText) {

	m_coverLabel->setText(appText);
}

The next step is setting the cover on the application. To set the cover, you create an instance of ActiveFrameQML and set it on the Application in the ApplicationUI constructor. You must also expose the ActiveFrameQML instance as a context object to the main.qml file, because that's where updates to the cover are initiated from.

ApplicationUI::ApplicationUI(bb::cascades::Application *app) :
		QObject(app) {

	QmlDocument *qml = 
        QmlDocument::create("asset:///main.qml").parent(this);

	ActiveFrameQML *activeFrame = new ActiveFrameQML();
	Application::instance()->setCover(activeFrame);

	qml->setContextProperty("activeFrame", activeFrame);

	AbstractPane *root = qml->createRootObject<AbstractPane>();
	app->setScene(root);
}

To initiate updates, your application must listen for the Application::thumbnail() signal, which is emitted every time the application moves to the background. This example shows how to connect a function to thumbnail() from within main.qml. Connecting to the signal from QML is partly for convenience, because the data that's used to update the cover is also located within this file. Depending on your application, you might want to connect to thumbnail() from C++ instead.

Whenever thumbnail() is emitted, the update() function is called. The update() function passes the text property from the TextField to the ActiveFrameQML instance, which is then used to update the Label on the cover.

import bb.cascades 1.0

Page {
    Container {
        TextField {
            id: appLabel
            text: "Hello World"
            textStyle.base: SystemDefaults.TextStyles.BigText
        }
    }
    onCreationCompleted: {
        Application.thumbnail.connect(onThumbnail)
    }
    function onThumbnail() {
        activeFrame.update(appLabel.text);
    }
}

To see the complete code for this example, expand the files listed below.

#include <bb/cascades/Application>

#include "applicationui.hpp"

#include <Qt/qdeclarativedebug.h>

using namespace bb::cascades;

Q_DECL_EXPORT int main(int argc, char **argv)
{
    Application app(argc, argv);

    new ApplicationUI(&app);

    return Application::exec();
}
#ifndef ApplicationUI_HPP_
#define ApplicationUI_HPP_

#include <QObject>
#include <bb/cascades/Application>

using namespace ::bb::cascades;

class ApplicationUI : public QObject
{
    Q_OBJECT

public:
    ApplicationUI(bb::cascades::Application *app);
    virtual ~ApplicationUI() { }
};

#endif /* ApplicationUI_HPP_ */
#include "applicationui.hpp"
#include "ActiveFrameQML.h"

#include <bb/cascades/Application>
#include <bb/cascades/QmlDocument>
#include <bb/cascades/AbstractPane>

using namespace bb::cascades;

ApplicationUI::ApplicationUI(bb::cascades::Application *app) :
		QObject(app) {

	QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);

	ActiveFrameQML *activeFrame = new ActiveFrameQML();
	Application::instance()->setCover(activeFrame);

	qml->setContextProperty("activeFrame", activeFrame);

	AbstractPane *root = qml->createRootObject<AbstractPane>();
	app->setScene(root);
}
/*
 * ActiveFrame.h
 *
 *  Created on: Apr 2, 2013
 *      Author: wbarichak
 */

#ifndef ACTIVEFRAMEQML_H_
#define ACTIVEFRAMEQML_H_

#include <QObject>
#include <bb/cascades/Label>
#include <bb/cascades/SceneCover>

using namespace ::bb::cascades;

class ActiveFrameQML: public SceneCover {
	Q_OBJECT

public:
	ActiveFrameQML(QObject *parent=0);

public slots:
	Q_INVOKABLE void update(QString appText);

private:
	bb::cascades::Label *m_coverLabel;
};

#endif /* ACTIVEFRAMEQML_H_ */
#include "ActiveFrameQML.h"

#include <bb/cascades/SceneCover>
#include <bb/cascades/Container>
#include <bb/cascades/Application>
#include <bb/cascades/QmlDocument>

using namespace bb::cascades;

ActiveFrameQML::ActiveFrameQML(QObject *parent)
	: SceneCover(parent)
{
	QmlDocument *qml = QmlDocument::create("asset:///AppCover.qml")
            .parent(parent);
	Container *mainContainer = qml->createRootObject<Container>();
	setContent(mainContainer);

	m_coverLabel = mainContainer->findChild<Label*>("TheLabel");
	m_coverLabel->setParent(mainContainer);
}

void ActiveFrameQML::update(QString appText) {

	m_coverLabel->setText(appText);
}
import bb.cascades 1.0

Page {
    Container {
        TextField {
            id: appLabel
            text: "Hello World"
            textStyle.base: SystemDefaults.TextStyles.BigText
        }
    }
    onCreationCompleted: {
        Application.thumbnail.connect(onThumbnail)  
    }
    function onThumbnail() {
        activeFrame.update(appLabel.text);
    }
}
import bb.cascades 1.0

Container {    
    Container {        
        layout: DockLayout {}
        background: Color.Black
        
        ImageView {
            imageSource: "asset:///images/application-cover.png"
            scalingMethod: ScalingMethod.AspectFit
        }
        
        Container {
            bottomPadding: 31
            horizontalAlignment: HorizontalAlignment.Center
            verticalAlignment: VerticalAlignment.Bottom
            
            Container {
                preferredWidth: 300
                preferredHeight: 42
                background: Color.create("#121212")
                layout: DockLayout {}
                
                Label {
                    objectName: "TheLabel"
                    horizontalAlignment: HorizontalAlignment.Center
                    verticalAlignment: VerticalAlignment.Center
                    text: "QML"
                    textStyle.color: Color.create("#ebebeb")
                    textStyle.fontSize: FontSize.PointValue
                    textStyle.fontSizeValue: 6
                }
            }
        }
    }
}
Screen showing the image file used in the application cover example.

Using Active Frames efficiently

When you create dynamic covers, you should consider how often you update the covers, and how it might affect performance and battery life. For more information about creating covers efficiently, see Update Active Frames efficiently.

Multiple covers 10.3

The BlackBerry Passport smartphone can display two sizes of Active Frames (also known as application covers):

  • Large Active Frames are 440 pixels wide by 486 pixels high
  • Small Active Frames are 440 pixels wide by 195 pixels high

By default, a minimized app uses a large Active Frame. The large Active Frame is created using the application cover with a CoverDetailLevel of High.

If there are more than six minimized apps running on a BlackBerry Passport, the application cover of the next app to be minimized uses a small Active Frame.

The small Active Frame is created using the application cover with a CoverDetailLevel of Medium.

Screen showing the different sizes of Active Frames on a BlackBerry Passport.

This technique might not create the user experience that you expect if the small Active Frame doesn't illustrate your app clearly. You can create multiple covers to improve your user experience.

To specify more than one application cover in your app, you can use the MultiCover class. In a MultiCover, you can specify either two SceneCover objects or one SceneCover and one ApplicationViewCover.

Using two SceneCover objects allows you to design the large and small application covers to show specific parts of your app, including dynamic content in your app.

Using a SceneCover and an ApplicationViewCover allows you to design one specific application cover and let the other application cover be a scaled version of the current scene in your app. Setting the CoverDetailLevel determines which application cover is used. A CoverDetailLevelof High indicates that this application cover is used for large Active Frames. A CoverDetailLevel of Medium indicates that this application cover is used for small Active Frames.

The following code sample shows how to create a MultiCover using two SceneCover objects in QML:

import bb.cascades 1.3

Page {
    content: Container {
        // This is the UI of your app
    }
    
    onCreationCompleted: {
        Application.setCover(multiCover)
    }
    
    attachedObjects: [
        MultiCover {
            id: multiCover
            
            SceneCover {
                id: bigCover
                // Use this cover when a large cover is required
                MultiCover.level: CoverDetailLevel.High
                content: Container {
                    // Your large cover layout
                    Label {
                        text: "My Large Cover"
                        textStyle.color: Color.Magenta
                        textStyle.fontSizeValue: 10.0
                    }
                }
                function update() {
                    // Update the large cover dynamically
                }
            } // sceneCover HIGH
            
            SceneCover {
                id: smallCover
                // Use this cover when a small cover is required
                MultiCover.level: CoverDetailLevel.Medium
                content: Container {
                    // Your small cover layout
                    Label {
                        text: "My Small Cover"
                        textStyle.color: Color.Cyan
                        textStyle.fontSizeValue: 5.0
                    }
                }
                function update() {
                    // Update the small cover dynamically
                }
            } // sceneCover MEDIUM
            
            function update() {
                bigCover.update()
                smallCover.update()
            }
        }
    ]
}
 

The following code sample demonstrates how to create one SceneCover and one ApplicationViewCover in QML:

import bb.cascades 1.3

Page {
    content: Container {
        // This is the UI of your app
    }
    
    onCreationCompleted: {
        Application.setCover(multiCover)
    }
    
    attachedObjects: [
        MultiCover {
            id: multiCover
            
            SceneCover {
                id: bigCover
                // Use this cover when a large cover is required
                MultiCover.level: CoverDetailLevel.High
                content: Container {
                    // Your large cover layout
                    Label {
                        text: "My Large Cover"
                        textStyle.color: Color.Magenta
                        textStyle.fontSizeValue: 10.0
                }
                function update() {
                    // Update the large cover dynamically
                    }
                } // sceneCover HIGH
            }
            
            ApplicationViewCover {
                id: appViewCover
                // Use this cover when a small cover is required
                MultiCover.level: CoverDetailLevel.Medium
            }
            
            function update() {
                bigCover.update()
            }
        }
    ]
}

The following code sample demonstrates how to create a MultiCover with one SceneCover and one ApplicationViewCover in C++:

#include "applicationui.hpp"

#include <bb/cascades/Page>
#include <bb/cascades/Application>
#include <bb/cascades/QmlDocument>
#include <bb/cascades/AbstractPane>

#include <bb/cascades/Container>
#include <bb/cascades/Button>
#include <bb/cascades/ApplicationViewCover>
#include <bb/cascades/SceneCover>
#include <bb/cascades/MultiCover>

using namespace bb::cascades;

ApplicationUI::ApplicationUI() :
        QObject()
{
    // Create your UI
    Container *contentContainer = Container::create()
        .add(Button::create().text("My Application"));

    // Add a SceneCover to your UI
    SceneCover* sceneCover = 
            SceneCover::create().content(contentContainer);

    // Create an ApplicationViewCover
    ApplicationViewCover* appViewCover = 
            ApplicationViewCover::create();

    // Create a MultiCover with the SceneCover
    // used when a large Active Frame is required
    // and the ApplicationViewCover used when
    // a small Active Frame is required
    AbstractCover* cover = MultiCover::create()
        .add(sceneCover, CoverDetailLevel::High)
        .add(appViewCover, CoverDetailLevel::Medium);

    // Create a page using the root container
    // and set the scene
    Page *page = new Page();
    page->setContent(contentContainer);
    Application::instance()->setScene(page);

    Application::instance()->setCover(cover);

}

Related resources

Web-based training

Design guidelines

 

Last modified: 2014-10-16



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus