Click here to Skip to main content
6,630,586 members and growing! (15,776 online)
Email Password   helpLost your password?
Platforms, Frameworks & Libraries » Mobile Development » Applications     Intermediate License: The Code Project Open License (CPOL)

End-to-End Real World BlackBerry Application, Part 5

By MiamiCoder

Real world BlackBerry application walkthrough, Part 5.
C#, JavaBlackberry, Dev
Posted:3 Sep 2008
Views:12,946
Bookmarked:11 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
6 votes for this article.
Popularity: 3.54 Rating: 4.56 out of 5

1

2
1 vote, 16.7%
3

4
5 votes, 83.3%
5

Introduction

This is the fifth part of my end-to-end BlackBerry application walk-through. In this article, I will go over the following subjects:

  • Using resource files for localizing text strings without coding changes
  • Setting an application icon on the Ribbon
  • Home Screen’s command list icons
  • Trimming the cached articles list
  • Encoding HTTP request data
  • Application “About” dialog
  • Alerting the user when a long-running operation is taking place

Using Resource Files for Localizing Text Strings

Although an in-depth discussion about resource files is beyond the scope of this article (a good place to see more information on this subject is the BlackBerry Java Development Environment Fundamentals Guide, which you can obtain from the BlackBerry developers site), I will tell you that each application needs a resource header file, a resource contents file for the root (global) locale, and a resource content file for each specific locale it will support.

The resource header file defines descriptive keys for each localized string. Resource content files map descriptive keys to values for the global and specific locales.

Resources for a given locale end up being stored in a ResourceBundle object. A ResourceBundleFamily object groups the resources for an application as a collection of ResourceBundle objects. This is the mechanism an application uses to switch languages depending on the user's locale.

For our application, I created the resource header file (KnowledgeBase.rrh) and a resource contents file for the root locale (KnowledgeBase.rrc). Based on these files, the BlackBerry IDE will create, at compile time, a resource interface, KnowledgeBaseResource, that we can use to gain access to the localized strings. The resource interface has the same name as the resource header file, with the word Resource appended.

To access our localized resources through the application’s resource bundle, we first need to obtain a reference to our resource bundle:

private ResourceBundle resources = 
         ResourceBundle.getBundle(KnowledgeBaseResource.BUNDLE_ID,
         KnowledgeBaseResource.BUNDLE_NAME);

Now, we’re ready to retrieve a localized string, like so:

String screenTitle = resources.getString(KnowledgeBaseResource.TTL_HOME_SCRN);

Like I said at the beginning of this section, there’s much more to resources than what I’ve covered here. You should plan on learning more, and I would suggest you start by checking out the BlackBerry Java Development Environment Fundamentals Guide, available on the BlackBerry developers site.

Setting an Application Icon

It’s time now to replace the default application icon with something nicer. To set the application icon through the BlackBerry JDE, add the image that will serve as the application icon to the Project and bring up its properties:

Setting-App-Icon.gif

In the File Properties applet, select “Use as application icon”:

Setting-App-Icon2.gif

And, when we run the application, we see the new application icon:

App-Icon-Ribbon.jpg

Note that although this procedure allows you to set the application icon, it does not allow you to change the image when the application is selected or unselected within the Ribbon. I will describe how to achieve this behavior in a future article.

Home Screen’s Command List Icons

Originally, the three commands on the Home Screen were preceded by the same icon, a green star:

Home-Screen1.gif

I’ve made some changes to the Home Screen so that now, each command is preceded by an icon that reflects the command’s function:

Home-Screen-Icons.jpg

I achieved this by modifying the code in the MyObjectListField class within the Home Screen. In a nutshell, MyObjectListField now has three private bitmaps, one per command, and when it’s time to draw the list row, the appropriate bitmap is drawn before the command. This is how the code looks:

private class MyObjectListField extends ObjectListField {
        
    private Bitmap searchIcon = 
      Bitmap.getBitmapResource(resources.getString(
      KnowledgeBaseResource.IMG_SEARCH));  
    private Bitmap tagsIcon = 
      Bitmap.getBitmapResource(resources.getString(
      KnowledgeBaseResource.IMG_TAGS_LIST)); 
    private Bitmap recentIcon = 
      Bitmap.getBitmapResource(resources.getString(
      KnowledgeBaseResource.IMG_VIEW_RECENT)); 
    private Bitmap icon;

