0% found this document useful (0 votes)
223 views241 pages

Android Dev Book

Book for android

Uploaded by

Nithya S
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
0% found this document useful (0 votes)
223 views241 pages

Android Dev Book

Book for android

Uploaded by

Nithya S
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
Download as pdf or txt
You are on page 1/ 241

Android Development: Lecture Notes

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

2 Activities and Logging 25


2.1 Making Activities . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.2 The Activity Lifecycle . . . . . . . . . . . . . . . . . . . . . . . . 26
2.2.1 Overriding the Callback Methods . . . . . . . . . . . . . . 29
2.3 Logging & ADB . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.3.1 Log Methods . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.3.2 Logcat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.4 Basic Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.5 Multiple Activities . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.5.1 Intents and Context . . . . . . . . . . . . . . . . . . . . . 33
2.6 Back & Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.6.1 Up Navigation . . . . . . . . . . . . . . . . . . . . . . . . 36

3 Resources and Layouts 39


3.1 Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.1.1 Alternate Resources . . . . . . . . . . . . . . . . . . . . . 40
3.1.2 XML Details . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.1.3 R . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.2 Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

3
4 CONTENTS

3.2.1 View Properties . . . . . . . . . . . . . . . . . . . . . . . 43


3.2.2 Practice . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
3.3 Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.3.1 LinearLayout . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.3.2 RelativeLayout . . . . . . . . . . . . . . . . . . . . . . . . 46
3.3.3 ConstraintLayout . . . . . . . . . . . . . . . . . . . . . . . 47
3.3.4 Other Layouts . . . . . . . . . . . . . . . . . . . . . . . . 47
3.3.5 Combining and Inflating Layouts . . . . . . . . . . . . . . 48

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

7 Providers and Loaders 81


7.1 Content Providers . . . . . . . . . . . . . . . . . . . . . . . . . . 81
7.2 Cursors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
7.3 Loaders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
7.4 Other Provider Actions . . . . . . . . . . . . . . . . . . . . . . . 86
7.4.1 Adding Words . . . . . . . . . . . . . . . . . . . . . . . . 86

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

10 Files and Permissions 105


10.1 File Storage Locations . . . . . . . .. . . . . . . . . . . . . . . . 105
10.2 Permissions . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . 107
10.3 External Storage . . . . . . . . . . .. . . . . . . . . . . . . . . . 109
10.4 Internal Storage & Cache . . . . . .. . . . . . . . . . . . . . . . 111
10.5 Example: Saving Pictures . . . . . .. . . . . . . . . . . . . . . . 112
10.6 Sharing Files . . . . . . . . . . . . .. . . . . . . . . . . . . . . . 113
10.6.1 Bonus: Sharing with a FileProvider . . . . . . . . . . . 114

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

14 Material Design 147


14.1 Resources: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147

15 Animation and Graphics 149


6 CONTENTS

15.1 Drawing Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . 149


15.1.1 Custom Views . . . . . . . . . . . . . . . . . . . . . . . . 149
15.1.2 SurfaceViews . . . . . . . . . . . . . . . . . . . . . . . . . 152
15.2 Touch and Gestures . . . . . . . . . . . . . . . . . . . . . . . . . 153
15.2.1 Advanced Gestures . . . . . . . . . . . . . . . . . . . . . . 154
15.3 Property Animation . . . . . . . . . . . . . . . . . . . . . . . . . 155

16 Databases and Providers 161


16.1 Review: Providers and Loaders . . . . . . . . . . . . . . . . . . . 161
16.2 SQLite Databases . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
16.3 Implementing ContentProvider . . . . . . . . . . . . . . . . . . 166
16.3.1 URIs and Types . . . . . . . . . . . . . . . . . . . . . . . 167
16.3.2 Query Methods . . . . . . . . . . . . . . . . . . . . . . . . 168

17 Publishing 173
17.1 Signing an App . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
17.1.1 A Release .apk . . . . . . . . . . . . . . . . . . . . . . . . 175

18 Special Topics: Kotlin 177


18.1 Kotlin Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
18.1.1 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
18.1.2 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
18.1.3 Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
18.2 Kotlin for Android . . . . . . . . . . . . . . . . . . . . . . . . . . 184
18.2.1 The Android Extension . . . . . . . . . . . . . . . . . . . 185
18.2.2 Anko . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185

II Additional Topics (Labs) 187


19 Accessibility 189
19.1 Universal Usability . . . . . . . . . . . . . . . . . . . . . . . . . . 189
19.2 Implementing Accessibility . . . . . . . . . . . . . . . . . . . . . . 190
19.2.1 Supporting Vision Impairment . . . . . . . . . . . . . . . 190
19.2.2 Supporting Alternative Inputs . . . . . . . . . . . . . . . 191
19.2.3 Supporting Internationalization . . . . . . . . . . . . . . . 192
19.2.4 Further Testing . . . . . . . . . . . . . . . . . . . . . . . . 194

20 Fragments: ViewPager 195


20.1 Define a SearchFragment . . . . . . . . . . . . . . . . . . . . . . . 195
20.2 Add the ViewPager and Adapter . . . . . . . . . . . . . . . . . . 196
20.3 Add User Interaction . . . . . . . . . . . . . . . . . . . . . . . . . 197

21 Bluetooth 199

22 Maps 203
22.1 Create a Map Activity . . . . . . . . . . . . . . . . . . . . . . . . 203
CONTENTS 7

22.1.1 Getting an API Key . . . . . . . . . . . . . . . . . . . . . 203


22.1.2 The SupportMapFragment . . . . . . . . . . . . . . . . . 204
22.2 Specifying the User Interface . . . . . . . . . . . . . . . . . . . . 205
22.3 Markers and Drawings . . . . . . . . . . . . . . . . . . . . . . . . 205
22.3.1 Drawing Shapes . . . . . . . . . . . . . . . . . . . . . . . 205

23 Styles & Themes 207


23.1 Defining Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
23.1.1 Style Inheritance . . . . . . . . . . . . . . . . . . . . . . . 208
23.1.2 Built-in Styles . . . . . . . . . . . . . . . . . . . . . . . . 210
23.2 Themes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
23.2.1 Material Themes . . . . . . . . . . . . . . . . . . . . . . . 211
23.2.2 Theme Attributes . . . . . . . . . . . . . . . . . . . . . . 211

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

25 Memory Management 217


25.1 Memory Allocation . . . . . . . . . . . . . . . . . . . . . . . . . . 217
25.2 The Memory Monitor . . . . . . . . . . . . . . . . . . . . . . . . 219
25.2.1 Garbage Collection . . . . . . . . . . . . . . . . . . . . . . 220
25.3 Memory Leaks . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220

Appendix 222

A Java Review 223


A.1 Building Apps with Gradle . . . . . . . . . . . . . . . . . . . . . 223
A.2 Class Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
A.3 Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
A.4 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
A.5 Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
A.6 Abstract Methods and Classes . . . . . . . . . . . . . . . . . . . 229
A.7 Generics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
A.8 Nested Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231

B Swing Framework 233


B.1 Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
B.2 Layouts and Composites . . . . . . . . . . . . . . . . . . . . . . . 236

C Threads and HTTP Requests 237


C.1 Concurrency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
C.1.1 An Example: Algorithm Races! . . . . . . . . . . . . . . . 237
C.1.2 Threading the Race . . . . . . . . . . . . . . . . . . . . . 238
C.2 HTTP Requests . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
8 CONTENTS
About this Book

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.

This book is licensed under a Creative Commons


Attribution-NonCommercial-ShareAlike 4.0 International License.

9
10 CONTENTS
Part I

Lectures

11
Chapter 1

Introduction

This course focuses on Android Development. But what is Android?


Android is an operating system. That is, it’s software that connects hardware
to software and provides general services. But more than that, it’s a mobile
specific operating system: an OS designed to work on mobile (read: handheld,
wearable, carry-able) devices.
• Note that the term “Android” also is used to refer to the “platform” (e.g.,
devices that use the OS) as well as the ecosystem that surrounds it. This
includes the device manufacturers who use the platform, and the applica-
tions that can be built and run on this platform. So “Android Develop-
ment” technically means developing applications that run on the specific
OS, it also gets generalized to refer to developing any kind of software
that interacts with the platform.

1.1 Android History


If you’re going to develop systems for Android, it’s good to have some familiarity
with the platform and its history, if only to give you perspective on how and
why the framework is designed the way it is.
• 2003: The platform was originally founded by a start-up “Android Inc.”
which aimed to build a mobile OS operating system (similar to what
Nokia’s Symbian was doing at the time)
• 2005: Android was acquired by Google, who was looking to get into
mobile
• 2007: Google announces the Open Handset Alliance, a group of tech
companies working together to develop “open standards” for mobile plat-
forms. Members included phone manufacturers like HTC, Samsung, and

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!

1.1.1 Android Versions


Android has gone through a large number of “versions” since it’s release:

Date Version Nickname API Level


Sep 2008 1.0 Android 1
Apr 2009 1.5 Cupcake 3
Sep 2009 1.6 Donut 4
Oct 2009 2.0 Eclair 5
May 2010 2.2 Froyo 8
Dec 2010 2.3 Gingerbread 9
1.1. ANDROID HISTORY 15

Date Version Nickname API Level


Feb 2011 3.0 Honeycomb 11
Oct 2011 4.0 Ice Cream Sandwich 14
July 2012 4.1 Jelly Bean 16
Oct 2013 4.4 KitKat 19
Nov 2014 5.0 Lollipop 21
Oct 2015 6.0 Marshmallow 23
Aug 2016 7.0 Nougat 24
Mar 2017 O preview Android O Developer Preview

Each different “version” is nicknamed after a dessert, in alphabetica order. But


as developers, what we care about is the API Level, which indicates what
different programming interfaces (classes and methods) are available to use.
• You can check out an interactive version of the history through Marshmal-
low at https://www.android.com/history/
• For current usage breakdown, see https://developer.android.com/about/
dashboards/
Additionally, Android is an “open source” project released through the “Android
Open Source Project”, or ASOP. You can find the latest version of the operating
system code at https://source.android.com/; it is very worthwhile to actually
dig around in the source code sometimes!
While new versions are released fairly often, this doesn’t mean that all or even
many devices update to the latest version. Instead, users get updated phones
historically by purchasing new devices (every 18m on average in US). Beyond
that, updates—including security updates—have to come through the mobile
carriers, meaning that most devices are never updated beyond the version that
they are purchases with.
• This is a problem from a consumer perspective, particularly in terms of
security! There are some efforts on Google’s part to to work around this
limitation by moving more and more platform services out of the base
operating system into a separate “App” called Google Play Services.
• But what this means for developers is that you can’t expect devices to be
running the latest version of the operating system—the range of versions
you need to support is much greater than even web development!

1.1.2 Legal Battles


When discussing Android history, we would be remiss if we didn’t mention
some of the legal battles surrounding Android. The biggest of these is Oracle
v Google. In a nutshell, Oracle claims that the Java API is copyrighted (that
the method signatures themeselves and how they work are protected), so because
Google uses that API in Android, Google is violating the copyright. In 2012
16 CHAPTER 1. INTRODUCTION

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.

1.2 Android Architecture and Code


Developing Android applications involves interfacing with the Android platform
and framework. Thus you need a high level understanding of the architecture
of the Android platform. See https://source.android.com/devices/ for more
details.
Like so many other systems, the Android platform is built as a layered architec-
ture:
• At it’s base, Android runs on a Linux kernel for interacting with the
device’s processor, memory, etc. Thus an Android device can be seen as
a Linux computer.
• On top of that kernel is the Hardware Abstraction Layer: an interface to
drivers that can programmatically access hardware elements, such as the
camera, disk storage, Wifi antenna, etc.
– These drivers are generally written in C; we won’t interact with them
directly in this course.
• On top of the HAL is the Runtime and Android Framework, which pro-
vides a set of abstraction in the Java language which we all know an love.
1.2. ANDROID ARCHITECTURE AND CODE 17

Figure 1.1: Android Architecture (image from: hub4tech)


18 CHAPTER 1. INTRODUCTION

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.

1.2.1 Programming Languages

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.

1.2.2 Building Apps

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 Development Tools


There are a number of different hardware and software tools you will need to
do Android development:

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

Software needed to develop Android applications includes:


• The Java 7 SDK (not just the JRE!) This is because you’re writing Java
code!
• Gradle or Apache ANT. These are automated build tools—in effect, they
let you specify a single command that will do a bunch of steps at once
(e.g., compile files, dex files, move files, etc). These are how we make the
“build script” that does the 6 build steps listed above.
1.4. HELLO WORLD 21

– 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.

1.4 Hello World


As a final introductory steps, this lecture will walk you through creating and
running a basic App so that you can see what you will actually be working with.
You will need to have Android Studio installed for this to work.
1. Launch Android Studio if you have it (may take a few minutes to open)
2. Start a new project.
• Use your UW NetID in the domain.
• Make a mental note of the project location so you can find your code
later!
• Target: this is the “minimum” SDK you support. We’re going to
target Ice Cream Sandwich (4.0.3, API 15) for most this class, as the
earliest version of Android most our apps will support.
– Note that this is different than the “target SDK”, which is the
version of Android you tested your application against (e.g., what
system did you run it on?) For this course we will be testing on
API 21 (Lollipop); we’ll specify that in a moment.
22 CHAPTER 1. INTRODUCTION

3. Select an Empty Activity


• Activities are “Screens” in your application (things the user can do).
Activities are discussed in more detail in the next lecture.
4. And boom, you have an Android app! Aren’t frameworks lovely?

1.4.1 The Emulator


We can run our app by clicking the “Play” or “Run” button at the top of the
IDE. But we’ll need a device to run the app on, so let’s make an emulator!
The Nexus 5 is a good choice for supporting “older” devices. The new Pixel is
also a reasonable device to test against.
• You’ll want to make sure you create a Lollipop device, using the Google
APIs (so we have special classes available to us), and amost certainly
running on x86 (Intel) hardware
• Make sure that you’ve specified that it accepts keyboard input. You can
always edit this emulator later (Tools > Android > AVD Manager).
After the emulator boots, you can slide to unlock, and there is our app!

1.4.2 Project Contents


So what does our app look like in code? What do we have?
Note that Android Studio by default shows the “Android” view, which orga-
nizes files thematically. If you go to the “Project” view you can see what the
actual file system looks like. In Android view, files are organized as follows:
• app/ folder contains our application
– manifests/ contains the Android Manifest files, which is sort of
like a “config” file for the app
– java/ contains the Java source code for your project. You can find
the MyActivity file in here
– res/ contains resource files used in the app. These are where we’re
going to put layout/appearance information
• Also have the Gradle scripts. There are a lot of these:
– build.gradle: Top-level Gradle build; project-level (for building!)
– app/build.gradle: Gradle build specific to the app use this one
to customize project!. We can change the Target SDK in here!
– proguard-rules.pro: config for release version (minimization, ob-
fuscation, etc).
– gradle.properties: Gradle-specific build settings, shared
– local.properties: settings local to this machine only
– settings.gradle: Gradle-specific build settings, shared
1.4. HELLO WORLD 23

Note that ANT would instead give:


– build.xml: Ant build script integrated with Android SDK
– build.properties: settings used for build across all machines
– local.properties: settings local to this machine only
We’re using Gradle, but it is good to be aware of ANT stuff for legacy
purposes
• res has resource files. These are XML files that specify details of the
app–such as layout.
– res/drawable/: contains graphics (PNG, JPEG, etc)
– res/layout/: contains UI XML layout files
– res/mipmap/: conatins launcher icon files in different resolutions
* Fun fact: MIP stands for “multum in parvo”, which is Latin
for “much in little” (because multiple resolutions of the images
are stored in a single file). “Map” is used because Mipmaps are
normally used for texture mapping.
– res/values/: contains XML definitions for general constants
See also: http://developer.android.com/guide/topics/resources/available-
resources.html, or Lecture 3.
We can also consider what the application code does. While we’ll revisit this in
more detail in the next lecture, it’s useful to start seeing how the framework is
structured:
We’ll start with the MyActivity Java source file. This class extends Activ-
ity (actually it extends a subclass that supports Material Design components),
allowing us making our own customizations to what the app does.
In this class, we override the onCreate() method that is called by the framework
when the Activity starts (see next lecture).
• We call the super method, and then setContentView() to specify what
the content (appearance) of our Activity is. This is passed in a value from
something called R. R is a class that is generated at compile time and
contains constants that are defined by the XML “resource” files! Those
files are converted into Java variables, which we can access through the R
class.
R.layout refers to the “layout” XML resource, so can go there (remember:
inside res/). Opening these XML files they appear in a “design” view. This
view lets you use a graphical system to lay out your application (similar to a
PowerPoint slide).
• However, even as the design view becomes more powerful, using it is still
frowned upon by many developers for historical reasons. It’s often cleaner
to write out the layouts and content in code. This is the same difference
between writing your own HTML and using something like FrontPage
24 CHAPTER 1. INTRODUCTION

or DreamWeaver or Wix to create a page. While those are legitimate


applications, they are less “professional”.
In the code view, we can see the XML: tags, attributes, values. Tags nested
inside one another. The provided XML code defines a layout, and inside that is
a TextView (a View representing some text), which has a value: text! We can
change that and then re-run the app to see it update!
• It’s also possible to define this value in values/strings (e.g., as a con-
stant), then refer to as @string/message. More on this proces later.
Finally, as a fun demonstration, try to set an icon for the App (in Android
Studio, go to: File > New > Image Asset)
Chapter 2

Activities and Logging

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).

2.1 Making Activities


We create our own activities by subclassing (extending) the framework’s Activ-
ity class. We use inheritance to make a specialized type of Activity (similar
to extending JFrame in Swing apps). By extending this class we inherit all of
the methods that are needed to control how the Android OS interacts with the
Activity.
If you look at the default Empty MainActivity, it actually subclasses AppCom-
patActivity, which is a already specialized kind of Activity that provides an
ActionBar (the toolbar at the top of the screen with the name of you app). If
you change the class to just extend Activity, that bar disappears.
To make this change, you will need to import the Activity class! The keyboard
shortcut in Android Studio is alt+return, or you can do it by hand (look up
the package)! You can also set Android Studio to automatically import classes
you use.
There are a number of other built-in Activity subclasses that we could subclass
instead. We’ll mention them as they become relevant. Many on the books have
been deprecated in favor of Fragments, which are sort of like “sub-activities”
that get nested in larger Activities. We’ll talk about Fragments more in a letter
lecture.
Other important point to note: does this activity have a constructor that we
call? No! We never write code that instantiates our Activity (we never call new
MainActivity()). There is no main method in Android. Activities are created
and managed by the Android operating system when the app is launched.

2.2 The Activity Lifecycle


Although we never call a constructor or main, Activities do have an incredibly
well-defined lifecycle—that is, a series of events that occur during usage (e.g.,
when the Activity is created, when it is stopped, etc).
When each of these events occur, Android executes a callback method, similar
to how you call actionPerformed() to react to a “button press” event in Swing.
We can override these methods in order to do special actions (read: run our
own code) when these events occur.
What is the lifecycle?
3 http://developer.android.com/images/activity_lifecycle.png
2.2. THE ACTIVITY LIFECYCLE 27

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

unpredictable, as the “necessity” of an App being open is dependent on


