Advertisement

Use jQuery Mobile to Build a Native Android News Reader App: Part 3

by

In Part 1 of this tutorial series, we introduced our sample application, described the page flow, and discussed how to construct the pages in the application via jQuery Mobile. In Part 2, we completed the implementation of our web application. In this final part, we will migrate the web application into a native Android application.


Converting Into A Native Android Application

The web application completed in Part 2 will now be converted into a native Android application. The discussion below applies to Android OS 2.2 & 2.3.

The Android application will use index.html as its UI component. We will write an android.app.Activity class to define the integration point between index.html and the native application. We will also write an android.webkit.WebViewClient class to make sure that the News Detail page is displayed inside the original android.webkit.WebView instance where the News application is launched.

Changes In index.html

We will update the NEWS_URI variable as follows:

var NEWS_URI = 'http://rss.news.yahoo.com/rss/';

We do not need bridge.php in the native Android application to forward AJAX calls to Yahoo! News. This is because the same-origin restriction does not apply here. When packaged as part of the native application, the index.html file is not downloaded from a web server. As such, it can make AJAX calls to remote URLs.

In addition, we add the following function:

var EMPTY = '';
...
function changeLocation(varURI){
  showProgress();	
  $.get(EMPTY,function(data){
    window.location = varURI;
  });	
}

The changeLocation() function will be called from the android.webkit.WebViewClient, which will be shown momentarily. The purpose of the function is to show the progress page during transition from the News page to the News Detail page.

  • The first step in changeLocation() is to display the progress page.
  • Remember that the jQuery get() function is a specialized jQuery ajax() function. We call get() passing to it an empty URL and a callback handler, that sets the window.location variable to the input argument. The input argument is the URL in the <a href='...'> attribute enclosed within an a tag for a news item, as discussed in Part 2, "Going To The News Detail Page From The News Page". When the URL loads, the progress page is replaced with contents from that URL.
  • As we point out below, the function changeLocation() is not an essential part of migrating the web application into a native one. It is only needed to display a progress page when transitioning from the News page to the News Detail page in the native application.
  • A progress page is not needed in the web application when transitioning from the News page to the News Detail page. This is because during the transition the web browser itself displays a progress indicator to the user. For example, in Android, both the native and Dolphin browsers display a spinning wheel and a progress bar in the navigation toolbar. In iOS, the Safari browser displays a similar progress indicator.

The Activity Class

The initial portion of our Activity class, named NewsActivity is shown below:

package com.news;

import android.app.Activity;
import android.webkit.WebView;
import android.os.Bundle;
...
public class NewsActivity extends Activity {
	WebView mWebView;

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    mWebView = (WebView) findViewById(R.id.webview);
    mWebView.setWebViewClient(new NewsClient());
    mWebView.getSettings().setJavaScriptEnabled(true);
    mWebView.getSettings().setDomStorageEnabled(true);
    mWebView.loadUrl("android_asset/www/index.html");
  }
  ...
}
  • In the onCreate() method, we first call the default implementation from the super class and then invoke setContentView() to load the layout file for this Activity. The input argument to setContentView() is R.layout.main which is a reference to main.xml in the res/layout folder.
  • We get a handle to the WebView via findViewById(R.id.webview). We set a custom WebViewClient on the WebView, named NewsClient (to be reviewed soon). Then, we configure the WebView to allow JavaScript execution and the DOM storage API (the latter is necessary to use HTML5 localStorage).
  • Finally, we ask the WebView to load the index.html page that has the UI code.

On the News Detail page, pressing the back button of the device will take the user back to the Categories page. To be assured of that, we first need to handle the onKeyDown event in our NewsActivity. This is shown below:

 public class NewsActivity extends Activity {
	WebView mWebView;

  public void onCreate(Bundle savedInstanceState) {
    ...
  }
  
  public boolean onKeyDown(int keyCode, KeyEvent event) {
    if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
      mWebView.goBack();            
      return true;
    }
    return super.onKeyDown(keyCode, event);
  }  
  ...
}

If the key event corresponds to the back button of the device and the WebView has history to go back to, we then ask the WebView to go back a single step in its history. In the News Detail page, this will correspond to index.html. When history goes one step back, the Categories page will be displayed following the steps described in Part 2, "Application Startup".

