Home

Stratos Kalogirou edited this page Nov 26, 2013 · 7 revisions
Clone this wiki locally

This is a demo WordPress blog administration client, modeled after the Nokia WordPress client. The application is optimised for the Nokia C3-00, which has a QWERTY keyboard and a wide screen.

imagePlaceholder  imagePlaceholder

This application demonstrates the creation and usage of a canvas-based user interface with text input capabilities. It also shows how to do XML server communication using HTTP. This article focuses on the text input part of the application.

Administering WordPress blogs requires a username and a password for access. The user may then read blog posts and comments, write posts and comments, and delete comments. The UI is custom made so that the available screen can be used optimally. This also makes it possible to use some animations and effects. Text input is done using the canvas-based TextEditor. Note that this API is available in Series 40 6th Edition SDK, FP1.

Prerequisites

You need the following to develop and test this MIDlet:

  • Eclipse Pulsar or NetBeans with Java ME support
  • Series 40 6th Edition SDK, Feature Pack 1
  • Series 40 6th Edition FP1 device or newer

For instructions on how to set up the Java ME development environment, see section Setting up the development environment

For more information about the MIDlet, see:

  • Design for details about the design and functionality of the MIDlet
  • Implementation for instructions on how to implement the classes that make up the MIDlet

You can download the project files for the MIDlet from the download page.

Design

The application consists of various views where different actions can be performed. The picture below depicts all the views and transitions between them.

imagePlaceholder

Splash view

The first view is the splash view, showing the splash graphics for a short while.

imagePlaceholder

Login view

The application will ask for a URL to the user's blog, a username, and a password. The URL is of the format http://blogname.wordpress.com/xmlrpc.php, where 'blogname' should be replaced with the correct name.

imagePlaceholder

Figure: Login screen with canvas !TextEditor-controls

Loader view

After login, blogs, posts and comments are loaded from the server. Loading progress is shown in this view.

imagePlaceholder

Figure: Loader view

Posts view

After a successful login, the posts view is displayed. The blog post titles are displayed here, newest first. A number on the right marks the number of comments written for the corresponding post. There is a tab bar at the top indicating that the posts view is active.

A new post can be written by selecting New post from the menu. This opens the new post view for editing the post title and content.

By clicking Open in the posts view, the selected post will be displayed using the single post view. From this view, the user can see the post comments and write a new comment for the selected post. Comments can also be deleted from the menu. When writing a new comment, the new comment view is used.

imagePlaceholder

Figure: Posts view

Comments view

Using the left and right direction keys, it's possible to switch between the posts and comments view, as indicated by the tab bar on top. The comments view displays all comments from all posts, newest first. Each row contains two lines: the first line displays the title of the post that the comment belongs to, and the second line displays the start of the comment content. Comments can be opened for displaying, and deleted.

Figure: Comments view

Comments for post view

This view displays all comments for a single post. The user can delete comments or create a new comment.

Figure: Comments for post view

Single post view

This view displays a single post. The user can comment on this post or view the existing comments.

Figure: Single post view.

Single comment view

This view displays a single comment. The user can delete the comment from the options menu.

Figure: Single comment view

New post view

Here the user can write a new post. !TextEditor is used for text input.

Figure: New post view

New comment view

A new comment to a post can be written here. !TextEditor is used for text input.

Figure: New comment view

Menu

Pop up menu will dim the screen and display on top, allowing the user to select one of the menu options.

Figure: Menu visible on top of the posts view

Implementation

The UI consists of custom Canvas-based views managed by the ViewMaster class. Each application view is handled by view classes inherited from !BaseView. !ViewMaster performs view switches and is able to do smooth view transition animations. Each view handles user input and decides which view to activate next in the work flow. The OptionsMenu class implements the custom menu control. Different menus are implemented in views by extending this class. Figure: Main application structure

PostsView and CommentsView use a custom list component class, List. It is able to draw a list on the screen with focus highlight and a scrollbar. An instance of ListDrawer is provided for the List for doing the actual drawing. The drawing needs differ as the other view displays posts and the other comments.

Figure: Views with the custom list component

Using the canvas TextEditor

LoginView, NewPostView and NewCommentView utilize the canvas text editor control, which provides full text editing features and customizable appearance. This is very useful for canvas based applications with a custom user interface.

imagePlaceholder

For more info, see TextEditor.

