O'Reilly logo

Android Cookbook, 2nd Edition by Ian F. Darwin

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 1. Getting Started

The famous “Hello, World” pattern came about back in 1978 when Brian Kernighan and P.J. Plauger wrote a “recipe” on how to get started in any new programming language and environment. Their idea was that, if you could get a computer program to print out “Hello, World,” then you had mastered how to use the system in general: how to create/edit a program’s source code, compile/translate/process it into a runnable program as needed, and run it. And once you’d done that you could, with elaboration, make the computer do anything! This chapter is affectionately dedicated to these fine gentlemen, and to everyone who has ever struggled to get started in a new programming paradigm.

This chapter is a smorgasbord of “how to get started” recipes. We show you how to create and build an Android app using almost no tooling, using Apache Maven, using Eclipse, using Gradle, and using Android Studio. Nobody will regularly use all these techniques, but we chose to cover them all because some readers will like each way of doing things. Feel free to pick and choose, and try different ways of working on your application!

1.1 Understanding the Android Application Architecture

Ian Darwin

Problem

An Android application consists of many “moving parts” whose natures and interactions need to be understood in order to develop effectively.

Discussion

An Android application consists of one or more of the following components, written as Java classes:

  • An Activity comprises the visual components (“views”) for one screen as well as the code that displays data into that screen and can respond to user events on that screen. Almost every application has at least one Activity class.

  • A Service is a component that has no user interface, and can run for a longer period of time than an Activity. Two main uses for Services are for long-running tasks (such as a music player), and running medium-length tasks without tying up the user-interface thread.

  • Broadcast receivers are less common, and are used to respond to system-wide events such as the network losing or regaining connectivity, the battery running low, the system rebooting, and so on.

  • Content providers are also relatively rare, and are used when one application needs to share its data with other applications; they can also be used with sync adapters.

  • Sync adapters synchronize data with cloud services; the best-known examples are the Contacts and Calendar apps on the device, which can easily be synchronized to your Google account.

Your code does not create these objects using the new operator, as in conventional Java, but requests the invocation of Activities, Services, etc., using an Intent, an object that specifies your intention to have something done. Intents can start Activities within your application (by class name), start Activities in other applications (by specifying content type and other information), start Services, and request other operations. The interactions among these components are outlined in Figure 1-1.

Of these, the Activity is the most basic component, and the place you need to start when learning to develop Android applications.

1.2 Understanding the Android Activity Life Cycle

Ian Darwin

Problem

Android apps do not have a “main” method; you need to understand how they get started and how they stop or get stopped.

Solution

The class android.app.Activity provides a number of well-defined life-cycle methods that are called when an application is started, suspended, restarted, and so on, as well as a method you can call to mark an Activity as finished.

ack2 0101
Figure 1-1. Android application components

Discussion

Your Android application runs in its own Unix process, so in general it cannot directly affect any other running application. The Android Runtime interfaces with the operating system to call you when your application starts, when the user switches to another application, and so on. There is a well-defined life cycle for Android applications.

An Android app can be in one of three states:

  • Active, in which the app is visible to the user and is running.

  • Paused, in which the app is partly obscured and has lost the input focus (e.g., when a dialog is in front of your Activity).

  • Stopped, in which the app is completely hidden from view.

Your app will be transitioned among these states by Android calling the following methods on the current Activity at the appropriate time:

void onCreate(Bundle savedInstanceState)
void onStart()
void onResume()
void onRestart()
void onPause()
void onStop()
void onDestroy()

You can see the state diagram for this life cycle in Figure 1-2.

ack2 0102
Figure 1-2. Android life-cycle states

The system’s call to onCreate() is how you know that the Activity has been started. This is where you normally do constructor-like work such as setting up the “main window” with setContentView(), adding listeners to buttons to do work (including starting additional Activities), and so on. This is the one method that even the simplest Android Activity needs.

Note that most applications today base their UI on Fragments. A Fragment is a part of the UI for an Activity. For example, in the early days of Android a typical list-detail application would use two Activities, one for the list and one for the detail. This is still allowed, of course, but has the drawback that, on a tablet or a large-screen phone in landscape mode, it isn’t possible to have both views side-by-side. An Activity can be divided into multiple Fragments (see Recipe 6.7), which solves this problem. A Fragment can only exist inside an Activity. The Fragment life cycle is similar to that of the Activity, but has a few additional methods.

You can see the invocations of the various life-cycle methods by creating a dummy project in Eclipse and overriding all the life-cycle methods with log “debug” statements (see also Recipe 3.11):

@Override
public void onPause() {
    Log.d(TAG, "In method onPause()");
}

1.3 Learning About Android Releases

Ian Darwin

Problem

You keep hearing about Ice Cream Sandwiches, Jelly Beans, Lollipops, KitKats, Marshmallows, and Nougats, and need to know what it all means.

Discussion

Android has gone through many versions in its lifetime. Each version has a version number, a code name, and an API level. The version number is a conventional versioning system like 2.1, 2.3.3, 3.0, 4.0, 4.1, 5.0, 6.0, and so on. When the first digit of the version changes, it’s a big deal with lots of new APIs; when the second digit changes, it’s more evolution than revolution (and occasionally a new code name); and if only the third digit changes, it’s a minor change. The API levels are numbered monotonically. The code names are alphabetical and always refer to sweet foods. API levels 1 and 2 did not officially have code names.

Note that the Android system is backward-compatible in the usual sense: an app built for an older release will run on a newer version of Android, but not vice versa (unless special care is taken; see Recipe 1.21). An app built for 1.5 should run without recompilation on Android 7, for example. But an app written for and compiled on Android 7 will probably use API calls that don’t exist on the 1.5 phone, so the phone will, in fact, refuse to install the newer app, unless you use some versioning and compatibility tricks that we’ll touch on later (Recipe 1.21). The major versions of Android are summarized in Table 1-1.

Table 1-1. Android versions
Version number API level Name Datea Major change/Notes CM version

1.0

1

2008-09-23

1.1

2

2009-02-09

1.5

3

Cupcake

2009-04-30

3

1.6

4

Donut

2009-09-15

4

2.0

5

Eclair

2009-10-26

5

2.1

7

Eclair

2010-01-12

2.2

8

Froyo

2010-05-20

6

2.3

9

Gingerbread

2010-12-06

Long the most widely-used version

7

2.3

10

Gingerbread

3.0

11

Honeycomb

2011-02-22

Tablets only; source code release delayed

3.1

12

Honeycomb

2011-05-10

3.2

13

Honeycomb

2011-07-15

4.0

14

Ice Cream Sandwich

2011-10-19

Merge tablet and phone support

9

4.0.3

15

Ice Cream Sandwich

2011-12-16

4.1.2

16

Jelly Bean

2012-07-09

10

4.2.2

17

Jelly Bean

2012-11-13

10.1

4.3

18

Jelly Bean

2013-07-24

10.2

4.4

19

KitKat

2013-10-31

Co-marketing deal with Nestlé (makers of KitKat chocolate bar)

11

5.0

21

Lollipop

2014-11-10

12

6.0

23

Marshmallow

2015-10-05

13

7.0

24

Nougat

2016-08-22

14.0

7.1

25

Nougat

14.1

a Date information sourced from Wikipedia.

The final column, “CM version,” shows the main version numbers of CyanogenMod, long the leading “alternate distribution” or “community build” of Android. Based on the Android Open Source Project, “CM” was much beloved by many open source fans because it was independent of Google, allowed easier “root” access, and so on. As this edition of this book was going to press, CyanogenMod, Inc. decided to terminate its support for CyanogenMod, leading the community to fork the project and rename it to LineageOS. There are many other community builds of Android. One focusing on security is CopperheadOS. Several others are built by people frequenting XDA Developers groups. Several commercial outfits claim to offer community builds, too; a web search will find these.

Of course, this table will continue to grow as new versions are released, and Android continues to grow.

1.4 Learning the Java Language

Ian Darwin

Problem

Android apps are written in the Java programming language before they are converted into Android’s own class file format, DEX. If you don’t know how to program in Java you will find it hard to write Android apps.

Solution

Lots of resources are available for learning Java. Most of them will teach you what you need, but will also mention some API classes that are not available for Android development. Avoid any sections in any resource that talk about topics listed in the lefthand column of Table 1-2.

Table 1-2. Parts of the Java API to ignore
Java API Android equivalent

Swing, applets

Android’s GUI; see Chapter 6.

Application entry point main()

See Recipe 1.2.

J2ME/Java ME

Most of android.* replaces the Java ME API.

Servlets/JSP/JSF, Java EE

Designed for server-side use.

Discussion

Here are some books and resources on Java programming:

  • Java in a Nutshell by David Flanagan (O’Reilly). This is a good introduction for programmers, particularly those who are coming from C/C++. The book has grown from an acorn to a coconut in size through its various editions, to keep up with the growth of Java SE over its lifetime.

  • Head First Java by Kathy Sierra and Bert Bates (O’Reilly). This provides a great visual-learner-oriented introduction to the language.

  • Thinking in Java by Bruce Eckel (Prentice-Hall).

  • Learning Java by Patrick Niemeyer and Jonathan Knudsen (O’Reilly).

  • “Great Java: Level 1,” by Brett McLaughlin (O’Reilly). This video provides a visual introduction to the language.

  • Java: The Good Parts by Jim Waldo (O’Reilly).

  • Java Cookbook, which I wrote and O’Reilly published. This is regarded as a good second book for Java developers. It has entire chapters on strings, regular expressions, numbers, dates and times, structuring data, I/O and directories, internationalization, threading, and networking, all of which apply to Android. It also has a number of chapters that are specific to Swing and to some EE-based technologies.

  • Java Testing for Developers, a video series I did on how to test out Java code as you develop it; covers both dynamic testing (with JUnit and many others) and static testing (with tools such as PMD and FindBugs).

Please understand that this list will probably never be completely up-to-date.

See Also

I maintain a list of Java resources online at http://www.darwinsys.com/java/.

O’Reilly has many of the best Java books around; there’s a complete list at http://oreilly.com/pub/topic/java.

1.5 Creating a “Hello, World” Application from the Command Line

Ian Darwin

Problem

You want to create a new Android project without using any IDEs or plug-ins.

Solution

Use the Android Software Development Kit (SDK) tool android with the create project argument and some additional arguments to configure your project.

Discussion

This discussion assumes you have installed the Android SDK—one of the easiest ways to do so is to follow Recipe 1.8—and installed at least one platform version.

In addition to being the name of the platform, android is also the name of a command-line tool for creating, updating, and managing projects. To use it, you can either navigate into the android-sdk-nnn directory or set your PATH variable to include its tools subdirectory.

You have a choice of creating your project in the old format, which is the default, or the “new” Gradle-based format. We’ll show the old way first, then the Gradle way. To create a new project, give the command android create project with some arguments. Example 1-1 shows running the command in a terminal window in a Microsoft environment.

Example 1-1. Creating a new project—old format
C:> PATH=%PATH%;"C:\Documents and Settings\Ian\My Documents\android-sdk-windows\tools"; \
    C:\Documents and Settings\Ian\My Documents\android-sdk-windows\platform-tools"
C:> android create project --target android-21 --package com.example.foo
    --name Foo --activity HelloWorldActivity --path .\MyAndroid
Created project directory: C:\Documents and Settings\Ian\My Documents\MyAndroid
Created directory C:\Documents and Settings\Ian\My Documents\MyAndroid\src\com\example\foo
Added file C:\Documents and Settings\Ian\My
Documents\MyAndroid\src\com\example\foo\HelloWorldActivity.java
Created directory C:\Documents and Settings\Ian\My Documents\MyAndroid\res
Created directory C:\Documents and Settings\Ian\My Documents\MyAndroid\bin
Created directory C:\Documents and Settings\Ian\My Documents\MyAndroid\libs
Created directory C:\Documents and Settings\Ian\My Documents\MyAndroid\res\values
Added file C:\Documents and Settings\Ian\My Documents\MyAndroid\res\values\strings.xml
Created directory C:\Documents and Settings\Ian\My Documents\MyAndroid\res\layout
Added file C:\Documents and Settings\Ian\My Documents\MyAndroid\res\layout\main.xml
Added file C:\Documents and Settings\Ian\My Documents\MyAndroid\AndroidManifest.xml
Added file C:\Documents and Settings\Ian\My Documents\MyAndroid\build.xml
C:>

On Unix or macOS you can use something like the following:

/Users/ian/android-sdk-macosx/tools/android create project --target android-21 \
    --package com.example.foo \
    --name Foo --activity HelloWorldActivity --path MyAndroid

Table 1-3 lists the arguments for the android create project command.

Table 1-3. List of android create project arguments
Name Meaning Example

--activity

Name of your “main class” and default name for the generated .apk file.

--activity HelloWorldActivity

--name

Name of the project and the generated .apk file.

--name MyProject

--package

Name of the Java package for your classes.

--package com.example.hello

--path

Path to create the project in (does not create a subdirectory under this, so don’t use /home/you/workspace, but rather / home / you / workspace / NewProjectName).

--path /home/ian/workspace/MyProject (see Example 1-1 for Windows example)

--target

API level of the Android platform to target; use android list targets to see list of targets. A number is an “ID,” not an API level; for that, use android- with the API level you want.

--target android-10

--gradle

Use Gradle format (requires --gradle-version).

--gradle

--gradle-version

Version of Gradle plug-in to use.

--gradle-version 3.3

If it cannot complete the requested operation, the android command presents a voluminous “command usage” message listing all the operations it can do and the arguments for them. If successful, the android create project command creates the files and directories listed in Table 1-4.

Table 1-4. Artifacts created by android create project
Name Content

AndroidManifest.xml

Config file that tells Android about your project

bin

Generated binaries (compiled class files)

build.properties

Editable properties file

build.xml

Ant build control file