Lastly, let us look at the custom WebViewClient which is implemented as an inner class of NewsActivity.

 public class NewsActivity extends Activity {
	WebView mWebView;

  public void onCreate(Bundle savedInstanceState) {
    ...
	mWebView.setWebViewClient(new NewsClient());
	...
  }
  
  public boolean onKeyDown(int keyCode, KeyEvent event) {
    ...
  }  
  
  private class NewsClient extends WebViewClient {

    public boolean shouldOverrideUrlLoading(WebView view, String url) {
      view.loadUrl("javascript:changeLocation('" + url + "')");
      return true;
    }
  }
...
}

The only operation we override from the parent class is shouldOverrideUrlLoading() where we instruct the WebView to call the JavaScript function changeLocation() in index.html.

  • Had we not defined a custom WebViewClient, the News Detail page would be displayed in a separate browser application, outside the News application. Therefore, defining a custom WebViewClient is essential to display the News Detail page as part of the News application (i.e. in the same WebView that hosts the index.html).
  • We could have written shouldOverrideUrlLoading() in a more simplified manner, as follows:
    public boolean shouldOverrideUrlLoading(WebView view, String url)
    {
        view.loadUrl(url);
        return true;
    }

    That would be sufficient to display the News Detail page in the same WebView that hosts index.html. However, the transition from the News page to the News Detail page would not include showing the progress page.

Having reviewed the Activity class, let us look at other components of our application.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.news" android:versionCode="1" android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
      <activity android:name=".NewsActivity" android:configChanges="orientation|keyboardHidden"
        android:label="@string/app_name">
          <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
      </activity>
    </application>
    <uses-permission android:name="android.permission.INTERNET" />
</manifest>

For a general discussion on the AndroidManifest.xml file refer to the official reference. In that file, there are two particular items worthy of commenting on.

  • As described in the android.app.Activity documentation, by default, a configuration change, including a change in orientation or keyboard accessibility, results in the current activity being destroyed. To prevent the default behavior, we configure our application by specifying the configuration changes that will be handled by the application itself. This is defined in the configChanges attribute where orientation corresponds to orientation change and keyboardHidden corresponds to a keyboard accessibility change (e.g. a user lays open the device keyboard). We are configuring the application so that if any of those changes occur, the current activity is not destroyed.
  • The element <uses-permission android:name="android.permission.INTERNET" /> allows the application to access the Internet.

strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="app_name">News</string>
</resources>

This file defines the constant named app_name which is used to identify the News application. The value of that attribute is displayed in various places in our Android device, as shown below. From left to right: under the application launch icon, the application title bar, Settings - Manage applications.

Application name

Figure 11. How the application name is displayed in Android.

Integrating The HTML-based UI With android.app.Activity

We implement the UI in our native Android application using index.html and the supporting JavaScript and css libraries. An integration point between index.html and the custom android.app.Activity class is the following line:

  mWebView.loadUrl("android_asset/www/index.html");

In addition, observe in "The Activity Class" that we enable JavaScript and DOMStorage in the android.webkit.WebView object as index.html needs to run JavaScript and access HTML5 localStorage.

  mWebView.getSettings().setJavaScriptEnabled(true);
  mWebView.getSettings().setDomStorageEnabled(true);

Finally, in the AndroidManifest.xml we allow Internet connections from our application via:

<uses-permission android:name="android.permission.INTERNET" />

Screen Images of the Android Application

The previously shown Figures 1 - 4 in Part 1 of this series are the screen images of the native Android application.


Application Launch Icon

To create the launch icon for the News application, we followed the icon design guidelines for Android applications. In that resource, various templates in Adobe PSD format are available for download. We downloaded icon_templates-v2.0.zip and extracted Launcher-icon-template.psd. In that file, we selected two templates to create our launch icon:

icon 1 icon 2

We placed those templates in two separate layers in Adobe Photoshop and added a graphical text, News, on an additional layer at the top to compose the launch icon. Per the guideline, we created three versions of the icon for low, medium and high density screens with 36 x 36, 48 x 48, and 72 x 72 pixel sizes, respectively. Each of those icons is named icon.png and they are placed in the Android project folders according to the following table:

Folder name File name Pixel size
res\drawable-ldpi icon.png 36 x 36
res\drawable-mdpi icon.png 48 x 48
res\drawable-hdpi icon.png 72 x 72

As an example, this is the 36 x 36 launch icon:

icon ldpi


Native Android Application Development Environment

