Develop applications for different BlackBerry Device Software versions and BlackBerry smartphone models

by Retired on ‎02-16-2010 10:43 AM - edited on ‎03-18-2011 02:04 PM by Retired (16,889 Views)

Summary

 

This article applies to the following:

 

  • BlackBerry® Java® Development Environment (BlackBerry JDE) 4.0.2 or later
  • BlackBerry® Java® Plug-in for Eclipse® 1.0 or later
  • BlackBerry smartphones

Details

 

Introduction

 

This article discusses how to develop applications for different BlackBerry® Device Software versions and BlackBerry smartphone models without restricting the application to the lowest common functionality. A number of methods are shown allowing access to the latest application programming interfaces (APIs). The following is an outline of the topics in this article:

 

Background

 

  • Forward compatibility of BlackBerry Device Software versions
    • Best practices for BlackBerry JDE builds
  • Approaches to minimize code variation
  • Selecting alternate String properties
  • Including multiple methods and using separate compilers
  • Using libraries to support different implementations
  • Using libraries to support incremental functionality
  • Using the preprocessor to target new APIs from one source

Appendices

 

  • A: Determining BlackBerry smartphone model and code level
  • B: Managing installation of multiple builds

Background

 

Forward compatibility of BlackBerry Device Software versions

 

BlackBerry smartphone applications built using a particular version of the API and compiler are compatible with BlackBerry smartphones running that same or later version of BlackBerry Device Software. The same application is not compatible with earlier versions of BlackBerry Device Software. For example, if you build an application using BlackBerry JDE 4.2.0, the application is compatible with BlackBerry smartphones running BlackBerry Device Software 4.2.0 and later but the application is not compatible with BlackBerry Device Software 4.1.0 or earlier.

 

Best practices for BlackBerry JDE builds

 

Due to the compatibility limitation, the suggestion given in this article is to develop using the earliest version of the BlackBerry JDE that supports the features or BlackBerry smartphone that is targeted by the project. However, there are situations where a subset of features is required from earlier BlackBerry Device Software versions, but newer features are valuable additions to an application wherever they are available. This article discusses building an application so that the main code is consistent between BlackBerry Device Software versions, but where additional features are accessed where available.

 

Approaches to minimize code variations

 

Selecting alternate String properties

 

There are several instances of APIs that accept String arguments where a different value might be used depending on the BlackBerry Device Software version. Connector.open() and System.getProperty() are two notable examples. In this case, the code could use alternative String values for different BlackBerry smartphone models or BlackBerry Device Software versions. A single build would be compiled using the earliest BlackBerry JDE version that is supported. Because the API would not be different between versions, only one build is required to support all BlackBerry smartphone models even with the different properties are used.

 

An example of such an implementation is using the FileConnection API to access the microSD card if it is available, otherwise access the system memory. The BlackBerry® Pearl™ smartphone and later models support microSD cards. The FileConnection API was added in BlackBerry Device Software 4.2.0, so some BlackBerry smartphones that use the BlackBerry Device Software 4.2.1 update, like the BlackBerry® 8700 smartphone and the BlackBerry® 7130 smartphone, would have the FileConnection API but not have microSD card capability. An application can use the approach illustrated in this article to determine if a microSD card is present in the BlackBerry smartphone. This application can be built in BlackBerry JDE 4.2.0 and it would not require separate builds or installation for later versions of BlackBerry Device Software, whether the BlackBerry smartphone has a microSD card slot or not.

 

For more information about the available System properties, see the Javadoc™ for the System class

.

Including multiple methods and using separate compilers

 