default.properties or project.properties (depending on tools version)

Stores SDK version and libraries used; maintained by plug-in

gen

Generated stuff

libs

Libraries, of course

res

Important resource files (strings.xml, layouts, etc.)

src

Source code for your application

src/packagename/ActivityName.java

Source of “main” starting Activity

test

Copies of most of the above

If we use the two Gradle-related arguments, we get a slightly different project structure, as shown in Example 1-2.

Example 1-2. Project creation—Gradle format
$ /Users/ian/android-sdk-macosx/tools/android create project \
    --target android-23 --package com.example.foo \
    --gradle --gradle-version 2.0.0  \
    --name Foo --activity HelloWorldActivity --path HelloGradle
Created project directory: HelloGradle
Created directory /home/ian/HelloGradle/src/main/java
Created directory /home/ian/HelloGradle/src/main/java/com/example/foo
Added file HelloGradle/src/main/java/com/example/foo/HelloWorldActivity.java
Created directory /home/ian/HelloGradle/src/androidTest/java
Created directory /home/ian/HelloGradle/src/androidTest/java/com/example/foo
Added file...
    HelloGradle/src/androidTest/java/com/example/foo/HelloWorldActivityTest.java
Created directory /home/ian/HelloGradle/src/main/res
Created directory /home/ian/HelloGradle/src/main/res/values
Added file HelloGradle/src/main/res/values/strings.xml
Created directory /home/ian/HelloGradle/src/main/res/layout
Added file HelloGradle/src/main/res/layout/main.xml
Created directory /home/ian/HelloGradle/src/main/res/drawable-xhdpi
Created directory /home/ian/HelloGradle/src/main/res/drawable-hdpi
Created directory /home/ian/HelloGradle/src/main/res/drawable-mdpi
Created directory /home/ian/HelloGradle/src/main/res/drawable-ldpi
Added file HelloGradle/src/main/AndroidManifest.xml
Added file HelloGradle/build.gradle
Created directory /home/ian/HelloGradle/gradle/wrapper
$

It is a normal and recommended Android practice to create your user interface in XML using the layout file created under res/layout, but it is certainly possible to write all the code in Java. To keep this example self-contained, we’ll do it the “wrong” way for now. Use your favorite text editor to replace the contents of the file HelloWorld.java with the contents of Example 1-3.

Example 1-3. HelloWorld.java
import android.app.Activity;
import android.widget.*;

public class HelloWorld extends Activity {

        /**
         * This method gets invoked when the Activity is instantiated in
         * response to, e.g., clicking on the app's icon in the Home screen.
         * Reminder: this is NOT a best-practices way of creating the UI!
         */
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                // Create a TextView for the current Activity
                TextView view = new TextView(this);
                // Make it say something
                view.setText("Hello World");
                // Put this newly created view into the Activity,
                // sort of like JFrame.getContentPane().add(view)
                setContentView(view);
        }
}

Although Google has moved from Eclipse to Android Studio, which uses the Gradle build tool, the command-line version of generated projects still uses the Ant build tool by default (i.e., if you omit the two Gradle-related arguments shown in Example 1-2). Assuming you have the Apache Software Foundation Ant build tool installed (and it is included with recent versions of the Android SDK), you can now (in a command-line window) change to the project directory (…MyDocuments\MyAndroid in Example 1-1) and issue the command:

$ ant debug

This will create an archive file named, for example, MyAndroid.apk (with “apk” standing for Android Package) in the bin directory.

If you are using the Gradle version, you can instead type:

gradlew build

The first time you run this, it may take a long time to complete. But it should work. If it doesn’t, use the HelloGradle project in this book’s GitHub repository.

If this is your first time here, you may need to create an Android Virtual Device (AVD), which is just a named configuration for the Android emulator specifying target resolution, API level, and so on. You can create an emulator using:

android create avd -n my_droid -t 21

The argument to -t is the target API level; see Recipe 1.3. For more details on creating an AVD, see Recipe 3.1.

You can then start the Android Debug Bridge (ADB) server for communication, and the emulator:

adb start-server
emulator -avd my_droid -t 19

Assuming you now have either the emulator running or your device plugged in and recognized via USB, you can issue a command similar to one of the following, depending on exactly what you built earlier. If you have both an emulator and a real device, add an argument of -e for emulator or -d for device between the adb command and the install operation:

$ adb install -r bin/HelloAndroid.apk # Ant build

$ adb install -r target/HelloAndroid-1.0-SNAPSHOT-debug.apk # Maven build

$ adb install -r build/outputs/apk/HelloAndroid-debug.apk # Gradle build

If you are handy with shell scripts or batch files, you’ll want to create one called, say, download, to avoid having to type the adb invocation on every build cycle.

Finally, you can start your app! You can use the application list: tap the little icon that looks like a 5×5 row of dots, scroll to your application by name, and tap its icon.

You will probably find it convenient to create an icon for your app on the Home screen of the device or emulator; this icon will survive multiple install -r cycles, as long as you don’t uninstall, so it’s the easiest way to test the running of your application.

1.6 Creating a “Hello, World” App with Apache Maven

Ian Darwin

Problem

The previous recipe used Android to create a project buildable with Apache Ant. However, many organizations are moving or have moved from Ant to Maven, due to Maven’s dependency management. In fact, Maven is almost certainly the most widely used build tool in the Java environment. Ant doesn’t handle dependencies on its own; although this can be grafted in (using Apache Ivy), Maven’s shorter configuration files make direct use of Maven a better fit most of the time.

Solution

Use Apache Maven. Use a “Maven archetype” to generate your project, and use Maven to build and run it.

Discussion

There are several approaches to using Apache Maven to build Android projects. Here’s one I’ve tested, based upon the akquinet maven-android-archetypes:

$ mvn archetype:generate \
-DarchetypeArtifactId=android-quickstart \
-DarchetypeGroupId=de.akquinet.android.archetypes \
-DarchetypeVersion=1.0.8 \
-DgroupId=com.androidcookbook \
-DartifactId=android-demo \
-Dplatform=17 \
-Dpackage=com.androidcookbook.hellomaven

Most of the -D arguments are obvious. platform is the API level. You can specify a number of other parameters and variations, including test projects.

Once you’ve created your project you can build it:

$ mvn clean package

Before the next step, you should plug in a device or start an emulator:

$ mvn android:deploy
# (not mvn deploy!) this will package and install, but not run, the app
$ mvn android:run # This will run the app

Maven and its Android plug-in offer support for other operations, including signing the APK for release.

There are also Eclipse plug-ins for Maven; these are included with the latest Eclipse builds, or see Recipe 1.16 and use the Marketplace to install M2E and M2E-Android. It is possible to “Eclipsify” a project such as the one you created using Maven. You can create minimal Eclipse project structures using mvn eclipse:eclipse, and make it into a full M2E project by right-clicking on the project in the Project Explorer and selecting Configure → Convert to Maven Project. This has been done to create many of the Eclipse files in the downloadable version of this project.

Incidentally, if you get an Eclipse error on your POM file stating “Plugin execution not covered by lifecycle configuration,” you can turn this into a warning or even ignore it, under Eclipse Preferences → Maven → Errors/Warnings → Plugin execution not covered by lifecycle configuration → Warning, as shown in Figure 1-3.

ack2 0103
Figure 1-3. Maven: Plugin execution not covered by lifecycle configuration

See Also

Akquinet’s guide to getting started with archetypes; the source code for the artifacts.

I have an experimental Maven archetype that creates a Maven project that should also work with Eclipse and Android Studio; you can try it out by referring to GitHub.

1.7 Choosing an IDE for Android Development

Ian Darwin

Problem

Using build tools is OK, but coding with a plain-text editor is too slow to be your regular development stream. You want to choose an integrated development environment (IDE) to use for your Android projects: Android Studio, Eclipse, or “other.”

Solution

Weigh the pros and cons of each, then roll the dice. Try each one on a reasonable-sized project.

Discussion

Whereas in the MS Windows world there is a single IDE that dominates, and in the Android world there is a single IDE that is “official,” in the larger Java world there are several that are worth considering.

Eclipse was created by IBM in the early days of Java, once it was clear that its then-current IDE Visual Age was not going to live a long life (Visual Age was written in Smalltalk, not in Java). In my experience teaching Java courses, about 80% of Java developers use Eclipse, and that figure has been fairly steady over the years. Spring Tool Suite (STS) and various IBM developer studios are based on Eclipse and included in that figure.

Android Studio is the official Android IDE supported by Google. It is based on IntelliJ IDEA, a Java IDE that has long been around but had a relatively small usage level in the Java community until Google incorporated its plug-in into ItelliJ and renamed this version to “Android Studio.”

NetBeans was written by a small company that Sun Microsystems acquired in 1999. Sun Microsystems was in turn acquired by Oracle in 2009. NetBeans has been the “official” Java IDE for a long time, but its usage was “eclipsed” by Eclipse (remember: an eclipse occurs when another body passes in front of the Sun). Relatively few developers use NetBeans specifically for Android, so to keep the discussion focused, NetBeans will not be covered in this book.

For the first decade of Android’s life, Google recommended use of Eclipse with its own plug-in, called Android Development Tools (ADT). Google offered it both as a standalone plug-in (for those who already had Eclipse up and running) and in a bundle already integrated with Eclipse. Around 2013 it announced the switch to Android Studio based on IntelliJ. Shortly thereafter, the Eclipse Foundation announced that a small team was picking up ADT (since it was open source) and merging in some additional tools. This new plug-in is called AndMore. Eclipse with AndMore is equivalent to and forward-compatible with Eclipse with ADT, though some names in the project files have to be changed (see Recipe 1.11). Note that some organizations may choose to stay with ADT; if you’re in that camp, you can (mostly) just substitute ADT where we say AndMore.

Your project structure and accompanying build tool might also be a factor in choosing. Eclipse supports a single-level project, which is typically what you need for an application, with an optional second project for testing if you use the official Android unit testing framework (see Chapter 3). ADT (and thus AndMore) does not require an external build tool; the plug-in contains all the smarts to build any type of Android application. It has only two project files that need to be kept under source control: .project and .classpath. A directory .settings file can be controlled as well, but it changes a lot and can just as easily be ignored. There is even an API in Eclipse for manipulating project structure. Because there are only two files, hacking a project by editing configuration files is not out of the question. As well, Eclipse is well supported by the Maven build tool using the M2E (Maven Eclipse) and M2E-Android plug-ins (you’ll want both). However, this setup can be a little bit quirky.

Android Studio, on the other hand, uses a maze of project files. Here is a list of the files (not including the source of your program!) in a project created by Android Studio 2.0:

./.gradle/2.4/taskArtifacts/cache.properties
./.gradle/2.4/taskArtifacts/cache.properties.lock
./.gradle/2.4/taskArtifacts/fileHashes.bin
./.gradle/2.4/taskArtifacts/fileSnapshots.bin
./.gradle/2.4/taskArtifacts/outputFileStates.bin
./.gradle/2.4/taskArtifacts/taskArtifacts.bin
./.idea/.name
./.idea/compiler.xml
./.idea/copyright/profiles_settings.xml
./.idea/encodings.xml
./.idea/gradle.xml
./.idea/libraries/appcompat_v7_23_0_1.xml
./.idea/libraries/design_23_0_1.xml
./.idea/libraries/hamcrest_core_1_3.xml
./.idea/libraries/junit_4_12.xml
./.idea/libraries/support_annotations_23_0_1.xml
./.idea/libraries/support_v4_23_0_1.xml
./.idea/misc.xml
./.idea/modules.xml
./.idea/runConfigurations.xml
./.idea/workspace.xml
./build/ - ignore
./build.gradle
./gradle/wrapper/gradle-wrapper.jar
./gradle/wrapper/gradle-wrapper.properties
./gradle.properties
./gradlew
./gradlew.bat
./local.properties
./MyApplication.iml
./settings.gradle
./mainapp/.gitignore
./mainapp/build.gradle
./mainapp/mainapp.iml
./mainapp/proguard-rules.pro

It appears to take Android Studio about 30 files to do what Eclipse does in just a few. Admittedly not all of those have to be kept under source control, but which ones do? To answer that, look in the .gitignore file in a project generated by Android Studio 2.x; this lists the files that should not be included in source control.

Android Studio also expects that every project have an extra level of directory structure, called app for the application, to cater to the relatively few applications that have multiple modules, such as a library. In Eclipse, you just make the project using the library depend on the library project. The extra directory structure put in by Studio encumbers pathnames, means the directory where a Studio project is created does not conform to the decade-old Maven project structure, and means that you can’t use the old familiar grep -r somePattern projectname/src; you have to remember to type an extra “app/” every time. Seemingly harmless, but annoying. Of course people who commonly use multiple projects but forget to create them as such when they start will appreciate the way Studio does things.

You should also consider speed. Both are fairly quick at entering code you type. Because Studio is not a complete IDE but depends on Gradle to build, it used to be a lot slower, but Studio 2.x is supposed to be much improved in this regard. Different people have different ideas on how to measure speed, and different results have been claimed, so you should try this yourself on representative development hardware.

Eclipse provides a single window with a tree-based “Package Explorer,” so you can easily move, copy, or compare files between projects. IntelliJ/Studio opens each project in a new window and, by default, closes the previous one.

So, there are many differences, but also many obvious similarities. It’s sort of like buying a car: GM, Ford, Chrysler, Tesla, BMW, Toyota, and many more make automobiles, but you have to pick one of these to buy. With IDEs the choice is not as exclusive, though. What if you like both? You could use Eclipse for your regular Java work and IntelliJ/Android Studio for your Android work—especially if you need the latest Android support—although switching back and forth might be annoying. You could even set up your Android projects to be openable in both IDEs—we’ve done so for most of the projects in the samples repository. However, it’s not a very profitable undertaking, and we don’t recommend it as a general practice.

