Quick Tip: Invoking the Media Preview "Card" [updated 12-31-2013]

The Invocation framework is one of the hallmarks and great strengths of the BlackBerry 10 operating system, and can allow us developers to easily integrate with core and third-party applications. To make the process easier, in this tutorial I will show you how to implement an Invocation Manager that you can can call at will from inside your QML to invoke just about any card or app available.

As usual, we will be starting with a standard empty Cascades project and working from there, so in Momentics, start a new empty project and name it whatever you wish (I will be using 'InvokeMediaCard'), and I will be using an API level of 10.2. 

 

Read more...

Fundamentals Brief: Calling C++ functions from QML

This tutorial is the first in a series of very brief and concise tutorials covering some basic fundamentals needed to develop BlackBerry apps using Cascades.

There comes a time when a Cascades developer may need to utilize C++ in conjunction with his or her QML. Many developers use JavaScript for app logic and sidestep using extra C++ logic entirely. Having the ability to use JavaScript  and/or C++ in conjunction with QML is one of the strenghts of Cascades development, so pick your poison. That being said, this tutorial is meant to be a brief and very basic introduction to the "how" when it comes to calling C++ functions from QML and returning the result of the function back to your QML.

Read more...

Keeping it pithy: Toasts in Cascades

What is a toast? Well according to BlackBerry...

"A SystemToast displays a small message that expires and disappears after a predefined amount of time. This message provides users with information and allows them to continue with the application after the toast expires."

You've seen toasts when you clear your browser history or delete an attachment from a BBM and in many other places throughout BlackBerry 10. When dealing with toasts and any other user prompts, developers need to be cognizant of the user experience and of the fact that users typically do not want to be interrupted (at least not too frequently).

Read more...

Simple BBM Integration in WebWorks

Jeremy wrote up a great article about integrating BBM into Cascades, and a few people mentioned they wanted to see an article about getting BBM integrated with WebWorks. I've been kind of off the radar lately, but figured this was a good topic for me to do a quick writeup on.

Now, to contnue with some friendly teasing I directed at Jeremy eariler: integrating BBM into Cascades may be easy, but integrating it into WebWorks makes integrating it into Cascades look like rocket science. Yes, it really is that easy.

Read more...

Simple BBM Integration in Cascades

Did I say simple? I meant very simple. In the following sample app I started with the standard empty cascades project and added a total of ~55 lines of code to the applicationui.cpp and applicationui.app files. In addition the UI is very plain as to not take away from the core purpose of a sample application which is to demonstrate how to do something, right?

Read more...

Easing into Cascades Animations

OverView

One of the many elements that makes the Cascades UI so beautiful is the widespread use of animations. Even without programming it yourself, animations are an integral part of your Cascades application. For example, when you pull in a sheet it slides in, or when you push a new pane within a NavigationPane, you see the built in animations in action.

There are many more subtle animations in Cascades that happen automatically, and I am not going to attempt to name them all because I would be here all day. We will just agree that animations are everywhere in Cascades. With that in mind, using any extra animation can easily become overwhelming to the end-user.  However, adding subtle animations in appropriate places in your app can really improve the overall UX and really give your app a premium feel.

Let's Get Started

So, with all this built in animation why would you want more? While it is true that you could easily overdo it with animation upon animation, adding subtle animations in appropriate places can really give your app a premium feel. Let's look at an example from the BlackBerry Cascades documentation.

freeform titlebar

Free-form title bar containing expandable content