During compilation, the RIM Application Program Compiler (RAPC) removes code lines that are not called. This behavior allows development of alternative implementations in the same code where the BlackBerry framework calls different methods depending on the BlackBerry Device Software version. The primary example of this is in the navigation methods added to the Screen class in BlackBerry JDE 4.2.0. These methods replace the trackwheel methods to support the trackball introduced with the BlackBerry Pearl smartphone. The trackwheel methods remain for forward compatibility, but the navigation methods should be used for BlackBerry smartphones that support them. However, due to the compiler optimization, it is possible to implement both sets of methods in the same code. The application should be compiled in two different BlackBerry JDE versions: 4.0 or 4.1 to support the earlier BlackBerry Device Software versions and the trackwheel methods, and BlackBerry JDE 4.2 to support the navigation methods. Because the navigation methods are never called for BlackBerry Device Software 4.1, they are removed by the compiler and the trackwheel methods are then used. In BlackBerry Device Software 4.2.0 and later, the navigation methods are used and in the implementation they should return true, which stops the trackwheel methods from running.

 

The following is an example that shows both implementations together. The sample code paints a small cube to the screen and intercepts the navigation commands to move the cube around the visible area.

 

This code defines the Cube as a Field, setting some internal variables that will be used for painting the cube and making the field take up the entire visible area:

 

 