Oh, and if you do run both, be sure to configure them to share the same “SDK” folder—the actual Android tools, libraries, and emulator images—so you won’t have to update everything twice.

As another path forward for the experienced Eclipse user, you could use Android Studio but tell it to use the Eclipse key-mappings, which will make it work somewhat like Eclipse—although many of the key sequence settings there are not quite right, and you’ll need to fiddle with them a bit. And if you do so but another developer in your team is a “native” user of Studio or the underlying IntelliJ, you will both get frustrated when doing pair programming.

Summary

If you want the best support of new features, Android Studio may be a better choice. If you want an IDE that is widely used across the Java community, Eclipse may be a better choice. There is no hard and fast answer.

1.8 Setting Up Android Studio

Daniel Fowler, Ian Darwin

Problem

You want to develop your Android applications using Android Studio, so a concise guide to setting up that IDE would be useful.

Solution

The use of the Android Studio IDE is recommended by Google for developing Android apps. Configuring the IDE is not a single-shot install; several stages need to be completed. This recipe provides details on those stages.

Discussion

The Android Studio integrated development environment (IDE) is provided for free by Google to develop applications. Studio comes with the Android Software Development Kit (SDK), which provides essential programs to develop Android software. To set up a development system you will need to download and run the installers for:

  • The Java Standard Edition Development Kit (JDK), if not already installed

  • Android Studio

Installing the JDK, if necessary

Go to the Java download page. Click the Java icon to access the JDK downloads:

ack2 01in01

The list of JDK downloads will be shown. Click the Accept License Agreement radio button; otherwise, you will not be allowed to continue. You’ll want to download and run one of the latest JDKs present; as of this writing, they are Java 8 builds whose version string ends in 8u121, but that will surely change by the time you read this. Choose the download appropriate for your operating system: Windows x86 or 64-bit.exe, MacOS .dmg, Linux .rpm or .tgz, and so on. Accept any security warnings that appear, but only if you are downloading from the official Java download web page.

When the download has completed, run the installer and go through all the screens, clicking Next until the installer has finished. You should not need to change any options presented. When the JDK installer has completed, click the Finish button. A product registration web page may load; you can close this or you can choose to register your installation.

For Android use, you do not need to download any of the “demos or samples” from this site.

Install Android Studio

Go to the Android Studio download page.

The installation process can take some time as the installer will download additional files. Click the Download Android Studio button, accept the terms and conditions, and begin the download by clicking a second Download Android Studio button. On MS Windows, the default is a file with a name like android-studio-bundle-X.X-windows.exe, which is over 1 GB and includes both the IDE and the Android SDK. If you already have the Android SDK installed, select Other Download Options and you will see the page in Figure 1-4, where you have a choice of that file or one without the SDK bundle, with a name like android-studio-ide-X.X-windows.exe. For macOS there is only the unbundled file, android-studio-ide-X.X-mac.dmg, where X.X is Studio’s build number (this may not match the displayed version number; e.g., Android Studio 2.0 has a build number of 143.2739321). On Windows, accept the Windows User Account Control dialog.

On some 64-bit versions of Windows the installer may require the JAVA_HOME environment variable to be set. Use the Control Panel to access the System settings, and open the “Advanced systems settings” section. The Advanced tab on the System Properties dialog should be visible. Click the Environment Variables button to add a new JAVA_HOME system variable, and set it to the path of the Java JDK (e.g., C:\Program Files\Java\jdk1.8.0; enter the correct path for your system).

ack2 0104
Figure 1-4. Android Studio download page

Proceed through the installer’s dialogs. The installer configures Studio and the Android SDK (if you installed the bundle version), and downloads an initial Android Virtual Device (AVD). The default install location for the Android SDK is under the logged-in user’s AppData\Local directory on Windows, or $HOME/android-sdk-nnn under macOS or Linux. You may want to select an easier-to-remember and shallower location (e.g., C:\AndroidSDK).

After installing Studio, further configuration (and downloading of the SDK if necessary) occurs when it is first run. Allow access through your desktop system’s configuration if required. Further SDK packages will be downloaded. As well, each time Studio runs it checks for updates and may display a message if updates are needed. Aside from updates to Studio itself, the Android SDK and additional SDK packages are best updated via the Android SDK Manager program (see Recipe 1.9).

Once that’s done, Studio is now configured to build and debug Android apps. See Recipe 3.1 to configure an Android emulator; then try a “Hello, World” app as a sanity check. Or plug a real Android device into the computer’s USB port and use its settings to turn on USB Debugging.

Tip

For a few Windows users Studio may not start the first time, and a DLL error is displayed. Installing the Microsoft Visual C++ 2010 SP1 Redistributable Package has been known to clear the error.

See Also

Recipe 3.1, Recipe 1.9.

1.9 Installing Platform Editions and Keeping the SDK Updated

Daniel Fowler

Problem

Whether using Android Studio, Eclipse, or command-line tools, you must install at least one Platform Edition before you can compile applications. The SDK should be kept updated so you can work with the latest APIs on the evolving Android platform.

Solution

Use the Android SDK Manager program to install, and later to update, the installed SDK packages and to install new SDK packages.

Discussion

Android itself is constantly evolving, and therefore so is the Android Software Development Kit. The ongoing development of Android is driven by:

  • Google’s research and development

  • Phone manufacturers developing new and improved handsets

  • Addressing security issues and possible exploits

  • The need to support new devices

  • Support for new hardware interfaces

  • Fixing bugs

  • Improvements in functionality (e.g., a new JavaScript engine)

  • Changes in the underlying Linux kernel

  • Deprecation of redundant programming interfaces

  • New uses (e.g., Android Wear, Android Auto)

  • The wider Android development community

Tip

The following discussion is illustrated with screenshots from Android Studio, but the same tooling can be invoked from within Eclipse or by invoking the command-line tool named simply android.

Installation of the IDEs and the Android SDK has been covered elsewhere; see Recipe 1.8 or the developer documentation. When Android Studio is run it will check for updates to both Studio and the SDK. A message is displayed when updates are available. Most updates will be performed via the SDK Manager program. If you OK the update, Studio will close and the SDK Manager will run. If you don’t want to update when the upgrade is offered, you can access the SDK Manager later from within Studio (see Figure 1-5) or directly from the Android SDK install location.

ack2 0105
Figure 1-5. SDK Manager toolbar icon

The following steps work through the update process.

In Studio, selecting SDK Manager from the toolbar or via the Tools → Android menu shows the Android SDK settings, which shows what packages are installed or available (see Figure 1-6).

ack2 0106
Figure 1-6. Android SDK settings, showing current installation status

To actually make changes, click the Launch Standalone SDK Manager link, which runs the external SDK Manager program shown in Figure 1-7. The Android SDK is divided into several packages. The SDK Manager automatically scans for updates to existing packages and will list new packages. Available updates will be shown in a list (alongside available optional packages).

ack2 0107
Figure 1-7. Standalone SDK Manager

Available updates will be checked ready for download and installation; uncheck the ones not required. (Unless you’re short on disk space, you can have as many of the API packages installed as you wish.) Then click the “Install x packages” button. If an update or package has license terms that require accepting, they will be listed. Highlight each package to read the license terms, and then accept or reject the package using the radio buttons. (Rejected packages will be marked with a red cross.) Alternatively, highlight the parent package and click Accept All to accept everything that is available. All packages and updates ready to download and install will be shown with a green tick. Click Install; while the download and installation is progressing, you can view the log by clicking the log icon in the bottom right of the Android SDK Manager dialog (see Figure 1-8).

ack2 0108
Figure 1-8. Android SDK update log

Any errors during the download and installation will be shown in red in the log dialog. Updates that affect the Android Debug Bridge (ADB) will result in a request to restart ADB; click Yes to restart it. Obsolete packages will have been deleted during the download and installation. When all packages have been updated and you’ve had a chance to inspect the log, you can close the log dialog, if open, and the Android SDK Manager dialog.

Android is an evolving platform, so checking for updates every few weeks allows you to work with the latest tools and APIs.

See Also

Recipe 1.8, The Android Studio User Guide documentation on installation.

1.10 Creating a “Hello, World” App Using Android Studio

Ian Darwin

Problem

You want to use Android Studio to develop your Android application.

Solution

Install Java, Android Studio, and one or more SDK versions. Create your project and start writing your app. Build it and test it under the emulator, all from within the IDE.

Discussion

Before you can create your application with Android Studio, you have to install these two packages:

For details on installing these items, please refer to Recipe 1.8.

Once you’ve done that, click on “Start a new Android Studio project” from the Welcome screen (Figure 1-9), which appears when you don’t have any projects open.

ack2 0109
Figure 1-9. Studio Welcome screen

On the Create New Project screen (Figure 1-10), choose the application name and Java code package.

ack2 0110
Figure 1-10. Studio New Project screen

On the next page of the same dialog, you can specify what kinds of devices (phone/tablet, Android Wear, Android TV, etc.) your project is going to target and, for mobile devices, the minimum and target SDK API levels (Figure 1-11).

Almost every Android application has at least one Activity class defined, and the “Hello, World” app is no exception. You can either pick Empty Activity (in which case you’ll have to add some code) or Basic Activity; we went with the latter (Figure 1-12).

The next page asks you to pick names for your Activity and its layout file (Figure 1-13). For a single-Activity app, the defaults are fine.

ack2 0111
Figure 1-11. Studio device and target APIs
ack2 0112
Figure 1-12. Defining the Activity class
ack2 0113
Figure 1-13. Studio customizing the Activity

After chugging for a while, Studio will create your project and give you a blank view of it, since you haven’t told it how to display the project (Figure 1-14).

ack2 0114
Figure 1-14. Studio blank view

Click the sideways label 1. Project at the upper left of the main window. Drill down into App → Java → package-name/MainActivity, or whatever you called the Activity. Have a brief look at the provided Java code (Figure 1-15).

ack2 0115
Figure 1-15. Studio-generated Activity

If you don’t see a graphic representation of your Activity soon, expand Res → Layout and double-click content_main.xml. You should see a visual UI editor (Figure 1-16).

ack2 0116
Figure 1-16. Studio layout editor

Note that the Studio Layout Editor isn’t really running your application, just interpreting the user interface layout. To actually run it, click the Run button in the middle of the top toolbar. In the process of starting the app, Studio will ask you which AVD (emulator) to use. Eventually the application should appear in its own emulator window (Figure 1-17).

ack2 0117
Figure 1-17. Studio running an app in AVD

1.11 Converting an Eclipse ADT Project to Android Studio

Ian Darwin

Problem

You have existing Eclipse/ADT projects but want to or have to use Android Studio.

Solution

Use the Android Studio Import project feature. This will make a copy of the files it needs in a new location, allowing you to build the project under Android Studio.

Discussion

Note that, at the time of writing, this works for ADT projects but not for AndMore projects.

To convert an ADT project to Studio, close any previous projects, or start Android Studio if it’s not running. Select “Import project” from the Welcome screen (Figure 1-18).

ack2 0118
Figure 1-18. Studio: Starting to convert a project

In the dialog that pops up, navigate to the root of the Eclipse folder. This will normally have res and src folders underneath it, assuming that standard ADT layout was used (Figure 1-19).

ack2 0119
Figure 1-19. Studio: Location of project to convert

Now you get to pick a new location for the converted project (Figure 1-20). The default is good for starting, unless you or your organization has a policy on where to place projects.

ack2 0120
Figure 1-20. Studio: Location to convert the project into

Note that you must pick a different location from where the Android project exists, which totally destroys the project history—your Git or CVS or Subversion history ends at the current state of the application under Eclipse, and a new history will begin with it under Studio. For people who think Studio is the best thing since the motor car, this will seem like a good thing. For those of us who understand that Studio is just another tool in a long series, it will seem like an aberration, or at least an annoyance. I have files on GitHub whose revision dates precede the existence of both Java IDEs and GitHub (and at least one repository whose creation predates the existence of Java), and I wouldn’t want to lose that history. It’s annoying because it could be done better, by more comprehensive integration with tools such as Git. However, it is what it is. If you want to keep the history, you can work around this as described in Recipe 1.12, instead of following the recipe you are now reading.

After you’ve specified the import directory more options will appear, related to replacing JARs with references to standard ones (Figure 1-21). Again, the defaults are usually what you want.

Finally, the converted project will appear (Figure 1-22). The editor window is filled with a summary of what happened. If it looks similar to the one here, it has probably succeeded.

You should now be able to run the project by selecting the “app” module and pressing the green Run button.

ack2 0121
Figure 1-21. Studio: Options for converting the project
ack2 0122
Figure 1-22. Studio: The converted project

1.12 Preserving History While Converting from Eclipse to Android Studio

Ian Darwin

Problem

As shown in Recipe 1.11, the Android Studio import mechanism creates a new project in a new directory, causing a break in your revision control history. You want instead to convert a project permanently from Eclipse to Android Studio, but without losing years of valuable source control history.

Solution

One approach is to use the source control program’s “move” command to restructure the project in place, into a form that Studio/Gradle can understand.

Warning

Eclipse can no longer process the project after it’s been rearranged this much; there doesn’t seem to be a way to tell it the new location for the Android manifest and the resource files. If you want to be able to use both IDEs, see Recipe 1.13 instead of the recipe you are now reading.

Discussion

The process will vary greatly depending on which source code management (SCM) system you use. One of the oldest widely used SCMs was CVS, the Concurrent Versions System. CVS did not support moving of files, so if your project is still in CVS (or RCS or SCCS, two even older SCMs) you will want to convert it into Git first, and learn to use Git, if you really want to preserve history. I know this process can work well because my public GitHub repositories contain some files with modification dates a full 10 years before Git was written. Accordingly, this example assumes you have your Eclipse Android project in the Git SCM. And I’ll describe the steps in the form of Unix/Linux/macOS command-line steps because that’s the most concise format. Understand that this is only general guidance; your mileage will certainly vary!