the OS’s resource allocation rules.
The onDestroy() callback can do final app cleanup, but its better to have
such functionality in onPause() or onStop().
Note that apps may not need to use all of these callbacks! For example, if there
is no difference between starting from scratch and resuming from stop, then you
don’t need an onRestart() (since onStart() goes in the middle). Similarly,
onStart() may not be needed if you just use onCreate() and onResume().
But these lifecycles allow for more granularity and the ability to avoid duplicate
code.

2.2.1 Overriding the Callback Methods

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?

2.3 Logging & ADB


In Android, we can’t use System.out.println() because we don’t actually
have a terminal to print to! More specifically, the device (which is where the
application is running) doesn’t have access to standard out (stdout), which is
what Java means by System.out.
30 CHAPTER 2. ACTIVITIES AND LOGGING

• 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.1 Log Methods

Log provides methods that correspond to different level of priority (importance)


of the messages being recorded. From low to high priority:
• Log.v(): VERBOSE output. This is the most detailed, for everyday
messages. This is often the go-to, default level for logging.
Ideally, Log.v() calls should only be compiled into an application during
development, and removed for production versions.
• Log.d(): DEBUG output. This is intended for lower-level, less detailed
messages (but still code-level, that is referring to specific programming
messages).
These messages can be compiled into the code but are removed at runtime
in production builds through Gradle.
• Log.i(): INFO output. This is intended for “high-level” information,
such at the user level (rather than specifics about code)
• Log.w(): WARN output. For warnings
• Log.e(): ERROR output. For errors
• Also if you look at the API… Log.wtf()!
These different levels are used to help “filter out the noise”. So you can look just
at errors, at errors and warnings, at error, warn, and info… all the way down
4 http://developer.android.com/reference/android/util/Log.html
2.3. LOGGING & ADB 31

to seeing everything with verbose. A huge amount of information is logged, so


filtering really helps!
Each Log method takes two Strings as parameters. The second is the mes-
sage to print. The first is a “tag”—a String that’s prepended to the output
which you can search and filter on. This tag is usually the App or Class name
(e.g., “AndroidDemo”, “MainActivity”). A common practice is to declare a TAG
constant you can use throughout the class:
private static final String TAG = "MainActivity";

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

ceptions). It harkens back to printline debugging, which is still a legitimate


debugging process.
Note that Android Studio does have a built-in debugger if you’re comfortable
with such systems.

2.4 Basic Events


Once you can “output” some content (via Log), the next step is to add some
“input” via an interface element: for example, a Button we can click.
In res/layouts/activity_main.xml (the Activity’s layout), add the following
code inside the <android.support.constraint.ConstraintLayout> element,
replacing the current <TextView> element.
<Button
android:id="@+id/my_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start Activity"
/>

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.

2.5 Multiple Activities


The whole point of interfacing with the Activity Lifecycle is handle the fact that
Android applications can have multiple activities and interact with multiple
other applications. In this section we’ll briefly discuss how to include multiple
Activities within an app (in order to sense how the life cycle may affect them).
Note that working with multiple Activities will be discussed in more detail in a
later lecture.
We can easily create a New Activity through Android Studio by using File >
New > Activity. We could also just add a new .java file with the Activity
class in it, but using Android Studio will also provide the onCreate() method
stub as well as a layout resource.
• For practice, make a new Empty Activity called SecondActivity. You
shuld edit this Activity’s layout resource so that the <TextView> displays
an appropriate message.
Importantly, for every Activity we make, an entry gets added to the Manifest
file AndroidManifest.xml. This file acts like the “table of contents” for our ap-
plication, telling the device Operating System information about what our app
looks (that is, what Activities it has) like so that the OS can open appropriate
Activities as needed.
Activities are listed as <activity> elements nested in the <application> ele-
ment. If you inspect the file you will be able to see an element representing the
first MainActivity; that entry’s child elements will be discussed later.
• We can add android:label attributes to these <activity> elements in
order to give the Activities nicer display names (e.g., in the ActionBar).

2.5.1 Intents and Context


In Android, we don’t start new Activities by instantiating them (remember,
we never instantiate Activities!). Instead, we send the operating system a mes-
sage requesting that the Activity perform a particular action (i.e., start up and
display on the screen). These messages are called Intents, and are used to
communicate between app components like Activities. The Intent system al-
lows Activities to communicate, even though they don’t have references to each
other (we can’t just call a method on them).
34 CHAPTER 2. ACTIVITIES AND LOGGING

• 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

• Inside an Activity object (e.g., in a lifecycle callback function), you can


refer to the current Activity using this. And since Activity is a Con-
text, you can also use this to refer to the current Activity context. You’ll
often see Context methods called as undecorated methods (without an ex-
plicit 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.
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 Back & Tasks


We’ve shown that we can have lots of Activities (and of course many more can
exist cross multiple apps), and we are able to move between them by sending
Intents and clicking the “Back” button. But how exactly is that “Back” button
able to keep track of where to go to?
The abstract data type normally associated with “back” or “undo” functionality
is a stack, and that is exactly what Android uses. Every time you start a new
Activity, Android instantiates that object and puts it on the top of a stack.
Then when you hit the back button, that activity is “popped” off the stack and
you’re taken to the Activity that is now at the top.
However, you might have different “sequences” of actions you’re working on:
maybe you start writing an email, and then go to check your Twitter feed
through a different set of Activities. Android breaks up these sequences into
6 http://developer.android.com/images/fundamentals/diagram_backstack.png
36 CHAPTER 2. ACTIVITIES AND LOGGING

Figure 2.2: An example of the Activity stack, from Google6 .

groups called Tasks. A Task is a collection of Activities arranged in a Stack,


and there can be multiple Tasks in the background of your device.
Tasks usually start from the Android “Home Screen”—when you launch an
application, that then starts a new Task. Starting new Activities from that
application will add them to the Stack of the task. If you go back to the Home
Screen, the Task you’re currently on is moved to the background, so the “back”
button won’t let you navigate that Stack.
• It’s useful to think of Tasks as being like different tabs or browsers, with
the “back stack” being the history of web pages visited wthin that tab.
• As a demonstration, try switching to another (built-in) app and then back
to the example app; how does the back button work in each situation?
An important caveat: Tasks are distinct from one another, so you can have
different copies of the same Activity on multiple stacks (e.g., the Camera activity
could be part of both Facebook and Twitter app Tasks if you are on a selfie
binge). It is possible to modify this behavior though, see - Though it is possible
to modify this, see Managing Tasks

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

• Then in the Manifest, add an android:parentActivityName attribute


to the SecondActivity, with a value set to the full class name (including
package and appname!) of your MainActivity. This will let you be able
to use the “back” visual elements (e.g., of the ActionBar) to move back to
the “parent” activity. See Up Navigation for details.
<activity android:name=".SecondActivity"
android:label="Second Activity"
android:parentActivityName="edu.uw.activitydemo.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="edu.uw.activitydemo.MainActivity" />
</activity>

The <meta-data> element is to provide backwards compatibilit for API


level 15 (since the android:parentActivityName is only defined for API
level 16+).
38 CHAPTER 2. ACTIVITIES AND LOGGING
Chapter 3

Resources and Layouts

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

– This is similar to how in web development we may want to have the


same JavaScript from different HTML.
What should be a resource? In general:
• Layouts should always be resources
• UI controls (buttons, etc) should mostly be defined as resources (part of
layouts), though behavior will be defined programmtically (in Java)
• Any graphic images (drawables) should be resources
• Any user-facing strings should be resources
• Style and theming information should be resources
As introduced in Lecture 1, there are a number of different resource types used
in Android, many of which can be found in the res/ folder of a default Android
project, including:
• res/drawable/: contains graphics (PNG, JPEG, etc)
• res/layout/: contains UI XML layout files
• res/mipmap/: conatins launcher icon files in different resolutions
• res/values/: contains XML definitions for general constants
– /strings: short string constants (e.g., labels)
– /colors: color constants
– /styles : constants for style and theme details
– /dimen : dimensional constants (like default margins); not created
by default in Android Studio 2.3+.
The details about these different kinds of resources is a bit scattered throughout
the documentation, but Resource Types1 is a good place to start, as is Providing
Resources.

3.1.1 Alternate Resources


These aren’t the only names for resource folders: as mentioned above, part
of the goal of resources is that they can be localized: changed depending
on the device! You are thus able to specify folders for “alternative” resources
(e.g., special handling for another language, or for low-resolution devices). At
runtime, Android will check the configuration of the device, and try to find
an alternative resource that matches that config. If it it can’t find a relevant
alternative resource, it will fall back to the “default” resource.
There are many different configurations that can be used to influence resources;
see Providing Resources2 . To highlight a few options, you can specify different
resources based on:
• Language and region (e.g., via two-letter ISO codes)
• Screen size(small, normal, medium, large, xlarge)
• Screen orientation (port for portrait, land for landscape)
1 https://developer.android.com/guide/topics/resources/available-resources.html
2 http://developer.android.com/guide/topics/resources/providing-resources.html
3.1. RESOURCES 41

• 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.2 XML Details


Resources are usually defined as XML (which is similar in syntax to HTML).
The strings.xml example used above involves fairly simple elements but more
complexresource is pretty simple, but more complex details can be seen in the
activity_main.xml resource inside layout/.
• Android-specific attributes are namespaced with a android: prefix, to
avoid any potential conflicts (e.g., so we know we’re talking about An-
droid’s text instead of something else).
• We can use the @ symbol to reference one resource from another, following
the schema @[<package_name>:]<resource_type>/<resource_name>
• We can also use the + symbol to create a new resource that we
can refer to; this is a bit like declaring a variable inside an at-
tribute. This is most commonly used with the android:id attribute
(android:id="@+id/identifier"), see below for details.

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

• For most resources, the identifier is defined as an element attribute (id


for specific View elements in layouts, name attribute for values). For more
complex resources such as entire layouts or drawables, the identifier is the
filename (without the XML); hence R.layout.activity_main refers to
the root element of the layout/activity_main.xml file.
• Note that that @ symbol used in the XML goes to the R Java file to look
things up, so follows the same reference syntax.

You can find the generated R.java file inside app/build/generated/source/r/debug/...


(Use the Project Files view in Android Studio).

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 other comment method that utilizes resources will be findView-


ById(int), which is used to reference a View element (e.g., a button)
from the resource in order to call methods on it in Java. This is the same
method used with the Button example in the Activities lecture

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).

3.2.1 View Properties


Before we get into how to group Views, let’s focus on the individual, basic
View classes. As an example, consider the activity_main layout in the lecture
4 http://developer.android.com/reference/android/view/View.html
5 http://developer.android.com/reference/android/view/ViewGroup.html
44 CHAPTER 3. RESOURCES AND LAYOUTS

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.

Do not define Views or View appearances in an Activity’s onCreate() callback,


unless the properties (e.g., content) truly cannot be determined before runtime!
Specify layouts in the XML instead.

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

For example: android:layout_verticalCenter centers the View vertically


within the parent. android:layout_toRightOf places the View to the right
of the View with the given resource id (use an @ reference to refer to the View
by its id):
<TextView
android:id="@+id/first"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="FirstString" />
<TextView
android:id="@+id/second"
android:layout_height="wrap_content"
android:layout_below="@id/first"
android:layout_alignParentLeft="true"
android:text="SecondString" />

(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

ConstraintLayout is a Layout provided as part of an extra support library,


and is what is used by Android Studio’s “Design” tool (and thus is the default
Layout for new layout resources). ConstraintLayout works in a manner con-
ceptually similar to RelativeLayout, in that you specify the location of Views
in relationship to one another. However, ConstraintLayout offers a more pow-
erful set of relationships in the form of constraints, which can be used to create
highly responsive layouts. See the class documentation for more details and
examples of constraints you can add.
The main advantage of ConstraintLayout is that it supports development
through Android Studio’s Design tool. However, since this course is focusing on
implementing the resource XML files rather than using the specific tool (that
may change in a year’s time), we will primarily be using other layouts.

3.3.4 Other Layouts

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

• FrameLayout is a sort of “placeholder” layout that holds a single child


View (a second child will not be shown). You can think of this layout as a
way of adding a simple container to use for padding, etc. It is also highly
useful for situations where the framework requires you to specify a Layout
resource instead of just an individual View.
• GridLayout arranges Views into a Grid. It is similar to LinearLayout, but
places elements into a grid rather than into a line.
Note that this is different than a Grid_View_, which is a scrollable, adapt-
able list (similar to a ListView, which is discussed in the next lecture).
• TableLayout acts like an HTML table: you define TableRow layouts which
can be filled with content. This View is not commonly used.

3.3.5 Combining and Inflating Layouts

It is possible to combine multiple layout resources. This is useful if you want


to dynamically change what Views are included, or to refactor parts of a layout
into different XML files to improve code organization.
As one option, you can statically include XML layouts inside other layouts by
using an <include> element:
<include layout="@layout/sub_layout">

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

Note that we never instantiate the LayoutInflator, we just access an object


that is defined as part of the Activity.
The inflate() method takes a couple of arguments:
• The first parameter is a reference to the resource to inflate (an int saved
in R)
• The second parameter is a ViewGroup to act as the “parent” for this
View—e.g., what layout should the View be inflate inside? This can be
null if there is not yet a layout context; e.g., you wish to inflate the View
but not show it on the screen yet.
• The third (optional) parameter is whether to actually attach the inflated
View to that parent (if not, the parent just provides context and layout
params to use). If not assigning to parent on inflation, you can later attach
3.3. LAYOUTS 49

the View using methods in ViewGroup (e.g., addView(View) similar to


what we’ve done with Swing).
Manually inflating a View works for dynamically loading resources, and we will
often see UI implementation patterns that utilize Inflators.
However, for dynamic View creation it tends to be messy and hard to maintain
(UI work should be specified entirely in the XML, without needing multiple
references to parent and child Views) so it isn’t as common in modern develop-
ment. A much cleaner solution is to use a ViewStub7 . A ViewStub is like an
“on deck” Layout: it is written into the XML, but isn’t actually shown until
you choose to reveal it via Java code. With a ViewStub, Android inflates the
View at runtime, but then removes it from the parent (leaving a “stub” in its
place). When you call inflate() (or setVisible(View.VISIBLE)) on that
stub, it is reattached to the View tree and displayed:
<!-- XML -->
<ViewStub android:id="@+id/stub"
android:inflatedId="@+id/subTree"
android:layout="@layout/mySubTree"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

//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" />

The callback method is declared in the Java code as taking in a View


parameter (which will be a reference to whatever View caused the event
to occur) and returning void:
public void handleButtonClick(View view) { }

• We will utilize a mix of both of these strategies (defining callbacks in both


the Java and the XML) in this class.
Author’s Opinion: It is arguable about which approach is “better”. Spec-
4.2. LISTVIEWS AND ADAPTERS 53

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).

In addition to listening for events, it is possible to call methods directly on


referenced Views to access their state. In addition to generic View methods
such as isVisible() or hasFocus(), it is possible to inquire directly about
the state of the input provided. For example, the isChecked() method returns
whether or not a checkbox is ticked.
This is also a good way of getting access to inputted content from the Java
Code. For example, call getText() on an EditText control in order to fetch
the contents of that View.
• For practice, try to log out the contents of the included EditText control
when the Button is pressed!
Between listening for events and querying for state, we can fully interact with
input controls. Check the official documentation for more details on how to use
specific individual widgets.

4.2 ListViews and Adapters

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" />

To make it look better, you can specify android:minHeight="?android:attr/listPreferredItemHeight"


(using the framework’s preferred height for lists), and some cen-
ter_vertical gravity. The android:lines property is also useful if
you need more space.
3. Finally, we specify the controller: the ListView itself. Add that item to
the Activity’s Layout resource (practice: what should its dimensions be?)
To finish the controller ListView, we ned to provide it with an Adapter2 which
will connect the model to the view. The Adapter does the “translation” work
between model and view, performing a mapping from data types (e.g., a String)
and View types (e.g., a TextView).
Specifically, we will use an ArrayAdapter, which is one of the simplest Adapters
to use (and because we have an array of data!) An ArrayAdapter creates Views
by calling .toString() on each item in the array, and setting that String as
the content of a TextView!
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
R.layout.list_item_layout, R.layout.list_item_txtView, myStringArray);

• 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);

And that’s all that is needed to create a scrollable list of data!


Each item in this list is selectable (can have an onClick callback). This allows
us to click on any item in order to (for example) view more details about the item.
2 https://developer.android.com/reference/android/widget/Adapter.html
56 CHAPTER 4. INTERACTIVE VIEWS

Utilize the AdapterView#setOnItemClickListener(OnItemClickListener)


function to register the callback.
• The postion parameter in the onItemClick() callback is the index of the
item which was clicked. Use (Type)parent.getItemAtPosition(position)
to access the data value associated with that View.
Additionally, each item does have an individual layout, so we can customize
these appearances (e.g., if our layout also wanted to include pictures). See this
tutorial for an example on making a custom adapter to fill in multiple Views
with data from a list!
And remember, a GridView is basically the same thing (in fact, we can just
change over that and have everything work, if we use polymorphism!)

4.3 Network Data