    // We are going to take care of drawing the item.
    public void drawListRow(ListField listField, Graphics graphics, 
            int index, int y, int width) {
                
        if ( mainMenuItems[index] == mnuSearchTitles) {
            icon = searchIcon;
        }   
        if ( mainMenuItems[index] == mnuBrowseTags) {
            icon = tagsIcon;
        }  
        if ( mainMenuItems[index] == mnuViewRecent) {
            icon = recentIcon;
        }

        if (null != icon) {
            int offsetY = (this.getRowHeight() - icon.getHeight())/2;
            graphics.drawBitmap(1,y + offsetY, icon.getWidth(),
                    icon.getHeight(),icon,0,0);
            graphics.drawText(mainMenuItems[index], 
                icon.getWidth() + 2, y, DrawStyle.ELLIPSIS, 
                width - icon.getWidth() + 2);
        } else {
            graphics.drawText("- " + mainMenuItems[index], 0,
                y, DrawStyle.ELLIPSIS, width - 
                graphics.getFont().getAdvance("- "));
        }
    }
}

Trimming the Cached Articles List

Based on the requirements I set, our application should maintain an in-memory cache with the most recently viewed articles.

The application’s DataStore class is already capable of handling the storage and retrieval of the cached articles via the setCachedArticles() and getCachedArticles() methods. We also have a way for the user to set the maximum number of cached articles there can be.

What we’re missing is the mechanism to add such an article to the cache (once a user opens an article) and prune the cache so the number of articles does not exceed the maximum the user defined. A good place to implement such a mechanism is the Article Screen. Let’s add some code that will take care of these possible scenarios:

  1. The cache is empty
  2. The cache has some articles, but it still has room
  3. The cache is full
  4. The article we’re viewing is already cached

When the cache is empty, we only need to add the currently viewed article:

// Handle the case when there's nothing cached.
if (null == cachedItems || cachedItems.length == 0) {
            
    cachedItems = new Article[]{article};
    DataStore.setCachedArticles(cachedItems);
    return;
            
}

When the cache is not empty but still has room, we only need to insert the current article at the top of the list:

// Handle the case when we need to add one more article to the list.
Article[] newList = new Article[cachedItems.length + 1];
                
// Insert the old items in the new list.
for (int i = 1; i <= cachedItems.length; i++) {
                     
 // We're shifting the articles too, to leave room at the top of the list
 // for the article we're currently viewing.
 newList[i] = cachedItems[i - 1];
                     
}
                
// Now, move the article we're viewing to the top of the list.
newList[0] = article;
                
DataStore.setCachedArticles(newList);

When the cache is full, we have to make room for the current article:

// Handle the case when the cached need to be trimmed.
Article[] newList = new Article[maxAllowed];
// Insert all the items, except the oldest, in the new list.
for (int i = 1; i < maxAllowed; i++) {
                    
 // We're shifting the articles too, to leave room at the top of the list
 // for the article we're currently viewing.
 newList[i] = cachedItems[i - 1];
                    
} 
                
// Now, move the article we're viewing to the top of the list.
newList[0] = article;
                
DataStore.setCachedArticles(newList);

When the article we’re viewing is already cached, we just bail out:

// Bail if the article we're viewing is already cached.
for (int i = 0; i < cachedItems.length; i++) {                
 if (article.id.equalsIgnoreCase(cachedItems[i].id)) {
  return;
 }
}

Looks like we took care of all the scenarios, doesn’t it? Well, not quite yet. What happens if the cache has N number of articles and the user, through the Options Screen, changes the maximum number of articles the cache can store to a number less than N? To handle this situation, we need to add some code to the Options Screen:

private void trimCachedArticlesList() {
        
    Article[] cached = DataStore.getCachedArticles();
    
    if (null == cached || cached.length == 0) {
        return;
    }
    
    int maxAllowed = DataStore.getMaxCachedArticles();
    
    if (cached.length &lt;= maxAllowed) {
        return;
    }
    
    Article[] newList = new Article[maxAllowed];
    
    for (int i = 0; i < maxAllowed; i++) {
        newList[i] = cached[i];
    }
    
    DataStore.setCachedArticles(newList);

}

And now, I think we’re in good shape. If I forgot some detail, I’m sure you will let me know. :)

Encoding HTTP Request Data

On the subject of forgetting details, back when I added the HTTP handling code, the routine that took care of creating the HTTP request data looked like this:

public static String createRequestString(final String[] keys, final String[] values) {
        
    StringBuffer requestContents = new StringBuffer(&quot;&quot;);
    if (keys != null) {               
          for (int i = 0; i < keys.length; i++) {
             requestContents.append(keys[i] + "=" +  values[i] + &quot;&amp;&quot;);
          }
    }
    // Terminate the request with a valid sequence.
    requestContents.append("0=0\r\n";);    
       
    return requestContents.toString();
}

Everything seems fine with this code until you start wondering what would happen if any actual values (any of the values[i]) in the request data contained reserved characters like ";", "/", "?", ":", "=", or "&". And, it can become as serious as it became for me last week when a production application started “randomly” failing because of this encoding issue.

What we need here is to make sure that our request data is correctly encoded so our server-side application can make sense of the request.