You will also want to have a variety of both Eclipse and Android Studio projects to compare with and to copy missing bits from; the examples from this book (see “Getting and Using the Code Examples”, or git clone https://github.com/IanDarwin/Android-Cookbook-Examples) would be a good resource to have at hand.

Plan A: Moving files around

I used this approach to convert a privately funded Android app, consisting of 30 Java files and numerous resource files, from Eclipse format to Android Studio format; I got it going in an hour or two, with its revision history intact.

First, create a copy, so that if the conversion messes up too badly you can delete the whole thing and start over (a Git purist might argue that you should just use a branch, but this is my way of doing it):

$ cp -r ~/git/myproject /tmp/myproject
$ cd /tmp/myproject

Now you need to convert the structure of the project. Eclipse used a simple src folder, whereas modern build tools like Maven (Recipe 1.6) and Gradle (Recipe 1.10) use a structure like src/main/java. If you have standalone (pure Java, non-Android) unit tests, they may be in a folder called test, which has to become src/test/java. Also, the resources folder (res) and the Android manifest file have to move to src/main:

$ rm -fr bin build gen target # Start with a cleanup
$ mkdir -p src/main/java
$ mkdir -p src/test/java
$ git mv src/com src/main/java
$ git mv test/com src/test/java/
$ git mv res src/main/
$ git mv AndroidManifest.xml src/main/
$ rmdir test

The next step is to convert the dependency information in your .classpath or pom.xml file into the build.gradle file:

$ cat ../__SomeExistingStudioProject__/build.gradle pom.xml > build.gradle
$ vi build.gradle # Merge the dependencies by hand
$ git rm -f pom.xml
$ git add build.gradle

Create a local.properties file containing the path to the Android SDK on your dev machine, using a command like one of the following:

$ cp ../__SomeExistingStudioProject__/local.properties .

$ echo 'sdk.dir=/Users/ian/android-sdk-macosx' > local.properties

Now you will need to copy a few files from an existing Android Studio project: at least gradlew (for Unix/Linux/macOS) and/or gradlew.bat (for Windows cmd.exe).

If your project doesn’t have a .gitignore file, create one, and add the local.properties file to it:

$ echo local.properties >> .gitignore

Now try building the project, either by typing gradlew build or by opening it in Android Studio. Then hack at it until it works. When you’re comfortable, git commit. When you’re really happy, git push.

Plan B: Moving files into a new project

An alternative approach would be as follows. I have not tested this myself, but it seems simpler:

  1. Create a new project using the Studio New Project wizard.

  2. Copy files from your existing project into the new project, using the earlier move commands as a guide.

  3. Copy your revision history into the new project, which won’t have one yet:

    $ cp -r oldProject/.git newProject/.git
  4. After verifying that the project is reasonably sane, save the changes, assuming that Git will recognize the moved files (it usually will):

    $ git commit -m "Reorganize project for Android Studio"
    
  5. Hack at it as needed until it works properly.

  6. git commit any last-minute changes, and git push.

See Also

Recipe 1.13.

1.13 Building an Android Application with both Eclipse and Android Studio

Ian Darwin

Problem

Your project team may have some developers who want to stay on Eclipse and some who want to work with Android Studio.

Solution

Make your project work with both major IDEs, by providing both Eclipse and Gradle build files.

Discussion

Assume you have a project that is working with Eclipse. It is possible to create a build.gradle file with all the file and directory paths set to the locations that Eclipse uses, and thus allow coexistence. This way you can edit and build the project using either Eclipse or Android Studio! I did this several years ago, when Studio was still in Alpha or Beta status, and it can still be done. The basic steps are:

  1. Copy the sample build.gradle file shown in Example 1-4 to the root of your Eclipse project.

  2. Edit the file, uncommenting and changing the string YOUR.PACKAGE.NAME.HERE to your package name (as per AndroidManifest.xml) in the +applicationId line.

  3. Either move the src folder to “Maven standard” src/main/java (and update your .classpath), or add a java.src entry to build.gradle. I recommend the first way because it’s more commonplace to use this structure today.

  4. Create the directory structure gradle/wrapper and copy the files gradle-wrapper.jar and gradle-wrapper.properties into the new directory.

  5. Tell your revision control system (e.g., Git) to ignore build, .gradle, .idea, and local.properties.

  6. Start Android Studio, choose “Open an existing Android Studio project,” and select the root of your project!

Example 1-4. Sample build.gradle starter file
apply plugin: 'com.android.application'

buildscript {
     repositories {
         jcenter()
     }
     dependencies {
         classpath 'com.android.tools.build:gradle:+'
     }
}

android {
     compileSdkVersion 24
     buildToolsVersion "24"

     defaultConfig {
         applicationId "YOUR.PACKAGE.NAME.HERE"
         minSdkVersion 15
         targetSdkVersion 23
         versionCode 1
         versionName "1.0"
     }

     sourceSets {
         main {
             // Comment next line out if using maven/gradle str src/main/java
             java.srcDirs = ['src']
             res.srcDirs = ['res']
             assets.srcDirs = ['assets']
             manifest.srcFile 'AndroidManifest.xml'
         }

         androidTest.setRoot('tests')
     }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:+'
}

A sample Unix/Linux/macOS shell script called add-gradle-to-eclipse that implements this (adapting the build.gradle to your src structure), as well as the files just mentioned, is provided in the Android Cookbook repository, in the subdirectory duellingIDEs (see “Getting and Using the Code Examples”).

Android Studio may need to “synchronize the project” and set up a few files the first time it opens this project, but you should eventually wind up with a working project structure like Figure 1-23.

ack2 0123
Figure 1-23. Android Studio editing an Eclipse project

Of course, if you have any extra JAR files you will need to tell Android Studio about them; if they are not already picked up by the reference to libs in the compile fileTree statement, you can either adjust that to refer to a different directory—older Eclipse projects used lib (singular), for example—or use the module settings as described in “Depend on a module or project”.

At this point your project should build and run under Android Studio (you may have to build it manually the first time, using the Build menu, to enable the green Run button). You will probably want to add the new files to your source repository.

Finally, commit (and push) your changes, and you should now have a working “bilingual” project!

1.14 Setting Up Eclipse with AndMore (Replacing ADT)

Daniel Fowler, Ian Darwin

Problem

You want to develop your Android applications using Eclipse, so a concise guide to setting up that IDE would be useful.

Solution

Many people use Eclipse for editing “standard Java” and Java Enterprise Edition (EE) projects. Some people would like to keep using the Eclipse IDE for developing Android apps. Configuring Eclipse is not a single-shot install; several stages need to be completed. This recipe provides details on those stages.

Discussion

The Eclipse integrated development environment for Java is one option for developing applications for Android. The formerly available Android Development Tools (ADT) plug-in has been discontinued by Google—it recommends switching to Android Studio (Recipe 1.8)—but ADT has arisen phoenix-like under the new name AndMore, from the Eclipse foundation. Like ADT (as well as the newer Android Studio), AndMore uses the Android Software Development Kit, which contains essential programs for developing Android software. To set up a development system you will need to download and install the following:

  • The Java Standard Edition Development Kit (JDK, not JRE)

  • The Eclipse IDE for Java Developers

  • The Android Software Development Kit

  • The AndMore Android plug-in (install from within Eclipse)

In the subsections that follow, we will cover these stages in detail for a PC running Windows. These steps have been tested on Windows 7 and 10 and on Mac OS X (though most of the screenshots and directory paths are Windows-based examples). Installing on Linux is similar but we haven’t tested these steps in current versions of Linux.

Installing the JDK, if necessary

Go to the Java download page. Click the Java icon to access the JDK downloads:

ack2 01in01

The list of JDK downloads will be shown. Click the Accept License Agreement radio button; otherwise, you will not be allowed to continue. You’ll want to download and run one of the latest JDKs present; as of this writing, they are Java 8 builds whose version string ends in 8u121, but that will surely change by the time you read this. Choose the download appropriate for your operating system: Windows x86 or 64-bit.exe, MacOS .dmg, Linux .rpm or .tgz, and so on. Accept any security warnings that appear, but only if you are downloading from the official Java download web page.

When the download has completed, run the installer and go through all the screens, clicking Next until the installer has finished. You should not need to change any options presented. When the JDK installer has completed, click the Finish button. A product registration web page may load; you can close this or you can choose to register your installation.

For Android use, you do not need to download any of the “demos or samples” from this site.

Installing Eclipse for Java development

Go to the Eclipse Downloads web page. The web page will usually autodetect your operating system (and 32- or 64-bit variant on systems that have this distinction); select the relevant (usually the latest) Eclipse IDE for Java Developers download link (see Figure 1-24).

On the next page you will be prompted to make a donation to the providers, the Eclipse Software Foundation, which is always a good thing to do when using open source software. The next step will download and run a typical software installer program. Then you will be prompted to specify the install location; the default is usually good (Figure 1-25).

ack2 0124
Figure 1-24. Choosing an Eclipse download
ack2 0125
Figure 1-25. Setting the Eclipse install location

You will eventually wind up with a lot of files in an Eclipse folder at that location, as shown in Figure 1-26 (obviously the exact list will vary from one release to the next). Enable the checkboxes to add a desktop icon, start menu entry, etc., as you prefer.

ack2 0126
Figure 1-26. Contents of the Eclipse folder

Run Eclipse so that it sets up a workspace. This will also check that both Java and Eclipse were installed correctly. When you run Eclipse a security warning may be displayed; select Run to continue. Accept the default workspace location or use a different directory.

You should install the SDK next, if you don’t already have it on your system.

Installing the Android SDK

Go to the Android Studio download page. The goal of this web page is to convince you to use Android Studio, but we just need the SDK. If you think you might want to use both Studio and Eclipse (see Recipe 1.13), you can install the “Studio and SDK” package here and share that SDK between the two IDEs—there is no need to download the SDK and all the add-ins twice! However, if you’re a dedicated Eclipse user, you can scroll to the very bottom of the page and get the “command line tools” (which are what both AndMore and Studio use). See Figure 1-27.

Choose the latest installer package for your operating system and run it. The Android SDK Tools installer will show some screens. Select the Next button on each screen; you should not need to change any options. Since C:\Program Files is a protected directory on some versions of MS Windows, you can either get permission to install there (“Run As Administrator”) or, as some developers do, install to your user folder or another directory—for example, C:\Android\android-sdk.

ack2 0127
Figure 1-27. Downloading the standalone SDK

When you click the Install button, a progress screen will briefly display while the Android files are copied. Click the final Next button and the Finish button at the end of the installation. If you left the Start SDK Manager checkbox ticked the SDK Manager will run. Otherwise, select SDK Manager from the Android SDK Tools program group (Start → All Programs → Android SDK Tools → SDK Manager). When the SDK Manager starts, the Android packages available to download are checked. Then a list of all available packages is shown, with some preselected for download. A Status column shows whether a package is installed or not. In Figure 1-28, you can see that the Android SDK Tools have just been installed but there is already an update available, as reflected in the Status column.

Check each package that needs to be installed. Multiple packages are available. These include SDK platform packages for each application programming interface (API) level, application samples for most API levels, Google Maps APIs, manufacturers’ device-specific APIs, documentation, source code, and a number of Google extra packages. From the “Extras,” you should install the Android Support Repository, Google Play Services, Google USB Driver if offered, and Intel X86 Emulator Accelerator (HAXM) if offered, and anything else that looks interesting.

ack2 0128
Figure 1-28. Android SDK Manager, showing installed and downloadable components

It is recommended that you download several SDK platforms to allow testing of apps against various device configurations. If in doubt about what to download, either accept the initial choices and rerun the SDK Manager to get other packages as and when required, or select all packages to download everything (the download may take a while). Click the “Install x packages” button.

The selected packages will be shown in a list; if a package has licensing terms that require acceptance, it is shown with a question mark. Highlight each package that has a question mark to read the licensing terms. You can accept or reject the package using the radio buttons. Rejected packages are marked with a red ×. Alternatively, click Accept All to accept everything that is available. Click the Install button and a progress log will show the packages being installed, as well as any errors that occur. On Windows a common error occurs when the SDK Manager is unable to access or rename directories. If this happens, rerun the SDK Manager as administrator and check that the directory does not have any read-only flags or files. When complete, close the SDK Manager by clicking the × button in the top corner of the window.

Note that, when updates to these packages become available, the SDK will notify you.

Installing the Android Tools (AndMore) plug-in

You install the ADT plug-in via Eclipse. Depending on where you installed Eclipse and/or the account you are using, you may need to run Eclipse with administrator privileges. If so, bring up the context menu (usually via a right-click), select “Run as administrator,” and accept any security warnings. On newer versions of Windows, and on macOS, you will get a prompt that the installer wants to make changes to your system. Say yes, as long as you’re installing from the official location.

If your Eclipse installation is so old that it lacks the “Eclipse Marketplace Client,” install it as per the instructions in Recipe 1.16. Start the Marketplace Client from the Help menu.

Type “andmore” in the search box on the left side of the Marketplace Client window, and press the Go button at the right side. Select AndMore in the search results. Click Install.

A screen displays the licenses; ensure that each license has been accepted (select the “I accept the terms of the license agreements” radio button). Then click the Finish button. A security warning may need to be accepted to complete the installation; select OK when you see this warning. Eclipse will ask you for a restart. Select the Restart Now button and Eclipse will close and reload, and then a “Welcome to Android Development” dialog will appear. Set the SDK location in the Existing Location box (since the SDK Manager will have already run), browse to the Android SDK folder (if you installed it somewhere other than the default location), and click Next (see Figure 1-29).

ack2 0129
Figure 1-29. Connecting the newly installed SDK to the newly installed AndMore plug-in

A Google Android SDK usage monitoring question will appear; change the option if required and click Finish. Eclipse is now configured to build and debug Android apps. See Recipe 3.1 to configure an Android emulator; then try Recipe 1.15 as a sanity check. Plug a physical device into the computer and use its settings to turn on USB Debugging (under Development in Applications).

1.15 Creating a “Hello, World” Application Using Eclipse

Ian Darwin

Problem

You want to use Eclipse to develop your Android application.

Solution

Install Eclipse and the AndMore plug-in. Then, create your project and start writing your app. Build it and test it under the emulator, from within Eclipse.

Discussion

Before you can start creating an app with Eclipse, you need to install these three items:

  • The Eclipse IDE

  • The Android SDK

  • The AndMore plug-in

For details on installing these items, please refer to Recipe 1.14.

Once you’ve done that, create a new Android project from the File → New menu, and you should see a screen like that in Figure 1-30.

ack2 0130
Figure 1-30. Starting to create an Eclipse project

Click Next. Give your new project a name (Figure 1-31), and click Next again.

ack2 0131
Figure 1-31. Setting parameters for a new Eclipse project

Select an SDK version to target (Figure 1-32). Version 4.0 gives you almost all the devices in use today; later versions give you more features. You decide.

ack2 0132
Figure 1-32. Setting SDK to target for a new Eclipse project

Figure 1-33 shows the project structure expanded in the Project panel on the right. It also shows the extent to which you can use Eclipse auto-completion within Android—I added the gravity attribute for the label, and Eclipse is offering a full list of possible attribute values. I chose center-horizontal, so the label should be centered when we get the application running.

ack2 0133
Figure 1-33. Using the Eclipse editor to set gravity on a TextView

In fact, if you set gravity to center_vertical on the LinearLayout and set it to center_horizontal on the TextView, the text will be centered both vertically and horizontally. Example 1-5 is the layout file main.xml (located under res/layout) that achieves this.

Example 1-5. The XML layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center_vertical"
    >
<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello"
    android:gravity="center_horizontal"
    />
</LinearLayout>

As always, Eclipse generates a compiled version whenever you save a source file. In an Android project it also creates the compiled, packaged APK that is ready to run, so you only need to run it. Right-click the project itself and select Run As → Android Application. (See Figure 1-34.)

ack2 0134
Figure 1-34. Running an Eclipse Android project

This will start the Android emulator if it’s not already running. The emulator window will start with the word Android by itself on the screen (Figure 1-35), then after a while, you’ll see the Android home screen.

ack2 0135
Figure 1-35. The Android project starting up in the emulator

After a little longer, your application should start up (Figure 1-36 only shows a screenshot of the application itself, since the rest of the emulator view is redundant).

ack2 0136
Figure 1-36. The Eclipse project running in the emulator

See Also

Recipe 1.5.

While a little dated, the blog “a little madness” has a detailed example of setting up an Eclipse project.

1.16 Installing the Eclipse Marketplace Client in Your Eclipse

Ian Darwin

Problem

The Eclipse Marketplace Client (MPC) is the best way to find and install Eclipse plug-ins. Some installations of Eclipse include the MPC, but some do not. Since the MPC is the easiest way to install new plug-ins into Eclipse, we document how to install it here.

Solution

If you don’t have the MPC in your Eclipse installation, use the traditional Install New Software mechanism to bootstrap the Marketplace Client.

Discussion

First, see if your Eclipse installation already has the Marketplace Client. Near the bottom of the Help menu, in the section with “Check for Updates,” you may or may not see an entry for “Eclipse Marketplace.” If you do, you are done. If not, continue.

To install the Marketplace, select “Install New Software” from the above-mentioned Eclipse menu. To see what versions of Marketplace Client are available, switch to a web browser and visit http://www.eclipse.org/mpc (see Figure 1-37).

Mouse over the link for the version of Eclipse you are running (e.g., Mars, which also works in Neon). Right-click, and select “copy link location” or whatever your browser calls that function. Switch back to Eclipse, and paste the URL in the “Work with” field on the Install dialog as shown in Figure 1-38. Click Add.

After a moment you will see some items to install. You probably don’t need the source code for the Marketplace Client, so you can deselect that, as in Figure 1-38. Click Next, Finish, Accept, and whatever it takes to complete this operation—remember that it will wake up part way through to remind you that you’re installing unsigned software.

ack2 0137
Figure 1-37. Download site for Eclipse Marketplace
ack2 0138
Figure 1-38. Install dialog for Eclipse Marketplace

When it’s done, and Eclipse has restarted, go back to the Help menu, and you should now see a menu item for the Eclipse Marketplace (see Figure 1-39). Click this, and you should see the market, looking something like Figure 1-40.

ack2 0139
Figure 1-39. Eclipse Marketplace now in menu

1.17 Upgrading a Project from Eclipse ADT to Eclipse AndMore

Ian Darwin

Problem

You have projects that were based on Eclipse ADT and want to use them with Eclipse AndMore.

Solution

Convert the projects by changing some strings in two config files, or use the AndMore “convert project” feature.

ack2 0140
Figure 1-40. Eclipse Marketplace in action

Discussion

Editing config files

In the .project file you will see lines referring to com.android.ide.eclipse.adt, like:

<projectDescription>
    <buildSpec>
        <buildCommand>
            <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
            ...
        </buildCommand>
    </buildSpec>
    <natures>
        <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
        <nature>org.eclipse.jdt.core.javanature</nature>
    </natures>
</projectDescription>

Change these to, for example, this:

<name>org.eclipse.andmore.ResourceManagerBuilder</name>

Similarly, in your .classpath you will see lines like:

<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>

Change them to, for example, this:

<classpathentry kind="con" path="org.eclipse.andmore.ANDROID_FRAMEWORK"/>

You can also make these changes globally. If you are experienced with command-line work you may have written a script like Brian Kernighan’s replace, which changes lines in a large number of files without your having to open each one in an editor. You can find this script (and its helpers) in my scripts project. Then you can change into the workspace root folder and convert tens or hundreds of projects with one command (be sure you have a backup, in case it goes wrong!):

$ cd workspace
$ replace com.android.ide.eclipse.adt org.eclipse.andmore */.classpath */.project

I in fact used that command to bulk-convert the Eclipse projects in the book’s GitHub repository. If you don’t like that approach, use the built-in AndMore converter to convert one project at a time.

Using the AndMore converter

Once you have installed AndMore as per Recipe 1.14, you can convert projects in your workspace. Open each project in Eclipse, with AndMore but not ADT installed, and you will see several errors, mainly to the effect that the class file for java.lang.Object cannot be found—a clear indication that the classpath is totally hosed (Figure 1-41).

ack2 0141
Figure 1-41. AndMore converter: Before, with classpath errors

To convert a project from ADT to AndMore, right-click the project in the Package Explorer and select Configure → Convert Android ADT Configuration (Figure 1-42).

ack2 0142
Figure 1-42. AndMore converter: Starting the conversion

Then just sit back and relax while the converter does its work and Eclipse rebuilds the project. It should wind up with no errors (Figure 1-43).

ack2 0143
Figure 1-43. AndMore converter: After, with errors resolved

You can repeat this for as many projects as you have.

1.18 Controlling Emulators/Devices Using Command-Line ADB

Rachee Singh

Problem

You have an application’s .apk file, and you want to install it on the emulator or a real device to check out the application, or because an application you are developing requires it.

Solution

Use the ADB command-line tool to install the .apk file onto the running emulator; you can also use this tool to install an .apk file onto a connected Android device, uninstall an .apk from such a device, list running/connected devices, etc.

Discussion

To install the .apk file, follow these steps:

  1. Find the location on your machine where you have installed the Android SDK. In the Android SDK directory, go to the tools directory.

  2. In the tools directory, look for an executable named adb. If it is not present, there should be an adb_has_moved.txt file. The contents of this file merely state that adb is present in the platform-tools directory instead of the tools directory.

  3. Once you have located the adb program, either cd to that location in a terminal (Linux) or command prompt (Windows), or, add that directory to your PATH, however that’s done on your operating system.

  4. Use the command adb install location of the .apk you want to install. If you get “command not found” on macOS or Linux, try using ./adb instead of just adb.

This should start the installation on the device that is currently running (either an emulator that is running on your desktop, or a physical Android device that is connected). You can also use adb to uninstall, but here, you must use the package name: e.g., adb uninstall com.example.myapp.

If you have more than one connected device or running emulator, you can list them with adb devices:

$ adb devices
List of devices attached
emulator-5554    device
ZX1G000BXB    device

$

In this listing, the ZX entry is a Nexus device, and one emulator is running.

When you only have one device connected or one emulator running, you can simply use adb -d … or adb -e …, respectively. There are also command-line options that let you refer to an emulator by its port number (port numbers are displayed at the top of the emulator window; they are the TCP/IP communications ports that start at 5554 and increment by 2 for each running emulator) or by its device serial number for a real device. The emulator in the preceding adb devices output is listening on TCP port 5554 for connections from adb.

One more thing you can do: adb shell gets you a Unix command-line shell on the device, which can be useful for developers to have access to. Unless your device is “rooted” it will run as a nonprivileged user, but at least you can look around, copy public files, etc.

After the installation finishes, you should see the icon of the application you just installed in the application drawer of the Android device/emulator. In this example we installed the HelloMaven application from Recipe 1.6, so the HelloMaven app icon appears near the lower left of Figure 1-44.

ack2 0144
Figure 1-44. The HelloMaven app icon in the app drawer after installation completes

The adb command with no arguments, or invalid arguments, prints a very long “help text,” which lists all its options.

1.19 Sharing Java Classes from Another Eclipse Project

Ian Darwin

Problem

You want to use a class from another project, but you don’t want to copy and paste.

Solution

Add the project as a “referenced project,” and Eclipse (and DEX) will do the work.

Discussion

Developers often need to reuse classes from another project. In my JPSTrack GPS tracking program, the Android version borrows classes such as the file I/O module from the Java SE version. You surely do not want to copy and paste classes willy-nilly from one project into another, because this makes maintenance improbable.

In the simplest case, when the library project contains the source of the classes you want to import, all you have to do is declare the project containing the needed classes (the Java SE version in my case) as a referenced project on the build path. Select Project → Properties → Java Build Path, select Projects, and click Add. In Figure 1-45, I am adding the SE project “jpstrack” as a dependency on the Android project “jpstrack.android.”

ack2 0145
Figure 1-45. Making one Eclipse project depend on another—using standard Eclipse

Alternatively, create a JAR file using either Ant or the Eclipse wizard. Have the other project refer to it as an external JAR in the classpath settings, or physically copy it into the libs directory and refer to it from there.

A newer method that is often more reliable and is now officially recommended, but is only useful if both projects are Android projects, is to declare the library one as a library project, under Project → Properties → Android → Library tab, and use the Add button on the other project on the same screen to list the library project as a dependency on the main project (see Figure 1-46).

ack2 0146
Figure 1-46. Making one project depend on another—using AndMore

For command-line fans, the first method involves editing the .classpath file, while the second method simply creates entries in the project.properties file. For example:

# Project target
target=android-7
android.library=false
android.library.reference.1=../wheel

Since you are probably keeping both projects under source control (and if these are programs you ever intend to ship, you should be!), remember to “tag” both projects when you release the Android project—one of the points in favor of source control is that you are able to re-create exactly what you shipped.

See Also

The Android Studio User Guide documentation on library projects.

1.20 Referencing Libraries to Implement External Functionality

Rachee Singh

Problem

You need to reference an external library in your source code.

Solution

There are several solutions:

  • Use Maven or Gradle to build your project. Just list a Maven or Gradle dependency, and your build tool will download and verify it.

  • Depend on a module (Studio) or a library project.

  • (last resort) Download the JAR file for the library that you require and add it to your project.

Discussion

We describe here various mechanisms for downloading and including JAR files into your projects. We do not discuss the burden of responsibility for licensing issues when including third-party JAR files; that’s between you and your organization’s legal department. Please be aware of, and comply with, the license requirements of JAR files that you use!

List the dependency

Few developers want to download JAR files explicitly, when tools like Maven and Gradle will handle dependency management for them. To use an external API, you need only find the correct “coordinates” and list them. The coordinates consist of three parts:

  • A group ID, which is often based on the JAR developer’s domain name and project name, such as com.darwinsys, org.apache.commons, etc.

  • An artifact ID, which is the name of the particular project from the developer, such as darwinsys-api, commons-logging-api, etc.

  • A version number/string, such as 1.0.1, 1.2.3-SNAPSHOT, 8.1.0-Stable, etc.

These three parts are combined into a form like this for Maven in build.xml:

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging-api</artifactId>
    <version>1.1</version>
</dependency>

For Gradle you use a shorter form in build.gradle:

compile 'commons-logging:commons-logging-api:1.1'

This shorter form of the coordinates is often used in documentation, even when working with other tools like Maven!

How do you find this information for a particular API? If you know the name of the API, you can use the ever-popular Maven Central site. Type the API name (e.g., commons-logging) into the search box, pick a match from the list, select the default version number (unless you know you need an older version), pick your build tool if it’s other than Maven (Maven is the default because the Maven people built up this entire wonderful infrastructure!), and copy and paste the dependency into your build tool. Alternatively, you can use your IDE (Studio, or Eclipse with the M2E plug-in) to explicitly add the dependency.

Once the dependency is in your build file, just build your project! If the JAR you need hasn’t been downloaded already, the build tool will download it and save it in a private repository (e.g., for Maven, in $USER_HOME/.m2/repository), so you never have to download it again until you change the version number you’re depending on.

For more on Apache Maven, see https://maven.apache.org. For more on Gradle, see https://gradle.org. The Gradle people are pretty zealous propagandists about their tool; the Maven people are a bit more laid back.

Note that in the future, if you develop an API that might be useful to others and are willing to make it available, and have a community around it or other ways of getting the word out about it, you are encouraged to give back to this pool of software; see http://central.sonatype.org to contribute your JAR.

Depend on a module or project

In Android Studio, right-click in the Project pane (upper left of screen) and select Open Module Settings. Choose the Dependencies tab. Click the Add button (the “+” sign in the lower left). Choose Library to use an existing library, File for a JAR file, or Module for a module in your project. Suppose you want to depend on my darwinsys-api JAR file, and have it downloaded from Maven Central so you don’t have to keep track of the file. Select the Library choice. Enter darwinsys in the search box, and press Enter. After a second or two, you should see the list of darwinsys projects. Click the darwinsys-api entry (the first one in Figure 1-47). If you need to add more libraries, repeat the Add process. When done, click OK. The build files will be updated and the dependency downloaded, and it will be available on your classpath.

ack2 0147
Figure 1-47. Adding a dependency in Android Studio

For Eclipse:

  • If you are using Maven and the M2E plug-in, you can add a Maven dependency by editing your pom.xml file, then right-click the project and select Maven → Update Project.

  • If you are not using Maven, use the following steps to download a JAR file manually.

Download a JAR file manually

Suppose you need to use AndroidPlot, a library for plotting charts and graphs in your application, or OpenStreetMap, a wiki project that creates and provides free geographic data and mapping. If so, your application needs to reference these libraries. You can do this in Eclipse in a few simple steps:

  1. Download the JAR file corresponding to the library you wish to use.

  2. With your Android project open in Eclipse, right-click the project name and select Properties in the menu (Figure 1-48).

  3. From the list on the left side, select Java Build Path and click the Libraries tab (Figure 1-49).

  4. Click the Add External JARs button (Figure 1-50).

  5. Provide the location where you downloaded the JAR file for the library you wish to use.

ack2 0148
Figure 1-48. Selecting project properties

At this point you will see a Referenced Libraries directory in your project. The JARs you added will appear.

An alternative approach is to create a libs folder in your project, physically copy the JAR files there, and add them individually as you did earlier, but instead clicking the Add JARs button. This keeps everything in one place (especially useful if your project is shared via a version control system with others who might use a different operating system and be unable to locate the external JARs in the same place).

Whichever way you do it, it’s pretty easy to add libraries to your project.

ack2 0149
Figure 1-49. Adding libraries to an Eclipse project
ack2 0150
Figure 1-50. Adding the external JAR file

1.21 Using New Features on Old Devices via the Compatibility Libraries

Ian Darwin

Problem

You want to use new features of Android but have your application run correctly on older versions.

Solution

Use the compatibility libraries—that’s what they’re there for.

Discussion

Android is a wonderful system for users—new features are added with every release. But there’s a problem—older devices don’t get the latest version of Android. Manufacturers at the low end (low-budget devices) might not ever issue an update to their devices. At the higher end (so-called “flagship” devices), users usually get 2 or 3 years of updates from the manufacturer. But manufacturers (and cell phone carriers) expect users to update often, like car manufacturers who come out with new models every year to embarrass owners into buying upgrades they don’t really need.

The downside of this for us as developers is that some features that have been added in modern versions, such as Android 7, will not exist on some users’ devices. If you don’t take this into account, you may wind up calling methods that exist in modern versions but not in the library on every user’s device. This will, of course, end badly.

The solution is the compatibility libraries. These provide replacement versions of common classes (such as Activity) that use only features found on the older Android version, but provide the functionality of newer versions.

Now, you might think that old versions fade away quickly, but a look at the Android Versions dashboard reveals this to be true only from a certain point of view (Figure 1-51).

Notice that Froyo, API 8, is at 0.1%, which is the threshold for inclusion. Thus you’d expect it to disappear any day now, but in fact, it’s been at 0.1% for several months. There are an estimated 1.5 billion Android devices. So 0.1% of that means there are still a million and a half active users of Froyo devices. For Gingerbread there are still 25 million active devices. If you’re prepared to overlook 26 million potential customers, fine. But even then it’s not that simple—there are features that were added in Android 4, 5, 6, … How do you keep track of what features are in what versions? For the most part, you don’t need to. That is, if you use the compatibility libraries!

ack2 0151
Figure 1-51. Android Platform Versions dashboard (as of August 2016)

If you create a new project using Android Studio, the project will by default use the compatibility library. If you are working on a project that doesn’t have “compat” support, you can add it easily. One way is to add the library manually by editing your build file to include the library with coordinates (see Recipe 1.20) com.android.support:design:24.1.1; add this to pom.xml for Maven or app/build.gradle for Android Studio (you may have to do a “Refresh” or “Project Sync” afterward). In Android Studio you can also select the “app” module and select Module Properties → Dependencies, click Add, and select the latest version of the compatibility library.

Then, the most important change is to ensure that your Activities—any that will need the latest facilities—are based on AppCompatActivity rather than the regular Activity:

public class MainActivity extends AppCompatActivity {
    ...
}

There are some other places where the “appcompat” libraries enter into the view of the coder; we will mostly call these out in place in the rest of this book.

1.22 Using SDK Samples to Help Avoid Head Scratching

Daniel Fowler

Problem

Sometimes it is a struggle to code up some functionality, especially when the documentation is sketchy or does not provide any examples.

Solution

Looking at existing working code will help. The Android SDK has sample programs that you can pick apart to see how they work.

Discussion

The Android SDK comes with many sample applications that can be useful when trying to code up some functionality. Looking through the sample code can be instructive. Once you have installed the Android SDK, several samples become available. In Android Studio, you can examine the list, and make a runnable copy of one, using the Import Sample wizard (File → Import Sample). The available samples change over time; part of the list as of late 2016 is shown in Figure 1-52. Some of the samples feature screenshot previews, as does the Camera app currently selected. All the samples are individual repositories that can also be directly downloaded from Google Samples.

ack2 0152
Figure 1-52. Google Android samples

To open a sample project, just select it in the list, and click Next. You will be given the option to change the application’s name and where it is stored. Click Finish and the project will be created.

After a short time, the sample will open as a project, and you will be able to browse the source code to see how it is all done.

See Also

The samples at Google Samples; Android Developers; and this cookbook, of course.

You can also search the web for additional programs or examples. If you still can’t find what you need, you can seek help from Stack Overflow, using “android” as the tag, or from the Internet Relay Chat (IRC) channel #android-dev on Freenode.

1.23 Taking a Screenshot/Video from the Emulator/Android Device

Rachee Singh, Ian Darwin

Problem

You want to take a screenshot or video of an application running on an Android device.

Solution

For screenshots, use any one of the following:

  • The camera button in the toolbar of modern emulators

  • On a physical device, your device’s hardware buttons

  • The Device Screen Capture feature of the Dalvik Debug Monitor Server (DDMS) view

  • adb screencap

For videos, use adb screenrecord.

Discussion

Capturing screenshots

There are several methods of recording screen captures. Modern versions of the Android emulator or AVD feature a toolbar with a camera button, which works as long as the emulated device is based on API 14 or higher. This is shown in Figure 1-53.

ack2 0153
Figure 1-53. Android emulator camera button

Pictures taken with this technique are stored on your desktop computer rather than on the device:

$ ls -lt ~/Desktop | head -2
total 12345
-rw-r--r--    1 ian  staff    254317 Nov  6 10:05 Screenshot_1478444709.png
open ~/Desktop/Screenshot_1478444709.png
$

On a Mac, the open command will normally cause the file to be opened (obvious, eh?) in the user’s chosen image handling application (Preview by default).

If you are running on a real device, use the built-in hardware feature for screen capture. This varies per device, but on many commercial devices long-pressing both the Power and Volume Down (or Volume Up and Volume Down), buttons at the same time will work—you’ll hear a camera sound, and on modern devices you’ll see a notification. You then have to locate and pull the file from your device, either using the Android Device Monitor in the IDE or from the command line, as shown here:

$ adb -d ls /sdcard/Pictures/Screenshots
$ adb -d pull /sdcard/Pictures/Screenshots/Screenshot_20160820-104327.png x.png
copy it someplace safe
$ adb -d shell rm /sdcard/Pictures/Screenshots/Screenshot_20160820-104327.png

This gets a listing (ls) of files in the Screenshots folder (whose location may vary slightly on different devices or versions) from the physical device (-d). Using -d avoids you having to shut down your running emulators or specify the device’s long name. Then we pull the file from the device to the desktop machine, picking a meaningful name for it in the process. After backing it up, we return here and remove (rm) the file from the device. You don’t have to do this, but if you don’t it will get harder and harder to find the images, as the ls output will get longer and longer, and it’s not displayed in any useful order.

It is believed that Android 7.1 will allow you to take a “partial screenshot” by starting in a similar fashion (perhaps pressing Power + Volume Up?), then dragging to select a region on the screen; the code exists but this is an unannounced feature, so we’ll have to see whether it becomes available.

To use the DDMS Device Screen Capture feature, follow these steps:

  1. Run the application in the IDE and go to the DDMS view (Window menu → Open Perspective → Other → DDMS or Window menu → Show View → Other → Android → Devices); the former is shown in Figure 1-54).

    ack2 0154
    Figure 1-54. Starting DDMS view
  2. In the DDMS view, select the device or emulator whose screen you want to capture.

  3. In the DDMS view, click the Screen Capture icon (see Figure 1-55).

    ack2 0155
    Figure 1-55. Device Screen Capture
  4. A window showing the current screen of the emulator/Android device will pop up. It should look like Figure 1-56. You can save the screenshot and use it to document the app.