In the previous section we created a ListView utilizing an adapter to display a
list of Strings. But Appendix C provides an implementation for fetching data
from the Internet which gave us a list of Strings. Can we combine these? You
betchya!
The lecture code provides a MovieDownloader class containing the exact same
networking code utilized in the Appendix. We can then simply specify that the
model String[] should be the result of the downloadMovieData() method,
rather than manually created with a loop.
If you test this code, you’ll notice that it doesn’t work! The program will crash
with a NetworkOnMainThreadException.
Android apps run by default on the Main Thread (also called the UI Thread).
This thread is in charge of all user interactions—handling button presses, scrolls,
drags, etc.—but also UI output like drawing and displaying text! See Android
Threads for more details.
• A thread is a piece of a program that is independently scheduled by the
processor. Computers do exactly one thing at a time, but make it look
like they are doing lots of tasks simultaneously by switching between them
(i.e., between processes) really fast. Threads are a way that we can break
up a single application or process into little “sub-process” that can be run
simultaneously—by switching back and forth periodically so everyone has
a chance to work
Within a single thread, all method calls are synchronous—that is, one has to
finish before the next occurs. You can’t get to step 4 without finishing step 3.
With an event-driven system like Android, each method call is fast enough that
this isn’t a problem (you’re done handling one click by the time the next occurs).
But long, drawn-out processes like network access (or processing bitmaps, or
4.3. NETWORK DATA 57

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

3. onProgressUpdate() can be indirectly called on the UI thread if we want


to update our progress (e.g., update a progress bar). Note that UI changes
can only be made on the UI thread!
4. onPostExecute(Result) is called on the UI thread to process any task
results, which are passed as parameters to this method when doInBack-
ground is finished.

The doInBackground() is what occurs on the background thread (and is the


heart of the task), so we put our network accessing method call in there.
We can then instantiate a new ASyncTask object in the Activity’s onCreate()
callback, and call ASyncTask#execute(params) to start the task running on
its own thread.
If you test this code, you’ll notice that it still doesn’t work! The program will
crash with a SecurityException.
As a security feature, Android apps by default have very limited access to the
overall operating system (e.g., to do anything other than show a layout). An
app can’t use the Internet (which might consume people’s data plans!) without
explicit permission from the user. This permission is given by the user at install
time.
In order to get permission, the app needs to ask for it (“Mother may I…”). We
do that by declaring that the app uses the Internet in the Manifest.xml file
(which has all the details of our app!)
<uses-permission android:name="android.permission.INTERNET"/>
<!-- put this ABOVE the <application> tag -->

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

This lecture discusses Android Fragments. A Fragment is “a behavior or a


portion of user interface in Activity.” You can think of them as “mini-activities”
or “sub-activities”. Fragments are designed to be reusable and composable,
so you can mix and match them within a single screen of a user interface.
While XML resource provide reusable and composable views, Fragments pro-
vide reusable and composable controllers. Fragments allow us to make re-usable
pieces of Activities that can have their own layouts, data models, event callbacks,
etc.
This lecture references code found at https://github.com/info448-s17/lecture05-
fragments. Note that this code builds upon the example developed in Lecture
4.
Fragments were introduced in API 11 (Honeycomb), which provided the first
“tablet” version of Android. Fragments were designed to provide a UI component
that would allow for side-by-side activity displays appropriate to larger screens.
Instead of needing to navigate between two related views (particularly for this
“master and detail” setup), the user can see both views within the same Ac-
tivity… but those “views” could also be easily split between two Activities for
smaller screens, because their required controller logic has been isolated into a
Fragment.
Fragments are intended to be modular, reusable components. They should
not depend on the Activity they are inside, so that you can be flexible about
when and where they are displayed!
Although Fragments are like “mini-Activities”, they are always embedded inside
an Activity; they cannot exist independently. While it’s possible to have
Fragments that are not visible or that don’t have a UI, they still are part of an
Activity. Because of this, a Fragment’s lifecycle is directly tied to its containing
1 https://developer.android.com/images/fundamentals/fragments.png

61
62 CHAPTER 5. FRAGMENTS

Figure 5.1: Fragment example, from Google1

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

Figure 5.2: Fragment lifecycle state diagram,from Google2


64 CHAPTER 5. FRAGMENTS

• onActivityCreated(): called when the containing Activity’s onCre-


ate() method has returned, and thus indicates that the Activity is fully
created. This is useful for retained Fragments.
This callback has no mirror!

5.1 Creating a Fragment


In order to illustrate how to make a Fragment, we will refactor the MainAc-
tivity to use Fragments for displaying the list of movies. This will help to
illustrate the relationship between Activities and Fragments.
To create a Fragment, you subclass the Fragment class. Let’s make one called
MovieFragment (in the MovieFragment.java file). You can use Android Studio
to do this work: via the File > New > Fragment > Fragment (blank) menu
option. (DO NOT select any of the other options for in the wizard for now;
they provide template code that can distract from the core principles).
There are two versions of the Fragment class: one in the framework’s an-
droid.app package and one in the android.support.v4 package. The later
package refers to the Support Library. These are libraries of classes designed to
make Android applications backwards compatible: for example, Fragment and
its related classes came out in API 11 so aren’t in the android.app package for
earlier devices. By including the support library, we can include those classes
as well!
• Support libraries also include additional convenience and helper classes
that are not part of the core Android package. These include interface
elements (e.g., ConstraintLayout, RecyclerView, or ViewPager) and
accessibility classes. See the features list for details. Thus it is often
useful to include and utilize support library versions of classes so that you
don’t need to “roll your own” versions of these convenience classes.
• The main disadvantage to using support libraries is that they need to be
included in your application, so will make the final .apk file larger (and
may potentially require workarounds for method count limitations). You
will also run into problems if you try and mix and match versions of the
classes (e.g., from different versions of the support library). But as always,
you should avoid premature optimization. Thus in this course you should
default to using the support library version of a class when given a choice!
After we’ve created the MovieFragment Java file, we’ll want to specify a layout
for that Fragment (so it can be shown on the screen). As part of using the New
Fragment Wizard we were provided with a fragment_movie layout that we can
use.
• Since we want the Movie list to live in that Fragment, we can move (copy)
the View definitions from activity_main into fragment_movie.
5.1. CREATING A FRAGMENT 65

• We will then adjust activity_main so that it instead contains an empty


FrameLayout. This will act as a simple “container” for our Fragment
(similar to an empty <div> in HTML). Be sure to give it an id so we can
refer to it later!.
It is possible to include the Fragment directly through the XML, using the
XML to instantiate the Fragment (the same way that we have the XML in-
stantiate Buttons). We do this by specifying a <fragment> element, with a
android:name attribute assigned a reference to the Fragment class:
<fragment
android:id="@+id/frag_movie"
android:name="edu.uw.fragmentdemo.MovieFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

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);

• Note that the inflater object we are calling inflate() on is passed as


a parameter to the onCreateView() callback. The parameters to the in-
flate() method are: the layout to inflate, the ViewGroup (container)
into which the layout should be inflated (also passed as a parameter to the
callback), and whether ot not to “attach” the inflated layout to the con-
tainer (false in this case because the Fragment system already handles
the attachment, so the inflate method doesn’t need to). The onCreate-
View() callback must return the inflated root view, so that the system
can perform this attachment.
With the Fragment’s layout defined, we can start moving functionality from the
66 CHAPTER 5. FRAGMENTS

Activity into the Fragment.


• The the background ASyncTask can be moved over directly, so that it
belongs to the Fragment instead of the Activity.
• The adapter declaration will need to be moved as well.
• The UI setup (including initializing the Adapter) will be moved from the
Activity’s onCreate() to the Fragment’s onCreateView(). However, you
will need to make a few changes during this refactoring:
– The findViewById() method is a method of the Activity class,
and thus can’t be called on an implicit this inside the Fragment.
Instead, the method can be called on the root view, searching just
that View and its children.
– The Adapter’s constructor requires a Context as its first parameter;
while an Activity is a Context, a Fragment is not—Fragments op-
erate in the Context of their containing Activity! Fragments can refer
to the Activity that they are inside (and the Context it represents)
by using the getActivity() method. Note that this method is used
primarily for getting a reference to a Context, not for arbitrart com-
muication withe Activity (see below for details)

5.1.1 Activity-to-Fragment Communication

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.

5.2 Dynamic Fragments


The real benefit from encapsulating behavior in a Fragment is to be able to
support multiple Fragments within a single Activity. For example, in the the
archetypal “master/detail” navgiation flow, one screen (Fragment) holds the
“master” (list) and another screen (Fragment) holds details about a particular
item. This is a very common navigation pattern for Android apps, and can be
seen in most email or news apps.
• On large screens, Fragments allow these two screens to be placed side by
side!
In this section, we will continue to refine the Movie app so that when the user
clicks on a Movie in the list, the app shows a screen (Fragment) with details
about the selected movie.

5.2.1 Instantiating Fragments

To do this, we will need to instantiate the Fragments dynamically (in Java


code), rather than statically in the XML using the <fragment> element. This
is because we need to be able to dynamically change which Fragment is currently
being shown, which is not possibly for Fragments that are “hard-coded” in the
XML.
68 CHAPTER 5. FRAGMENTS

Unlike Activities, Fragments (such as MovieFragment) do have constructor


methods that can be called—in fact, Android requires that every Fragment
include a default (no-argument) constructor that is called when Fragments are
created by the system. While we are able to call this constructor, it is considered
best practice to not call this constructor directly when you want to instantiate
a Fragment, and to in fact leave the method empty. This is because we do
not have full control over when the constructor is executed: the Android system
may call the no-argument constructor whenever it needs to recreate the Activity
(or just the Fragment), which can happen at arbitrary times. Since only this
default constructor is called, we can’t add an additional constructor with any
arguments we may want the Fragment to have (e.g., the searchTerm)… and
thus it’s best to not use it at all.

Instead, we specify a simple factory method (by convention called newIn-


stance()) which is able to “create” an instance of the Fragment for us. This
factory method can take as many arguments as we want, and then does the
work of passing these arguments into the Fragment instantiated with the de-
fault constructor:
public static MyFragment newInstance(String argument) {
MyFragment fragment = new MyFragment(); //instantiate the Fragment
Bundle args = new Bundle(); //an (empty) Bundle for the arguments
args.putString(ARG_PARAM_KEY, argument); //add the argument to the Bundle
fragment.setArguments(args); //add the Bundle to the Fragment
return fragment; //return the Fragment
}

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

MyFragment fragment = MyFragment.newInstance("My Argument");

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();

• The third argument for the add() method is a “tag” we apply to


the Fragment beign added. This gives it a name that we can use to
find a reference to this Fragment later if we want (via FragmentMan-
ager#findFragmentByTag(tag)). Alternatively, we can save a reference
3 https://developer.android.com/reference/android/support/v4/app/

FragmentTransaction.html
70 CHAPTER 5. FRAGMENTS

to the Fragment as an instance variable; this is faster but more memory


intensive (and can cause possible leaks, since the reference keeps the
Fragment from being reclaimed by the system).

5.2.3 Inter-Fragment Communication

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
}
}

• Then when an action occurs in the Fragment (e.g., a movie is selected),


you call the interface’s method on the callback reference.
• Finally, you will need to make sure that the Activity implements this
callback. Remember that a class can implement multiple interfaces!
In the Activity’s implementation of the interface, you can handle the in-
formation provided. For example, use the FragmentManager to create a
replace() transaction to load a new DetailFragment for the appropriate
data.
In the end, this will allow you to have one Fragment cause the application to
switch to another!
This is not the only way for Fragments to communicate. It is also possible to
have a Fragment send an Intent to the Activity, who then responds to that as
appropriate. But using the Intent system is more resource-intensive than using
interfaces.

5.2.4 The Back Stack

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.

6.1 The Action Bar


Let’s start one of the most prominent visual components in the default app:
the App Bar or Action Bar. This acts as the sort of “header” for your app,
providing a dedicated space for navigation and interaction (e.g., through menus).
The ActionBar1 is a specific type of Toolbar that is most frequenly used as the
App Bar, offering a particular “look and feel” common to Android applications.
While the AppCompatActivity used throughout this course automatically pro-
vides an Action Bar for the app, it is also possible to add it directly (such as
if you are using a different Activity subclass). To add your own Action Bar,
you specify a theme that does not include an ActionBar, and then include an
<android.support.v7.window.Toolbar> element inside your layout wherever
you want the toolbar to go. See Setting up the App Bar for details. This will
also allow you to put the Toolbar anywhere in the application’s layout (e.g., if
you want it to be stuck to the bottom).
• To see this in action, change the android:theme attribute of the <appli-
cation> element in the Manifest to "@style/Theme.AppCompat.Light.NoActionBar".
We’ll discuss this process in more detail when we talk about Themes and
Styles.
1 http://developer.android.com/reference/android/support/v7/app/ActionBar.html

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

prioritizing can be useful if you want to add suggestions about whether


Fragment options should come before or after the Actiity options.
See the Menu resources guide3 for the full list of options!
It is possible to include one level of sub-menus (a <menu> element inside an
<item> element). Menu items can also be grouped together by placing them
inside of a <group> element. All items in a group will be shown or hidden
together, and can be further ordered within that group. Grouped icons can also
be made checkable.
In order to show the menu in the running application, we need to tell the Action
Bar which menu resource it should use (there may be a lot of resources). To
do this, we override the onCreateOptionsMenu() callback in the Activity or
Fragment, and then use the component’s MenuInflater object to expand the
menu:
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_menu, menu); //inflate into this menu
return true;
}

• This procedure is similar in concept to how a Fragment’s onViewCre-


ated() method would inflate the Fragment into the Activity. In this case,
the Menu is being inflated into the Action Bar.
We can respond to the menu items being selected by overriding the onOp-
tionsItemSelected() callback. By convention, we use a switch on the
item.getItemId() to determine what item was selected, and then act
accordingly.
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
case R.id.menu_item1 :
//do thing;
return true;
default:
return super.onOptionsItemSelected(item);
}
}

• 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.

6.2.1 Context Menus

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
}
});

AlertDialog dialog = builder.create();


dialog.show();

An important part of learning to develop Android applications is being able


to read the API to discover effective options. For example, can you read the
AlertDialog.Builder API and determine how to add a “cancel” button to the
4 https://developer.android.com/guide/topics/ui/dialogs.html
5 https://developer.android.com/reference/android/support/v7/app/AlertDialog.html
78 CHAPTER 6. UI COMPONENTS

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 {

public static HelloDialogFragment newInstance() {


Bundle args = new Bundle();
HelloDialogFragment fragment = new HelloDialogFragment();
fragment.setArguments(args);
return fragment;
}

public Dialog onCreateDialog(Bundle savedInstanceState) {


AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
//...
6.4. TOASTS 79

AlertDialog dialog = builder.create();


return dialog;
}
}

Finally, we can actually show this DialogFragment by instantiating it (remem-


ber to use a newInstance() factory method!) and then calling the show()
method on it to make it show as a Dialog. The show() method takes in a
FragmentManager used to manage this transaction. By using a DialogFrag-
ment, it is possible to change the device configuration (rotate the phone) and
the Dialog is retained.
Here’s the other neat trick: a DialogFragment is just a Fragment. That means
we can use it anywhere we normally used Fragments… including embedding
them into layouts! For example if you made the MoviesFragment subclass
DialogFragment instead of Fragment, it would be able to be used in the exact
same as before. It’s still a Fragment, just with extra features—one of which is
a show() method that will show it as a Dialog!
• Use setStyle(DialogFragment.STYLE_NO_TITLE, android.R.style.Theme_Holo_Light_Dialog)
to make the Fragment look a little more like a dialog.
The truth is that Dialogs are not very commonly used in Android (compare to
other GU systems). Apps are more likely to just dynamically change the Frag-
ment or Activity being shown, rather than interrupt the user flow by creating
a pop-up modal. And 80% of the Dialogs that are used are AlertDialogs. Nev-
ertheless, it is worth being familiar with this process and the patterns it draws
upon!

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

int duration = Toast.LENGTH_SHORT;

//use factory method instead of constructor


Toast toast = Toast.makeText(context, text, duration);
toast.show();

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

Providers and Loaders

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.

7.1 Content Providers

Consider the WordListFragment utilized by the example code (though these


concepts apply to any Fragment or Activity). This Fragment includes a
ListView that shows a list of words. Recall that a ListView utilizes the
model-view-controller architecture… and in this case, the “model” (data) is
a hard-coded list of array of words. But there are other lists of words as well!
Entire databases of words! Previous lectures have discussed how to use network
requests to access online data APIs, but there are also databases (of words no
less) built into your Android phone.
For example, Android keeps track of the list of the spellings of “non-standard”
words in what is called the User Dictionary. You can view this list on the de-
vice at Settings > Language & Input > Personal Dictionary. You can
even use this Settings interface to add new words to the dictionary (e.g., “em-
biggen”, “cromulent”, “fleek”).
Note that the User Dictionary keeps track of a database of words. You can
think of this database as being like a single SQL table: it’s a set of entries

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

provides methods for accessing the data in a provider (represented as a Con-


tentProvider object). Each Context has a singleton ContentResolver, which is
accessed via the getContentResolver() method (note that for a Fragment, the
Context is the containing Activity). The ContentResolver’s methods support
the basic CRUD operations: insert(), query(), update(), and delete().
ContentResolver methods take multiple parameters, supporting the different
options available in a generic SQL query. For example, consider the the query()
method:
getContentResolver().query(
uri, // The content URI
projection, // The an array of columns to return for each row
selectionClause // Selection criteria (as an SQL WHERE clause)
selectionArgs, // An array of values that can be injected into the selection clause
sortOrder); // The sort order for the returned rows (as an SQL ORDER BY clause)

• This is basically a wrapper around an SQL SELECT statement!


The projection is a String[] of all the “columns” (attributes) we want to
fetch from the data source. This is what you’d put after SELECT in SQL. (Note
we can pass in null to represent SELECT *, but that’s inefficient—better to give
a list of everything).
• We can see what column names are available for the User Dictionary in
UserDictionary.Words. Again, these are defined as constants!
• Be sure to always select the _ID primary key: it will be needed later!
The other parameters can be used to customize the SELECT statement. THe
“selection” (WHERE) clause needs to parameters: the second are values that will be
escaped against SQL injection attacks. Passing null for any of these parameters
will cause the clause to be ignored:
ContentResolver resolver = getActivity().getContentResolver();
String[] projection = new String[] { UserDictionary.Words.WORD, UserDictionary.Words._ID };
resolver.query(UserDictionary.Words.CONTENT_URI, projection, null, null, null);

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

To use a CursorLoader, we need to specify that our Fragment implements the


LoaderManager.LoaderCallback<Cursor> interface—basically saying that
this fragment can react to Loader events.

• Loaders need to work with Fragments, unless the Activity subclasses


FragmentActivity (as AppCompatActivity does) and thereby provides
the “Fragment” capabilities needed to use a Loader. So we can use Load-
ers in our Activities or Fragments.

We will need to fill in the interfaces callbacks functions in order to use the
CursorLoader:

• In onCreateLoader() we specify what the Loader should do. Here we


would instantiate and return a new CursorLoader(...) that queries the
ContentProvider. This looks a lot like the .query() method we wrote
earlier, but will run on a background thread!

• In the onLoadFinished() callback, we can swap() the Cursor into our


SimpleCursorAdapter in order to feed that model data into our controller
(for display in the view). See the guide for more details.

• 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

7.4 Other Provider Actions

7.4.1 Adding Words


To insert a new Word into the ContentProvider, we just call a different method
on the ContentResolver:
//Example from Google:
ContentValues mNewValues = new ContentValues();
mNewValues.put(UserDictionary.Words.APP_ID, "edu.uw.loaderdemo");
mNewValues.put(UserDictionary.Words.LOCALE, "en_US");
mNewValues.put(UserDictionary.Words.WORD, word);
mNewValues.put(UserDictionary.Words.FREQUENCY, "100");

Uri mNewUri = getContentResolver().insert(


UserDictionary.Words.CONTENT_URI, // the user dictionary content URI
mNewValues // the values to insert
);

• Note that we specify the “details” of the Word in a ContentValues object,


which is a HashMap almost exactly like a Bundle (but only supports values
that work with ContentProviders)
Chapter 8

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

8.1 Intents for Another Activity (Explicit)

The most basic kind of Intent is an Intent sent to a specific Activity/Context,


such as for telling that Activity to open.
An Intent1 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 constructors, but the one we’ll start
with looks like:
// context, target
Intent intent = new Intent(MainActivity.this, SecondActivity.class);

• 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");

So we can have Activities communicate, and even share information between


them! Yay!

8.2 Intents for Another App (Implicit)


We can send Intents to our own Activities, but we can even address them to
other Apps. When calling on other apps, we usually use Implicit Intents.
• This is a little bit like letters that have weird addresses2 , but still get
delivered. “For that guy at the end of the block with the red mailbox.”
An Implicit Intent includes an Action and some Data. The Action says what
the target should do upon receiving the intent (a Command), and the Data
gives more detail about what to run that action on.
• Actions can be things like ACTION_VIEW to view some data, or AC-
TION_PICK to choose an item from a list. See a full list under “Standard
Action Activities”.
• ACTION_MAIN is the most common (just start the Activity as if it were a
“main” launching point). So when we don’t specify anything else, this is
used!
• Data gives detail about what to do with the action (e.g., the Uri to VIEW
or the Contact to DIAL).
• Extras then support this data!
For example, if we specify a DIAL action, then we’re saying that we want our
Intent to be delivered to an App that is capable of dialing a telephone number.
- If there is more than one app that supports this action, the user will pick
one! This is key: we’re not saying exactly what app to use, just what kind of
functionality we need to be supported! It’s a kind of abstraction!
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:206-685-1622"));
if (intent.resolveActivity(getPackageManager()) != null) {

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!).