Working to fix my last week’s emergency, I stumbled upon net.rim.blackberry.api.browser.URLEncodedPostData. URLEncodedPostData is precisely a class that encodes form data using URL-encoding. Let’s use it to take care of our encoding issues:

public static byte[] createPostData(final String[] keys, final String[] values) {

    URLEncodedPostData postData = 
      new URLEncodedPostData(URLEncodedPostData.DEFAULT_CHARSET, true);
    
    if (keys != null) {
         for (int i = 0; i < keys.length; i++) {
             postData.append( keys[i], values[i]);
         }
     }

     return postData.getBytes();
}

Application “About” Dialog

I thought of the “About” dialog as a nice touch for our application. It is triggered by the “About” menu that I just added to the Home Screen.

About-Dialog.jpg

Of interest here is the use of ApplicationDescriptor to obtain the application name and the version number:

private MenuItem aboutMenu = 
     new MenuItem(resources.getString(KnowledgeBaseResource.MNU_ABOUT),150,10) {
   public void run() {
       String crNotice = resources.getString(KnowledgeBaseResource.LBL_ABOUT);
       ApplicationDescriptor descriptor = 
              ApplicationDescriptor.currentApplicationDescriptor();
       String appVersion = descriptor.getName() + " " + descriptor.getVersion();
       Dialog.alert(appVersion + "\n" + crNotice);
   } 
};

Alerting Users When Long-Running Operations Take Place

Some of the features of our application, like searching for articles, can potentially involve long execution times. It’s important that we don’t frustrate our users by starting a long-running operation without giving them some visual clue indicating that our program is actually working and they might need to wait some time for an operation to complete.

Some developers prefer to use screens as status indicators, but a simple dialog does just fine.

Downloading-Dialog.jpg

This is how it’s done:

Bitmap icon = Bitmap.getBitmapResource(
              resources.getString(KnowledgeBaseResource.IMG_DOWNLOAD)); 
statusDlg = new Dialog(resources.getString(
                KnowledgeBaseResource.LBL_DOWNLOADING),null,null,0,icon); 
private void sendHttpRequest(String[] keys, String[] values) {
        
   String url = DataStore.getAppServerUrl();
   // Make sure we can create an http request.
   // If the app. server URL has not been set, we cannot make any http requests.
    if (null == url || url.length() == 0) {
        Dialog.alert(resources.getString(
               KnowledgeBaseResource.ERR_INVALID_APP_SERVER_URL));
        return;
    }
    
    statusDlg.show();
 .
 .
 .
}
public void processResponse(final int HTTPResponseCode, final String data) {
    UiApplication.getUiApplication().invokeLater(new Runnable() {
        public void run()
        {
            statusDlg.close();
 .
 .
 .
}

What's Next

Having covered the above important details, we still need to write the server-side code to handle incoming HTTP requests, retrieve information from the database, and send responses back to the application on the BlackBerry device.

History

Here are the links to the previous articles of this series:

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

MiamiCoder


Member
I run a small development team for an international Law Firm.
Occupation: Team Leader
Location: United States United States

Other popular Mobile Development articles:

  • Writing Your Own GPS Applications: Part 2
    In part two of the series, the author of "GPS.NET" teaches developers how to write GPS applications suitable for the real world by mastering GPS precision concepts. Source code includes a working NMEA interpreter and sample high-precision application in C# and VB.NET.
  • Writing Your Own GPS Applications: Part I
    What is it that GPS applications need to be good enough to use for in-car navigation? Also, how does the process of interpreting GPS data actually work? In this three-part series, I will cover both topics and give you the skills you need to write a commercial-grade GPS application.
  • Learn How to Find GPS Location on Any SmartPhone, and Then Make it Relevant
    A step by step tutorial for getting GPS from any SmartPhone, even without GPS built in, and then making location useful.
  • iPhone UI in Windows Mobile
    It's an interface that works with transparency effects. As a sample I used an interface just like the iPhone one. In this tutorial I am explaining how simple is working with transparency on Windows Mobile.
  • Pocket 1945 - A C# .NET CF Shooter
    An article on Pocket PC game development
Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 5 of 5 (Total in Forum: 5) (Refresh)FirstPrevNext
GeneralVery helpful series! Pinmembertracytranngo13:22 6 Oct '09  
GeneralThank You very much for your great article PinmemberMember 385676121:47 23 Jul '09  
GeneralServer side PinmemberBESExpress11:55 14 Apr '09  
GeneralResizing icons Pinmemberjacytan19:39 15 Feb '09  
GeneralRe: Resizing icons PinmemberMiamiCoder14:21 16 Feb '09  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 3 Sep 2008
Editor: Smitha Vijayan
Copyright 2008 by MiamiCoder
Everything else Copyright © CodeProject, 1999-2009
Web22 | Advertise on the Code Project