ack2 0156
Figure 1-56. The screenshot

Alternatively, to do this at a command-line level, use adb. You will have to use adb shell to run the “mount” command to find a writable directory on your particular device, since most Android versions do not feature the otherwise-universal /tmp folder. Once that’s done, use adb shell to run the screencap program on the device, then “pull” the file down to your desktop, as shown here:

$ adb shell screencap -p /mnt/asec/pic.png # Now in a file on the device
$ adb -e pull /mnt/asec/pic.png            # Now in a file on my dev machine
[100%] /mnt/asec/pic.png
$ ls -l pic.png                            # Make sure!
-rw-r--r--@ 1 ian  staff  59393 Jun 21 17:30 pic.png
$ adb shell rm /mnt/asec/pic.png           # Free up storage on the device
$ # ... now do something with pic.png on the developer machine

If you create your screenshot with the on-device key sequence (usually pressing and holding Power and Volume Down at the same time), the screenshot will be created in a fixed directory with a datestamp in the name; you can then “list” (ls) the directory and pull the file down and rename it to a better name:

$ adb -d ls /sdcard/Pictures/Screenshots
000041f9 00001000 57c62dd8 .
000041f9 00001000 578f7813 ..
000081b0 000a231c 55760303 Screenshot_20150608-170256.png
000081b0 0001176d 55760308 Screenshot_20150608-170303.png
000081b0 0006b9b4 557a1619 Screenshot_20150611-191328.png
000081b0 0001968a 55869982 Screenshot_20150621-070121.png
... and a bunch more ...
$ adb -d pull /sdcard/Pictures/Screenshots/Screenshot_20160714-093303.png
[100%] /sdcard/Pictures/Screenshots/Screenshot_20160714-093303.png
$ mv Screenshot_2016-07-14-09-33-03.png SomeBetterName.png
$