The above example is shown in the official docs (http://developer.blackberry.com/cascades/reference/bb__cascades__titlebar.html) and demonstrates how to have an expanable area that drops down from the TitleBar. An example of this in action can be seen in the native BlackBerry World app when viewing any top list.  

IMG 00000147

There are some key differences between the example above and the implementation in the BlackBerry World app, and it's those differences that give the BBW implementation a more premium feel. The list is styled a bit more and uses check marks instead of radio toggle, and there is an arrow in the top right that flips based on whether the exanable area is expanded or collapsed (similiar to in a dropdown).

Let's take the code from the top example from the the official docs...

Page {
    titleBar: TitleBar {
        kind: TitleBarKind.FreeForm
        kindProperties: FreeFormTitleBarKindProperties {
            Container {
                layout: StackLayout { orientation: LayoutOrientation.LeftToRight }
                leftPadding: 10
                rightPadding: 10
                Label {
                    text: "Show expandable content"
                    textStyle {
                        color: Color.White
                    }
                    verticalAlignment: VerticalAlignment.Center
                    layoutProperties: StackLayoutProperties { spaceQuota: 1 }
                }
                CheckBox {
                    id: toggleExpanded
                    verticalAlignment: VerticalAlignment.Center
                }
            }
            expandableArea {
                content: RadioGroup {
                    Option { id: option1; text: "option 1"; selected: true }
                    Option { id: option2; text: "option 2" }
                    Option { id: option3; text: "option 3" }
                }
                indicatorVisibility: TitleBarExpandableAreaIndicatorVisibility.Hidden
                expanded: toggleExpanded.checked
                onExpandedChanged: {
                    toggleExpanded.checked = expanded
                }
            }
        }
    }
}

So in the above example a CheckBox is used to toggle the expansion and collapse of the expandableArea, and radio toggles are used to on the list items within the expandableArea. While this is fine, and gets the job done, why not style it a bit to make it a bit fancier? In the code below we take the example from the docs and do the following:

  • Replace the checkbox with an ImageView
  • Add RotateTransition animation to ImageView
  • Create and use a custom bool property
  • Dynamically change label to reflect the selected option

 

Page {
    titleBar: TitleBar {
        kind: TitleBarKind.FreeForm
        kindProperties: FreeFormTitleBarKindProperties {
            Container {
                layout: StackLayout {
                    orientation: LayoutOrientation.LeftToRight
                }
                leftPadding: 10.0
                rightPadding: 10.0
                Label {
                    text: radioGroup.selectedOption.text
                    textStyle {
                        color: Color.White
                    }
                    verticalAlignment: VerticalAlignment.Center
                    layoutProperties: StackLayoutProperties {
                        spaceQuota: 1
                    }
                }
                ImageView {
                    id: toggleExpanded
                    property bool checked: false
                  
                    onTouch: {
                        if (checked == false) {
                            if (event.touchType == TouchType.Down) {
                            } else if (event.touchType == TouchType.Move) {
                            } else if (event.touchType == TouchType.Up) {
                                rt1.play()
                                checked = true
                            }
                        } else {
                            if (event.touchType == TouchType.Down) {
                            } else if (event.touchType == TouchType.Move) {
                            } else if (event.touchType == TouchType.Up) {
                                rt2.play()
                                checked = false
                            }
                        }
                    }
                    animations: [
                        RotateTransition {
                            id: rt1
                            duration: 150
                            toAngleZ: -180.0
                            fromAngleZ: 0.0
                            easingCurve: StockCurve.CubicIn
                        },
                        RotateTransition {
                            id: rt2
                            duration: 150
                            toAngleZ: 0
                            fromAngleZ: -180.0
                            easingCurve: StockCurve.CubicIn
                        }
                    ]
                    imageSource: "asset:///ic_to_bottom.png"
                    verticalAlignment: VerticalAlignment.Center
                    horizontalAlignment: HorizontalAlignment.Center
                }
            }
            expandableArea {
                content: RadioGroup {
                    id: radioGroup
                    Option {
                        id: option1
                        text: "Option 1"
                        selected: true
                    }
                    Option {
                        id: option2
                        text: "Option 2"
                    }
                    Option {
                        id: option3
                        text: "Option 3"
                    }
                    onSelectedOptionChanged: {
                        rt2.play()
                        toggleExpanded.checked = false
                    }
                }
                indicatorVisibility: TitleBarExpandableAreaIndicatorVisibility.Hidden
                expanded: toggleExpanded.checked
                onExpandedChanged: {
                    toggleExpanded.checked = expanded
                }
            }
        }
    }
}

 

So there you have it. Import the zip attached to this post into the Momentics IDE and run it on your device to see it in action.  Tinker with the code and see what you can improve and/or create.

Relevant Links:

Read more...

Example of a First Run Tutorial in Cascades

I'm sure many of you have installed an app onto your device that displayed a brief tutorial the first time you launched it, right?  If you have ever wondered how you could do that in your own app, this sample application will show you one simple way to accomplish this in a Cascades app. 

Info

This sample app utilizes QSettings, so see my previous tutorial about QSettings if you need some help there.

This post will not be a full-fledged tutorial, but I do want to explain some of the aspects of the sample application. First, here are some of the "stuffs" used in the sample application...

QML elements/components used:

  • Sheet {}
  • Page {}
  • Container {}
  • Label {}
  • ListView {}

QML Signals used:

  • onCreationCompleted:
  • onClicked:
  • onTriggered:

Other used:

  • DockLayout
  • StackLayout
  • XmlDataModel 

 How does the application work?

The application is pretty simple, and it uses QSettings to store a bool (true/false) property that is used to determine if the tutorial should open or not when the app is launched.  By default the bool property is set to true and therefore the tutorial fires up when the app is launched. If the user taps the close button to close the tutorial, the tutorial closes, but it will open up again when the user launches the app the next time.  However, if the user taps the Dismiss button to close the tutorial, the tutorial is closed forever or until the app is uninstalled, reinstallled, and relaunched.  

FirstRunImage 1

For the purposes of this sample app I placed a button on the main.qml file that is visible when the tutorial is closed. This button allows you to restore the tutorial popup functionailty without having to uninstall the app. There is also a Label above to button that displays whether the tutorial pop up is disabled or enabled.  

  FirstRunImage 2 

So, head over to the OSBB GitHub and check it out and I have attached a prebuilt and presigned bar file to this post that can be side loaded onto your device (Q10/Q5/Z10/Dev Alpha).  

If you have issues or REALLY want a tutorial, sound off in the comments, and I will whip one up. Also, if you have specific questions, hit me up on twitter @BerryInformed or via the comment section below.

Thanks guys and happy coding!

Read more...

Simple Example of QSettings in Cascades

There comes a time when building apps, when it is necessary to store a setting, a value, or a string of text in a persistant manner--meaning it remains stored even after the app is closed and reopened. In Cascades, QSettings is one of the options available to developers to accomplish just that.

In this short tutorial, you will learn how to store a string of text via QSettings.  

Starting in the application.cpp (for full source see Github) file we need to add a few things to be able to use QSettings.

...
//!!!ADD!!!
#include <QSettings> ... ApplicationUI::ApplicationUI(bb::cascades::Application *app) : QObject(app) { ... //!!!ADD!!!
qml->setContextProperty("_app", this); ... } ... //!!!ADD!!!
QString ApplicationUI::getValueFor(const QString &objectName, const QString &defaultValue) { QSettings settings; if (settings.value(objectName).isNull()) { return defaultValue; } return settings.value(objectName).toString(); } //!!!ADD!!!
void ApplicationUI::saveValueFor(const QString &objectName, const QString &inputValue) { QSettings settings; settings.setValue(objectName, QVariant(inputValue)); }

