I’ve received many questions about the best way to use BPS. The Blackberry Platform Services, or BPS for short, is an API that allows your application to receive events form the system on the Blackberry 10/Playbook OS. It is similar to the Win32 APIs that allow you to create and control a window. The API is relatively well documented and there are many examples available. However, depending on what tutorial you read, it only showcases a specific functionality of the API. So, this blog entry will detail the way to properly initialize BPS and have it do whatever you like.
Initialization of the screen is rather simple. You start with a screen context, which defines your application. It is similar to registering a WNDCLASSEX in Win32.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// CDevice::screen_context_t *m_pCtx = NULL; bool CDevice::init() : { // Create the Context that will receive events from the system screen_create_context(&m_pCtx, NULL); return (NULL != m_pCtx); } bool CDevice::destroy() { if (m_pCtx) { // Delete the context so that events are no longer received screen_destroy_context(m_pCtx); m_pCtx = NULL; } return (NULL == m_pCtx); } |
Initialization of the context is first and the destruction of it must occur last. It is good practice to put these in a constructor and destructor. Next, the initialization of BPS must happen.
1 2 3 4 5 6 7 8 |
// Initialize BPS library if (BPS_SUCCESS == bps_initialize()) { // ..... Do More initialization ..... // Shut down BPS library for this process bps_shutdown(); } |
This allows the application to receive events. In Win32, you would provide a WNDPROC, a callback that is called when events come in. Unlike the even callback mechanism in Windows, BPS is a poll mechanism. It stores (or caches) events until they are used. It is now safe to create a window to draw on.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// screen_window_t *m_pScrWin = NULL; int32 rc = screen_create_window(&m_pScrWin, m_pCtx); if (!rc) { int32 iUsage = SCREEN_USAGE_OPENGL_ES2; int32 iFormat = SCREEN_FORMAT_RGBX8888; int32 iBuffers = 2; int32 iIntr = 1; int32 iTrans = SCREEN_TRANSPARENCY_NONE; rc |= screen_set_window_property_iv(m_pScrWin, SCREEN_PROPERTY_FORMAT, &iFormat); rc |= screen_set_window_property_iv(m_pScrWin, SCREEN_PROPERTY_USAGE, &iUsage); rc |= screen_set_window_property_iv(m_pScrWin, SCREEN_PROPERTY_SWAP_INTERVAL, &iIntr); rc |= screen_set_window_property_iv(m_pScrWin, SCREEN_PROPERTY_TRANSPARENCY, &iTrans); rc |= screen_create_window_buffers(m_pScrWin, iBuffers); if (BPS_SUCCESS == rc) { return true; } return false; } |
There are many options for window initialization. The window properties in your application may vary, depending on what you want to display. Please refer to this documentation for details on window properties.
Finally, you need event handling. The event handling mechanism is based on event IDs. You need to register for them explicitly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// Register for events of a certain domain switch (eEventType) { case DT_ScreenEvents: return (BPS_SUCCESS == screen_request_events(m_pCtx)); case DT_NavigationEvents: return (BPS_SUCCESS == navigator_request_events(0)); case DT_KeyboardEvents: return (BPS_SUCCESS == virtualkeyboard_request_events(0)); case DT_OrientationEvents: return (BPS_SUCCESS == orientation_request_events(0)); case DT_AudioMixerEvents: return (BPS_SUCCESS == audiomixer_request_events(0)); case DT_NetworkStatusEvents: return (BPS_SUCCESS == netstatus_request_events(0)); case DT_AccelerometerEvents: return accelerometer_is_supported() && (BPS_SUCCESS == accelerometer_set_update_frequency(FREQ_40_HZ)); } |
It should be noted that the accelerometer events are not actual events. Rather, they need to be polled for in addition to window events. The checking of events is fairly straight forward. I would advise that event IDs are not hard coded. Poll for the Event IDs once, and cache them in an array. This will keep your code forwards compatible in the event that the IDs are changed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
enum EDeviceEventTypes { DT_ScreenEvents, DT_NavigationEvents, DT_AccelerometerEvents, DT_KeyboardEvents, DT_OrientationEvents, DT_AudioMixerEvents, DT_NetworkStatusEvents, // Always leave Last DT_MaxEvents }; // uint32 CDevice::m_iEventIDs[DT_MaxEvents]; void CDevice::setupEventIDs() { // Set up even IDs for when they are caught m_iEventIDs[DT_ScreenEvents] = screen_get_domain(); m_iEventIDs[DT_NavigationEvents] = navigator_get_domain(); m_iEventIDs[DT_KeyboardEvents] = virtualkeyboard_get_domain(); m_iEventIDs[DT_OrientationEvents] = orientation_get_domain(); m_iEventIDs[DT_AudioMixerEvents] = audiomixer_get_domain(); m_iEventIDs[DT_NetworkStatusEvents] = netstatus_get_domain(); m_iEventIDs[DT_KeyboardEvents] = virtualkeyboard_get_domain(); } |
Here is my message loop. It resolves the event domain ID using the mapping created above for fast look up. Then call your event handlers. The getNextEvent() method is also capable of waiting until an event is posted.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
bool CDevice::getNextEvent(CDeviceEventHandler *pEvHndlr, bool bBlock) { // Sanity Check if (m_pCtx && m_pScrWin && pEvHndlr) { // Currently there isn't a mechanism to register for Accelerometer events if (m_bRecvAcclEvents) { handleAccelerometerEvent(NULL, pEvHndlr); } // Request and process BPS next available event bps_event_t *pEv = NULL; if (BPS_SUCCESS == bps_get_event(&pEv, bBlock ? -1 : 0) && NULL != pEv) { uint32 iEvID = uint32(bps_event_get_domain(pEv)); // Handle Events in special handlers if (iEvID == m_iEventIDs[DT_ScreenEvents]) handleScreenEvent(pEv, pEvHndlr); if (iEvID == m_iEventIDs[DT_NavigationEvents]) handleNavigatorEvent(pEv, pEvHndlr); if (iEvID == m_iEventIDs[DT_KeyboardEvents]) handleKeyboardEvent(pEv, pEvHndlr); if (iEvID == m_iEventIDs[DT_OrientationEvents]) handleOrientationEvent(pEv, pEvHndlr); if (iEvID == m_iEventIDs[DT_AudioMixerEvents]) handleAudioMixerEvent(pEv, pEvHndlr); if (iEvID == m_iEventIDs[DT_NetworkStatusEvents]) handleNetStatusEvent(pEv, pEvHndlr); if (iEvID == m_iEventIDs[DT_KeyboardEvents]) handleKeyboardEvent(pEv, pEvHndlr); } return true; } return false; } |
That’s pretty much it, in terms of setup. The next part will contain event handling for each of the event domains.