8.3 Intents for a Response


We’ve been using intents to start Activities, but what if we’d like to get a result
back from the Activity? That is, what if we want to look up a Contact or take
a Picture, and then be able to use the Contact or show the Picture?
To do this, we’re going to create Intents in the same way, but use a different
method to launch them: startActivityForResult(). This will launch the
resolved Activity. But once that Action is finished, the launched Activity will
send another Intent back to us, which we can then react to in order to handle
the result.
• This is a bit like including an “RSVP” note in a letter!
For fun, let’s do it with the Camera–we’ll launch the Camera to take a picture,
and then get the picture and show it in an ImageView we have.
• Note that your Emulator will need to have Camera emulation on!
• See Taking Photos Simply for walkthrough.
In the activity, we can specify an intent that uses the MediaStore.ACTION_IMAGE_CAPTURE
action (the action for “take a still picture and return it”).
• The “request code” is used to distinguish this intent from others we may
send (kind of like a “tag”).
• Note that we could pass an Extra for where we want to save the large
picture file to. However, we’re going to leave that off and just work with
the thumbnail for this demonstration. See the guide4 for details; if time
we can walk through it!
static final int REQUEST_IMAGE_CAPTURE = 1;

private void dispatchTakePictureIntent() {

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

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);


if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}

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);
}
}

8.4 Listening for Intents


We’re able to send implicit Intents that can be heard by other Apps, but what
if we wanted to receive implicit Intents ourselves? What if we want to be able
to handle phone dialing?!
In order to receive an implicit Intent, we need to declare that our Activity is
able to handle that request. Since we’re specifying an aspect of our application,
we’ll do this in the Manifest using what is called an <intent-filter>.
• The idea is that we’re “hearing” all the intents, and we’re “filtering” for
the ones that are relevant to us. Like sorting out the junk mail.
An <intent-filter> tag is nested inside the element that it applies to (e.g.,
the <activity>). In fact, you can see there is already one there: that responds
to the MAIN action sent with the LAUNCHER category (meaning that it responds
to intents from the app launcher).
Similarly, we can specify three “parts” of the filter:
• a <action android:name="action"> filter, which describes the Action
we can respond to.
92 CHAPTER 8. INTENTS

• 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>

You can see many more examples in the Intent documentation.

8.5 Broadcasts and Receivers


There is one other kind of Intent I want to talk about: Broadcasts. A broad-
cast is a message that any app can receive. Unlike Explicit and Implicit Intents,
broadcasts are heard by the entire system–anything you “shout” with a broad-
cast is publicly available (security concerns!)
• Mass mailings question mark?
Other than who receives them, broadcasts work the same as normal implicit
intents! We create an Intent with an Action and Data (and Category and
Extras…). But instead of using the startActivity() method, we use the
sendBroadcast() method. That intent can now be heard by all Activities
on the phone,
• We’ll skip a demo for time and motivation… we’ll generate broadcasts later
in the course.
But more common than sending broadcasts will be receiving broadcasts; that is,
we want to listen and respond to System broadcasts that are produced (things
like power events, wifi status, etc). Or more germane to this week’s homework–
to incoming text messages!!
8.5. BROADCASTS AND RECEIVERS 93

We can receive broadcasts by using a BroadcastReceiver. This is a base class


that is used by an class that can receive broadcast Intents. We subclass it
and implement the onReceive(Context, Intent) callback in order to handle
when broadcasts are received.
public void onReceive(Context context, Intent intent)
{
Log.v("TAG", "received! "+intent.toString());
else if(intent.getAction() == Intent.ACTION_BATTERY_LOW){
Toast.makeText(context, "Battery is low!", Toast.LENGTH_SHORT).show();
}
}

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);

• We’re dynamically declaring an intent-filter as well! This can be used not


94 CHAPTER 8. INTENTS

just for BroadcastReceivers, but Activities too.

8.6 An Example: SMS


One specific use of Intents is when working with text messages (SMS, Short
Messaging Service, the most popular form of data communication in the world).
While it is possile to fetch a list of messages usin a ContentProvider, it is also
possible to send SMS as well. This will also let us show off one more type of
Intent.

• 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

We will need permission: <uses-permission android:name="android.permission.SEND_SMS"


/>

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

public static final String ACTION_SMS_STATUS = "edu.uw.intentdemo.ACTION_SMS_STATUS";


...
Intent intent = new Intent(ACTION_SMS_STATUS);
PendingIntent pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, intent, 0);

smsManager.sendTextMessage("5554", null, "This is a test message!", pendingIntent, null);

We’re doing a couple of steps here:


• We’re defining out own custom Action. It’s just a String, but name-
spaced to avoid conflicts
• We then create an implicit intent for this action
• And then create a PendingIntent. We’re using the getBroadcast()
method to specify that the intent should be sent via a Broadcast (c.f.
getActivity() for startActivity()).
• First param is content that should send the intent, then a request code
(e.g., for result callbacks if we wanted), then the Intent, and finally any
extra flags (none for now).
We can then have our BroadcastReceiver respond to this Intent just like any
other one!
if(intent.getAction() == MainActivity.ACTION_SMS_STATUS) {
if (getResultCode() == Activity.RESULT_OK) {
Toast.makeText(context, "Message sent!", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(context, "Error sending message", Toast.LENGTH_SHORT).show();
}
}

• Don’t forget to add our custom intent to the <intent-filter>!


We’ll see more with PendingIntents in the next chapter when we talk about
notifications.

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

How to use it:


• We’re going to add another item to our menu’s XML. This will look like
most items, except it will have an extra field app:actionProviderClass
<item
android:id="@+id/menu_item_share"
android:title="Share"
app:showAsAction="ifRoom"
app:actionProviderClass="android.support.v7.widget.ShareActionProvider"
/>

• We’ll then add the item to our menu in onCreateOptionsMenu()


MenuItem item = menu.findItem(R.id.menu_item_share);
mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(it

Intent intent = new Intent(Intent.ACTION_DIAL);


intent.setData(Uri.parse("tel:206-685-1622"));

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

This lecture discusses how to include additional user interface components in an


Android application: namely Notifications1 and Settings Menus. As before,
this lecture aims to provide exposure rather than complete coverage to these
concepts; for more options and examples, see the official Android documenta-
tion.
This lecture references code found at https://github.com/info448-s17/lecture09-
notifications-settings.

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

The example Notification is also using the a TaskStackBuilder to construct


an “artificial” backstack. This is used to specify, for example, that if the user
clicks on the Notification and jumps to a “detail” view (say), they can still hit
the back button to return to the “master” view, as if they had navigated to the
“detail” view following a normal application phone.
• We build this backstack not just with methods, but by integrating with the
“parent-child” relationship we’ve otherwise set up between Activities. In
the Manifest, we had specified that SecondActivity's parent is Main-
Activity. This is what gave us the nice back button in the ActionBar.
These sequence of parentActivityName attributes form a hierarchy that
will be the “back navigation hierarchy.” We add the “endpoint” of the hier-
archy to the builder using addParentStack(MyResultActivity.class),
and then finally put the Intent we actually want to use “on top” of the
stack with addNextIntent(resultIntent).
The resultIntent is not the PendingIntent… yet. While we could
define a PendingIntent manually, the example uses the TaskStack-
Builder#getPendingIntent() method to build an appropriate PendingIn-
tent object.
• Pass it an ID to refer to that request (like we’ve done when sending Intents
for Results), and a flag PendingIntent.FLAG_CURRENT_UPDATE so that
if we re-issue the PendingIntent it update instead of replace the pending
Intent.
• We can then assign that PendingIntent to the Notification builder (with
setContentIntent()).
Finally, we can use the NotificationManager (similar to the FragmentMan-
ager, SmsManager, etc.) to fetch the notification service (the “application” that
handles all the notifications for the OS). We tell this manager to actually issue
the built Notification object.
• We also pass the notify() method an ID number to refer to the particular
Notification (not the PendingIntent, but the Notification). Again, this will
allow us to refer to and update that Notification.
This allows us to have working Notifications! We can click the button to launch
a Notification, and then click on the Notification to be taken to our app, which
has a working back stack!
We can also update this notification later, and it’s really straightforward: we
simply re-issue a Notification with the same ID number, and it will “replace”
the previous one!
• For example, we can have our text be based on some instance variable,
and have the Notification track the number of clicks!
You may notice that this notification doesn’t “pop up” in a way we might expect.
This is because its priority isn’t high enough (it needs to be Notification-
Compat.PRIORITY_HIGH or higher) and because it doesn’t use either sound or
100 CHAPTER 9. UI COMPONENTS II

vibration (it needs to be really important to get a heads-up pop).


• We can make the Notification vibrate by using the setVibrate() method,
passing it an array of times (in milliseconds) at which to turn vibration
on and off.
• Pattern is [delay, vibrate, sleep, vibrate, sleep, ...]
• We can also assign a default sound with (e.g.,) builder.setSound(Settings.System.DEFAULT_NO
• See the design guide for best practices on priority.
As always, there are a number of other pieces/details we can specify, but I leave
those to you to look up in the documentation.
As a focus on development, this lecture references but does not discuss the UI
Design guidelines: e.g., what kind of text should you put in your Notification?
When should you choose to use a notification? Android has lots of guidance on
these questions in their “design” documentation, and further HCI and Mobile
Design guidelines apply here just as well. In general, this course will leave the
UI design up to you. But major guidelines apply (e.g., make actions obvious,
give feedback, avoid irreversible actions, etc.).

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.

9.2.2 Preference Settings

While SharedPreferences acts a generic data store, it is called Shared Preferences


because it’s most commonly used for “user preferences”—e.g., the “Settings” for
an app.
The “Preference Menu” is a user-facing element, so we’ll want to define it as an
XML resource. But we’re not going to try and create our own layout and inter-
action: instead we’re just going to define the list of Preferences3 themselves
as a resource!
• We can create a new resource using Android Studio’s New Resource wizard.
The “type” for this is actually just XML (generic), though our “root element”
will be a PreferenceScreen (thanks intelligent defaults!). By convention,
the preferences resource is named preferences.xml
Inside the PreferenceScreen, we add more elements: one to represent each
preference we want to let the user adjust (or each “line” of the screen Set-
tings window). We can define different types of Preference objects, such as
<CheckBoxPreference>, <EditTextPreference>, <SwitchPreference>, or
<ListPreference> (for a dialog of radio buttons). There are a couple of other
options as well; see the Preference base class.
3 https://developer.android.com/reference/android/preference/Preference.html
102 CHAPTER 9. UI COMPONENTS II

• 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

Files and Permissions

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.

10.1 File Storage Locations


Android devices split file storage into two types: Internal storage and Ex-
ternal storage. These names come from when devices had built-in memory
as well as external SD cards, each of which may have had different interactions.
However, with modern systems the “external storage” can refer to a section of a
phone’s built-in memory as well; the distinctions are instead used for specifying
access rather than physical data location.
• Internal storage is always accessible, and by default files saved inter-
nally are only accessible to your app. Similarly, when the user uninstalls
your app, the internal files are deleted. This is usually the best place for
“private” file data, or files that will only be used by your application.
• External storage is not always accessible (e.g., if the physical storage
is removed), and is usually (but not always) world-readable. Normally
files stored in External storage persist even if an app is uninstalled, unless
certain options are used. This is usually used for “public” files that may
be shared between applications.

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

additionally need to grant dangerous permission access at runtime, when


the app tries to actually invoke the “permitted” dangerous action.
– The user grants permission via a system-generated pop-up dialog.
Note that permissions are granted in “groups”, so if the user agrees
to give you RECEIVE_SMS permission, you get SEND_SMS permission
as well. See the list of permission groups.
– When the user grant permission at runtime, that permission stays
granted as long as the app is installed. But the big caveat is that
the user can choose to revoke or deny privileges at any time (they
do this though System settings)! Thus you have to check each time
you want to access the feature if the user has granted the privileges or
not—you don’t know if the user has currently given you permission,
even if they had i
Writing to external storage is a dangerous permission, and thus we will need to
do extra work to support the Marshmallow runtime permission system.
• In order to support runtime permissions, we need to specify our app’s
target SDK to be 23 or higher AND execute the app on a device run-
ning Android 6.0 (Marshmallow) or higher. Runtime permissions are only
considered if the OS supports and the app is targeted that high. For
lower-API devices or apps, permission is only granted at install time.
First we still need to request permission in the Manifest; if we haven’t
announced that we might ask for permission, we won’t be allowed to ask
in the future. In particular, saving files to External storage requires an-
droid.permission.WRITE_EXTERNAL_STORAGE permission (which will also
grant us READ_EXTERNAL_STORAGE access).
Before we perform a dangerous action, we can check that we currently have
permission:
int permissionCheck = ContextCompat.checkSelfPermission(activity, Manifest.permission

• This function basically “looks up” whether we’ve been granted a


particular permission or not. It will return either PackageMan-
ager.PERMISSION_GRANTED or PackageManager.PERMISSION_DENIED.

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

• This method takes a context and then an array of permissions that we


need access to (in case we need more than one). We also provide a re-
quest code (an int), which we can use to identify that particular request
for permission in a callback that will be executed when the user chooses
whether to give us access or not. This is the same pattern as when we sent
10.3. EXTERNAL STORAGE 109

an Intent for a result; asking for permission is conceptually like sending


an Intent to the permission system!
We can then provide the callback that will be executed when the user decides
whether to grant us permission or not:
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResult
switch (requestCode) {
case REQUEST_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//have permission! Do stuff!
}
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}

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.

10.3 External Storage


Once we have permission to write to external file, we can actually do so! Since
we’ve verified that the External storage is available, we now need to pick what
directory in that storage to save the file in. With External storage, we have two
options:
• We can save the file publicly. We use the getExternalStoragePub-
licDirectory() method to access a public directory, passing in what
type of directory we want (e.g., DIRECTORY_MUSIC, DIRECTORY_PICTURES,
DIRECTORY_DOWNLOADS etc). This basically drops files into the same fold-
ers that every other app is using, and is great for shared data and common
formats like pictures, music, etc.. Files in the public directories can be eas-
ily accessed by other apps (assuming the app has permission to read/write
from External storage!)
• Alternatively starting from API 18, we save the file privately, but still
on External storage (these files are world-readable, but are hidden from
the user as media, so they don’t “look” like public files). We access this
110 CHAPTER 10. FILES AND PERMISSIONS

directory with the getExternalFilesDir() method, again passing it a


type (since we’re basically making our own version of the public folders).
We can also use null for the type, giving us the root directory.
Since API 19 (4.4 KitKat), you don’t need permission to write to private Ex-
ternal storage. So you can specify that you only need permission for versions
lower than that:
xml <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
We can actually look at the emulator’s file-system and see our files by cre-
ated using adb. Connect to the emulator from the terminal using adb -s
emulator-5554 shell (note: adb needs to be on your PATH). Public external
files can usually be found in /storage/sdcard/Folder, while private external
files can be found in /storage/sdcard/Android/data/package.name/files
(these paths may vary on different devices).
Once we’ve opened up the file, we can write content to it by using the same IO
classes we’ve used in Java:
• The “low-level” way to do this is to create a a FileOutputStream object
(or a FileInputStream for reading). We just pass this constructor the
File to write to. We write bytes to this stream… but can write a String
by calling myString.getBytes(). For reading, we’ll need to read in all
the lines/characters, and probably build a String out of them to show.
This is actually the same loop we used when reading data from an HTTP
request!
• However, we can also use the same decorators as in Java (e.g., Buffere-
dReader, PrintWriter, etc.) if we want those capabilities; it makes read-
ing and writing to file a little easier
• In either case, remember to .close() the stream when done (to
avoid memory leaks)!
//writing
try {
//saving in public Documents directory
File dir = getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
if (!dir.exists()) { dir.mkdirs(); } //make dir if doesn't otherwise exist
File file = new File(dir, FILE_NAME);
Log.v(TAG, "Saving to " + file.getAbsolutePath());

PrintWriter out = new PrintWriter(new FileWriter(file, true));


out.println(textEntry.getText().toString());
out.close();
} catch (IOException ioe) {
Log.d(TAG, Log.getStackTraceString(ioe));
}
10.4. INTERNAL STORAGE & CACHE 111

//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();

//read the file


String line = reader.readLine();
while (line != null) {
text.append(line + "\n");
line = reader.readLine();
}

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)!

10.4 Internal Storage & Cache

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

• Note that you can wrap a FileInputStream in a InputStreamReader in


a BufferedReader.
We can access the Internal Cache directory with getCacheDir() (and same
read/write process), or the External Cache directory with getExternal-
CacheDir(). We almost always use the Internal Cache, because why would
you want temporary files to be world-readable (other than maybe temporary
images…)
And again, once you have the file, you use the same process for reading and
writing as External storage.
For practice make the provided toggle support reading and writing to an
Internal file as well. This will of course be different file than that used with the
External switch. Ideally this code could be refactored to avoid duplication, but
it gets tricky with the need for checked exception handling.

10.5 Example: Saving Pictures

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()); //

//ideally should check for permission here, skipping for time


File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PI
file = new File(dir, "PIC_"+timestamp+".jpg");
boolean created = file.createNewFile(); //actually make the file!
Log.v(TAG, "File created: "+created);

} catch (IOException ioe) {


10.6. SHARING FILES 113

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.

10.6 Sharing Files

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.

10.6.1 Bonus: Sharing with a FileProvider

This section has not be edited for formatting or content.


What happens if we try and share an Internal file? You’ll get an error (actually
notified the use!), because the other (email) app doesn’t have permission to read
that file!
• There is a way around this though, and it’s by using a ContentProvider
(haha!) A ContentProvider explicit is about making content available
outside of a package (that’s why we declared it in the Manifest). Specif-
ically, a ContentProvider can convert a set of Files into a set of data
contents (e.g., accessible with the content:// protocol) that can be used
and returned and understood by other apps!
– Kind of like a “File Server”
• Android includes a FileProvider class in the support library that does
exactly this work.
Setting up a FileProvider is luckily not too complex, though it has a couple
of steps. You will need to declare the <provider> inside you Manifest (see the
guide link for an example).
10.6. SHARING FILES 115

<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>

The attributes you will need to specify are:


• android:authority should be your package name followed by
.fileprovider (e.g., edu.uw.myapp.fileprovider). This says
what source/domain is granting permission for others to use the file.
• The child <meta-data> tag includes an androd:resource attribute that
should point to an XML resource, of type xml (the same as used for your
SharedPreferences). You will need to create this file! The contents of this
file will be a list of what subdirectories you want the FileProvider to be
able to provide. It will look something like:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_maps" path="maps/" />
</paths>

The <files-path> entry refers to a subdirectory inside the Internal Storage


files (the same place that .getFilesDir() points to), with the path specifying
the name of the subdirectory (see why we made one called maps/?)
Once you have the provider specified, you can use it to get a Uri to the “shared”
version of the file using:
Uri fileUri = FileProvider.getUriForFile(context, "edu.uw.myapp.fileprovider", fileToShare);

(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.

• The classic example of context-awareness is having software determine if


you are at home, in the office, or on a bus, and change its usage accordingly.

– In fact, one of the winners of the first Android Developer Challenge


(2008) was Ecorio, an app that figured out whether you were driving,
walking, or busing and calculated your carbon footprint from that.

• Note that the emphasis on context-awareness comes out of Ubiquitous


Computing, a discipline that considers technology that is ubiquitous or
everywhere, to the point that it “blends into the surroundings.” That’s
the author’s take on why phone development is important; so that you
can compute without thinking about it.

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 Localization Techniques


Ubicomp researchers have been developing localization systems for years. A
classical reference is a survey paper by Jeff Hightower (who was getting his
PhD in the CSE department at UW!)
• As an early example: the Active Badge (AT&T) system had a name-
badge emit an infrared message, that was picked up by sensors in a room
to determine where the wearer was! This is room-level precision, bbut
improvements and triangulation (calculating angles to what you see) got
it down to about 10cm precision. However, this system required a lot of
infrastructure to be built into a particular room.
With Android, we’re usually interested in more general-purpose localization.
Mobile devices use a couple of different kinds of localization (either indepen-
dently or together).

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!

11.1.2 Cell Tower Localization

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.

11.1.3 WiFi Localization

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!

11.1.4 Representing Location


So once we have a location, how do we represent it?
First, note that there is a philosophical difference between a “place” and a
“space.” A space is a location, but without any social connotations. For example,
GPS coordinates, or Cartesian xy-coordinates will all indicate a “space.” A
place on the other hand is somewhere that has social meaning: Mary Gates
Hall; the University of Washington; my kitchen. Space is a computational
construct; place is a human construct. When we talk about localization with
a mobile device, we’ll be mostly talking about space. But often place is what
we’re really interested in, and we may have to convert between the two (Google
does provide a few ways to convert between the two, such as with its Places
API).
Our space locations will generally be reported as two coordinates: Latitude
and Longitude. (Altitude or height can also be reported, but that isn’t very
relevant for us).
• Latitude (“lat”) is the angle between the equatorial plane and a line
that passes through a point and the center of the Earth—the angle you
have to go up the earth’s surface from the equator. Effectively, it’s a
measure of “north/south”. Latitude is usually measured in degrees north,
so going south of the equator gives a negative latitude (though this can
be expressed positively as “degrees south”).
11.2. ANDROID LOCATION 121

• 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”).

11.2 Android Location

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.

11.2.1 Google Play Services

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.

11.2.2 Accessing Location

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

• The first parameter is going to be the GoogleApiClient object, and the


second will be the request we just made. The third parameter for the
requestLocationUpdates() method is a LocationListener—an object
with a callback that can be executed when the location is updated (i.e.,
when we move). To provide this listener, we’ll make the Activity into one
by implementing the interface and filling in the onLocationChanged()
method.
– Yes, this is a third asynchronous callback after the GoogleApiClient
connection has been established and permission has been granted and
a location has been received!
– This listener’s callback will be handed a Location object, which
contains the latitude/longitude of the location. We can then use
that location (such as display it). We can access the latitude and
longitude with getters:
textLat.setText("" + location.getLatitude());
textLng.setText("" + location.getLongitude());

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.

12.1 Motion Sensors

To continue to emphasize to mobility of Android devices (they can be picked up


and moved, shook around, tossed in the air, etc.), this lecture will demonstrate
how to use motion sensors to detect how an Android device is able to mea-
sure its movement. Nevetheless, Android does provide a general framework for
interacting with any arbitrary sensor (whether built into the device or external
to it); motion sensors are just one example.
• There are many differents sensor types1 defined by the Android framework.
The interfaces for these sensors are defined by the Android Alliance rather
than Google, since the interface needs to exist between hardware and
software (so multiple stakeholders are involved).
In particular, we’ll focus on using the accelerometer, which is used to detect
acceleration force (e.g., how fast the device is moving in some direction). This
sensor is found on most devices, and has the added benefit of being a relatively
1 https://source.android.com/devices/sensors/sensor-types.html

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.

12.1.1 Accessing Sensors

In Android, we start working with sensors by using the SensorManager2 class,


which will tell us information about what sensors are available, as well as let
us register listeners to record sensor readings. The class is actually a service
(an application that runs in the background) which manages all of the external
sensors—very similar to the NotificationManager used to track all of the
system notifications. We can get a reference to the SensorManager object using:
mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);

• 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);