The code in bold is what we add to the standard Cascades project source files.

Now to the applicationui.hpp (for full source see GitHub) file

 

 
public:
	...
//!!!ADD!!!
Q_INVOKABLE QString getValueFor(const QString &objectName, const QString &defaultValue); //!!!ADD!!!
Q_INVOKABLE void saveValueFor(const QString &objectName, const QString &inputValue); ...

Now the main.qml file, let's use a TextField to enter a string of text to be stored. When we submit (onSubmitted), we utilize the context property we set in the applicationui.cpp file to call the C++ function from QML. This is what stores the string.

import bb.cascades 1.0
Page {
    Container {
        TextField {
            id: inputField
objectName: "inputField" input { onSubmitted: { //Here is where we save the value or string
_app.saveValueFor(inputField.objectName, inputField.text) outputText.text = inputField.text instructionsLabel.visible = false testLabel.visible = true } } } }

 Now, for the rest of the main.qml the code in this sample is as follows:

import bb.cascades 1.0
Page {
    Container {
        TextField {
            id: inputField
            objectName: "inputField"
            
            input {
                onSubmitted: {
                    //Here is where we save the value or string
                    _app.saveValueFor(inputField.objectName, inputField.text)
                    outputText.text = inputField.text
                    instructionsLabel.visible = false
                    testLabel.visible = true
                }
            }
        
        }
        Label {
            id: outputText
            //Here is where we call or get that value and display it
            text: _app.getValueFor(inputField.objectName, "No Text Set")
            textStyle.base: SystemDefaults.TextStyles.BigText
        }
        Container {
            id: instructionsLabel
            topPadding: 100.0
            visible: true
            Label {
                text: "Type a string of text into the field above and press enter."
                multiline: true
                textStyle.fontSize: FontSize.Large
                textStyle.fontWeight: FontWeight.W100
            }
        }
        Container {
            id: testLabel
            topPadding: 100.0
            visible: false
            Label {
                text: "Now close the app, and the Label above should still have the text you set it to when you open the app back up!"
                multiline: true
                textStyle.fontSize: FontSize.Large
                textStyle.fontWeight: FontWeight.W100
                textStyle.color: Color.Red
            }
        }
    }
}

That's it! Remember to check out the full source on the OSBB GitHub at https://github.com/OpenSourceBB/Cascades-Samples/tree/master/QSettingsExample and you can download the prebuilt and signed bar file attached to this post (Will work for Q10 or Z10 or Dev Alpha).

If you compile and run this sample, it should look similar to the images below.

 

Read more...

WebWorks File API Framework

One of the things I love about developing for BlackBerry 10 is that BlackBerry has really done well at implementing HTML5 standards (and a quick visit to http://html5test.com confirms it). While this is awesome as far as creating robust HTML5 apps is concerned, it means that developers have to learn new technologies that might not be fully implemented in your web browser. As an example, webkitGetUserMedia only recently showed up in Chrome.

The File API isn't all that new (it's been around a while now), but it's something that I couldn't find a whole lot of documentation for it. There is a very, very good article at http://www.html5rocks.com/en/tutorials/file/dndfiles/, but it's (in my opinion) a fairly involved API.

The great folks over at BlackBerry have implemented the File API, but that doesn't necessarily mean it's easy for developers to start using. And since the web is all about frameworks these days (JQuery, Sencha, etc), I figured I'd do a mini framework of BlackBerry specific File API functions to make things easy for other WebWorks Devs to implement saving and loading (including backup and restore) to their apps!

This framework is Open Source (so you can browse my typos without even downloading it), and you can check it out at My Github.

You can check out the readme in the github link to see how it works, but here's the quick how-to:

1. Include the javascript in your index.html

Example: <script type="text/javascript" src="/cssjs/bbfiles.js"></script>

2. Add the required feature IDs to your config.xml:

<feature id="blackberry.invoke.card" />
<feature id="blackberry.io" />
<feature id="blackberry.ui.toast" />

3. Create a function for whatever you want to do. Functions that are available: 

Save text to a specified file:
saveText(Text to save, save location and file name)
Example: saveText('Hello World!', '/accounts/1000/shared/misc/helloWorld.txt')

Save text to a file of the user's choice:
saveText(Text to save, location to open the file picker, 'picker')
Example: saveText('Hello World', '/accounts/1000/shared/misc/', 'picker')

Load text from a file:
loadData(location of document)
Example: loadData('/accounts/1000/shared/misc/helloWorld.txt')

Load text from a file of the user's choice:
loadData(location to open the file picker, 'picker')
Example: loadData('/accounts/1000/shared/misc/', 'picker')

Delete a specified file:
deleteFile(file location and name)
Example: deleteFile('/accounts/1000/shared/misc/helloWorld.txt')

Delete a file of the user's choice:
deleteFile(location to open the file picker, 'picker')
Example: deleteFile('/accounts/1000/shared/misc/', 'picker')

Create a folder:
createFolder(folder to create)
Example: createFolder('/accounts/1000/shared/misc/New')

Delete a folder:
deleteFolder(folder to delete)
Example: deleteFolder('/accounts/1000/shared/misc/New')

Move a file:
moveFile(location to move the file to, current file location and name)
Example: moveFile('/accounts/1000/shared/misc/downloads/', '/accounts/1000/shared/misc/helloWorld.txt')

Move a file of the user's choice:
moveFile(location to move the file to, location to open the file picker, 'picker')
Example: moveFile('/accounts/1000/shared/misc/downloads/', '/accounts/1000/shared/misc/', 'picker')

Move and Rename a file:
moveRenameFile(location to move the file to, current file location and current file name, new name)
Example: moveRenameFile('/accounts/1000/shared/misc/downloads/', '/accounts/1000/shared/misc/helloWorld.txt', 'newFile.txt')

Move and Rename a file of the user's choice:
moveRenameFile(location to move the file to, location to open the file picker, new name, 'picker')
Example: moveRenameFile('/accounts/1000/shared/misc/downloads/', '/accounts/1000/shared/misc/', 'newFile.txt', 'picker')

Copy a file:
copyFile(location to move the file to, current file location and file namee)
Example: copyFile('/accounts/1000/shared/misc/downloads/', '/accounts/1000/shared/misc/helloWorld.txt')

Copy a file of the user's choice:
copyFile(location to move the file to, location to open the file picker, 'picker')
Example: copyFile('/accounts/1000/shared/misc/downloads/', '/accounts/1000/shared/misc/', 'picker')

Copy and Rename a file:
copyRenameFile(location to copy the file to, current file location and current file name, new name)
Example: copyRenameFile('/accounts/1000/shared/misc/downloads/', '/accounts/1000/shared/misc/helloWorld.txt', 'newFile.txt')

Copy and Rename a file of the user's choice:
copyRenameFile(location to copy the file to, location to open the file picker, new name, 'picker')
Example: copyRenameFile('/accounts/1000/shared/misc/downloads/', '/accounts/1000/shared/misc/', 'newFile.txt', 'picker')

Rename a file:
renameFile(file location and current name, new name)
Example: renameFile('/accounts/1000/shared/misc/helloWorld.txt', 'yay.txt')

Rename a file of the user's choice:
renameFile(file location, new name, 'picker')
Example: renameFile('/accounts/1000/shared/misc/', 'yay.txt', 'picker')

Save Canvas to an image:
saveCanvas(canvas element, location and name you want to save canvas to)
Example: saveCanvas(document.getElementById('canvas'), '/accounts/1000/shared/misc/image.png')

Save Canvas to an image of the user's choice:
saveCanvas(canvas element, location to open the file picker, 'picker')
Example: saveCanvas(document.getElementById('canvas'), '/accounts/1000/shared/misc/', 'picker')

Load image to Canvas:
loadCanvas(canvas element, image to load', 'nopicker', width you want image to load, height you want image to load)
loadCanvas(document.getElementById('canvas'), '/accounts/1000/shared/misc/image.png', 'nopicker', 500, 300)
Note: 'nopicker' can be any text except 'picker'. It is optional, unless you specify a height and width (which are also optional).

Save image of the user's choice to Canvas:
loadCanvas(canvas element, location to open the file picker, 'picker', width you want image to load, height you want image to load)
loadCanvas(document.getElementById('canvas'), '/accounts/1000/shared/misc/', 'picker', 500, 300)

That's it! Maybe not as simple as simple can get, but a lot better than writing the function out yourself :)

Read more...
Subscribe to this RSS feed
Subscribe to the official OSBB BBM Channel!

osbbchannelQR

C00013E89

Back to top