private class CubeField extends Field {
private int width = 20;
private int height = 20;
private int arcWidth = 10;
private int arcHeight = 10;
private int x;
private int y;
private int color = 0x00000099;

public CubeField() {
}

public int getPreferredHeight() {
return getScreen().getVisibleHeight();
}

public int getPreferredWidth() {
return getScreen().getVisibleWidth();
}

public boolean isFocusable() {
return true;
}


 

The next set of code does the actual layout of the field and painting of the cube in the correct location, while clearing the old painting:

 

 

protected boolean navigationMovement(int dx, int dy, int status, int time) {
moveCube(width * dx, height * dy);
return true;
}

protected boolean trackwheelRoll(int dir, int status, int time) {
int dx = dir;
int dy = 0;
if (status > 0) {
dx = 0;
dy = dir;
}
moveCube(width * dx, height * dy);
return true;
}

private void moveCube(int dx, int dy) {
x += dx;
y += dy;
int maxX = getPreferredWidth() - width;
int maxY = getPreferredHeight() - height;
if (x > maxX) {
x = maxX;
} else if (x < 0) {
x = 0;
}
if (y > maxY) {
y = maxY;
} else if (y < 0) {
y = 0;
}
invalidate();
}


Using libraries to support different implementations

 

The previous two examples are not applicable to situations which require access to an API that has been introduced in a later BlackBerry JDE version. To take advantage of new functionality without maintaining entirely separate branches of code, it is possible to use Java's dynamic class loading ability to load a commonly named library class. This library will implement a common interface and will be built in the appropriate BlackBerry JDE for the APIs that it utilizes. The main application must be built in the earlier BlackBerry JDE version. At application load time, the correct implementation for the BlackBerry Device Software must be loaded with the rest of the application.

 

The following example shows how separate libraries can be used with the Invoke class. This class is present in BlackBerry JDE 4.0 and it has been gradually added to since then, so it provides a very useful example for the library approach.

 

The workspace in BlackBerry JDE 4.1 has been set up to build three projects. These projects include the overall application, which is a single screen that will have menu items added to it. Also built in BlackBerry JDE 4.1 is a library project for all the other libraries to build against. This library includes only the common interface definition that every other library must implement. Finally, there is a library for the functionality that we want to use on BlackBerry smartphones with BlackBerry Device Software 4.1.

 

The following is the common base library definition:

 

 

import net.rim.device.api.ui.MenuItem;

public interface LibraryInterface {
public MenuItem[] getMenus();
}

 

The following shows the 4.1.0 compatible library, where a MenuItem to Invoke the Address Book is added to the application's menu:

 

 

import net.rim.blackberry.api.invoke.Invoke;
import net.rim.device.api.ui.MenuItem;

public class Library implements LibraryInterface {
public MenuItem[] getMenus() {
return new MenuItem[] { new InvokeAB() };
}

private static class InvokeAB extends MenuItem {
public InvokeAB() {
super("Run AddressBook", 0, 100);
}

public void run() {
try {
Invoke.invokeApplication(Invoke.APP_TYPE_ADDRESSBOOK, null);
} catch (Exception e) {
System.err.println(e.toString());
The application that loads the library consists of a single screen, which loads the library dynamically by the class name and calls getMenus() to add all implemented MenuItems. The key lines are as follows: }
}
}

}

The application that loads the library consists of a single screen, which loads the library dynamically by the class name and calls getMenus() to add all implemented MenuItems. The key lines are as follows:

 

try {
LibraryInterface library = (LibraryInterface)
(Class.forName("Library")).newInstance();
public MenuItem[] items = library.getMenus();
for (int i=0; i<items.length; i++) {
addMenuItem(items[i]);
}
} catch (Exception e) {
log("Library is not available");
}

The full code is as follows:

 

 

import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.BasicEditField;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.MenuItem;

public class TestHarness extends UiApplication {

public TestHarness() {
HarnessScreen screen = new HarnessScreen();
pushScreen(screen);
}

public static void main(String[] args) {
TestHarness app = new TestHarness();
app.enterEventDispatcher();
}

private static class HarnessScreen extends MainScreen {

private BasicEditField log = new BasicEditField("Log: ", "");
public HarnessScreen() {
setTitle("Test Screen");
add(log);

try {
LibraryInterface library = (LibraryInterface)
(Class.forName("Library")).newInstance();
MenuItem[] items = library.getMenus();
for (int i=0; i<items.length; i++) {
addMenuItem(items[i]);
}
} catch (Exception e) {
log("Library is not available");
}
}

public void log(String message) {
if (message != null) {
synchronized (UiApplication.getEventLock()) {
log.setText(log.getText() + "\n" + message);
}
}
}
}
}


 

Each of the 4.2.0 and 4.2.1 libraries duplicate the Address Book MenuItem provided by the 4.1.0 library and they add additional MenuItems to invoke other core BlackBerry smartphone applications. The following is the 4.2.0 library:

 

 

import net.rim.blackberry.api.invoke.Invoke;
import net.rim.device.api.ui.MenuItem;

public class Library implements LibraryInterface {
public MenuItem[] getMenus() {
return new MenuItem[] { new InvokeAB(), new InvokeMaps() };
}

private static class InvokeAB extends MenuItem {
public InvokeAB() {
super("Run AddressBook", 0, 100);
}

public void run() {
try {
Invoke.invokeApplication(Invoke.APP_TYPE_ADDRESSBOOK, null);
} catch (Exception e) {
System.err.println(e.toString());
}
}
}

public static class InvokeMaps extends MenuItem {
public InvokeMaps() {
super("Run Maps", 0, 100);
}

public void run() {
try {
Invoke.invokeApplication(Invoke.APP_TYPE_MAPS, null);
} catch (Exception e) {
System.err.println(e.toString());
}
}
}
}


 

The following is the 4.2.1 library with the complete set of MenuItems:

 

 

import net.rim.blackberry.api.invoke.Invoke;
import net.rim.device.api.ui.MenuItem;

public class Library implements LibraryInterface {
public MenuItem[] getMenus() {
return new MenuItem[] { new InvokeAB(), new InvokeMaps(),
new InvokeCalc() };
}

private static class InvokeAB extends MenuItem {
public InvokeAB() {
super("Run AddressBook", 0, 100);
}

public void run() {
try {
Invoke.invokeApplication(Invoke.APP_TYPE_ADDRESSBOOK, null);
} catch (Exception e) {
System.err.println(e.toString());
}
}
}

public static class InvokeMaps extends MenuItem {
public InvokeMaps() {
super("Run Maps", 0, 100);
}

public void run() {
try {
Invoke.invokeApplication(Invoke.APP_TYPE_MAPS, null);
} catch (Exception e) {
System.err.println(e.toString());
}
}
}

public static class InvokeCalc extends MenuItem {
public InvokeCalc() {
super("Run Calculator", 0, 100);
}

public void run() {
try {
Invoke.invokeApplication(Invoke.APP_TYPE_CALCULATOR, null);
} catch (Exception e) {
System.err.println(e.toString());
}
}
}
}

 

The duplication of the code between libraries is required because only a single library will be installed on a BlackBerry smartphone at a time. Therefore, this approach is more applicable for situations where alternate implementations will be used. The next approach removes the duplication of code by installing multiple libraries at once

 

Special Signing Requirements

 

When working with libraries and using dynamic class loading with Class.forName() and the newInstance() method, special care needs to be taken to ensure your main application is signed with the correct RIM signing keys. The reason for this is as follows;

 

When signatures get checked at run time, all modules in the call stack are checked to verify that they are signed with the key which is needed to allow a given call to be made. So if your main application is constructing instances of classes that are implemented within one of your library cod files, it is at the bottom of the call stack.

 

If the constructor of the class being dynamically loaded from your library cod file makes a call to a method which requires a signature, for example a call to Display.getWidth() then your application cod file must be signed with the key which enables this call (in the case of Display.getWidth() this is the "RRT" key).

 

The normal code signing process will ensure that the library cod file hosting the dynamically loaded class *is* signed with the necessary key because the signature tool is able to “see” directly that the call to Display.getWidth() is being made. But the main application cod file is using dynamic class loading and so the signature tool cannot deduce that ultimately this gives rise to a method call for which the RRT signature is required. The net of this is that the main application cod file will not have the RRT signature and this will cause the run time check to fail and an IllegalAccessException to be thrown in response to the call to Class.forName(...).

 

The solution to this issue is to sign the application cod file with RRT either by creating a csl file containing:

52525400=RIM Runtime API within your Eclipse project or simply by having a dummy call in your main application to a method which will cause the signature tool to apply the required signature, e.g. to Display.getWidth(). The latter is easier because the csl file gets deleted every time Eclipse builds your project and you otherwise have to reinstate it manually before signing.

.

Using libraries to support incremental functionality

 

In situations where the application needs to support increased functionality on later BlackBerry Device Software versions, but where the previous functionality is still valid, a slightly altered approach can be used with dynamically loaded libraries. For example, the application may implement some base features, while additional libraries add capability that works with the original application. The main application dynamically loads any libraries that are present, and the right collection of libraries is installed based on the BlackBerry Device Software of the BlackBerry smartphone. As before, the main application and interface library must be built in the earliest BlackBerry JDE version, while each library is built in the appropriate BlackBerry JDE version for the APIs accessed.

 

The following example is again based on the Invoke API and it does the same as the previous example, but it does not duplicate code in each library because multiple libraries are installed when supported by the BlackBerry Device Software. Besides the removal of duplicate code from each library, the only code change in this example is the way the libraries are loaded:

 

 

try {
LibraryInterface library420 = (LibraryInterface) (Class.forName("Library420")).newInstance();
MenuItem[] items = library420.getMenus();
for (int i=0; i<items.length; i++) {
addMenuItem(items[i]);
}
} catch (Exception e) {
log("4.2.0 Library is not available");
}
try {
LibraryInterface library421 = (LibraryInterface)
(Class.forName("Library421")).newInstance();
MenuItem[] items = library421.getMenus();
for (int i=0; i<items.length; i++) {
addMenuItem(items[i]);
}
} catch (Exception e) {
log("4.2.1 Library is not available");
}

Using the preprocessor to target new APIs from one source

A preprocessor is available in the BlackBerry JDE and in the BlackBerry Java Plug-in for Eclipse, which allows developers to include or exclude use of advanced APIs without separating into libraries or other source files. Using the preprocessor, the same source code is compiled into multiple builds configured with the proper preprocessor directives to control the code that is included. The CubeField example used above can be updated to support TouchEvents using the preprocessor as follows, creating a custom application that will support all three input methods.

 

First add the preprocessor support and make the import dependent on a preprocessor tag:

 

 

//#preprocess

//#ifdef 470Min
import net.rim.device.api.ui.TouchEvent;
import net.rim.device.api.ui.TouchGesture;
//#endif