• The first parameter is a SensorEventListener, which will handle the


callbacks when a SensorEvent (a reading) is produced. It is common to
make the containing Activity the listener, and thus have it implement the
interface and its two callbacks (described below).
• The second parameter is the sensor to listen to, and the third pa-
rameter is a flag indicating how often to sample the environment.
SENSOR_DELAY_NORMAL corresponds to a 200,000 microsecond (200ms)
delay between samples; use SENSOR_DELAY_GAME for a faster 20ms delay
(e.g., if making a motion-based game).
• Important be sure to unregister the listener in the Activity’s onPause()
callback in order to “turn off” the sensor when it is not directly being used.
Sensors can cause significant battery drain (even though the accelerometer
is on the low end of that), so it is best to minimize that. Equivalently, you
can register the sensor in the onResume() function to have it start back
up.
We can utilize the sampled sensor information by filling in the onSensorChanged(event)
callback. This callback is exected whenever a new sensor reading occurs (so
possibly 50 times a second)! The onAccuracyChanged() method is used to
handle when the sensor switches modes in some way; we will leave that blank
for now.
In the onSensorChanged() method, sensor readings are stored in the
event.values variable. This variable is an array of floats, but the size of the
array and the meaning/range of the values in that array are entirely depending
3 http://developer.android.com/reference/android/hardware/Sensor.html
130 CHAPTER 12. SENSORS

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):

Figure 12.1: Coordinate system (relative to a mobile device). Image from


source.android.com5 .

12.1.2 Composite Sensors

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

However, an easier solution is to utilize a second sensor. For example, we can


read the current force due to gravity from a magnetometer or a gyroscope, and
then do some math to subtract that reading from the accelerometer.
We can effectively combine these two sets of readings by listening not to the
accelerometer, but to the Linear acceleration sensor instead. This is an ex-
ample of a composite sensor, a “virtual” sensor that combines readings from
multiple pieces of hardware to produce useful results. Composite sensors allow
us to query a single sensor for a set of data, even if that data is being synthesized
from multiple other sensor components (similar to how the FusedLocationApi
allows us to get location from multiple receivers). For example, the linear ac-
celeration sensor uses the accelerometer in combination with the gyroscope, or
the magnetometer if there is no gyroscope. This sensor is thus able to sample
the acceleration independent of the gravity automatically.
• It is theoretically possible for a device to provide dedicated hardware for
a composite sensor, but no distinction is made by the Android software.
• Note that not all devices will have a linear acceleration sensor!
Android provides many such compound sensors, and they are incredibly useful
for simplying sensor interactions.

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

(linear) mapping. Because a “direction” (e.g., the phone’s facing) is represented


by a vector, that direction can be multiplied by a matrix to represent a “change”
in the direction. A matrix that happens to correspond with a transformation
that rotates a vector by some angle is called a rotation matrix.

Figure 12.2: Derivation of a 2D rotation matrix.

• 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

basic Math and String functions:


String.format("%.3f",Math.toDegrees(orientation[0]))+"\u00B0" //include the degree symbol!

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

As illustrated above, motion sensors use a standard 3D Cartesian coordinate


system, with the x and y axes matching what you expect on the screen (with
the “origin” at the center of the phone), and the z coming out of the front of
the device (following the right-hand rule as a proper coordinate system should).
However, there are a few “gotchyas” to consider.
For example, note that the values returned by the getOrientation() method
are not in x,y,z order (but instead in z, x, y order)—and in fact rotates around
the -x and -z axes. This is detailed in the documentation, and can be confirmed
through testing. Thus you need to be careful about exactly what units you’re
working with when accessing a particular sensor!
Moreover, the coordinate system used by the sensors is based on the device’s
frame of reference, not on the Activity or the software configuration! The x
axis always goes across the “natural” orientation of the device (portrait mode
for most devices, though landscape mode for some tablets), and rotating the
device (e.g., into landscape mode) won’t actually change the coordinate system.
This is because the sensors are responding to the hardware’s orientation, and
not considering the software-based configuration.
• One solution to dealing with multiple configurations is to use the
SensorManager#remapCoordinateSystem() method to “remap” the
rotation matrix. With this method, you specify which axes should be
134 CHAPTER 12. SENSORS

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();

• It is also common for some motion-based applications (such as games or


other graphical systems) to be restricted to a single configuration, so that
you wouldn’t need to dynamically handle coordinate systems within a
single device.
Chapter 13

Services

This lecture discusses a component of Android development that is not imme-


diately visible to users: Services1 .
Services are sort of like Activities that don’t have a user interface or user
interaction directly tied to them. Services can be launched by Activities or
Applications, but then do their own thing in the background even after the
starting component is closed (this is distinct from ASyncTask, which had its
lifecycle tied to that of its containing Activity). Once a Service is started, it
keeps running until it is explicitly stopped (Services can be destroyed by the OS
to save memory, similar to an Activity, but Services have higher “priority” and
so aren’t killed as readily).
Some common uses for a Service include:
• Downloading or uploading data from/to a network in the background even
if the app is closed
• Saving data to a database without crashing if the user leaves the app
• Running some other kind of long-running, “background” task even after
the app is closed, such as playing music!
An important thing to note about Services: a Service is not a separate process;
it runs in the same process as the app that starts it (unless otherwise specified).
Similarly, a Service is not a Thread, and in fact doesn’t need to run outside the
UI thread! However, we quite often do want to run the service outside of the
UI Thread, and so often have it spawn and run a new Runnable Thread. When
we say a Service runs “in the background”, we mean from a user’s perspective
rather than necessarily on a background thread.
This lecture references code found at https://github.com/info448-s17/lecture13-
services.
1 https://developer.android.com/guide/components/services.html

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" />

Finally, we can send an Intent to the Service by using the startService()


method. This is similar to startActivity(), but is for services! We can use
explicit intents just like with Activities, and could even include Extras if we
wanted to label and track the specific Intents sent to the service.
• When the Service starts, we can see it start counting (but without blocking
the UI Thread). We can also destroy the Activity and see that the Service
keeps running.
If we want to have the Service to interact with the user interface, such as dis-
playing a Toast, we will need to make sure that occurs on the UI Thread (you
cannot change the UI on a separate thread). This requires inter-thread commu-
nication: we need to get a message (a function call) from the background thread
to the UI Thread. We can perform this communication using a Handler, which
is an object used to pass messages between Threads—it “handles” the messages!
• We instantiate a new Handler() object (e.g., in the Service’s onCreate()
callback), calling a method on that object when we want to “send” a
message. The easiest way to send a message is to use the post() function,
which takes a Runnable() method which will be executed on the Main
Thread:
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(CountingService.this, "Count: " + count, Toast.LENGTH_SHORT).show()
}
});

13.1.1 The Service Lifecycle


Having demonstrated the basic usage of a service, consider what is going
on “under the hood”—starting with the Service lifecycle. There are actually
two different “types” of Services, with different variations of the lifecycle.
Started Services (or “unbound” Services) are those that are initiated via the
startService() function, as in the above example. The other option, Bound
Services, are Services that have “client” Activities bound to them to interact
with; see below for details.
• Just like with Activities, Services have an onCreate() method that is
called when the Service is first created. Since Services don’t have a user
interface to set up, so we don’t often do a lot in here.
2 https://developer.android.com/images/service_lifecycle.png
138 CHAPTER 13. SERVICES

Figure 13.1: Service lifecycle diagram, from Google2


13.1. INTENTSERVICES 139

– IntentService already overrides this in order to set up a “message


queue” so that it can queue up Intents (tasks) to run one at a time.
• The most important callback for a Started Service is called onStartCommand().
This method is called when the Service is sent a command by another
component (via an Intent). Importantly, onStartCommand() is not
only called when the Service is started for the first time, but whenever
the Service receives an Intent to start (even if the Service was already
running)! These Intents are those sent via startService().
– Note that when working with an IntentService specifically,
onStartCommand() will “queue” any incoming Intents. When
it is the specific Intent’s turn to be “run”, that Intent is automati-
cally passed to the onHandleIntent() method, which executes on
a background thread (similar to AsyncTask#doInBackground()).
This callback is not part of the normal Service lifecycle, but is
a special helper method used by IntentService—similar to how
onCreateDialog() is a special method used by DialogFragments.

• 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

• START_NOT_STICKY indicates that if the Service is destroyed by the system,


it should not be recreated. This is the “safest option” to avoid extraneous
service executions; instead, just have the application restart the Service.
• START_STICKY indicates that if the Service is destroyed by the system, it
should be recreated when possible. At that point, onStartCommand() will
be called with delivered a null Intent (unless there were other start Intents
waiting to be delivered, in which case those are used). This option works
well for media players or similar services that are running indefinitely
(rather than executing specific commands).
• START_REDELIVER_INTENT indicates that if the Service is destroyed
by the system, it should be recreated when possible. At that point,
onStartCommand() will be called with the last Intent that was delivered
to the Service (and any other Intents are delivered in turn). This option
is good for Services that actively perform jobs that need to be resumed,
such as downloading a file.
In other words: services may get killed, but we can specify how they get resur-
rected! And of course, we can and should return different values for different
starting commands (Intents): so the Intent to download a music file might re-
turn START_REDELIVER_INTENT, but the Intent to play the music file might
return START_STICKY.

13.2 Example: A Music Service


One of the classic uses for a background service is to play music, so we will use
that as an example. It is possible to play music directly from an Activity, and
the music will even keep playing as long as the Activity is alive. But remember
that Activities are fragile, and can be destroyed at any moment (whether by
us or by the system to save resources). So in order to keep our music playing
even as we go about other tasks, we should use a Service. Services have higher
“priority” in the eyes of the Android system, and so don’t get sacrificed for
resources as readily as normal Activities.

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;

We can also implement and register a MediaPlayer.OnCompletionListener


to handle when a song finishes playing.
Finally, it is possible to use MediaPlayer to play files from a ContentProvider
or even off the Internet!
String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepareAsync(); //prepare media in the background (buffering, etc)
//.prepare() for synchronous buffering
mediaPlayer.setOnPreparedListener(this); //handle when file is buffered
mediaPlayer.start();

13.2.2 Creating a Service

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

• onDestroy() can stop, release, and clean-up the MediaPlayer. We can


implement this in a separate helper function for reuse.
– If we want to handle pausing, we can specify that in the Intent we
send to the service (e.g., via a custom ACTION or an Extra). But
a more effective approach is to use Service Binding; see below for
details.
We can now have our Activity startService() and stopService() in order to
play our music in the “background”, even if we leave and return to the Activity!

13.3 Foreground Services


Services are normally “background” tasks, that run without any user interface
and that the user isn’t aware of (e.g., for downloading or uploading data, etc).
But music playing is definitely something that the user is aware of, and in fact
may want to interact with it! So we’d like the Service to have some kind of user
interface, but we’d like to still keep that Service separated from an Activity (so
that it can run with the Activity being active).
To do this, we use what is called a Foreground Service. Foreground Services
represent Services that are divorced from Activities (they are Services after all),
but the user is aware of them—and accordingly, have an even higher survival
priority if the OS gets low on memory!
Foreground services require a Notification in the status bar, similar to the
Notifications we’ve created before. This Notification will effectively act as the
“user interface” for the Service, and let the user see and be aware of its execution!
We create this Notification inside the Service’s onStartCommand() method,
then pass it to the startForeground() method in order to put our Service
in the foreground:
String songName = "The Entertainer";

PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0,


new Intent(getApplicationContext(), MainActivity.class), PendingIntent.FLAG_U

Notification notification = new NotificationCompat.Builder(this)


.setSmallIcon(android.R.drawable.ic_media_play)
.setContentTitle("Music Player")
.setContentText("Now playing: "+songName)
.setContentIntent(pendingIntent)
.setOngoing(true) //cannot be dismissed by the user
.build();
startForeground(NOTIFICATION_ID, notification); //make this a foreground service!

Some details about this Notification:


13.4. BOUND SERVICES 143

• 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.

13.4 Bound Services

As mentioned above, there are two “types” of Services: Started Services


(Services launched with startService()) and Bound Services. A Bound
Service is a Service that acts as the “server” in a client-server setup: it allows
for client Activities to “connect” to it (bind it) and then exchange messages
with—primarily by calling methods on the Service. These messages can even be
across processes, allowing for interprocess communication! This is useful when
you want interact with the Service from an Activity in some way (e.g., if we
want to pause() our music), or if we want to make the service’s capabilities
available to other applications.
We make a Bound Service by having the Service implement and utilize the on-
Bind() callback. This method returns an IBinder object (the I indicates that
it’s an Interface, following a Java convention common in enterprise software).
When Activities connect to this Service (using the bindService() method),
this IBinder object is passed to them, and they can use it to get access to the
Service process to call methods on it. Effectively, the binding process produces
an object that represents that Service, so Activities can call methods on the
Service without needing to send it Intents!
As an example, let’s add the ability to “pause” the music being played as a
bound service:
The first thing we need to do is have our Service implement the onBind()
method. To do this, we will need an IBinder object to return (that is: an
object of a class that implements the IBinder interface). For “local services”
(e.g., Services that are run in the same process), the easiest way to get an
IBinder is to extend the Binder class:
144 CHAPTER 13. SERVICES

private final IBinder mBinder = new LocalBinder(); //singleton instance variable

public class LocalBinder extends Binder { //implements IBinder


//binder class methods will go here!
}

• 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;
}
};

The onServiceConnected() callback handles the actual connection, and


is where we get access to that IBinder. We can use that IBinder to fetch
the Service object to call methods on, saving that as an instance variable
to do stuff to it! We’ll also track whether or not the Service has been
bound to avoid any null errors.
• Finally, the flag parameter indicates some options for how the
Service should be bound (e.g., for specifying survival priority).
Conetxt.BIND_AUTO_CREATE is a good default, specifying that the
binding should create the Service object if needed.
Once a Service is bound, we can call methods on that Service (which must
exist!)—allowing us to support the “pause” button.
We should also call unbindService() when the Activity stops, in order to free
up resources:
protected void onStop() {
if (mBound) {
unbindService(this);
}
super.onStop();
}

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

Animation and Graphics

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.

15.1 Drawing Graphics


Android provides a 2D Graphics API similar in both spirit and usage to the
HTML5 Canvas API: it provides an interface by which the developer can pro-
grammatically generate 2D, raster-based images. This API can be used for
drawing graphs, showing manipulated images, and even performing animations!
As in HTML5, in Android this API is available via the Canvas1 class. Similar to
the HTML5 <canvas> element or the Java SE Graphics2D class, the Android
Canvas provides a graphical “context” upon which the developer can “draw”
rectangles, circles, and even images (Bitmaps) to be shown on the screen.

15.1.1 Custom Views

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;

We can also add in wall collisions:


if(ball.cx + ball.radius > viewWidth) { //left bound
ball.cx = viewWidth - ball.radius;
ball.dx *= -1;
}
else if(ball.cx - ball.radius < 0) { //right bound
ball.cx = ball.radius;
ball.dx *= -1;
}
else if(ball.cy + ball.radius > viewHeight) { //bottom bound
ball.cy = viewHeight - ball.radius;
ball.dy *= -1;
}
else if(ball.cy - ball.radius < 0) { //top bound
ball.cy = ball.radius;
ball.dy *= -1;
}