The datestamp portion of the filenames is, in case it isn’t obvious, in the ISO international standard order—year, month, and day, then a dash (-), then hour, minute, and second—though not quite in the ISO format.

Capturing screen video

To record your device’s screen for documentation or screencast purposes, you need to run the screenrecord program on the device, which creates a file there, then use adb pull or other means to bring the file over to your desktop system. Remember that both your desktop computer and your device have a similar hierarchical filesystem (if you run Unix, including macOS, or Linux, the filesystem structure is almost identical, since Android is based on Linux, and Linux began as a copy of Unix). Just keep in mind which filenames are on which computer! The adb command communicates with the device and runs a “shell” or command interpreter, which we use to run the screenrecord command on the device, capturing output into a temporary file in the /sdcard directory:

$ adb shell screenrecord /sdcard/tmpFileName.mp4

Now you can interact with the application to show the bug or feature that you want to document. When you are done, stop the adb command, e.g., with Ctrl-C in the terminal window. Then pull the temporary file from the /sdcard folder on the device to some convenient place on your desktop (you can assign it a better name at the same time if you like, such as myScreencastSegment.mp4):

$ adb pull /sdcard/tmpFileName.mp4 myScreencastSegment.mp4

You can then view or post-process the video using whatever tools you have on your desktop system. Figure 1-57 shows a simple video, recorded and downloaded in this way, playing on my desktop.

Once the video is safely on your desktop (and backed up!), you’ll probably want to remove the file from your device so you don’t run out of disk space:

$ adb rm /sdcard/tmpFileName.mp4
ack2 0157
Figure 1-57. Captured video playing

See Also

The Android Studio User Guide documentation on the screencap tool and screenrecord tool.

1.24 Program: A Simple CountDownTimer Example

Wagied Davids

Problem

You want a simple countdown timer, a program that will count down a given number of seconds until it reaches zero.

Solution

Android comes with a built-in class for constructing CountDownTimers. It’s easy to use, it’s efficient, and it works (that goes without saying!).

Discussion

The steps to provide a countdown timer are as follows:

  1. Create a subclass of CountDownTimer. This class’s constructor takes two arguments: CountDownTimer(long millisInFuture, long countDownInterval). The first is the number of milliseconds from now when the interval should be done; at this point the subclass’s onFinish() method will be called. The second is the frequency in milliseconds of how often you want to get notified that the timer is still running, typically to update a progress monitor or otherwise communicate with the user. Your subclass’s onTick() method will be called with each passage of this many milliseconds.

  2. Override the onTick() and onFinish() methods.

  3. Instantiate a new instance in your Android Activity.

  4. Call the start() method on the new instance created!