 Override the touchEvent(TouchEvent) method on the CubeField class, conditionally based on the same preprocessor tag:

 

 

// #ifdef 470Min
protected boolean touchEvent(TouchEvent message) {
int eventCode = message.getEvent();

// Get the screen coordinates of the touch event
int touchX = message.getX(1);
int touchY = message.getY(1);

// code for handling various touch events
if (eventCode == TouchEvent.MOVE) {

moveCube(touchX - x, touchY - y);
return true;
} else if (eventCode == TouchEvent.GESTURE) {

TouchGesture gesture = message.getGesture();
int gestureCode = gesture.getEvent();

if (gestureCode == TouchGesture.TAP) {
moveCube(touchX - x, touchY - y);
return true;
}
}
return false; // we didn't handle this event

}
// #endif

To build with touch support, set the BlackBerry Java Plug-in for Eclipse to use the 4.7.0 component pack, and within our Project Properties, define the same tag used in the code:

 

DB-00770_01.jpg

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

This example application now has one source file, but it needs to be built three times to support the full range of BlackBerry smartphone models. BlackBerry smartphones running BlackBerry Device Software 4.7.0 or later are built with the Preprocessor tag defined and built using the 4.7.0 compiler. The application should also be built without the Preprocessor tag, with both the 4.1 and 4.2 compilers, following the instructions provided earlier in this article. For more information on using the preprocessor, see this article

 

Appendix A: Determining BlackBerry smartphone model and code level

 

While most of the examples in this article do not require programmatic access to the BlackBerry smartphone model type or BlackBerry Device Software version, this information may sometimes be necessary. There are useful methods for determining this data in the DeviceInfo class.

 

DeviceInfo.getName()

 

This method retrieves the current BlackBerry smartphone’s product name, with an optional one-letter suffix to distinguish the radio type. For example, BlackBerry 7130 smartphones that operate on General Packet Radio Service (GPRS) networks return 7130, while BlackBerry 7130 smartphones that operate on Code Division Multiple Access (CDMA) networks return 7130e and BlackBerry 7130 smartphones that operate on Universal Mobile Telecommunications System (UTMS) networks return 7130u.

 

DeviceInfo.getSoftwareVersion()

 

This method returns a string representing the BlackBerry Device Software version or an empty string if the BlackBerry Device Software version could not be retrieved. However, this method was introduced in BlackBerry JDE 4.3.0, so for earlier versions of BlackBerry Device Software you must use the approach described in this article This approach involves testing the version of standard BlackBerry smartphone application modules on the BlackBerry smartphone to determine the BlackBerry Device Software version.

 

Appendix B: Managing installation of multiple builds

 

With separate BlackBerry JDE builds and library modules comes the requirement to make sure that the correct modules are installed. Both desktop and wireless installations support methods for installing the correct modules based on BlackBerry Device Software version and BlackBerry smartphone model. For more information, see the Deploying Java Applications whitepaper.

 

Desktop installation

Installation using the application loader tool in BlackBerry® Desktop Manager can be configured to handle the entire set of application modules in a single package using the .alx file. The requires element can be used to force the installation of library files, while the fileset element can use additional attributes to specify separate installation files based on BlackBerry smartphone model, BlackBerry Device Software version, and wireless service provider. Examples using these attributes can be found i this article

 

Wireless installation

 

The .jad file that is used to install applications wirelessly does not support the same features as the .alx file for desktop use, but the browser reports the capabilities of the BlackBerry smartphone in the headers of the HTTPRequest. The server hosting the application can examine these headers for the necessary information. The User-Agent header provided by the browser is in the format: BlackBerry<BlackBerry-model>/<software-version>. For example, BlackBerry8800/4.2.1.

 

In addition to the User-Agent header, the profile header can also be used to access information about the capabilities of the BlackBerry smartphone. This header is actually a web address that the server can access for a Resource Description Framework (RDF) Extensible Markup Language (XML) document that fully describes the BlackBerry smartphone. An example of such a document can be found here:

 

http://www.blackberry.net/go/mobile/profiles/uaprof/8800/4.2.1.rdf

 

The web server hosting the application will be required to redirect the browser to a .jad file that includes the correct versions of the application code and additional libraries that should be installed. Separate .jad files will be required for each set of application and libraries.