Animation is the process of “imparting life” (from the Latin “anima”). We


tend to mean giving something motion—making an object appear to move over
time. Consider what that means for how people understand “life”.
Video animation involves showing a sequences of images over time. If the images
go fast enough, then the human brain interprets them as being part of the same
successive motion, and any objects in those images will be considered to be
“moving”. Each image in this sequence is called a “frame”. Film tends to be 24
frames per second (fps), video is 29.97fps, and video games aim at 60fps. Any
video running at at least 16fps will be perceived as mostly smooth motion.
Hitting that 16fps can actually be pretty difficult, since determining what to
draw is computationally expensive! If we’re calculating every pixel on a 600x800
152 CHAPTER 15. ANIMATION AND GRAPHICS

display, that’s half a million pixels we have to calculate! At 60fps, that’s 28


million pixels per second. For scale, a 1Ghz processor can do 1 billion operations
per second—so if each pixel requires 5 operations, we’re at 15% of our processor.
This is part of why most graphical systems utilize a dedicated GPU (graphical
processing unit)—it provides massive parallelization to speed up this process.

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

– onSurfaceCreated() starts our background thread (because the sur-


face has now been created)
– onSurfaceChanged() ends up acting a lot like onSizeChanged()
from the basic DrawingView
– onSurfaceDestroyed() stops the background thread in a “safe” way
(code adapted from Google)
• If we look at the Runnable, it’s basically an infinite loop:
1. Grab the Surface’s Canvas, “locking” it so only used in this (back-
ground) thread.
2. Draw on it.
3. Then “push” the Canvas back out to the rest of the world, basically
saying “we’re done drawing, you can show it to the user now”.
Overall, this process will cause the Surface to “redraw” as fast as possible, all
without blocking the UI thread! This is great for animation, which can be
controlled and timed (e.g., in the update() helper method by only updating
variables at a particular rate). Moreover, it provides a drawable surface that
can be interacted with!
And that gives us a drawable surface that we can interact with in the same way,
using the same kind of movement/interaction logic.
• This demonstrates one way to create low-level game and animation logic
using basic Java work; no specific game engines are required (though those
exist as well).

15.2 Touch and Gestures


As this point we have some simple animation and movement, but we would like
to make it more interactive. Our View takes up the entire screen so we don’t
want to add buttons, but there are other options available.
In particular, we can add Touch Gestures. Touch screens are a huge part of
Android devices (and mobile devices in general, especially since the first iPhone)
that are incredibly familiar to most users. We’ve already indirectly used the
touch interface, with how we’ve had users click on buttons (theoretically using
the touch screen). But here we’re interested in more than just button clicks,
which really could come from anywhere: instead, how we can react to where the
user might touch the screen and even the different ways the user might caress
the screen: flings, drags, multi-touch, etc.
Android devices automatically detect various touching interactions (that’s
how buttons work); we can respond to these touch events by overriding
the onTouchEvent() callback, which is executed whenever there something
happens that involves the touch screen
• For example, we can log out the event to see the kind of details it includes.
154 CHAPTER 15. ANIMATION AND GRAPHICS

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.

15.2.1 Advanced Gestures


We can also detect and react to more complex gestures: long presses, double-
taps, or “flings” (a flick or swipe on the screen). As these gestures, the Material
Design specification details some specific patterns that you should consider when
utilizing these interactions
Android provides a GestureDetector class that can be used to identify these ac-
tions. The easiest way to use this class—particularly when we’re interested in a
particular gesture (like fling)—is to extend GestureDetector.SimpleOnGestureListener
to make our own “listener” for gestures. We can then override the callbacks for
the gestures we’re interested in responding to: e.g., onFling().
• Note that the official documentation says we should also override the
onDown() method and have it return true to indicate that we’ve “con-
15.3. PROPERTY ANIMATION 155

sumed” (handled) the event—similar to what we’ve done with Options-


Menus. If we return false from this method, then “the system assumes
that you want to ignore the rest of the gesture, and the other methods
of GestureDetector.OnGestureListener never get called.” However, in my
testing the gesture detection works either way, but we’ll follow the spec
for now.
We can instantiate a GestureDetector by passing in our listener into a
GestureDetectorCompat constructor:
mDetector = new GestureDetectorCompat(this, new MyGestureListener());

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;
}

• Since the detector’s onTouchEvent() returns a boolean for whether or


not a gesture was detected, we can check for whether we should otherwise
handle the gesture ourselves.
This gives us the abilty to “fling” the Ball by taking the detecting fling velocity
and assigning that as the Ball’s velocity. Note that we need to negate the
velocities since they are registered as “backwards” from the coordinates our
drawing system is utilizing expect (though this doesn’t match the documented
examples). Scaling down the velocity to 3% produce a reasonable Ball movement
speed. work okay for me. We can also have the Ball slow down by 1% on each
update so it drifts to a stop!

15.3 Property Animation

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

• Android also supports OpenGL Animations for doing 3D animated sys-


tems. This requires knowing the OpenGL API.
In this section, we will discuss how to use Property Animation. This is a
general animation framework in which you specify a start state for an Object
property (attribute), an end state for that property, and a duration for the
animation; the Android systems then changes the property from the start state
to the end over that length of time—thereby producing animation!
• The change in property state over time (that is, at any given “frame”)
is calculated using interpolation. This is basically a “weighted average”
between the the start and end states, where the weight is determined
by how close you are to the “start” or “end”. While we often use linear
interpolation (so that being 70% across means the end gets 70% of the
weight), it is also posible to use non-linear interpolation) (e.g., you need
to get 70% across in for the end to have 50% of the weight).
The main engine for doing this kind of interpolated animation in Android is
the ValueAnimator2 class. This class lets you specify the start state, end state,
and animation duration. It will then be able to run through and calculate
all of the “intermediate” values throughout the interpolated animation. The
ValueAnimator class provides a number of static methods (e.g., .ofInt(),
.ofFloat(), .ofArgb()) which creates “animators” for interpolating different
value types. For example:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f); //interpolate between 0 and
animation.setDuration(1000); //over 1000ms (1 second)
animation.start(); //run the animation

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

Figure 15.1: Linearly interpolating colors


158 CHAPTER 15. ANIMATION AND GRAPHICS

//change the "alpha" property of foo: will call `foo.setAlpha(float)`


ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();

• This example will mutate the object by calling te setAlpha() method


(the name of the method is generated from the property name following
normal CamelCasing style).
• If the object lacks such a setter, such as because we are using a class
provided by someone else, we can either make a “wrapper” which will call
the appropriate mutating function, or just utilize a ValueAnimator with
an appropriate listener.
For example, we can use this approach to change the Ball’s radius or position
using an interpolated animation (make it “pulse”).
• Note that we’re just changing the object property; the only reason we see
the changed display is because the SurfaceView is refreshing continuously.
The ObjectAnimator interpolator methods support a number of vari-
ations as well: - As long as the object has an appropriate getter, it
is possible to only pass the Animator an ending value (indicating that
the animation should interpolate “from current state to specified end
state”) - We can use .setRepeatCount(ObjectAnimator.INFINITE) and
.setRepeatMode(ObjectAnimator.REVERSE) to cause it to repeat back and
forth.
If we want to include multiple animations in sequence, we can use an
AnimatorSet, which gives us methods used to specify the ordering:
//example from docs
ObjectAnimator animX = ObjectAnimator.ofFloat(obj, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(obj, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();

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

<!-- ... -->


</set>

• See Property Animation Resources4 for the full XML schema.


• Note that by defining animations as resources, it also mens that we can
easily have different device configurations use different animations (e.g.,
perhaps objects move faster on larger displays).
In order to utilize the XML, we will need to inflate the Animator resource, just
as we have done with Layouts:
ObjectAnimator anim = (ObjectAnimator)AnimatorInflater.loadAnimator(context, R.anim.my_animatio
anim.setTarget(myObject);
anim.start();

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

Databases and Providers

This lecture provides an in-depth discussion of working with Databases and


Content Providers in Android. Accessing Content Providers (via a Loader)
was discussed in a previous lecture. This lecture will instead talk about how to
make your own database and a provider for it.
Databases are appropriate when you want to store data locally on the device
(e.g., not on the cloud, which may require expensive network transactions as
well as user accounts), but that data has greater scale or complexity than is
appropriate for a SharedPreferences XML file—that is, you need to store more
than just key-value pairs.
This lecture references code found at https://github.com/info448-s17/lecture17-
databases.
Note that the intial version of this program accesses the device’s User Dictionary,
which is only available to on API 22 (Lollipop) or earlier. The tutorial does
support all versions of Android.

16.1 Review: Providers and Loaders

As discussed previously, a Content Provider is an abstraction for a source of


structured data (like a database, but also possibly files, internet resources, etc).
It acts as an interface for interacting with that data, supporting the developing
in reading, adding to, updating, or deleting data from the source (e.g., the basic
CRUD operations).
The lecture reference code demonstrates again how to access and modify struc-
tured data—specifically the User Dictionary—via a Content Provider using a
Loader. You can read this example to review how to utilize an existing Provider:

161
162 CHAPTER 16. DATABASES AND PROVIDERS

• The application displays a ListView, which is backed by a SimpleCursorAdapter.


This adapter takes a “cursor” (think: a “pointer” or an Iterator—also a
list of data that has been loaded into memory) and connects each item
to a View on the screen. This View shows the word (WORD) and the
“frequency”/prominence (FREQUENCY) of that word.

• 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).

• Finally, the example supports the update operation: by clicking the on


an item in the list, we can update that entry in the data store (increasing
the word’s frequency). We get the Cursor containing the items from the
AdapterView, with the “pointer” of that iterator set to the item that was
clicked on. We then again use the ContentResolver to update() the
item with the a ContentValues bundle of the fields we wish to change.

– 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

16.2 SQLite Databases


Content Providers can abstract all kinds of data stores (files, urls, etc.). They
abstract these as a structured information similar to a database… and in fact the
most common kind of store they represent is a relational database (specifically,
an SQLite database). Android comes with an API for creating an querying
a database; these databases are stored on internal storage meaning that each
application can have its own private database (or multiple databases, in fact)!
If you have worked with SQL or another relational database system (e.g., in
the iSchool’s INFO 340 course), this interaction will seem familiar. If you’ve
never worked with a database, the simplest explanation is to think of them as a
spreadsheet (like in an Excel file) where you manipulate rows of data given a set
of pre-defined columns. SQL (Structured Query Language) is its own command
language for working with these spreadsheets; we’ll see some samples of those
queries in this lecture. SQLite is a “flavor” (version) of SQL; the full SQLite spec
can be found here. A short tutorial (borrowed from Google) is also available in
the code repository.
In this lecture, we will build our own database of words (separate from the User
Dictionary—and so which will work on API 23+) that we can access through a
Content Provider. We will simply change which “data store” is being accessed;
the rest of the application’s interface will remain the same. This will let us
demonstrate how to put together a Content Provider from scratch. We will
start by setting up the database, and then implementing the ContentProvider
that abstracts it.
• Setting up a database is somewhat wordy and round-about, though it does
not involve many new concepts.
The step to effectively utilizing a database in Android is to create a class (e.g.,
WordDatabase) to act as a “namespace” for the various pieces of our database.
This class will not be instantiated (and so could even have a private default
constructor).
The WordDatabase class will contain a number of constants:
• DATABASE_NAME to refer to the name of the database file stored on the
device (e.g., words.db)
• DATABASE_VERSION to refer to the current version number of our
database’s schema. This is used more for supporting migrations like if we
want to update our database later.
We’re then going to specify constants that define the database’s schema or
contract. This is so that other classes (e.g., the ContentProvider and the
MainActivity) can refer to column names consistently without having to re-
member or even know the specific text we utilize in the database. This is
similar to how we used the variable UserDictionary.Words.WORD rather than
the String value "word". By convention, we define this schema as a separate
164 CHAPTER 16. DATABASES AND PROVIDERS

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(){}

public static final String TABLE_NAME = "words";


public static final String COL_WORD = "word";
public static final String COL_COUNT = "count";
}

• The class implements BaseColumns, which lets it inherit a few framework


specific constants for free—in particular, the _ID variable which Content
Providers rely on the database to have.
• We create a different nested class for each table in the database (sheet in a
spreadsheet). This allows us to use Java-style namespacing (dot notation)
to refer to different tables in a single database.
Once we have defined the schema, we are ready to create and work with
the database. In order to help us do this, we’re going to use a class called
SQLiteOpenHelper. This class offers a set of methods to help manage the
database being created and upgraded (e.g., for migrations). Specifically, we will
will subclass SQLiteOpenHelper, creating another nested class that represents
the specific helper for our database.
The subclass has a constructor that takes in a Context, and then “passes up”
the database name and version to the parent class.
public DatabaseHelper(Context context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

SQLiteOpenHelper also has two abstract methods (event callbacks) that we


need to implemet: what happens when the database is created, and what hap-
pens when the database is upgraded.
When the database is first created, we’ll need to actually create the table to
hold our words. This involves sending it an SQL command to create the table!
We’ll write this out as yet another constant for readability.
private static final String CREATE_TASKS_TABLE =
"CREATE TABLE " + WordEntry.TABLE_NAME + "(" +
WordEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT" + ", "+
WordEntry.COL_WORD + " TEXT" + ","+
WordEntry.COL_COUNT + " INTEGER" +
")";

private static final String DROP_TASKS_TABLE =


16.2. SQLITE DATABASES 165

"DROP TABLE IF EXISTS "+ WordEntry.TABLE_NAME;

• We can do the same for dropping (deleting) the table as well.


• This is the only SQL we will write in this tutorial.
We can run these SQL statements by using the execSQL() method, called on the
SQLiteDatabase object that is passed to these callbacks. Note that this runs
a “raw” SQL query (that doesn’t return anything, so not SELECT), without any
kind of checks against SQL injection attacks. But since we’re hard-coding the
information to run, it’s not a problem. Aside from this situation, you should
never use this method.
• We can also use the insert() method to add some sample words to the
database (similar to how we used the Content Provider), for clarity when
testing:
ContentValues sample1 = new ContentValues();
sample1.put(WordEntry.COL_WORD, "Embiggen");
sample1.put(WordEntry.COL_COUNT, 0);
db.insert(WordEntry.TABLE_NAME, null, sample1);

• 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

Cursor results = builder.query(


db,
166 CHAPTER 16. DATABASES AND PROVIDERS

new String[] {WordDatabase.WordEntry.COL_WORD, WordDatabase.WordEntry.CO


null, null, null, null, null); //5 nulls!

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!

16.3 Implementing ContentProvider

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

16.3.1 URIs and Types

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

• This URI indicates that it is a identifier for a particular provider (the


authority), which has a particular resource type (think: which
database table of information), which may have a particular resource id
(think: the ID of the record in the table)
• Leaving off the id would refer to the entire table, or rather the “list” of
resources. So really we have two different “categories” of URIs: the whole
list, and an individual resource within that list. Both have the same
“base”, but will need to be handled slightly differently.
• See designing content URIs for more discussion on how to structure these.
We will define these URIs piece-wise using constants (of course). One for the au-
thority, one for the resource type (which happens to be the name of the database
table, but doesn’t need to be), and finally the overall Content URI (parsed into
a Uri object):
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/"+WORD_RESOURCE);

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);
}
}

– vnd stands for “vendor specific”—in this case, a format specific to


Android.

16.3.2 Query Methods

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);
}

• For insert(), it is also possible to make sure that no “empty” entries


are added to the database, and to return the result if the insertion is
successful:
if(!values.containsKey(WordDatabase.WordEntry.COL_WORD)){
values.put(WordDatabase.WordEntry.COL_WORD, "");
}
if(!values.containsKey(WordDatabase.WordEntry.COL_COUNT)){
values.put(WordDatabase.WordEntry.COL_COUNT, 0);
}

long rowId = db.insert(WordDatabase.WordEntry.TABLE_NAME, null, values);


if (rowId > 0) { //if successful
Uri wordUri = ContentUris.withAppendedId(CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(wordUri, null);
return wordUri; //return the URI for the entry
}
throw new SQLException("Failed to insert row into " + uri);

• The update() method can be somewhat awkward because we need to


basically add our id restriction to the user-given selection args:
int count;
switch (sUriMatcher.match(uri)) {
case WORD_LIST_URI:
count = db.update(WordDatabase.WordEntry.TABLE_NAME, values, selection, sele
break;
case WORD_SINGLE_URI:
String wordId = uri.getLastPathSegment();
count = db.update(WordDatabase.WordEntry.TABLE_NAME, values, WordDatabase.Wo
+ (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), s
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
throw new SQLException("Failed to update row " + 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.

17.1 Signing an App


As described in lecture 1, building an Android application involves compiling
Java and XML code into DVM bytecode, and then packing this code (along
with assets and graphics) into a .apk file.
But in order to install this .apk file onto a device, it needs to be cryptograph-
ically signed by including a public-key certificate. This certificate acts
somewhat like a “password”, in that it corresponds with a private key (a secret
code) held by you as the developer. Because each public-key cert is associated
with a code only you as the developer know (like a password), it is able to act
as an identifying signature for your app: only you know the secret password,
so only you are able to provide this particular certificate. Thus by signing the
.apk with your signature, you are marking the package as being developed by
you and not someone else (just like a signature on a check). Android uses these

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.

17.1.1 A Release .apk


In order to generate a shareable release .apk, you will need to produce a
release certificate to sign the app with, then build and sign the .apk with that
certificate.
Android studio makes it easy to sign a release build (follow the link for more
details and examples). In short, select Build > Generate Signed APK from
the menu, and follow the wizard’s instructions!
• You will be prompted for a location for the .keystore file to use (e.g.,
where to store your release pens). I recommend making a file somewhere
in your user’s home directory (e.g., ~/android-release.jks). Note that
you can use the same private key (found in the keystore) for multiple apps.
• It is also possible to configure Android Stduio to automatically sign your
application when building for release. Be sure you remove signing informa-
tion from your build files so your passwords don’t get uploaded to GitHub!
The built and signed .apk will be created in the destination folder you selected.
This file can then be shared: uploaded to the Google Play Store, hosted on a
web page, or emailed directly to someone to install.
• Note that installing .apk files from outside the Play Store—even when
signed—requires the user to opt-in for apps from unknown sources.
176 CHAPTER 17. PUBLISHING
Chapter 18

Special Topics: Kotlin

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.

This lecture references code found at https://github.com/info448-s17/lecture16-


kotlin. Kotlin demo code can be found in the kotlin folder; build and run this
code through gradle with the command gradle -q.

• Note that the provided gradle build script includes the Kotlin standard
library for Java 8, making (simplied) versions of Java classes available.

18.1 Kotlin Syntax

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!

var possiblyNull:String? = "Hello"


possiblyNull = null //no problems!

• 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

This casting needs to occur explicitly:


val integer:Int = 5
var root:Double = Math.sqrt(integer) //compile type error!
//sqrt() takes in a double
root = Math.sqrt(integer.toDouble()) //no problems!

• Fun feature: in Kotlin, numeric literals can be written with underscores


(_) as readability separators; the underscores are removed during compile
time.
val oneMillion:Int = 1_000_000

• Fun feature: Kotlin also supports string templating, in which variables


used within the String are automatically evaluated (avoiding the need for
complex String concatenation). Variables to evaluate are prepended with
a dollar sign $, while more complex expressions can be evaluated from
inside ${}
val quantity = 5
val price = 2.95
val cost:String = "$quantity items at $price costs ${quantity*price}"

• Note: Arrays in Kotlin are represented by the Array<T> class. The


Array<T> class has a private constructor though; in order to create an
array, use the arrayOf() factory method (to create an array out of the
given parameters), or arrayOfNulls() to create an array of n null el-
ements. Note that arrays still support using bracket notation ([]) for
accessing values.
var numbers:Array<Int> = arrayOf(1,2,3,4,5) //create an array
println(numbers.size) //kotline uses `size` property for array length
println(numbers[3]) //4
numbers[4] = 10 //assign value as normal

– 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
}

val fullName = makeFullName("Ada", "Jones")

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")
}

greet() //use defaults for arguments


greet("y'all") //use default for second argument
greet(personFrom="Myself", personTo="You") //named arguments (out of order)

Similar to JavaScript arrow functions, Kotlin functions whose body is just a


single expression can leave off the block entirely, instead writing the expression
to return after an equals sign (=)—as though that expression were assigned to
the function:
//a function square() that takes in an Int and returns that number squared
fun square(n:Int):Int = n*n

• 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
}

val numbers:IntArray = intArrayOf(1,2,3,4,5)


println(numbers.contentToString()) //[1, 2, 3, 4, 5]

val squares:List<Int> = numbers.map(square) //transform each element with square()


println(squares.joinToString()) //1, 4, 9, 16, 25

• 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

The argument types are listed in parentheses, followed by an arrow ->,


followed by the return type.
Anonymous functions that are a single expression can also be written as lambda
expressions. Lambda expressions are placed inside curly braces {}, with the
parameter list coming first (not in parentheses). This is followed by a arrow ->,
followed by the expression that should be evaluated.
val add = { x:Int, y:Int -> x+y }

//with an explicit typed (so inferred in the expression)


val add:(Int, Int) -> Int
add = { x,y -> x+y }

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)

//filter() takes a callback with a single parameter that returns boolean,


//so the signature can be inferred
val filtered:List<Int> = numbers.filter( {it > 2} )

println(filtered.joinToString()) //3, 4, 5

This allows us to write somewhat readable code using functional programming


approaches. As an example from the documentation:
182 CHAPTER 18. SPECIAL TOPICS: KOTLIN

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
}

