Getting Started with Android Library Projects, Part 2
Google offers Android library projects as a way to manage reusable code. In the first part of this three-part series on Android library projects, I introduced you to library project fundamentals.
In this article, I move from theory to practicality by presenting a reusable about dialog box library project as a useful example.
I used Android SDK Release 20 along with the Android 2.3.3 (API Level 10) and Android 4.1 (API Level 16) platforms to develop and test this article’s code.
About Dialog Box Android Library Project
An about (also known as info) dialog box is a good example of reusable code that can be stored in an Android library project. About dialog boxes typically identify apps (names, version numbers, icons) and their authors, provide email addresses and other links for obtaining support and visiting app author websites, offer license/disclaimer information, and so on.
Before I started to develop this library, I pondered various architectural questions. What is the library’s minimum API level and package name? What classes and other reference types are part of the library? Do I instantiate the classes or treat them as singletons? What is each type’s public interface? Do I store resources as well as code? If so, what kind(s) of resources will be stored? I concluded with the following answers:
-
The minimum API level is 10 so that the library can be used by Android 2.3.3 and above. The package name will be
ca.tutortutor.about
. -
The library will consist of a single class named
About
. -
About
will serve as a singleton (also known as a utility class). I won’t instantiate this class, but will invoke static methods. After all, apps typically present one about dialog box. -
About
will expose a singlepublic static void show(Activity activity, String aboutText, String okButtonText)
method. This method will use Android’s package manager to obtain the app’s version number, icon, and name. -
About
is associated with a layout resource; there are no other resources. I decided to not include string resources to simplify localization. I’d rather not have to keep updating the library project to support additional locales. Instead, I believe that localization should occur at the app level; the app should pass localized text to the about dialog box.
Exploring Source Code and Resources
The about dialog box library project consists of a single source file (About.java
) and a single resource file (ca_tutortutor_about.xml
). Listing 1 presents About.java
.
package ca.tutortutor.about;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
public class About
{
public static void show(Activity activity, String aboutText,
String okButtonText)
{
String versionNumber = "unknown";
Drawable icon = null;
String appName = "unknown";
try
{
PackageManager pm = activity.getPackageManager();
versionNumber = pm.getPackageInfo(activity.getPackageName(), 0)
.versionName;
icon = pm.getApplicationIcon(activity.getPackageName());
ApplicationInfo ai = pm.getApplicationInfo(activity.getPackageName(),
0);
appName = (String) pm.getApplicationLabel(ai);
if (appName == null)
appName = "unknown";
}
catch (NameNotFoundException e)
{
}
View about;
TextView tvAbout;
try
{
LayoutInflater inflater = activity.getLayoutInflater();
about = inflater.inflate(R.layout.ca_tutortutor_about, null);
tvAbout = (TextView) about.findViewById(R.id.ca_tutortutor_aboutText);
}
catch(InflateException e)
{
about = tvAbout = new TextView(activity);
}
tvAbout.setText(Html.fromHtml(aboutText));
tvAbout.setMovementMethod(LinkMovementMethod.getInstance());
new AlertDialog.Builder(activity)
.setTitle(appName+" "+versionNumber)
.setIcon(icon)
.setPositiveButton(okButtonText, null)
.setView(about)
.show();
}
}
Listing 1: About.java
Listing 1 reveals an About
class with its solitary public static void show(Activity activity, String aboutText, String okButtonText)
class method:
-
activity
contains a reference to the calling activity, which typically is the main activity. -
aboutText
contains the about dialog box’s text, which can include HTML tags such as<b>
(bold),<i>
(italics),<u>
(underline), and<p>
(paragraph). Here is where you identify your website for obtaining support, present license/disclaimer information, and so on. -
okButtonText
contains the text that appears on the button that closes the dialog box.
This method first obtains the package manager for accessing the app’s version number, icon, and name. The package manager is described by the PackageManager
class, which is accessed by calling Activity
‘s inherited PackageManager getPackageManager()
method.
PackageManager
declares a PackageInfo getPackageInfo(String packageName, int flags)
method for obtaining information from the app’s package. The package is obtained by invoking Activity
‘s inherited String getPackageName()
method.
The returned PackageInfo
object provides access to package information via its fields. For example, versionName
contains the string-based version name of this package (such as 1.0
), as specified by androidmanifest.xml
‘s <manifest>
tag’s versionName
attribute.
PackageManager
‘s Drawable getApplicationIcon(String packageName)
method lets you obtain the app’s icon, as a Drawable
instance. This method is called with the activity’s package name as an argument.
getApplicationIcon()
looks for the icon specified by the <application>
tag’s icon
attribute, returning the default icon when it doesn’t find this attribute.
The app’s name is now obtained. First, PackageManager
‘s ApplicationInfo getApplicationInfo (String packageName, int flags)
method is called to return an ApplicationInfo
object that corresponds to the manifest’s <application>
tag. This object is then passed as an argument to PackageManager
‘s CharSequence getApplicationLabel(ApplicationInfo info)
method, which returns the value of the <application>
tag’s label
attribute or null when this attribute is missing.
The getPackageInfo()
and getApplicationInfo()
methods are capable of throwing PackageManager.NameNotFoundException
. The former method throws this exception when a package with the given name cannot be found on the system. The latter method throws this exception when an application with the given package name cannot be found on the system. Because of the possibility for this exception, versionName
, icon
, and appName
are initialized to default values before these methods are called.
Listing 1’s next major task is to inflate the contents of ca_tutortutor_about.xml
(described shortly) and access its inflated textview widget. It obtains a layout inflator by invoking Activity
‘s LayoutInflater getLayoutInflater()
method, and uses this method to inflate the XML file into an about
view. It then uses about
to access this view’s textview widget. Notice the ca_tutortutor_
prefix in each of R.layout.ca_tutortutor_about
and R.id.ca_tutortutor_aboutText
for avoiding resource conflicts.
The textview widget is now configured. First, aboutText
‘s contents are converted from HTML to text by invoking Html
‘s Spanned fromHtml(String source)
class method, and the result is assigned to the textview for display. Second, a link movement method is assigned to this widget so that pressing on a link causes the user to be taken to the link’s destination. (In the emulator, clicking on an email link results in an Unsupported Action dialog box.)
The final task is to instantiate AlertDialog
and configure and show this instance. For convenience, I use AlertDialog
‘s nested Builder
class to accomplish these tasks.
Listing 2 presents ca_tutortutor_about.xml
.
<?xml version="1.0" encoding="utf-8"?>
<scrollview xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ca_tutortutor_aboutView"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<linearlayout android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp">
<textview android:id="@+id/ca_tutortutor_aboutText"
android:layout_width="wrap_content"
android:layout_height="fill_parent"></textview>
</linearlayout>
</scrollview>
Listing 2: ca_tutortutor_about.xml
Listing 2 describes a layout consisting of a scrollview that nests a linearlayout that nests a textview. The scrollview is present so that you can specify as much content as you require for the about dialog box and allow the user to access this content via scrolling.
Each of the scrollview and textview is assigned an ID so that it can be accessed from About.java
‘s show()
method, which you learned about earlier in this article. The ca_tutortutor_
prefixes are necessary for avoiding resource conflicts.
Creating and Initializing About
Let’s create and initialize this library project. The first step is to create the project. I used the following command line to accomplish this task on my Windows platform:
android create lib-project -t 1 -p C:\prj\ap\About -k ca.tutortutor.about
Target 1 identifies Android 2.3.3 on my platform. (Execute android list targets
to obtain the equivalent target number on your platform.) I store this project in my C:\prj\ap\About
directory. Finally, the project’s package name is ca.tutortutor.about
.
Next, I created a ca\tutortutor\about
subdirectory hierarchy under About\src
and copied an About.java
file containing Listing 1’s contents into about
. I also copied a ca_tutortutor_about.xml
file containing Listing 2’s contents into About\res\layout
.
About\res\layout
also contains a main.xml
file, which is placed in this directory during library project creation. You can erase this file or leave it because your app projects most likely provide their own main.xml
files, which override this main.xml
.
Similarly, you’ll find an About\res\values
directory containing strings.xml
. Because erasing this file prevents you from building About (to test any source code changes for bugs), you might as well leave this file alone — it’s overridden by an app project’s strings.xml
file.
At this point, you might want to ensure that the library project builds correctly. Accomplish this task by executing the following command:
ant debug
You should observe a BUILD SUCCESSFUL message.
Conclusion
Now that you’ve explored the source code and resources that contribute to the about dialog box library project, and have created and initialized this project, you’ll want to integrate it into your own app projects. In the final part of this series, I present an app project that supports the about dialog box via the options menu.