Below is the constructor for the LoginView constructor. It will set up three text editors for getting the login information. The class implements TextEditorListener to get events from the editors. When a text editor is created, various visual settings are done. The most important one is the setParent() call. This makes the editor appear on the canvas and therefore the screen. In this case, the ViewMaster represents the canvas.

Finally, an options menu with an exit option is added, as well as labels for the three soft keys.

/**
 * View for logging in.
 * @author user
 */
public class LoginView extends BaseView implements TextEditorListener {

    private TextEditor usernameEditor;
    private TextEditor passwordEditor;
    private TextEditor blogUrlEditor;
    private Font font = Visual.SMALL_FONT;
    private int fontHeight = font.getHeight();

    LoginView(Graphics g, int x, int y, int width, int height) {
        super(g, ViewMaster.VIEW_LOGIN, x, y, width, height);
        haveTabs = false;

        int border = 2;
        int editorWidth = width - 2 * border;

        // Reserve three rows at the top for text
        int yPosition = fontHeight * 3;

        usernameEditor = TextEditor.createTextEditor(32, TextField.ANY, editorWidth, 1);
        usernameEditor.setFont(font);
        usernameEditor.setBackgroundColor(Visual.EDITOR_ACTIVE_BACKGROUND_COLOR);
        usernameEditor.setForegroundColor(Visual.EDITOR_ACTIVE_FOREGROUND_COLOR);
        usernameEditor.setContent(data.getLoginUsername());
        usernameEditor.setParent(viewMaster);
        usernameEditor.setPosition(border, yPosition);
        usernameEditor.setVisible(false);
        usernameEditor.setTextEditorListener(this);

        // Reserve some space for text for draw()
        yPosition += fontHeight + usernameEditor.getHeight();

        passwordEditor = TextEditor.createTextEditor(32, TextField.ANY, editorWidth, 1);
        passwordEditor.setFont(font);
        passwordEditor.setBackgroundColor(Visual.EDITOR_PASSIVE_BACKGROUND_COLOR);
        passwordEditor.setForegroundColor(Visual.EDITOR_PASSIVE_FOREGROUND_COLOR);
        passwordEditor.setContent(data.getLoginPassword());
        passwordEditor.setParent(viewMaster);
        passwordEditor.setPosition(border, yPosition);
        passwordEditor.setVisible(false);
        passwordEditor.setTextEditorListener(this);

        yPosition += fontHeight + usernameEditor.getHeight();

        blogUrlEditor = TextEditor.createTextEditor(200, TextField.ANY, editorWidth, 3);
        blogUrlEditor.setFont(font);
        blogUrlEditor.setBackgroundColor(Visual.EDITOR_PASSIVE_BACKGROUND_COLOR);
        blogUrlEditor.setForegroundColor(Visual.EDITOR_PASSIVE_FOREGROUND_COLOR);
        blogUrlEditor.setContent(data.getBlogUrl());
        blogUrlEditor.setParent(viewMaster);
        blogUrlEditor.setPosition(border, yPosition);
        blogUrlEditor.setVisible(false);
        blogUrlEditor.setTextEditorListener(this);

        // Create options menu
        menu = new OptionsMenu(width, height) {
            {
                addExitItem();
            }
        };

        softkey1Label = "Options";
        softkey2Label = "Login";
        softkey3Label = "Exit";
    }

When the LoginView becomes active, the text editors are made visible and the first one gets the focus, receiving the subsequent key events. This is done in the activate() method:

    public void activate() {
        System.out.println("login activate");
        super.activate();

        usernameEditor.setVisible(true);
        usernameEditor.setFocus(true);
        passwordEditor.setVisible(true);
        passwordEditor.setFocus(false);
        blogUrlEditor.setVisible(true);
        blogUrlEditor.setFocus(false);
    }

TextEditorListener provides method inputAction() which is implemented as follows in the LoginView. The purpose here is to determine which of the three editors receives focus by checking for actions ACTION_TRAVERSE_NEXT or ACTION_TRAVERSE_PREVIOUS. When this is the case, the correct editor receives focus and the others lose it. Similarly, editor background colors are modified to indicate which editor is active to the user.