As part of this tutorial, we provided the necessary project files to import the native Android News application into Eclipse development environment. The project prerequisites are:

The project has been successfully tested against both Android platform 2.2 API level 8 and platform 2.3 API level 9.

Importing The Project

Before importing the project into your Eclipse environment, make sure that Eclipse ADT plugin points to the correct location of Android SDK in your local system. To check this, in the Eclipse menu go to Window -> Preferences -> Android. The SDK Location window must be set to the location of the Android SDK. Once set up correctly, you should see something similar to the image below:

Preferences

Figure 12. Eclipse Preferences.

The project files are provided in an archive file named news.zip. To import the project in the Eclipse menu, go to File -> Import and then in the file import wizard select General -> Existing Projects into Workspace (see below).

Import

Figure 13. Project Import.

On the next page of the wizard, choose the Select archive file radio button and browse to where news.zip is located in your file system. The Projects window will be automatically populated where the News project is already selected. This is shown below. Press the Finish button to complete the import.

Project File Selection

Figure 14. Project File Selection.

Eclipse will build the application automatically after import. Now, you should see the News project in the project explorer, as shown below:

Project Explorer

Figure 15. Project Explorer.

For building/debugging your project, you can choose between Android OS 2.3 and 2.2 platforms as the build target. To do this, select the News project in the project explorer and from the right-click menu choose Properties. On the left hand side listing of properties, select Android as the property. The available build targets are displayed on the right, as shown below:

Android Build Target

Figure 16. Android Build Target.

File Listing

A listing of files in the project is given below.

Project Contents

Figure 17. Project Contents.

We have already discussed some of those files. Below is a quick review/recap:

  • The src folder contains the source code for the NewsActivity class.
  • The gen folder contains the files automatically generated by Eclipse ADT.
  • The assets\www folder and its subfolders contain all the files needed for the UI, including index.html; assets\www\css-js has the css and JavaScript files used by index.html. In particular:
    • jquery-1.4.4.min.js, jquery.mobile-1.0a2.min.js, jquery.mobile-1.0a2.min.css are the jQuery Mobile framework libraries.
    • jquery.ba-dotimeout.js is the jquery-dotimeout-plugin library.
    • jquery.dst.js is the DST.js plugin library.
    • assets\www\css-js\images\icons-18-white.png is an image file referenced by the jQuery Mobile framework libraries.
  • assets\www\img\wait.gif is the spinning icon used in progress page.
  • The res\drawable* folders store the launch icons, as discussed in the "Application Launch Icon" section of this tutorial.
  • The res\layout\main.xml file is the Android XML layout file. Since the UI in our application is defined in index.html using jQuery Mobile framework, this file is very simple and needs no further explanation.
  • We have already reviewed res\values\strings.xml and AndroidManifest.xml.
  • The file default.properties defines the build target and is part of news.zip. It will be overwritten by Eclipse ADT depending on your selection of the build target.

Conclusions

In addition to developing cross-platform mobile web applications, the jQuery Mobile framework can be used to implement native Android applications. In this tutorial series, we developed a web application using jQuery Mobile and then migrated it into a native Android application with only slight modifications. The main idea is to use the android.webkit.WebView object as a container to run the html file of the web application and the jQuery Mobile JavaScript code it contains. Some closing remarks are given below.

  • When packaged as part of a native Android application, an html page running in android.webkit.WebView is not subjected to same-origin restrictions when making AJAX calls.
  • The jquery-dotimeout-plugin and the DST.js plugin, although originally developed for the jQuery framework, perform well for jQuery Mobile. There are a vast amount of plugins written for jQuery and, although case-by-case analysis is needed, those might be readily available for jQuery Mobile. This is an immense asset for this new framework!
  • We tested the web application with an Android OS 2.2 phone and an iPod Touch iOS 4.1 & 4.2. The native Android application was tested with Android OS 2.2 & 2.3 emulators and an Android OS 2.2 phone. In all cases, the look and feel and functional attributes were very similar.
  • In the Android platform, there are known techniques to establish JavaScript-to-Java and Java-to-JavaScript method calls. In fact, we demonstrated how to call back a jQuery Mobile JavaScript function from Java code in our application. It is possible that with a reasonable amount of effort, one could develop jQuery Mobile plugins to access native Android APIs. This indicates further opportunities for the jQuery Mobile framework to develop native Android applications.
Advertisement