The example Countdown Timer program consists of an XML layout (shown in Example 1-6) and some Java code (shown in Example 1-7). When run, it should look something like Figure 1-58, though the times will probably be different.

Example 1-6. main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <Button
        android:id="@+id/button"
        android:text="Start"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <TableLayout
        android:padding="10dip"
        android:layout_gravity="center"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
        <TableRow>
            <TextView
                android:id="@+id/timer"
                android:text="Time: "
                android:paddingRight="10dip"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

            <TextView
                android:id="@+id/timeElapsed"
                android:text="Time elapsed: "
                android:paddingRight="10dip"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </TableRow>
    </TableLayout>
</LinearLayout>
Example 1-7. Main.java
package com.examples;

import android.app.Activity;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class Main extends Activity implements OnClickListener {
        private MalibuCountDownTimer countDownTimer;
        private long timeElapsed;
        private boolean timerHasStarted = false;
        private Button startB;
        private TextView text;
        private TextView timeElapsedView;

        private final long startTime = 50 * 1000;
        private final long interval = 1 * 1000;

        /** Called when the Activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);
                startB = (Button) this.findViewById(R.id.button);
                startB.setOnClickListener(this);

                text = (TextView) this.findViewById(R.id.timer);
                timeElapsedView = (TextView) this.findViewById(R.id.timeElapsed);
                countDownTimer = new MalibuCountDownTimer(startTime, interval);
                text.setText(text.getText() + String.valueOf(startTime));
            }

        @Override
        public void onClick(View v) {
                if (!timerHasStarted) {
                        countDownTimer.start();
                        timerHasStarted = true;
                        startB.setText("Start");
                    }
                else {

                        countDownTimer.cancel();
                        timerHasStarted = false;
                        startB.setText("RESET");
                    }
            }

        // CountDownTimer class
        public class MalibuCountDownTimer extends CountDownTimer {

                public MalibuCountDownTimer(long startTime, long interval) {
                        super(startTime, interval);
                    }

                @Override
                public void onFinish() {
                        text.setText("Time's up!");
                        timeElapsedView.setText("Time Elapsed: " +
                            String.valueOf(startTime));
                    }

                @Override
                public void onTick(long millisUntilFinished) {
                        text.setText("Time remain:" + millisUntilFinished);
                        timeElapsed = startTime - millisUntilFinished;
                        timeElapsedView.setText("Time Elapsed: " +
                            String.valueOf(timeElapsed));
                    }
            }
    }
ack2 0158
Figure 1-58. Timer reset

Source Download URL

The source code for this example is in the Android Cookbook repository, in the subdirectory CountDownTimerExample (see “Getting and Using the Code Examples”).

1.25 Program: Tipster, a Tip Calculator for the Android OS

Sunit Katkar

Problem

When you go with friends to a restaurant and wish to divide the check and tip, you can get into a lot of manual calculations and disagreements. Instead, you want to use an app that lets you simply add the tip percentage to the total and divide by the number of diners. Tipster is an implementation of this in Android, to show a complete application.

Solution

This is a simple exercise that uses the basic GUI elements in Android, with some simple calculations and some event-driven UI code to tie it all together.

Discussion

Android uses XML files for the layout of widgets. In our example project, the Android plug-in for Eclipse generates a main.xml file for the layout. This file has the XML-based definitions of the different widgets and their containers.

There is a strings.xml file, which has all the string resources used in the application. A default icon.png file is provided for the application icon.

Then there is the R.java file, which is automatically generated (and updated when any changes are made to any XML resource file). This file has the constants defined for each layout and widget. Do not edit this file manually; the SDK tooling does it for you when you make any changes to your XML files. In our example we have Tipster.java as the main Java file for the Activity.

Creating the layout and placing the widgets

The end goal is to create a layout similar to the one shown in Figure 1-59. For this screen layout we will use the following layouts and widgets:

TableLayout

THis lays out View components in tabular form. Analogous to the HTML Table tag paradigm.

TableRow

This defines a row in the TableLayout. It’s like the HTML TR and TD tags combined.

TextView

This View provides a label for displaying static text on the screen.

EditText

This View provides a text field for entering values.

RadioGroup

This groups together “radio buttons,” only one of which can be pressed at a time (named by analogy with the station selection button on a car radio).

RadioButton

This provides a radio button, for use in a group.

Button

This is a regular button.

View

We will use a View to create a visual separator with certain height and color attributes.

Familiarize yourself with these widgets, as you will be using these quite a lot in applications you build. When you go to the Javadocs for the given layout and widget components, look up the XML attributes. This will help you correlate the usage in the main.xml layout file and the Java code (Tipster.java and R.java) where these are accessed.

Also available is a visual layout editor in the IDE, which lets you create a layout by dragging and dropping widgets from a palette, like any form designer tool. However, it’s probably a good exercise for you to create the layout by hand in XML, at least in your initial stages of learning Android. Later on, as you learn all the nuances of the XML layout API, you can delegate the task to such tools.

The layout file, main.xml, has the layout information (see Example 1-8). A TableRow widget creates a single row inside the TableLayout, so you use as many TableRows as the number of rows you want. In this tutorial we will use eight TableRows—five for the widgets up to the visual separator below the buttons, and three for the results area below the buttons and separator.

Example 1-8. /res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Using TableLayout to have HTML table-like control over layout -->
<TableLayout
        android:id="@+id/TableLayout01"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:stretchColumns="1"
        xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Row 1: Text label placed in column zero,
         text field placed in column two and allowed to
         span two columns. So a total of 4 columns in this row -->
        <TableRow>
        <TextView
                android:id="@+id/txtLbl1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_column="0"
                android:text="@string/textLbl1"/>
        <EditText 1
                android:id="@+id/txtAmount"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:numeric="decimal"
                android:layout_column="2"
                android:layout_span="2"
                />
        </TableRow>
    <!-- Row 2: Text label placed in column zero,
         text field placed in column two and allowed to
         span two columns. So a total of 4 columns in this row -->
        <TableRow>
        <TextView
                android:id="@+id/txtLbl2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_column="0"
                android:text="@string/textLbl2"/>
        <EditText 2
                android:id="@+id/txtPeople"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:numeric="integer"
                android:layout_column="2"
                android:layout_span="2"/>
        </TableRow>
   <!-- Row 3: This has just one text label placed in column zero  -->
        <TableRow>
        <TextView
                android:id="@+id/txtLbl3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/textLbl3"/>
        </TableRow>
   <!-- Row 4: RadioGroup for RadioButtons placed at column zero
        with column span of three, thus creating one radio button
        per cell of the table row. Last cell number 4 has the
        textfield to enter a custom tip percentage -->
        <TableRow>
        <RadioGroup
                android:id="@+id/RadioGroupTips"
                android:orientation="horizontal"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_column="0"
                android:layout_span="3"
                android:checkedButton="@+id/radioFifteen">
                <RadioButton android:id="@+id/radioFifteen"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/rdoTxt15"
                        android:textSize="15sp" />
                <RadioButton android:id="@+id/radioTwenty"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/rdoTxt20"
                        android:textSize="15sp" />
                <RadioButton android:id="@+id/radioOther"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/rdoTxtOther"
                        android:textSize="15sp" />
        </RadioGroup>
                <EditText
                        android:id="@+id/txtTipOther"
                        android:layout_width="fill_parent"
                        android:layout_height="wrap_content"
                        android:numeric="decimal"/>
        </TableRow>
   <!--  Row for the Calculate and Rest buttons. The Calculate button
         is placed at column two, and Reset at column three -->
        <TableRow>
        <Button
                android:id="@+id/btnReset"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_column="2"
                android:text="@string/btnReset"/>
        <Button
                android:id="@+id/btnCalculate"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_column="3"
                android:text="@string/btnCalculate"/>
        </TableRow>

    <!-- TableLayout allows any other views to be inserted between
         the TableRow elements. So insert a blank view to create a
         line separator. This separator view is used to separate
         the area below the buttons which will display the
         calculation results -->
        <View
                android:layout_height="2px"
                android:background="#DDFFDD"
                android:layout_marginTop="5dip"
                android:layout_marginBottom="5dip"/>

    <!-- Again TableRow is used to place the result textviews
         at column zero and the result in textviews at column two -->
        <TableRow android:paddingBottom="10dip" android:paddingTop="5dip">
        <TextView
                android:id="@+id/txtLbl4"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_column="0"
                android:text="@string/textLbl4"/>
        <TextView
                android:id="@+id/txtTipAmount"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_column="2"
                android:layout_span="2"/>
        </TableRow>

        <TableRow android:paddingBottom="10dip" android:paddingTop="5dip">
        <TextView
                android:id="@+id/txtLbl5"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_column="0"
                android:text="@string/textLbl5"/>
        <TextView
                android:id="@+id/txtTotalToPay"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_column="2"
                android:layout_span="2"/>
        </TableRow>

        <TableRow android:paddingBottom="10dip" android:paddingTop="5dip">
        <TextView
                android:id="@+id/txtLbl6"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_column="0"
                android:text="@string/textLbl6"/>
        <TextView
                android:id="@+id/txtTipPerPerson"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_column="2"
                android:layout_span="2"/>
        </TableRow>
    <!--  End of all rows and widgets -->
</TableLayout>

TableLayout and TableRow

After examining main.xml, you can gather that TableLayout and TableRow are straightforward to use. You create the TableLayout once, then insert a TableRow. Now you are free to insert any other widgets, such as TextView, EditView, and so on, inside this TableRow.

Do look at the attributes, especially android:stretchColumns, android:layout_column, and android:layout_span, which allow you to place widgets the same way you would use a regular HTML table. I recommend that you follow the links to these attributes and read up on how they work for a TableLayout.

Controlling input values

Look at the EditText widget in the main.xml file at 1. This is the first text field for entering the “Total Amount” of the check. We want only numbers here. We can accept decimal numbers because real restaurant checks can be for dollars and cents—not just dollars, so we use the android:numeric attribute with a value of decimal. This will allow whole values like 10 and decimal values like 10.12, but will prevent any other type of entry.

Similarly, 2 uses android:integer because you can’t eat dinner with half a guest!

This is a simple and concise way to control input values, thus saving us the trouble of writing validation code in the Tipster.java file and ensuring that the user does not enter nonsensical values. This XML-based constraints feature of Android is quite powerful and useful. You should explore all the possible attributes that go with a particular widget to extract the maximum benefit from this XML shorthand way of setting constraints. In a future release, unless I have missed it completely in this release, I hope that Android allows for entering ranges for the android:numeric attribute so that we can define what range of numbers we wish to accept.

Since ranges are not currently available (to the best of my knowledge), you will see later on that we do have to check for certain values like zero or empty values to ensure that our tip calculation arithmetic does not fail.

Examining Tipster.java

Now we will look at the Tipster.java file, which controls our application. This is the main class that does the layout and event handling and provides the application logic. The Android Eclipse plug-in creates the Tipster.java file in our project with the default code shown in Example 1-9.

Example 1-9. Code snippet 1 of TipsterActivity.java
package com.examples.tipcalc;

import android.app.Activity;

public class Tipster extends Activity {
    /** Called when the Activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

The Tipster class extends the android.app.Activity class. An Activity is a single, focused thing that the user can do. The Activity class takes care of creating the window and then laying out the UI. You have to call the setContentView(View view) method to put your UI in the Activity, so think of Activity as an outer frame that is empty and that you populate with your UI.

Now look at the snippet of the Tipster.java class shown in Example 1-10. First we define the widgets as class members. (Look at 1 through 2 in particular for reference.) Then we use the findViewById(int id) method to locate the widgets. The ID of each widget, defined in your main.xml file, is automatically defined in the R.java file when you clean and build the project in Eclipse. (By default, Eclipse is set to build automatically, so the R.java file is instantaneously updated when you update main.xml.)

Each widget is derived from the View class and provides special GUI features, so a TextView provides a way to put labels on the UI, while EditText provides a text field. Look at 3 through 6 in Example 1-10. You can see how findViewById() is used to locate the widgets.

Example 1-10. Code snippet 2 of TipsterActivity.java
public class Tipster extends Activity {
    // Widgets in the application
    private EditText txtAmount; 1
    private EditText txtPeople;
    private EditText txtTipOther;
    private RadioGroup rdoGroupTips;
    private Button btnCalculate;
    private Button btnReset;

    private TextView txtTipAmount;
    private TextView txtTotalToPay;
    private TextView txtTipPerPerson; 2

    // For the ID of the radio button selected
    private int radioCheckedId = -1;

    /** Called when the Activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Access the various widgets by their ID in R.java
        txtAmount = (EditText) findViewById(R.id.txtAmount); 3
        // On app load, the cursor should be in the Amount field
        txtAmount.requestFocus(); 4

        txtPeople = (EditText) findViewById(R.id.txtPeople);
        txtTipOther = (EditText) findViewById(R.id.txtTipOther);

        rdoGroupTips = (RadioGroup) findViewById(R.id.RadioGroupTips);

        btnCalculate = (Button) findViewById(R.id.btnCalculate);
        // On app load, the Calculate button is disabled
        btnCalculate.setEnabled(false); 5

        btnReset = (Button) findViewById(R.id.btnReset);

        txtTipAmount = (TextView) findViewById(R.id.txtTipAmount);
        txtTotalToPay = (TextView) findViewById(R.id.txtTotalToPay);
        txtTipPerPerson = (TextView) findViewById(R.id.txtTipPerPerson); 6

        // On app load, disable the Other Tip Percentage text field
        txtTipOther.setEnabled(false); 7

Addressing ease of use or usability concerns

Our application must try to be as usable as any other established application or web page. In short, adding usability features will result in a good user experience. To address these concerns, look at Example 1-10 again.

First, look at 4 where we use the requestFocus() method of the View class. Since the EditText widget is derived from the View class, this method is applicable to it. This is done so that when our application loads, the Total Amount text field will receive focus and the cursor will be placed in it. This is similar to popular web application login screens where the cursor is present in the Username text field.

Now look at 5 where the Calculate button is disabled by calling the setEnabled(boolean enabled) method on the Button widget. This is done so that the user cannot click on it before entering values in the required fields. If we allowed the user to click Calculate without entering values in the Total Amount and No. of People fields, we would have to write validation code to catch these conditions. This would entail showing an alert pop-up warning the user about the empty values. This adds unnecessary code and user interaction. When the user sees the Calculate button disabled, it’s quite obvious that unless all values are entered, the tip cannot be calculated.

Now look at 7 in Example 1-10. Here, the Other Tip Percentage text field is disabled. This is done because the “15% tip” radio button is selected by default when the application loads. This default selection on application load is done via the main.xml file, in the following statement:

android:checkedButton="@+id/radioFifteen"

The RadioGroup attribute android:checkedButton allows you to select one of the RadioButton widgets in the group by default.

Most users who have used popular applications on the desktop as well as the web are familiar with the “disabled widgets enabled in certain conditions” paradigm. Adding such small conveniences always makes an application more usable and the user experience richer.

Processing UI events

Like popular Windows, Java Swing, Flex, and other UI frameworks, Android provides an event model that allows you to listen to certain events in the UI caused by user interaction. Let’s see how we can use the Android event model in our application.

First let’s focus on the radio buttons in the UI. We want to know which radio button the user selected, as this will allow us to determine the tip percentage in our calculations. To “listen” to radio buttons, we use the static interface OnCheckedChangeListener(). This will notify us when the selection state of a radio button changes.

In our application, we want to enable the Other Tip Percentage text field only when the Other radio button is selected. When the “15% tip” or “20% tip” button is selected, we want to disable this text field. Besides this, we want to add some more logic for the sake of usability. As we discussed before, we should not enable the Calculate button until all the required fields have valid values. In terms of the three radio buttons, we want to ensure that the Calculate button gets enabled for the following two conditions:

  • The Other radio button is selected and the Other Tip Percentage text field has a valid value.

  • The “15% tip” or “20% tip” radio button is selected and the Total Amount and No. of People text fields have valid values.

Look at Example 1-11, which deals with the radio buttons. The source code comments are quite self-explanatory.

Example 1-11. Code snippet 3 of TipsterActivity.java
/*
* Attach an OnCheckedChangeListener to the
* radio group to monitor radio buttons selected by user
*/
rdoGroupTips.setOnCheckedChangeListener(new OnCheckedChangeListener() {

   @Override
   public void onCheckedChanged(RadioGroup group, int checkedId) {
      // Enable/disable Other Tip Percentage field
     if (checkedId == R.id.radioFifteen
                || checkedId == R.id.radioTwenty) {
         txtTipOther.setEnabled(false);
         /*
          * Enable the Calculate button if Total Amount and No. of
          * People fields have valid values.
          */
         btnCalculate.setEnabled(txtAmount.getText().length() > 0
                   && txtPeople.getText().length() > 0);
     }
     if (checkedId == R.id.radioOther) {
        // Enable the Other Tip Percentage field
        txtTipOther.setEnabled(true);
        // Set the focus to this field
        txtTipOther.requestFocus();
        /*
         * Enable the Calculate button if Total Amount and No. of
         * People fields have valid values. Also ensure that user
         * has entered an Other Tip Percentage value before enabling
         * the Calculate button.
         */
        btnCalculate.setEnabled(txtAmount.getText().length() > 0
                && txtPeople.getText().length() > 0
                && txtTipOther.getText().length() > 0);
     }
     // To determine the tip percentage choice made by user
     radioCheckedId = checkedId;
    }
});

Monitoring key activity in text fields

As I mentioned earlier, the Calculate button must not be enabled unless the text fields have valid values. So we have to ensure that the Calculate button will be enabled only if the Total Amount, No. of People, and Other Tip Percentage text fields have valid values. The Other Tip Percentage text field is enabled only if the Other radio button is selected.

We do not have to worry about the type of values—that is, whether the user entered negative values or letters—because the android:numeric attribute has been defined for the text fields, thus limiting the types of values that the user can enter. We just have to ensure that the values are present.

So, we use the static interface OnKeyListener(). This will notify us when a key is pressed. The notification reaches us before the actual key pressed is sent to the EditText widget.

Look at the code in Examples 1-12 and 1-13, which deal with key events in the text fields. As in Example 1-11, the source code comments are quite self-explanatory.

Example 1-12. Code snippet 4 of TipsterActivity.java
/*
 * Attach a KeyListener to the Tip Amount, No. of People, and Other Tip
 * Percentage text fields
 */
txtAmount.setOnKeyListener(mKeyListener);
txtPeople.setOnKeyListener(mKeyListener);
txtTipOther.setOnKeyListener(mKeyListener);

Notice that we create just one listener instead of creating anonymous/inner listeners for each text field. I am not sure if my style is better or recommended, but I always write in this style if the listeners are going to perform some common actions. Here the common concern for all the text fields is that they should not be empty, and only when they have values should the Calculate button be enabled.

Example 1-13. Code snippet 5 from KeyListener of TipsterActivity.java
/*
 * KeyListener for the Total Amount, No. of People, and Other Tip Percentage
 * text fields. We need to apply this key listener to check for the following
 * conditions:
 *
 * 1) If the user selects Other Tip Percentage, then the Other Tip Percentage text
 * field should have a valid tip percentage entered by the user. Enable the
 * Calculate button only when the user enters a valid value.
 *
 * 2) If the user does not enter values in the Total Amount and No. of People fields,
 * we cannot perform the calculations. Hence we enable the Calculate button
 * only when the user enters valid values.
 */
private OnKeyListener mKeyListener = new OnKeyListener() {
    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {

        switch (v.getId()) { 1
        case R.id.txtAmount: 2
        case R.id.txtPeople: 3
            btnCalculate.setEnabled(txtAmount.getText().length() > 0
                    && txtPeople.getText().length() > 0);
            break;
        case R.id.txtTipOther: 4
            btnCalculate.setEnabled(txtAmount.getText().length() > 0
                    && txtPeople.getText().length() > 0
                    && txtTipOther.getText().length() > 0);
            break;
        }
        return false;
    }
};

At 1 in Example 1-13, we examine the ID of the View. Remember that each widget has a unique ID, defined in the main.xml file. These values are then defined in the generated R.java class.

At 2 and 3, if the key event occurred in the Total Amount or No. of People field, we check for the value entered in the field. We are ensuring that the user has not left both fields blank.

At 4 we check if the user has selected the Other radio button, and then we ensure that the Other text field is not empty. We also check once again if the Total Amount and No. of People fields are empty.

So, the purpose of our KeyListener is now clear: ensure that all text fields are not empty and only then enable the Calculate button.

Listening to button clicks

Now we will look at the Calculate and Reset buttons. When the user clicks these buttons, we use the static interface OnClickListener(), which will let us know when a button is clicked.

As we did with the text fields, we create just one listener and within it we detect which button was clicked. Depending on the button that was clicked, the calculate() or reset() method is called. Example 1-14 shows how the click listener is added to the buttons.

Example 1-14. Code snippet 6 of TipsterActivity.java
/* Attach listener to the Calculate and Reset buttons */
btnCalculate.setOnClickListener(mClickListener);
btnReset.setOnClickListener(mClickListener);

Example 1-15 shows how to detect which button is clicked by checking for the ID of the View that receives the click event.

Example 1-15. Code snippet 7 of TipsterActivity.java
/**
 * ClickListener for the Calculate and Reset buttons.
 * Depending on the button clicked, the corresponding
 * method is called.
 */
private OnClickListener mClickListener = new OnClickListener() {

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btnCalculate) {
            calculate();
        } else {
            reset();
        }
    }
};