    public void inputAction(TextEditor editor, int actions) {
        // Set focus to a correct editor.
        if ((actions & TextEditorListener.ACTION_TRAVERSE_NEXT) != 0) {
            if (editor == usernameEditor) {
                usernameEditor.setFocus(false);
                passwordEditor.setFocus(true);
            }
            if (editor == passwordEditor) {
                passwordEditor.setFocus(false);
                blogUrlEditor.setFocus(true);
            }
        }
        if ((actions & TextEditorListener.ACTION_TRAVERSE_PREVIOUS) != 0) {
            if (editor == blogUrlEditor) {
                passwordEditor.setFocus(true);
                blogUrlEditor.setFocus(false);
            }
            if (editor == passwordEditor) {
                usernameEditor.setFocus(true);
                passwordEditor.setFocus(false);
            }
        }

        // Set colors to indicate active and passives states.
        if (usernameEditor.hasFocus()) {
            usernameEditor.setBackgroundColor(Visual.EDITOR_ACTIVE_BACKGROUND_COLOR);
            usernameEditor.setForegroundColor(Visual.EDITOR_ACTIVE_FOREGROUND_COLOR);
        } else {
            usernameEditor.setBackgroundColor(Visual.EDITOR_PASSIVE_BACKGROUND_COLOR);
            usernameEditor.setForegroundColor(Visual.EDITOR_PASSIVE_FOREGROUND_COLOR);
        }
        if (passwordEditor.hasFocus()) {
            passwordEditor.setBackgroundColor(Visual.EDITOR_ACTIVE_BACKGROUND_COLOR);
            passwordEditor.setForegroundColor(Visual.EDITOR_ACTIVE_FOREGROUND_COLOR);
        } else {
            passwordEditor.setBackgroundColor(Visual.EDITOR_PASSIVE_BACKGROUND_COLOR);
            passwordEditor.setForegroundColor(Visual.EDITOR_PASSIVE_FOREGROUND_COLOR);
        }
        if (blogUrlEditor.hasFocus()) {
            blogUrlEditor.setBackgroundColor(Visual.EDITOR_ACTIVE_BACKGROUND_COLOR);
            blogUrlEditor.setForegroundColor(Visual.EDITOR_ACTIVE_FOREGROUND_COLOR);
        } else {
            blogUrlEditor.setBackgroundColor(Visual.EDITOR_PASSIVE_BACKGROUND_COLOR);
            blogUrlEditor.setForegroundColor(Visual.EDITOR_PASSIVE_FOREGROUND_COLOR);
        }
    }

When the user has entered the required information, a middle key button press is checked in keyReleased() method. Editor contents are read with getContent() and passed to application data object. Then it's just a matter of moving to the login phase, which will be handled by LoaderView.

    public void keyReleased(int keyCode) {

        if (menu != null && menu.notifyKeyEvents(keyCode)) {
            // menu used this keycode, let's not use for anything else.
            viewMaster.draw();
            return;
        }

        switch (keyCode) {
            case KeyCodes.MIDDLE_SOFTKEY:
                data.setLoginUsername(usernameEditor.getContent());
                data.setLoginPassword(passwordEditor.getContent());
                data.setBlogUrl(blogUrlEditor.getContent());
                viewMaster.setView(ViewMaster.VIEW_LOADER);
                break;
            case KeyCodes.RIGHT_SOFTKEY:
                midlet.commandExit();
                break;
            default:
                break;

        }
    }

Networking and data

The application communicates with the !WordPress servers by sending and receiving XML-messages with HTTP POST. The XML-RPC API documentation can be found here: http://codex.wordpress.org/XML-RPC_wp

Operations are encapsulated in their own classes, such as NewPostOperation and DeleteCommentOperation. When invoking the start() function of an operation, a new thread is started, an HttpConnection is created, and an HTTP POST is sent using a formatted XML request. After a response is received, it is parsed using the SAX parser from org.xml.sax. When all pertinent data has been parsed, a listener method is called. The UI application has implemented these, and can react and update the UI.

imagePlaceholder

Figure: Classes for handling network operations. Only one operation is depicted here: GetRecentPostsOperation. The other operations follow the same structure.

The blog, post, and comment data are represented using classes, and stored in Vectors in class DataModel.

Figure: !DataModel class contains all the application data.

Conclusion

The QWERTY keyboard eases the use of applications that depend on entering large amounts of text. From the developers' point of view, canvas QWERTY input support requires no extra effort. The canvas-based text editor is easy to use.

A graphically intensive UI requires a fair amount of work due to the nature of Canvas. On the other hand, the performance is quite good. In this application, a small UI framework was implemented to handle the various views.