Android Dev Book
Android Dev Book
Joel Ross
2017-05-24
2
Contents
I Lectures 11
1 Introduction 13
1.1 Android History . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.1.1 Android Versions . . . . . . . . . . . . . . . . . . . . . . . 14
1.1.2 Legal Battles . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.2 Android Architecture and Code . . . . . . . . . . . . . . . . . . . 16
1.2.1 Programming Languages . . . . . . . . . . . . . . . . . . . 18
1.2.2 Building Apps . . . . . . . . . . . . . . . . . . . . . . . . 18
1.3 Development Tools . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.3.1 Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.3.2 Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.4 Hello World . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.4.1 The Emulator . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.4.2 Project Contents . . . . . . . . . . . . . . . . . . . . . . . 22
3
4 CONTENTS
4 Interactive Views 51
4.1 Inputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.2 ListViews and Adapters . . . . . . . . . . . . . . . . . . . . . . . 53
4.3 Network Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
5 Fragments 61
5.1 Creating a Fragment . . . . . . . . . . . . . . . . . . . . . . . . . 64
5.1.1 Activity-to-Fragment Communication . . . . . . . . . . . 66
5.2 Dynamic Fragments . . . . . . . . . . . . . . . . . . . . . . . . . 67
5.2.1 Instantiating Fragments . . . . . . . . . . . . . . . . . . . 67
5.2.2 Transactions . . . . . . . . . . . . . . . . . . . . . . . . . 69
5.2.3 Inter-Fragment Communication . . . . . . . . . . . . . . . 70
5.2.4 The Back Stack . . . . . . . . . . . . . . . . . . . . . . . . 71
6 UI Components 73
6.1 The Action Bar . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
6.2 Menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
6.2.1 Context Menus . . . . . . . . . . . . . . . . . . . . . . . . 76
6.3 Dialogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
6.3.1 DialogFragments . . . . . . . . . . . . . . . . . . . . . . . 78
6.4 Toasts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
8 Intents 87
8.1 Intents for Another Activity (Explicit) . . . . . . . . . . . . . . . 88
8.1.1 Extras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
8.2 Intents for Another App (Implicit) . . . . . . . . . . . . . . . . . 89
8.3 Intents for a Response . . . . . . . . . . . . . . . . . . . . . . . . 90
8.4 Listening for Intents . . . . . . . . . . . . . . . . . . . . . . . . . 91
8.5 Broadcasts and Receivers . . . . . . . . . . . . . . . . . . . . . . 92
8.6 An Example: SMS . . . . . . . . . . . . . . . . . . . . . . . . . . 94
CONTENTS 5
8.7 ShareActionProvider . . . . . . . . . . . . . . . . . . . . . . . . . 95
9 UI Components II 97
9.1 Notifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
9.2 Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
9.2.1 SharedPreferences . . . . . . . . . . . . . . . . . . . . . . 100
9.2.2 Preference Settings . . . . . . . . . . . . . . . . . . . . . . 101
11 Location 117
11.1 Localization Techniques . . . . . . . . . . . . . . . . . . . . . . . 118
11.1.1 GPS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
11.1.2 Cell Tower Localization . . . . . . . . . . . . . . . . . . . 119
11.1.3 WiFi Localization . . . . . . . . . . . . . . . . . . . . . . 119
11.1.4 Representing Location . . . . . . . . . . . . . . . . . . . . 120
11.2 Android Location . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
11.2.1 Google Play Services . . . . . . . . . . . . . . . . . . . . . 121
11.2.2 Accessing Location . . . . . . . . . . . . . . . . . . . . . . 123
12 Sensors 127
12.1 Motion Sensors . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
12.1.1 Accessing Sensors . . . . . . . . . . . . . . . . . . . . . . . 128
12.1.2 Composite Sensors . . . . . . . . . . . . . . . . . . . . . . 130
12.2 Rotation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
12.2.1 Coordinates . . . . . . . . . . . . . . . . . . . . . . . . . . 133
13 Services 135
13.1 IntentServices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
13.1.1 The Service Lifecycle . . . . . . . . . . . . . . . . . . . . . 137
13.2 Example: A Music Service . . . . . . . . . . . . . . . . . . . . . . 140
13.2.1 MediaPlayer . . . . . . . . . . . . . . . . . . . . . . . . . 140
13.2.2 Creating a Service . . . . . . . . . . . . . . . . . . . . . . 141
13.3 Foreground Services . . . . . . . . . . . . . . . . . . . . . . . . . 142
13.4 Bound Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
17 Publishing 173
17.1 Signing an App . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
17.1.1 A Release .apk . . . . . . . . . . . . . . . . . . . . . . . . 175
21 Bluetooth 199
22 Maps 203
22.1 Create a Map Activity . . . . . . . . . . . . . . . . . . . . . . . . 203
CONTENTS 7
24 Multi-Touch 213
24.1 Identifying Fingers . . . . . . . . . . . . . . . . . . . . . . . . . . 213
24.2 Drawing Touches . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
24.3 Moving Fingers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
24.4 Other Multi-Touch Gestures . . . . . . . . . . . . . . . . . . . . . 216
Appendix 222
This book compiles lecture notes and tutorials for the INFO 448 Mobile
Development: Android course taught at the University of Washington In-
formation School (most recently in Spring 2017). The goal of these notes is to
provide learning materials for students in the course or anyone else who wishes
to learn the basics of developing Android applications.
These notes are primarily adapted from the official Android developer documen-
tation, compiling and synthesizing those guidelines for pedagogical purposes
(and the author’s own interpretation/biases). Please refer to that documenta-
tion for the latest information and official guidance.
This book is currently in alpha status, as pure lecture notes are converted into
more generic formats.
9
10 CONTENTS
Part I
Lectures
11
Chapter 1
Introduction
13
14 CHAPTER 1. INTRODUCTION
Sony; mobile carriers like T-Mobile, Sprint, and NTT DoCoMo; hardware
manufacturers like Broadcom and Nvidia; and others. The Open Handset
Alliance now (2017) includes 86 companies.
– Note this is the same year the first iPhone came out!
• 2008: First Android device is released: the HTC Dream (a.k.a. T-Mobile
G1)
Specs: 528Mhz ARM chip; 256MB memory; 320x480 resolution capacitive
touch; slide-out keyboard! Author’s opinion: a fun little device.
• 2010: First Nexus device is released: the Nexus One. These are Google-
developed “flagship” devices, intended to show off the capabilities of the
platform.
Specs: 1Ghz Scorpion; 512MB memory; .37” at 480x800 AMOLED capac-
itive touch.
– For comparison, the iPhone 7 Plus (2016) has: 2.34Ghz dual core
A10 64bit Fusion; 3GB RAM; 5.5” at 1920x1080 display.
As of 2016, this program has been superceded by the Pixel range of devices.
• 2014: Android Wear, a version of Android for wearable devices (watches)
is announced.
• 2016: Daydream, a virtual reality (VR) platform for Android is an-
nounced
In short, Google keeps pushing the platform wider so it includes more and more
capabilities.
Today, Android is incredibly popular (to put it mildly). Android is incredibly
popular! (see e.g., here, here, and here)
• In any of these analyses there are some questions about what exactly is
counted… but what we care about is that there are a lot of Android devices
out there! And more than that: there are a lot of different devices!
a California federal judge decided in Google favor (that one can’t copyright an
API). This was then reversed by the Federal Circuit court in 2014. The verdict
was appealed to the Supreme courset in 2015, who refused to hear the case. It
then went back to the the district court, which ruled that Google’s use of the
API was fair use. See https://www.eff.org/cases/oracle-v-google for a summary,
as well as https://arstechnica.com/series/series-oracle-v-google/
• One interesting side effect of this battle: the latest version of Android
(Nougat) uses the OpenJDK implementation of Java, instead of Google’s
own in-violation-but-fair-use implementation see here. This change
shouldn’t have any impact on us as developers, but it’s worth keeping an
eye out for potentially differences between Android and Java SE.
There have been other legal challenges as well. While not directly about An-
droid, the other major relevant court battle is Apple v Samsung. In this
case, Apple claims that Samsung infringed on their intellectual property (their
design patents). This has gone back and forth in terms of damages and what is
considered infringing; the latest development is that the Supreme Court heard
the case and sided with Samsung that infringing design patents shouldn’t lead
to damages in terms of the entire device… it’s complicated (the author is not a
lawyer).
So overall: Android is a growing, evolving platform that is embedded in and
affecting the social infrastructures around information technology in numerous
ways.
For this course, Android Development will involve writing Java applica-
tions that interact with the Android Framework layer, which handles the
task of interacting with the device hardware for us.
There are two programming languages we will be working with in this course:
1. Java: Android code (program control and logic, as well as data storage
and manipulation) is written in Java.
Writing Android code will feel a lot writing any other Java program: you
create classes, define methods, instantiate objects, and call methods on
those objects. But because you’re working within a framework, there is
a set of code that already exists to call specific methods. As a developer,
your task will be to fill in what these methods do in order to run your
specific application.
• In web terms, this is closer to working with Angular (a framework)
than jQuery (a library).
• Importantly: this course expects you to have “journeyman”-level
skills in Java (apprenticeship done, not yet master). We’ll be us-
ing a number of intermediate concepts (like generics and inheritance)
without much fanfare or explanation (though see the appendix).
2. XML: Android user interfaces and resources are specified in XML
(EXtensible Markup Language). To compare to web programming: the
XML contains what would normally go in the HTML/CSS, while the
Java code will contain what would normally go in the JavaScript.
XML is just like HTML, but you get to make up your own tags. Except
we’ll be using the ones that Android made up; so it’s like defining web
pages, except with a new set of elements. This course expects you to have
some familiarity with HTML or XML, but if not you should be able to
infer what you need from the examples.
As stated above, we will write code in Java and XML. But how does that code
get run on the phone’s hardware?
Pre-Lollipop (5.0), Android code ran on Dalvik: a virtual machine similar to
the JVM used by Java SE.
• Fun fact for people with a Computer Science background: Dalvik uses a
register-based architecture rather than a stack-based one!
1.2. ANDROID ARCHITECTURE AND CODE 19
A developer would write Java code, which would then be compiled into JVM
bytecode, which would then be translated into DVM (Dalvik virtual machine)
bytecode, that could be run on Android devices. This DVM bytecode was stored
in .dex or .odex (“[Optimized] Dalvik Executable”) files, which is what was
loaded onto the device. The process of converting from Jave code to dex files is
called “dexing” (so code that has been built is “dexed”).
Dalvik does include JIT (“Just In Time”) compilation to native code that runs
much faster than the code interpreted by the virtual machine, similar to the
Java HotSpot. This navite code is faster because no translation step is needed
to talk to the actual hardware (the OS).
From Lollipop (5.0) on, Android instead uses Android Runtime (ART) to
run code. ART’s biggest benefit is that it compiles the .dex bytecode into
native code on installation using AOT (“Ahead of Time”) compilation. ART
continues to accept .dex bytecode for backwards compatibility (so the same
dexing process occurs), but the code that is actually installed and run on a
device is native. This allows for applications to have faster execution, but at
the cost of longer install times—but since you only install an application once,
this is a pretty good trade.
After being built, Android applications (the source, dexed bytecode, and any
resources) are packaged into .apk files. These are basically zip files (they use
the same gzip compression); if you rename the file to be .zip and you can
unpackage them! The .apk files are then cryptographically signed to specify
their authenticity, and either “side-loaded” onto the device or uploaded to an
App Store for deployment.
• The signed .apk files are basically the “executable” versions of your pro-
gram!
• Note that the Android application framework code is actually “pre-
DEXed” (pre-compiled) on the device; when you write code, you’re
actually compiling against empty code stubs (rather than needing to
include those classes in your .apk)! That said, any other 3rd-party
libraries you include will be copied into your built App, which can
increase its file size both for installation and on the device.
To summarize, in addition to writing Java and XML code, when building an
App you need to:
1. Generate Java source files (e.g., from resource files, which are written XML
used to generate Java code)
2. Compile Java code into JVM bytecode
3. “dex” the JVM bytecode into Dalvik bytecode
4. Pack in assets and graphics into an APK
5. Cryptographically sign the APK file to verify it
6. Load it onto the device
There are a lot of steps here, but there are tools that take care of it for us. We’ll
20 CHAPTER 1. INTRODUCTION
just write Java and XML code and run a “build” script to do all of the steps!
1.3.1 Hardware
Since Android code is written for a virtual machine anyway, Android apps can
be developed and built on any computer’s operating system (unlike some other
mobile OS…).
But obviously Android apps will need to be run on Android devices. Physical
devices are the best for development (they are the fastest, easiest way to test),
though you’ll need USB cable to be able to wire your device into your computer.
Any device will work for this course; you don’t even need cellular service (just
WiFi should work). Note that if you are unfamiliar with Android devices, you
should be sure to play around with the interface to get used to the interaction
language, e.g., how to click/swipe/drag/long-click elements to use an app.
• You will need to turn on developer options in order to install development
apps on your device!
If you don’t have a physical device, it is also possible to use the Android Em-
ulator, which is a “virtual” Android device. The emulator represents a generic
device with hardware you can specify… but it does have some limitations (e.g.,
no cellular service, no bluetooth, etc).
• While it has improved recently, the Emulator historically does not work
very well on Windows; I recommend you develop on either a Mac or a
physical device. In either case, make sure you have enabled HAXM (Intel’s
Acceleration Manager, which allows the emulator to utilize your GPU for
rendering): this speeds things up considerably.
1.3.2 Software
– ANT is the “old” build system, Gradle is the “modern” build system
(and so what we will be focusing on).
– Note that you do not need to install Gradle separately for this course.
• Android Studio & Android SDK is the official IDE for developing Android
applications. Note that the IDE comes bundled with the SDK. Android
Studio provides the main build system: all of the other software (Java,
Gradle) goes to support this.
The SDK comes with a number of useful command-line tools. These in-
clude:
– adb, the “Android Device Bridge”, which is a connection between
your computer and the device (physical or virtual). This tool is used
for console output!
– emulator, which is a tool used to run the Android emulator
– deprecated/removed android: a tool that does SDK/AVD (Android
Virtual Device) management. Basically, this command-line utility
did everything that the IDE did, but from the command-line! It has
recently been removed from the IDE.
I recommend making sure that the SDK command-line tools are installed.
Put the tools and platform-tools folders on your computer’s PATH; you
can run adb to check that everything works. All of these tools are built
into the IDE, but they can be useful fallbacks for debugging.
This lecture introduces Activities, which are the basic component used in An-
droid applications. It aims to demonstrate how the interactive patterns used in
other graphical applications are utilized in Android.
This lecture references code found at https://github.com/info448-s17/lecture02-
activities, in the android/ folder. As a first step, you’ll need to create a new
Android application with a single Empty Activity (e.g., MainActivity). Fu-
ture chapters will have starter code to work from, but it is good practice to
make a new application from scratch!
According to Google:
An Activity is an application component that provides a screen with
which users can interact in order to do something.
You can think of an Activity as a single screen in your app, the equivalent
of a “window” in a GUI system (or a JFrame in a Swing app). Note that
Activities don’t need to be full screens: they can also be floating modal windows,
embedded inside other Activities (like half a screen), etc. But we’ll begin by
thinking of them as full screens. We can have lots of Activities (screens) in
an application, and they are loosely connected so we can easily move between
them.
In many ways, an Activity is a “bookkeeping mechanism”: a place to hold state
and data, and tell to Android what to show on the display. It functions much
like a Controller (in Model-View-Controller sense) in that regard!
Also to note from the documentation1 :
An activity is a single, focused thing that the user can do.
which implies a design suggestion: Activities (screens) break up your App into
“tasks”. Each Activity can represent what a user is doing at one time. If the
1 https://developer.android.com/reference/android/app/Activity.html
25
26 CHAPTER 2. ACTIVITIES AND LOGGING
user does something else, that should be a different Activity (and so probably
a different screen).
Figure 2.1: Lifecycle state diagram, from Google3 . See also an alternative,
simplified diagram here.
28 CHAPTER 2. ACTIVITIES AND LOGGING
There are 7 “events” that occur in the Activity Lifecycle, which are designated
by the callback function that they execute:
• onCreate(): called when the Activity is first created/instantiated. This
is where you initialize the UI (e.g., specify the layout to use), similar to
what might go in a constructor.
• onStart(): called just before the Activity becomes visible to the user.
The difference between onStart() and onCreate() is that onStart()
can be called more than once (e.g., if you leave the Activity, thereby
hiding it, and come back later to make it visible again).
• onResume(): called just before user interaction starts, indicating that
the Activity is ready to be used! This is a little bit like when that Activity
“has focus”.
While onStart() is called when the Activity becomes visible, onResume()
is called when then it is ready for interaction. It is possible for an Activity
to be visible but not interactive, such as if there is a modal pop-up in front
of it (partially hiding it).
• onPause(): called when the system is about to start another Activity (so
about to lose focus). This is the “mirror” of onResume(). When paused,
the activity stays visible!
This callback is usually used to quickly and temporarily store unsaved
changes (like saving an email draft in memory) or stop animations or
video playback. The Activity may be being left (on its way out), but
could just be losing focus.
• onStop(): called when the activity is no longer visible. (e.g., another Ac-
tivity took over, but this also be because the Activity has been destroyed.
This callback is a mirror of onStart().
This callback is where you should persist any state information (e.g., sav-
ing the user’s document or game state). It is intended to do more complex
“saving” work than onPause().
• onRestart(): called when the Activity is coming back from a “stopped”
state. This event allows you to run distinct code when the App is being
“restarted”, rather than created for the first time. It is the least commonly
used callback.
• onDestroy(): called when the Activity is about to be closed. This can
happen because the user ended the application, or (and this is important!)
because the OS is trying to save memory and so kills the App.
Android apps run on devices with significant hardware constraints in terms
of both memory and battery life. Thus the Android OS is very aggressive
about not leaving Apps running “in the background”. If it determines
that an App is no longer necessary (such as because it has been hidden
for a while), that App will be destroyed. Note that this destruction is
2.3. LOGGING & ADB 29
In the default MainActivity the onCreate() callback has already been over-
ridden for us, since that’s where the layout is specified.
Notice that this callback takes a Bundle as a parameter. A Bundle is an ob-
ject that stores key-value pairs, like a super-simple HashMap (or an Object
in JavaScript, or dictionary in Python). Bundles can only hold basic types
(numbers, Strings) and so are used for temporarily “bunding” small amounts of
information.
This Bundle parameter in particular stores information about the Activity’s
current state (e.g., what text they may have typed into a search box), so that if
the App gets killed it can be restarted in the same state and the user won’t notice
that it was ever lost! The Bundle stores current layout information in it by
default (if the Views have ids)—technically, it calls a onSaveInstanceState()
callback for each View in the layout, and the provided Views that we utilize
tend to save important state information (like entered text) already. See Saving
and restoring activity state for details.
Also note that we call super.onCreate(). Always call up the inheritance
chain!. This allows the system-level behavior to continue without any problem.
We can also add other callbacks: for example, onStart() (see the documenta-
tion for examples).
But how can we know if the lifecycle events are getting called?
• It is possible to get access to stdout with adb using adb shell stop;
adb shell setprop log.redirect-stdio true; adb shell start,
but this is definitely not ideal.
Instead, Android provides a Logging system that we can use to write out de-
bugging information, and which is automatically accessible over the adb (An-
droid Debugging Bridge). Logged messages can be filtered, categorized, sorted,
etc. Logging can also be disabled in production builds for performance reasons
(though it often isn’t).
To perform this logging, we’ll use the android.util.Log4 class. This class
includes a number of static methods, which all basically wrap around println
to print to the device’s log file, which is then accessible through the adb.
• Remember to import the Log class!
The device’s log file is stored persistantly… sort of. It’s a 16k file, but it is shared
across the entire system. Since every single app and piece of the system writes
to it, it fills up fast. Hence filtering/searching becomes important, and you tend
to watch the log (and debug your app) in real time!
2.3.2 Logcat
You can view the logs via adb (the debugging bridge) and a service called Logcat
(from “log” and “conCATenation”, since it concats the logs). The easiest way
to check Logcat is to use Android Studio. The Logcat browser panel is usually
found at the bottom of the screen after you launch an application. It “tails” the
log, showing the latest output as it appears.
You can use the dropdown box to filter by priority, and the search box to search
(e.g., by tag if you want). Android Studio also lets you filter to only show the
current application, which is hugely awesome. Note that you may see a lot of
Logs that you didn’t produce, including possibly Warnings (e.g., I see a lot of
stuff about how OpenGL connects to the graphics card). This is normal!
It is also possible to view Logcat through the command-line using adb, and
includes complex filtering arguments. See Logcat Command-line Tool for more
details.
Demo: And now we can finally log out some of the Lifecycle callbacks to see
them being executed!
• Start by implementing onResume(). Note the wonders of tab completion!
Have it log out at INFO level. On the device, hit the main menu (circle)
button to send the Activity to the background, and watch the callback be
executed.
• Implement onStop() and switch out of the app to watch it be stopped.
• onDestroy() can easily be called if you set the phone to “Don’t Keep
Activities” (at bottom of developer settings). Or you can simply rotate
the phone (which causes the Activity to be destroyed and then recreated
in the new orientation).
• Something else to test: Cause the app to throw a runtime Exception in
one of the handlers. For example, you could make a new local array and
try to access an item out of bounds. Or just throw new RuntimeExcep-
tion() (which is slightly less interesting). Can you see the Stack Trace
in the logs?
Logging is fantastic and one of the the best techniques we have for debugging,
both in how Activities are being used or for any kind of bug (also RuntimeEx-
32 CHAPTER 2. ACTIVITIES AND LOGGING
This XML defines a Button. The android:text attribute specifies the text that
is on the button. The next lecture will describe in more detail how exactly this
XML works (and what’s is meant by the id, and layout_width/height), but
you should be able to make a pretty good educated guess based on the names.
• Defining this in XML is basically the same process as creating the JButton
and adding it to the JFrame in Java!
Now we have a button, but we want to be able to click on it. So we need to
register a “listener” for it (in Java), just like with Swing apps:
Button button = (Button)findViewById(R.id.my_button);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
}
});
First we need to get access to a variable that represents that Button we defined in
the XML. The findViewById() method “finds” the appropriate XML element
with the given id. We discuss why we wrote the parameter as R.id.my_button
in the next lecture tomorrow. Note that this method returns a View, so we want
to cast the value into the more specific Button (which has methods we want to
use).
We can register a listener with that button through the .setOnClickLis-
tener() method, passing in an anonymous class to act as the listener. (Again,
2.5. MULTIPLE ACTIVITIES 33
tab-completion is our friend!). This is just like what you would do with a Swing
app.
Finally, we can fill in the method to have it log out something when clicked.
Overall, this button is an example of an Input Control. These will be discussed
in more detail in Lecture 4.
• I don’t have a good justification for the name, other than Intents announce
an “intention” for the OS to do something (like start an Activity)
• You can think of Intents as like envelopes: they are addressed to a par-
ticular target (e.g., another Activity—or more properly a Context), and
contain a brief message about what to do.
An Intent is an object we can instantiate: for example, we can create a new
Intent in the event handler for when we click the button on MainActivity.
The Intent class has a number of different cnstructors, but the one we’ll start
with looks like:
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
The second parameter to this constructor is the class we want to send the Intent
to (the .class property fetches a reference to the class type; this is metapro-
gramming!). Effectively, it is the “address” on the envelop for the message we’re
sending.
The first parameter refers to the current Context5 in which the message should
be delivered. Context is an abstract class (and a superclass of Activity) that
acts as a reference for information about the current running environment: it
represents environmental data (information like “What OS is running? Is there
a keyboard plugged in?”). You can almost think of the Context as representing
the “Application”, though it’s broader than that (Application is actually a
subclass of Context!)
The Context is used to do “application-level” actions: mostly working with
resources (accessing and loading them), but also communicating between Activ-
ities like we’re doing now. Effectively, it lets us refer to the state in which we
are running: the “context” for our code (e.g., “where is this occurring?”). It’s
a kind of reflection or meta-programming, in a way.
There are a couple of different kinds of Contexts we might wish to refer to:
• The Application context (e.g., an Application object) references the
state of the entire application. It’s basically the Java object that is built
out of the <application> element in the Manifest (and so contains that
level of information).
• The Activity context (e.g., an Activity object) that references the state of
that Activity. Again, this roughly corresponds to the Java objects created
out of the <activity> tags from the Manifest.
Each of these Context objects exist for the life of its respective component: that
is, an Activity Context is available as long as the Activity exists (disappearing
after onDestroy()), whereas Application Contexts survive as long as the
application does. Note htat we’ll almost always use the Activity context, as
it’s safer and less likely to cause memory leaks.
5 https://developer.android.com/reference/android/content/Context.html
2.6. BACK & TASKS 35
This method will “send” the message to the operating system, which will deliver
the Intent to the appropriate Activity, telling that Activity to start as soon as
it receives the message.
With this interaction in place, we can now click a button to start a second
activity, and see how that impacts our Lifecycle callbacks.
• And we can use the back button to go backwards!
There are actually a couple of different kinds of Intents (this is an Explicit
Intent, because it is explicit about what Activity it’s sent to), and a lot more
we can do with them. We’ll dive into Intents in more detail later; for now we’re
going to focus on mostly Single Activities.
• For example, if you look back at the Manifest, you can see that the Main-
Activity has an <intent-filter> child element that allows it to receive
particular kinds of Intents—including ones for when an App is launched
for the first time!
2.6.1 Up Navigation
We can make this “back” navigation a little more intuitive for users by provid-
ing explicit up navigation, rather than just forcing users to go back through
Activities in the order they viewed them (e.g., if you’re swiping through emails
and want to go back to the home list). To do this, we just need to add a little
bit of configuration to our Activities:
• In the Java code, we want to add more functionality to the ActionBar.
Think: which lifecycle callback should this specification be put in?
//specify that the ActionBar should have an "home" button
getSupportActionBar().setHomeButtonEnabled(true);
2.6. BACK & TASKS 37
This lecture discusses Resources, which are used to represent elements or data
that are separate from the behavior (functional logic) of the app. In particular,
this lecture focuses on how resources are used to define Layouts for user inter-
faces. While the Activities lecture focused on the Java portion of Android apps;
this lecture focuses on the XML.
This lecture references code found at https://github.com/info448-s17/lecture03-
layouts.
3.1 Resources
Resources can be found in the res/ folder, and represent elements or data that
are “external” to the code. You can think of them as “media content”: often
images, but also things like text clippings (or short String constants). Textual
resources are usually defined in XML files. This is because resources represent
elements (e.g., content) that is separate from the code (the behavior of the app),
so is kept separate from the Java code to support the Principle of Separation
of Concerns
• By defining resources in XML, they can be developed (worked on) without
coding tools (e.g., with systems like the graphical “layout design” tab).
Theoretically you could have a Graphic Designer create these resources,
which can then be integrated into the code without the designer needing
to do a lick of Java.
• Similarly, keeping resources separate allows you to choose what resources
to include dynamically. You can choose to show different images based on
device screen resolution, or pick different Strings based on the language
of the device (internationalization!)—the behavior of the app is the same,
but the “content” is different!
39
40 CHAPTER 3. RESOURCES AND LAYOUTS
• Specifc screen pixel density (dpi) (ldpi, mdpi, hdpi, xhdpi, xxhdpi, etc.).
xxhdpi is pretty common for high-end devices. Note that dpi is “dots per
inch”, so these values represent the number of pixels across relative to the
device size!
• Platform version (v1, v4, v7… for each API number)
Configurations are indicated using the directory name, giving them the form
<resource_name>(-<config_qualifier>)+
• You can see this in action by using the New Resource wizard (File >
New > Android resource file) to create a welcome message (a string
resource, such as for the app_name) in another language3 , and then chang-
ing the device’s language settings to see the content automatically adjust!
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Mon Application</string>
</resources>
• Switch to the Package view in Android Studio to see how the folder struc-
ture for this works.
3.1.3 R
Although XML resources are defined separately from the Java code, resources
can be accessed from within Java. When an application is compiled, the build
tools (e.g., gradle) generate an additional Java class called R (for “resource”).
This class contains what is basically a giant list of static “constants”—one for
3 https://www.webucator.com/blog/2010/03/saying-hello-world-in-your-language-using-
javascript/
42 CHAPTER 3. RESOURCES AND LAYOUTS
each resource! These constants are organized into subclasses, one for each re-
source type. This allows you to refer to a specific resource in the Java code
as [(package_name).]R.resource_type.identifier similar to the kind of
syntax used to refer to a nested JSON object! For example: R.string.hello
(the hello string resource), R.drawable.icon or R.layout.activity_main
The static constants inside the R.java file are often just ints that are pointers
to element references (similar to passing a pointer* around in the C language).
So in the Java, we usually work with int as the data type for XML resources,
because we’re actually working with pointers to those resources.
• You can think of each int constant as a “key” or “index” for that resource
(in the list of all resources). Android does the hard work of taking that
int, looking it up in an internal resource table, finding the associated
XML file, and then getting the right element out of that XML. (By hard
work, I mean in terms of implementation. Android is looking up these
references directly in memory, so the look-up is a fast O(1)).
Because the R class is included in the Java, we can access these constants di-
rectly in our code (as R.resource_type.identifier). For example, the set-
ContentView() call in an Activity’s onCreate() takes in a resource int.
The R class is regenerated all time (any time you change a resource, which is
often); when Eclipse was the recommend Android IDE, you often needed to
manually regenerate the class so that the IDE’s index would stay up to date!
You can perform a similar task in Android Studio by using Build > Clean
Project and Build > Rebuild Project.
3.2. VIEWS 43
3.2 Views
The most common type of element we’ll define in resources are Views4 . View is
the superclass for visual interface elements—a visual component on the screen
is a View. Specific types of Views include: TextViews, ImageViews, Buttons,
etc.
• View is a superclass for these components because it allows us to use poly-
morphism to treat all these visual elements the same way as instances
of the same type. We can lay them out, draw them, click on them, move
them, etc. And all the behavior will be the same—though subclasses can
also have “extra” features
Here’s the big trick: one subclass of View is ViewGroup5 . A ViewGroup can
contain other “child” Views. But since ViewGroup is a View… it can contain
more ViewGroups inside it! Thus we can nest Views within Views, following
the Composite Pattern. This ends up working a lot like HTML (which can have
DOM elements like <div> inside other DOM elements), allowing for complex
user interfaces.
• Thus Views are structured into a tree, what is known as the View hier-
archy.
Views are defined inside of Layouts—that is, inside a layout resource, which is
an XML file describing Views. These resources are “inflated” (rendered) into
UI objects that are part of the application.
Technically, Layouts are simply ViewGroups that provide “ordering” and “posi-
tioning” information for the Views inside of them. they let the system “lay out”
the Views intelligently and effectively. Individual views shouldn’t know their
own position; this follows from good good object-oriented design and keeps the
Views encapsulated.
Android studio does come with a graphical Layout Editor (the “Design” tab)
that can be used to create layouts. However, most developers stick with writing
layouts in XML. This is mostly because early design tools were pathetic and
unusable, so XML was all we had. Although Android Studio’s graphical editor
can be effective, for this course you should create layouts “by hand” in XML.
This is helpful for making sure you understand the pieces underlying develop-
ment, and is a skill you should be comfortable with anyway (similar to how we
encourage people to use git from the command-line).
code. This layout contains two individual View elements (inside a Layout): a
TextView and a Button.
All View have properties which define the state of the View. Properties are
usually defined within the resource XML as element attributes. Some examples
of these property attributes are described below.
• android:id specifies a unique identifier for the View. This identifier needs
to be unique within the layout, though ideally is unique within the entire
app (for clarity).
Identifiers must be legal Java variable names (because they are turned
into a variable name in the R class), and by convention are named in
lower_case format.
– Style tip: it is useful to prefix each View’s id with its type (e.g., btn,
txt, edt). This helps with making the code self-documenting.
You should give each interactive View a unique id, which will allow its state
to automatically be saved as a Bundle when the Activity is destroyed. See
here for details.
• android:layout_width and android:layout_height are used to spec-
ify the View’s size on the screen (see ViewGroup.LayoutParams for doc-
umentation). These values can be a specific value (e.g., 12dp), but more
commonly is one of two special values:
– wrap_content, meaning the dimension should be as large as the
content requires, plus padding.
– match_parent, meaning the dimension should be as large as the
parent (container) element, minus padding. This value was renamed
from fill_parent (which has now been deprecated).
Android utilizes the following dimensions or units:
– dp is a “density-independent pixel”. On a 160-dpi (dots-per-inch)
screen, 1dp equals 1px (pixel). But as dpi increases, the number of
pixels per dp increases. These values should be used instead of px,
as it allows dimensions to work independent of the hardware’s dpi
(which is highly variable).
– px is an actual screen pixel. DO NOT USE THIS (use dp instead!)
– sp is a “scale-independent pixel”. This value is like dp, but is scale
by the system’s font preference (e.g., if the user has selected that the
device should display in a larger font, 1sp will cover more dp). You
should always use sp for text dimensions, in order to support user
preferences and accessibility.
– pt is 1/72 of an inch of the physical screen. Similar units mm and in
are available. Not recommended for use.
• android:padding, android:paddingLeft, android:margin, an-
droid:marginLeft, etc. are used to specify the margin and padding for
3.2. VIEWS 45
Views. These work basically the same way they do in CSS: padding is the
space between the content and the “edge” of the View, and margin is the
space between Views. Note that unlike CSS, margins between elements
do not collapse.
• android:textSize specifies the “font size” of textual Views (use sp
units!), android:textColor specifies the color of text (reference a color
resource!), etc.
• There are lots of other properties as well! You can see a listing of generic
properties in the View6 documentation, look at the options in the “Design”
tab of Android Studio, or browse the auto-complete options in the IDE.
Each different View class (e.g., TextView, ImageView, etc.) will also have
their own set of properties.
Note that unlike CSS, styling properties specified in the layout XML resources
are not inherited; we’re effectively specifying an inline style attribute for that
element, and one that won’t affect child elements. In order to define shared
style properies, you’ll need to use styles resources, which are discussed in a later
lecture.
While it is possible to specify these visual properties dynamically via Java meth-
ods (e.g., setText(), setPadding()). You should only use Java methods to
specify View properties when they need to be dynamic (e.g., the text changes in
response to a button click)—it is much cleaner and effective to specify as much
visual detail in the XML resource files as possible. It’s also possible to simply
replace one layout resource with another (see below).
• Views also have inspection methods such as isVisible() and hasFo-
cus(); we will point to those as we need them.
3.2.2 Practice
Add a new ImageView element that contains a picture. Be sure and specify its
id and size (experiment with different options).
You can specify the content of the image in the XML resource using the an-
droid:src attribute (use @ to reference a drawable), or you can specify the
content dynamically in Java code:
ImageView imageView = (ImageView)findViewById(R.id.img_view);
imageView.setImageResource(R.drawable.my_image);
6 http://developer.android.com/reference/android/view/View.html#lattrs
46 CHAPTER 3. RESOURCES AND LAYOUTS
3.3 Layouts
As mentioned above, a Layout is a grouping of Views (specifically, a ViewGroup).
A Layout acts as a container for other Views, to help organize things. Layouts
are all subclasses of ViewGroup, so you can use its inheritance documentation
to see a (mostly) complete list of options, though many of the listed classes are
deprecated in favor of later, more generic/powerful options.
3.3.1 LinearLayout
Probably the simplest Layout to understand is the LinearLayout. This Layout
simply orders the children View in a line (“linearly”). All children are laid out
in a single direction, but you can specify whether this is horizontal or vertical
with the android:orientation property. See LinearLayout.LayoutParams for
a list of all attribute options!
• Remember: since a Layout is a ViewGroup is a View, you can also utilize
all the properties discussed above; the attributes are inherited!
Another common property you might want to control in a LinearLayout is how
much of any remaining space the elements should occupy (e.g., should they
expand). This is done with the android:layout_weght property. After all el-
ement sizes are calculated (via their individual properties), the remaining space
within the Layout is divided up proportionally to the layout_weight of each
element (which defaults to 0 so they get no extra space). See the example in
the guide for more details.
• Useful tip: Give elements 0dp width or height and 1 for weight to make
everything in the Layout the same size!
You can also use the android:layout_gravity property to specify the “align-
ment” of elements within the Layout (e.g., where they “fall” to). Note that this
property is specified on individual child Views.
An important point Since Layouts are Views, you can of course nest Lin-
earLayouts inside each other! So you can make “grids” by creating a vertical
Layout containing “rows” of horizontal Layouts (which contain Views). As with
HTML, there are lots of different options for achieving any particular interface
layout.
3.3.2 RelativeLayout
A RelativeLayout is more flexible (and hence powerful), but can be more
complex to use. In a RelativeLayout, children are positioned “relative” to the
parent OR to each other. All children default to the top-left of the Layout, but
you can give them properties from RelativeLayout.LayoutParams to specify
where they should go instead.
3.3. LAYOUTS 47
(Recall that the @+ syntax defines a new View id, like declaring a variable!)
You do not need to specify both toRightOf and toLeftOf; think about plac-
ing one element on the screen, then putting another element relative to what
came before. This can be tricky. For this reason the author prefers to use Lin-
earLayouts, since you can always produce a Relative positioning using enough
LinearLayouts (and most layouts end up being linear in some fashion anyway!)
3.3.3 ConstraintLayout
There are many other layouts as well, though we won’t go over them all in depth.
They all work in similar ways; check the individual class’s documentatoion for
details.
48 CHAPTER 3. RESOURCES AND LAYOUTS
But it is also possible to dynamically load views “manually” (e.g., in Java code)
using the LayoutInflator. This is a class that has the job of “inflating” (ren-
dering) Views. The process is called “inflating” based on the idea that it is
“unpacking” or “expanding” a compact resource description into a complex Java
Object. LayoutInflator is implicitly used in the setContentView() method,
but can also be used independently with the following syntax:
LayoutInflator inflator = getLayoutInflator(); //access the inflator (called on the A
View myLayout = inflator.inflate(R.layout.my_layout, parentViewGroup, true); //to att
//Java
ViewStub stub = (ViewStub)findViewById(R.id.stub);
View inflated = stub.inflate();
7 http://developer.android.com/training/improving-layouts/loading-ondemand.html
50 CHAPTER 3. RESOURCES AND LAYOUTS
Chapter 4
Interactive Views
This lecture discusses how to use Views to support user interaction and dy-
namic content, building on the previous lecture as while drawing on concepts
introduced in the Threads and HTTP Requests Appendix.
This lecture references code found at https://github.com/info448-s17/lecture04-
inputs-lists.
4.1 Inputs
The previous lecture discussed Views and ViewGroups (Layouts), and intro-
duced some basic Views such as TextView, ImageView, and Button.
A Button is an example of an Input Control. These are simple (single-purpose;
not necessarily lacking complexity) widgets that allow for user input. There are
many such widgets in addition to Button, mostly found in the android.widget
package. Many correspond to HTML <input> elements, but Android provided
additional widgets at well.
Launch the lecture code’s MainActivity with a content View of R.id.input_control_layout
to see an example of many widgets (as well as a demonstration of a more
complex layout!). These widgets include:
• Button, a widget that affords clicking. Buttons can display text, images
or both.
• EditText, a widget for user text entry. Note that you can use the an-
droid:inputType property to specify the type of the input similar to an
HTML <input>.
• Checkbox, a widget for selecting an on-off state
• RadioButton, a widget for selecting from a set of choices. Put RadioBut-
ton elements inside a RadioGroup element to make the buttons mutually
51
52 CHAPTER 4. INTERACTIVE VIEWS
exclusive.
• ToggleButton, another widget for selecting an on-off state.
• Switch, yet another widget for selecting an on-off state. This is just a
ToggleButton with a slider UI. It was introduced in API 14 and is the
“modern” way of supporting on-off input.
• Spinner, a widget for picking from an array of choices, similar to a drop-
down menu. Note that you should define the choices as a resource (e.g.,
in strrings.xml).
• Pickers: a compound control around some specific input (dates, times,
etc). These are typically used in pop-up dialogs, which will be discussed
in a future lecture.
• …and more! See the android.widget package for further options.
All these input controls basically work the same way: you define (instantiate)
them in the layout resource, then access them in Java in order to define inter-
action behavior.
There are two ways of interacting with controls (and Views in general) from the
Java code:
1. Calling methods on the View to manipulate it. This represents “outside
to inside” communication (with respect to the View).
2. Listening for events produced by the View and responding to then. This
represents “inside to outside” communication (with respect to the View).
An example of the second, event-driven approach was introduced in Lecture 2.
This involved registering a listener for the event (after acquiring a reference to
the View with findViewById()) and then specifying a callback method (by
instantiating the Listener interface) that wiould be “called back to” when the
event occurs.
• It is also possible to specify the callback method in the XML resource
itself by using e.g., the android:onClick attribute. This value of this
attribute should be the name of the callback method: It is also possible
to
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="handleButtonClick" />
ifying the callback method in the Java code helps keep the appearance
and behavior separate, and avoids introducing hidden dependencies for
resources (the Activity must provide the required callback). However, as
buttons are made to be pressed, it isn’t unreasonable to give a “name”
in the XML resource as to what the button will do, especially as the
corresponding Java method may just be a “launcher” method that calls
something else. Specifying the callback in the XML resource may often
seem faster and easier, and we will use whichever option best supports
clarity of our code.
Event callbacks are used to respond to all kind of input control widgets. Check-
Boxes use an onClick callback, ToggleButtons use onCheckedChanged, etc.
Other common events can be found in the View documentation, and are han-
dled via listeners such as OnDragListener (for drags), OnHoverListener (for
“hover” events), OnKeyListener (for when user types), or OnLayoutChange-
Listener (for when layout changes display).
The remainder of the lecture utilizes the list_layout Layout in the lecture
code. Modify MainActivity so that it uses this resource as its viewContent.
Having covered basic controls, this section will now look at some more advanced
interactive Views. In particular, it will discuss how to utilize a ListView1 , which
is a ViewGroup that displays a scrollable list of items! A ListView is basically
a LinearLayout inside of a ScrollView (which is a ViewGroup that can be
scrolled). Each element within the LinearLayout is another View (usually a
Layout) representing a particular item in a list.
1 https://developer.android.com/guide/topics/ui/layout/listview.html
54 CHAPTER 4. INTERACTIVE VIEWS
But the ListView does extra work beyond just nesting Views: it keeps track of
what items are already displayed on the screen, inflating only the visible items
(plus a few extra on the top and bottom as buffers). Then as the user scrolls, the
ListView takes the disappearing views and recycles them (altering their content,
but not reinflating from scratch) in order to reuse them for the new items that
appear. This lets it save memory, provide better performance, and overall work
more smoothly. See this tutorial for diagrams and further explanation of this
recycling behavior.
• Note that a more advanced and flexible version of this behavior is offered
by the RecyclerView. See also this guide for more details.
The ListView control uses a Model-View-Controller (MVC) architecture.
This is a deisgn pattern common to UI systems which organizes programs into
three parts:
1. The Model, which is the data or information in the system
2. The View, which is the display or representation of that data
3. The Controller, which acts as an intermediary between the Model and
View and hooks them together.
The MVC pattern can be found all over Android. At a high level, the resources
provide models and views (separately), while the Java Activities act as con-
trollers.
• Fun fact: The Model-View-Controller pattern was originally developed
as part of the Smalltalk language, which was the first Object-Oriented
language!
Thus in order to utilize a ListView, we’ll have some data to be displayed (the
model), the views (Layouts) to be shown, and the ListView itself will connect
these together act as the controller. Specifically, the ListView is a subclass
of AdapterView, which is a View backed by a data source—the AdapterView
exists to hook the View and the data together (a controller!)
• There are other AdapterViews as well. For example, GridView works
exactly the same way as a ListView, but lays out items in a scrollable
grid rather than a scrollable list.
In order to use a ListView, we need to get the pieces in place:
1. First we specify the model: some raw data. We will start with a simple
String[], filling it with placeholder data:
String[] data = new String[99];
for(int i=99; i>0; i--){
data[99-i] = i+ " bottles of beer on the wall";
}
While we could define this data as an XML resource, we’ll create it dy-
namically for testing (and to make it changeable later!)
4.2. LISTVIEWS AND ADAPTERS 55
2. Next we specify the view: a View to show for each datum in the list.
Define an XML layout resource for that (list_item is a good name and
a common idiom).
For simplicity’s sake we don’t need to specify a full Layout, just a basic
TextView. Have the width match_parent and the height wrap_content.
Don’t forget an id!
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/txtItem"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
• Note the parameters of the constructor: a Context, the item layout re-
source, the TextView reource, and the data array. Also note that this
instance utilizes generics, since we’re using an array of Strings (as op-
posed to an array of Dogs or some other type).
We acquire a reference to the ListView with findViewById(), and call
ListView#setAdapter() to attach the adapter to that controller.
ListView listView = (ListView)findViewById(R.id.listview);
listView.setAdapter(adapter);
accessing a database), could cause other tasks to have to wait. It’s like a traffic
jam!
• Tasks such as network access are blocking method calls, which stop the
Thread from continuing. A blocked Main Thread will lead to the infamous
“Application not responding” (ANR) error!
Thus we need to move the network code off the Main Thread, onto a back-
ground thread, thereby allowing it to run without blocking the user interaction
that occurs on the Main Thread. To do this, we will use a class called ASync-
Task3 to perform a task (such as network access) asynchronously—without wait-
ing for other Threads.
Learning Android Development involves knowing about what classes exist, and
can be used to solve problems, but how were we able to learn about the existing
of this highly useful (and specialized) ASyncTask class? We started from the
official API Guide on Processes and Threads Guide4 , which introduces this class!
Thus to learn about new Android options, read the docs.
Note that an ASyncTask background thread will be tied to the lifecycle of the
Activity: if we close the Activity, the network connection will die as well. A
better but much more complex solution would be to use a Service—which is
covered in a future lecture. But since this example just involves getting a small
amount of data, we don’t really care if the network connection gets dropped.
ASyncTask can be fairly complicated, but is a good candidate to practice learn-
ing from the API documentation. Looking at that documentation, the first
thing you should notice (or would if the API was a little more readable) is that
ASyncTask is abstract, meaning you’ll need to subclass it in order to use it.
Thus you can subclass it as an inner class inside the Activity that will use it
(MovieDownloadTask is a good name).
You should also notice that ASyncTask is a generic class with three (3) generic
parameters: the type of the Parameter to the task, the type of the Progress
measurement reported by the task, and the type of the task’s Result. We can
fill in what types of Parameter and Result we want from our asynchronous
method (e.g., take in a String and return a String[]), and use the Void type
for the Progress measurement (since we won’t be tracking that).
When we “run” an AsyncTask, it will do four (4) things, represented by four
methods:
1. onPreExecute() is called on the UI thread before we run the task. This
method can be used to perform any setup for the task.
2. doInBackground(Params...) is called on the background thread to do
the work we want to be performed asynchronously. We must override
this method (it’s abstract!) The params and return type for the method
need to match the ASyncTask generic types.
3 https://developer.android.com/reference/android/os/AsyncTask.html
4 https://developer.android.com/guide/components/processes-and-threads.html
58 CHAPTER 4. INTERACTIVE VIEWS
Note that Marshmallow introduced a new security model in which users grant
permissions at run-time, not install time, and can revoke permissions whenever
they want. To handle this, you need to add code to request “dangerous” per-
missions (like Location, Phone, or SMS access; Internet is not dangerous) each
time you use it.
• For “normal” permissions (e.g., Internet), you declare the permission need
in the Manifest.
• For “dangerous” permissions (e.g., Location), you declare the permission
need in the Manifest and request permission programmatically in code
each time you want to use it.
Once we’ve requested permission (and have been granted that permission by
virtue of the user installing our application), we can finally connect to the In-
ternet to download data. We can log out the request results to provide it.
In order to get the downloaded data into a ListView, we utilize the doPos-
tExecute() method. This method is run on the UI Thread so we can use it
to update the View (we can only change the View on the UI Thread, to avoid
collisions). It also gets the results returned by doInBackground() passed to it!
4.3. NETWORK DATA 59
We take that passed in String[] and put that into the ListView. Specifically,
we feed it into the Adapter, which then works to populate the views.
• First clear out any previous data items in the adapter using
adapter.clear().
• Then use adapter.add() or (adapter.addAll()) to add each of the new
data items to the Adapter’s model.
• You can call notifyDataSetChanged() on the Adapter to make sure that
the View knows the data has changed, but this method is already called
by the .add() method so isn’t necessary in this situation.
To finalize the app: we can enable the user to search for different movies by
copying the EditText and Button Views from the previous input_layout re-
source, accessing the text from the former when the later is pressed. We can
then pass the EditText content String into the ASyncTask#execute() func-
tion (since we’ve declared that the generic ASyncTask takes that type as the
first Parameter).
• We can actually pass in multiple String arguments using the String...
params spread operator syntax (representing an arbitrary number of items
of that type). See here for details. The value that the ASyncTask methods
actually get is an array of the arguments.
In the end, we are able to downlod data from the Internet and show an inter-
active list of that data in the app! We’ve done a whirl-wind tour of Android
in this process: Layouts in the XML, Adapters in the Activity, Threading in a
new class, Security in the Manifest… bringing lots of parts together to provide
a particular piece of functionality.
60 CHAPTER 4. INTERACTIVE VIEWS
Chapter 5
Fragments
61
62 CHAPTER 5. FRAGMENTS
Activity’s lifecycle. (e.g., if the Activity is paused, the Fragment is too. If the
Activity is destroyed, the Fragment is too). However, Fragments also have their
own lifecycle with corresponding lifecycle callbacks functions.
The Fragment lifecycle is very similar to the Activity lifecycle, with a a couple
of additional steps:
• onAttach(): called when the Fragment is first associated with (“added
to”) an Activity, and thus gains a Context. This callback is generally used
for initializing communication between the Fragment and its Activity.
This callback is mirrored by onDetach(), for when the Fragment is re-
moved from an Activity.
• onCreateView(): called when the View (the user interface) is about to
be drawn. This callback is used to establish any details dependent on the
View (including adding event listeners, etc).
Note that code intializing data models, or anything that needs to be per-
sisted across configuration changes, should instead be done in the onCre-
ate() callback. onCreate() is not called if the fragment is retained (see
below).
This callback is mirrored by onDestroyView(), for when the Fragment’s
UI View hierarchy is being removed from the screen.
2 https://developer.android.com/images/fragment_lifecycle.png
63
Defining the Fragment in the XML works (and will be fine to start with), but in
practice it is much more worthwhile to instantiate the Fragemnts dynamically
at runtime in the Java code—thereby allowing the Fragments to be dynamically
determined and changed. We will start with the XML version to built the
Fragment, and then shift to the Java version.
We can next begin filling in the Java logic for the Fragment. Android Studio pro-
vides a little starter code: a constructor and the onCreateView() callback—the
later is more relevant since we will use that to set up the layout (similar to in the
onCreate() function of MainActivity). But the MainActivity#onCreate()
method specifies a layout by calling setContentView() and pasing a resource
id. With Fragments, we can’t just “set” the View because the Fragment belongs
to an Activity, and so will exist inside its View hierarchy! Instead, we need to
figure out which ViewGroup the Fragment is inside of, and then inflate the
Fragment inside that View.
This “inflated” View is referred to as the root view: it is the “root” of the
Fragment’s View tree (the View that all the Views inside the Fragment’s layout
will be attached to). We access the root view by inflating the fragment’s layout,
and saving a reference to the inflated View:
View rootView = inflater.inflate(R.layout.fragment_layout, container, false);
The example code intentionally has left the input controls (the search field and
button) in the Activity, rather than making them part of the Fragment. Apart
from being a useful demonstration, this allows the Fragment to have a single
purpose (showing the list of movies) and would let us change the search UI
independent of the displayed results. But since the the button is in the Activity
but the downloading functionality is in the Fragment, we need a way for the
Activity to “talk” to the Fragment. We thus need a reference to the contained
Fragment—access to the XML similar to that provided by findViewById.
We can get a reference to a contained Fragment from an Activity by using a
FragmentManager. This is an object responsible for (ahem) managing Frag-
ment. It allows us to “look up” Fragments, as well as to manipulate which
Fragments are shown. We access this FragmentManager by calling the get-
SupportFragmentManager() method on the Activity, and then can use find-
FragmentById() to look up an XML-defined Fragment by its id:
//MovieFragment example
MovieFragment fragment = (MovieFragment)getSupportFragmentManager().findFragmentById(
• Note that we’re using a method to explicit access the support Fragment-
Manager. The Activity class (API level 15+) is able to work with both
the platform and support FragmentManager classes. But because these
5.2. DYNAMIC FRAGMENTS 67
classes don’t have a shared interface, the Activity needs to provide dif-
ferent Java methods which can return the correct type.
Once you have a reference to the Fragment, this acts just like an other object—
you can call any public methods it has! For example, if you give the Fragment
a public method (e.g., searchMovies()), then this method can be called from
the Activity:
//called from Activity on the referenced fragment
fragment.searchMovies(searchTerm)
(The parameter to this public method allows the Activity to provide information
to the Fragment!)
At this point, the program should be able to be executed… and continue to
function in exactly the same way! The program has just been refactored, so that
all the movie downloading and listing work is encapsulated inside a Fragment
that can be used in different Activities.
• In effect, we’ve created our own “widget” that can be included in any
other screen, such as if we always wanted the list of movies to be available
alongside some other user interface components.
In order to pass the arguments into the new Fragment, we wrap them up in a
Bundle (an object containing basic key-value pairs). Values can be added to a
Bundle using an appropriate putType() method; note that these do need to
be primative types (int, String, etc.). The Bundle of arguments can then be
assignment to the Fragment by calling the setArguments() method.
• We will be able to access this Bundle from inside the Fragment (e.g., in the
onCreateView() callback) by using the getArguments() method (and
getType() to retrieve the values from it). This allows us to dynamically
adjust the content of the Fragment’s Views! For example, we can run
the downloadMovieData() function using this argument, fetching movie
results as soon as the Fragment is created (e.g., on a button press).
• Since the Bundle is a set of key-value pairs, each value needs to have a
particular key. These keys are usually defined as private constants (e.g.,
ARG_PARAM_KEY in the above example) to make storage and retrieval eas-
ier.
We will then be able to instantiate the Fragment (e.g., in the Activity class),
passing it any arguments we wish:
5.2. DYNAMIC FRAGMENTS 69
5.2.2 Transactions
Once we’ve instantiated a Fragment in the Java, we need to attach it to the
view hierarchy: since we’re no longer using the XML <fragment> element, we
need some other way to load the Fragment into the <FrameLayout> container.
We do this loading using a FragmentTransaction3 . A transaction represents
a change in the Fragment that is being displayed. You can think of this like
a bank (or database) transaction: they allow you to add or remove Fragments
like we would add or remove money from a bank account. We instantiate new
transactions representing the change we wish to make, and then “run” that
transaction in order to apply the change.
To create a transaction, we utilize the FragmentManager again; the Fragment-
Manager#beginTransaction() method is used to instantiate a new Frag-
mentTransaction.
Transactions represent a set of Fragment changes that are all “applied” at the
same time (similar to depositing and withdrawing money from multiple accounts
all at once). We specify these transactions using by calling the add(), remove(),
or .replace() methods on the FragmentTransaction.
• The add() method lets you specify which View container you want to
add a particular Fragment to. The remove() method lets you remove a
Fragment you have a reference to. The replace() method removes any
Fragments in a container and then adds the specified Fragment instead.
• Each of these methods returns the modified FragmentTransaction, so
they can be “chained” together.
Finally, we call the commit() method on the transaction in order to “submit”
it and have all of the changes go into effect.
We can do this work in the Activity’s search click handler to add a Fragment,
rather than specifying the Fragment in the XML:
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
//params: container to add to, Fragment to add, (optional) tag
transaction.add(R.id.container, myFragment, MOVIE_LIST_FRAGMENT_TAG);
transaction.commit();
FragmentTransaction.html
70 CHAPTER 5. FRAGMENTS
We can use this structure for instantiating and loading (via transactions) a
second Fragment (e.g., a “detail” view for a selected Movie). We can add
functionality (e.g., in the onClick() handler) so that when the user clicks on
a movie in the list, we replace() the currently displayed Fragment with this
new detailed Fragment.
However, remember that Fragments are supposed to be modular—each Frag-
ment should be self-contained, and not know about any other Fragments that
may exist (after all, what if we wanted the master/detail views to be side-by-side
on a large screen?)
Using getActivity() to reference the Activity and getSupportFragmentMan-
ager() to access the manager is a violation of the Law of Demeter—don’t do
it!
Instead, we have Fragments communicate by passing messages through their
contained Activity: the MovieFragment should tell its Activity that a particular
movie has been selected, and then that Activity can determine what to do about
it (e.g., creating a DetailFragment to dispaly that information).
The recommended way to provide Fragment-to-Activity communication is to
define an interface. The Fragment class should specify an interface (for one
or more public methods) that its containing Activity must support—and since
the Fragment can only exist within an Activity that implements that interface,
it knows the Activity has the specified public methods that it can call to pass
information to that Activity.
As an example of this process:
• Create a new interface inside the Fragment (e.g., OnMovieSelect-
edListener). This interface needs a public method (e.g., onMovieSe-
lected(Movie movie)) that the Fragment can call to give instructions
or messages to the Activity.
• In the Fragment’s onAttach() callback (called when the Fragment is first
associated with an Activity), we can check that the Activity actually im-
plements the interface by trying to cast it to that interface. We can also
save a reference to this Activity for later:
public void onAttach(Context context) {
super.onAttach(context);
try {
5.2. DYNAMIC FRAGMENTS 71
callback = (OnMovieSelectedListener)context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement OnMovieSelected
}
}
But what happens when we hit the “back” button? The Activity exits! Why?
Because “back” normally says to “leave the Activity”—we only had one Activity,
just multiple fragments.
Recall that the Android system may have lots of Activities (even across multiple
apps!) with the user moving back and forth between them. As described in
lecture2, each new Activity is associated with a “task” and placed on a stack4 .
When the “back” button is pressed, that Activity is popped off the stack, and
the user is taken to the Activity that is now at the top.
Fragments by default are not part of this “back-stack”, since they are just compo-
nents of Activities. However, you can specify that a transaction should include
the Fragment change as part of the stack navigation by calling FragmentTrans-
action#addToBackStack() as part of your transaction (e.g., right before you
commit()):
getSupportFragmentManager().beginTransaction()
.add(detailFragment, "detail")
// Add this transaction to the back stack
4 http://developer.android.com/images/fundamentals/diagram_backstack.png
72 CHAPTER 5. FRAGMENTS
.addToBackStack()
.commit();
Note that the “back” button will cause the entire transaction to “reverse”. Thus
if you performed a remove() then an add() (e.g., via a replace()), then
hitting “back” will cause the the previously added Fragment to be removed and
the previously removed Fragment to be added.
• FragmentManager also includes numerous methods for manually manipu-
lating the back-stack (e.g., “popping” off transactions) if necessary.
Chapter 6
UI Components
This lecture discusses how to include menus and pop-up dialogs in an Android
application as additional navigation and display components. Note that this
lecture aims to provide exposure rather than depth to these concepts; for further
details and options, see the official Android documentation.
This lecture references code found at https://github.com/info448-s17/lecture06-
menus-dialogs.
73
74 CHAPTER 6. UI COMPONENTS
From in the Activity’s Java code, we can get access to the Action Bar by calling
the getSupportActionBar() method (for a support Toolbar). We can then
call utility methods on this object to interact with it; for example .hide() will
hide the toolbar!
6.2 Menus
However, the main use for the Action Bar is a place to hold Menus. A Menu
(specifically, an options menu) is a set of items (think: buttons) that appear
in the Action Bar. Menus can be specified both in the Activity and in a
Fragment; if declared in both places, they are combined into a single menu in
the Action Bar. This allows you to easily make “context-specific” options menus
that are only available for an appropriate Fragment, while keeping Fragments
modular and self-contained.
• Fun fact: before API 11, options menus appeared as buttons at the bottom
of the screen!
Menus, like all other user-facing elements, are defined as XML resources, specif-
ically of type menu. You can create a new menu resource through Android
studio using File > New > Android resource file and then choosing the
Menu Resource type. This will create an XML file with a main <menu> element.
Options can be added to the menu by specifying child XML elements, particu-
larly <item> elements. Common <item> attributes include:
• android:id: a unique id used to refer to the specific option in the Java
code
• android:title (required attribute): the text to display for the option.
As user-facing text, the content should ideally be defined as an XML String
resource.
• app:showAsAction: whether or not the option should be listed in the
Action Bar, or collapsed under a “three-dots” button. Note when working
with the appcompat library, this option uses the app namespace (instead
of android); you will need to include this schema in the <menu> with the
attribute xmlns:app="http://schemas.android.com/apk/res-auto".
• android:icon: an image to use when showing the option as a button on
the menu //CHECK THIS
You can use one of the many icons built into the Android, referenced as
"@android:drawable/ic_*". Android Drawables2 includes the full list,
though not all drawables are publicly available through Android Studio.
• android:orderInCategory: used to order the item in the menu (or in
a group). This acts as a “priority” (default 0; low comes first). Such
2 http://androiddrawables.com/
6.2. MENUS 75
• On default (if the item selected isn’t handled by any cases), we pass the
callback up to super for “higher-level” components to check. For exampe,
if a the menu option isn’t handled by the Fragment (because the Fragment
didn’t add it), the event can be passed up through the Framework for
eventually handling by the Activity (who did add it).
3 https://developer.android.com/guide/topics/resources/menu-resource.html
76 CHAPTER 6. UI COMPONENTS
• This method should return true if the selection even has been handled
(and thus should not be considered by anyone else). Return false if you
want other components (e.g., other Fragments) to be able to respond to
this option as well.
There are many other menu items that can be placed on Action Bar as well.
We can also add Action Views that provide more complex interactions than just
clicking buttons (for example, including a search bar). An Action Provider (like
ShareActionProvider) is an action with its own customized layout, expanding
into a separate View when clicked. We wil discuss how to utilize these features
in a future lecture.
In addition to options menus available in the Action Bar, we can also specify
contextual menus that pop up when the user long-presses on an element. This
works similarly to using an options menu, but with a different set off callbacks:
• When setting up the the View layout (e.g., in an Activity’s onCreate()),
we specify that an element has a context menu using the registerFor-
ContextMenu() method, passing it the View we want to be able to create
the menu for.
• Specify the context menu to use through the onCreateContextMenu()
callback. This works exactly like setting up an options menu.
• In fact, a context menu can even use the same menu as an options menu!
This reuse is one of the advantages of defining the user interface as XML.
• And mirroring the options menu, respond to context menu items being
selected with the onContextItemSelected() callback.
This section has provided a very brief introduction to menus, but there are many
more complex interactions that they support. I highly recommend that you read
through the guide in order to learn what features may be available.
If you ever are using an app and wonder “how did they add this interface
feature?”, look it up! There is almost always a documented procedure and
example for providing that kind of component.
6.3 Dialogs
While it is simple enough to make menu items that log out some text, logs
cannot be seen the user. Instead, we woud like to show the message to the user
as a kind of “pop-up” message.
6.3. DIALOGS 77
A Dialog 4 is a “pop-up” modal (a view which doesn’t fill the screen) that either
asks the user to make a decision or provides some additional information. At
it’s most basic, Dialogs are similar to the window.alert() function used in
JavaScript.
There is a base Dialog class, but almost always we use a pre-defined subclass
instead (similar to how we’ve use AppCompatActivity). AlertDialog5 is the
most common version: a simple message with buttons you can respond with
(confirm, cancel, etc).
We don’t actually instantiate an AlertDialog directly (in fact, it’s constructors
are protected so inaccessible to us). Instead we use a helper factory class
called an AlertDialog.Builder. There are a number of steps to use a builder
to create a Dialog:
1. Instantiate a new builder for this particular dialog. The constructor takes
in a Context under which to create the Dialog. Note that once the builder
is initialized, you can create and recreate the same dialog with a single
method call—that’s the benefits of using a factory.
2. Call “setter” methods on the builder in orer to specify the title, message,
etc. for the dialog that will appear. This can be hard-coded text or a
reference to an XML String resource (as a user-facing String, the later
is more appropriate for published applications). Each setter method will
return a reference to the builder, making it easy to chain them.
3. Use appropriate setter methods to specify callbacks (via a DialogInter-
face.OnClickListener) for individual buttons. Note that the “positive”
button normally has the text "OK", but this can be customized.
4. Finally, actually instantiate the AlertDialog with the builder.create()
method, using the show() method to make the dialog appear on the
screen!
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Alert!")
.setMessage("Danger Will Robinson!");
builder.setPositiveButton("I see it!", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User clicked OK button
}
});
alert?
While AlertDialog is the most common Dialog, Android supports other sub-
classes as well. For example, DatePickerDialog and TimePickerDialog pro-
vide pre-defined user interfaces for picking a date or a time respectively. See
the Pickers guide for details about how to utilize these.
6.3.1 DialogFragments
The process described above will create and show a Dialog, but that dialog has
a few problems in how it interacts with the rest of the Android framework—
namely with the lifecycle of the Activity in which it is embedded.
For example, if the device changes configurations (e.g., is rotated from portrait
to landscape) then the Activity is destroyed and re-created (it’s onCreate()
method will be called again). But if this happens while a Dialog is being shown,
then a android.view.WindowLeaked error will be displayed and the Dialog is
lost!
To avoid these problems, we need to have a way of giving that Dialog its own
lifecycle which can interact with the the Activity’s lifecycle… sort of like making
it a modular piece of an Activity… that’s right, we need to make it a Fragment!
Specifically, we will use a subclass of Fragment called DialogFragment, which
is a Fragment that displays as a modal dialog floating above the Activity (no
extra work needed).
Just like with the Fragment examples from the previous lecture, we’ll need to
create our own subclass of DialogFragment. It’s often easiest to make this a
nested class if the Dialog won’t be doing a lot of work (e.g., shows a simple
confirmation).
Rathern than specifying a Fragment layout through onCreateView(), we can
instead override the onCreateDialog() callback to specify a Dialog object that
will provide the view hierarchy for the Fragment. This Dialog can be created
with the AlertDialog.Builder class as before!
public static class MyDialogFragment extends DialogFragment {
6.4 Toasts
Dialogs are a powerful way of providing messages and information to users, but
they are pretty “heavy” in terms of both their interaction (they stop all other in-
teraction to show the user a message) and the effort required to implement them.
Sometimes you just want a “pop-up” message that isn’t quite as prominent and
doesn’t require the user to click “okay” once they’ve seen it.
A simple, quick way of giving some short visual feedback is to use what is called
a Toast. This is a tiny little text box that pops up at the bottom of the screen
for a moment to quickly display a message.
• It’s called a “Toast” because it pops up!
Toasts are pretty simple to implement, as with the following example (from the
official documentation):
Context context = this; //getApplicationContext(); //use application context to avoid disappear
String text = "Hello toast!";
80 CHAPTER 6. UI COMPONENTS
But since this Activity is a Context, and we can just use the Toast anony-
mously, we can shorten this to a one-liner:
Toast.makeText(this, "Hello toast!", Toast.LENGTH_SHORT).show();
Boom, a quick visual alert method we can use for proof-of-concept stuff!
Toasts are intended to be a way to provide information to the user (e.g., giving
them quick feedback), but they can possibly be useful for testing too (though
in the end, Logcat is going to be your best bet for debugging, especially when
trying to solve crashes or see more complex output).
Chapter 7
This lecture discusses how to access data from a Content Provider using a
Loader. A Content Provider is an abstraction of a data base or other data store,
allowing us easily systematically work with that data in Java (rather than in a
separate data manipulation language such as SQL). A Loader is then used to
efficiently perform this data access in the background (off the UI Thread), while
also easily connecting that data to Views.
This lecture references code found at https://github.com/info448-s17/lecture07-
loaders.
81
82 CHAPTER 7. PROVIDERS AND LOADERS
(rows) each of which have some values (columns). The primary key of the table
is named (by convention) ID.
While you don’t need to know SQL to utilize a built-in database like the User
Dictionary, it helps to have a passing familiarity with relational databases (e.g.,
what is taught in the iSchool’s INFO 340 course).
Since this data is stored in a (essentially) a simple SQL table, it is possible for us
to access and modify it programmatically&mash;moreover, the Android frame-
work allows us to do this without needing to know or write SQL! For example,
we can access this list of words in order to show them in the WordFragment’s
ListView.
• To do this, we’ll need to request permission to access the database, just as
we asked permission to access the Internet. Include the following in the
Manifest:
<uses-permission android:name="android.permission.READ_USER_DICTIONARY">
Although the words are stored in a database, we don’t know the exact format
of this database (e.g., exact table or column names, or even whether it is an
SQL database or just a .csv file!). We want to avoid having to write code
that only works with a specific format, especially as the words may be stored
in different kinds of databases on different devices or across different versions
of Android. (The Android framework does include support for working directly
with a local SQLite database, but it is a lot more work, requires knowing SQL,
and produces a more fragile application).
In order to avoid relying on the specific format of how some data is stored,
Android offers an abstraction in the form of a Content Provider. A Content
Provider offers an interface to interact with structured data, whether that data
is stored in a database, in a file, in multiple files, online, or somewhere else. You
can thus think of “a Content Provider” as meaning “a data source” (e.g., the
source/provider of content)!
• It is possible to create your own Content Providers (described in a later
lecture), but this lecture focuses purely on utilizing existing Providrs.
All Content Providers (data sources) have a URI (Universal Resource Identifier,
a generalization of a URL used for resources not necessarily on the Internet). It
is possible to query this URI, similar in concept to how web APIs are accessed
via queries to their URI endpoints. In particular, Content Provider URIs utilize
the content:// protocol (instead of https://), since the their data is accessed
as via “content requests” rather than “HTTP requests”.
The URI for the Dictionary’s content is defined by the constant UserDic-
tionary.Words.CONTENT_URI. We utilize constants to refer to URIs and paths
to make it easier to refer to them and to generalize across devices that may have
different directory structures.
We are able to access this Content Provider via a ContentResolver. This class
7.2. CURSORS 83
So overall, the query is breaking apart the components SQL SELECT statement
into different pieces as parameters to a method, so you don’t quite have to
write the selection yourself. Moreover, this method abstracts the specific query
language, allowing the same queries to be used on different formats of database
(SQLite, PostgreSQL, files, etc).
7.2 Cursors
The ContentResolver#query() method returns a Cursor. A Cursor provides
an interface to the list of records in a database (e.g., those returned by the
query). A Cursor also behaves like an Iterator in Java: it keeps track of
84 CHAPTER 7. PROVIDERS AND LOADERS
which record is currently being accessed (e.g., what the i would be in a for
loop). You can think of it as a “pointer” to a particular record, like the cursor
on a screen.
We call methods on the Cursor to specify which record we want it to “point”
to, as well as to fetch values from the record object at that spot in the list. For
example:
cursor.moveToFirst(); //move to the first item
String field0 = cursor.getString(0); //get the first field (column you specified) as
String name = cursor.getString(cursor.getColumnIndexOrThrow("word")); //get the "word
cursor.moveToNext(); //go to the next item
The nice thing about Cursors though is that they can easily be fed into
AdapterViews by using a CursorAdapter (as opposed to the ArrayAdapter
we’ve used previously). The SimpleCursorAdapter is a concrete implementa-
tion that is almost as easy to use as an ArrayAdapter:
You instantiate a new SimpleCursorAdapter, passing it:
1. A Context for loading resources
2. A layout resource to inflate for each record
3. A Cursor (which can be null)
4. An array of column names to fetch from each entry in the Cursor (the
projection, similar to before)
5. A matching list of View resource ids (which should all be TextViews) to
assign each column’s value to. This is the “mapping” that the Adapter
will perform (from projection columns to TextView contents).
6. Any additional option flags (0 means no flags, and is the correct option
for us).
Then we can use this adapter for the ListView in place of the ArrayAdapter!
7.3 Loaders
In order to get the Cursor to pass into the adapter, we need to .query()
the database. But we’ll be doing this a lot, and so would like to do it off the
UI Thread—database accessing is slow! And every time we do that query (or
any other database manipulation), we want to update the Adapter so that the
changes to the list show up.
In order to easily update your list with new data loaded on a background thread,
we’re going to use a class called a Loader. This is basically a wrapper around
ASyncTask, but one that lets you execute a backgroup task repeatedly whenever
the data source changes. In particular, Android provides a CursorLoader specif-
ically used to load data from ContentProviders through Cursors—whenever the
content changes, a new Cursor is produced which can be “swapped” into the
adapter.
7.3. LOADERS 85
We will need to fill in the interfaces callbacks functions in order to use the
CursorLoader:
• In the onLoaderReset() callback just swap in null for our Cursor, since
there now is no content to show (the loaded data has been “reset”).
Finally, in order to actually start our background activity, we’ll use the get-
LoaderManager().initLoader(...) method. This is similar in flavor to the
AsyncTask.execute() method we’ve used before (using a manager similar to
the FragmentManager).
getLoaderManager().initLoader(0, null, this);
The first parameter to the initLoader() method is an id number for which cur-
sor you want to load, and is passed in as the first param to onCreateLoader()
(or is accessible via Loader#getId()). This allows you to have multiple Loaders
using the same callback function (e.g., a Fragment can handle multiple Loaders
for multiple data sources). The second param is a Bundle of args, and the third
is the LoaderCallbacks (e.g., who handles the results)!
• Note that you can use the .restartLoader() method to “recreate” the
CursorLoader (without losing other references), such as if you want to
change the arguments passed to it.
And with that, we can fetch the words from our database on a background
thread—and if we update the words it will automatically change!
86 CHAPTER 7. PROVIDERS AND LOADERS
Intents
This lecture discusses how to use Intents to communicate between different Ac-
tivities and Applications. The Intent system allows Activities to communicate,
even though they don’t have references to each other (and thus we can’t just
call a method on them).
This lecture references code found at https://github.com/info448-s17/lecture08-
intents. Note that you will need to have a working camera on your device. To
enable the camera in the emulator, use the Tools > Android > AVD menu to
modify the emulator, and select “webcam” for the front camera option. Confirm
that it is enabled by launching the Camera app.
An Intent is a message that is sent between app components, allowing them to
communicate!
• Most object communication we do is via direct method call; you have a
reference to an Object and then you call a method on it. We’ve also seen
event callbacks, where on an event one of our callbacks gets executed by the
system (really just a wrapper around direct method call via the Observer
pattern)
• Intents step outside of this a little bit: they allow us to create objects
that can be “given” to another component (read: Activity), who can then
respond upon receiving that. Similar to an event callback, but working at
a slightly higher system level.
You can think of Intents as like letters you’d send through the mail: they are
addressed to a particular target (e.g., another Activity—more properly a Con-
text), and have room for some data called extras to go inside (held in a
Bundle). When the envelope arrives, the recipient can get that data out and
do something with it… and possibly sending a response back.
Note that there are couple of different kinds of Intents; we’ll go through examples
of each.
87
88 CHAPTER 8. INTENTS
• The first parameter refers to the current Context in which the message
should be delivered. The second parameter to this constructor is the class
we want to send the Intent to (the .class property fetches a reference to
the class type; this is metaprogramming!). Effectively, it is the “address”
on the envelop for the message we’re sending.
– We’re using MainActivity.this as the context, because the this
would refer to the anonymous listener class (for methods in Main, we
can just use this).
After having instantiated the new Intent, we can use that message to start an
Activity by calling the startActivity() method (inherited from Activity),
passing it the Intent:
startActivity(intent);
This method will “send” the message to the operating system, which will deliver
the Intent to the appropriate Activity, telling that Activity to start as soon as
it receives the message.
• And we can use the back button to go backwards! See the Activities
lecture for details.
This is called an Explicit Intent because we’re explicit about what target we
want to receive it. It’s a letter to a specific Activity.
8.1.1 Extras
We can also specify some extra data inside our envelope. These data are referred
to as Extras. This is a Bundle (so a set of primitive key-value pairs) that we
can use to pass limited information around!
intent.putExtra("package.name.key","value");
1 http://developer.android.com/reference/android/content/Intent.html
8.2. INTENTS FOR ANOTHER APP (IMPLICIT) 89
• Docs say that best practice is to include the full package name on keys, so
avoid any collisions or misreading of data. There are also some pre-defined
values (constants) that you can use in the Intent class.
We can then get the extras from the Intent in the Activity that receives it:
//in onCreate();
Bundle extras = getIntent().getExtras(); //All activities are started with an Intent!
String value = extras.getString("key");
2 http://www.theguardian.com/world/2015/jul/18/postman-turns-detective-to-deliver-
letter-with-cryptic-address-in-ireland
90 CHAPTER 8. INTENTS
startActivity(intent);
}
Here we’ve specified the Action (ACTION_DIAL) for our Intent, as well as some
Data (a phone number, converted into a Uri). The resolveActivity() method
looks up what Activity is going to receive our action–we check that it’s not null
before trying to start it up.
• This should allow us to “dial out” !
Note that we can open up all sorts of apps. See Common Intents3 for a list of
common implicit events (with examples!).
3 http://developer.android.com/guide/components/intents-common.html
4 http://developer.android.com/training/camera/photobasics.html#TaskPath
8.4. LISTENING FOR INTENTS 91
In order to handle the “response” Intent, we need to provide a callback that will
get executed when that Intent arrives. Called onActivityResult().
• We can get information about the Intent we’re receiving from the params.
And we can get access to the returned data (e.g., the image) by getting
the "data" field from the extras.
• Note that this is a Bitmap, which is the Android class representing a raster
image. We’ll play with Bitmaps more in a couple weeks, because I like
graphics.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Bundle extras = data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
mImageView.setImageBitmap(imageBitmap);
}
}
• a <data ...> filter, which specifies aspects of the data we accept (e.g.,
only respond to Uri’s that look like telephone numbers)
• a <category android:name="category"> filter, which is basically a
“more information” piece. You can see the “Standard Categories” in the
documentation.
• Note that you must include the DEFAULT category to receive implicit in-
tents. This is the category used by startActivity() and startActiv-
ityForResult.
Note that you can include multiple actions, data, and category tags. You just
need to make sure that you can handle all possible combinations selected from
each type (they are “or” not “and” filters!)
Responding to that dial command:
<activity android:name="SecondActivity">
<intent-filter>
<action android:name="android.intent.action.DIAL"/>
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel" />
</intent-filter>
</activity>
But in order to register our receiver (so that intents go past its desk), we also
need to specify it in the Manifest. We do this by including a <receiver>
attribute inside our <application>. Note that this is not an Activity, but a
separate component! We can put an <intent-filter> inside of this to filter
for broadcasts we care about.
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
<action android:name="android.intent.action.BATTERY_CHANGED" />
<action android:name="android.intent.action.BATTERY_OKAY" />
<!-- no category because not for an activity! -->
</intent-filter>
</receiver>
We can test these power events easily using the latest version of the emulator.
In the “extra options” button (the three dots at the bottom) in the emulator’s
toolbar, we can get to the Battery tab where we can effectively change the
battery status of the device (which our app can respond to!)
• Note that there is a Phone tab where you can send Text Messages to the
emulator… you’ll need this for your homework this week.
We can also register these receivers in code (rather than in the manifest). This
is good for if we only want to temporarily listen for some kind of events, or if
we want to determine the intent-filter on the fly.
IntentFilter batteryFilter = new IntentFilter();
batteryFilter.addAction(Intent.ACTION_BATTERY_LOW);
batteryFilter.addAction(Intent.ACTION_BATTERY_OKAY);
batteryFilter.addAction(Intent.ACTION_POWER_CONNECTED);
batteryFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
this.registerReceiver(new MyReceiver(), batteryFilter);
• Important note: the SMS APIs changed drastically in KitKat (API 19).
So we’re going to make sure that is our minimum so we can get all the
helpful methods and support newer stuff (check gradle to confirm!).
The main thing to note about sending SMS is that as of KitKat, each system has
a default messaging client—who is the only one who can actually send messages.
Luckily, the API lets you get access to that messaging client’s services in order
to send a message through it:
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage("5554", null, "This is a test message!", null, null);
// target, message
If we look at the documentation for this method5 , you can see that this works
by looking at the inbox in the Messages app… but there is another way as well.
Those last two parameters are for PendingIntents: one for when messages are
sent and one for when messages are delivered.
• What’s a PendingIntent? The details are not super readable… It’s basi-
cally a wrapper around an Intent that we give to another class. Then
when that class receives our PendingIntent and reacts to it, it can run
the Intent (command) we sent it with as if that Activity was us (whew).
• Basically we’re saying “when I call you, you can come pick me up using
my car” kind of thing.
• Or like if you gave a stamped envelope to someone to put your letter or
recommendation inside (do this!)
• So the idea is we specify what Intent should be delivered when the mes-
sage is finished being sent (that Intent becomes “pending”). Effectively,
this let’s us send Intents in response to some other kind of event.
Let’s go ahead and set one up:
5 https://developer.android.com/reference/android/telephony/SmsManager.html
8.7. SHAREACTIONPROVIDER 95
8.7 ShareActionProvider
But wait there’s more we can do with Intents One of the other things we can add
to menus are Action Views that are expandable widgets in the action bar (e.g.,
search example). Or, to play around with Intents more, we can add an Action
Provider (like ShareActionProvider), which gives us a bunch of interaction
built into the menu! This is the “quick share with these social media sites”
button that we see commonly.
• We’d want to look at class documentation for how to set this up (it’s much
clearer than the training docs).
96 CHAPTER 8. INTENTS
mShareActionProvider.setShareIntent(intent);
• We get access to the item using findItem(), and then cast it to a Share-
ActonProvider (make sure you’re using the support version!)
• We can then specify an implicit Intent that we want that “Share Button”
to be able to perform. This would commonly use the ACTION_SEND action
(like for sharing a picture or text), but we’ll use the DIAL action because
we have a couple of dialers but don’t actually have many SEND responders
on the emulator.
The Menu item will then list a dropdown with all of the different Activities that
resolve to handling that implicit intent!
Chapter 9
UI Components II
9.1 Notifications
We have previously covered how to let the user know what’s going on by pop-
ping up a toast or even an AlertDialog, but often we want to notify the user of
something outside of the normal Activity UI (e.g., when the app isn’t running,
or without getting in the way of other interactions). To do this, we can use No-
tifications. These are specialized views that show up in the notification area
(the icons at the top of the operating system display) and in the system’s no-
tification drawer, which the user can get to at any point—even when outside
the app—by swiping down on the screen.
Android’s documentation for UI components is overall quite thorough and us-
able (after all, Google wants to make sure that developers can build effective
apps, thereby making the platform worthwhile). And because there are so
many different UI elements and they change all the time, in order to do real-
world Android development you need to be able to read, synthesize, and apply
this documentation. As such, this lecture will demonstrate how to utilize that
documentation and apply it to create notifications. We will utilize this docu-
mentation in order to add a feature that when we click on the appropriate menu
1 https://developer.android.com/guide/topics/ui/notifiers/notifications.html
97
98 CHAPTER 9. UI COMPONENTS II
button, a notification will appear that reports how many times we’ve selected
that option.
• To follow along this, open up the Notifications documentation.
• Looking at the documentation we see an overview to start. There is also
a link to the Notification Design Guide, which is a good place to go to
figure out how to design effective notifications.
• There is a lot of text about how to make a Notification… I personally prefer
to work off of sample code, modifying it until I have something that does
what I want, so I’m going to scroll down slowly until I find an example I
can copy/paste in, or at least reference. Then we can scroll back up later
to get more detail about how that code works.
• Eventually you’ll find a subsection “Creating a Simple Notification”, which
souds like a great place to start!
The first part of this Notification is using NotificationCompat.Builder (use
the v4 support version). We have previously seen this kind of Builder class
with AlertBuilder, and the same concept applies here: it is a class used to
construct the Notification for us. We call setters to specify the properteis of the
Notification
• I don’t have a drawable resource to use for an icon, which makes me want
to not include the icon specification. However, scrolling back up will reveal
that a notification icon is required, so we will need to make one.
We can produce an new Image Asset for the notificaton icon (File > New
> Image Asset), just as we did previously with launcher icons. Specify
the “type” as Notification, give it an appropriate name, and pick a
clipart of your choosing.
The next line makes an Intent. We’ve done that too… but why create an Intent
for a Notificatoin? If we scroll up and look where Intent is referenced, we
can find out about Notification Actions, which specify what happens when the
user clicks on the Notification. Usually this opens the relevant application, and
since Intents are messages to open Activities, it makes sense that clicking a
Notification would send an Intent.
• Notice that the Intent will actually be wrapped in a PendingIntent.
Thus we will give the Notification a PendingIntent, which contains an
“RSVP” (to open the Activity) that it can send to the system when some-
one clicks on it. (the Intent is “pending” delivery/activation by another
service).
In particular, we use a PendingIntent in order to make sure that the Ac-
tivity who will be executing it (the “notification service” component) will
have permission to send the contained Intent. The Intent the notification
service sends is to wake up our Activity, run with our permissions. It is
as if we had sent the Intent ourselves!
9.1. NOTIFICATIONS 99
9.2 Settings
The second topic of this lecture is to support letting the user decide whether
clicking the button should create notifications or not. For example, maybe
sometimes the user just want to see Toasts! The cleanest way to support this
kind of user preference is to create some Settings using Preferences.
9.2.1 SharedPreferences
Shared Preferences2 are another way that we can persist data in application
(besides putting it into a database via a ContentProvider, or using the file system
as described in the next lecture). SharedPreferences store key-value pairs of
primitives (Strings, ints, etc), similar to what we’ve been putting in Bundles.
This data will be stored across application sessions: if I save some data to the
Preferences and close the app, it will be there when I come back.
• Preferences are stored in an XML File in the file system. Basically we
save in lists of key-value pairs as a basic XML tree in a plain-text file. Note
that this is not a resource, rather a file that happens to be structured as
XML.
• This is not great for intricate or extensive structured data (since it only
stores key-value pairs, and only primitives at that). Use other options for
more complex data persistence.
2 https://developer.android.com/guide/topics/data/data-storage.html#pref
9.2. SETTINGS 101
Even though they are called “Preferences”, they not just for “user preferences”.
We can persist any small bits of primitive data in a Preferences file.
We can get access to this SharedPreferences file using the .getSharedPref-
erences(String, int) method. The first parameter String is the name of
the Preference File we want to access (we can have multiple XML files; just use
getPreferences() to use a single default). The second parameter int is a flag
about whether other apps should have access to that file. MODE_PRIVATE (0) is
the default, MODE_WORLD_READABLE and MODE_WORLD_WRITEABLE are the other
options.
We can edit this XML file by calling .edit() on the SharedPreferences object
to get a SharedPreferences.Editor, which is a Bundle-esque object we can
put values into.
• We need to call .commit() on the editor to save our changes to the file
system!
Finally, we can just call get() methods on the SharedPreferences object in
order to fetch data out of it! The second parameter of these methods is a default
value for if a preference doesn’t exist yet, making it easy to avoid null errors.
For practice, try saving the notification count in the Activity’s onStop()
function, and retrieving it in onCreate(). This will allow you to persist the
count even when the Activity is destroyed.
• These elements should include the following XML attributes (among oth-
ers):
– android:key the key to store the preference in the SharedPreferences
file
– android:title a user-visible name
– android:defaultvalue a default value for the preference (use true
or false for checkboxes).
– More options cam be found in the the Preference documentation.
• We can further divide these Preferences to organize them: we can place
them inside a PreferenceCategory tag (with its own title and key) in
order to group them together.
• Finally we can specify that our Preferences have multiple screens by nest-
ing PreferenceScreen elements. This produces “subscreens” (like sub-
menus): when we click on the item it will take us to the next screen.
Note that a cleaner (but more labor-intensive) way to do this if you have lots
of settings is to use preference-headers which allows for better multi-pane
layouts… but since we’re not making any apps with that many settings this
process is left as exercise for the reader.
Once we have the Preferences all defined in XML: we just need to show them in
our application! To do this, we’re going to use the PreferenceFragment class
(a specialized Fragment for showing lists of Preference objects).
• We don’t need to specify an onCreateView() method, instead we’re just
going to load that Preference resource in the onCreate() method using
addPreferencesFromResource(R.xml.preferences). This will cause
the PreferenceFragment to create the appropriate layout!
We’ll put this Fragment inside a plain Activity, which just loads that Fragment
via a FragmentTransaction:
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
• The Activity doesn’t even need to load a layout: just specify a transaction!
But if we want to include other stuff (e.g., an ActionBar), we’d need to
structure the Activity and its layout in more detail.
• Note that android.R.id.content refers to the “root element” of the cur-
rent View–basically what setContentView() is normally inflating into.
• There is a PreferenceActivity class as well, but the official recommen-
dation is do not use it. Many of its methods are deprecated, and since
we’re using Fragments via the support library, we should stick with the
Fragment process.
9.2. SETTINGS 103
Finally, how do we interact with these settings? Here’s the trick: a preferences
XML resource is automatically associated with a SharedPreferences file.
And in fact, every time we adjust a setting in the PreferenceFragment, the
values in that file are edited as well! We never need to write to the file, just
read from it (similar to any other SharedPreferences file).
The preference XML corresponds to the “default” SharedPreferences file,
which we’ll access via:
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
• And then we have this object we can fetch data from with getString(),
getBoolean(), etc.
This will allow us to check the preferences before we show a notification!
That’s the basics of using Settings. For more details see the documentation, as
well as the design guide for best practices on how to organize your Settings.
104 CHAPTER 9. UI COMPONENTS II
Chapter 10
This lecture discusses how to working with files in Android. Using the file system
allows us to have persistant data storage in a more expansive and flexible manner
than using the SharedPreferences discussed in the previous lecture (and as a
supplement to ContentProvider databases).
This lecture references code found at https://github.com/info448-s17/lecture10-
files-permissions.
In order to demonstrate all of the features discussed in this lecture, your device
or emulator will need to be running API 23 (6.0 Marshmallow) or later.
105
106 CHAPTER 10. FILES AND PERMISSIONS
When do we use each? Basically, you shuold use Internal storage for “private”
files that you don’t want to be available outside of the app, and use External
storage otherwise.
• Note however that there are publicly-hidden External files—the big dis-
tinction between the storage locations is less visibility and more about
access.
In addition, both of these storage systems also have a “cache” location (i.e.,
an Internal Cache and an External Cache). A cache is “(secret) storage for the
future”, but in computing tends to refer to “temporary storage”. The Caches are
different from other file storage, in that Android has the ability to automatically
delete cached files if storage space is getting low… However, you can’t rely on
the operating system to do that on its own in an efficient way, so you should
still delete your own Cache files when you’re done with them! In short, use
the Caches for temporary files, and try to keep them small (less than 1MB
recommended).
• The user can easily clear an application’s cache as well.
In code, using all of these storage locations involve working with the File class.
This class represents a “file” (or a “directory”) object, and is the same class you
may be familiar with from Java SE.
• We can instantiate a File by passing it a directory (which is another
File) and a filename (a String). Instantiating the file will create the file
on disk (but empty, size 0) if it doesn’t already exist.
• We can test if a File is a folder with the .isDirectory() method, and
create new directories by taking a File and calling .mkdir() on it. We
can get a list of Files inside the directory with the listFiles() method.
See more API documentation for more details and options.
The difference between saving files to Internal and External storage, in practice,
simply involves which directory you put the file in! This lecture will focus on
working with External storage, since that code ends up being a kind of “super-
set” of implementation details needed for the file system in general. We will
indicate what changes need to be made for interacting with Internal storage.
• This lecture will walk through implementing an application that will save
whatever the user types into an text field to a file.
Because a device’s External storage may be on removable media, in order to
interact with it in any way we first need to check whether it is available (e.g.,
that the SD card is mounted). This can be done with the following check
(written as a helper method so it can be reused):
public static boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
10.2. PERMISSIONS 107
return false;
}
10.2 Permissions
Directly accessing the file system of any computer can be a significant security
risk, so there are substantial protections in place to make sure that a malicious
app doesn’t run roughshod over a user’s data. So in order to work with the
file system, we first need to discuss how Android handles permissions in more
detail.
One of the most import aspect of the Android operating system’s design is the
idea of sandboxing: each application gets its own “sandbox” to play in (where
all its toys are kept), but isn’t able to go outside the box and play with someone
else’s toys. The “toys” (components) parts that are outside of the sandbox are
things that would be impactful to the user, such as network or file access. Apps
are not 100% locked into their sandbox, but we need to do extra work to step
outside.
• Sandboxing also occurs at a package level, where packages (applications)
are isolated from packages from other developers; you can use certificate
signing (which occurs as part of our build process automatically) to mark
two packages as from the same developer if we want them to interact.
• Additionally, Android’s underlying OS is Linux-based, so it actually uses
Linux’s permission system under the hood (with user and group ids that
grant access to particular files or processes).
In order for an app to go outside of its sandbox (and use different components),
it needs to request permission to leave. We ask for this permission (“Mother
may I?”) by declaring out-of-sandbox usages explicitly in the Manifest, as
we’ve done before with getting permission to access the Internet or send SMS
messages.
Android permissions we can ask for are divided into two categories: normal and
dangerous:
• Normal permissions are those that may impact the user (so require
permission), but don’t pose any serious risk. They are granted by the user
at install time; if the user chooses to install the app, permission is granted
to that app. See this list for examples of normal permissions. INTERNET
is a normal permission.
• Dangerous permissions, on the other hand, have the risk of violat-
ing a user’s privacy, or otherwise messing with the user’s device or other
apps. These permissions also need to be granted at install time. But IN
ADDITION , starting from Android 6.0 Marshmallow (API 23), users
108 CHAPTER 10. FILES AND PERMISSIONS
If permission has been granted, great! We can go about our business (e.g.,
saving a file to external storage). But if permission has NOT been explicitly
granted (at runtime), then we have to ask for it. We do this by calling:
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.PERMISSI
We check which request we’re hearing the results for, what permissions were
granted (if any—the user can piece-wise grant permissions), and then we can
react if everything is good… like by finally saving our file!
• Note that if the user deny us permission once, we might want to try
and explain why we’re asking permission (see best practices) and ask again.
Google offers a utility method (ActivityCompat#shouldShowRequestPermissionRationale())
which we can use to show a rationale dialog if they’ve denied us once.
And if that’s true, we might show a Dialog or something to explain
ourselves–and if they OK that dialog, then we can ask again.
//reading
try {
File dir = getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
File file = new File(dir, FILE_NAME);
BufferedReader reader = new BufferedReader(new FileReader(file));
StringBuilder text = new StringBuilder();
textDisplay.setText(text.toString());
reader.close();
} catch (IOException ioe) {
Log.d(TAG, Log.getStackTraceString(ioe));
}
This will allow us to have our “save” button write the message to the file, and
have our “read” button load the message from the file (and display it on the
screen)!
Internal storage works pretty much the same way as External storage. Remem-
ber that Internal storage is always private to the app. We also don’t need
permission to access Internal storage!
For Internal storage, we can use the getFilesDir() method to access to the
files directory (just like we did with External storage). This method normally
returns the folder at /data/data/package.name/files.
Alternatively, we can use Context#openFileOutput() (or Context#openFileInput())
and pass it the name of the file to open. This gives us back the Stream object
for that file in the Internal storage file directory, without us needing to do any
extra work (cutting out the middle-man!)
• These methods take a second parameter: MODE_PRIVATE will create the
file (or replace a file of the same name). Other modes available are:
MODE_APPEND (which adds to the end of the file if it exists instead of
erasing). MODE_WORLD_READABLE, and MODE_WORLD_WRITEABLE are dep-
recated.
112 CHAPTER 10. FILES AND PERMISSIONS
As another example of how we might use the storage system, consider the “take
a selfie” system from lecture 8. The code for taking a piecture can be found in
a separate PhotoActivity (which is accessible via the options menu).
To review: we sent an Intent with the MediaStore.ACTION_IMAGE_CAPTURE
action, and the result of that Intent included an Extra that was a BitMap of a
low-quality thumbnail for the image. But if we want to save a higher resolution
version of that picture, we’ll need to store that image in the file system!
To do this, we’re actually going to modify the Intent we send so it includes
an additional Extra: a file in which the picture data can be saved. Effectively,
we’ll have our Activity allocate some memory for the picture, and then tell the
Camera where it can put the picture data that it captures. (Intent envelops are
too small to carry entire photos around!)
Before we send the Intent, we’re going to go ahead and create an (empty) file:
File file = null;
try {
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); //
Log.d(TAG, Log.getStackTraceString(ioe));
}
We will then specify an additional Extra to give that file’s location to the camera:
if we use MediaStore.EXTRA_OUTPUT as our Extra’s key, the camera will know
what to do with that! However, the extra won’t actually be the File but a Uri
(recall: the “url” or location of a file). We’re not sending the file itself, but the
location of that file (because it’s smaller data to fit in the Intent envelope).
• We can get this Uri with the Uri.fromFile(File) method:
//save as instance variable to access later when picture comes back
pictureFileUri = Uri.fromFile(file);
• Then when we get the picture result back from the Camera (in our onAc-
tivityResult callback), we can access that file at the saved Uri and use
it to display the image! The ImageView.setImageUri() is a fast way of
showing an image file.
Note that when working with images, we can very quickly run out of memory
(because images can be huge). So we’ll often want to “scale down” the images as
we load them into memory. Additionally, image processing can take a while so
we’d like to do it off the main thread (e.g., in an AsyncTask). This can become
complicated; the recommended solution is to use a third-party library such as
Glide, Picasso, or Fresco.
Once we have a file storing the image, we can also save that image with other
apps!
As always, in order to interact with other apps, we use an Intent. We can craft
an implicit intent for ACTION_SEND, sending a message to any apps that are able
to send (share) pictures. We’ll set the data type as image/* to mark this as an
image. We will also attach the file as an extra (specifically an EXTRA_STREAM).
Again note that we don’t actually put the file in the extra, but rather tha Uri
for the file!
Since multiple activities may support this action, we can wrap the intent in a
“chooser” to force the user to pick which Activity to use:
Intent chooser = Intent.createChooser(shareIntent, "Share Picture");
//check that there is at least one option
if (shareIntent.resolveActivity(getPackageManager()) != null) {
startActivity(chooser);
}
114 CHAPTER 10. FILES AND PERMISSIONS
There is one complication though: because we’re saving files in External storage,
the app who is executing the ACTION_SEND will need to have permission to read
the file (e.g., to access External storage). The Messenger app on the emulator
appears to lack this permission by default, though we need to take a slightly
different approach:
Rather than putting the file:// Uri in the Intent’s extra, we’ll need to create
a content:// Uri for a ContentProvider who is able to provide files to anyone
who requests them regardless of permissions (the provider grants permission
to access its content). Luckily, each image stored in the public directories is
automatically tracked by a ContentProvider known as the MediaStore. It easy
to fetch a content:// Uri for a particular image file from this provider:
MediaScannerConnection.scanFile(this, new String[] {file.toString()}, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
mediaStoreUri = uri; //save the content:// Uri for later
Log.v(TAG, "MediaStore Uri: "+uri);
}
});
This provides a Uri that can be given to the Intent, and that the Messenger app
will be able to access! We can generate this Uri as soon as we have a file for the
image to be saved in.
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="edu.uw.mapdemo.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/fileprovider" />
</provider>
(note that the second parameter is the “authority” you specified in your
<provider> in the Manifest). You can then use this Uri as the EXTRA_STREAM
extra in the Intent that you want to share!
116 CHAPTER 10. FILES AND PERMISSIONS
Chapter 11
Location
This lecture discusses localization: the process for determining location. This is
particularly important for Android, which is primarily intended as an operating
system for mobile devices. What makes phones and tablets special, and different
from desktops, is that they can and do move around. And this mobility makes
means that a device’s position and location can matter significantly for how
they are used; it’s a major part of what separates the functionality of Android
apps from any other computer application. Indeed: localization gives apps the
ability to create new kinds of user experiences, and to adjust their functionality
to fit the user’s context, supporting context-aware applications.
I highly recommend you read Mark Weiser’s original 1991 Scientific Amer-
ican article. It’s a neat vision and is foundational for a lot of modern
research into mobile systems. It marks the “official” start of the field of
Ubicomp.
In short: localization can let us know about the user’s situation (though mobile
phone location is not necessarily a proxy for user location).
117
118 CHAPTER 11. LOCATION
11.1.1 GPS
GPS is the most common, and what most people think of when they think of
localization. GPS stands for “Global Position System”—and yes, it can work
anywhere on the globe.
GPS’s functionality depends on satellites: 24 satellites in high orbit (not geo-
synchronous) around the Earth. Satellites are distributed so that 4 to 12 are
visible from any point on Earth at any time, and their locations are known with
high precision. These satellites are each equipped with an atomic, synchronized
clock that “ticks” every nanosecond. At every tick, the satellite broadcasts its
current time and position. You can almost think of them as really loud alarm
clocks.
The thing in your phone (or your car, or your watch) that you call a “GPS”
or a “GPS device” is actually a GPS receiver. It is able to listen for the
messages broadcast by these satellites, and determine its position based on that
information.
First, the receiver calculates the time of arrival (TOA) based on its own clock
and comparing time-codes from the satellites. It then uses the announced time
of transmission (TOT; what the satellite was shouting) to calculate the time
of flight, or how long it took for the satellite’s message to reach the receiver.
Because these messages are sent at (basically) the speed of light, the [time of
flight] is equivalent to the distance from the satellite!
• There is some other synchronization work that is done to make sure clocks
are all the same, but we won’t go into that here.
And once it has distances from the satellites, the receiver can use trilateration
to determine its position based on the satellites it “sees”. (Trilateration is like
Triangulation, but relies on measuring distances rather than measuring angles.
11.1. LOCALIZATION TECHNIQUES 119
Basically, you construct three spheres of given radii, and then look to see where
they intersect).
GPS precision is generally about 5 meters (15 feet); however, by repeatedly cal-
culating the receiver’s position (since the satellites tick every nanosecond), we
can use differential positioning to extrapolate position with even higher preci-
sion, increasing precision to less than 1 meter! This is in part how Google can
determine where you’re walking.
While GPS is ubiquitous, scalable, and appropriate accurate, it does have some
limitations. The biggest problem with GPS is that you need to be able to
see the satellites! This means that GPS frequently doesn’t work indoors, as
many building walls block the signals. Similarly, in highly urban areas (think
downtown Seattle), the buildings can bounce the signal around and throw off
the TOF calculations, making it harder to pinpoint position accurately.
• Additionally, receivers requires a lot of energy to constantly listen for the
satellite messages. This means that utilizing the GPS can lead to a big
hit on device battery life—which is of particular importance for mobile
devices!
But your phone can also give you a rough estimate of your location even without
GPS. It does this through a couple of techniques, such as relying on the cell
phone towers that provide the phone network service. This is also known as
GSM localization (Global System for Mobile Communications; the standard
for cell phone communication used by many service providers). The location of
these towers are known, so we can determine location based off them in a couple
of ways:
• If you’re connected to a tower, you must be within range of it. So that
gives you some measure of localization right off the bat. This would not
be a very accurate measure though (you might be anywhere within that
range).
• If you can see multiple towers (which important for “handoff” purposes,
so your call doesn’t drop as you move), you can trilaterate the position
between them (e.g., finding the possible overlapping area and picking a
location in the middle of that). This can give accuracy within 50m in
urban areas, with more towers producing better precision.
But wait there’s more! What other kinds of communication antennas do you
have in your phone? WiFi! As WiFi has became more popular, efforts have
120 CHAPTER 11. LOCATION
been made to identify the location of WiFi hotspots so that they too can be
used for trilateration and localization.
This is often done through crowdsourced databases, with information gathered
via war driving. War driving involves driving around with a GPS receiver and a
laptop, and simply recording what WiFi routers you see at what locations. This
then all gets compiled into a database that can be queried—given that you see
these routers, where must you be?
• Google got in hot water for doing this as it was driving around to capture
Street-View images.
WiFi localization can then be combined with Cell Tower localization to produce
a pretty good estimate of your location, even without GPS.
And in fact, Google provides the ability to automatically use all of these different
techniques, abstracted into a single method call!
I want to flag that just like the old Active Badge systems, all of these local-
izations systems rely on some kind of existing infrastructure: GPS requires
satellites; GSM requires cell towers, and WiFi needs the database of routers.
All these systems require and react to the world around them, making local-
ization influenced by the actual location as well, as well as both social and
computational systems!
• Longitude (“lng”) is the angle between the prime meridian plane and a
line that passes through a point and the center of the Earth—the angle
you have to go across the earth’s surface from the meridian. Effectively,
it’s a measure of “east/west”. Latitude is measured in degrees east, so
going east of the meridian. That mean that the western hemisphere has
“negative longitude” (though this can be expressed as positive “degrees
west”).
As an example: UW’s GPS Coordinates1 are N/W, so this would be expressed
as N (positive) and E (negative).
The distance between degrees and miles depends on where you you are (partic-
ularly for longitude—the curvature of the earth means that each degree has less
space between it as you get closer to their “joining” at the poles). However, for
a very rough sense of scale, in the American Northwest, .01 degrees corresponds
with a distance of about a mile (again: this is not an accurate convertion, and
is intended only for a sense of the “units”).
The remainder of the lecture will discuss how to implement an app that is able
to access the device’s location. This location will simply be displayed for now;
connecting the location to a visual display (e.g., a map) is left as an exercise to
the reader.
This lecture references code found at https://github.com/info448-s17/lecture11-
location.
In order to effectively access location, we first need to make sure we include the
Google Play Services. These are a special set of libraries (similar to the support
libraries) that provide additional functionality to Android. That functionality
will include the location and mapping tools we’re interested in. (Much of this
functionality was originally built into core Android, but Google has since been
moving it into a separate app that can be more easily distributed and updated!)
There are a few steps to including the Play Services library:
1. Make sure you’ve downloaded the proper library. Go to Tools > An-
droid > SDK Manager to open up the manager for the various versions
of Android installed. Under SDK Tools, select Google Play Service
and Google Play Repository to download those.
1 https://www.google.com/search?q=uw+gps+coordinates
122 CHAPTER 11. LOCATION
2. Make sure the device supports these services (e.g., that it’s a Google device
and not an Amazon device). For the emulator, go to the AVD Manager,
and confirm the target platform includes the Google APIs.
3. Modify your build.gradle file so that you can get access to the Location
classes. In the module-level build.gradle file, under dependencies
add
compile 'com.google.android.gms:play-services-location:10.2.4'
This will load in the location services (but not the other play services,
which take up extra space and may require additinal API keys). Note
that you can specify a different version of the services, as long as it is
greater than 8.3.0.
Additionally, you’ll need to request permission to access the device’s location.
There are two permission levels we can ask for: ACCESS_COARSE_LOCATION
(for GSM/WiFi level precision), and ACCESS_FINE_LOCATION (for GPS level
precision). We’ll use the later because we want GPS-level precision.
This is a dangerous permission, so in Marshmallow we need to make sure to
ask for permission at run-time! See the lecture on permissions for details.
We’re going to use Google Play Services to access the device’s location. The
Google APIs provide a nice set of methods for accessing location (without us
needing to specify the source of that localization, GPS or GSM), and is the
recommended API to use.
• There is a built-in android.location API (e.g., for non-Google based
Android devices), but it’s not recommended practice and is harder to use.
The first thing we need to do is get access to the API; we do this with a
GoogleApiClient object. We construct this object in the onCreate() callback,
using a GoogleApiClient.Builder:
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
This builder requires us to specify what are called the Connection Callbacks:
callbacks that will occur when we connect to the Google Play Services (a
Service or separate application managing all of Google’s API work). We
do this by implementing the GoogleApiClient.ConnectionCallbacks and
GoogleApiClient.OnConnectionFailedListener interfaces. Each require
methods that we must fill in; in particular, the onConnected() method is
where we can “start” our API usage (like asking for location!)
11.2. ANDROID LOCATION 123
• onSuspended and onFailed are for when the connection is stopped (sim-
ilar to onStop()) or if we fail to connect. See Accessing Google APIs for
details.
Note we also specify that we want to access the LocationServices API in the
builder.
Finally, we need to actually connect to the client. We do this in the Activity’s
onStart() method (and disconnect in onStop()):
protected void onStart() {
mGoogleApiClient.connect();
super.onStart();
}
This of course, will lead to our onConnected() callback being executed once
the connection to the service is established.
Once we have the the client connected to the service, we can start getting the
location!
To access the location, we’re going to use a class called the FusedLocationApi2 .
This is a “unified” interface for accessing location. It fuses together all of the
different ways of getting location, providing which-ever one best suits our spec-
ified needs. You can think of it as a “wrapper” around more detailed location
services.
• It will let us specify at a high level whether we want to trade accuracy
for power consumption, rather than us needing to be explicit about that.
And it will make decisions about what how best to fetch location given
our stated needs and other contextual information.
We’re going to specify this “high level” requirement using a LocationRequest3
object, which represents the details of our request (e.g., how we want to have
our phone search for it’s location).
LocationRequest request = new LocationRequest();
request.setInterval(10000);
request.setFastestInterval(5000);
request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
• We create the object, then specify the “interval” that we want to check for
updates. We can also specify the “fastest” interval, which is the maximum
2 https://developers.google.com/android/reference/com/google/android/gms/location/
FusedLocationProviderApi
3 https://developers.google.com/android/reference/com/google/android/gms/location/
LocationRequest
124 CHAPTER 11. LOCATION
rate we want updates (assuming they are available). It’s a bit like a
minimum and maximum. 5 to 10 seconds is good for real-time navigation.
• We also specify the priority, which is the indicator to the FusedLoca-
tionApi about what kind of precision we want. HIGH_ACCURACY basically
means GPS (trade power for accuracy!)
Before actually sending the request, check for run-time permissions! This will
introduce another layout of callbacks: first you wait for the GoogleApiClient to
connect in one callback, then you wait for permission to be granted in another!
Once we have this request in place, we can send it off through the
FusedLocationApi:
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, request, t
It is possible to test this out (even when indoors) by using the emulator. Al-
though the emulator doesn’t actually have a GPS receiver, it is possible to give
it a “fake” location using the emulator’s options sidebar (where we previously
sent SMS messages from). This allos us to “send” the phone a location, almost
as if we as humans were the GPS receiver!
• You can test by giving the emulator UW’s coordinates (47.6550 N, -
122.3080 E), and you can watch it update!
– Note that you may need to start up the Maps application to make
sure the devices’s location settings are enabled and correct. See here
for how we can prompt for that ourselves (it’s a lot of repetitous code,
so leaving it as exercise to the reader).
• The FusedLocationApi class also has a setMockLocation() method,
which can allow you to programmatically define locations (e.g., you can
11.2. ANDROID LOCATION 125
make a button that changes your location). This can be useful for testing
and debugging.
To review the process:
• We start by creating and connecting to a GoogleApiClient, which is
going to let us talk to the Play Services application running in the back-
ground of our phone.
• This may not be able to connect (or may take a moment), so we have a
asynchronous callback for when it does.
• Once it connects (in that callback), we check to make sure we have permis-
sion to get location, asking for it if we don’t. This requires the user to make
a decision, which may take some time, so we have another asynchronous
callback for when we finally get permision.
• Once we have permission, we start up a repeated request for location
updates. These updates may take some time to arrive, so we have yet
another asynchronous callback for when they do!
• And when we get a location update, we finally update our View.
That’s pretty much what is involved in working with location. Lots of pieces
(because callbacks all over the place!), but this does the work of tracking loca-
tion.
126 CHAPTER 11. LOCATION
Chapter 12
Sensors
This lecture discusses how to access and utilize hardware sensors built into
Android devices. These sensors can be used to detect changes to the device
(such as it’s motion via the accelerometer) or its surrounding environment
(such as the weather via a thermometer or barameter). Additionally, the
system is structured so you can develop and connect your own sensor hardware if
needed—such as connecting to a medical device or some other kind of tricorder.
This lecture references code found at https://github.com/info448-s17/lecture12-
sensors.
127
128 CHAPTER 12. SENSORS
“low-powered” sensor—its ubiquity and low cost of usage makes it ideal for
detecting motions!
The accelerometer is an example of a motion sensor, which are used to detect
how the device moves in space: tilting, shaking, rotating, or swinging. Motion
sensors are related to but different from position sensors which determine where
the device is in space: for example, the device’s current rotation, facing, or
proximity to another object (e.g., someone’s face). Position sensors different
from location sensors (like GPS), in that they measure position relative to the
device rather than relative to the world.
• It is also possible to detect motion using a gravity sensor (which measures
the direction of gravity relative to the device), a gyroscope (measures the
rate of spin of the device), or a number of other sensors. However, the
accelerometer is the most common and can be used in combination with
other sensors as needed.
We do not need any special permissions to access the accelerometer. But because
our app will rely on a certain piece of hardware that—while common—may not
be present on very device, we will want to make sure that anyone installing
our app (e.g., from the Play Store) has that hardware. We can specify this
requirement in the Manifest with a <uses-feature> element:
<uses-feature android:name="android.hardware.sensor.accelerometer"
android:required="true" />
• This declaration doesn’t actually prevent the user from installing the app,
though it will cause the Play Store to list it as “incompatible”. Effectively,
it’s just an extra note.
• Just like how we accessed the Notification Service, we ask the An-
droid System to “get” us a reference to the Sensor Service, which is a
SensorManager.
The SensorManager class provides a number of useful methods. For example,
the SensorManager#getSensorList(type) method will return a list of
2 https://developer.android.com/reference/android/hardware/SensorManager.html
12.1. MOTION SENSORS 129
sensors available to the device (the argument is the “type” of sensor to list;
use Sensor.TYPE_ALL to see all sensors). The sensors are returned as a
List<Sensor>—each Sensor3 object represents a particular sensor. The
Sensor class includes information like the sensor type (which is not represented
via subclassing, because otherwise we couldn’t easily add our own types!
Composition over inheritance).
Devices may have multiple sensors of the same type; in order to access the
“main” sensor, we use the SensorManager#getDefaultSensor(type) method.
This method will return null if there is no sensor of that type (allowing us to
check if the sensor even exists), or the “default” Sensor object of that type (as
determined by the OS and manufacturer).
• If no valid sensor is available, we can have the Activity close on its own
by calling finish() on it.
In order to get readings from the sensor, we need to register a listener for the
event that occurs when a sensor sample is available. We can do this using the
SensorManager:
mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
on the sensor type that produced the event (which can be determined via the
event.sensor.getType() method).
When working with the accelerometer, each element in the float[] is the
acceleration force (in m/s2 ) along each of the three Cartesian axes (x, y, and
z in order):
If you Log out these coordinates while the phone sitting flat on a table (not
moving), you will notice that the numbers are not all 0.0. This is because
gravity is always exerting an accelerating force, even when the device is at rest!
Thus in order to determine the actual acceleration, we would need to “factor
out” the force due to gravity. This requires a little bit of linear algebra; the
Android documentation has an example of the math (and an additional version
can be found in the sample app).
5 https://developer.android.com/images/axis_device.png
12.2. ROTATION 131
12.2 Rotation
Acceleration is all good and well, but it only detects motion when the phone is
moving. If we tilt the phone to one side, it will measure that movement… but
then the acceleration goes back to 0 since the phone has stopped moving. What
if we want to detect something like the tilt of the device?
The gravity sensor (TYPE_GRAVITY) can give this information indirectly, but it is
a bit hard to parse out. So a better option is to use a Rotation Vector Sensor.
This is another composite (virtual) sensor that is used to determine the current
rotation (angle) of the device by combining readings from the accelerometer,
magnetometer, and gyroscope.
After registering a listener for this sensor, we can see that the onSensorChanged(event)
callback once again provides three float values from the sensed event. These
values represent the phone’s rotation in quaternions. This is a lovely but
complex coordinate system (literally complex: it uses imaginary numbers to
measure angles). Instead, we’d like to convert this into rotation values that we
understand, such as the degrees of device roll, pitch, and yaw.
• Our approach will be somewhat round-about, but it is useful for under-
standing how the device measures and understands its motion.
In computer systems, rotations are almost always stored as matrices (a math-
ematical structure that looks like a table of numbers). Matrices can be used to
multiply vectors to produce a new, transformed vector—the matrix represents a
132 CHAPTER 12. SENSORS
• You can actually use matrices to represent any affine transformation (in-
cluding movement, skewing, scaling, etc)… and these transformations can
be specified for things like animation. 30% of doing 3D Computer Graph-
ics is simply understanding and working with these transformations.
Luckily, we don’t actually need to know any of the math for deriving rotation
matrices, as Android has a built-in method that will automatically produce
a rotation matrix from a the rotation quaternion produces by the rotation vec-
tor sensor: SensorManager.getRotationMatrixFromVector(targetMatrix,
vector)
• This method takes in a float[16], representing a 4x4 matrix (one dimen-
sions for each axis x, y, and z, plus one dimension to represent the “origin”
in the coordinate system. These are known as homogenous coordinates.
This array will be filled with the resulting values of the rotation matrix.
The method doesn’t produce a new array because allocating memory is
time-intensive—so you need to provide your own (ideally reused) array.
A 4x4 rotation matrix may not seem like much of an improvement towards
getting human-readable orientation angles. So as a second step we can use the
SensorManager.getOrientation(matrix, targetArray) method to convert
that that rotation matrix into a set of radian values that are the angles the
phone is rotated around each axis—thereby telling us the orientation. Note this
method also takes a (reusable) float[3] as a parameter to contain the resulting
angles.
• The resulting angles can be converted to degrees and outputted using some
12.2. ROTATION 133
The rotation vector sensor works well enough, but another potential option in
API 18+ is the Game rotation vector sensor. This compound sensor is
almost exactly the same as the ROTATION_VECTOR sensor, but it does not use
the magnetometer so is not influenced by magnetic fields. This means that
rather than having “0” rotation be based on compass directions North and
East, “0” rotation can be based on some other starting angle (determined by
the gyroscope). This can be useful in certain situations where magnetic fields
may introduce errors, but can also involve gyroscope-based sampling drift over
time.
• We can easily swap this in, without changing most of our code. We can
use even check the API version dynamically in Java using:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
//... API 18+
}
This strategy is useful for any API dependent options, including external
storage access and SMS support!
12.2.1 Coordinates
transformed into which other axes (e.g., which axes will become the new x
and y), and then pass in a rotation matrix to adjust. You can then fetch
the orientation from this rotation matrix as before. You can determine
the the device’s current orientation with Display#getRotation()
method:
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).get
display.getRotation();
Services
135
136 CHAPTER 13. SERVICES
13.1 IntentServices
To create a Service, we’re going to subclass Service and override some lifecycle
callbacks, just like we’ve done with Activity, Fragment, BroadcastReceiver,
and most other Android components. In this sense, Services are in fact imple-
mented like Activities that run without a user interface (“in the background”).
Because Services will be performing extra background computation, it’s impor-
tant to also create a separate background thread so that we don’t block the Main
Thread. Since making a Service that does some (specific) task in a background
thread is so common, Android includes a Service subclass we can use to do
exactly this work. This class is called IntentService—a service that responds
to Intents and does some work in response to them.
• An IntentService does similar work to ASyncTask, but with the advantage
that it will keep doing that work even if the Activity is destroyed!
• IntentService will listen to any incoming “requests” (Intents) and
“queue” them up, handling each one at a time. Once the service is out
of tasks to do, the service will shut itself down to save memory (though it
will restart if more Intents are sent to it). Basically, it handles a lot of
the setup and cleanup involved in Service on its own!
• (This lecture will start with IntentService because it’s simpler, and then
move into the more generic, complex version of Services).
We create an IntentService by defining a new class (e.g., CountingService)
that subclasses Intent Service.
• Implement a defaultconstructor that can call super(String
nameForDebugging). This allows the class to be instantiated (by
the Android framework; again, like an Activity).
• Also implement the onHandleIntent(Intent) method. Incoming
Intents will wait their turn in line, and then each is delivered to this
method in turn. Note that all this work (delivery and execution) will
happen in a background thread supplied by IntentService.
For example, we can have the Service (when started) log out a count,
pausing for a few seconds between. This will represent “expensive” logic
to perform, a la accessing a network or a database.
for(int count=0; count<=10; count++){
Log.v(TAG, "Count: "+count);
try {
Thread.sleep(5000); //sleep for 5 seconds
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
13.1. INTENTSERVICES 137
Just like with Activities, we also need to declare the <service> in the Manifest,
as a child of the <application>:
<service android:name=".CountingService" />
• The onBind() and onUnbind() callbacks are used for bound services, and
are discussed below.
– IntentService does have a default onBind() implementation that re-
turns null.
• Finally, Services have an onDestroy() callback, which is again equivalent
to the Activity callback.
– In general, Services have to be manually told to stop. We stop a
Service by using stopService(Intent) to send that Service a “stop”
Intent. The Service can also stop itself by calling stopSelf().
– Important: When told to stop, an IntentService will finish up han-
dling any Intents that are currently “running”, but any other Intents
that are “queued” will be removed. Once there are no more queued
Intents, an IntentService will call stopSelf(), thereby causing the
onDestroy() callback to be executed.
Practice: fill in the callback functions with Log or Toast messages to see how
and when they are executed. For example:
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "Intent received", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}
As a last point to note about the Service lifecycle consider the int that is
returned by onStartCommand(). This int is a flag that indicates how the
Service should behave3 when it is “restarted” after having been destroyed:
3 https://developer.android.com/guide/components/services.html#ExtendingService
140 CHAPTER 13. SERVICES
13.2.1 MediaPlayer
In order to make a music service, we need to briefly explain how to play music
with MediaPlayer. This is the main API for playing sound and video (e.g., if
you want to play .mp3 files).
Android actually has a toal of three (3) audio APIs! The SoundPool API is great
for short sound effects that play simultaneously (though you need to do extra
work to load those clips ahead of time), as with simple games. The AudioTrack
API allows you to play audio at a very low level (e.g., by “pushing” bytes to a
13.2. EXAMPLE: A MUSIC SERVICE 141
stream). This is useful for generated Audio (like MIDI music) or if you’re trying
to do some other kind of low-level stuff.
MediaPlayer is very simple to use, particularly when playing a locally defined
resource (e.g., something in res/raw/). You simply use a factory to make a
new MediaPlayer object, and then call .play() on it:
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.my_sound_file);
mediaPlayer.start(); // no need to call prepare(); create() does that for you
We can also call .pause() to pause the music, .seekTo() to jump to a partic-
ular millisecond, and .stop() to stop the music.
• Note that when we stop(), we also need to release any resources used by
the MediaPlayer (to free up memory):
mediaPlayer.release();
mediaPlayer = null;
In order to make our music service, we are going to subclass the Service class
itself (don’t forget to include the Service in the Manifest) and manually set up
all the pieces. Specifically, we will fill in the lifecycle callbacks:
• onCreate() we can include, though it doesn’t need to do anything.
• onStartCommand() should create and start our MediaPlayer. We can
return START_NOT_STICKY so the music doesn’t start up again randomly
if the system needs to destroy the Service (since it won’t get recreated).
Important: Normally with a Service we would create a background thread
to handle any extraneous work (such as preparing the music to play, as
done with the create() method). However, this may not be necessary
for MediaPlayer when loading resources.
142 CHAPTER 13. SERVICES
• We build and set the icon, title, and text as in the previous lecture.
• We give the Notification a PendingIntent to run when selected. This
PendingIntent can just open up our MainActivity, allowing us to control
the Player. (Alternatively, we could use Notification Actions to control
the music directly).
• We set the Notification to be ongoing, in order to enforce that it cannot
be dismissed by the user.
Importantly, once we are done doing foreground work (e.g., playing music), we
should call stopForeground(true) to get rid of the foreground service. This
is a good thing to do in our stopMusic() helper (called from onDestroy()).
There are a couple of other details that you should handle if you’re making a full-
blown music player app, including: keeping the phone from going to sleep, play-
ing other audio at the same time (e.g., notification sounds; ringtones), switching
to external speakers, etc. See the guide for more details.
• Our local version starts “empty” for now; we’ll add details below.
• We will just return this object from onBind().
Because the Activity is given a copy of this LocalBinder object, that class can
be designed to support the Activity communicating with the Service in a couple
of different ways:
1. The IBinder can provide public methods for the Activity to call. These
methods can then access instance variables of the Service (since the
LocalBinder is a nested class). This causes the IBinder to literally act
as the “public interface” for the Service!
//in LocalBinder
public String getSongName() {
return songName; //access Service instance variable
}
2. The IBinder can provide access to the Service itself (via a getter that re-
turns the Service object). The Activity can then call any public methods
provided by that Service class.
//in LocalBinder
public MusicService getService() {
// Return this instance of this Service so clients can call public methods o
return MusicService.this;
}
3. The IBinder can provide access to some other object “owned” by the Ser-
vice, which the Activity can then call methods on (e.g., the MediaPlayer).
This is somewhat like a compromise between the first two options: you
don’t need to implement a specific public interface on the IBinder, but
you also don’t have to provide full access to your Service object! This is
good for only exposing part of the Service.
In the Activity, we need to do a bit more work in order to interact with
the bound Service. We bind to a Service using bindService(Intent,
ServiceConnection, flag). We can do this whenever we want the Service to
be available (onStart() is a good option, so it is available when the Activity
is active).
• The Intent parameter should be addressed to the Service we want to bind.
• The ServiceConnection parameter is a reference to an object that im-
plements the ServiceConnection interface, providing callbacks that can
13.4. BOUND SERVICES 145
be executed whe the Activity connects to the Service. We can have the
Activity implement the interface, or create a separate anonymous class:
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
Keep in mind that binding a Service DOES NOT call onStartCommand(), but
simply creates the Service and gives us access to it! Bound Services are not by
defaul considered “started”, and are generally kept around by the OS as long as
they are bound. If we do “start” a Bound Service (e.g., with startService()),
we will need to remember to stop it later!
This example is just for local services (that are accessed within the same
process—within the same Application). If we want the Service to be available
to other processes (i.e., other Applications), we need to do more work. Specifi-
cally, we use Handler and Messenger objects to pass messages between these
146 CHAPTER 13. SERVICES
processes (similar to the example we did for passing messages between threads);
for more details, see the example in the guide, as well as the complete sample
classes MessengerService and MessengerServiceActivities.
Chapter 14
Material Design
This lecture discusses Material design: the design language created by Google to
support mobile user interfaces. The design language focuses on mobile designs
(e.g., for Android), but can also be used across platforms (e.g., on the web
through css frameworks).
This lecture references code found at https://github.com/info448-s17/lecture14-
material.
This lecture is under development and will be added later.
14.1 Resources:
Some links and resources as a placeholder
• https://developer.android.com/design/material/index.html
• https://material.io/guidelines/material-design/introduction.html#
introduction-principles
• https://material.io/guidelines/material-design/material-properties.html
• https://material.io/guidelines/material-design/elevation-shadows.html#
elevation-shadows-elevation-android
• http://saulmm.github.io/mastering-coordinator
• https://lab.getbase.com/introduction-to-coordinator-layout-on-android/
147
148 CHAPTER 14. MATERIAL DESIGN
Chapter 15
This lecture discusses some different ways to add “visual motion” (graphical
animation) to Android applications. It covers 2D drawing with custom Views,
Property Animations (also used in Material effects), and how to handle touch-
based gestures.
This lecture references code found at https://github.com/info448-s17/lecture15-
animation.
In order to draw pictures, we need to have a View to draw on (which will provide
the Canvas context). The easiest way to get this View is to create it ourselves:
define a custom View which we can specify as having a “drawn” appearance.
We can make our own special View by subclassing it (remember: Buttons and
1 https://developer.android.com/reference/android/graphics/Canvas.html
149
150 CHAPTER 15. ANIMATION AND GRAPHICS
EditTexts are just subclasses of View!), and then filling in a callback function
(onDraw()) that specifies how that View is rendered on the screen.
The word render in this case means to “bring into being”, meaning to generate
a graphical image and putting it on the screen.
Customizing a View isn’t too hard, but to save time a complete example is
provided in the lecture code in the form of the DrawingView class. Notes about
this classes implementation are below:
• The class extends View to subclass the base View class.
• View has a number of different of constructors. We override them all, since
each one is used by a different piece of the Android system (and thus we
need to provide custom implementations for each). However, we’ll have
each call the “last” one in order to actually do any setup.
– In the constructor we set up a Paint, which represents how we
want to draw: color, stroke width, font-size, anti-aliasing options,
etc. We’ll mostly use Paint objects for color.
• We override the onSizeChanged() callback, will get executed when the
View changes size. This occurs on inflation (which happens as part of
an Activity’s onCreate, meaning the callback will be called on rotation).
This callback can act a little bit like a Fragment’s onCreateView(), in
that we can do work that is based on the created View at this point.
• We should also override the onMeasure() callback, as recommend by the
Android guides. This callback is used to specify how the View should be
sized in response to its width or height being set as wrap_content. This
is particularly important for making things like custom Buttons. However,
our example will skip this for time and space, since the DrawingView is
intended to always take up the entire screen.
• Finally, we override onDraw(), which is where the magic happens.
This method gets called whenever the View needs to be displayed (like
paintComponent() in the Swing framework). This callback is passed a
Canvas object as a parameter, providing the context we can draw on!
– Like all other lifecycle callbacks: we never call onDraw()!! The
Android UI system calls it for us!
The provided Canvas can be drawn on in a couple of ways:
• We can call methods like drawColor() (to fill the background),
drawCircle(), or drawText() to draw specific shapes or entities on it.
These methods are similar in usage to the HTML5 Canvas.
• We can also draw a Bitmap, which represents a graphics raster (e.g., a 2D
array of pixels). If we have a Bitmap, we can set the colors of individual
pixels (using setPixel(x,y,color)), and then draw the Bitmap onto the
Canvas (thereby double-buffering!). This is useful for pixel-level drawing,
or when you want to make more complex graphics or artwork.
15.1. DRAWING GRAPHICS 151
– If you’ve used MS Paint, it’s the difference between the shape drawing
options and the “zoomed in” pixel coloring.
Note that we cause the Canvas to be “redrawn” (so our onDraw() method to
be called) by calling invalidate() on the View: this causes Android to need
to recreate it, thereby redrawing it. By repeatedly calling invalidate() we
can do something approximating animation!
• We can do this via a recursive loop by calling invalidate() at the end
of onDraw(). This lets us “request” that Android cause onDraw() to be
called again once it is finished, but we don’t call it.
• As a demo, we can make the Ball slide off the screen by changing it’s
position slightly:
ball.cx += ball.dx;
ball.cy += ball.dy;
15.1.2 SurfaceViews
Since all this calculation (at pixel-level detail) can take some time, we want to
be careful it doesn’t block the UI thread! We’d like to instead do the drawing
in the background. The rendering itself needs to occur on the UI Thread, but
we want all of the drawing logic to occur on the background thread, so that the
UI work is as fast as possible (think: hanging up a pre-printed poster rather
than needing to print it entirely).
• However, an AsyncTask isn’t appropriate, because we want to do this
repeatedly. Similarly, an IntentService may not be able to respond fast
enough if we need to wait for the system to deliver Intents.
Android provides a class that is specially designed for being “drawn” on a
background thread: the SurfaceView. Unlike basic Views that are somewhat
ephemeral, a SurfaceView includes a dedicated drawing surface that we can
interact with in a separate thread. It is designed to support this threading work
without requiring too much synchronizatoin code.
These take even more work to setup, so a complete example (DrawingSurfaceView)
is again provided in the lecture code:
• This class extends SurfaceView and implements SurfaceHolder.Callback.
A SurfaceHolder is an object that “holds” (contains) the underlying
drawable surface; somewhat similar to the ViewHolder pattern utilized
with an Adapter. We interact with the SurfaceView through the holder
to make sure that we’re thread-safe: that only one thread is interacting
with the surface at a time.
– In general there will be two threads trading off use of the holder:
our background thread that is drawing on the surface (“printing the
poster”), and then UI thread that is showing the surface to the user
(“hanging the printed poster”). You can think of the holder as the
poster in this metaphor!
• We register the holder in the constructor with the provided getHolder()
method, and register ourselves for callbacks when the holder changes. We
also instantiate a new Runnable, which will represent the callback exe-
cuted in a separate (background) thread to do the drawing.
• The SurfaceHolder.Callback interface requires methods about when
the surface changes, and so we fill those in:
15.2. TOUCH AND GESTURES 153
There are lots of things that can cause TouchEvents, so much of our work
involves trying to determine what semantic “gesture” the user made. Luckily,
Android provides a number of utility methods and classes to help with this.
The most basic is MotionEventCompat.getActionMasked(event), which ex-
tracts the “action type” of the event from the motion that was recorded:
int action = MotionEventCompat.getActionMasked(event); //get action constant
float x = event.getX(); //get location of event
float y = event.getY() - getSupportActionBar().getHeight(); //closer to center...
switch(action) {
case (MotionEvent.ACTION_DOWN) : //put finger down
//e.g., move ball
view.ball.cx = x;
view.ball.cy = y;
return true;
case (MotionEvent.ACTION_MOVE) : //move finger
//e.g., move ball
view.ball.cx = x;
view.ball.cy = y;
return true;
case (MotionEvent.ACTION_UP) : //lift finger up
case (MotionEvent.ACTION_CANCEL) : //aborted gesture
case (MotionEvent.ACTION_OUTSIDE) : //outside bounds
default :
return super.onTouchEvent(event);
}
This lets us react to basic touching. For example, we can make it so that taps
(ACTION_DOWN) will “teleport” the ball to where we click! We can also use the
ACTION_MOVE events to let us drag the ball around.
Then in the Activity’s onTouchEvent() callback, we can pass the event into
the Gesture Detector to process:
boolean gesture = this.mDetector.onTouchEvent(event); //check gestures first!
if(gesture){
return true;
}
We’ve seen how we can create animations simply by adjusting the drawing we do
on each frame. This is great for games or other complex animations if we want to
have a lot of control over our graphical layout… but sometimes we want to have
some simpler, platform-specific effects (that run smoother!) Android actually
involves a number of different animation systems that can be used within and
across Views:
• We’ve previously talked about some Material Animations built into the
Material Design support library; in particular we discussed creating Ac-
tivity transition. Android also includes a robust framework for Scene
Transitions even outside of Material; see Adding Animations for more de-
tails.
156 CHAPTER 15. ANIMATION AND GRAPHICS
Of course, just performing this interpolation over time doesn’t produce any
visable result—it’s changing the numbers, but those numbers don’t correspond
to anything.
We can access the interpolated values by registering a listener and overriding
the callback we’re interested in observing (e.g., onAnimationUpdate() from
ValueAnimator.AnimatorUpdateListener). But more commonly, we want
to have our interpolated animation change the property of some object—for
example, the color of a View, the position of an Button, or the instance variables
of an object such as a Ball.
We can do this easily using the ObjectAnimator3 subclass. This subclass runs
an animation just like the ValueAnimator, but has the built-in functionality to
change a property of an object on each interpolated step. It does this by calling
a setter for that property—thus the object needs to have a setter method for
the property we want to animate:
2 http://developer.android.com/guide/topics/graphics/prop-animation.html#value-
animator
3 http://developer.android.com/guide/topics/graphics/prop-animation.html#object-
animator
15.3. PROPERTY ANIMATION 157
AnimatorSet animatoins can get complicated, and we may want to reuse them.
Thus best practice is to instead define them in XML as resources. Animation
resources are put inside the /res/animator directory (not the /res/anim/
folder, which is for View Animations).
<set android:ordering="together"> <!-- together is default -->
<objectAnimator
android:propertyName="x"
android:duration="500"
android:valueTo="400"
android:valueType="intType"/>
15.3. PROPERTY ANIMATION 159
Note that we can also use this same framework to animate changes to Views:
Buttons, Images, etc. Views are objects and have properties (along with appro-
priate getter and setter methods), so we can use just an ObjectAnimator! See
Animating Views for a list of properties we can change (a list that includes x,
y, rotation, alpha, scaleX, scaleY, and others)
To make this process even simpler, Android also provides a ViewPropertyAnimator
class. This Animator is able to easily animate multiple properties together (at
the same time), and does so in a much more efficient way. We can access this
Animator via the View#animate() method. We then call relevant shortcut
methods on this Animator to “add in” addition property animations to the
animation set:
//animate x (to 100) and y (to 300) together!
myView.animate().x(100).y(300);
This allows you to easily specify moderately complex property animations for
View objects.
• But really, if you want to animate layout changes on a modern device, you
should use transitions, such as the ones we used with Material design.
There are many more ways to customize exactly what you want your animation
to be.You can also look at official demos for more examples of different styles of
animation.
4 http://developer.android.com/guide/topics/resources/animation-resource.html#
Property
160 CHAPTER 15. ANIMATION AND GRAPHICS
Chapter 16
161
162 CHAPTER 16. DATABASES AND PROVIDERS
• In order to get this list of items into memory from data store itself (to
perform the read operation), we set up a Loader. The Loader fetches 3
“columns” (attributes) from the data store: _ID, WORD, and FREQUENCY;
the _ID field is not shown on the View, but the Loader needs that to keep
track of the displayed items). We do not utilize any selection or sorting
criteria, though we could add them in if we wanted.
The loader fetches (queries) data from the data store to load it into
memory. It then tells the Adapter to take that loaded data and display
it in the View. By using a Loader for this process, we gain two benefits:
(1) the data is loaded into memory on a background thread (not the UI
Thread), and (2) the data is automatically reloaded when the data store’s
content changes.
• The example also supports the create operation: by clicking the “Add
Word” button, we can insert a new entry into the data store represented
by the Content Provider. We construct a ContentValues object (simi-
lar to a Bundle, but for Content Providers) that contains the attribute
values for the new provider entry. We then use the ContentResolver to
insert() this item into the Provider (indicated by its URI).
– Note that we make sure to update only that particular item by spec-
ifying the URI of that item. We do this by constructing a new URI
representing/identifying that item, effectively by appending "/:id"
to the URI. This means that we don’t need to use the selection cri-
teria (though we could do that as well).
To review: the Content Provider acts as the data store, abstracting information
at some location (e.g., in a database). The Loader grabs a bunch of rows and
columns from that database, and hands it to the Adapter. The Adapter takes
a subset of those rows and columns and puts them in the ListView so that they
display to the user. User interaction allows us to add or modify the data in that
database.
16.2. SQLITE DATABASES 163
static nested class (e.g., WordEntry), to keep things organized. This class
contains the constants to hold the column names:
static class WordEntry implements BaseColumns {
//class cannot be instantiated
private WordEntry(){}
• In the onUpdate() callback, we’ll just “drop” the table and recreate it (by
calling onCreate()).
If we want to interact with this database in MainActivity, we can initial-
ize the DatabaseHelper object (which will create the database if needed)
and then use that helper to fetch the database we want to query (using
getReadableDatabase()).
• Note that querying a database could take a long time, and so we should
not be doing it on the UI Thread… this example is simply for testing.
We can check that our database is set up correctly in one of two ways:
• We can directly explore the SQLite database that is on your device by
using adb and the sqlite3 tool. See this link for more details.
$ adb -s emulator-5554 shell
# sqlite3 /data/data/edu.uw.package.name/databases/words.db
# sqlite> select * from words;
# sqlite> .exit
• We can call a query() method on our SQLiteDatabase, and log out the
results. A SQLiteQueryBuilder can offer some help if our query was
going to be complex (e.g,. with JOIN):
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(WordDatabase.WordEntry.TABLE_NAME); //set the table to use
while(results.moveToNext()) {
String word = results.getString(results.getColumnIndexOrThrow(WordDatabase.W
int freq = results.getInt(results.getColumnIndexOrThrow(WordDatabase.WordEnt
Log.v(TAG, "'"+word+"' ("+freq+")");
}
– This is the exact same Cursor processing work used when logging out
the clicked item, but using our column names instead!
– We could even remove the Loader call and just pass in this query
directly to the Adapter, if we wanted to display our database in the
list.
Voila, we have a database that we can call methods on to access!
We don’t want to do this database creation and querying on the main thread
(because it may take a while). And since we also want to easily let our ListView
update when the database changes, we we like to be able to use a Loader to
access this database. In order to use a Loader, we need to wrap the database
in a ContentProvider.
There are a lot of steps and a lot of code involved inmaking a ContentProvider,
and most of them are “boilerplate” for most databases. So much so that there
is thorough example code in the Google documentation, which you can copy-
and-paste from as needed.
We’ll start by creating another class that extends ContentProvider (can you
understand why?). Since this will have a lot of abstract methods we’ll need
to fill in, so we can actually use one of Android Studio’s generators via New >
Other > Content Provider to help us along (I normally say not to use these,
but with the ContentProvider it’s not too messy).
We will have to specify an authority for the Provider. This acts as a unique,
Android-internal “name” for the database (to indicate which it is, or who “owns”
it). This is the “name” by which others will be able to refer to our particular
Provider. This is thus sort of like a package name—and in fact, we usually use
the package name with an extra .provider attached as the authority name.
Also notice that an entry for this <provider> has been added to the Manifest,
including the authority name. android:enabled means the Provider can be
instantiated, and android:exported means it is available to other applications.
16.3. IMPLEMENTING CONTENTPROVIDER 167
The most important piece of a ContentProvider (that makes it more than just
helper methods for a database) is how it can be accessed at a particular URI.
So the first thing we need to do is specify this URI for our provider.
• We’ll actually want to specify multiple URIs. This is because each piece
of content we provide (each record in the database!) is itself a distinct
resource, and thus should have its own URI. Thus we need to design a
schema for the URIs so that we know how to refer to each kind of content
offered by our provider.
Designing a URI schema is like designing a URL structure for a website; this
will feel familiar to specifying routes for a web application.
The most common URI approach for Content Providers is to give each resource
we provide a URI of the format:
content://authority/resource/id
But we also need to handle both types of resources: the “list” of words, and the
individual words themselves. To enable this, we’re going to use a class called
a UriMatcher. This class provides a mapping between URIs and the actual
“type” of data we’re interested in (either lists or word objects). This will help
us do “routing” work, without needing to parse the path of the URI ourselves.
• We’ll represent the “type” or “kind” with int constants, allwoing us to
easily refer to “which” kind of resource we’re talking about.
//integer values representing each supported resource Uri
private static final int WORD_LIST_URI = 1; // /words
private static final int WORD_SINGLE_URI = 2;// /words/:id
168 CHAPTER 16. DATABASES AND PROVIDERS
– So if you give me a /words URI, I can tell you that you’re interested
in “resource kind #1”
We want to make a a static UriMatcher object (like a constant) that we can
use to do the mapping… but because it takes more than one line to set this up
(we add an entry for each mapping), we need to put it inside a static block so
that all this code is run together:
private static final UriMatcher sUriMatcher; //for handling Uri requests
static {
//setup mapping between URIs and IDs
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITY, WORD_RESOURCE, WORD_LIST_URI);
sUriMatcher.addURI(AUTHORITY, WORD_RESOURCE + "/#", WORD_SINGLE_URI);
}
• Note the wildcard #, meaning “any number” (after the slash) will “match”
this URI.
We can then figure out which “kind” of task by using the UriMatcher#match(uri)
method, which will return the “kind” int that matches the given Uri.
As an example of this, let’s fill in the getType() method. The purpose of
this method is to allow the ContentProvider to let whoever queries it know the
MIME Type (media type) of the resource a URI is accessing. This lets the
program specify whether the content provided by the Content Provider is an
image, text, music, or some other type.
• The type we’re going to give back is a Cursor (list of rows in a table), so
we’ll specify MIME Types for that:
public String getType(Uri uri) {
switch(sUriMatcher.match(uri)){
case WORD_LIST_URI:
return "vnd.android.cursor.dir/"+AUTHORITY+"."+WORD_RESOURCE;
case WORD_SINGLE_URI:
return "vnd.android.cursor.item/"+AUTHORITY+"."+WORD_RESOURCE;
default:
throw new IllegalArgumentException("Unknown URI "+uri);
}
}
Once all of the URIs are specified, we can start responding to requests for
content at those URIs. Specifically, when a request for content at a URI comes
16.3. IMPLEMENTING CONTENTPROVIDER 169
in, we’re going to fetch data from the database we made earlier and then return
that data. We handle these “requests” through 4 different methods: query(),
insert(), update(), and delete() (mirroring the CRUD operations, drawing
on standard SQL query names). We will fill in those methods to have them
fetch and return the database data.
First, we need to get access to the database (through a helper), just as we
did in the MainActivity. We’ll instantiate the DatabaseHelper in the
ContentProvider#onCreate() callback, saving that helper as an instance
variable to reference later. Then in the CRUD methods (which will be executed
in a background thread), we can call getWriteableDatabase() to get access
to that database.
We will start with implementing the query() method. Basically, we need to
do the same query we used in MainActivity—though can pass in the extra
query parameters (e.g., projection, selection, sortOrder) instead of always
having them be null or defined manually.
However, we also need to be able to handle both types of resources that
our Provider serves (lists or single words). We can use the UriMatcher
to determine how to adjust our query: for example, by using the
UriBuilder#appendWhere() method to add a “selection” argument:
switch(sUriMatcher.match(uri)){
case WORD_LIST_URI: //all words
break; //no change
case WORD_SINGLE_URI: //single word
builder.appendWhere(WordDatabase.WordEntry._ID + "=" + uri.getLastPathSegment()); //restr
default:
throw new IllegalArgumentException("Unknown URI "+uri);
}
We’ll then just return the Cursor that we get as a result of the query.
But there is also one more piece. We want to make sure that the Loader that
is reading from our Content Provider (that loaded this Cursor object) is noti-
fied of any changes to the results of its query. This will allow the Loader to
“automatically” query for new content if any of the data at that URI changes.
cursor.setNotificationUri(getContext().getContentResolver(), uri);
With this step in place, we can go back to our MainActivity and swap all the
column names and URIs for our own custom WordProvider! Rerun the app…
and voila, we see our own list of words!
We can do basically the same thing to support insert() and update() to
enable all of our use cases.
• Use the UriMatcher to make to only respond to proper Uris—you can’t
insert into a single record, and you can’t update the entire list.
170 CHAPTER 16. DATABASES AND PROVIDERS
if(sUriMatcher.match(uri) != WORD_LIST_URI) {
throw new IllegalArgumentException("Unknown URI "+uri);
}
But in the end, we have a working ContentProvider that supports the same
behaviors as the built in User Dictionary (well, except for delete()). We can
16.3. IMPLEMENTING CONTENTPROVIDER 171
now store data in our own database and easily access it off the UI Thread for
use in things like ListViews. This is great for if you want to track and store any
kind of structured information in your apps.
172 CHAPTER 16. DATABASES AND PROVIDERS
Chapter 17
Publishing
This short chapter discusses how to publish your Android application, producing
a version of the app that can be shared with others. In particular, it explains
how to cryptographically sign your app and build it so it can be installed by
people who are not using Android Studio.
Before you actually distribute your application, you should make sure it is fully
ready to be published. Google provides an excellent list of things to do before
releasing your application, as well as a more detailed checklist for releasing an
app on the Play Store.
• For example: remove extranous Logging commands, check for accessibility
and localization, etc.
Once you have completed these steps, you are ready to build and sign your app.
173
174 CHAPTER 17. PUBLISHING
signatures as a security feature to ensure that any future updates come from the
same person (no malicious app updates!), as well as to help verify the source of
an installed package.
• The secret private keys are stored on your computer in .keystore files
(think: a database of private keys). You may have multiple different key-
stores on your machine.
By default, when you build and run an app in Android Studio, the IDE auto-
matically generates a debug certificate for you to sign your application with.
This certificate is not secure (it’s an automatically generated password!) so isn’t
trustworthy for app stores (like the Play Store)… but it is sufficient for being
able to install and run your application through Android Studio.
• By default keys are stored in the ~/.android folder on Mac and Linux,
and the C:\Users\USER_NAME\.android\ folder on Windows. You can
view the debug key (e.g., on Mac) using the command:
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -sto
– The -alias is a name of the particular certificate, and the
-storepass and keypass arguments are literal passwords asso-
ciated with the store (database) and certificate (in the database)
respectively. The fact that this store is password protected is what
makes it accessible only to the developer.
– Scroll down to see the “Certificate fingerprints”; for example, the
SHA1 certificate is used whe getting a Google Maps API key.
• Importantly, each computer running Android Studio will produce it’s own
debug certificate. That means that the “signature” identifying your app
will different for every different computer: even if it has the same package
and the same source code, Android will consider it a “different” program
because it was built (in debug mode) on a different machine. This is
particularly important when things like API keys (e.g., for Google Maps)
are linked to a particular digital signature; it means that each development
machine would need to have its unique signature associated with the API!
In addition to the automatically generated debug certificates, you can sign apps
with your own generated release certificate. This is a certificate not automat-
ically created by Android Studio, but is still associated with a secret “password”
that only you know. These certificates are also stored in a .keystore file, which
is created and password-protected by the developer. Because this keystore is
kept secret and locked, only the developer is able to sign the built .apk with a
verifiable signature, thereby ensuring that any updates to the application must
have come from that develper.
I like to think of debug certificates as like cheap, easily-reproducable Bic pens,
and release certificates like fancy golden quills. When multiple developers are
working on an app, each will be signing their testing versions with their own
cheap Bics, but when it comes to releasing the project, you need to get out the
17.1. SIGNING AN APP 175
expensive golden quill to do the signing. In this metaphor, the .keystore file
is a pen case.
This lecture introduces Kotlin, an open source programming language that runs
on the Java Virtual Machine and is fully interoperable with Java Code. It is
designed to be a more concise, more flexible, and faster alternative to Java while
offering language-level support to features such as null-checking and functional
programming techniques. As of May 2017, Google offers first-class support for
using Kotlin when developing Android applications.
• Kotlin was developed by JetBrains, the creators of the IntelliJ IDE which
forms the basis for Android Studio.
• Note that the provided gradle build script includes the Kotlin standard
library for Java 8, making (simplied) versions of Java classes available.
The Kotlin language draws many syntactical structures from popular modern
languages, including JavaScript and Swift (used for iOS development); I find it
has a lot in common with TypeScript.
The full documentation for the Kotlin language can be found at https:
//kotlinlang.org/docs/reference/; practice and examples are available in the
form of Kotlin Koans. This lecture does not aim to be a complete reference
but rather to highlight some of the most noticable or interesting aspects of the
langauge.
177
178 CHAPTER 18. SPECIAL TOPICS: KOTLIN
18.1.1 Variables
Kotlin variables are declared by using the keyword var (for mutable variables)
or val (for read-only variables); this is similar to let and const in JavaScript
ES5. Like Java, Kotlin is a statically-typed language—the variable type is
written after the variablen name, separated by a colon (:). For example:
val x:Int = 448 //read-only
var stepCount:Int = 9000 //mutable
stepCount++ //can change the variable
val message:String = "Hello" + " " + "world" //supports normal operators
Notice that each of these statements lacks semicolons! Similar to many script-
ing languages, statements can separated either by a colon or by a newline.
In Kotlin, primitive types (e.g., int, double, boolean) and implemented using
classes, allowing for all values to support instance methods and variables (called
properties). These classes are named after their Java equivalents (though of
course start with a capital letter, since they are classes!)
• The compiler optimizes the types to avoid extraneous overhead.
Additionally, variable type can often be inferred from the assigned value, so
may be omitted:
var x = 448 //`Int` type is inferred
x-40 //valid mathematical operation
• Note even without the explicit type declaration, the variable is strongly
typed—it’s just that the variable type is determined by the compiler. I
encourage you to include the explicit type declaration, particularly when
it helps clarify the semantic meaning of the variable.
One of Kotlin’s major features is null safety, or the ability to catch null-pointer
errors at compile time. In Kotlin, we need to explicitly declare that a variable
can be assigned null as a value. We do this by including a question mark ?
immediately after the type declaration:
var message:String = "Hello"
message = null //compilation error!
• Kotlin provides a number of other structures using the ? to check for and
handle null values; see Null Safety for details.
Basic Kotlin types work pretty much the same as basic Java types. A few
notable differences and features:
• Warning: Because of the classes Kotlin uses for numeric types, it is unable
to automatically convert to a “larger” type (e.g., from Int to Double).
18.1. KOTLIN SYNTAX 179
– Kotlin also provides specialized classes for arrays of basic types, e.g.,
IntArray
18.1.2 Functions
Kotlin functions are in some ways closer to JavaScript functions that to Java
methods. For one, Kotlin supports package-level functions that do not exist as
members of a particular class, as an alternative to static functions in Java.
Thus we can talk about Kotlin functions independent of classes (which are dis-
cussed below).
Functions in Kotlin are declared with the fun keyword (not because they are
fun, though they can be). This is folowed by the name of the function and the
180 CHAPTER 18. SPECIAL TOPICS: KOTLIN
parameter list (where the parameters are given explicit types). The return type
is declared after the parameter list, following a colon (:). For example:
fun makeFullName(first:String, last:String): String {
val full = first + " " + last
reurn full
}
Java’s void return type is in Kotlin represented by the singular value Unit. If
a function returns Unit, it can and should be omitted:
fun sayHello() { //returns Unit, but type omitted
println("Hello world");
}
Kotlin functions also support named default argments: you can provide a default
value for an argument in the function declaration. If that (positional) argument
is omitted, then the default value is used instead. Additionally, arguments can
be specified by name when calling the function, allowing you to include them
out of order.
fun greet(personTo:String = "world", personFrom:String = "Me") {
println("Hello $personTo, from $personFrom")
}
• Note that you can omit the return type from single expression functions,
as it can be inferred.
This provides a very concise way of writing simple functions!
Similar to JavaScript (and drawing from Java 8), Kotlin supports higher order
anonymous functions and lambda functions. These are functions that can be
assigned to variables (including function parameter), just like any other object!
These are highly effective when using function programming paradigms (e.g.,
map() and reudce()), or when specifying event callback functions.
Anonymous functions are normal functions (whether with a block body or just a
18.1. KOTLIN SYNTAX 181
single expression), but written without a name. These functions can be assigned
to variables, or passed directly to functions that take in appropriate callbacks:
val square = fun(n:Int):Int {
return n*n
}
• Note that the square variable here has an inferred type that is a function
which takes in an Int and returns an Int. We can explicitly declare this
type using the following syntax:
val square:(Int) -> Int
In fact, is Kotlin is able to figure out the signature for a lambda function with
only a single parameter (e.g., because the lambda is being anonymous passed
in as a defined callback function, so must meet the required interface), it will
allow us to omit the signature entirely. In this case, the single parameter is
automatically assigned to the variable it:
val numbers:IntArray = intArrayOf(3,1,4,2,5)
println(filtered.joinToString()) //3, 4, 5
fruits
.filter { it.startsWith("a") }
.sortedBy { it }
.map { it.toUpperCase() }
.forEach { println(it) }
18.1.3 Classes
Kotlin is an object-oriented language, just like Java. Thus despite being able to
support package-level functions, Kotlin is primarily written using classes with
attributes and methods.
As in Java, Kotlin classes are declared using the class keyword. Additionally,
most Kotlin classes have a primary constructor, the parameters of which can be
included as part of the class declaration.
//declares a class `Dog` whose constructor takes two parameters
class Dog(name:String, breed:String) {
var name = name //assign to properties
val breed = breed
}
We can specify a classes inheritance by putting the parent type after a colon
(:) in the declaration. If the child class has a primary constructor, then we also
include the parameters which should be passed to the parent’s constructor.
class Dog(var name:String, val breed:String) : Animal() {
Note that Kotlin does not have static class methods; instead, just use package-
184 CHAPTER 18. SPECIAL TOPICS: KOTLIN
elevel functions!
Kotlin also supports a few other special kinds of classes:
• Data Classes are declared using the data keyword as an annotation in the
class declaration. These are classes that do nothing but hold data: they
have only attributes, no methods (similar to a struct in C). The compiler
will automatically provide implementations of toString() and equals()
for these classes.
data class Ball(var x:Int, var y:Int, val color:String)
• Nested classes can be made into inner classes (e.g., “non-static” classes
with access to the containing class’s instance variables) using the inner
annotation in the class declaration.
• Anonymous classes (called Object expressions) can be created with the
object keyword in place of a class and the class name:
button.addActionListener(object : ActionListener() {
//class definition (including methods to override) goes in here!
override fun actionPerformed(ActionEvent event) {
//...
}
});
This is pretty similar to Java, but using object instead of new to instantiate
the anonymous class.
https://kotlinlang.org/docs/tutorials/android-plugin.html
To include:
apply plugin: 'kotlin-android-extensions'
To avoid findViewById()
import kotlinx.android.synthetic.main.<layout>.*
18.2.2 Anko
https://github.com/Kotlin/anko
dependencies {
compile "org.jetbrains.anko:anko-commons:$anko_version"
}
Logging: https://github.com/Kotlin/anko/wiki/Anko-Commons-%E2%80%
93-Logging
• Avoids the explicit call to Log, and uses the class name as the TAG. There
are other Java libraries that support easier logging as well.
info("London is the capital of Great Britain")
debug(5) // .toString() method will be executed
warn(null) // "null" will be printed
Intents: https://github.com/Kotlin/anko/wiki/Anko-Commons-%E2%80%93-
Intents
startActivity(intentFor<SomeOtherActivity>("id" to 5).singleTop())
//or
startActivity<SomeOtherActivity>("id" to 5)
//toasts
toast("Hi there!")
longToast("Wow, such a duration")
//alert
alert("Hi, I'm Roy", "Have you tried turning it off and on again?") {
yesButton { toast("Oh…") }
noButton {}
}.show()
Part II
187
Chapter 19
Accessibility
This chapter will discuss how to support Accessibility when developing An-
droid applications—specifically, supporting users with levels of physical disabil-
ity. Accessibility is an incredibly important software quality that is often over-
looked, but making Android apps accessible requires only a few minor changes
to the implementation details discussed in this course.
When developing any kind of interactive system, there are different design prin-
ciples (e.g., Shneiderman and Plaisant’s Golden Rules) that can provide guide-
lines for how to develop effective and usable system.
One of the most important design principles is Universal Usability (also
known as Universal Design), which is the principle that designed products should
be inherently accessible. This principle takes as its premise that designing for
accessibility—to be usable by all people no matter their ability (physical or
otherwise)—benefits not just those with some form of limitation or disability,
but everyone.
The classic example of Universal Design are curb cuts: the “slopes” built into
curbs to accommodate people in wheelchairs. However, this design decision
end up making curbs more usability for everyone: curb cuts help people with
rollerbags, strollers, temporary injuries, or who just have problems climbing
steps.
• If you design a piece of technology to be used by a person with only one
arm, then you support people with a disablity. But you also support
people with a temporary disability (e.g., their arm is unusable because it
is in a sling or a cast), and people who are just currently inconvenienced
189
190 CHAPTER 19. ACCESSIBILITY
(e.g., they are holding a baby in that arm). You make the interaction and
life better for everyone.
Universal usability is equally important in the domain of mobile design:
• If you support people with vision impairments (e.g., by providing touch
and voice controls), you also support people who just want to use the app
while driving or otherwise visually occupied.
• If you support people who cannot afford high-end devices with unlimited
4G connections (e.g., by functioning on older versions of Android, or being
frugal when downloading data), you also support people who are currently
without data connections (being out in the woords, on an airplane, over
their data plan, etc).
People with disabilities cannot ethically be excluded from consideration in app
design, and by considering their needs you will also improve the usability of your
app for all population—two for the price of one! This guideline is increasingly
being acknowledged by companies as key to usability, and thus it is important
that you apply it to your own design work.
elements as the user focuses on them, as well as allow the user to drag a finger
around a screen and get verbal feedback of what is there.
• TalkBack can be turned enabled by going to Settings > Accessibility
> TalkBack. This service is available on most consumer devices, but will
need to be installed manually on the emulator. You can download the
packaged .apk from here (version 5.1.0 works fine), and install it on the
emulator using adb on the Terminal:
# replace with the package-name
adb install package-name.apk
Turn on TalkBack and use it to explore your phone and the test the
Loader Demo app. You should do this without looking at your phone
(avert you eyes, flip it upside down, etc)—try acting as if you were blind but
still need to use the device!
• The TalkBack service will start with a tutorial that you can complete (you
can also read the user documentation).
In short: drag your finger to browse the device (letting TalkBack tell you
what you are selecting), and then double-tap to “click” on an element.
As you should notice in testing your app, many interface designs give usabil-
ity hints (e.g., what a button does) though visual cus: images, icons, and la-
bels. While this may cause the app to “look” nice, it is not very effective for
vision-impaired users—such as how the “icon” buttons are just explored as (e.g.)
“Button 59”.
Thus for these purely visual elements (e.g., ImageButton, ImageView) we need
to specify what text should be read by TalkBack. Do this by including an
android:contentDescription attribute on these elements, which are given
a value of the text that TalkBack should read. Do this for all of the visual
elements in the MovieFragment layout. (You can also set this description for
dynamic elements using the setContentDescription() function in Java).
• This is equivalent to adding an alt or ARIA attribute in HTML.
Incuding the android:contentDescription attribute is an incredibly easy ad-
dition (low-hanging fruit!) that does quite a lot to support accessibility of
Android apps.
A second easy change involves supporting interaction that doesn’t use the Touch
Screen. This could be because of physical limitations: the user may interact with
the device through an external device such as a keyboard, trackball, or switch.
The best way to support these alternative inputs is by making sure that each
navigational element (things the user may select) are focusable. You can do
192 CHAPTER 19. ACCESSIBILITY
this by specifying the android:focusable attribute in the XML (or use the
View#setFocusable() method in Java).
Buttons are already focusable by default. But you can also specify the order
by whch elements get focus (similar to the “tab order” in HTML). This is
done using XML attributes android:nextFocusDown, android:nextFocusUp,
android:nextFocusLeft, android:nextFocusRight. Each of these takes an
id reference as a value (e.g., "@id/nextElement"), which refers to which View
should gain focus instead of the “natural” order.
• To practice this, modify the focus order so that the the “search input”
has focus first, with the “search button” gaining focus on down from there
and the “clear button” gaining focus on up.
• In order to test this, you will need to make sure your device supports a
physical keyboard and/or D-Pad, but you can also use the arrow keys for
the emulator.
3. For custom icons and drawables: define separate resources for LTR and
RTL (use the ldrtl resource qualitifier to specify the “layout direction”
as right-to-left). This will allow for icons (such as the arrows in the upper
start corner of the example) to change direction with the text—you want
“back” to actually point “back”!
Make these changes to the MovieFragment (there are no custom drawables
to adjust). You can test that your changes work by selecting Settings >
Developer options > Force RTL layout direction.
Fragments: ViewPager
In this chapter, you will practice working with Fragments and layouts. Specif-
ically, you will modify the Movie application so that it uses a ViewPager, an
interactive View offered by the Android Support Library that will allow you to
“page” (swipe) through different Fragments. You will modify the application so
that the user can swipe through a “search” screen, the list of search results, and
the details about a particular movie.
IMPORTANT NOTE: you should not modify the MovieFragment or the
DetailFragment (those Fragments are self-contained and so can be used in
multiple layouts!). You will need to create one new Fragment though, and
make substantial modifications to the MainActivity
This chapter will build on the lecture code found at https://github.com/
info448-s17/lecture05-fragments.
If you haven’t already, you should Fork and Clone this repo in order to com-
plete the tutorial. Note that you should complete this tutorial on a separate
viewpager-work branch. You can create this branch either off of the completed
branch (containing the completed lecture code), or from the master branch of
code if you were able to complete the work described in lecture 5:
git checkout completed
git checkout -b lab-work
Your ViewPager will need to support three different Fragments. While the
MovieFragment and DetailFragment are defined already, you will need to
create a third.
195
196 CHAPTER 20. FRAGMENTS: VIEWPAGER
Create a new Fragment called SearchFragment (use the File > New
> Fragment > Fragment (Blank) menu in Android Studio). Your
SearchFragment will need to include the following components
1. The layout for the Fragment should contain the seach EditText and
Button taken from the activity_main layoout. You can add some
layout_gravity to center the inputs. You can also remove the onClick
XML attribute, as click handling will be specified in the Java
2. In the SearchFragment class, be sure to define a newInstance() factory
method. The method doesn’t need to take any arguments (and thus you
don’t need to specify an argument bundle).
• Typing newInstance will allow Android Studio to tab-complete the
method!
3. The SearchFragment will need to communicate with other Fragments,
and thus you will need to define an interface (e.g., OnSearchListener)
that the containing Activity can implement. This interface should support
a single public method (e.g., onSearchSubmitted(String searchTerm))
which will allow the Fragment to pass the entered search term to the
Activity.
• Remember to check that the containing Activity implements the in-
terface in the Fragment’s onAttach() callback.
4. Finally, in the onCreateView() callback, add a click listener to the but-
ton so that when it is clicked, it calls the onSeachSubmitted() callback
function on the containing Activity (which you’ve established has that
method!)
• Remember that you can call findViewById() on the root view.
When the search term is submitted from the SearchFragment, your Activity
should instantiate a new (potentially different) MoviesFragment result list for
that search term. The PagerAdapter should return an appropriate page count
depending on whether a result list has been instantiated or not.
• However, simply creating a different Fragment will not cause the Adapter
to change—you need to let the Adapter know that the model it is
adapting into a view has changed! You can do this by calling the
notifyDataSetChanged() method on the adapter.
After you’ve modified (and notified!) the Adapter, you can change which page is
displayed using the ViewPager#setCurrentItem() method. This will let you
take the user to the “results” page!
Similarly, modify the movie selection callback so that when a movie is se-
lected from the list, your Activity instantiates a new (potentially different)
DetailFragment. Remember to notify the adapter that the data set has
changed, and to change which page is currently shown.
• This will replace the previous behavior of the callback.
Once you’ve made these changes, you should be able to search for movies, see
the results, and view the details for movies. Swipe left and right to navigate
between pages!
Chapter 21
Bluetooth
In this chapter you will learn about some of the pieces for creating a connection
between two co-located devices using Bluetooth. This will let you gain some
familiarity with the Bluetooth API, as well as further practice working with
Intents.
The code for this tutorial can be found at https://github.com/info448-s17/lab-
bluetooth.
This tutorial involves filling in the remaining pieces from a Sample Project
provided by Google. Google includes lots of samples demonstrating how to use
particular pieces of functionality; reading and adapting the provided projects
is a great way to learn new skills. There are a lot of comments, though that
sometimes makes it hard to follow all the pieces. Read carefully!
• Also be sure to open the API documentation for reference!
The emulator doesn’t support Bluetooth, so you will need to run this project
on a physical device.
Your task is to fill in the missing pieces of code, following the instructions below.
I’ve marked each location with a TODO comment, which should show up in blue
in Android Studio.
1. Start by reading through The Basics to get a sense for what classes
will be used and what their roles are. You only need to focus on the
first 4: BluetoothAdapter, BluetoothDevice, BluetoothSocket, and
BluetoothServerSocket (the rest are for other kinds of Bluetooth con-
nections, like audio transfer and stuff). You don’t need to know all the
methods or details of these classes, but should be familiar with their gen-
eral, one-sentence purposes!
2. You’ll need to request permission to use Bluetooth. Add the appropriate
<uses-permission> attributes: one for BLUETOOTH (for communication;
199
200 CHAPTER 21. BLUETOOTH
included) and one for BLUETOOTH_ADMIN (to “discover” devices and make
connections).
3. The main UI is defined in the BluetoothChatFragment class, which is a
Fragment that holds the chat system. Start by filling in the onCreate()
callback by fetching the default Bluetooth adapter and saving it to an
instance variable (mBluetoothAdapter). If the adapter doesn’t exist (is
null), you should Toast a message that Bluetooth isn’t available (using
the Activity's Application Context so that the Toast lasts), and then
call finish() on the Fragment’s Activity (to close the application).
4. You’ll want your app to make sure that the user has Bluetooth turned on.
In the Fragment’s onCreate(), check whether the the BluetoothAdapter
is enabled. If not, you’ll want to prompt the user to enable it, such as
by launching the “Settings” app. Create an Implicit Intent for the ac-
tion BluetoothAdapter.ACTION_REQUEST_ENABLE, and send this Intent
for a result (with the result code of REQUEST_ENABLE_BT). Look in the
Fragment’s onActivityResult() method to see what happens when we
get a response back!
• The BluetoothChatService (stored in the instance variable
mChatService) is an object representing a “background service”—
think an AsyncTask but with a much longer lifespan. This particular
service handles sending bytes of data back and forth over Bluetooth.
We’ll talk about Services more later in the course.
5. In order for a device to connect to yours over Bluetooth, your device
will need to be discoverable: effectively, it has to respond to public
queries about its existence (sort of like having your instant messaging
status as “Online/Available”). In the Fragment’s ensureDiscoverable()
helper method, check if the device is currently discoverable by calling
getScanMode() on the BluetoothAdapter; it should return a value of
BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE.
• If this IS NOT the case, then you should send another Implicit Intent
to handle the BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE
action.
This intent should include (put) an extra that has the key BluetoothAdapter.EXTRA_DISCOVERABL
and a value of 300, so that we are in “discoverable” mode for 300 seconds.
Note that this intent does NOT need to be started for a result!
6. The discovery of devices is controlled by the DeviceListActivity Ac-
tivity. This is a separate Activity that will actually appear as a popup
dialog (though it doesn’t use DialogFragment; it just “themes” the Ac-
tivity as a dialog in the Manifest). The Activity’s onCreate() does a
lot of UI work (including setting up an Adapter!), but it also needs to set
up a BroadcastReceiver to listen for events like when devices are found.
(This is the equivalent of declaring a <receiver> and <intent-filter>
201
Maps
This chapter will introduce you to the Google Maps Android API which allows
you to very easily add an interactive map to your application.
In order to access and show a Google Map, you need to register and get an
API key (this is like a special password that lets your app access Google’s map
data). When you create the Maps Activity, Android Studio should open up the
google_maps_api.xml resource file. This file contains instructions on how to
get an API key (i.e., paste the giant pre-generated link into your browser, and
then copy the generated key into the XML resource).
(If you already have a Google Maps API Key, you can add this package & device
to that key in the Google Developer Console).
203
204 CHAPTER 22. MAPS
After you’ve entered the key, you should be able to build and run your app, and
see a displayed map!
The layout resource contains a single <fragment> element (like we’ve defined
before), in this case referring to an instance of the SupportMapFragment class.
This fragment represents the interactive map. It’s just a subclass of Fragment
(with a few extra methods), so everything we’ve learned about Fragments ap-
plies.
Once the the object is available via the callback, we can start calling methods
on it: e.g., to show a marker at a particular latitude/longitude (LatLng), and
to position the map’s “camera” to focus on that spot.
22.2. SPECIFYING THE USER INTERFACE 205
The Java code is able to position the map, but if we want to specify a “default”
position, you should instead do that work in the Fragment’s definition in the
XML resource file.
Check out the list of available XML attributes for defining the user
interface of your map. Customize the map so that:
1. It is by default centered on Mary Gates Hall. (You will need to delete the
positioning Java code so that doesn’t override your XML).
2. It is zoomed in so that we can see the see the whole fountain on the map
3. Make the map shows the “zoom control buttons” (so that you can zoom
in using the emulator!)
4. Make the map show both satellite imagery and roads/buildings.
You can also draw free-form shapes on the map, anchored to particular locations.
These include lines, circles, and generic polygons.
One of the best options for drawing is the Polyline, which is a series of connected
line segments (like a “path” in SVG).
206 CHAPTER 22. MAPS
In this chapter you will learn to use Android Styles & Themes to easily
modify and abstract the appearance of an app’s user interfaces—that is, the
XML resource attributes that define what your Views look like.
This tutorial will walk you through creating and using styles to modify views,
though you should also reference the official documentation for more details and
examples.
The code for this tutorial can be found at https://github.com/info448-s17/lab-
styles.
You will be working almost exclusively with the XML resources (e.g.,
res/layout/activity_main.xml) in the provided code, so make sure to look
those over before you begin. The main layout describes a very simple screen
showing a pile of TextViews organized in a RelativeLayout.
If you look at the TextViews, you’ll see that they share a lot of the same
attributes: text sizing, width and height, boldness, etc. If you decided that all
of the text should be bigger (e.g., for readability), then you’d need to change 6
different attributes—which is a lot of redundant work.
Enter Styles. Styles encapsulate a collection of XML properties, allowing
you to define a set of properties once and then use a single attribute to apply
all of those properties to a view. This provides almost the same functionality
as a CSS rule describing a class decalaration, but without the “cascading” part.
Styles are themselves defined as an XML resource&mdsah;specifically a <style>
element inside the res/values/styles.xml file.
207
208 CHAPTER 23. STYLES & THEMES
• This XML file was created for us when Android Studio created the project.
Open the file, and you can see that it even has some initial content in it!
• Style resource files, like String resource files, use <resource> as a top-level
element, declaring that this XML file contains (generic-ish) resources to
use.
Styles are declared with a <style> tag, which represents a single style. You can
almost think of this as a “class” in CSS (though again, without the cascading
behavior).
• The <style> element is given a name attribute, similar to how we’d define
a CSS class name. Names are normally written using PascalCase: they
need to be legal Java identifiers since they will be compiled into R, and
since they are “classes” we capitalize them!
• We’ll discuss the parent attribute in the starter code in the next section.
We define <item> elements as nested children of the <style> element. Each
<item> represents a single attribute we want our style to include, similar to a
single property of a CSS rule.
• <item> elements get a name attribute which is the the name of the prop-
erty you want to include. For example: name="android:layout_width"
to specify that this item refers to the layout_width attribute. The con-
tent of the <item> tag is the value we want to assign to that attribute,
e.g., wrap_content (not in quotes, because the content of an XML tag is
already a String!)
Finally, you can specify that you want a particular View (e.g., in your layout) to
have a style by giving that View a style attribute, with a value that references
the style that you’ve defined (using @style/..., since this resource has type
“style”).
• Note that the style attribute does not use the android namespace!
Practice: Define a new style (e.g., TextStyle) that specifies the attributes
shared by the 6 TextViews: the text size, the with, and the height. Addition-
ally, have the style define the text color as UW purple. Then, refactor these
TextViews so that they use the style you just defined instead of duplicating
attributes. See how much code you’ve saved?
• After you’ve done that, go ahead and change the size of all the TextViews
to be 22sp. You should be able to make this change in exactly one place!
TextStyle (size and color). Ideally we would like to not have to redefine a style
if it only changes a little bit.
Luckily, while styles don’t cascade, they can inherit items from one another (a
la Java inheritance, e.g., extends). We can establish this inheritance relation-
ship by specifying the parent attribute for the <style>, and having it reference
(with @) the “parent” style:
<style name="ChildStyle" parent="@style/ParentStyle"> ... </style>
This will cause the ChildStyle to include all of the <item> elements defined
in the parent style.
• We can then “override” the inherited properties by redefining the <item>
you want to change, just like when inheriting and overriding Java methods.
When inheriting from our own custom styles (e.g., ones that we’ve defined within
the same package), it’s also possible to use Dot Notation instead of the parent
attribute. For example, naming a style ParentStyle.ChildStyle will define a
style (ChildStyle) that inherits from ParentStyle. This would be referenced
in the layout as @style/ParentStyle.ChildStyle. The dot notation is used
to “namespace” the inherited class as if it were a “nested” class we wanted to
reference.
• We can chain these together as much as we want: MyStyle.Red.Big is
a style that inherits from Red and MyStyle. However, this style cannot
also be referenced as MyStyle.Big.Red style—it’s not using a CSS class
selector, but Java class inheritance!
• Note that often name style classes based on this namespaced inheritance,
so the “child class” is named after an adjective (e.g., Big) that is used to
describe the appearance change of the parent element. Text.Big would
be an appropriate style naming convention.
Pratice: Define another style (e.g., Label) that inherits from your first
style to encapsulate attributes shared by the labels (e.g., boldness). Refactor
your layout so that the labels use this new style.
Define another style (e.g., Gold) that inherits from your Label’s style and has
a background color that is UW Gold and a text color of black. Apply this style
to one of your labels.
It is best to utilize styles for elements that share semantic meaning, not just
specific attributes! For example, buttons and labels that will be duplicated,
headers shared across screens, etc. This will help you avoid needing to fre-
quently change or overwrite styles in the future just because you want to make
one button look different; changes to the style should reflect changes to the
appearance of semantic elements. This is the same guideline that is used for
determining whether you should define a CSS class or not! This blog post has
a good summary.
210 CHAPTER 23. STYLES & THEMES
Android also includes a large number of built-in platform styles that we can
apply and/or inherit from. These must be inherited via the parent attribute
(you can’t use dot notation for them). They are referenced with the format:
<style name="MyStyle" parent="@android:style/StyleName">...</style>
There are a bunch of these, all of which are defined in the R.style. This makes
discoverability difficult, as not all of the styles are documented. To understand
exactly what style effects you’re inheriting, Android recommends you browse
the source code and seeing how they are defined.
• Yes, this is like trying to learn Bootsrap by reading the CSS file.
• Author’s opinion: most of the styles are not very effective bases for inher-
itance; you’re often better using your own.
Practice: Define a new style for the Button at the bottom of the screen that
inherits from the built-in MediaButton style (but give it a text size of 22sp).
What does the inheritance do to the appearance?
23.2 Themes
Unlike CSS, Android styling is NOT inherited by child elements: that is, if you
apply a style to a ViewGroup (a layout), that style will not be applied to all the
child components of that ViewGroup. Thus you can’t “style” a layout and have
the styling rule apply throughout the layout.
The option that is available is to apply a that style as a Theme. Themes
are styles that are applied to every View in a Context (an Activity or the
whole Application). You can’t get any finer granularity of style sharing (without
moving to per-View Styles). Theme styles will apply to every View in the
context, though we can overwrite the styling for a particular View as normal.
Themes are styles, and so are defined the exact same way (as <style> ele-
ments inside a resource XML file). You can define them in either styles.xml,
theme.xml, or any other values file—resource filenames are arbitrary, and
their content will still be compiled into R no matter which file the elements are
defined in.
Themes are applied to an Activity or Application by specifying an
android:theme attribute in the Manifest (where the Activity/Application
is defined). If you look at the starter project’s Manifest created by Android
Studio, you’ll see that it already has a theme (AppTheme). In fact, this is the
<style> that was provided inside styles.xml!
Practice: Experiment with removing the theme attribute from the
application. How does your app’s appearance change? NOTE: you will need
23.2. THEMES 211
Indeed, one of the advantages of the Material Themes is that they are imple-
mented to utilize a small set of color theme attributes, making it incredibly easy
to specify a color scheme for your app. See Customize the Color Palette for a
diagram of what theme attributes color what parts of the screen.
• Note it is possible to apply a Theme to an individual View or ViewGroup,
causing the theme attributes (and ONLY the theme attributes!) to be
“inherited” by any child elements. This allows you to specify color palettes
for specific parts of your layout.
Practice: Redefine the colors in your custom Styles (from the first prac-
tice steps) so that they reference the theme attribute colors instead of purple and
gold. What happens now when you change the application’s Material Theme
between light and dark?
• Can you have the logo image reference those color theme attributes as
well? Hint: use the tint attribute.
Practice: Modify the provided AppTheme style (in styles.xml) with the
following changes:
• Have it inherit from a Material Theme (your choice of which)
• Have it define theme attribute colors using the UW colors (purple and
gold). Use these theme attribute colors to “Huskify” your app (including
the colors in your custom Styles)
Finally, set the theme of your app back to AppTheme—and you should now have
a UW flavored app!
Chapter 24
Multi-Touch
In this short chapter, you will practice working with touch interaction by im-
plementing support for multi-touch gestures, or the ability to detect two
or more different “contacts” (fingers) independently. Multi-touch is actually a
pretty awesome interaction mode; it’s very “sci-fi”.
Specifically, you will be adding support to the drawn animation demo so that
the drawn graphics tracks the location of all 5 of your fingers. This chapter
will thus build on the lecture code found at https://github.com/info448-
s17/lecture15-animation.
If you haven’t already, you should Fork and Clone this repo in order to com-
plete the tutorial. Note that you should complete this tutorial on a separate
multitouch branch. You can create this branch either off of the completed
branch (containing the completed lecture code), or from the master branch of
code if you were able to complete the work described in lecture 15:
git checkout completed
git checkout -b multitouch-work
The emulator doesn’t support multi-touch, so you will need to run this project
on a physical device.
• The first finger starts the gesture with an ACTION_DOWN event. then sub-
sequent fingers produce ACTION_POINTER_DOWN events.
213
214 CHAPTER 24. MULTI-TOUCH
Memory Management
Android is designed to run on resource constrained devices, and one of the most
constrained resources is the device’s memory. Because of this, a large part
of the Android framework and how we interact with application components
deals with how the system manages memory, making sure sufficient memory is
available for whatever the user wants to do.
In this short tutorial, you will explore how the Android operating system handles
memory, and learn how to use the Android Monitor’s Memory Monitor to view
your application’s memory usage and identify potential memory leaks.
The code for this tutorial can be found at https://github.com/info448-s17/lab-
memory. Note that you will not be required to write much code for this lab; it
is more about using the tools and inspecting the logged output to get a sense
for how memory is handled in Android. You should be able to complete this
tutorial on either the emulator or a physical device.
that value (448) is “written down” in memory so it can be used later. For
example, this int takes up 32 bits, or 4 bytes, of memory. Every single variable
you create takes up some amount of memory (see e.g., here).
217
218 CHAPTER 25. MEMORY MANAGEMENT
This data is stored in a section of memory called the “heap”, which is the part
of memory dedicated to dynamic memory (e.g., values that may only need to
be remembered for a short time). You can think of the heap as a something like
a giant array or list of bytes, a certain number of which are allocated (given)
to each new value that needs to be stored. Thus if spots 0 through 100 in the
heap are allocated, declaring a new variable may be allocated (placed) at spot
101.
Java (and by extension, Android) answers the second question in part by using
what is called the garbage collector. This is a system process that periodically
“sweeps” (searches) for any values that are no longer being used—that is, the
variables are out of scope or otherwise cannot be referenced. These values are
then “garbage collected”: the space is deallocated so that future values can be
placed there instead.
The Android framework is structured so that when memory gets low, the sys-
tem will destroy any stopped Activities, thereby allowing them to be garbage
collected (and freeing up the memory to be used by a different application).
You’ll be able to see this process in action in the next section.
25.2. THE MEMORY MONITOR 219
Since we’re interested in the memory used by the application, you should enable
and view the Memory Monitor provided by Android Studio. If you look at the
bottom panel (the Android Monitor panel) where you normally see the Logcat
results, you should see a tab called Monitors. Click on this to view the memory
usage over time (you can ignore the other monitors for now).
The dark-blue section represents allocated memory (i.e., the amount of mem-
ory being used to store variables), while the light-blue section represents free
memory (in this case, the amount of memory that the application is budgeted
by the operating system). Each section also has a size in megabytes listed on
the right. We’re primarily interested in the dark-blue allocated memory, but
check in with the instructor if you have questions about the free memory.
When you first start up the app, you should see that some memory has been
allocated, but it shouldn’t be changing at all (since the MainActivity doesn’t
create any new variables after it is created). But let’s change that!
While watching the monitor, click on the “BLANK ACTIVITY” button to
be taken to a new (blank) Activity. Did you see the bar go up?! This is
because opening a new Activity required allocating additional memory for that
Activity (particularly its Views, since it doesn’t do much else).
• Of course, the bar didn’t go up by much; that’s because this simple Ac-
tivity doesn’t require a lot of memory. In practice, even a lot of Buttons
and TextViews don’t require large amounts of memory.
Navigate back to the MainActivity. You might see the memory allocation
increase a little more, as restarting the Activity can cause the Views to need to
be recreated.
220 CHAPTER 25. MEMORY MANAGEMENT
are in scope). However, if you’re not careful it’s often easy to leave an extra
reference floating around that you didn’t intend, thereby keeping the memory
from being deallocated. This is referred to as a memory leak (because the
amount of available space is “leaking” away).
For example, consider the BirdActivity class, that you can view by clicking
on the “BIRD ACTIVITY” button. This activity shows a relatively high-
resolution image of a bird—when you open the Activity, you should see the
amount of allocated memory jump up dramatically since we need to load that
Drawable into memory.
Now you should have a sense for how values in Android can influence memory
usage. The main take-away is that you want to be careful to de-reference any
object that take significant memory (like images or other media; this is why we
released the MediaPlayer when discussing Services).
• Additionally: note that static variables are never de-referenced and so
are never garbage-collected. Thus you want to avoid static variables for
anything more complex than a String to avoid more memory leaks!
Finally, if you use the memory monitor and see memory usage stacking up, that
is not necessarily a memory leak or other problem: it could just be that the
system hasn’t run garbage collection yet! A memory leak is when resources that
should be able to be reclaimed cannot be—but as long as you keep variables
local and de-reference large objects, you’ll be fine!
Android Studio includes numerous other monitors for profiling the performance
of your application. See Android Monitor and Performance and Power for more
details.
Avoid pre-mature optimization! Make sure that you can get your app work-
ing first, and then utilize these tools to solve specific problems, such as slow
performance.
Appendix A
Java Review
Android applications are written primarily in the Java Language. This appendix
contains a review of some Java fundamentals needed when developing for An-
droid, presented as a set of practice exercises.
The code for these exercises can be found at https://github.com/info448-s17/
lab-java-review.
223
224 APPENDIX A. JAVA REVIEW
Running is then done with the java command: you specify the full package
name of the class you wish to run, as well as the classpath so that Java knows
where to go find classes it depends on:
# Runs the Tester#main() method with the `src/main/java` folder as the classpath
java -classpath ./src/main/java edu.info448.review.Tester
# on Windows
gradlew tasks
Will give you a list of available tasks. Use gradlew classes to compile the
code, and gradlew run to compile and run the code.
• Helpful hint: you can specify the “quite” flag with gradlew -q <task>
to not have Gradle output its build status (handy for the run task)
Practice: Use gradle to build and run your Dog program. See how
much easier that is?
We will be using Gradle to build our Android applications (which are much
more complex than this simple Java demo)!
• Notice that all of these attributes are private, meaning they are not
accessible to members of another class! This is important for encap-
sulation: it means we can change how the Dog class is implemented
without changing any other class that depends on it (for example, if
we want to store breed as a number instead of a String).
• Method signatures are very important! They tell us what the inputs
and outputs of a method will be. We should be able to understand
how the method works just from its signature.
Notice that to call the createPuppies() method you didn’t need to have a
Dog object (you didn’t need to use the new keyword): instead you went to
the “template” for a Dog and told that template to do some work. Non-static
methods (ones without the static keyword, also called “instance methods”)
need to be called on an object.
In general, in 98% of cases, your methods should not be static, because you
want to call them on a specific object rather than on a general “template” for
objects. Variables should never be static, unless they are also final constants
(like the BEST_BREED variable).
A.3 Inheritance
Practice: Create a new file Husky.java that declares a new Husky class:
package edu.info448.review; //package declaration (needed)
The extends keyword means that Husky is a subclass of Dog, inheriting all of
its methods and attributes. It also means that that a Husky instance is a Dog
instance.
Practice: In the Tester, instantiate a new Husky and call bark() on
it. What happens?
• Because we’ve inherited from Dog, the Husky class gets all of the methods
defined in Dog for free!
• Try adding a constructor that takes in a single parameter (name) and calls
the appropriate super() constructor so that the breed is "Husky", which
makes this a little more sensible.
We can also add more methods to the subclass that the parent class doesn’t
have. Practice: add a method called .pullSled() to the Husky class.
• Try calling .pullSled() on your Husky object. What happens? Then try
calling .pullSled() on a Dog object. What happens?
Finally, we can override methods from the parent class. Practice: add a
bark() method to Husky (with the same signature), but that has the
Husky “woof” instead of “bark”. Test out your code by calling the method
in the Tester.
A.4 Interfaces
Practice: Create a new file Huggable.java with the following code:
package edu.info448.review;
• This is a lot like hanging a sign outside your business that says “Accepts
Visa”. It means that if someone comes to you and tries to pay with a Visa
card, you’ll be able to do that!
• Implementing an interface makes no promise about what those methods do,
just that the class will include methods with those signatures. Practice:
change the Husky class declaration:
java public class Husky extends Dog implements Huggable {...}
Now the the Husky class needs to have a public void hug() method, but
what that method does is up to you!
• A class can still have a .hug() method even without implementing the
Huggable interface (see TeddyBear), but we gain more benefits by an-
nouncing that we support that method.
– Just like how hanging an “Accepts Visa” sign will bring in more
people who would be willing to pay with a credit card, rather than
just having that option available if someone asks about it.
Why not just make Huggable a superclass, and have the Husky extend that?
• Because Husky extends Dog, and you can only have one parent in Java!
• And because not all dogs are Huggable, and not all Huggable things are
Dogs, there isn’t a clear hierarchy for where to include the interface.
• In addition, we can implement multiple interfaces (Husky implements
Huggable, Pettable), but we can’t inherit from multiple classes
– This is great for when we have other classes of different types but sim-
ilar behavior: e.g., a TeddyBear can be Huggable but can’t bark()
like a Dog!
– Practice: Make the class TeddyBear implement Huggable. Do
you need to add any new methods?
What’s the difference between inheritance and interfaces? The main
rule of thumb: use inheritance (extends) when you want classes to share code
(implementation). Use interfaces (implements) when you want classes to share
behaviors (method signatures). In the end, interfaces are more important for
doing good Object-Oriented design. Favor interfaces over inheritance!
A.5 Polymorphism
Implementing an interface also establishes an is a relationship: so a Husky
object is a Huggable object. This allows the greatest benefit of interfaces and
inheritance: polymorphism, or the ability to treat one object as the type of
another!
Consider the standard variable declaration:
228 APPENDIX A. JAVA REVIEW
The variable type of myDog is Dog, which means that variable can refer to any
value (object) that is a Dog.
Practice: Try the following declarations (note that some will not
compile!)
Dog v1 = new Husky();
Husky v2 = new Dog();
Huggable v2 = new Husky();
Huggable v3 = new TeddyBear();
Husky v4 = new TeddyBear();
If the value (the thing on the right side) is an instance of the variable type
(the type on the left side), then you have a valid declaration.
Even if you declare a variable Dog v1 = new Husky(), the value in that object
is a Husky. If you call .bark() on it, you’ll get the Husky version of the
method (Practice: try overriding the method to print out "barks like
a Husky" to see).
You can cast between types if you need to convert from one to another. As
long as the value is a instance of the type you’re casting to, the operation will
work fine.
Dog v1 = new Husky();
Husky v2 = (Husky)v1; //legal casting
Practice: What happens if you run the above code? Because Huskies
and Teddy Bears share the same behavior (interface), we can treat them as
a single “type”, and so put them both in a list. And because everything in the
list supports the Huggable interface, we can call .hug() on each item in the
list and we know they’ll have that method—they promised by implementing
the interface after all!
A.6. ABSTRACT METHODS AND CLASSES 229
Take another look at the Huggable interface you created. It contains a single
method declaration… followed by a semicolon instead of a method body. This
is an abstract method: in fact, you can add the abstract keyword to this
method declaration without changing anything (all methods are interfaces are
implicitly abstract, so it isn’t required):
public abstract void hug();
An abstract method is one that does not (yet) have a method body: it’s just
the signature, but no actual implementation. It is “unfinished.” In order to
instantiate a class (using the new keyword), that class needs to be “finished”
and provide implementations for all abstract methods—e.g., all the ones you’ve
inherited from an interface. This is exactly how you’ve used interfaces so far:
it’s just another way of thinking about why you need to provide those methods.
If the abstract keyword is implied for interfaces, what’s the point? Consider
the Animal class (which is a parent class for Dog). The .speak() method is
“empty”; in order for it to do anything, the subclass needs to override it. And
currently there is nothing to stop someone who is subclassing Animal from
forgetting to implement that method!
We can force the subclass to override this method by making the method
abstract: effectively, leaving it unfinished so that if the subclass (e.g., Dog)
wants to do anything, it must finish up the method. Practice: Make the
Animal#speak() method abstract. What happens when you try and
build the code?
If the Animal class contains an unfinished (abstract) method… then that class
itself is unfinished, and Java requires us to mark it as such. We do this by
declaring the class as abstract in the class declaration :
public abstract class MyAbstractClass {...}
Practice: Make the Animal class abstract. You will need to provide an
implementation of the .speak() method in the Dog class: try just having it call
the .bark() method (method composition for-the-win!).
Only abstract classes and interfaces can contain abstract methods. In addi-
tion, an abstract class is unfinished, meaning it can’t be instantiated. Prac-
tice: Try to instantiate a new Animal(). What happens? Abstract
classes are great for containing “most” of a class, but making sure that it isn’t
used without all the details provided. And if you think about it, we’d never
want to ever instantiate a generic Animal anyway—we’d instead make a Dog or
a Cat or a Turtle or something. All that the Animal class is doing is acting as
an abstraction for these other classes to allow them to share implementations
(e.g., of a walk() method).
230 APPENDIX A. JAVA REVIEW
• Abstract classes are a bit like “templates” for classes… which are them-
selves “templates” for objects.
A.7 Generics
Speaking of templates: think back to the ArrayList class you’ve used in the
past, and how you specified the “type” inside that List by using angle brackets
(e.g., ArrayList<Dog>). Those angle brackets indicate that ArrayList is a
generic class: a template for a class where a data type for that class is itself a
variable.
You should notice that the only difference between TeddyGiftBox and
HuskyGiftBox and StringGiftBox would be the variable type of the
contents. So rather than needing to duplicate work and write the same code
for every different type of gift we might want to give… we can use generics.
Generics let us specify a data type (e.g., what is currently TeddyBear or String)
as a variable, which is set when we instantiate the class using the angle brackets
(e.g., new GiftBox<TeddyBear>() would create an object of the class with that
type variable set to be TeddyBear).
We specify generics by declaring the data type variable in the class declaration:
public class GiftBox<T> {...}
(T is a common variable name, short for “Type”. Other options include E for
Elements in lists, K for Keys and V for Values in maps).
And then everywhere you would have put a datatype (e.g., TeddyBear), you
can just put the T variable instead. This will be replace by an actual type at
compile time.
public LinkedList() {
this.start = new Node(448);
}
}
Or maybe we want to define a Smell class inside the Dog class to represent
different smells, allowing us to talk about different Dog.Smell objects. (And of
course, the Dog.Smell class would implement the Sniffable interface…)
Nested classes we define are usually static: meaning they belong to the class
not to object instances of that class. This means that there is only one copy of
that nested blueprint class in memory; it’s the equivalent to putting the class
in a separate file, but nesting lets us keep them in the same place and provides
a “namespacing” function (e.g., Dog.Smell rather than just Smell).
Non-static nested classes (or inner classes) on the other hand are defined for
each object. This is important only if the behavior of that class is going to
depend on the object in which it lives. This is a subtle point that we’ll see as
we provide inner classes required by the Android framework.
232 APPENDIX A. JAVA REVIEW
Appendix B
Swing Framework
233
234 APPENDIX B. SWING FRAMEWORK
You can compile and run this program with ./gradlew -q run. And voila, we
have a basic button app!
B.1 Events
If we click the button… nothing happens. Let’s make it print out a message
when clicked. We can do this through event-based programming (if you
remember handling click events from JavaScript, this is the same idea).
Most computer systems see interactions with its GUI as a series of events: the
event of clicking a button, the event of moving the mouse, the event of closing
a window, etc. Each thing you interact with generates and emits these events.
So when you click on a button, it creates and emits an “I was clicked!” event.
(You can think of this like the button shouting “Hey hey! I was pressed!”) We
can write code to respond to this shouting to have our application do something
when the button is clicked.
Events, like everything else in Java, are Objects (of the EventObject type) that
are created by the emitter. A JButton in particular emits ActionEvents when
pressed (the “action” being that it was pressed). In other words, when buttons
are pressed, they shout out ActionEvents.
In order to respond to this shouting, we need to “listen” for these events. Then
whenever we hear that there is an event happening, we can react to it. This
is like a person manning a submarine radar, or hooking up a baby monitor, or
following someone on Twitter.
But this is Java, and everything in Java is based on Objects, we need an object
to listen for these events: a “listener” if you will. Luckily, Java provides a
type that can listen for ActionEvents: ActionListener. This type has an
actionPerformed() method that can be called in response to an event.
We use the Observer Pattern to connect this listener object to the button
(button.addActionListener(listener)). This registers the listener, so that
B.1. EVENTS 235
the Button knows who to shout at when something happens. (Again, like follow-
ing someone on Twitter). When the button is pressed, it will go to any listeners
registered with it and call their actionPerformed() methods, passing in the
ActionEvent it generated.
But look carefully: ActionListener is not a concrete class, but an abstract
interface. This means if we want to make an ActionListener object, we
need to create a class that implements this interface (and provides the
actionPerformed() method that can be called when the event occurs). There
are a few ways we can do this:
1. We already have a class we’re developing: MyGUI! So we can just make that
class implement ActionListener. We’ll fill in the provided method, and
then specify that this object is the listener, and voila.
• This is my favorite way to create listeners in Java (since it keeps
everything self-contained: the JFrame handles the events its buttons
produce).
• We’ll utilize a variant of this pattern in Android: we’ll make classes
implement listeners, and then “register” that listener somewhere else
in the code (often in a nested class).
2. But what if we want to reuse our listener across different classes, but
don’t want to have to create a new MyGUI object to listen for a button to
be clicked? We can instead use an inner or nested class. For example,
create a nested class MyActionListener that implements the interface,
and then just instantiate one of those to register with the button.
• This could be a static nested class, but then it wouldn’t be able
to access instance variables (because it belongs to the class, not the
object). So you might want to make it an inner class instead. Of
course then you can’t re-use it elsewhere without making the MyGUI
(whose instance variables it referenes anyway)… but at least we’ve
organized the functionality.
3. It seems sort of silly to create a whole new MyActionListener class that
has one method and is just going to be instantiated once. So what if
instead of giving it a name, we just made it an anonymous class? This is
similar to how you’ve made anonymous variables by instantiating objects
without assigning them to named variables, you’re just doing the same
thing with a class that just implements an interface. The syntax looks
like:
button.addActionListener(new ActionListener() {
//class definition (including methods to override) goes in here!
public void actionPerformed(ActionEvent event) {
//...
}
});
236 APPENDIX B. SWING FRAMEWORK
This is how buttons are often used in Android: we’ll create an anonymous
listener object to respond to the event that occurs when they are pressed.
• Java has different LayoutManagers that each have their own way of orga-
nizing components. We’ll see this same idea in Android.
What if we want to do more complex layouts? We could look for a more complex
LayoutManager, but we can actually achieve a lot of flexibility simply by using
multiple containers.
For example, we can make a JPanel object, which is basically an “empty” com-
ponent. We can then add multiple buttons to this this panel, and add that panel
to the JFrame. Because JPanel is a Component (just like JButton is), we can
use the JPanel exactly as we used the JButton—this panel just happens to
have multiple buttons.
And since we can put any Component in a JPanel, and JPanel is itself a Com-
ponent… we can create nest these components together into a tree in an example
of the Composite Pattern. This allows us to create very complex user interfaces
with just a simple BoxLayout!
• This is similar to how we can create complex web layouts just by nesting
lots of <div> elements.
Appendix C
C.1 Concurrency
Concurrency the process by which we have multiple processes (think: methods)
running at the same time. This can be contrasted with processes that run
serially, or one after another.
As an example, note that one of the main concerns of computer science and
software in general is speed: how fast will a particular program or algorithm
run? For example, give two of the many sorting algorithms that have been
invented, which one can sort a list of numbers more quickly?
237
238 APPENDIX C. THREADS AND HTTP REQUESTS
• Sorting algorithms are usually covered in UW’s CSE 373 course, but don’t
worry if you haven’t taken that course yet! All you need to know is that
there are different techniques for sorting numbers, these techniques are
given funny names, and one technique may be faster than another
Consider the provided SortRacer.java class (found in the src/main/java
folder). The main method for this program runs two different sorting algorithms
(currently Merge Sort and Quicksort), reporting when each one is finished.
Practice: Run this program using gradle: ./gradlew -q runSorts. Note
that it may take a few seconds for it to build and begin running, and the sorting
itself may take a few seconds!
Of course, it’s not really a “race” at the moment: rather, each sorting algorithm
is run serially (that is, one after another). If we really wanted them to race,
we’d like the algorithms to run concurrently (at the same time).
Computers as a general rule do exactly one thing a time: your central processing
unit (CPU) just adds two number together over and over again, billions of times
a second
• The standard measure for rate (how many times per second) is the hertz
(Hz). So a 2 gigahertz (GHz) processor can do 2 billion operations per
second.
However, we don’t realize that computers do only one thing at a time! This is
because computers are really good at multitasking: they will do a tiny bit of one
task, and then jump over to another task and do a little of that, and then jump
over to another task and do a little of that, and then back to the first task, and
so on.
These “tasks” are divided up into two types: processes and threads. Read
this brief summary of the difference between them.
So by breaking up a program into threads (which are “interwoven”), we can
in effect cause the computer to do two tasks at once. This is especially useful
if one of the “tasks” might take a really long time–rather than blocking the
application, we can let other tasks also make some progress while we’re waiting
for the long task to finish.
Currently the two sorting algorithms run in the same thread, one after another.
You should break them into two different threads that can run concurrently,
letting them actually be able to race!
In Java, we create a Thread by creating a class that implements the Runnable
interface. This represents a class that can be “run” in a separate thread! The
run() method required by the interface acts a bit like the “main” method for
C.1. CONCURRENCY 239
that Thread: when we start the Thread running, that is the method that will
get called.
Practice: Create two new Runnable classes, one for each Sorting
method.
• These should be nested classes (think: should they be static?).
• When each Runnable is run, you should create a new shuffled array of
numbers and then call the appropriate sorting method on that list. Re-
member to print out when you start and finish sorting (just like is currently
done in the main() method).
If we just instantiate the Runnable() and call its run() method, that won’t
actually execute the method on a different thread (remember: an interface is
just a “sign”; we could have called the interface and method whatever we wanted
and it would still compile). Instead, we execute code on a separate thread by
using an instance of the Thread class. This class actually does the work of
running code on a separate thread.
Thread has a constructor that takes in a Runnable instance as a parameter—
you pass an object representing the “code to run” to the Thread object (this is
an example of the Strategy Pattern). You then can actually start the Thread
by calling its .start() method (not the run method!).
Practice: Modify the main() method so you create new Threads to
execute each Runnable Make sure you actually start() the threads!
• Anonymous variables will be useful here; you don’t need to assign a vari-
able name to the Runnable objects or even the Thread objects if you just
use them directly.
Now run your program! Do you see the Threads running at the same time? Try
running the program multiple times and see what kind of differences you get.
• There are some print statements you can uncomment in the Sorting
class if you want to see more concrete evidence of the Threads running
concurrently.
• You are also welcome to try racing different sorting algorithms (you’ll
want to use a smaller list of numbers, particularly for the painfully slow
BubbleSort). You can even race more than two algorithms—just create
additional Threads!
And that’s the basics of creating Threads in Java!