• Of course constructor parameters can support default named arguments


as well!
• Kotlin properties (instance variables) default to being public, but can
be declared private if desired. See Visibility Modifiers.
• Note that methods in Kotlin are simply functions (written as above)
declared inside the class body.
The primary constructor parameters can directly declare the variables by includ-
ing either var or val as appropriate. Thus a shortcut for the above constructor
and initialization is:
//declares instance variables
class Dog(var name:String, val breed:String) {

• Additional constructors are specified using the constructor keyword as


the method name. See the documentation for details.
Objects in Kotlin are instantiated by simply calling the constructor as a normal
function, without the new keyword.
val myDog = Dog("Fido","mutt") //no new keyword!
myDog.name //"Fido"
myDog.breed //"mutt"
18.1. KOTLIN SYNTAX 183

As with any other object-oriented language, Kotlin also supports inheri-


tance, with all classes existing within an inheritance hierarchy. Note that
in Kotlin, all classes by default inherit from the class Any, rather than from
java.lang.Object.

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() {

• Interfaces are implemented the same way, as part of a comma-separated


list of inherited classes. That is, there is no distinction between extending
a class and implementing an interface.
Importantly, Kotlin requires us to explicitly mark classes as “subclassable”
(they are otherwise compiled to final classes). We do this by using the open
keyword as an annotation in the class declaration:
//declare class as open
open class Animal(var name:String) { }

//constructor parameters can be passed to parent constructor


class Dog(var name:String) : Animal(name) { }

The open keyword is also needed if we want to allow a method to be overridden.


Additionally, any methods that override a parent version need to be annotated
as such with the override keyword.
open class Animal() {
//function can be overridden
open fun speak() {
println("The animal speaks")
}
}

class Dog() : Animal() {


//overrides parent function
override fun speak() {
println("The dog barks")
}

//overriding toString() using an expression function


override fun toString() = "A dog"
}

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)

val myBall = Ball(30,30,"red")


print(myBall) //Ball(x=30, y=30, color=red)

• 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.

18.2 Kotlin for Android


Kotlin is built into Android Studio 3.0 by default, but is also available via a
plugin for earlier versions. You can install this plugins through the Preferences
> Plugins menu: click the Install JetBrains plugin... button at the
bottom and select Kotlin. Note that you will need to restart Android Studio.
After you’ve installed the plugin, you can use Android Studio to convert an
existing Java file to a Kotlin source code file (.kt) by selecting Code > Convert
Java File to Kotlin File from the menu. Once you start editing the file,
you will be prompted to configure the project to use Kotlin. This will modify
the Gradle build scripts to include Kotlin support, allowing you to build and
run your app as usual.
For example, we can convert the provided MainActivity, and consider the
changes that have been made:
18.2. KOTLIN FOR ANDROID 185

• The adapter instance variable can be null, so its type includes a ?.


• Notice how Intents are addressed, particularly the ::class.java syntax.
• For loops use for ... in syntax, and Kotlin supports ranges.
• You can fix the syntax error with the adapter by explicitly casting the
ListView as a ListView (this is an example of the converting misunder-
standing the code; it happens!)
• Static variables are wrapped in a companion object.
This section of the lecture is incomplete.

18.2.1 The Android Extension

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)

Toast and Alerts: https://github.com/Kotlin/anko/wiki/Anko-Commons-


%E2%80%93-Dialogs
186 CHAPTER 18. SPECIAL TOPICS: KOTLIN

//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

Additional Topics (Labs)

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.

19.1 Universal Usability

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.

19.2 Implementing Accessibility


The Android framework provides a number of ways to make apps more ac-
cessible, including a handy accessibility developer checklist that you can
follow. Some specific actions are described in more detail below: you should
perform the listed tasks to test and improve an app’s accessibility.
These exercises build on the lecture code found at https://github.com/info448-
s17/lecture07-loaders (use the completed branch). You will need to adjust
MainActivity so that it shows the provided MovieFragment by default; you
will be testing and improving the accessibility of that Fragment.

19.2.1 Supporting Vision Impairment


One of the most important ways to support accessibility is to make sure your
app is accessible to users with vision impairments.
As mentioned previously, one way to support users with trouble seeing content
on small screens is to always use scalable pixels (sp) as units on TextViews. This
allows the size of the content to scale with user preferences: so if someone wants
everything on the phone to be large, it can be!
However, users who are blind require extra support. This support is provided
by Accessibility Services, which are “background services” that can respond to
specific accessibility events (we will discuss Services more in a future lecture).
The most common built-in service is called TalkBack, which is the Google-
developed screen reader for Android. This service will “speak” the name of UI
19.2. IMPLEMENTING ACCESSIBILITY 191

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.

19.2.2 Supporting Alternative Inputs

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.

19.2.3 Supporting Internationalization


Finally you can make an application accessible to a wider diversity of users by
providing Internationalization and supporting different languages and cultures.
Internationalization (i18n) is primarily done by specifying alternative resource,
such as using XML to define user-facing Strings in multiple languages. We did
an example of this in lecture 3
However, changes in language may also require adjustments to the layout re-
sources themselves: phrases in some languages are significantly longer or shorter
than in English, and so may cause problems with spacing or word wrapping.
• One way to test this is to enable pseudo-localization, a “fake” language
that almost looks like English, but utilizes special characters and extra-
neous text to help test what the application may look like with different
length content. See the link for details on enabling this.
Another significant change involves support right-to-left (RTL) languages such
as Arabic. With these languages the “flow” of text goes in the opposite direc-
tion than in English, so many positioning elements in layouts may need to be
reversed:
In particular, do the following to best support both LTR and RTL languages:
1. Declare that your app supports RTL in the Manifest by including an
attribute android:supportsRtl="true" in the <application> element.
2. With RelativeLayouts, use positioning attributes based on start and end
rather than left and right. For example, android:layout_toRightOf
should instead be android:layout_toEndOf. This will allow the relative
positioning to automatically “switch” between LTR and RTL. Note that
LinearLayouts automatically reverse direction!
19.2. IMPLEMENTING ACCESSIBILITY 193

Figure 19.1: English and Arabic layouts. Image by Ryder Ziola.


194 CHAPTER 19. ACCESSIBILITY

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.

19.2.4 Further Testing


These are fairly trivial changes you can make to how you define and implement
user interfaces, that will go a long way to supporting use by all users no matter
their level of ability.
The Android Studio IDE will identify and suggest additional changes as “linted”
style suggestions—watch out for these warnings and learn to correct them as
you develop.
Finally, Google has also developed an Accessibility Scanner app that can be
used to check for further accessibility issues (such as contrast levels or touchable
areas).
And of course, keep the checklist handy to help you design applications to
support universal usability.
Chapter 20

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

20.1 Define a SearchFragment

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.

20.2 Add the ViewPager and Adapter


Your MainActivity will need to contain a ViewPager View (since all the other
Views have been moved to Fragments!).
Add a android.support.v4.view.ViewPager element in the activity_main.xml
layout resource, finding this View in the Activity’s onCreate() callback.
Just like with a ListView, a ViewPager requires a (custom) adapter in
order to map from which “page” is shown to the Fragment that is ren-
dered. Add a new inner class (e.g., MoviePagerAdapter) that subclasses
FragmentStatePagerAdapter.

• As in the documentation example, You will need to provide a constructor


that takes in a (Support) FragmentManager, and calls the appropriate
super constructor.
20.3. ADD USER INTERACTION 197

• The getItem() function returns which Fragment is shown for a particular


page number. You should implement this function so that page 0 shows a
SearchFragment, page 1 shows a MoviesFragment, and page 2 shows a
DetailsFragment.

– You can declear each of these three Fragments as instance variables,


then simply return them from this method.
– It’s okay to “hard-code” this logic for the purposes of this demonstra-
tion.
• The getCount() function returns how many pages the Pager supports.
Note that you will need to include some logic for this: before a search has
occured, there is only one page! After the search, there are two pages (the
search and the results), and after a result option is selected there are three
pages (the search, the results, and the details).
• Finally, we will be “replacing” Fragments inside the Pager as the user
interacts with the app (e.g., changing the MoviesFragment to one with
different search results)—such as by changing the objects that the instance
variables refer to. However, the ViewPager “preloads” adjacent Fragment
pages as an optimization technique; thus it “caches” the Fragments and
won’t actually load any updated Views.
As a work-around, override the getItemPosition() function (which is
called whenever the Pager needs to determine if an item’s position has
changed):
public int getItemPosition(Object object) {
return POSITION_NONE;
}

Note that this is a memory-intensive workaround (but works for demon-


stration purposes); for a cleaner solution, see this discussion.
Once you’ve defined the your adapter, instantiate it in the Activity’s
onCreate() callback, and use ViewPager#setAdapter() to specify the
Pager’s adapter.
If you also instantiate a SearchFragment in the onCreate() callback, then you
should be able to run the application and see that Fragment appear as a page
(though there is nothing else to swipe to yet).

20.3 Add User Interaction


Finally, you will need to adjust the Fragment callback methods inside the Ac-
tivity (e.g., onSearchSubmitted() and onMovieSelected()) so that they in-
teract with the ViewPager. Note that this will involve removing previous code
(the ViewPager does not need to utilize FragmentTransactions).
198 CHAPTER 20. FRAGMENTS: VIEWPAGER

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

in the Manifest, but we need to do it in Java since the Receiver isn’t a


separate class and since we want to do it dynamically).
• First instantiate a new IntentFilter object (giving it the
BluetoothDevice.ACTION_FOUND action).
• Then use the registerReceiver(receiver, intentFilter)
method, passing it the already-existing receiver (mReceiver) and
the IntentFilter you just created!
• Then repeat the above two steps, but this time for the
Bluetooth.ACTION_DISCOVERY_FINISHED action. Thiswill
register an additional IntentFilter on the same receiver.
7. We can actually begin searching for devices by filling in the Activity’s
doDiscovery() helper method (which is called when the Scan button is
pressed).
• Add a check to see if the BluetoothAdapter currently isDiscovering().
If so, then you should tell the adapter to cancelDiscovery().
• Whether or not the check was true (so even if we canceled the dis-
covery), tell the adapter to startDiscovery() to begin searching
for devices!
8. Once the user has selected a device to connect to, we handle that
connection back in the BluetoothChatFragment. Fill in that class’s
connectDevice() helper method to connect to the device!
• First you’ll want to get the device’s “address” (a MAC address
that acts as a unique identifier) from the Intent’s extras: get the
Bundle of extras from the Intent, then get the String with the key
DeviceListActivity.EXTRA_DEVICE_ADDRESS.
• You can then find the device (a BluetoothDevice object) by calling
the .getRemoteDevice() method on the BluetoothAdapter and
passing this address.
• Finally, you can use the mChatService’s .connect() method to
connect to this device (passing down the secure option as a second
parameter). The BluetoothChatService#connect() method cre-
ates a new Thread to do the communication work, and opens up
network sockets so that messages can be passed between the devices.
(This is actually part of the hard part or working with Bluetooth;
luckily we have a class to abstract that for us!)
9. The last part is to actually send a message! In the sendMessage() helper
in BluetoothChatFragment, fill in the details so that the String can be
sent to the socket in the chat service.
• First you need to convert the message String into a byte[] (for com-
munication over the socket). Use the String’s getBytes() method
to convert.
202 CHAPTER 21. BLUETOOTH

• Then you can tell mChatService to .write() those bytes!


• We then need to reset the mOutStringBuffer instance variable
(which keeps track of the message that has been typed so far). Use
.setLength() to give it a length of 0, which will effectively make
it empty.
• And finally, because we’ve changed the outgoing message, set
the text of the mOutEditText TextView to be the (now empty)
mOutStringBuffer.
And that’s it! You should now have a working chat system! Search for and
connect to someone else’s device and try saying “hello”!
Chapter 22

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.

22.1 Create a Map Activity


There is no scaffolding code for this tutorial; instead, you should create a new
project from scratch. Note that you can test your project either on the emulator
or a physical device: just make sure the device has the Google APIs included.
Start a new project in Android Studio (“Map Lab” is a fine project name).
Target SDK 15 like usual.
But here’s where things get different! Instead of starting with an “Empty Ac-
tivity”, start with a Google Maps Activity. This will create you a new
Activity whose layout includes an XML-defined SupportMapFragment. (This
is just another Fragment subclass, so you could include it in a layout however
you wanted. You can stick with the default layout for now).

22.1.1 Getting an API Key

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

Figure 22.1: The instructions found in the google_maps_api.xml file.

After you’ve entered the key, you should be able to build and run your app, and
see a displayed map!

22.1.2 The SupportMapFragment

Take a moment to open up the generated MapsActivity and its associated


layout (by default layouts/activity_maps.xml) and read over the initial
code.

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.

• The Fragment is defined in the XML, so we don’t need to use a


FragmentTransaction to add it in the Java code.

But we do use the FragmentManager to get access to that fragment so that we


can call a single method on it: getMapAsync(). This gets access to a GoogleMap
object, which does all the work of downloading map tiles, handling pans and
zooms, and drawing markers and shapes.

• The getMapAsync() method loads this object asynchronously (like


we’ve with the GoogleApiClient), and will notify a listener when
the object is available. Because the MapsActivity implements the
OnMapReadyCallback interface, it is a listener and so its onMapReady()
callback will be called and passed the object for us to handle.

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

22.2 Specifying the User Interface

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.

22.3 Markers and Drawings

Showing a map is great, but what we really want to do is customize what it


shows: that is, we want to draw on it! There are a couple of things we can draw,
one of the most common of which is Markers that indicate a single location on
the map.
You can create a Marker by instantiating a new MarkerOptions object and
passing it into the GoogleMap object’s addMarker() method.
• See the documentation for ways to customize these markers. The most
common options are setting the position (required), the title (text that
appears when the user clicks the marker), and snippet (additional text).
Create a marker centered in the center of the fountain. The marker should be
purple or gold in color, and clicking on it should tell the user something about
the ducks who dwell there!
• You can show customized information (including pictures, etc) when mark-
ers are clicked using Info Windows.

22.3.1 Drawing Shapes

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 order to create a Polyline, you actually instantiate a PolylineOptions


object. You can add() points (LatLng objects) to this object, extending
the line from one point to the next. This PolylineOptions object is then
passed to the GoogleMap object’s addPolyline() method.
• You can also specify other visual properties such as the color and the
width of the Polyline. Note that the width is measure in screen pixels—it
will be the same no matter what zoom level you are at! (If you wanted it
to change dynamically, you’d need to do that work on your own).
Using a Polyline, draw a giant “W” in either purple or gold centered on the
fountain. Bonus if want to make it look like the UW logo!
• Or better yet: can you use a combination of Polylines, Circles, and Poly-
gons to draw an approximation of the iSchool logo?
Chapter 23

Styles & Themes

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.

23.1 Defining Styles

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!

23.1.1 Style Inheritance


This is a good start, but we still have some duplicated attributes—in particular,
the “labels” share a couple of attributes (e.g., they are all bold). Since each View
can only have a single style and there is no cascading, if we wanted to create a
separate LabelStyle style, it would end up duplicating some attributes of the
23.1. DEFINING STYLES 209

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

23.1.2 Built-in Styles

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

to change MainActivity to subclass Activity, not AppCompatActivity, in


order to fully adjust the theme.
• You might also try commenting out the stylings you applied to the indi-
vidual TextViews to really see what happens.

23.2.1 Material Themes


Along with built-in styles, Android provides a number of platform-specific
themes. And again, the somewhat unhelpful recommendation is to understand
these by browsing the source code, or the list in R.style (scroll down to
constants that start with Theme).
One of the most useful set of Android-provided themes are the Material
Themes. These are themes that support Google’s Material Design, a visual
design language Google uses (or aims to use) across its products, including
Android. Material themes were introduced in Lollipop (API 21) and so are only
available on devices running API 21+ (though there are compatibility options).
There are two main Material themes:
• @android:style/Theme.Material: a Dark version of the theme
• @android:style/Theme.Material.Light: a Light version of the theme
And many variants:
• @android:style/Theme.Material.Light.DarkActionBar: a Light ver-
sion with a Dark action bar (Dark backgroud, Light contents)
• @android:style/Theme.Material.Light.LightStatusBar: a Light
version with a Light action bar (Light background, Dark contents)
• @android:style/Theme.Material.Light.NoActionBar: a Light
version with no action bar.
• … etc. See R.style for more options (do a ctrl-f “find” for Material)
Practice: Experiment with applying different material themes to your
application How does your app’s appearance change? Give your application a
Dark Material theme!

23.2.2 Theme Attributes