Resetting the application

When the user clicks the Reset button, the input text fields should be cleared, the default “15% tip” radio button should be selected, and any results calculated should be cleared. Example 1-16 shows the reset() method.

Example 1-16. Code snippet 8 of TipsterActivity.java
/**
 * Resets the results text views at the bottom of the screen and
 * resets the text fields and radio buttons.
 */
private void reset() {
    txtTipAmount.setText("");
    txtTotalToPay.setText("");
    txtTipPerPerson.setText("");
    txtAmount.setText("");
    txtPeople.setText("");
    txtTipOther.setText("");
    rdoGroupTips.clearCheck();
    rdoGroupTips.check(R.id.radioFifteen);
    // Set focus on the first field
    txtAmount.requestFocus();
}

Validating the input to calculate the tip

As I said before, we are limiting what types of values the user can enter in the text fields. However, the user could still enter a value of zero in the Total Amount, No. of People, or Other Tip Percentage text fields, thus causing error conditions like divide by zero in our tip calculations.

If the user enters zero, we must show an alert pop-up asking the user to enter a nonzero value. We handle this with a method called showErrorAlert(String errorMessage, final int fieldId), which we will discuss in more detail later.

First, look at Example 1-17, which shows the calculate() method. Notice how the values entered by the user are parsed as double values. Now notice 1 and 2 where we check for zero values. If the user enters zero, we show a pop-up requesting a valid value. Next, look at 3, where the Other Tip Percentage text field is enabled because the user selected the Other radio button. Here, too, we must check for the tip percentage being zero.

Example 1-17. Code snippet 9 of TipsterActivity.java
/**
 * Calculate the tip as per data entered by the user.
 */
private void calculate() {
    Double billAmount = Double.parseDouble(
        txtAmount.getText().toString());
    Double totalPeople = Double.parseDouble(
        txtPeople.getText().toString());
    Double percentage = null;
    boolean isError = false;
    if (billAmount < 1.0) { 1
        showErrorAlert("Enter a valid Total Amount.",
            txtAmount.getId());
        isError = true;
    }

    if (totalPeople < 1.0) { 2
        showErrorAlert("Enter a valid value for No. of People.",
            txtPeople.getId());
        isError = true;
    }

    /*
     * If the user never changes the radio selection, then it means
     * the default selection of 15% is in effect. But it's
     * safer to verify
     */
    if (radioCheckedId == -1) {
        radioCheckedId = rdoGroupTips.getCheckedRadioButtonId();
    }
    if (radioCheckedId == R.id.radioFifteen) {
        percentage = 15.00;
    } else if (radioCheckedId == R.id.radioTwenty) {
        percentage = 20.00;
    } else if (radioCheckedId == R.id.radioOther) {
        percentage = Double.parseDouble(
            txtTipOther.getText().toString());
        if (percentage < 1.0) { 3
            showErrorAlert("Enter a valid Tip percentage",
                txtTipOther.getId());
            isError = true;
        }
    }

    /*
     * If all fields are populated with valid values, then proceed to
     * calculate the tip
     */
    if (!isError) {
        Double tipAmount = ((billAmount * percentage) / 100); 4
        Double totalToPay = billAmount + tipAmount;
        Double perPersonPays = totalToPay / totalPeople; 5

        txtTipAmount.setText(tipAmount.toString()); 6
        txtTotalToPay.setText(totalToPay.toString());
        txtTipPerPerson.setText(perPersonPays.toString()); 7
    }
}

When the application loads, the “15% tip” radio button is selected by default. If the user changes the selection, we assign the ID of the selected radio button to the member variable radioCheckedId, as we saw in Example 1-11, in OnCheckedChangeListener.

But if the user accepts the default selection, radioCheckedId will have the default value of –1. In short, we will never know which radio button was selected. Of course, we know which one is selected by default and could have coded the logic slightly differently, to assume 15% if radioCheckedId has the value –1. But if you refer to the API, you will see that we can call the method getCheckedRadioButtonId() only on the RadioGroup and not on individual radio buttons. This is because OnCheckedChangeListener readily provides us with the ID of the radio button selected.

Showing the results

Calculating the tip is simple. If there are no validation errors, the Boolean flag isError will be false. Look at 4 through 5 in Example 1-17 for the simple tip calculations. Next, the calculated values are set to the TextView widgets from 6 to 7.

Showing the alerts

Android provides the AlertDialog class to show alert pop-ups. This lets us show a dialog with up to three buttons and a message.

Example 1-18 shows the showErrorAlert() method, which uses this AlertDialog to show the error messages. Notice that we pass two arguments to this method: String errorMessage and int fieldId. The first argument is the error message we want to show to the user. The fieldId is the ID of the field that caused the error condition. After the user dismisses the alert dialog, this fieldId will allow us to request the focus on that field, so the user knows which field has the error.

Example 1-18. Code snippet 10 of TipsterActivity.java
/**
 * Shows the error message in an alert dialog
 *
 * @param errorMessage
 *            String for the error message to show
 * @param fieldId
 *            ID of the field which caused the error.
 *            This is required so that the focus can be
 *            set on that field once the dialog is
 *            dismissed.
 */
private void showErrorAlert(String errorMessage,
    final int fieldId) {
    new AlertDialog.Builder(this).setTitle("Error")
    .setMessage(errorMessage).setNeutralButton("Close",
            new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog,
                        int which) {
                    findViewById(fieldId).requestFocus();
                }
            }).show();
}

When all this is put together, it should look like Figure 1-59.

Conclusion

Developing for the Android OS is not too different from developing for any other UI toolkit, including Microsoft Windows, X Windows, or Java Swing. Of course Android has its difference, and, overall, a very good design. The XML layout paradigm is quite cool and useful for building complex UIs using simple XML. In addition, the event handling model is simple, feature-rich, and intuitive to use in code.

ack2 0159
Figure 1-59. Tipster in action

Source Download URL

The source code for this example is in the Android Cookbook repository, in the subdirectory Tipster (see “Getting and Using the Code Examples”).

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required