One of the big advantages of Themes is that they can be used to define theme
attributes that can be referenced from inside individual, per-View Styles. This
allows the Theme to effective “skin” the View. For example, a Theme could
define a “color scheme” as a set of attributes; these attributes can then be
referenced by the Style as e.g., “the primary color of the current theme”.
Theme-level attributes are referenced in the XML using the ? symbol (in place
of the @ symbol). For example: ?android:textColorPrimary will refer to the
value of the <item name="textColorPrimary"> element inside the Theme.
212 CHAPTER 23. STYLES & THEMES

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.

24.1 Identifying Fingers


Android lets you react to multiple touches by responding to ACTION_POINTER_DOWN
events, which occur when a second “pointer” (finger) joins the gesture (after
ACTION_DOWN).

• 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

• Similarly, there are ACTION_POINTER_UP events for removing fingers, until


the last finger which causes the ACTION_UP event.
Practice: add further cases to the onTouchEvent() callback and log
out when subsequent fingers are placed and lifted.
Here the tricky part: each finger that is currently “down” can cause events
independently. That is, if we move a finger, then an ACTION_MOVE event will
occur. Similarly, if we remove a finger, then an ACTION_POINTER_UP event will
occur. So the question is, how do we know which finger caused the event?
Underneath the hood, pointers are stored in a list (think: an ArrayList), so
each has a pointer index (a number representing their index in that list). But
these indices can change as you interact with the device. For example, lifting up
a finger will could cause that pointer to be removed from the list, thus moving
all of the other pointers up an index. IN fact, the index is allowed to change
between each event—while they often stay in order, there is no assurance that
they will. The exact behavior of these indices is not specified or enforced by the
framework, so we need to treat those values as unstable and cannot use them
to “track” particular fingers.
However, each pointer that comes down is assigned a consistent pointer id
number that we can refer to it by. This id will be associated with that finger for
the length of time that contact is being made. In general the first finger down
will be id 0, and no matter what happens to the list order that pointer’s id will
stay the same.
Practice: track pointer ids using the following procedure:
1. When a Touch event occurs, determine the pointer index which caused
the event. Do this by calling the MotionEventCompat.getActionIndex(event)
method. Note that this only actually applies to POINTER_DOWN or
POINTER_UP events, otherwise it will just return 0 (for the “main finger”
of the event).
2. Get the unique pointer id for whatever finger caused the event.
Do this by calling the MotionEventCompat.getPointerId(event,
pointerIndex) method. This will give you the unique id associated with
the event’s finger.
The MotionEventCompat class is just a wrapper around MotionEvent methods,
so that the correct version of MotionEvent is used. We could alternatively just
call event.getActionIndex().

24.2 Drawing Touches


Once you know which pointer has gone up and down, you can respond to
it by modifying the drawing displayed by the App. Add the falling func-
24.3. MOVING FINGERS 215

tionality to the custom drawing View. Modify either the DrawingView or


DrawingSurfaceView (your choice of which to use):
• Add an instance variable touches that is a HashMap mapping pointer ids
(Integers) to Ball objects. This will track a single “ball” for each touch.
• Add a method addTouch() that takes in a pointer id as well as the x,y
coordinates of the touch point. This method should add a new Ball (at
the given coordinates) to the touches map (with the given pointer id).
– Because this method will need to work across threads in the
DrawingSurfaceView, you should make sure the method is
synchronized (specifying that keyword in the method signature).
– Call this method on the drawing View from MainActivity
when a new finger is put down—including the first finger!
This may be from two different types of events.
* Pass the pointer index as a parameter to the getX() and
getY() methods to determine the coordinates of that particu-
lar pointer!
• Add a method removeTouch() that takes in a pointer id, and removes the
Ball that corresponds to that touch.
– This method should also be synchronized.
– Call this method on the drawing View from MainActivity
when a finger is lifted—including the last finger!
• Modify the render() method (or the onDraw() method) so that the View
draws each of the Ball objects in the HashMap at their stored location. You
can use gold paint for this. Recall that you can get an iterable sequence
of the values for a HashMap using the .values() method.
This should cause your app to show a small ball underneath each finger that
goes down, with the balls disappearing when a finger is lifted. Make sure each
ball is big enough to see!

24.3 Moving Fingers


Now we just need to handle finger movements. With a MOVE action, the event
doesn’t track which finger has moved—instead, the pointer index of that action
is always going to be 0 (the main pointer), even if a different finger moved!
However, the event does include informaion about how many pointers are in-
volved in it (that is: how many pointers are currently down): we can get that
number with the MotionEvent.getPointerCount() method (or an equivalent
MotionEventCompat method). We don’t know which pointer index each finger
has, but we do know that they will be consecutive indices (because they are
stored in a list). Moreover, as above each pointer will have its own x and y
216 CHAPTER 24. MULTI-TOUCH

coordinates, representing the current position of that pointer—this may or may


not have “moved” from the previous event.
Thus we can just loop through all of the pointer indices and get the pointer
id for each one. We can then specify that we want the corresponding Ball to
update its position to match the “current” pointer position. Again, most of the
Balls will not have moved, but we know at least one of them did and so we will
just update everything to make sure it works!
• Add a method moveTouch() to the drawing View that takes in a pointer id
(e.g., Ball id), and the “latest” x,y coordinates for that Ball. Update the
appropriate Ball’s position to reflect these latest coordinates (use .get()
to access a HashMap value by its key).
– This method should again be synchronized.
• In MainActivity, when a MOVE event occurs, loop through all of the
pointer indices in the event. Get the pointer id and x,y coordinates of
each, and use those to call the moveTouch() method on the drawing View.
You will be “moving” all of the balls, even if most are just moving to the
same place they were.
And with that, you should have multi-touch tracking! Try adding and removing
fingers in unique orders, moving them around, and make sure that the Balls
follow the contacts.
• Note that tracking individual ids in this way is more commonly used to
make sure you’re ignoring extra multiple touches. See the docs or this
developer blog post for details.

24.4 Other Multi-Touch Gestures


We can respond to common multi-touch gestures (like “pinch to scale”) by using
another kind of GestureDetector called a ScaleGestureDetector. As before,
we subclass the simple version (ScaleGestureDetector.SimpleOnScaleGestureListener),
and fill in the onScale() method. You can get the “scale factor” from the
gesture with the .getScaleFactor() method. As a further bonus exercise,
you might try to use the gesture to scale the size of a single ball.
Chapter 25

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.

25.1 Memory Allocation


First, a short introduction to how memory is handled by a computer (including
mobile computers):
The basic idea is that your computer has some amount of storage space (in RAM)
dedicating to remembering all of the variables and objects that you create in
your program. If you declare a new variable
int number = 448;

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.

Figure 25.1: Example of memory allocation, from dynatrace.com.

Memory management thus involves answering two questions:

1. Where do we put new values that we need to allocate memory for?


2. What do we do with variables that we no longer need to remember?

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.

• Garbage collection is a bit like going through an apartment and marking


rooms as “vacant” if no one lives there anymore.

• Java’s garbage collection means we don’t need to handle deallocating mem-


ory ourselves—unlike in languages such as C where you need to manually
manage your own memory!

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

25.2 The Memory Monitor

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).

Figure 25.2: Android Memory Monitor (Google)

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

25.2.1 Garbage Collection


Let’s see how the Java (technically, Dalvik) virtual machine handles memory.
From the MainActivity, click on the button to go to the ProgressActivity.
This Activity will show the progress towards completing a number of undefined
tasks (it’s just showing a bunch of ProgressBar Views, which use a bit more
memory than standard buttons).
• When you visited this Activity, you should have seen the memory allo-
cation jump up!
Now try navigating back and forth between the MainActivity and the
ProgressActivity. You should see the allocated memory continue to increase
(since we need to keep recreating those spinners), getting closer and closer to
the amount of “free” memory. Eventually, Android will decide that it’s out of
memory, and will initiate garbage collection, deallocating the memory that
has previously been used to create the old spinners which are no longer needed
(because that instance of the Activity is gone).
• Android will sometimes increase the amount of free memory available as
the app asks for more. This is normal.
• When the garbage collection occurs, you should see the amount of allo-
cated memory suddenly drop. Those “cliffs” are the garbage collector
running.
There are actually different forms of garbage collection (which make different
decisions about which values are “old enough” to be deallocated), and the one
that you just triggered may not have collected everything. You can also use
the memory monitor to manually initiate garbage collection—this is useful for
checking if memory is actually being used, or just hasn’t been cleaned up yet.
• Manually initiate the garbage collection by clicking the “Initiate GC”
button ( ) at the top of the memory monitor panel. What happens?
Were there other values that could be cleaned up?
• Note that in practice, you should not try to perform garbage collection
on your own—let the system do its own work (it is optimized to make
sure that the computer doesn’t slow down too much when trying to do
cleanup, which requires additional processing). This button is purely for
testing and inspection.

25.3 Memory Leaks


Android’s garbage collection does a fine job of cleaning up memory… assum-
ing that it can correctly identify the values that aren’t being used. This can
sometimes be tricky: Java decides that a value is eligible for garbage collection
whenever there are no longer any references to that value (e.g., no variables
25.3. MEMORY LEAKS 221

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.

If you click on the BirdActivity’s button, you’ll be taken to (another) blank


Activity. Since the bird image is no longer being shown, it should no longer
be used and thus should be eligible for garbage collection. Hit the “Initiate
GC” button again to request that Android sweep up and reclaim that memory.
What happens?
You’ll notice that the large amount of memory allocated to the image doesn’t
go away… it looks like the bird isn’t being garbage collected!
• Why not? Recall that when we start new Activities, old Activities are
put on a back stack, allowing us to be able to hit the “back” button and
return to the previous Activity. This means that each Activity in the
stack contains a reference to the previous ones… including the resources
that they have loaded into memory! So there is in fact a reference to that
image—it’s in the object that is stored on the backstack!
• This can be a problem. It means that because we have that history, the
amount of memory available to our application is smaller even though
we’re not actually looking at the pretty bird right now.
We can fix this by utilizing the Activity lifecycle callbacks. Specifically, you
can remove the reference to the image when the the Activity stops, and then
reload the image when the Activity starts again.
• For practice, in the BirdActivity, override the onStop() method. In-
side this method, set the ImageView’s drawable to be null.
• Similarly, override the the onStart() method so it loads and displays the
image when the Activity is (re)started. You can just move the relevant
code from onCreate().
Once you’ve done this, try navigating to the BirdActivity and clicking the
button to go to the BlankBirdActivity. Use the “Initiate GC” button to
force a garbage collection sweep—does the image’s memory get cleaned
up now?
• While this does mean that we’re not using as much memory when the
Activity isn’t shown, it does mean that returning to the Activity is
slightly slower (since we need to reload the image). This is the fundamental
trade-off in all of programming optimization: use of space vs. use of time.
222 CHAPTER 25. MEMORY MANAGEMENT

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.

A.1 Building Apps with Gradle


Consider the included Dog class found in the src/main/java/edu/info448/review/
folder. This is a very basic class representing a Dog. You can instantiate and
call methods on this class by building and running the Tester class found in
the same folder. - You can just use any text editor, like VS Code, Atom, or
Sublime Text to view and edit these files.
You’ve probably run Java programs using an IDE, but let’s consider what is
involved in building this app “by hand”, or just using the JDK tools. There are
two main steps to running a Java program:
1. Compiling This converts the Java source code (in .java files) into JVM
bytecode that can be understood by the virtual machine (in .class) files.
2. Running This actually loads the bytecode into the virtual machine and
executes the main() method.
Compiling is done with the javac (“java compile”) command. For example,
from inside the code repo’s directory, you can compile both the .java files
with:
# Compile all .java files
javac src/main/java/edu/info448/review/*.java

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

Practice: Compile and run this application now.


Practice: Modify the Dog class so that it’s .bark() method barks twice
("Bark Bark!"). What do you have to do to test that your change
worked?
You may notice that this development cycle can get pretty tedious: there are two
commands we need to execute to run our code, and both are complex enough
that they are a pain to retype.
Enter Gradle. Gradle is a build automation system: a “script” that you can
run that will automatically perform the multiple steps required to build and
run an application. This script is defined by the build.gradle configuration
file. Practice: open that file and look through its contents. The task
run() is where the “run” task is defined: do you see how it defines the same
arguments we otherwise passed to the java command?
You can run the version of Gradle included in the repo with the gradlew <task>
command, specifying what task you want to the build system to perform. For
example:
# on Mac/Linux
./gradlew tasks

# 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)!

A.2 Class Basics


Now consider the Dog class in more detail. Like all classes, it has two parts:
A.2. CLASS BASICS 225

1. Attributes (a.k.a., instance variables, fields, or member variables). For


example, String name.

• 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).

2. Methods (a.k.a., functions). For example bark()

• Note the method declaration public void wagTail(int). This


combination of access modifier (public), return type (void), method
name (wagTail) and parameters (int) is called the method signa-
ture: it is the “autograph” of that particular method. When we call
a method (e.g., myDog.wagTail(3)), Java will look for a method
definition that matches that signature.

• 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 one of the methods, .createPuppies() is a static method. This


means that the method belongs to the class, not to individual object instances
of the class! Practice: try running the following code (by placing it in
the main() method of the Tester class):
Dog[] pups = Dog.createPuppies(3);
System.out.println(Arrays.toString(pups));

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.

Practice: Try to run the code Dog.bark(). What happens? This is


because you can’t tell the “template” for a Dog to bark, only an actual Dog
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).

• In Android, static variables cause significant memory leaks, as well as


just being generally poor design.
226 APPENDIX A. JAVA REVIEW

A.3 Inheritance
Practice: Create a new file Husky.java that declares a new Husky class:
package edu.info448.review; //package declaration (needed)

public class Husky extends Dog {


/* class body goes here */
}

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;

public interface Huggable {


public void hug();
}

This is an example of an interface. An interface is a list of methods that a


class promises to provide. By implementing the interface (with the interface
keyword in the class declaration), the class promises to include any methods
listed in the interface.
A.5. POLYMORPHISM 227

• 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

Dog myDog; //= new Dog();

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

The biggest benefit from polymorphism is abstraction. Consider:


ArrayList<Huggable> hugList = new ArrayList<Huggable>(); //a list of huggable things
hugList.add(new Husky()); //a Husky is Huggable
hugList.add(new TeddyBear()); //so are Teddybears!

//enhanced for loop ("foreach" loop)


//read: "for each Huggable in the hugList"
for(Huggable thing : hugList) {
thing.hug();
}

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

A.6 Abstract Methods and Classes

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.

Consider the GiftBox class, representing a box containing a TeddyBear. What


changes would you need to make to this class so that it contains a
Husky instead of a TeddyBear? What about if it contained a String
instead?

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.

• Warning: always use single-letter variable names for generic types!


If you try to name it something like String (e.g., public class
GiftBox<String>), then Java will interpret the word String to be
that variable type, rather than refering to the java.lang.String class.
This a lot like declaring a variable int Dog = 448, and then calling
Dog.createPuppies().

Practice: Try to make the GiftBox class generic and instantiate a


new GiftBox<Husky>
A.8. NESTED CLASSES 231

A.8 Nested Classes


One last piece: we’ve been putting attributes and methods into classes… but we
can also define additional classes inside a class! These are called nested or
inner classes.
We’ll often nest “helper classes” inside a bigger class: for example, you may
have put a Node class inside a LinkedList class:
public class LinkedList {
//nested class
public class Node {
private int data;

public Node(int data) {


this.data = data;
}
}

private Node start;

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

Android applications are user-driven graphical applications. In order to become


familiar with some of the coding patterns involved in this kind of software (with-
out the overhead of the Android framework), let’s consider how to build simple
graphical applications in Java using the Swing library
This appendix references code found at https://github.com/info448-
s17/lecture02-activities, in the java/ folder. Note that this tutorial in-
volves Java Programming: while it is possible to do this in Android Studio, it’s
often easier to just utilize a light-weight text editor such as Visual Studio Code
or Sublime Text.
The Swing library is a set of Java classes used to specify graphical user interfaces
(GUIs). These classes can be found in the javax.swing package. They also
rely on the java.awt package (the “Advanced Windowing Toolkit”), which is
an older GUI library that Swing builds on top of.
• Fun fact: Swing library is named after the dance style: the developers
wanted to name it after something hip and cool and popular. In the
mid-90s.
Let’s look at an incredibly basic GUI class: MyGUI found in the src/main/java/
folder. The class subclasses (extends) JFrame. JFrame represents a “window”
in your operating system, and does all the work of making that window show up
and interact with the operating system in a normal way. By subclassing JFrame,
we get that functionality for free! This is how we build all GUI applications using
this framework.
Most of the work defining a Swing GUI happens in the JFrame constructor
(called when the GUI is “created”).
1. We first call the parent constructor (passing in the title for the window),
and then call a method to specify what happens when we hit the “close”
button.

233
234 APPENDIX B. SWING FRAMEWORK

2. We then instantiate a JButton, which is a class representing a Java Button.


Note that JButton is the Swing version of a button, building off of the
older java.awt.Button class.
3. We then .add() this button to the JFrame. This puts the button inside
the window. This process is similar to using jQuery to add an HTML
element to web page.
4. Finally, we call .pack() to tell the Frame to resize itself to fit the contents,
and then .setVisible() to make it actually appear.
5. We run this program from main by just instantiating our specialized
JFrame, which will contain the button.

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.

B.2 Layouts and Composites


What if we want to add a second button? If we try to just .add() another
button… it replaces the one we previously had! This is because Java doesn’t
know where to put the second button. Below? Above? Left? Right?
In order to have the JFrame contain multiple components, we need to specify a
layout, which knows how to organize items that are added to the Frame. We
do this with the .setLayout() method. For example, we can give the frame a
BoxLayout() with a PAGE_AXIS orientation to have it lay out the buttons in a
vertical row.
container.setLayout(new BoxLayout(container, BoxLayout.PAGE_AXIS));
container.add(theButton);
container.add(otherButton);

• 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

Threads and HTTP


Requests

This appendix introduces concepts in concurrency and threading, which are


used extensively by Android though a framework-specific classes and options.
For clarity, these concepts are introduced though a set of practice exercises in
straight Java (though similar code can be utilized in Android).
The code for these exercises can be found at https://github.com/info448-s17/
lab-threads-http.
Additionally, this appendix introduces the Java code used to send network
requests. Android will use exactly this code, but in order to experiment with
it separate from the Android framework you’ll be making network connections
directly from Java.

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.

C.1.1 An Example: Algorithm Races!

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.

C.1.2 Threading the Race

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

Figure C.1: Diagram of thread switching (source unknown)


240 APPENDIX C. THREADS AND HTTP REQUESTS

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!

C.2 HTTP Requests


Consider the provided MovieDownloader.java class (found in the
src/main/java/ folder). This Java code (which is directly portable to
C.2. HTTP REQUESTS 241

Android) accesses the database at omdbapi.com, a wrapper around the IMDB


API calls for getting information about movies.
You can run this program with the ./gradlew -q runMovies task. It will
prompt you for a movies to search for, and then print out the results (in JSON
format).
Practice: add descriptive comments to the downloadMovieData()
method, explaining what the code does and how it works. The goal is to
understand the classes and methods are that are being used here (particularly
the use of HttpUrlConnection, InputStream, and BufferedReader), and
demonstrate that understanding through explanatory comments. You should
also pay particular attention to the use of try/catch blocks (see here for one
explanation).
Note that we’ll utilize this exact code in Android, so you should be familiar with
what it is doing!

You might also like