Coming up next: Getting the Most
Out of Android Lint by Matthew 
Gharrity  SPEAKER: Howdy, 
welcome to another Android 
developer summit!  So how many 
people went to the last
one?
How many people enjoyed the last
one? We liked it here, which is 
why we did it immediately again 
three years later.  We were just
building suspense to make it 
extra special.  Hopefully we can
do it again.  A lot of this 
content, all of the -- most of 
the content is live streamed, 
all of it is being recorded.  If
you prefer consuming this 
through a live stream, I can 
pause occasionally and buffer 
and maybe that will make it more
of a live stream situation for 
you.  I'm Che it, I
-- Chet, I lead the toolkit 
team, I'm emceeing today.  And I
am making it a nice enough 
event, but short enough so you 
are not tired of the emcee.  I 
will tell you how this thing is 
going to work today.
So Dan went over some of it, and
I assumed you went over the 
keynotes as much as I do.
The most important to me is 
office hours, there are two 
reasons why we like to have 
these events in Mountain View, 
it is such an interesting city 
with so much to do, we live 
here, and every day we are here.
So obviously, Mountain View, for
the culture.
And the second reason is Google 
is just up the street, which 
means we can drag a lot of 
engineers from the team that 
build all of the bugs that you 
are now dealing with into the 
product, and we like to bring 
them into the conference to help
answer all of your questions.  
The key thing for you to know, 
though, is all of those people 
aren't here all day.
We have broken up the event into
four half-day sessions, and 
there are different people in 
different teams in different 
technoloies represented at 
different times.
And so there are three scheduled
posted, there are screens around
the conference.  This is one of 
the screen sscreens, if you 
can't read this, maybe you can 
read the screens outside the 
room instead.  There's a 
rotating schedule, will tell you
when people are on where, if you
have a question for the location
team, make sure you are at the 
right time.  You can skip 
sessions if you want, they are 
recorded, and there are people 
in all of those sessions.  So 
ask the right questions, find 
people, they are here at the 
right time.  There's App Review 
clinices, there's the hashtag on
Twitter, #AndroidDevSummit, 
there's the  application.  And 
if you don't have it, it is an 
instant app.  Do your favorite 
search in the search engine, 
click on the link if  -- for the
instant app and it downloads 
instantly.  There's a party 
tonight after the session after 
6:00, that will kick in, a party
with a bunch of engineers, where
we all have the excellent 
experience of talking to each 
other while the DJ is playing 
the loud music and having 
conversations that go along the 
lines of, what!  What!
Enjoy the party, enjoy those 
deep conversations with the new 
friends, that's about it.  And 
one more thing, there are three 
different session links.  
There's the long session, 40 
minutes.  There are shorter 
sessions of 20 minutes.  The 
intention is to link these 
things, you will go for both of 
them at one slot.  It is awkward
to get up in the middle of a 
crowd, so we are about to see 
two in a middle here.  Please 
stick around for both of them, 
if you need to get to one on the
other side, try to do so quickly
and quietly, we are going to 
roll from one into the other.  
And then there's lightning talks
that start at 12:05 and will run
for an hour, those are going to 
go back to back into each other.
So stick around for 
those.
And that is about it.  I'm 
standing in the way between you 
and technical content.  I would 
like to bring up Matthew 
Gharrity to talk about Lint.  
Thanks.
[ Applause ]
MATTHEW GHARRITY: 
All right, let's get started.
I'm Matthew, on the Android 
Studio team, I work on project 
stability, call integration, and
we will go to the
next slide.
And one of the most exciting 
things I get to work on besides 
project stability is Android 
lint.  It is a very powerful 
tool, you know a lot about it, 
but you might not know the cool 
things it can do.
So despite the name, it is not 
just a linting-tool, it is a 
full-fledged stack analytics 
framework, it is powerful, you 
can write your own checks and I 
will talk about
that today.
No
worries.
This talk is really for anyone 
really interested in programming
languages, compilers, stack 
analysis, and for anyone that 
has to deal with bugs in the 
project.  So that is for most
of you.
Yeah, one last thing, I only 
have 20 minutes, there's a lot 
of material, I will go fast 
through the slides.  Try to keep
up.  At the end of the day, you 
can watch it in half speed on 
YouTube and get a sense of it 
then.
Perfect.
 All right, let's get started.
So what is Android lint? A lot 
of you probably already  know, 
if you don't know the name, you 
know what Android lint is.  It 
shows up in the IDE saying that 
you did something wrong.  In 
some cases, it is like telling 
you that Gradle is out of date, 
do you want to update it, or 
maybe you are not calling a 
super method in a method that 
expects you to do so.  In 
Android Studio, in the IDE, it 
will say that there is 
suspicious code, take a look.  
Sometimes there are warnings, 
sometimes there are errors that 
will fail the builds.
So some of you might not know 
it, there's a lot of the ways to
use Android Lint.  The first is 
on-the-fly, that I just 
mentioned, in the IDE as you are
typing, shows the error messages
right away.  You can also do 
inspect code, this is an action 
in Android Studio where you can 
run a batch analysis of the 
project, a good way to audit 
your project for common errors. 
And an action called run 
inspection by name.  If you have
a particular bug in mind, say 
you want to find a lot of thread
annotation violations, you can 
run the inspection by name, run 
the entire projects in one batch
analysis and look through those 
one pie  by one.  You can run 
Lint on the command line with 
Gradle, Gradle Lint debug, it 
will do a batch analysis for the
project.  And the great thing 
about that is it will make an 
html and XML report.  You can 
parse the XML report, or you can
do the html report and do the 
warnings.
If you know what Lint is, I want
to dive into the advanced use 
cases, because it is a powerful 
tool.  I will talk about 
configuration, and it is 
actually really important.  For 
big projects, you want to make 
sure that Android Lint is 
working for your projects and 
needs.  I will talk about 
annotations, it is a powerful 
way to mark up your code to help
Lint help you more.  Lint does 
as much as it can, but sometimes
you need to give it hints to 
help you more.  We will talk 
about Lint internals, I'm 
excited about this, I want to 
give you a good mental model for
how it works under the hood.  
This will get you set up for 
kind of realizing how powerful a
tool it is and writing your 
customcustom Lint chuck schecks.
We will talk about 
configuration.  The easiest way 
to see what Lint is capable of, 
Android Studio, preferences, 
inspections window.
You will see the heading, Lint, 
and all of the inspections that 
lint  has.  There are are 
hundreds, most are enabled by 
default.  There are some  that 
are not, and take a look at 
them, some of them may apply to 
your project, even though we 
don't enable them by default.  
One example is call 
interoperability, this is a set 
of checks we created so you can 
call it from link code and vice 
versa.  If you do something that
makes it difficult, Lint will 
tell you to re-name the function
so it is easy to call from the 
other language.  There is 
another way to configure Lint, 
with Gradle.  If you go into the
Build.gradle file, you have this
Android block, as you know.  You
can write a Lint options block, 
and there are powerful options 
here.
So this first option, I will go 
through a few examples.  This 
first option is a powerful tool,
if you have a big project, you 
want to pay attention to this.  
The Lint baseline, when you add 
this to your options, Lint will 
look at all of the warnings and 
errors you currently have in the
project.  And this will give you
a clean slate, so you can say, 
hey, I want to take a baseline 
of this point of the project, 
forget about all of the 
thousands of warnings that I 
already have.  And I only want 
to look at new warnings and 
errors and new code that checks 
it.  And this will help you get 
a handle on the massive amount 
of code that you have so you can
have clean code going forward.  
When you have more time, free 
time, you can knack  go back and
look at the baseline issues that
Lint stashed away.  Once you 
have a clean slate, through a 
baseline, or you want to do a 
new project, you want to say, I 
want to have clean code forever,
starting now, you can turn on 
warnings as errors.  I encourage
you to try it out.  And if you 
want clean code, try to get this
to work for you.  And then, like
in the last slide, you can 
enable specific checks that are 
not on by default, like 
interopability.  Some of you may
have this Lint options block set
up.  And some more advanced 
things I want to talk about is 
performance issues.
  So one of the reasons we have 
this lint  Lint options block is
a lot of people run Lint on the 
continuous integration server, 
they are checking the 
application for warnings, 
errors, they block Lint submit 
if you have it checked in.  And 
some people are concerned with 
performance.
So just a few tips here, real 
quick.
Try to avoid the check all 
warnings option, I know it is 
tempting.  I know I told you to 
enable more checks, but some of 
the checks are off by default 
for performance reasons, for 
example.  If you check all the 
warnings, man, my time to check 
the project just doubled.  Be 
more selective in the checks 
that you
turn on.
 Also avoid the Gradle lint 
tasks, it is a parent task of 
Lint debug or release.  It will 
run it multiple times on the 
project in each Lint variance, 
that's a simple gotcha to avoid.
  Use the ignore test sources 
function.  By default, we don't 
look at the test sources for 
warnings or errors, the theory 
that you don't care about how 
clean your tests are.  But this 
option makes it so you don't 
parse your test sources at all. 
So presumably, all of you have 
well-tested code.  So this 
option will help with that, to 
make things go faster.
  Let's switch gears a bit, I 
want to talk about annotations. 
They are a really powerful way 
to live Lint hints for specific 
issues that you want to look 
for.
And so, I'm just going to go for
a few examples, you know about 
the nullability annotations, you
use it a lot in Java, you don't 
need them, it is built into the 
language.
But we also have really 
Android-specific annotations, 
that you should check out.  
There's the required permission,
this is an interesting example, 
we kind of annotate certain 
Android APIs with requires 
permission, like the set 
wallpaper permission, and Lint 
does analysis on the code that 
calls into the API, is the 
program checking that they have 
this permission? If you don't, 
we can worry, hey, it looks like
you have not checked you have 
this permission, this may crash 
at runtime.  And this impacts 
the users quite a bit.  There is
also one of my favorites, kind 
of annotations, which is UI 
thread and worker thread 
annotations, there's thread 
annotations in url.
And these are really important, 
actually.
So a lot of -- a big problem 
with application development is 
a lot of UI frameworks require 
that you only up Tate the UI on 
a single thread, the network 
thread, and everything else is 
on background threads.  If it is
on the UI thread, it might 
block, you will not see a crash 
report, the user is frustrated, 
it is hard to track these 
issues.  We have these threat 
annotations where we say these 
method should only be called on 
the UI thread, or the worker 
thread.  And we have some 
analyses.  So I will actually do
a demo of these analyses.  
Hopefully that is set up.
So let's switch
over.
Awesome.
So I will go through an example.
This is just Kotlin, let's say 
that we
have a function, oh, the screen 
is black.  You're
right.
It was there for a
second.
Awesome.  Let's get started.  We
have a function, update UI, this
is run on the UI thread.  This 
is updating the user interface, 
whatever you want, and the 
network request.  This is a 
contrived 
example.
And this does some stuff.
All
right, and unfortunately, 
someone decided to call a 
network request from the update 
UI method.  So this is a 
problem, as you can see.  There 
is no errors or warnings, and 
studying this is hard to track 
down in general.  If you know 
about the threat annotations, 
you can add the UI thread here, 
you can add the worker thread 
annotation here.  And instance  
instantly, Lint says you are 
making a worker request on the 
UI thread, that freezes the UI, 
you don't want that.  So I 
encourage you to use these.  And
you might think, this is a trite
example.  This is too simple.  
In the application, we have the 
UI method that calls the 
function, FU, and that function 
calls something called bar, and 
then that bar function calls the
network request.
There is just multiple layers of
indirection here.  And you might
think that Lint cannot help us 
here.  It can't be default, it 
for performance reasons, we 
don't do advanced analyses.  We 
have an analysis for this.  I 
will show you an example of 
that.  You can do run inspection
by name.  The name of the 
inspection is called wrong 
thread, and then 
interprocedural.  You select 
that, select the scope, I will 
say module scope.  And bam, it 
will find the path from Fu to 
bar to network request.  You 
cannot see the error message 
right here, so I will copy it 
for you.  I will remove the 
class names, you can see the 
error message says, hey, you are
doing the UI, going to fu, bar, 
the network request, that is 
annotated with worker thread and
that is an issue.  Let's go back
to the slides.
So please check that analysis is
off by default for -- it is 
being performant and doing a 
program analysis, pretty 
expensive.  Use it when you can,
try to see if you can find any 
bugs in the application.
All
right.
NERBS  next I will talk about 
Lint internals.  This part is 
really cool.
The way Lint works, it starts 
out parsing a source file, so 
you have some Java or Kotlin 
laying around, it builds 
abstracts syntax tree, and then 
we wrap
it in UST, universal Syntax 
Tree, and this may sound pretty 
familiar.  And this step is 
really important because it 
means that when you write a 
check, you only have to write it
once, it works in both Java and 
Kotlin.  That is really 
convenient.  When you write a 
Lint analysis, you are writing 
directly on the UAST.  When you 
are writing the Lint check, you 
are scanning the AST for the 
source code, calls, and 
expressions you are interested 
in and that is how it works.  
And Lint actually works on all 
types of files, so you can look 
at Gradle files, you can look at
XML files and resources.  This 
is powerful, it gives a 
wholistic view of your 
application and can provide a 
lot more helpful messages with 
that.
A couple more points here, so 
type information is available.  
It is not just -- you are not 
just looking at text, you have 
the whole AST, you can do method
resolution, examine the class 
hiarko of of the class you are 
looking at, that is important 
for preventing false positives 
and having powerful Lint checks.
That is what makes Lint useful. 
We have tight integration with 
the IDE.  And Lint does show you
the warning messages right in 
the IDE.  That is critical, if 
you don't have that, a lot of 
your messages are lost in some 
build-up somewhere and it is not
useful to most people.
And so, with that, we are going 
to move into writing custom Lint
checks.  This means that, you 
know, Android Lint is a 
framework.  You can write your 
own Lint checks.  If you have 
something that annoys you, a 
common bug that you run into on 
your team, or something that you
want your colleagues to watch 
out for, you can run your own 
Lint check, upload it to the 
code base, and all of your 
colleagues will see in the I it 
DE this highlighted.
  And Lint has an unstable API, 
we don't promise that we keep it
the same from release to 
release.  The good news is that 
it is not changing too often, 
but no promises.
The other good news is that 
unstable APIs are really fun to 
play with.
All right.  And with that, we 
are going to move into the demo.
So let's switch over, awesome.
All right.
So don't bother reading code too
much.
The example I want to set up 
here is that you see that your 
call logs are calling 
thread.yields.  If you look at 
the documentation, there ask  is
not a useful method.  If you are
running concurrent data 
structures, it is useful.  But 
in other ways, you want to avoid
it.  It doesn't do what you 
think it does.  So let's say we 
will write our own lint check 
that calls thread.yields.  So 
right now, it is not 
highlighted, there are no Lint 
checks yet.  I want to show you 
how I have this set up.  You 
have an Android application 
module here, here it is called 
app.  The way you will write 
your on Lint checks, you will 
have a Java module, checks, for 
example.  And you are going to 
add a dependency from your 
Android application to the 
checks module.
And we have this nice Lint 
checks block here that makes it 
easy to do that.  Once you have 
that set up, and I'm skipping 
over some of the details here, 
but I'm going to have some links
at the end.  I can look at 
sample projects.  Once that is 
set up, I can go to the Lint 
check itself. You have a class, 
yield detector.  This is a 
detector for calls to 
thread.yields, it extends some 
classes in the Lint framework, 
and I have typed up some of the 
metadata for this check that we 
want to have.  There's a name, 
brief description, and 
explanation for how to fix the 
issue, the severityseseverity, 
whether it breaks the build, 
etc. And once I have the 
metadata, I'm going to start 
typing some code here.
So one method to be aware of is 
called Git applicable method 
names.
So the way lint works is, you 
know, there's a couple options. 
You can make your own AST 
scanner, which will give you 
total control.  You can scan the
source file, looking for 
whatever you want.  And for 
performance reasons, we have 
these hooks where you can 
register with Lint, hey, I'm 
only interested in method calls 
with this particular name.  And 
so here, we are actually only 
interested in method calls with 
name yield.
And if I have any typos, it is 
not going to work.  So let me 
know.
Haha.
Once you have the hook in place,
we have to write the hook 
itself.  So here, we can say 
visit 
method.
And I wil will make
sure that is -- this method is 
only called when we come across 
a yield function call.  We will 
say, okay, let's assert that the
method name we are looking at is
actually yield.  So this should 
always be the case.
And then, because we have type 
information, we can do due 
diligence and making sure we 
don't have a false positive 
here.  Maybe there's another 
class with a function called 
yield, and we want to make sure 
that the Java
link thread.yield.
So we will check to make sure we
is have the methods, look at the
evaluat evaluator, is the method
in class, we will pass the 
revolve method node and give it 
the class name.  So 
Java.link.thread.  So this means
that if this method that they 
are callingging is part of the 
exact class, we can record the 
error. 
And context.report, I will give 
it the issue that holds the 
metadata we have above.  I will 
give it the node, I will give it
the locations.  We are telling 
Lint, this is where the error 
is, what you should highlight.
And I think I have to give it a 
message.
So let's say, please don't use 
this.
And also, you should use a more 
helpful error message.
All right.
That's all it takes.
If I have no typos, I'm going to
make the
projects.
It looks like it is done.  If I 
go back to our application, bam,
lint scanned the source code, 
picked up the check right in the
IDE.  And this is powerful.  You
can make it pop up for your 
colleagues and everything.  
Awesome, we will go back to the 
slides.
All right.
So 50 seconds left, happy with 
the timing here.
Here are some links, I want you 
to check them out.  If you never
read the overview on the 
development page, go there, 
there's a lot of information 
from this talk and a little bit 
more. 
Please  check out the 
annotations page, there are so 
many that can prevent a lot of 
bugs ahead of time.
If you are really excited about 
this demo I just gave, check out
the sample project on GitHub, 
where you can run custom Lint 
checks.  If you have an issue, 
we have a Lint mailing list.  
Many of you know Tor, he is 
active on the mailing list and I
am, too.  We answer all of the 
questions that are asked there. 
And with that, thank you so 
much.
If you have any questions, I 
don't -- don't ask them now, I 
wills  will be in the sandbox 
afterward.  Please check out 
custom Lint checks.  Thanks.
[ Applause ]
Coming up next: Testing Rebooted
(with AndroidX Test) by Yuki 
Hamada, John Gerrish  SPEAKER: 
Good morning, everyone.  Great 
to see so many people here 
interested in testing.
My name is John Gerrish, with my
colleague Yuki Hamada, we're 
presenting a session on testing 
APIs today.
To get started, hands up, anyone
who has written unit tests.  It 
is not a trick question, good.  
And what about integration 
tests? You can be honest.  
Awesome.
Okay.
So let's get
started.
Click.
SPEAKER: I have a question while
you are setting up.  Is this 
related to the project 
languages? 
SPEAKER: There's a project 
nitrogen talk tomorrow, it is 
near the end of the day.  I 
strongly encourage CHERK 
checking it out.  The topic we 
are introducing today is part of
the grander vision, along with 
project nitrogen, and they work 
in con 
conjunction.
Any more questions? We can do 
this in 
reverse.
[ Laughter ].
  SPEAKER: I will ad-lib this.  
On Android, there are two kinds 
of tests you might be familiar 
with.  There's local unit tests,
and then there's instrumentation
tests.  So let's start by 
looking at local unit tests.
So these are ones that -- these 
are tests that are executed on 
your local developer's 
workstation, on the local VM 
running there.
And, because you don't need to 
run the entire Android build 
chain, to avoid dexing, 
packaging, installing on the 
device, these tests are actually
really, really fast.  And on 
these kinds of tests, you can 
use a tool like 
robolectric, which comes with 
its own set of testing APIs to 
set up the state for your 
Android aenvironment, or use a 
tool like Mokito and you can 
step out the interactions with 
the Android framework.  And 
either way, they allow you to 
write tests, setting up the 
state of the environment that 
satisfied the preconditions that
you might want in your test 
case.
The second kind of tests are 
instrumentation tests.  And 
these are the tests that will 
run on a virtual, or a real, 
device.  A real device could 
just be a phone connected to 
your workstation, or if could be
a form of devices somewhere in 
the cloud.
These kind of tests run slower 
because you have to execute the 
whole build chain and install an
application on to the device.  
But they have the advantage of 
being a lot more accurate, 
because the real lure of a 
virtual device is very similar, 
or in some cases identical, to 
devices your users are using out
until the field.  And this 
brings you the confidence your 
app will behave as expected.
  One criticism we heard, on 
these tests, there's a lack of 
testing APIs available.  And 
that makes it difficult for you 
to set up the state of your 
environment in a way that 
satisfies certain preconditions,
or edge cases, that you might 
want to be testing.  We heard 
you loud and clear, this is 
something that we are
actively working on.
So a bit of a history less on, 
2017, Android I/O, we wrote out 
the testing story, the software 
development pyramid.  In this 
model, we encouraged you to 
write lots and lots of fast, 
scalable unit tests that tested 
all of your exhaustive 
conditions.  We encouraged you 
to write a smaller number of 
instrumentation tests that will 
actually prove that all of these
units assemble together, behave 
as you would expect, on a real 
device.
And, in some ways, this was kind
of a compromise.  It was a 
compromise between the 
advantages of one set of -- one 
kind of tests, and the tradeoffs
of another.  It brings you a 
wholistic way of testing your 
app.
And we showed how this kind of 
approach can lead to 
test-drivern development on 
Android.
First of all, you would start 
with a failing UI test.  This 
would be an instrumentation 
test, probably written with 
Espresso, and it would test the 
UI of your component, or your 
feature.  And then you would 
satisfy that feature by a series
of units, classes, coming 
together that are interactions.
And you could test drive these 
as well, using a tool like 
roboelectric, or Mokeyto, as a 
unit test.  And this gives you 
fast development cycles.  When 
you bring them all together, you
can run the slower-running, but 
faithful, instrumentation test 
and hopefully it goes green and 
you're done.
Well, we enter a refactoring 
cycle because maybe your code 
leaves a little to be desired 
and you want to do some 
clean-up.  You can spend some 
refactoring cycles there before 
coming around to the beginning 
of the cycle where if you have 
any more work on the feature, 
you might add another test, test
another aspect to the that 
feature.  And you will keep 
iterating until you are 
complete, at which time you are 
good to submit your code and 
move on to the next task.
So at Google I/O this year, we 
realized there was somewhat of a
test writing crisis.
And because there's so many 
tools available, it is not 
always clear which one to use.
And each of these tools all have
their own different styles and 
APIs and paradigms for the same 
concepts that exist on Android.
And the problem with this is 
that tests written
in different levels are not 
portable across levels, it is 
stuck in the environment that 
you have written on.  So this 
year, in Google I/O, we 
announced the render next test, 
it brings in testing as part of 
the tool chain, as part of 
Jetpack.  It includes some of 
the existing libraries you've 
used before, some new APIs, full
Kotlin support, which allows you
to run beautiful and concise 
tests.  It is available on and 
off device.
Well, last week, AndroidX test 
moved out of beta into release, 
1.0.  And it is also, as of last
week, fully open-sourced.  So we
look forward to welcoming your 
contribution contribute  -- 
contributeutions.
.  All of it is being revamp ed 
to show you the new styles of 
APIs.  So please go and check 
that out.
Let's take a look inside.
The first module that we pulled 
across was the existing JUnit 
for support, the runner and the 
rules that you may have
used before.
We've already added a new 
module, which we call core.  
This includes some new APIs: 
ApplicationProvider, as its name
suggestsZsuggests, is a quick 
and easy way to get a hold of 
the application context.  
ActivityScenario, which is a 
brand-new API that provides a 
course and 
fine-grained activities, and 
FragmentScenerio that provides a
set of testing features for 
fragments.
We also brought
Espresso into the AndroidX 
family.  It is a set of 
view-matching libraries -- a set
of view matching APIs and 
actions, allowing you to match 
and interact with those UI 
elements.
It also includes some other 
things like the ability to 
capture intents for the system.
And finally, we've also released
some truth Android extensions.  
And truth is Google's open 
source, fluent testing 
assertions library.
And we brought a bunch of 
components for Android subjects 
, which allow you to test your 
Android objects beautifully and 
concisely.
And those of you who have been 
using roboelectric will know 
that we had a version, 4.0, in 
beta for a while.
And, as of last week, we did a 
simultaneous release that has 
now gone into final.  
Roboelectric four fully supports
all of the unified APIs that are
in AndroidX test, as well as a 
number of its own new features 
and 
improvements.
Okay.  So I would like to 
welcome Yuki on stage, who will 
give a deeper dive into some of 
the APIs available. 
IA
[ Applause ]. 
SPEAKER: Thanks.  Hi, everyone.
So let me introduce our new API.
This starts with 
ApplicationProvider, a new way 
of accessing context from your 
test support.  So when you look 
on Android testing, you need to 
handle two different context 
objects.  The first one comes
from application under your 
test, and the second one comes 
from the instrumentation APK, 
where your test code is
stored.
So with today's library, we have
two different methods to access 
this context object.  And this 
makes your test code harder to 
understand, because the
library used, targets the 
context meaning the context from
your instrumentation APK, while,
sorry, the target context means 
that context from your 
application while get-context 
means the context from your 
instrumentation or APK also -- 
it is not obviously which one to
use for your tests.
So, in our new API, we hide the 
instrumentation context from 
target API, and we only -- only 
the ApplicationProvider provides
an application context in the 
form of your application graph.
And let's take a look at the 
example.
So here, let's say we have a 
test for the location tracker 
activity.  And in the set up 
method, we get the target 
context in the old-fashioned 
way, and we type cast the 
location tracker application, so
that we can reduce the mark 
object for testing, and the 
second line, set a mark object 
for that.  This code is simple, 
and it actually works.  But you 
could use the run context and 
face the runtime error, and 
ended up with wasting your
time for debugging.
Now, with the new way, 
application context provider 
provides your context in the 
application graph.
So you can do exactly the same 
thing, but there is less chance 
for confusion.
Okay.
And let me move on to the more 
complicated stuff, activity 
scenario.
Actually, before I -- we dive 
into it, I have a few questions 
for you.  How many of you have 
written your own activity, and 
handled the live cycle 
transitions by yourself? Raise 
your hands if you are.  And how 
many of you have shifted your 
activity with a bug related to 
life cycle?
Many of you.  Who didn't write 
the tests for that? Who did not 
write the tests?
Cool.
Well, yeah, I see some hands up,
keeping up.  And to be honest, I
do, too.  And I agree.
Writing tests for the activity 
life cycle transition is pretty 
hard, and there is no good API 
in the testing library now.
So that's why our team have 
sought for a solution, and we 
developed the ActivityScenario, 
which you can use it for driving
your activity state to 
laboratory state for
testing.
So let's visit the activity 
state first.
So the creative state is where 
activity is created, and 
instantiateinstantiated.  But it
is not visible to users yet.
Or activity, sorry, can be 
created state, while it is 
running in background.
And the started state is where 
the activity is created and 
started.  It is partially 
visible to the users, but not 
the fore ground of activities.  
The activities running in 
picture mode is also in this 
state.
And the  the resumed state is 
where the activity state is 
visible to users and running in 
the fore ground.
And under the framework, you can
change the life cycle state 
anytime the user interactions, 
so the activity has to handle 
those transitions properly for a
good user experience, otherwise 
you see some bugs.  And activity
scenario provides a method 
moveToState, where you can move 
it to testing.  We will look at 
the testing code.  We have a 
test, location Tracker Activity,
and here we want to verify that 
location listener is properly 
registered from the system when 
the activity becomes a creative 
state.
So across, we have to start the 
activity.  The launch activity 
takes your activity graph and it
starts the activity and waits 
for it until it becomes a
reasonable state.
And move to state initiates the 
transition, and moves the life 
cycle state to the created 
state. And the older method, the
older ActivityScenario method 
works as a broken code.  So 
after this method call, it is 
guaranteed that activity's life 
cycle state to be created.
And then you can inspect your 
activity's internal state by 
calling activity method. Yes, 
that is easy, and now we have 
our API.
And also you can use 
ActivityScenario for testing the
creation of
your activity.  So activity 
creation happens when your 
activity is running in 
background for a long time and 
where you can come back to it 
later.  And your activity has to
save its
internal state, to a saved 
instance state bundle, where it 
is destroyed, otherwise you will
lose the state.  We have a 
re-create where you can use the 
testing scenario.  This is the 
example code.
  So here, in this test, we 
wanted to make sure that input 
text is restored properly after 
the activity is re-created after
disruption.
So first, we have some test 
data, like test user, as our 
input.  And again, we started 
the activity.  And then we input
-- we filled the text box by u 
using the Espresso library.
The ActivityScenario works 
nicely with Espresso, and we 
call re-create, which disrupts 
the activity, re-creates the 
instance, and waits for the 
activity to be a reasonable 
state.  And using the Espresso 
again, we can make sure that the
text is there as we expected.  
Yes, it is also simple.
Okay.  And finally, I would like
to show you one more example 
from the Truth extension.  
Intent Subject, by using Intent 
Subject, you can verify your 
Intent values and it produced 
really good schema-friendly 
error messages, if that error 
happens.
This testing example, again, 
this time, we want to make sure 
that Intent -- we want to make 
sure that the data intent has an
expected contact name in extras.
So first, we create a bundle of 
the data intent.  And then we 
start with three lines, one is 
checking if that intent has the 
expect action, the second is 
type, and the third is extra 
bunted  bundles.  If the value 
does not meet the expectation, 
you see this error.  And you can
immediately know the, in this 
example, the intent action is 
not what you expected.
And there's a lot of components
that come under the release 1.0.
I cannot show you everything 
today.  For example, we have 
more assertions, and we also 
have Android Builders where you 
can create your testing data 
easily.  And also scenarios for 
activity and fragment.  You can 
look at the documentation to see
more.  I hope you try it out 
after the talk.
Okay, so this is our solution 
for the test writing crisis.
So with unified API, you no 
longer consider whether you have
to write an instrumentation test
because you can now just write 
the Android test, and that test 
runs on both runtime 
environmentsenvironments nicely.
With the unified API, you can 
focus on what to test and you 
can forget about where and how, 
and to ensure the contistancy of
the behavior of our API, we have
a verification test and we run 
the same tests against the, 
sorry, we run the same test on 
locally 
with Robolectric and on a bunch 
of devices from the API 15 to 
the latest 
version.
And let's go back to the 
workflow that we showed you 
earlier in this talk.
So we can execute the test 
driven development much more 
effectively using the device 
agnostic tests, with the tests, 
writing it in a unified API.
As a recommendation, we 
recommend that you write that 
test with
Robolectric until the call is 
ready to submit, and run the 
same test, but on a battery of 
devices before you submit the 
test to maximize your 
confidence.
And also, you can run the same 
test as a continuous integration
test against the
binaries.
With the upcoming nitrogen tool 
change, you can set up such a 
configuration easily.
If you want to know more about 
the Project Nitrogen, we have a 
session tomorrow, and we highly 
recommend that you attend it.
Thank you very much for 
listening, and have a happy 
testing.
[ Applause ]
.
Coming up next: Optimizing User 
Flows through Login by Sean 
McQuillan, Jeremy Orlow.
SPEAKER: Hey, everybody.  With 
that interstitial music, the 
point is to make you really 
happy for when the next 
presentation starts.  We will do
lightning talks that we have not
done before.  We will see how 
well it works.  The way it is 
supposed to work, we are going 
to roll from one into the other.
They have 5 minutes to go, when 
it is up, I will call time.  In 
the back, they are going to 
press a magic button that makes 
this sound.
So that's how we know it is time
for the next talk.  And 
otherwise, I have no purpose 
being here.  I will talk about 
the next speakers, optimizing 
user flows through login, and I 
will start my timer.
Thunderous applause for the 
lightning talk, please!
[ Applause ].
SPEAKER: Welcome to lightning 
talks, I'm excited to talk to 
you today about how to welcome 
your users to your Android app 
whether you when you get a new 
phone.  Why is this important? 
On the Android system, we get a 
new phone every 2 or 3 years and
we reinstall the apps that have 
been installed, and all of the 
users do this as well.  If you 
buy a new phone every two years,
half of your users are going to 
get a new phone this year.  And 
if you don't do anything at all 
with the application, you will 
give them a cold welcome when 
they come back.  You will show 
is  them an email and a password
form that shows them that they 
have never used it before, you 
will disrupt the flow of the 
app.  You want to welcome them 
back to the application and 
create the continuous user 
experience that jumps from their
old device to the new device, so
you don't have friction or 
retention problems for these 
users that are switching devices
and using your application.
There's a couple products you 
can use in Google that help you 
do this.  So one of them is 
Google sign-in, it is a button 
you press when you log in.  It 
is a button you 
press, you log in, it is amazing
for the flow because it makes it
simple to do the log in.  It is 
good for the activation because 
it makes it easier to log into 
the app.  In your phone, it is 
good for reactivating the users 
because they have to click a 
button.  On Android, you can 
configure it so you don't can  
have to click the button on the 
new device.  
Google sign-in works on iOS, 
Android, as well as the web.  So
you can use it everywhere, and 
it is a great way to add 
federated log-in to your 
application.
And another feature on Android 
is Smart Lock for passwords.  It
is a password manager, stores 
user names and passwords, 
federated credentials in the 
cloud, when the user confirms 
they want to save the password 
after they log in.  So in the 
old phone, they save the 
password, on the new device, in 
the application, you will give 
them a warm welcome and say, I 
know who you are, do you want to
log in with the new account? And
it will create the seamless 
experience and log the user into
the application.  One of our 
partners, Netflix who is here 
today, they used this and they 
found that they had 20 percent 
fewer support requests.  If you 
think about this, every time a 
user contacts you to get a user 
name and password fixed, there 
are five that didn't, and they 
will just become a retention 
problem and may never come back.
This is a great way to improve 
your business metrics.
  And Autofill is another 
feature we launched in Android 
O, works like smart lock.  It is
an Autofill system, it Autofills
TextViews the way you would 
think, and one of the most 
common ways is user names and 
passwords.  After they log into 
the old device, they will choose
to save it to the Autofill 
service, that comes to the same 
data store, smart lock.  In the 
new device, it Autofills.  
There's a few ways to get the 
application ready for Autofill. 
One is to set up a digital asset
link that links the web and the 
Android app.  This is great, it 
has pluggable password manager 
and all of them support this and
then the user can log in on the 
web.  If they are using the same
password manager on the Android 
device, they are go to  going to
get the credentials transferred.
And then the password is saved 
and you get the seamless 
experience to the Android 
application.  And another thing 
you have to do for Autofill, you
should set up Autofill hints.  
Tell the service what the user 
name and the password field is, 
and that way the service does 
not have to guess and maybe get 
it wrong, probably the worst 
experience you can give your 
users.  And the last thing we 
are going to talk about is auto 
back up, I will hand it over to 
Jeremy. 
SPEAKER: It is a great way to 
also provide continuity to your 
users.
And so even if your app syncs to
the cloud, there are still 
device specific settings you can
back up, and no user who has 
been using your app for a year 
wants to see a tutorial on how 
to launch an app on the new 
phone.  So auto back up, when 
the phone is idle, we back up to
the cloud, the data is stored 
securely, you can have up to 
25MG, if you go over that, the 
back ups fail.  On the new 
phone, when the user restores 
the app, it will restore the 
data before the app runs for the
first time.  And the user picks 
up where they left off without 
any friction or churn.
And you can also include and 
exclude specific files, so you 
can exclude OAuth tokens, and 
keep users engaged when you 
switch phones.  We will be back 
in the back.
[Gong ringing]. 
SPEAKER: Be around if you have 
any 
questions.
Coming up next: Kotlin + Watch 
Faces by Jeremy Walker.
SPEAKER: Hi, I'm Jeremy Walker, 
a developer platform engineer 
here at Google.  I wanted to 
talk to you about how I use 
Kotlin to make watch face 
creation much easier on Wear OS.
So watch faces are kind of hard 
right now, and you have to write
about 600-plus lines of code, it
is not like a nice app you do in
Android or iOS, where you 
declare the UI in the XML 
format, you have to manually 
paint everything, and then you 
have to include a bunch of code 
so when you are in ambient mode,
you are not hurting the battery 
life.  I was trying to think of 
how to improve this.  Last year,
we launched the support for 
Kotlin.  So I thought, how can I
use Kotlin to make it easier? I 
converted the whole thing to 
Kotlin, which reduced the code 
lines a lot.  But I found 
something else with Kotlin that 
made it more cool, and that was 
something called DSL.
So what is Kotlin DSL?
Well, the best way to understand
what domain-specific language is
to compare it to a regular 
programming language.
So, again, domain-specific lang
language, that did not really 
help me what it meant.  If you 
compare to a general programming
language, Kotlin or Java, you 
have a bunch of key words and 
classes and languages, and you 
make a big app, on the other 
side is a DSL that focuses on a 
task or domain.  It foregoes a 
lot of functionality and allows 
you to do that specific task.  
You probably used external DSL 
and have not realized it.  For 
example, SQL, for manipulating 
databases, that is a DSL.  Those
are expressions for manipulating
strings.  They have an 
independent syntax, reduce 
functionality, you don't have 
methods or a class to make them 
work.  You are not going to 
write a full-on application in 
them, either, at least I hope 
not.  And for SQL, the first key
word indicates as a verb what 
you are going to do.  You are 
going to select and update.  The
disadvantage is you have to put 
it as a string literal.  You 
have to pray and hope that you 
spelled everything right until 
run time, it fails and you have 
to see what is going on.  And 
Kotlin DSL, it extracts that out
the string and puts it in the 
code so you can have type 
checking, you can have code hint
and all of that good stuff that 
comes with your IDE.
So now that I have an idea of 
what it is, I like to show you 
two structures really quick.
And I used the latter, there is 
one called creating ones for 
chain method calls, and the 
other is nesting of Lambda.  So 
recognizing DSL is really 
subjective.  You know it when 
you see it.  So let's see some.
So this is Kotlin DSL for SQL.  
You can probably look at it, and
if you know SQL, you can 
understand it right away.
And it is all words, like Slice,
then I select all, group by,ords
order by, and limit.  This is 
all type-checked, you get code 
hinting so you don't spell 
anything wrong.  This is great 
and understandable, this is DSL.
The DSL for the watch face that 
I liked is the nested Lambda 
structure.  So this is to create
a watch face, you can see it 
right there, create a watch face
is a verb.  If you look at the 
structure, you may not know 
anything about making a watch 
face, but you probably 
understand what is going on 
right away.  Analog watch face, 
it is not digital, it has arms. 
Colors, the dimensions for the 
hand, the hour, the minute, the 
second, I understand that.  And 
watch face background image, 
that is all -- it is very 
declarative, you can understand 
it right away.  This is type 
checked, and I get code hints 
and all of that good stuff.  In 
the end, I get this nice little 
watch face with no work. And the
600 lines, they did not go away.
I put them in a helper class and
combined them with the more 
important class that interprets 
the DSL.  If you are making a 
watch face, you only need to 
know about the DSL.
So what is next?
I -- this is kind of an 
experiment that I did as a code 
a lab, check it out, it will 
take eye  you 15 minutes to make
a new watch face, search for 
Google codelab, it is under the 
Wear OS, search under Kotlin.  
You can see how to make a watch 
face, you can see the source 
code for how I made the 
transition between DSL and 
interpreted it into a nice 
little watch face.  And more 
importantly, hopefully now you 
are a
little bit interested in DSL, 
you can use it in a project to 
make it better, or you can go 
and use something like the DSL 
for SQL, for testing, or in html
in the Kotlin coding type 
checked.  So thank you for the 
talk, for letting me talk, and 
hopefully I have gotten you a 
little bit interested in DSL.  
Thank you.
[ Applause ] 
Coming up next: Push 
Notifications: An India 
Perspective by Amrit Sanjeev 
SPEAKER: Well down, no gong.  
Next up, push notifications. 
SPEAKER: I'm Amrit, a developer 
advocate at Google.  I want to 
talk about push notifications 
and how interapp developers are 
getting the delivery higher in 
the region.  I want to talk 
about a stat in India, we have 
314 million-plus smartphone 
users, growing on a 16 percent 
year on year growth rate, your 
apps have a chance for users and
great business in the region, 
and it comes with a lot of 
competition.  There's a need to 
have a better engagement as well
as lighting your users.  And 
push noteification is one of the
mechanisms a lot of developers 
use to engage your users.  And 
when it comes to push delivery 
ratess some in India affect the 
delivery.  Until recently, data 
plans were expensive and people 
have the habit of turning off 
mobile data when they are not 
using the internet.  So when 
they go out the WiFi zone, they 
will turn the mobile data off, 
and that changes the behavior of
time to lift parameter for the 
push notification, if you have a
short one and they turn the data
off during that period, the 
notification is never delivered.
 Recently, new carriers have 
come in and reduced the data 
costs drastly, and now those 
behaviors are changing, but not 
enough for you to ignore that 
change.
Secondly, a lot of devices with 
custom behavior, or Android 
builds, where your notification 
and, because of battery 
optimization, they kill off the 
notification services.  They 
sometimes force close the apps, 
and avoid the app for never 
getting the notification 
delivered to it.  That is 
another problem.
  And then your time to 
parameter, if you actually 
increase it by, say, from four 
to-eight eight hours, you see a 
drastic improvement, or a large 
improvement, in notification 
flivverdelivery.  You might go 
over the WiFi user.  So that 
makes it interesting.  And in 
any other region, notification 
frequency also, the number of 
notifications that are sent to 
the users is affected and people
get irritated and turn off 
notifications if you do it too 
many times.  So collapsible keys
and things like that are very 
important here.
And I want to talk about three 
design patterns that developers 
are actually trying out for 
querying use cases to improve 
the notification delivery.  One 
is buffering the notifications, 
to give you understanding of 
your use case and how the 
buckets work better for you.  
The neckinism developed here, 
there's a period job that runs, 
refreshes the npm token, sends 
it to the server, when the 
server gets the data, it maps 
the fcm and the update.  When it
you send out oo cap  -- a 
campaign, they split it
based on recently.  And you put 
in the first batch based on what
is in the last 7 days, the next 
batch is to update it from 7 to 
31 day said, all the tokens from
the 7 to the 31 days, and the 
third is 31 is above. And what 
this allows us, in each of these
buckets, the parameters for each
of these buckets will improve 
notfication delivery, you have a
better understanding of which 
users get the notification and 
might react to your notification
better.  And because you have a 
lot of tokens, which are not 
updated frequently, or the users
never send that update cycle, 
you don't end up calculating 
that in peer notification 
delivery and you miscalculate 
the effectiveness of push 
notifications.
  The second one is basically 
where you have, again, a job 
that comes in.  It checks 
whether a notification was 
delivered to the device from the
last one or not.  If not, it 
will go to a service, an RS 
service in the notification to 
the user.
And you have to ensure that this
does not wake up the device.  
This is not the most efficient 
way of doing things, but it 
still works.  And the last I 
will call out is data messages 
in the scenario.  In the case 
you want to send a message, 
there's a sale that is going to 
start at 12:00 you want to show 
the message at 12:00, and your 
users are 20 percent more on 
this service.  Instead of 
senting at -- sending at 12:00 
and hoping that the users 
receive it, they will send it 
two days before, which as a data
message.  So the app will 
receive it, and then based on 
the schedule that is within the 
data message, they will schedule
a job to come up at exactly the 
right time to display the 
message to the user. 
SPEAKER: That is time's up.  
SPEAKER: 
[Gong]. 
SPEAKER:Coming up next: 
Understand the impact of Generic
System Images (GSI) by Hung-ying
Tyan.
SPEAKER: Hello, I'm Hung-ying, 
I'm talking about GSI, the 
generic STIMP  system images.  
It is the purest form of Android
framework that we can build from
AOSP.  By purest, it does not 
have a device maker or carrier 
customization.
You will
want to ask -- AOSP has been 
there for 10 years.  Why does it
become a thing right now?
It is because GSI is the central
piece in travel compliance.  So 
remember the project travel, we 
took out
the Android 
updateability prom in trial.  We
architected between the flame 
work and the hardware
original vendor implementation. 
With this, we no longer need to 
update the vendor
implementation with the Android 
framework.  
This reduces the time for the 
device maker to update their 
Android framework.
Now, to make sure that the 
boundary is not altered by 
device makers at will, we 
require every treble-certified 
device to pass a series of CTS 
and VTS tests with the Android 
framework replaced by GSI.  So 
with all of these powerful tools
in place, about a year ago, we 
started to see the momentum of 
faster Android adoption this 
year.
So let's take a look
at the Android developer program
at Google iOS last year.  Only 
Nexus and Pixel forms were able 
available for you to try out 
Android Oreo.  And fast forward 
one year later at Google I/O 
this year, it was the first time
we had non-Google phones join 
the beta line up.  It is not 
one, it is not two, it is seven 
device makers joining us.
So the progress was phenomenal.
We know we are on the right 
track, and we will continue to 
push for
it.
Now, despite that GSI is the 
central piece in Treble 
compliance, we feel like it has 
a lot more potential than that.
So we set up a goal to make GSI 
be more accessible and useful, 
not just for device makers, but 
also for the general public.
Including app developers like 
you, and even consumers.
An important first step toward 
that goal is to make GSI 
available in AOSP.
So, for this, we have
published pie GSI in AOSP, so 
you can download and build the 
pie GSI today.  We are exploring
ways to make future GSI 
available earlier than the 
release of the next Android 
version.
So you will be able to try out 
next century version earlier 
than GSI.
And, at the same time, we can 
get early feedback from you.  So
the benefit is mutual.
So please stay tuned for our 
further announcement on this.
Finally, we understand that 
trying out GSI can be cumbersome
sometimes.
We are looking into different 
ways to improve the process.
Right now, we have an idea of 
trying out GSI without actually 
flashing GSI out to the device.
But I'm not going into detail 
here.  We do have a working 
early prototype at our demo 
booth.
In addition to that, we also 
prepare a set of devices.  They 
are all running on the same pie 
GSI i.  So please do check out 
our demoes at the Android 
platform 
sandbox. 
SPEAKER: Thank you. 
SPEAKER: Coming up next: Buiding
Reliable Apps When Connectivity 
Is Unreliable by Naheed Vora, 
Ryan Hamilton.
 SPEAKER: Hi, I'm a product 
manager at Android checkativity.
SPEAKER: I'm Ryan Hamilton, a 
manager on the Chrome networking
team. 
SPEAKER: So we will
start with the -- how many of 
you have noticed this in the 
last few years? The signal is 
flaky and you turn off the WiFi.
Connectivity keeps changing 
underneath you, and the 
application is trying to 
understand what is going on 
underneath, is really 
complicated.
And, for creating a good 
experience, we need to hide this
complexity from the user.  We 
don't want users to go and turn 
off their WiFi in order to use 
the app.  It could be any app 
that you can think of that the 
user is using at a given point 
in time.
So who should care?
If you are building a 
user-facing application, maybe 
your app depends on mobility.  
Maybe it is Google maps, or 
something like that.  Or you are
building a gaming application or
communicating app.  Anytime you 
know you need to react really, 
really fast, irrespective of 
what is happening in the 
activity is critical.  This is 
important to make, that you 
address this point in your 
application.
And one thing I would add here 
is that the user dropoff rate 
with had when the delayss are 
1-3 seconds, or 10 seconds, it 
may vary in markets.  If you are
in a market that is emerging 
like India, it may be resilient 
to delays.  If you are in the 
United States, if you go beyond 
three seconds, you will lose the
user.
  So what I have here is a demo.
And it is a Google assistant 
demo, what we are doing is we 
are using the technology and we 
will talk about that a little 
bit later.  On the left-hand 
side, there is no optimization 
on the phone.  On the right hand
side, there's an optimization, 
and we are doing a Google 
assistant query and the user is 
walking out of the building.  
The phone is connected to WiFi, 
as the user walks out, the user 
loses connectivity.  Let's
see 
what happens.
Trying to click.  This is 
interesting.
Okay, in the interest of time, 
this is what happens.
The one on the left-hand side, 
it takes -- is it possible?
Awesome.  Okay.
So the -- we will make a query, 
and what it is doing -- it is in
direction to Oxford service in 
London.  And you can see on the 
right, the response came back 
already.  The left one, it is 
still waiting and waiting.  The 
right one is like, eh, I don't 
have anything to do.  I'm out.  
The left one is like, ah, 
finally.  This is a lot from 
reaction time perspective.  You 
want to have this resilience in 
your app, it can reconnect fast 
irrespective of what is 
happening in the connectivity
stack.
So let's get out of here.  It is
a separate problem.
Okay, so what it is using, what 
you need is you need a protocol 
that can handle this different 
mobility where the device is 
moving between the networks.
And so that's where the Cronet 
and QUIC comes into play, the 
application layer and the 
protocols that you are using can
handle this mobility.  So I will
hand it over to Ryan to talk 
about Cronet and QUIC.  
SPEAKER: I will do this quickly.
We have taken the network stack 
that is inside of the Chrome web
browser and made it available as
a library for apps to use, we 
call it Cronet, Chrome, network,
Cronet.  Since it comes from 
Chrome, it has the reliability 
and interoperability that you 
would expect.  It is used by a 
variety of Google apps, search, 
YouTube, maps, and available on 
iOS as well.  Coming soon, it is
an option to pull from the Play 
Store, you will not need to 
bundle it in the app.  You can 
get the automatic opdates and 
security and performance 
improvementes.
Part of the coolness of Cronet 
is that it comes with support 
for QUIC, a new transport we 
have implemented in the user 
space on top of GDP. And it does
some things that TCP can.  It is
a modern transporter, provides 
new teachers and better 
performance.  And with QUIC 
connection migration, you can 
switch back and forth between 
WiFi and Sailor.  It has a bunch
of awesome performance 
highlights, we release every six
weeks, it comes with state of 
the art security. 
SPEAKER: And the best part is 
that the platform optimizations 
are built in, you do not have to
do everything time there are 
Android releases, it will take 
advantage of the latest and 
greatest that Android has to 
offer.
For any more questions, we have 
a booth outside.  You can talk 
to us, we are happy to help. 
Thank you. 
SPEAKER: Thanks.
Coming up next: Quick Ways to 
Ensure App Compatibility with 
Android Autofill by Felipe Leme.
 SPEAKER: Next talk, as a bonus,
this person lets 10 minutes on 
Autofill. 
SPEAKER: Hello, everybody.
So my name
is Felipe Leme, and I'm talking 
about what Android Auto fill is 
and why it is important for you 
to optimize your apps for it.  
It is a new feature that we 
introduced last year on the 
Android Auto release, and the 
goal is to provide a safe and 
fast way for platform managers 
to do their job.  To use 
Autofill, you need to select an
Autofill service which could be 
provided by Google, or 
third-party apps, like One 
password, and many others.  Last
time I checked, there are about 
30 different apps on the Play 
Store that provides an Autofill 
service.
And one key decision we made 
when we designed the API is that
it should work out of the box 
with existing apps.  In order 
words, as an app developer, you 
do not have to support the app 
-- if you don't do anything, the
password manager will figure out
what to do for you.
But just because you don't need 
to change it, it doesn't mean 
you shouldn't.  After all, it is
your app that is on the line, 
and you don't want to depend on 
the third party app it has no 
control over.  And the tip I 
want to give you, you should not
rely on Autofill service 
heuristics, you want to make 
sure that the password manager 
does their job properly, you can
do it quickly and easily by 
annotating your XML views.
  The first thing you should do 
is make sure that you annotate 
what should be Autofilled, and 
you do that using the Android 
Auto fills filling stack.  So 
let's say you have the user name
and password loading screen.  If
you don't annotate it for 
Autofill, and the layout XML is 
using user name fields and 
password fields, it is going to 
work fine.  When they use the 
instructor, they will say, 
there's a user name string here 
and password here, and that is 
the user name and passwords.  
And say you are in Brazil, your 
name is in Portuguese, and I 
grew up there and it is common.
So my fields are [speaking in 
language other than language].
So the password manager you get 
is this instructor, and it knows
what it is.  So it is providing 
the Autofill data, and the user,
you have to manually type in the
user name
and password, which is the 
problem to avoid with password 
managers.  So you will annotate 
the fields, the user name, and 
the password, you use password.
And on
Java, we provide fields for the 
user name and passwords, like 
telephone number, etc.
  And there is another issue, 
you should annotate what should 
not be Autofills, using the 
Android import texts.
  So to see another example, in 
developing the API last year, I 
wanted to send an SMS to my 
friend, and then I got a pop-up 
with my own telephone number as 
a recipient of the SMS.  Am  
when you are composing an SMS, 
or a spreadsheet, you want to 
type something dynamically, you 
do not want to use a pre-define 
said ed variance.  This was 
annoying for me, I knew the API,
it is confusing for ra a user 
that doesn't.  So you can 
disable the field for the 
activity, you can do it on the 
whole activity layer by
annotating the root view, it is 
important for Autofill, no 
exclude descendantdescendants.  
It will not use password manager
when it doesn't make sense and 
it will improve the experience.
  And say you are using the tags
and you are so excited to 
change, that you skip the 
presentation, the summit, and go
back to your laptop and change 
your apps.  Don't do that.  If 
you are going to change, how are
you going to test the changes? 
You need to select a service to 
see what is the data provided by
the service, and you will say, 
we will choose whatever comes 
with my device, Google Autofill,
or install a third-party 
password manager.  You can do 
that, but you can go back to 
that problem of relying on the 
password manager heuristic, and 
we can provide it without a lot 
of changes, and you cannot see 
what is going on with making 
these changes.  You can use the 
standard Autofill service, and 
we can provide some samples on 
GitHub.  We provide a couple 
Autofill samples permutations 
project, one is the 
basicservice, which is a service
that only understand Autofill 
hints.  If you are using auto 
fithints on your app, you will 
see something like this that 
will pop up, because in this 
field, the user name, you will 
see is the the options. And on 
the other screens, the base fill
will not show anything.
  And it has a pretty simple 
service, less than 200 lines of 
code.  If you are interested in 
seeing how it works, you can do 
this a well.  And debugservice, 
it will try to fill anything 
based on your search IDs.  This 
is useful to test what would 
happen if you are not tagging 
your views.
So back to my SMS app, you would
see something like this, and you
will click on two.  And I'm just
making sure that -- okay.
So we will make sure that I'm 
seeing, I'm looking on the 
screen.
So when you click on two, you 
are going to see this pop-up 
with some user data, which is 
two, which is just the name of 
the resource ID.  You can see 
how confused it is for the user 
if they see something like that 
on the app.
And then, the final tip that I 
would like to give is you should
also make sure that the app 
works when the Autofill service 
requires authentication.  So 
what does that mean? So most 
password managers, they don't 
retain the data right away, it 
is encrypted somehow.  So they 
will ask the user to 
authenticate, a master password 
or a finger print to unlock the 
data.  The way the framework 
works, we are launching this, it
belongs to the service on top of
the app stack.  The user does 
the authentication, and when you
are finished, the activity 
resums.
  So for most apps, it is not a 
problem.  If your app is doing 
something where it is resumed, 
you are revalidating, or 
rebuilding this new hierarchy  
hierarchy, you might break it 
out of view.
So to test this, to make sure 
that the app works in this 
scenario, you need an Autofill 
service that requires a 
permpermutation, the 
debugservice.  
And when you launch the 
debugservice, you need to launch
the Autofill and click and 
select the debugservice, you 
will see the debugservice is one
of the options.  We provided 
that example.  And then you can 
select this authenticate 
response option on the top.  
When you go back to the app, 
when you trigger out of view, 
instead of getting the data, and
unlock it, you will have this 
response toa thent caught.  So 
icate.  So when the user 
attempts to -- it will ask for 
yes or no.  So when you click on
yes, this screenshot, the sample
app where I have re-created a 
new hierarchy.  When you click 
yes, you will return to your 
activity, and now from the 
framework point of view, all of 
the data is different.  So they 
don't know that they have the 
locked data for your activity, 
for your user name field, it is 
a different ID.  They are going 
to show the response again.  
When there's a click there, it 
will launch the authenticate, 
and then it go backs and asks it
again.  So the user is now on 
this groundhog loop where it 
never goes forward, it is 
frustrating.  And the solution 
for this case is to make sure 
that you don't -- you create a 
view hierarchy.  So the user 
attempts to authenticate, we 
will launch the authenticated 
activity, and now when we go 
back, we will see the unlocked 
data and when the user selects 
the data, and we are Autofill 
and everybody is going to be 
happy.  These are the main tips 
I would like to give from this 
session.  Make sure that you 
have your notater views, the 
tess in the standard service, 
and the work with usera thent
authentication.  I have links 
for all of these talks for 
Autofill service as well that 
explains all of the apps that 
I'm using.  That's it.  Thank 
you for coming, and I'm supposed
to say that the QA will be out. 
I have 25 seconds before I'm 
going to be gonged  out it. 
SPEAKER: SPEAKER: Do you want to
say anything else, or are you 
done? 
SPEAKER: We are done, I guess. 
SPEAKER: Thank you. 
SPEAKER: Thank you to everybody 
that played the game
today.  $1
did.
And we will assume that the 
users are able to touch the 
device, or they can see what is 
on the screen.  That turns out 
not to be true.
So these assumption
s assumptions helps people with 
disabilities to overcome
obstacles.
Not what they are interested in 
or what they are actually able 
to do.
And so the way Android does 
accessibility is it depends on 
the entire ecosystem.  So we 
were directly on Android, and we
-- we build a lot of the core of
it.  But we really depend on the
entire ecosystem to make this 
work.
So the accessibility 
framework is able to handle a 
lot of low-level features, like 
magnification, you are not able 
to build it into every app, if 
you do, we will have a nightmare
experience.  And that is handled
down close to the graphics 
level, you want to magnify 
things.  Accessibility 
developers who work on services,
TalkBack, select to speak, they 
build these plugin services and 
the idea is that they generally 
have a particular type of user 
in mind, they will go through 
and really understand what those
users need, what their 
challenges are, and
try to build something that 
works for them.  
But they cannot do everything on
their own, they need to get 
information from, somebody who 
is blind, you need to be able to
speak what is on the screen.  We
need to find that out from the 
apps what is going on in the UI,
and that is where we really need
the help of the ecosystem and 
all developers.
So as a general model, we have 
these plugin services, like 
switch access, voice access, and
Braille, they are able to query 
an API for the accessibility 
service to tell me what Windows 
are on the screen and the views 
inside the windows, what text is
where, what actions are 
available on each view.
And then they can present this 
to the user in whatever way they
need to.  The user does not 
touch the screen, it do do you 
want to perform this control, 
that control, that gesture.  And
we need the apps to provide the 
right semantics to do that.
And the -- I want to try to make
this as simple as possible.  So 
a few things to keep in mind.
One is just to make sure that 
the information that you are 
presenting to all your users is 
as visible as possible.
So color -- luminous contrast is
the most importantsingle thing 
you can do in that regard to 
make sure you are not using gray
on slightly lighter gray to 
convey information. Thatt  can 
look cool, but it is difficult 
for people to use.  And the 
second is to prefer controls to 
be big and 
simple.
It is sometimes -- you want to 
cram a bunch of stuff to get as 
much options as possible for 
users, and you want to figure 
out what is important for your 
users can simplify things for 
everybody, and for somebody who 
does not have perfect dexterity 
to use it.  We can have a 48 by 
48 density independent pixel, 
that's a guideline we can throw 
out there, to have a uniform 
standard throughout the 
ecosystem.  That is set as a min
height and width thing.
Next is to label toff  stuff. 
If you are conveying information
visually, if you have a button, 
just have a label and the labels
should be precise and concise.  
So the users who cannot see the 
screen can find out what they 
do.
And so people ask when in the 
project should I consider 
accessibility? Early and often 
is the short answer.  The longer
answer is, in each of these 
different phases of your 
project, there is something that
you can do.
So in design, ZERL generally the
things that I mentioned, keeping
the contrast, control size, 
labels, that will get you a long
way.  If you are using standard 
UI patterns.
But the more innovation you are 
doing on your user interface, 
the further away you get from 
the stuff we built into the 
Android framework.  At that 
point, it is really helpful if 
you can think
broadly about the full range of 
users who use your project.  So 
the more gesture type things 
that you are doing, you want to 
make sure that you are taking 
into account how people are 
going to interact with that that
cannot perhaps perform that 
gesture.
  During development, we have a 
series of APIs, Qasid is going 
to show those, and we have 
testing tools that show, as much
as possible, we can automate the
processes to make sure you are 
handling these things correctly.
So just to explain a bunch of 
stuff about the complexities of 
users and things you can do, you
can forgive me for saying making
accessibility easy, and I said 
making, and not that we already 
made.  In general, we want this 
to be easy.  We want to make 
sure that we are doing that, if 
we are not, please let us know. 
If you are using standard
com POFE components and ways, it
should not be complicated, like 
how a particular user is going 
to use your project.  It is 
something like, what does this 
control do?
And as you evolve, it gets less 
easy.  But we want to make sure 
that the incremental work that 
you do for accessibility grows 
more slowly than the work you 
need to do to serve whatever 
users you have in mind to begin 
with.
And now, Qasid is going to talk 
about a way to think about the 
development process for folks 
with disabilities. 
SPEAKER: Hey, my name is Qasid, 
I work for Phil on the Android 
accessibility team.
Let's get into it.  If you 
really think about it, there are
two ways that your user can 
interact with your application.
Right?
The first is consuming 
information.  This can be 
content that the user wants, or 
indications how to use the UI.  
And the way we use information 
varies from user to user.  I 
look at it on a screen and I 
process it that way.  A TalkBack
user will hear a description of 
what is on the screen from a 
device speech synthesizer.  Once
a user understood and consumed 
that information and combined it
for the real world, they can act
on your application to make it 
do things.  And just like 
consuming information, this 
varies dramatically from user to
user and from circumstance to 
circumstance.
And now these actions can be 
something like a tap, a swipe, a
scroll.
It can be speaking into a 
device, some users drive the 
whole device with a single 
button.
And now once you combine these 
two modes of interaction, we see
this cycle of action and 
information, right?
And we can see if any part of 
this is su subtly broken for a 
user, the application is 
useless.  How to make it 
complete for every single user? 
That seems like a daunting task,
like we mentioned before, the 
way the users interact with our 
devices varies a lot and trying 
to understand those users is a 
complicated task.  If you don't 
try to do anything through 
non-standard, you trust us, and 
you use APIs, it is a pretty 
easy task.  I will show you some
trivial things that you can do, 
that are fundamental, and push 
your accessibility to be much 
better than it otherwise would
have been.
Let's start with the way that we
consume information.  And I 
started this search application,
a novel idea.  And the way that 
I'm indicating this is a search 
UI is that, by that magnifying 
glass on the little line.  You 
will notice something, that's a 
very light shade of gray on a 
slightly lighter shade of gray. 
That is going to be difficult 
for people who -- for a lot of 
people to see, because that is a
very low contrast ratio.  It is 
frustrating and downright 
impossible for others.  So we 
should darken it up.
And just like that, many other 
people are able to use our 
application.  The goal is to 
make sure that all of the 
information is visible in terms 
of size and contrast.  If you 
want concrete guidelines on 
this, you can go to the material
website and we can give you 
contrast ratios under 
circumstances and the hard 
numbers in terms of size, like 
the 48 by 48.
  That was straightforward, but 
there are dramatic variations in
how we consume information. For 
those situations, what we want 
you to do is to fill in the 
blanks for our frameworks.  What
we mean is that your framework 
can infer a lot from the view 
hierarchy from the code we have 
written, but there are some 
situations where we need your 
help.  I will show you.  Let's 
assume that I'm a TalkBack user 
and I'm putting a finger on a 
screen to hear the description 
of what is under my finger.  I 
put my finger on the mag finying
icon, but there is no way for 
our frameworks to figure out 
what that means, because there 
is no text associated with that.
It is essentially an arrangement
of pixels on a screen.  You have
to fill in the
blanks as app developers, you do
it by a label, search.  Make 
your labels concise and to the 
point.  If it indicates an 
action, make it a simple action 
word.  And this is easy, in this
situation, set the content 
description, make sure it is 
localized, because accessibility
users do not only exist in 
America.  And now that the user 
can understand what is generally
happening on screen, let's make 
sure we allow them to act on our
device or application 
appropriately.
So I decided to add a new 
feature, clear text button, 
instead of having a back space, 
you can reset and start stip 
typing again.  That's a tiny 
butpen, buttonbutton, if you 
have a fine motor disability, 
that is going to be impossible 
for them to tap.  We will make 
it bigger.  This works for many 
more users.  And make sure that 
your controls are simple and
large.
I added another feature, a 
history feature.  You can type 
in the queries and see the 
results of the query again.
And now if you end up swiping on
that, any of these items, you 
will see a trash icon indicating
this can be removed from 
history.  If you continue 
swiping, that item will be 
removed from history.  This is 
great and all, but it is a 
pretty custom-built gesture 
overloaded on to an item.  It is
going to be hard for our 
frameworks to detect.  So you 
guys are going to have to fill 
in the blanks for our frameworks
and for accessibility users.  
You can do that by adding an 
action, or an accessibility 
action.  And all you have to do 
here is specify a user-facing 
label or description of that 
action, a simple verb, and the 
code that should be executed 
when the action is performed.  
You can do that with the 
accessibility action API, but we
are adding something to the 
Android X library which allows 
you to do it in a single line 
call, with a Lambda and a string
passed in.
So I have shown you a way of 
thinking about accessibility 
issues and how to address them, 
but you still need to know how 
to find these issues and how to 
verify that you have actually 
fixed them.  This is where Casey
comes in.  Casey? 
CASEY BURKHARDT: Thanks, Qasid. 
I'm Casey Burkhardt, I'm a 
software engineer on Google's 
accessibility engineering team, 
and I lead the development on 
accessibility testing tools for 
Android.  So as Qasid pointed 
out, there are many 
accessibility issues that 
actually fixes our fairly 
straightforward from the 
development side and can improve
the accessibility of your app.  
The big question, though, is how
it  do we go about finding those
issues? There are three types of
accessibility testing today I 
would like to cover.  Automated 
testing, use of automated tools,
manual testing, where you use 
Android's accessibility services
itself to understand the user's 
experience, and and  and user 
testing, where you bring in 
users with disabilities and get 
their feedback using your 
application.
My section of the talk focuses 
on automation because, right 
now, we see that as one big area
where we can make vast 
improvements across the 
ecosystem if we have some 
developers' cooperation using 
these tools.  So automated 
accessibility testing, until 
several years ago, on Android 
didn't really exist.  Around 
2015, we launched a project 
known as Android's accessibility
test framework, which is an 
Android and Java library that 
houses a lot of detection logic 
for the more common 
accessibility issues that we can
identify within an app's UI, a 
rule-based fashion.  This is an 
open source project, so you can 
find it on GitHub.  And 
essentially what it is aiming to
do is to look at the app's UI at
runtime and find the 
automateable, mechanical aspects
of accessibility that we see 
day-to-day that affect users the
most.  We can find the common 
issues that Qasid discuss, so we
can find an UI that is missing a
label for a screen reader, we 
can identify low-contrast text 
and images.  We can tell you if 
you have lickable clickable 
items within an UI that meet the
minimums for touch target 
guidelines, and we identify a 
number of various other 
implementation-specific issues, 
and the core testing library ATF
is growing constantly to find 
new and more interesting issues.
  We have taken this library, 
integrated it with common 
developer end points, tools that
you will use commonly throughout
the development life cycle and 
you can leverage throughout your
project.
I want to talk through some of 
those integrations today and how
you can use them and get started
with them quickly.  So first and
foremost, we have integrations 
with two test frameworks that 
are commonly used throughout 
unit and UI tests on Android 
apps.  The first is Espresso, 
and Robolectric.  The idea with 
these integrations of ATF is 
they will piggy back on top of 
your existing tests.  You have a
test that loads, runs UI with 
the application, and inserts a 
state.  During the phase of your
tests, if you used Espresso or 
Robolectric, if we identify an 
accessibility issue that we 
believe will affect a user with 
a disability's ability to 
interact with your application, 
we will failure the existing 
test.  If you have strong 
testing coverage with 
Robolectric or Espressoo, 
enabling this will allow you to 
get a decent amount of coverage 
in accessibility testing for 
your app.
  And each framework offers what
is known as an accessibility 
validator.  And this is an 
object that you can use to 
essentially configure ATF's 
behavior inside of your tests.
  So you can configure ATF, for 
example, to not fail your test 
and to log something, and we've 
run into an issue.
And you can set up ATF to crawl 
from the root of your view 
hierarchy when you perform a 
view action, rather than 
validating the item that was 
interacted with for additional 
coverage.  You can use 
accessibility validator to set a
white list.  If you want to turn
on accessibility tests within 
your Robolectric or Espresso 
tests, you can do so.  You can 
maintain a green presubmit and 
burn through issues you know 
about by creating and 
registering these white lists 
for issues.
And how you leverage these in 
Espresso and Robolectric, you 
will call accessibility 
checks.enable within your test 
set-up.  This is going to 
trigger our global assertion to 
run whenever you use a view 
action within an Espresso test. 
So in this case, the view action
is run, runs a performant 
evaluation on the view 
interacted with and its sub 
tree.  Accessibility val  
validator is returned through 
enable.  So if you need to 
customize the enable, you can do
so through the object return in 
that call.
Within Robolectric, it is 
slightly different.  Instead of 
calling accessibility 
checks.enable, it is annotation,
and it will annotate the test 
method or class that you would 
like to enable accessibility 
testing within.  It allows you 
using shadow view.clickon for 
you to interact with your 
elements.  Avoid the temptation 
to use view perform click, do it
correctly.
  It is available in a different
class that mirrors the same API,
you can use for accessibility 
Util and make data calls there 
to configure your behavior.
So, in addition to integrations 
with these automated test 
frameworks, we built a separate 
stand-alone tool, known as 
accessibility scanner.
And this is a direct integration
of ATF, and accessibility 
scanner acts s scanner as as a 
front-end for the library.  It 
will evaluate
the fore ground for the device. 
It installs the accessibility 
scanner, you turn it on, it will
add a floating button to the 
device's screen, you will open 
up the application, navigate to 
the UI you would like to 
evaluate, and just tap the 
button.  What you see is 
essentially a report that 
describes the ways in which you 
can improve that UI for 
accessibility.
And again, these will mirror the
same types of issues that ATF 
and Espresso and Robolectric can
identify as well.  It is easy to
take these reports from 
accessibility scanner and share 
them with your team, you can 
export to email or drive.  It 
does not require technical 
skills, you don't need a debug 
version of your application, or 
a user debug device, you don't 
need to -- you don't need to do 
anything special to set it up.  
It works on any app or any 
Android device running 
Marshmallow or later.  And you 
don't have to have a lot of 
experience related to 
accessibility.  Each issue that 
accessibility scanner can 
identify comes with extensive
documentation that gives you 
background, and how to think 
about the issue during the 
design, development, and test 
phases of your project.
And so please do download 
accessibility scanner, it is 
something that I highly 
recommend we use when we build 
UI.  G.co/accessibilityscanner 
will take you to the Play Store 
page.
And one last integration I would
like to cover today, this is a 
new one.  We launched it a few 
months ago.  An integration of 
the accessibility testing 
framework and the Play Store 
developer console's pre-launch 
report.  For those of you who 
have not used pre-launch report 
yet, it is a great tool to get a
sanity check during the launch 
or release process for an APK to
the Play Store on an open or 
closed channel.  The way it 
works, you upload an APK, 
pre-launch report takes that 
APK, instrument it, and push it 
to a number of different 
physical devices in a lab.
And it will crawl your app, 
essentially on these different 
devices and generate reports 
that include findings about 
performance, security, and now 
accessibility as well.
And so ATF is running along side
pre-launch report as it is 
crawling your application and 
generating reports at each stage
in the crawl, and it is taking 
all of the results and 
deduplicating them.  So if you 
would like to check this out, it
is available now in Play Store 
developer console under release 
management, you should be able 
to see accessibility results for
any APK that you have uploaded 
to the store since fairly early 
in July.  So please do cleck 
that out.  Here is what it looks
like, to give you an idea.  The 
main entry point for an APK, it 
will show you the categories of 
issues we have identified.  It 
will show you the clusters of 
issues, you can click on any one
of those and it will show you 
detame  tails about the problem.
So in this case, we are pointing
out an issue related to touch 
target size.  In the column on 
the left, you see many examples 
of the same de-duplicated issue 
across different crawls of the 
application that prelaunch 
report has performed.  You have 
access to the same additional 
documentation here as well, too.
So if you are not familiar with 
a particular issue, the learn 
more link will give you the 
details you need to resolve it, 
regardless of the stage your 
project is currently
in.
I want to wrap up by talking 
about an accessibility testing 
strategy.  We talked about 
automation, but we did not go 
into manual testing and user 
testing.  And these are equally 
important.
Automation is great because it 
helps you find issues very 
quickly, early in the 
development cycle, especially if
you have good test coverage with
our automated tools.  So think 
about automation as a way to 
catch very common issues 
quickly, but not as a way to 
guarantee that your app or your 
UI is fully accessible.
To really understand your user's
experience, to get that -- to 
get that awaterness of how your 
UI is performing within an 
accessibility service, we really
highly recommend you go and 
actually turn on TalkBack, turn 
on switch Access, try them out, 
learn how to use them, and gain 
a true understanding of your 
user's experience.  The way I 
like to describe it is 
automation is capable of finding
a missing label for a screen 
reader.  But we can't really 
tell you if your labels make 
sense.
So really only by understanding 
and putting yourself in the 
user's shoes and understanding 
their experience, we are asking 
users directly about their 
experience.  Only then can you 
truly understand how accessible 
your UI is for users with 
various different disabilities.
  And we have found at Google, 
both looking at our first party 
and third-party applications, 
the most successful, the most 
highly accessible apps we see 
day-to-day are the apps that 
combine multiple accessibility 
testing strategies, like 
presubmit, continuous 
integration, and a process for 
manual accessibility testing and
bringing users in and 
understanding the perspective of
an accessibility application.  
These are things to consider.  
With that, I will hand it back 
to Qasid that will talk about 
the newer APIs for expressing
app semantics.
SPEAKER: I'm back, say you have 
adopted APIs that I talked about
earlier and the testing that 
Qasid suggested.  And you have a
pretty good foundation of 
accessibility in your 
application, and the more you 
are testing, the more you 
realize that there are holes in 
your experience, that is 
breaking the cycle of 
interaction that I mentioned 
earlier.  We are adding API and 
new things to make it so those 
holes get plugged.
So the first thing is clickable 
spans, the clickable bits of 
text.  Before API 26, non-url 
spans were fundamentally 
inaccessible.  And developers 
had to write a bunch of hackie 
work arounds to make these 
things work.  In the latest 
alpha of the AndroidX library, 
we made it all the way back to 
API 19.  You can look out for 
that, too.
And there are users that make 
apps behave like -- they have 
their own life cycle and so 
forth.  They are accessibility 
panes.  You need to develop your
frameworks that present these 
differently to the user. And the
way you do that is by
passing a string to the 
accessibility pane title on the 
view.  Make sure it is concise 
and localized.  This is 
available on API 28, and we 
added this API in the latest 
AndroidX alpha that will work 
all the way back to API 19.
And finally, there are headings.
These are used by TalkBack users
to navigate through sections 
quickly and easily.
And the way you specify that is 
exactly what you probably 
expect, pass in a boolean to set
an accessibility heading on a 
view.  This is also available in
28, and we added this to the 
latest alpha of the AndroidX 
library that will work all the 
way back to '19.
Now it is back to
Phil.  
SPEAKER: Circlng back to the 
title of this, making 
accessibility easy.  We really 
want this to be easy.  The only 
way that this -- that 
user's didn't disabilities are 
going to be able to access the 
ecosystem.  
Sometimes you see somebody that 
wants to go the extra mile and 
dig into the app and all the 
issues around accessibility, 
that is great but not every 
developer can do that.  We need 
to make it as easy as possible 
so everyone can build it into 
their workflow in a natural way.
So certainly, to get the basics,
we want it to be 
straightforward.
So if you have reasonable 
contrast, to make the 
information visible to everyone,
you have simple controls, you 
are using labels.  We want that 
to kind of get you almost all 
the way, and some of the other 
APIs, like Qasid was describing,
should be able to handle the 
specialized situations.  If you 
have a full screen fragment 
transition, you can use the pane
title to make sure that that 
gets handled the same way a 
window transition would.
So we want it to be easy.  That 
means, if it is hard, we have 
messed up and we should fix 
that.
And we found some developers 
that really, well, the framework
seems to be coming up short, I 
will engineer a workaround to 
get around the problems with the
framework.  Honestly, please 
don't do that.
Let us fix it, because we can 
fix it at scale, and it needs to
be inside Android.  You find it 
in AndroidX, you know, if you 
wanted to fix it, by all means, 
upload it to AndroidX and we 
would be happy to accept fixes. 
We want it fixed centrally so we
can get a consistent experience 
throughout the
whole COREMS.  ecosystem. 
If you are engineering a custom 
solution, if an engineer at a 
different company is going to do
this work, and the answer is no,
then there is probably something
wrong.
So please reach out if we are 
messing this up.  You can file 
bugs on AOSP, you can ask 
questions on Stack Overflow.  
But we much prefer to, we 
probably prefer to get the 
feedback that something is 
difficult so we can get an 
elegant solution that everyone 
can use.  Some of the things 
that Qasid just presented before
he did that, the effort required
to do some of these things 
really require, like, learning a
new API surface.  We wanted to 
condense everything we could to 
one line.  So we are trying to 
present solutions that really 
are -- if you have this thing, 
here is one line of code, all 
you need.  If you get to 
something that seems like it 
should be one line of code and 
it is not, let us know.
And another place you can go for
other resources is to 
G.co/Androidaccessibility, 
there's a link for accessibility
testing, how to get to 
accessibility scanner, and the 
test framework project that 
Qasid described.  That is 
available on open source on 
GitHub if you are interested in 
that.
  So, I really appreciate your 
time, I would be very happy -- 
if you have feedback for us, and
you think things should be 
easier than they are, we will be
having office hours for a while 
this afternoon.  We would love 
to talk to you.  Thanks a lot 
for coming and for your efforts 
and hopefully making a 
consistent ecosystem of 
accessible apps.  
Thanks.
[ Applause ]. 
SPEAKER: Everyone, the next 
session will begin in 10 
minutes.
Coming up next: Bundling an App 
in an Instant by Wojtek 
Kalicinski, Ben Weiss.
12C3W4R50I6R7B8G9SDS
Wojtek
SPEAKER: Hello, everyone.
So today at the keynote, you 
heard that we are unifying the 
developerdeveloper experience 
for instant apps and Android app
bundles.  
My colleague, Ben, and I are 
going to talk about how that 
works for the developer 
perspective.
So instant apps, it is a 
technology that we introduced a 
little over two years ago at 
Google I/O.  It lets you give 
your users a native Android app 
experience without them having 
to explicitly go and install the
app.
And now how that works is you 
split up your app, into smaller
pieces, and the user can reach 
that via a link.  And now to 
QUET your apps small enough and 
to launch it instantly, instant 
apps for the first time used a 
technology in Lollipop, called
split APKs.  And they are able 
to use the smallest and most 
optimized version of the app to 
the user's device.  In order to 
do that, the build system, the 
Gradle Android build system that
you have that you build your 
apps with, built all of those 
split APKs locally, bundled them
in a zip file, and you upload 
that to the Play Store they can 
choose from the set of APKs and 
deliver them to the devices.
  To do that, the developer had 
to do some significant 
refactoring in your African  
apps.  The project structure of 
the instant app, as we looked to
one year ago, probably looked 
something like this.  First of 
all, you have to take all of 
your base application code and 
move it from the application 
module into a base feature.
And then you had to split your 
app into feature month featuree
modules containing the feature 
of your activities.  And then 
you have almost two dummy 
modules, application and instant
ach  app.  So the APK, and the 
instant app zip bundle that you 
upload to a separate track in 
the Play Store.  This was not 
ideal.  As I said, it required 
significant work.  Your project 
no longer looked simple.
And at the same time this year, 
we introduced a new
publishing format called the 
Android App Bundle.  This 
contains metadata about the 
targeting of the resource 
resources, native libraries, and
so on, and it contains some of 
the module information for a 
module called dynamic feature.  
I called it a publishing 
feature, we use it to upload a 
bundle, telling the Play Store 
everything about your app to 
play.  And together with Google 
play dynamic delivery, we are 
able to serve an optimized set 
of APKs to user devices.  In 
order to do that, there's a big 
difference.  Because it happens 
on the server side on the Play 
Store, we need to be able to 
sign the app.  If you use an 
Android App Bundle, you
need to enable sending by Google
play.  
If you allow us to store the key
for you, it is more secure.  
There is another benefit, 
because we can transform the 
APKs and make them more 
optimized, as we develop more 
apt miizations on the Play Store
side, what we bring to the users
is more optimized from 
compressed libraries.  It makes 
sense to move instant apps to 
this new model.  Why build all 
the APKs locally and not be able
to optimize them further on the 
Play Store? What if we could use
the new App Bundle for the  mat 
to deliver instant app 
experience to our users?
So let me tell you how to go 
back to that simpler, better 
project structure for your
instant apps.
If you have -- we need to go 
back to the simple project you 
use as your build your APK.
So we no longer need the instant
app plugin, everything we need 
is baked into the application 
plugin that can now build 
bundles.  We don't need the base
feature anymore.
Instead, we can move our code 
back to the application, where 
we will be using that to build 
our unified bundle artifact.
And so, again, with feature 
modules, we replace them with 
dynamic features that work in 
the App Bundle world.
So ultimately, we want something
simple.  You can have your 
application module with any 
additional library modules that 
you need.  Optionally, we have 
dynamic features that can be 
downloaded on demand, adding 
functionality alt run time.  We 
have single piece of metadata 
that tells the Play Store that 
this is instant bundle app 
enables.  That would be great if
you can upload it to the Play 
Store and all have it downloaded
on the install and instant track
which, what we  is what we are 
aiming for.
We are testing it, but you are 
not able to upload it just yet. 
If you want to try it right now,
create two project variants, it 
is in the simple project still. 
In one of the variants, enable 
the metadata, enable the instant
app for your bundle and use the 
other variant to build it 
without that entry, or man  
manifest.  Still one app, one 
codebase, and you may have to do
that in order to upload to the 
Play Store.
  There is another white list n 
, if you want to use dynamic 
features in order to let your 
users download them on demand as
they run the app, this is on a 
white list for developers who 
want to test that.  You can 
apply online, however, you are 
currently not able to publish 
your app to the production track
if you have more than one 
module.
Okay, so how to try it yourself 
right now.  First thing you need
to do is use the new Android 
Studio 3.3 that is currently on 
the channel.  If you are 
creating a project from scratch,
you can select this check box.  
We will collect the modules with
the necessary metadata in there 
already.  That's what it looks 
like, thin  in the base 
manifest, you enable it, you are
good to go.  If your app is 
compliant with the other instant
app restrictions, such as file 
size, you will be able to 
publish an instantly enabled 
bundle.  If you have a project 
that you use to enable an App 
Bundle, you can use the wizard 
to allow a bundle.  This will 
instant enable your base module 
if you haven't done so.
  And next, we will build an App
Bundle through Gradle, through 
the bundle release command 
instead of assembly release, or 
through the wizard and the UI.
By the way if you are using our 
Android dev summit app at this 
conference, it has been built 
exactly as an instant enable App
Bundle.  We publish that to our 
channels on the Play Store, what
you are using right now is 
exactly what I'm talking about.
And it has been great.  It is 
has greatly simplified what we 
need to do in order to have that
working.
Let me invite on stage Ben who 
will tell you about best 
practices for modeler 
modularizing this copy of your 
app.  Round of applause, please.
SPEAKER: Thank you very much.  I
will talk a little bit about 
discoverability of your instant 
apps.  One of them you saw, the 
try now functionality.  I will 
talk about this.  Try now is 
basically a second bud  button 
on the Play Store that allows 
users to download and run the 
app without having to 
permanently install it on the 
device.  That's a great 
experience that we can easily 
see how it works, you can go to 
Play Store now and check it out 
through the Android dev summit 
app, and you can get to it 
directly from the dev landing 
page.  If you don't have an url 
associated with your app, that 
works as well.  You don't have 
the restrictions that you have 
to map to your urls anymore.
How do you do that? That is 
basically it.  I think that 
everybody has this somewhere in 
their application, the main 
launcher.  You don't have to do 
anything else other than add the
instant metadata that you were 
shown earlier, there is nothing 
else you have to do in order to 
be eligible for the try now 
experience.  What is your app, 
you want to have your app 
available with an url mapping?
That allows you basically to 
access your app through any url 
that you have associated with 
your app.  The first thing you 
have to do, you have to verify 
if you own the domain.  In order
to do that, you
upload an asset links JSON file.
There are resources where you 
can identify what you uploaded 
is correct and the mapping you 
have is correct as well, you can
share the link with anyone and 
they can open the app straight 
away.  I will show you how that 
works where we started with the 
main launcher. 
You have to set a second filter 
with it set to true.  That tells
the Play Store it should check 
for the asset links JSON file on
the url provided below.  Then 
you add the action view, the 
browseable default, this is what
I want to use as the default to 
view this url.  You have the 
scenes for https, or http, and 
multiple path prefixes for 
multiple activities.  If you 
want to use a default activity, 
whether it come from try now or 
the home screen, is the metadata
here, the tag for the filter.  
This is the default url, this is
where I want users to come in 
initially.  Like I said earlier,
if you don't have an url in the 
first place, you don't have to 
do this.  If you have to have an
url mapping, then this is the 
way to go.
And it is not a lot of work to 
do.  It gives you the -- it uses
the experience that you can 
share any link that leads to 
your app and any user can open 
the app straightaway without 
having to install it 
permanently.  And I think for a 
conference, it is a good use 
case to have an app that you use
once, you don't have to fully 
install install it.  You have a 
couple features, like 
notifications, you have to 
install the app.  But it is fair
enough to have the experience 
that you can have straight away 
without the first steps that are
necessary.
And also there is another thing 
that we use for app bundles in 
order to download code 
dynamically.
So going back to the previous 
section is basically, you can do
that with the zip and feature 
instant apps.  What we are going
on forward now, you have to 
ship, as the App Bundle, and you
have to use dynamic feature 
modules in order to use the play
core library.  This allows you 
to download features dynamically
on demand, not during the 
instillation,
andapp.  And it does all the 
heavy lifting.  If you say to 
the library, download this, it 
connects to the Play Store, 
downloads it, puts it in the 
right place.  For instant apps, 
it puts it into the shared catch
there, and then it installs it 
on the device permanently.  So 
how does it work?
So you add it as a dependency, 
available from Google.com, you 
can use it in your project.  You
create a 
splitinstallmanagerfactory, 
create a request where you can 
install one or multiple modules 
at the same time.  Those module 
names have to match the module 
name that you set for the 
dynamic feature module directly 
in the manifesto module.  You 
build it, and you tell the 
manager to start the 
instillation.
And that's all the code you need
to get started with it.  There's
a couple things to go around it,
I will go into it in a second.  
If you don't want to do 
instillation straight away, you 
can do deferred instillation.  
So you have the app, the user is
in a flow where they are buying 
something, so they have logged 
in and then they started the 
purchase process and you want to
download a payment module, for 
example.  You can do that 
deferred, or during the flow 
with had  the user is in that, 
you can do the instillations.
  And so the deferred -- it is 
not done straight away, the 
system says I'm cleaning up and 
this is where the module is 
removed.  And you can also 
cancel the instillation requests
where, for any reason, you want 
to cancel that, there's an 
option to do it.
And there's a listener that you 
can set on the manager that is 
my preferred way of listening 
for the updates, the split 
install state update listener.  
That's quite a word.  And 
usually what happens, the happy 
path is you trigger an install, 
the app starts with Pending and 
goes to downloading, downloaded,
installing, installed.  That's 
the happy path, for an app or 
module that is small enough -- 
that required user confirmation.
If you go into that state, you 
will get the -- you will get 
some information that you can 
start the intent with, to show a
dialogue whether they want to 
confirm or deny whether they 
want to install the module at 
this point.  If they con firm  
firm they want to install, you 
continue down the happy path.  
The instllation can be canceled 
in the cancelling state, and for
a couple reasons, it can fail.  
So that's the path where you 
have to handle all those states.
We do have a sample al -- 
available for that, I will share
the urls later in the session so
you can see how it works.
I talked about file size.
The limit that triggers, for 
example, the -- that requires 
user confirmation coincides with
a couple apps for instant apps 
in general and for dynamic 
delivery.
One of the things we do is we 
don't take the installed size, 
or the downloaded size, as the 
main factor anymore.
So if your app or device has a 
file size that is larger than 
the limit, that is all right.  
We can take the download size 
into account.  You get 
compression over the wire, 
that's what we take into account
for that.  And this we show in 
the play console as well.  You 
will see the download size is 
this, you are right above or 
below the threshold.  And 
dynamic feature modules do not 
fall into the initial download 
size as well.  What falls into 
it is basically your base module
and your instant entry module.  
If you have more than one, that 
you can have, it is the base 
module and the largest instant 
entry module.  And those, under 
a white list, have to be less 
than 10MB.
Dynamic feature modules that you
download later on or other 
instant entry modules do not 
fall into the 10 instant MB in 
the first place.  If your app is
larger than 10MB, you do not 
have the instant app benefits, 
you are not discoverable to 
users as an instant app.  If it 
is less than 10 or more than  
4MB, your app can be seen and 
used as an instant app.  So you 
can access it through try now, 
on the Play Store, and also you 
can is show it in the web 
banners and share it viaU urls. 
If your base module and your 
instant entry model is less than
4MB, your app can be discovered 
from anywhere, like ads and 
search results.  If you research
for Android dev summit app, you 
will see that and go directly to
the instant app because it is 
under the 4MB threshold.  While 
you are at it, you can continue 
to modularize.  It is a tricky 
topic because it entails some 
work that some people are not 
100 percent certain how that is 
done best. 
We recently went through the 
whole process with a project, it
is called Platt, it is on GitHub
and I will talk about how we 
managed to move away from a 
monolithic app to an app that 
uses bundles.  It uses the same 
technology underneath for 
modularizing an instant app as 
well.  So what we did is we, and
most apps will do it, we created
a base module which hosts the 
domain code and data so it 
shares the shared preferences, 
log in, for example, some of the
repository, some persistent API 
calls, and things like that.  On
top of that are different 
feature modules.  Those have 
their own domain code, logic, 
and the UI which is only 
displayed in this module.  And 
that set up can be used for many
apps, if you have a base module 
that shares information and the 
different feature modules.  I 
will share a little bit more in 
depth what we did.
So we initially had an app, a 
module that most people had, a 
monolith.  If we shift to an 
APK, that is fine.  There is no 
way to go into the whole 
modularization part if we have, 
in the end, one single 
monolithic app, APK, that we 
ship to our users.  And since we
were considering going towards 
dynamic features and app 
bundles, we move everything into
a base, with the shared 
dependencies, that means 
everything that we include, 
coming from whatever 
repositories externally as well 
as a couple of local, 
third-party APIs and libraries 
that we have forked and worked 
with.
After we tested this, we worked 
on features that we extracted 
from the app itself.  The first 
thing we started with is an 
about screen.  Well, an about 
screen is kind of untangled from
most of the stuff within the 
app.  It is a good starting 
point for getting our hands wet 
with -- what does it actually 
entail to make the dynamic 
feature module, how we make it 
do all the things.  Then we did 
a search screen, and then we 
have two new sources that we 
extracted into the dynamic 
feature modules as well.  They 
all depend on the app, and 
everything shares the 
information through that as 
well.  You can read up more 
in-depth on a blog post on the 
Android developer's medium 
publication, and we also have 
information available on instant
apps on general, and on App 
Bundle on dynamic features, and 
instant apps without urls.  The 
instant app scene is outside 
during office hours to share 
information and knowledge.  If 
you have questions, please come 
there.  And with that, thank you
very much.
[ Applause ] 
Coming up next: Android Slices 
Best Practices by Arun 
Venkatesan, Artur Tsurkan.
SPEAKER: Hi, everyone.  My name 
is Artur, I'm a product manager 
on Android. 
SPEAKER: My name is Arun. 
SPEAKER: We're going to tell you
about Android Slices, part of 
Android pie.
So we're going to start an 
introduction to Android Slices, 
user experiences, best practices
in constructing your Slices, and
more details on search indexing 
best practices, and finally 
through the -- what we have been
running over the course of the 
summer, the important developer 
gotchas for when you start 
building slices whether  when 
they are available.
We will start by reintroducing 
get SNOOT  slices.
Before we into it, I wanted to 
remind you of some resources.  
We introduced slices at Google 
I/O, you can find documentation 
at g.co/slices.  
And you can look at our I/O 
session video, Android slices, 
building interactive results for
Google search and that will give
you information on building 
slices, building a custom 
template and an UI for your 
slice .  Slices are a new way 
for you to present remote app 
content in Android.
Think of Slices as embeddable 
app snippits that can be 
inserted inside another app or 
part of a system.  Slices are 
meant to work across multiple 
possible surfaces, the whole 
idea of a slice is the ability 
to constrain and provide you 
with a lot of power in how you 
express your app.  Slices 
contain a variety of components,
including text and images, and 
they are powerful.  They are not
just static pieces of content, 
they can house realtime data and
interactive controls, and 
TalkBack to your app in the 
interaction.  So all of these 
examples you see here are 
examples of slices that you can 
build.
I mention that slices can be 
embedded across a number of 
contexts, and we are excited to 
be bringing them to search as 
the first surface to insert 
slices.  So as they are typing 
in a search on Android, you can 
enhance the predictions offered 
by Google with lives, personal 
content from your apps, 
constructed through the slices 
you create.  There are two types
of slices you can provide
in search predictions.  The 
first are app name queries, if 
somebody can search is searching
for your app, you can direct 
them to a canonical part of your
app.  So maybe in YouTube, you 
want to get the visitors to the 
video they were watching.
Or maybe you want to get beyond 
the name, and the settings app 
will give them toggles for data 
usage.
How do we build these slices? 
We will start with default or 
app name slices.  It is easy to 
build an app name slice.  
Construct it as you would.  
Android's recent updates include
tooling.  When you create a 
slice, you will create a 
definition in the manifest, like
this one, and the exception to 
the slice provider class.  That 
provides details of how the 
system can find your slice and 
what templates it offers to the 
system when it does.  To make 
this slice map to your activity,
or to make it map to the app 
name, you need to add the slice 
content URI to the content 
activity and specify it in the 
metadata field.  So this tells 
the system, this is a slice that
I want to point to the main 
activity, this is the content 
from which you can reference 
that slice.
The implementation details for 
general terms are a little bit 
different.
  So first, you actually don't 
provide the same linking in an 
activity as you would have an 
app name slice.
You are going to use updates to 
the Firebase app indexing APIs 
to expose the content URI for a 
specific set of key words.  
Using the Firebase APIs, you 
would construct an indexable.  
That would have a public url, it
will also have the name for that
indexable content, some very 
targeted key words for users to 
find that content, a 
description, and then finally 
with the update to the Firebase 
app indexing APIs, you can add 
the content URI or slice URI for
this indexable.  When the user 
is searching for the key words 
you specified, search on Android
will replace the prediction with
the slice predicted by the 
content URI in this indexable.
  Finally, in order for 
indexables to be transilated 
transilatedilated from the 
public urls to the content, you 
need to over write the URI 
method into the slice provider. 
This allows you to talk between 
the two formats in order for the
URIs to be visible as a search 
prediction.  Overriing this 
method allows us to provide you 
as a developer with flexibility 
over what schemas are important 
to you, without attaching them 
one to one.
  Now we know how to build a 
slice and also how to expose it 
to search on Android.  Let's go 
into a little bit more detail on
what makes a good slice, and the
user experience best practices 
to keep in mind.
So the first is try to focus 
your slice on one task or a set 
of sight  tightly related tasks.
Avoid making your slice do too 
many things, and that the 
content in the slice is coherent
coherent.  So those slices are 
embeddable as a search 
prediction for now, we would 
like to think that the slices 
that you build should be 
reusable in many other parts of 
the operating system and you 
should consider when building 
your slice for those 
possibilities.  So it focuses on
consuming and previewing one 
video, the slice on the right 
goes to other functionality, 
like channel selection, and 
stresses the definition and use 
of a slice, which we discourage 
you doing. 
Try to keep interaction in the 
slice lightweight, highlight the
most important features, excuse 
me, and make sure that the 
actions you are presenting to 
the user are using clear and 
familiar iconography so they 
make sure that they understand 
what they represent.  Avoid 
making it do too many things.  
So the slice on the left focuses
on users listening to the 
content and play list, and 
offers them the ability to like 
certain content.  The slice on 
the right leans into things you 
can do with the play list items,
functionality that is probably 
best within your app.
When sharing your slices to 
different platforms, make sure 
you are surfacing recognizable 
features and content so that 
users know that they can query 
for them.  When they query for 
them and see it as a prediction,
they can understand how it might
have appeared for that 
particular query.  And 
Arun will go into those 
principles, but try to keep that
the case to avoid promotional or
advertiing content in the slice.
  And finally, have the slice 
accelerate critical user 
journeys and make sure that the 
slice accomplishes as much of 
the user journey in line as 
possible without going into the 
app.  By providing the 
information and actions that the
user would need to accomplish 
that action.  So on the left, 
the user has all of the stuff 
that they need to know to 
understand what the slice is 
for, what kind of information it
is representing, what kind of 
actions I can do with that 
information, changing the song, 
adjusting the transfer or 
brightness, and they don't need 
to click into the app to do 
that.  The slices on the right 
are missing some critical pieces
of information or critical 
component as part of
that user journey and adds an 
additional layer of friction for
users in completing the task 
they want to complete.
There is more that Arun will go 
into. 
SPEAKER: Thanks.  So here are 
the main pockets when you think 
about use cases for building 
slices.  They should be 
familiar, such as requesting a 
ride on a ridesharing app.  They
should be 
targeted.  And finally, recall 
and reengage with content that 
the user has seen, rather than 
discovery use cases.
Next we will see guidelines 
around the content you can 
surface in slices and the data 
that needs to be
indexed.
Slices should be targeted and 
personalized.
  Like the video streaming app 
that shows slices, when a movie 
a user is searching for is 
available in the app.  The app 
can index movies, like the users
previously, or it is curated 
based on the in-app behavior. 
Do not create one for all the 
users in the database.
  Slices should give 
information.
A ridesharing app gives users 
the ability to search for rides.
Create index cases based on past
history, or popular places near 
the user, such as concert
venues.
.
Slices should give users timely 
information.
Let's say that
a
smartphone -- 
they are likely in their home.
It is not recommended to create 
an indexed slices for 
controlling lights at any time 
or in any place.
Here are some more examples of 
the type of app content to index
and display in the slice.
A food ordering app can search 
relevant realtime data, such as 
food price, when the user 
fetches the slice.
Similarly, a news app slice can 
index and display information to
the readers.
Travel and local can allow users
to look at their content, such 
as bookings and upcoming flight 
reservations.
Let's now see how to keep your 
slices fresh.
When a slice is indexed, the 
indexable object includes the 
matadata and the cached slice.  
And in order to make sure it is 
accurate and not still, you 
should set a time to live.  This
is used by the Google search app
to make sure the cache is not 
displayed.  In this example 
here, the time is set to 1R 
expiration.  And if the slice 
has content that is not time 
sensitive, use it to time to 
infinity.  In order to present 
to the user, reindex the slices 
in the background when content 
changes, and only when content
changes.
Earlier, arrtur showed us how to
index.  What are the best 
practices for key words?
Limit key words to 10 per slice.
Account for likely
permutations.
This is probably the most 
important, avoid key word 
stuffing, use the minimum number
needed per slice.  So the app 
slice will be in the search 
rankings, if you use too many 
key words that the users are 
tapping on.
  How do you
determine if the --
the most important signal is the
of times that the user uses the 
app on the device.
It is also important that the 
user has viewed the content 
previously and interacted with 
it.
And finally, in order for us to 
know that the user interacted 
with the content, log it to the 
indexing API.
We conclude the session today by
sharing the lessons learned from
the early access
program.
  Content URI can be confusing. 
Content URIs are mentioned with 
a slice and start with 
content://, and deep uris start 
with http or https.
Content URIs use the package 
name, or the reverse of the 
package name shown in the 
example.  When you see a content
URI, think of a slice.  Key 
words must be included in the 
slice title or subtitle.
This is done to ensure that the 
relevant content is displayed to
the user when they search for 
something.
How do you handle location?
So slices do not provide 
location to apps, and therefore 
use the last cached location 
when displaying the slice.  
Reindex the key words based on 
the last location.  And note 
that indexables are not
instantly updated and it leads 
to bad ratios.  So keep in place
when handling location changes 
for the app, and apply those to 
slices as well.
It is recommended to keep the 
slice initialization short.  The
on create slice provider method 
is called for all registered 
slice providers on the 
application's main thread at 
lunchtime, and therefore do not 
perform lengthy operations for 
the application start up will be
delayed.
And if you have multiple 
indexables, in the case of apps 
with multiple slices, put in all
-- pass in all the indexables 
together as we showed in the 
example.  And make sure to test 
the indexable.  If one is 
invalid, that update called it 
failed.  So here is a small code
snippet that shows it, how to 
pass multiple indexables to the 
Firebase app indexing API.
So now that you have
built your slice, you are 
viewing the API.
That's all we have for the talk 
for today.  We want to give a 
shout out for the developer, to 
the early access program.  Thank
you and have a fun rest of
the session.
SPEAKER: We are now on break, 
this is the worst fab I have 
ever seen, I recommend people 
put it on lower right and not 
have it be that color, there is 
something disturbing about that 
one.  It is snack time t , it 
says on the agenda.  There is 
coffee as well as tea, and there
is also coffee, there is snacks,
and there is coffee, and I have 
it on good authority that there 
is also coffee downstairs.
There is coffee, excellent 
question.
Thank you for asking.  Let's 
see, we are back here at 4:00.
Building a great TV app.  
There's a talk on building 
Gradle best practices.
See you back here at 4:00,
thanks.
#
#. 
SPEAKER: Hey, everyone.  Today, 
we're going to talk about 
building a great Android TV app.
So before we dig into the 
details, we will talk about the 
ecosystem.  We continue to see 
two times year over year growth.
We are investing more in smart 
TVs, desktop boxes, the 
ecosystem has been pretty 
strong.  But you are all 
developers, let's get into how 
you can make a good TV app.
So before we talk about what the
innards of the app are, we will 
talk about what the TV is and 
why it is so important.  We have
living rooms and other rooms 
dedicated and focused around a 
TV.  It is a pretty key point 
for users, it is the focus of an
entire room.  Just think about 
that for a second.  So your app 
matters.  The content matters.  
So if we try to think about what
is the foundation for an app, 
the biggest piece is your
content.
That is your value prop to your 
users.  If you add on to that, 
usant, how do you make it easier
for users to add.  You have 
great content, how can they 
discover more of your content 
inside of your app.  If you want
the cherry on top, think about 
the experience.  How can you 
layer in all of these extra 
things to build a sweet 
experience for users, no matter 
where they are in your app?
We will dive into each of these 
concepts and I will call 'em out
later on.  But the key take away
is they come from your content, 
but they stay for your app.
So what I would like to talk 
about is how to build a great TV
app.  There are three things to
think 
they can see other content in 
your app easily, and 
distributing, making your 
content easy to find should be 
as easy as making your app easy 
to
find.
Let's talk about the player.  
Player can make or break an app.
We will talk about this review, 
feel free to 
read.
So the key take away for me is 
that it constantly stalls, this 
app is completely frustrating.
They ended up with just a 
one-star review, just because 
they are a player.  And reviews 
matter, there are whole other 
talks about Google play and how 
to improve your reviews.  But 
the player was the key point 
here, it is the why their app 
was not as good astle could it 
could be.  So it is very clear 
to users that the play back is 
important.  They don't want to 
have stutters and stales, they 
want to be able to watch the 
content.  And even in that 
review, they talked about XHURNS
commercials and ads. They are 
okay with it, they don't it  
like that they stalled.  
Soshowing things that may be 
annoying, such as commercials, 
as long as they play fine, users
are okay with it.  So the 
player, we have many options to 
build a good player.  Media 
Player is a good tool, comes out
of the box in the framework, you
give it a data source, it chugs 
long in play, you can build a 
great experience with media 
player.  If you have more 
advanced things you want to do, 
Exoplayer is a great tool.  We 
work hard to make it customable,
there's a lot of extensions.  If
you are using lean back, there's
an extension that hooks into the
lean back controller.  If you 
are doing ads, there's a bunch 
of ad stitching support.  Lets 
talk about ads, ads are 
important.  You are going to 
make money from showing ads.  
And ads are just as important as
the content shown and displayed 
to the user.  Focus on ads and 
make sure that adstitching 
works, whether you do it server 
or client-side, these are 
considerations you should make 
for your app.
So there's many options for 
players, media player, 
ExoPlayer, custom player.  And 
having a player is a good start.
But there are things that you 
can layer in, that top part of 
the pyramid, the experience, 
there are things that you can to
to build an experience around 
the player to make it even 
better.  So we talked about this
at I/O, play back controls.  
Everyone's phone should be 
ready, here we go.  Okay, 
Google, skip 5 minutes.  Okay, 
Google, pause.
Okay, Google, play.  Thesep  
types of transport controls can 
be commands through the 
assistant.
Adding this extra little 
feature, this little nice nugget
of delight, helps build that 
experience  experience for your 
app.
  This works with Media Session,
if you have Media Session call 
back, you get all of these 
features for free.  Since I 
talked about this at I/O and 
there are other talks, I will 
jam through this fast.  Pay 
attention, here we go.
Boom, beautiful.
Six wonderful methods: Pause, 
play, stop, seek, next, and 
previous.  But in reality, 
that's a lot.  That's a lot to 
think about, all of these 
different casesism if.  If you 
use ExoPlayer, it can be done 
for you.  There's an extension, 
all you do is connect the player
to the media session and it 
works out of the box.  Making a 
media session is pretty simple, 
there is documentation and talks
ants  about Media Session.  I 
will not go into it, set it to 
be active, set the controller, 
set anything else you need to 
set.  Set the current state, are
you currently playing, what 
position are you in, set up the 
media session to be what you 
need it to be, and once you have
a media session and you have an 
ExoPlayer instance, connect 
them.  There's the extension 
library for ExoPlayer, add in 
the media session as the
parameter, and set the ExoPlayer
instance.  The media session 
connecter helps understand how 
to set up the call back, the 
edge cases around playing and 
seeking, you don't want to go 
back past the end of the video, 
rewind before the video starts. 
It handles the edge cases for 
you.  In this sample, we are 
saying set player, player, and 
then null.
And you can set a custom play 
back refair.  There are other 
customizations you can do as 
well, so if you are music app 
and you have a custom playlist, 
and you want to set a different 
order for how the songs go 
through the queue, you can set 
up a custom queuing mechanism on
the extension.  That's it.
Three wonderful lines
of code, and the instance is 
taken care of for you.  All the 
default behavior you would 
expect, done.
So having a great player is 
great, that is one example of 
how to layer thin experience to 
make the player even better.
We're going to skip ahead to 
discovering content.
So the whole point of 
discovering is you want users to
stayp in your app, and you want 
them to discover and watch 
content faster.
So let's look at this review, I 
love the first sentence, they 
love love love -- so many loves 
in this ach.
So the key takeaway here is that
it was a five-star review, they 
loved that all of the content 
was there.  It is easy to find, 
they can do whatever they need 
to do inside of that app and 
watch what they want to watch.  
Funny story, it is the same app 
that got the one-star review.  
So even though they had a bad 
player, they worked on 
discoverability and they are 
able to have good review in the 
flay  Play Store.
So how can we make content 
discoverable? Everything happens
in threes, that's a rule of 
comedy, and a really good rule 
in life.  And discoverability 
also happened  happens in 
threes.  We can work in in-app 
browsing, search with the 
assistant, and the home screen. 
We can start with in-app 
browsing, there's a beautiful 
library, Lean Back.  If you have
done TV development, you are 
very familiar with it.  It is a 
templated UI system where you 
can plugin data and it works on 
building the UI for you, so you 
don't have to worry about the 
focus handling and the user 
input.  You can just, this is 
the content we have, and it will
show it for you.  It is not just
how to browse content, though, 
leanback also works and showss 
details.  So there's a bunch of 
information about content, you 
have duration, content rating, 
the rotten tomatoes score, the 
album, artist, I can keep going 
on and on for the rest of the 30
minutes of this talk.  But I 
think you get the point, there's
tons
of information.
You can show it using lean back 
and in multiple places on the 
home screen, in search.  And by 
showing this information sooner,
it lets users make these 
microdecisions faster and they 
don't have to go in and out, in 
and out, to figure out hot  what
they want to watch.  Make your 
user's lives easier by showing 
this sooner.  Let's look at 
another example, search.  We 
talked about this at I/O, 
there's tons of documentation on
this.  I want to breeze through 
some of these things quickly.
Search is all supplied with the 
content.  Content providers are 
simple, they return a cursor, 
you can do whatever you want in 
the background with the content
provider.
If this did a network call, 
maybe you have a bunch of pojos.
This is a database call, you 
have a cursor.  That's fine, the
trick for the search provides
provider is it needs to return 
results that match the search 
manager's criteria.  A search 
manager is way of saying this 
cursor has a bunch of columns 
with these names, and then the 
assistant is able to pull in 
from that cursor and say, here's
the title, here's the duration, 
and is able to figure out what 
content is where.
Super simple to do to do with 
the matrix cursor, we will dive 
into it closer.  You need to 
take each of the results, add 
them into a row as a matrix 
cursor, and then return the 
matrix cursor.  The matrix 
cursor is really just like a 
mock cursor, it is a 2D array, 
essentially, under the covers.
  So if you don't have to go 
about, how it  do I store all of
these in a database, you can 
mock it out at the very end of 
your search.
So mapping, this is where the 
hard work happens.
You have a matrix cursor, it 
takes in a query projection.  
This query projection is going 
to have all of the columns 
defined that match the search 
manager.
So here we have suggest column, 
text one, is the title of the 
content.
An action, the data ID, the ID 
is what is unique to your 
content inside the
app.
And if you take the context into
this row, you
supply an array, the name, and 
it corresponds to the order in 
which the query projection was. 
So the ID, the title, the 
action, etc. All of the fields 
you have, you can return it 
back.
So search manager, and with the 
search and assistant, you can 
make the return result mump  
much faster.
And so we will cover the new 
stuff that
is happening.
The app will have the channel, 
the play next row, and for the 
video apps, you will have 
previews.  We have seen up to 2X
increase in engagement
, you can see a trailer for a 
movie, or a recap, but they take
a little bit more work because 
it requires the content team to 
make the content for you.  We 
will not talk about play next, 
or video.
So inserting it into the content
provider.  So set the preview, 
the link, when they open the 
channel, they put it into the 
app and set the internal 
provider ID.  This is what the 
app keeps track of and knows 
about.  And then you just get 
the content resolver, you call 
the insert, give the content 
values, and you are good to
go.
So you do stuff, the channel ID,
you keep track of it for sink  
synchronization.
So it has the deep link, the 
internal provider ID, and the 
logo, those are the key pieces 
of the channel.
So what just happened? We 
created a channel, we inserted 
it, and then we stored the logo.
So we did two things with the 
home screen, insert the channel,
store the logo.
So, as of AndroidX 1.00, we have
a new API.  This API looks very 
similar, small differences.  We 
have a preview channel helper 
class.  It takes in a context, 
and then it does a bunch of 
look-ups to get the content 
resolver for you, so you don't 
have to do context, content 
resolver.insert, it does the 
work for you.  It makes a 
channel, you have the builder, 
you set the name, the 
description, the app link, the 
intent provider url, you think 
you should set the type.  But 
this class knows it is a preview
channel.  It knows the preview, 
and you don't have to set the 
type.  Instead, you can set the
logo.
Now, all of this is contained in
one united, you can call the 
helper.publish channel, and it 
gives you all of the work for 
you.
And you can get the channel ID 
back.  So what it does under the
covers, it inserts the channel 
into the provider, and it goes 
to add a logo.
And if the channel isn't able to
be inserted, maybe you have bad 
data and you are hitting an 
error or something, it will 
return an error back to you.
If it is able to insert the 
channel, it tries to store the 
logo on that channel.  If the 
loFWO it go cannot be persisted,
it wraps it all up, unwinds the 
channel, so you have half one on
the home screen.  It treats 
everything as an atomic unit.  
Pretty convenient.  It does 
everything CRUD does.  So we 
talked about publishing the 
channel, you can read all of the
chanles les and get individual 
channel, you can update a 
channel and you can delete them.
And all of this also happens for
preview programs, and there is 
support for
the play next proin this class. 
There are two options to do it, 
which one is better? You could 
say I want to use content 
providers, I want to fine tune 
the performance, I can do batch 
inserts, bulk operations, I can 
go lower-level control.  I don't
need an entire program with all 
of that metadata, maybe I want 
the title and the duration, and 
now I can slow down that query 
projection, and have faster 
results.
And it is based out of the 
framework, you don't have to do 
all of this extra work to access
it.  You get it out of the box 
from the framework.  If you want
to use Android X, you get more 
convenience, you don't have to 
worry about all of the nuances 
of a content provider, it is 
a-liner for all intents and 
purposes, and you get the 
benefits of having the AndroidX 
in your app.
Discovering content is great, 
three ways to go about it.  In 
the app, searching the 
assistant, and on the home 
screen with chan LSS. Les.  How 
do you make your app 
discoverable? The third thing.  
The app store on TV is a
bit different, it makes sure 
that only apps designed for TV 
are shown.  When the user opens 
up the app store, they are 
looking at apps that can be 
played on or installed on TV.  
Try to make your app stick out 
can be hard, but there are 
simple things that you can do to
have your app appear on the Play
Store.  The first is to declare 
features, and even if you don't 
use it, there's a giant 
asterisk.  Don't start declarng 
bluetooth or location just for 
fun.  There are two features 
that
really matter.
The first is touch screen, you 
want to declare it as false.  It
is not a touch screen, this is 
not a phone, this isn't a TV 
from way back in the day, these 
are smart TVs.
You don't need touch screen 
support.  The second thing is to
declare lean back as true.  This
tells the Play Store is ready to
be deployed on
a TV.
If you have all of the code in a
single code base and making a 
single APK that deploys on 
mobile and TV, set the lean back
to false.  This tells the Play 
Store that the APK is compatible
on mobile and TV.  The second 
thing you should do is try to be
visible in the launcher.  If you
are a headless app, a screen 
saver or a keyboard, go away for
two minutes.  And I will see it 
then.
So you need to supply a banner 
to see it in the application or 
activity.  The launcher will go 
in through the manifest, find 
the resource, this is what it 
uses to show the icon on the 
launcher.
And once the user selects the 
icon, it needs to launch 
something.  So the launcher 
fires an intent, and you need to
have an activity that accepts 
this intent.
It is called the lean back 
launcher intent, cleverly named.
And from that it will trigger 
the lean back experience.  There
are three things you need to 
have.  One, declare the two 
features so the app is found on 
the Play Store.  Two, how the 
have the banner, and three, have
the lean back intent so the app 
launches when the user wants to 
enter your app.
And that's it.  You are ready to
go in the Play Store.  This talk
is done.
All right.
But, in a sense, that is kind of
the minimum viable product.  You
are able to have a strong 
player, you are able to have 
easy-to-find content, and you 
are able to distribute on the 
Play Store.  That is just a good
app.
How do you make it great?
To start with this, you should 
look at your
users.
Imagine the spectrum, they start
from one side of the spectrum.  
I bought a TV, I want to be 
cool, everybody is doing it, sit
s in my closet but I have one.  
The next part of the spectrum, I
have one, I watch a show every 
week.  You go further down, I 
love how to get away with 
murder, field of disease  
daisies is awesome, I should 
watch suicide squad, theis in 
that.  Or for sports, here is a 
fantasy team, the jersey and the
player I like and I keep going 
into it.  That side, the left, 
is called the lean back user.  
They are sitting back, watching 
TV.  That's all they want
to do.
This is the lean-in user, they 
are sitting on the edge of the 
seat, this is awesome, who are 
the people in the show and going
deeper into the content.  
Everything I talked about until 
now, having a good player, 
making the app usable, this is 
for the lean-back, the right 
side of the screen.  If you 
think about it, how can you tap 
into that lean-back user? Here 
you have a beautiful living 
room, a very beautiful living 
room.  I wish it was mine, but 
it is not.  If you look closer, 
you see a camera, a microphone, 
a tablet phone, and then, as you
start to think about it, the TV 
is the center piece of the 
living room.  There are so many
devices around.  You don't have 
to just do stuff in TV, you can 
tap everything around the liveic
ing room.  I love the Android 
TV, it is the focal point around
everything that is happening.  
Again, we do it in threes, 
threes are great.  In concept, 
if you want to tap into the 
other surfaces, what should we 
do? The first is controlling 
media, the play back controls we
talked about earlier is a great 
step.  It is going to go a 
little bit farther advanced, you
are building an experience 
around your app.  And another 
option is to have notefication
notifications, the big gomis 
game is going to start.  Do you 
want to watch in your TV? And 
the next is going deeper into 
the content, the cast and crew, 
behind the scenes of this 
production, are there extra 
sponsored content I want to know
more about.  And the third 
pillar is about reducing 
friction, how do Iinstall your 
app, it is installed, how do I 
sign into
your ach.
I want to make a payment, how do
I authenticate it in a secure 
way 
inside the TV.  Everybody who 
has the TV has done the third 
step, the frictionless 
interaction.  The Android TV set
up does it for you, during the 
flow, it says, do you want to 
set up on the phone? They give 
the UX indicator, you get the 
notification, you say, this is 
me, this is my account, and the 
TV takes over from that 
information and it was
really frictionless.
And how do they do that? It is 
something that you can do today,
it is 
nearby.  We try to use that on 
TV, what you can
do.
You do the work on the phone, 
and not on the TV.  We will set 
up a peer-to-peer wireless 
connection that is encrypted, 
you don't have to worry about a 
lot of things, you have an 
intimate connection between the 
phone and the TV.  We will dive 
in a little bit deeper, let's 
get started with this, we will 
start on the TV side.  So I'm on
the Android TV, so I'm biassed. 
So TV will start advertising, 
you set up the 
nearby.get-connectionsclient.  
That is a helper class from the 
nearby API that has all of these
things to get you started.  You 
call the start advertising, you 
give it a name, a service ID, a 
package name is perfectly fine. 
You are going to give it a 
connection, a life cycle call   
callback, and set a strategy.  A
cluster is a really good 
strategy.  If you notice, 
there's a P to P, point to 
point, strategy.  And it might 
be one TV, one point, point to 
point, that is great.  If you 
try to do multi-device set-up, I
have a TV in my living room, in 
my bedroom, bathroom, all of a 
sudden that point to point 
breaks down.  So to make it a 
more robust app, think about 
using cluster.  You also set a 
success and failure listeners.  
These listeners are not saying, 
oh, I have been found, I have 
been advertising on non found, 
the users will say you can start
sad  advertising, great for 
debugging and adding extra 
information inside the app.  The
big elephant in the room is the 
connection Lifecycle Callback, 
this talks about how the devices
talk to each other.  What is 
going to be said is later, how 
they are saying it now is 
handled in the connection life 
cycle callback.
And a simple three methods: On 
initialized, results, and 
disconnected.  And they are 
pretty straightforward, but 
let's dive in a little bit more.
So when the connection is 
initialized, that means the 
phone requested a connection, 
you are going to prompt for 
security and do a couple things.
Eventually, you will see 
nearby.get connections client, 
and you will accept it.  Based 
on that, there's a result, was 
it okay, continue on, was it 
rejected, maybe it will ask for 
another re-try.  And based on 
that result, you should handle 
appropriately.
And the last one on disconnected
is pretty simple.
So clean up any metadata that 
you may have started collecting.
The big line here is the 
connections client, accept 
connections.
  And here you pass in a payload
callback.  This payload doll  
callback is what the devices 
communicate.  So you have a 
contract on your phone and on 
your TV for what they are going 
to say to each other.  Hey, 
phone, we want to do this.  Hey,
TV, we're going to do this.  And
this is all handled inside the 
payload callback.  So here are a
couple tips.
What you are going to 
communicate is very specific to 
your app, but here are some 
tips.  The payload received and 
payload transfer update are the 
only two methods you get.
They are pretty succinct.  
Payload receive, if you want to 
send an acknowledgement back, 
hey, thanks for telling us this,
phone.  We will send back an 
acknowledge.
acknowledge.Ment, so you know 
the message that has been 
received.  You call send to 
payload.  You give it the end 
point ID and some body, in this 
case, it says ack, or 
acknowledge.
And if you want to disconnect, 
hey, I received this payload, I 
want to
disconnect the connection and 
close the session, you should do
it in the transfer update.  And 
in the transfer update, you 
should see if it is in progress 
or not.  If you are sending 
messages like ack and send, 
those are fast.  
If you are sending something big
like a file, that can take a 
while.  You want to make sure 
that all of the bytes have been 
sent.  Once all of the bytes 
have been sent, you can call 
disconnect from end
point.
So now you are going to say, 
hey, I'm a TV, accept the 
connection and you are going to 
communicate.  On the phone, what
happens on the phone
side?
You are going to
discover the TV this time, 
accept the connection, and 
everything else looks like the 
slides I showed you.
To discover the TV, this time 
you call start discovery.  Mind 
blown.  You give it a service 
ID.  This time, I'm using a 
constant, and there's a reason 
for the service ID, depending on
your app, it should be the 
package name or the constant.  
If you have a package name that 
is the same for both your TV app
and your mobile app, it is going
to work great.  If you have 
something like 
com.mycompany.Android, or 
Android TV as your two package 
names, imagine they are on their
own channels.  So the nearby 
connections library will not be 
able to find the phone and the 
TV.  Having a service ID used be
both by both sides is a good 
practice.  You will give it a 
mobile end point discovery call 
back, I love really big
words.
And you will have a strategy, 
and I encourage you to use 
cluster for
this use case.  You get 
listeners, they are pretty 
important, and they are able to 
start discovery.  This does
require location permission, so 
if you get a failure listener, 
like permission for location 
hasn't been enabled, so they are
great for debugging and trying 
to urge the user down the
correct path.
 So the next part, accept the 
connection.  Really simple, you 
have the mobile end point 
discovery call back.  It has two
methods, you found the end point
or lost the end point.  Pretty 
simple.  If you find the end 
point, go ahead and request a
connection.
This requests the TV initialized
on the TV you saw earl
ierearlier.  If you lost the end
point, maybe the user is no 
longer nearby, or they gave up 
and closed the app and said 
forget it.  Hopefully it is the 
first for you, not the second.  
You should clean up whatever 
metadata you collected already. 
And anything after this is 
identical, you will have the 
connections live cycle call back
that shows how they communicate,
and the payload call back that 
shows what they are going to 
communicate.  And that's it, 
that's nearby in a nutshell.  
And nearby is a cool tool, it is
a nice box in your kits to have 
more experience in a TV app.  We
wills go on payments, we will 
look at one more example.  
Payments is cool, it adds a 
family-friendly idea. 
Imagine you are at work, your 
kid is at home, and they want
to buy the next season of a TV 
show.  You get a push 
notification, are you sure you 
want to purchase this, you say, 
man, my kid is at home buying 
stuff, no, or yeah, they are 
bored, I will use my finger 
print or anything from the phone
to authenticate yourself.  And 
you enabled a purchase at home 
from your
office.
And I have not seen a lot of 
this in Android.  This is not a 
good fit, it is not a good tool 
in the toolbox, but it is not 
necessarily the best fit.  So 
let's talk about a good fit, you
get a push notification, and it 
says watch on TV, or watch here.
This a big game, you want to 
watch it, watching on a small 
phone.  Eh.  Watching on a big 
TV? Awesome.
So you can use nearby to figure 
out proximity.  They are close 
to the TV, we should watch the 
TV button.  That's a great use 
for nearby.  Receiving
the notification an example of 
content immersion.  When you 
said watch on TV, the background
lit up, the schedule for the 
game, the highlights, the score.
And then whenever the user 
wants, they can put the phone to
the side, it is very 
non-intrusive, and they can 
focus on their game.  So in a 
sense, this is kind of kicked 
off from a notification.
And in a way, you would say that
is more of a push model.  Nearby
feels more like a pulling, I'm 
pulling a conversation between 
the two devices, and whereas in 
this case you are pushing that 
information to the user.
And you are talking about 
Firebase cloud messaging after 
this, I will not step on their 
toes too much.  We will talk 
about it for fun, what is the 
worst that can happen.  You set 
up the Firebase members of the 
jury 
messaging messaging service, and
this is what to do.
And if the action happens to be 
watch this movie or game, start 
watching and you are good to go.
Start watching,
then it should literally launch 
an activity.  
This is the Android Dev Summit, 
I will assume that everybody has
launched before.  In this case, 
we will set up the intent, the 
extras, this is a
video to watch and the activity.
And next, what happens when the 
TV is off? I am at home, I get a
push notification, oh, man, the 
game is about to start.  I have 
to hit power on the remote, I 
have to tune to the channel, 
man, this is first-world 
problems at its finest.
But you can solve this.  With 
the fragment activity, you can 
call, set turn on screen to 
true.  And this is a cool API on
activity.  It is actually 
introduced in OMR1.  So if you 
are on API 27 or higher, you 
should do a check.  Hey, turn on
screen to true, otherwise you 
can add the flag.  So start with
your player, your content is
king.  So really focus on the 
player, whether it is content or
an ad, make sure the player is 
solid.  How do you make the app 
more usable and get the 
lean-back experience so users 
can quickly find other content 
to watch? The third pillar is 
distribution, is my app deployed
to set up on the Play Store 
correctly? When you have all 
three of these, you have happy 
users, and who DUNCHT  doesn't 
want that? If you want to take 
it further, use these lean-in 
experiences, have payments, push
notification control, add the 
immersive content, the details 
of the game.
Thank you, and everyone go build
great TV apps.  If you have any 
questions, you can see us at the
office hours, thank you
very much. 
SPEAKER: The next
session will 
begin.
Coming up next: Android 
Suspenders by Chris Banes, Adam 
Powell.
 S
PEAKER: Hey, good afternoon, 
everyone.  Thanks.
SPEAKER: I'm Adam. 
SPEAKER: I'm Chris. 
SPEAKER: This is Android 
suspenders.  We're going to talk
about coroutines, and to get 
started, many of you are 
probably familiar with Android's
main thread.  Like any other 
toolkit, it exposes a UI or main
thread for exposing updates to 
parts of the UI.  You may be 
inflating views, measuring 
layout operations to change the 
shape of your view hierarchy, 
drawing, and many other things 
like processing input events and
so on and so forth.
So today you are probably 
familiar with the 16 millisecond
time limit, the vast majority of
devices out there are have a 
refresh of 16 hertz, which means
you have 16 mill secondss to 
load the display framework.  You
have less and less time to get 
this work done as the hertz go 
up.  
The things that cause you to 
miss a frame and have Jenk in 
your application is app code.  
If you are binding items to a 
recycler view, these are all 
bits of work that are great if 
we can keep it out of the main 
thread and off the critical 
path.  How do we fix it? This 
talk is not about achieving 8 
millisecond refresh boundaries, 
but using the resources we have 
available.  All the phones we 
have have multiple cores in them
today.  How do we make use of 
that? Some of you probably 
remember this thing, it
has been around for some time.  
It has issues around rotation, a
lot that it gets wrong.  There 
are executors, that developers 
are familiar with.  It has 
thread pools that are nice, but 
it is a raw API.  You can build 
things out of it, but it is not 
convenience  -- convenient to 
use on its own.
Loaders are a thing out there 
that solved a few problems, 
narrowly-scoped, it is 
deprecated and there's not a 
whole lot there.  We can use 
features, there's listenable 
future that showed up in the 
AndroidX APIs, but you might not
want to pull in all of the 
infrastructure that helps you to
leverage some of the thingsout  
you can do with it.  And unless 
you are working in min SDK24, 
you cannot use importable future
either and there's a lot of 
reasons you don't want to use it
to begin with.
  So Guava, you can use that to 
pull into the app.  And RSDK is 
helpful as well.  If you are 
here, you probably want to talk 
about coroutines, which went 
stable in Kotlin 1.3 this fall. 
So why should we use them?
All right.
So whaI when I think about this 
question, what does the typical 
mobile app do? They are CRUD 
apps, they Create, Read, Update,
Delete data.  And usually it is 
a source, a database, or 
whatever it may be.  And it has 
some kind of sync.  They will 
upload data and pull it back 
from some kind of web service.  
And apps are pretty simple 
computationally, you are not 
taxing the CPU very much.  And I
guess the logic can be tricky to
get right, but they are pretty 
simple.  And in Android, we put 
a lot of stuff in your way.  We 
make your life harder, but your 
apps are pretty simple from a 
logic point of view.
So why coroutines? And how do we
fix that? They are great for I/O
tasks, especially a 
resource-constrained system, 
like the phone and tablets you 
are using today.  And you cannot
create a thread for everything 
network request that you ever 
use, because threads take in the
space of that, they take about a
megabyte of RAM every time you 
create one.  That is why thread 
pools cache threads for you.  
And coroutines take in the realm
of 10s of kilobytes in a 
Coroutine, they use threads 
underneath in a much more 
optimal way.  They have an 
easier development model.  It is
easier for developers to come 
into a new source, and see an 
imperative, line-by-line 
Coroutine of something like an 
RX Java chain.  To understand 
what is happening there, in my 
mind it is easier, and the same 
for call-backs, everyone knows 
about call back hell, and going 
line by line and seeing what you
have called and stuff.  And 
coroutines hopefully, anyway, 
fix that.
And so a lot of this talk was 
written in mind with an app I 
have been writing called Tivi, I
went all in in RX Java, it is 50
percent coroutines
and 50 percent RX Java.  I use 
both, they both have a place in 
Android development, so I say 
use both.  And as developers, we
have to care about APK size and 
method cans.  I'm using free 
libraries from the
coroutines core, and RX2 that 
allows you to interact with RX 
Java.
And if you are actually pulling 
down the jars files to central, 
they come to 774 kilobytes.  
That is quite big.  And once you
are actually putting that in 
your APK, it shrinks down.  It 
comes down to 500 kilobytes.  
And the references are quite 
high, 54 percent of your 64K.  
And as soon as you turn on 
minify, and now this is tree 
shaken, no optimization turned 
on here.  And they both have 
results.
  You are looking at 113K.  So a
lot less, and again, the 
references is dropped.  As soon 
as you turn on optimization, and
you fix all the program rules, 
you are coming down to the magic
value, which is less than 100 
kilobytes.  And your reference 
is now 834, less than 1 percent 
of your references.
And now one thing to note, when 
you are using R8, you need to 
use this rule.  It is not 
bundled with Java, hopefully 
added soon, but a simple rule to
add. 
SPEAKER: Okay.
So hopefully you are thinking 
about how to use coroutines in 
your app, we will talk about how
to write them.  Anything you do 
with a call back feature, you 
can do with a suspend function. 
Anything with coroutines is a 
suspend function for creating 
APIs, they can suspend without 
blocking and resumed later with 
a call back and can only be 
called from another suspend 
function to set up the machinery
involved in that.  The core 
thing is all of this fits in one
slide, the language works the 
same in thethe presence of 
suspend functions and we will 
spend the talk talking about 
that.  This is the suspend from 
Chris's app, a data repository 
for TV shows, you call the 
update show function with the 
ID, we get some shows, and we 
get a little bit more data from 
a remote source, and a little 
bit more data from a second 
source.  We merge all of that 
together and we save that data.
So these three main tasks where 
we spend the bulk of the time, 
these are done sequentially, but
none of them have a dependency 
on one another.  Wouldn't it be 
nice to do it concurrently? With
the async builder, we can do it.
We start from the top here, we 
open a Coroutine scope, and this
allows us to build a parallel 
composition using the async 
builder, it has the receiver in 
the scope for the Lambda block  
block we have.  We build the 
async operation, the second, and
the third.  And we await the 
result of each one in turn.  So 
the nice one is that we have 
launched all of these things, 
let them run independently and 
then we bring them back together
again.
So since all of these things can
be now done in parallel, things 
should complete faster.  Trying 
to do this with raw threads by 
hand is a lot more code than you
need to maintain along the way. 
The async builder is when you 
want to run something and await 
the result after giving it a 
chance to finish while doing 
something else.  It is similar 
to C# or promises in JavaScript.
  What if you want to launch 
something and forget? There is 
that, too, it is called launch 
and works the same way.  In this
case, it is a lot more similar 
to executors and just sort of 
submitting a task, submitting 
something to an Android hand led
handler when we just want to 
fire and forget and deal with it
later.
Those are the basics of running 
coroutines in isolation, how do 
you handle it on Android?
So you may have the components 
ViewModel, wouldn't it be nice 
to have a way to put all of this
stuff together automatically? 
You need to open a scope, where 
can you open those things to 
begin with that you can launch 
into? In this case, the model 
has a show repository, the data 
layer, we have the viewer state 
that the viewer activity or 
fragment observe.  We refresh on
construction and we have the 
construct property.  
This is coming up soon.  The 
refresh function uses the 
Coroutine, the launch builder, 
and updates the function on the 
repository.  And the Coroutine 
updates back on the main thread 
so we can manipulate the view 
hierarchy, so we have a nice, 
clean, sequential ordering of 
operations.
  So those of you who want to 
check out this thing that is 
upcoming, you can go to this 
link, take a look at the change 
so far in advance of the actual 
release.  It is a release of the
KTX libraries.  We will 
demystify how this works.  
Before we go deep, we will talk 
about the other primitives that 
are under the hood here.
Woah, we lost our deck.
Uh-oh.  Did we switch to the 
wrong -- keynote crash
.
SPEAKER: We will talk about 
jobs, so what is a job? So when 
you look at the code snippet, 
you are actually using the 
launch method.  And now when you
are actually running that launch
method, it returns what we call 
a job.  And a job allows us to 
reference, keep a reference of 
the ongoing piece of work, it 
has one method on it, it is 
called cancel.  In this example,
we would not call cancel 
straight away after we launch 
something, that is ridiculous.  
What it does allow us to do is 
to handle double refreshes, if 
you have something that pulled 
the refresh in your app, you 
don't want the bugs to happen at
the same time and then you have 
two things happen at the same 
time.  So here, this code 
snippet, you can keep a 
reference of the one that is 
currently running, and if
first one.  And that is the kind
of how the job works, it is a 
very simple object.  It allows 
you to keep a reference of the 
on going piece of work.  So you 
may have seen that scope and 
wondered what it is, Adam 
explained it earlier.  You can 
have a scope, and it provides 
you with all the context that 
you need to run a launch or an 
async.
So let's look at how they work 
underneath.
So Coroutinescope ask an 
interface that allows objects to
provide a scope for coroutines, 
think things like with a life 
cycle, like fragment, 
activities, ViewModel.  They can
provide a life cycle full of 
Coroutine itself and start and 
stop it as it needs.  Async and 
launch used to be global 
methods, and a recent refactor 
brought them as instant methods 
on the coroutines scope.  And 
what it means is that, mentally,
instead of just launching 
something, you are launching a 
Coroutine on X.  So I'm 
launching a Coroutine on the 
activity.  It changes it in your
head, so it is tied to the life 
cycle of something else. 
SPEAKER: Right if you are used 
to working with the life cycle 
owner in arch components, the 
life cycle owner has a life 
cycle that you can observe and 
attach things to.  A Coroutine 
scope has a context that carries
everything necessary to launch 
the Coroutine. 
SPEAKER: YOU CAN
s. 
SPEAKER: You can essentially map
the context.  
SPEAKER: And it crashed again.  
SPEAKER: Well, it is a good day 
for slides. 
SPEAKER: Got it back? 
SPEAKER: Okay, cool. 
SPEAKER: So we will look at 
another example.  We are not 
using the V model scope, we're 
going to add the data ourselves 
.  We created a job, a simple 
instantiation, and then we are 
going to keep that -- we are 
going to create a Coroutine 
scope using add a job.  Anything
that runs on it allows us to 
track back using that Java 
object.  We will give it a 
default dispatcher, talk about 
it later.  And anything that is 
launched on that scope will be 
run on the main thread, so the 
Android main thread.  So once 
we've done that, we will have 
the refresh method, and we will 
use our own creative scope, the 
same code, using a different 
scope.  And this time the launch
we scoped to the job object woe 
created -- we created earlier.
  And what has been torn down, 
we can call the job.cancel, and 
any coroutines that are running 
when it goes down will be 
canceled at the same time.  It 
reduces memory leaks and allows 
it to tidy up.  So if you can 
look at how things are running 
now, so we have launched the 
Coroutine and we are going to go
into the update/share method.  
So here we are in Coroutine, the
launch, which is that blue thing
going around.  And in the update
share method, which is denoted 
by the yellow.  And this is the 
async builder, we have a first 
Coroutine running.  And so it is
running nicely, doing its thing.
And the outer Coroutine goes 
past that, and the second async,
which is the remote.  We have 
two coroutines running, well, we
have three.  But two in the 
launch.
And once they are going along, 
we go into the first await.  And
that first async, the local, to 
finish itself off and return a 
result, what is what the await 
will return.  Because we are 
waiting on the first Uync, the 
outer coroutines are suspended.
And during that time, the 
ViewModel is down, and we call 
job.cancel.  And at this point, 
the outer Coroutine is cancels 
and the inner two are canceled. 
And the child coroutines inherit
from the parent.  So if the 
parent has been canceled, 
anything below it will also be. 
That is something that was added
recently.
WLAUPS if what happens if you 
are using view models? So as 
part of the Android architecture
components, we added a listener 
functionality of the life psych
cycles.  You create a life cycle
observer, in this case we are 
using default, you can add 
create, destroy, stop, or 
whatever it be.  And you can 
create instance or observer, 
hopefully you have seen this API
before.  And this builds a scope
of where life cycle observer, 
which allows us to scope 
coroutines to the actual life 
cycle instance.  The primary API
is it passes the Lambda, and 
that is what everything runs 
with when you are started, that 
is kind of what you are using 
most of the time.  So it will 
allow the governance, and the 
first thing we want to do is on 
start, we are running the piece 
of code, creating the Coroutine 
scope, and running the 
dispatches domain.  And then we 
will call script.launch and call
the Lambda.  Pretty simple.  And
finally, on stop, it seems like 
a good life cycle to use that 
will call and will eventually 
cancel the job.  And that means 
the Coroutine will be canceled. 
You can see that it is not 
actually that complex.  In 
AndroidX, if you look deep down 
into it, it is pretty simple.  
To finish it off, we will have a
nice build function, you pass 
the Lambda, and it will observe 
it for you.
And what it allows us to do is 
like this.  So here we have a 
detailss fragment, the live 
scope, and then run something.  
And it will automatically start 
it, when we get to -- it will be
started when we go to start and 
new fragment, and then it will 
be closed or ended when we get 
on stop.
All right.  Brings us to 
cancellation. 
SPEAKER: . 
SPEAKER: We talked a lot about 
cancelling a Coroutine, what 
happens when this cancels? If 
you have a block of code that is
running, what is torn down when 
you need to clean up? So when a 
Coroutine is canceled, if it is 
while it is suspended and 
waiting for something else to 
happen.  In call back terms, it 
has not been invoked yet.  It 
will throw a cancellation 
exceptionexception, it will 
resume from the point it was 
suspended with a cancellation 
exception.  What does that look 
like? This is the example from 
before.  What happens if we need
to clean something up if this is
canceled in the middle of the 
update show function? Because it
throws a cancellation exception,
this is something we know how to
do already.  The blocks run as 
expected, we don't need to add 
concepts from what we already 
know from the rest of Kotlin.  
If the block-in code is running,
it requires cooperation in order
to stop what it is doing.  So we
can check for that cancellation 
explicitly in a couple ways.  
Generally this means checkinging
to see if the Coroutine is 
active. 
And one of the patterns is if 
you know the job is canceled, 
you can call the stock 
suspendingsuspending method, 
such as yield, to force the 
cancellation to be thrown.  If 
you are canceled, when you are 
trying to suspendsuspend, you 
will resume when the 
cancellation exception, so it 
will immediately throw if we 
happen to be canceled.  If you 
throw a tight loop, you can 
check the is active that is 
available from any suspending 
scope and you can stop what you 
are doing.  There is no reason 
to involve an exception here if 
you are doing a tight 
intercomputational loop you need
to break out of.  And that leads
into how exceptions are handled 
with coroutines in general, and 
there's a few things to point 
out, especially if you followed 
Kotlin's development leading up 
to release, because there were 
significant things that 
happened.  Launch will re-throw 
unhandled exceptions as they 
happen.  It fails the parent, it
cancels the parent job.  The 
parent sees the cancellation 
with the original exception as 
the cause.  And they get thrown 
back to the exception handler in
the root of the job tree, and 
the Coroutine context gets a 
chance to intercept.  You can 
attach a special element to the 
Coroutine concept itself that 
you can use to handle
handle unhandled exceptions.  
How does it work? So say that 
save show throws a 
domain-specific exception in 
this case.
So in this case, this will be 
treated like an on-call 
exception at run time, just like
anything else that calls the 
exception on the main thread.  
Async is different, if the 
exception is thrown while 
something you launch with async 
is running, it will hold the 
exception and only throw when 
the caller calls await.  So 
going back to the example from 
before, we will use what we 
know.
So we throw our exception from 
one of these async jobs, and 
that gets thrown from this call 
to await itself.  So we know 
exactly where we need to try and
catch that exception in the 
normal way and handle that 
error.
But there's a gotcha here, and 
that is that async works the 
same as launch in terms of how 
the nested job tree is handled. 
The deferred object is another 
kind of job, it will cancel the 
parent, just like launch does, 
and it will do it even if we did
an await and called it.  This is
important, if something throws 
an exception, it is important 
that your app knows about it, it
should not disappear into the 
Ether, but
it  we caught it, what do we do?
So instead of using Coroutine, 
we can use supervisor scope.  It
works like a Coroutine scope, 
but it is a supervisor job that 
will not be 
canceled if it is handled with 
an unhandled exception. 
SPEAKER: And earlier, we 
mentioned that me can decide 
when coroutines are run and what
thread they run on.  On Android,
we run on threads, and we are 
using the thread pull 
underneath.  And we can decide 
when that is dispatched on.
And now let's have a look.  We 
are running a very simple 
launch, that is missing a scope.
We are looking at the example.  
And default is the context we 
are using what is called a 
dispatch.default, that is what 
is given to you for free, and it
is default for everyone 
FWLNCHLTS . 
SPEAKER: A computation thread 
pool. 
SPEAKER: So
what is a Coroutine dispatcher? 
It schedules it to run on 
something, in a thread in this 
case.
And the default, which you can 
get, is it uses CPU threads.  So
your device has four CPUs in it,
you will get a thread pull of 
four.  So it is not as great as 
I/O, it is more like a 
computational-type dispatcher.  
And it is also an Elastic thread
executor, that I will talk about
in a minute, but it is the 
default.  There are 
dispatchers.I/O, that was added 
recently, and it was designed 
for blocking I/O tasks, so 
things that we care about, 
network, image loading, reading 
disks, database, blah blah blah.
It uses 64 parallelism, which 
means you can have 64 tasks run 
at a time.  It can launch it 
like that.  And the really great
thing about the I/O dispatcher 
is it shares thread pulls with 
the default dispatcher. And the 
point where it is great is this.
We have an async that is using 
the default dispatcher, and then
we load an image on the I/O 
dispatcher, we do disk reading, 
and then we're going to use that
result and process it somehow.  
It is a computational task.
And now what?
Because this is running on the 
default dispatcher, there is no 
actual switch in there, we are 
using the shared thread pools, 
the I/O uses the shared threads,
and there is no actual thread 
switch which makes it a whole 
lot quicker.  And we have 
dispatch.main, it allows running
coroutines on the main thread.  
And it uses service loader to 
load the dispatcher in your 
code, which is tricky when we 
have things like 
Pergyle, so you need to be 
careful and add the Android 
dependency.  So use it,
launch the dispatch.  That 
brings us to reactivity.
How many launches can be 
summarized by this slide? I have
been guilty of this.
And I will make the premise and 
the statement that most devs use
RX Java because you can switch a
thread, or switch multiple 
threads, that is when most RX 
Java is useful.  And I think, 
for 80 percent of the cases, it 
is a switcher thread.  And that 
is because the APIs that we 
have, and we spoke about them 
earlier, are not so great to 
use.
And because of that, most people
end up using things like single,
maybe, and completeable.  That's
what they are, they are a 
single, one-shot thing, a single
allows you to have a type, maybe
it is nullable, and the 
completeable doesn't have a 
return type.  They are all 
pretty similar.  But in fact, 
they only exist on RX 
Java, RX Ruby.
  So they can be replaced by 
single, maybe, and completeable.
They do what you think.  They 
replace call-backs, and they 
replace these nicely.  So as an 
example, we have a retrofit 
interface, and it has a Git and 
returns a single.  And the way 
you use that a in RX Java, we 
have the scheduler, and we will 
do some calls when it is finish 
ed.  The nice thing about the RX
two libraries of coroutines, you
can use that exact single as an 
await.  So you can actually use 
it as a suspending deferred.  So
it is handy for when you are 
slowly migrating towards 
coroutines and you don't want to
change from day one.  You can 
actually keep those interfaces, 
and you can actually just call 
await on them and you never 
receive using coroutines.  It is
a handy way to slowly 
migrate.
And wouldn't it be great if we 
can make the retrofit call, a 
suspending function, and remove 
the RX from the start?
Well, we can.  And that is 
coming to retrofit soon.  Jake 
has a PR, in review, and he 
tells me it is
soon.  And if you look at 
consuming code, it is a normal 
suspending call.  And that 
brings us to a final section, 
bringing it together and trying 
to think of two scenarios that 
we will show you to use 
coroutines to make your lives 
easier on Android.  Both of 
these examples are about 
location.  The first is about 
getting the last known 
location, which is the call 
back.  And we will use the fused
location provider client.  And 
it is kind of cool, it combines 
all the providers that we have, 
like WiFi, GPS, mobile, and 
bluetooth, it provides all the 
providers for you into one 
single API.  It is a nice API to
use.  And it returns a task, a 
futury-type thing that the 
library has.
And so you get the 
classification and it returns 
the task.  And then you add the 
complete listener and you get 
the result back.  So it is 
completely async.
So what we are doing is we are 
converting the call back API 
into a suspended function.  The 
coroutines library has two 
builders that do what we want.  
The first is suspend Coroutine, 
and you pass a Lambda under it 
that allows you to set up the 
call back.  So we call play 
services.  And then, at that 
point, the Coroutine suspends 
waiting for the results to come 
back, and the call back can wake
up, basically.
So right now, you are getting a 
continuation to later resume. 
And then you will pass the 
result back.
And it allows
the 
call.
So there is the suspend 
cancelable Coroutine.  So let's 
say it is canceled, you can -- 
under the line API, play 
services, to cancel its call.
So we will build that function, 
get classification, and it 
returns a location, not the 
future or anything like that.  
Just a straight location.  And 
so we are going to use our 
suspend cancelable Coroutine 
builder, and then you get a 
continuation, the call-back type
thing, and then we are going to 
set up, so you are going to call
the location client, the play 
services API, the last location,
and then we get a task back and 
add the complete listener.  At 
that point, we are going to wake
up our Coroutine.  So that is 
how we pass back to the 
suspended Coroutine, the result,
and it wakes up in the suspended
function basically, or it 
resumes.
And because this is a 
cancelable, because this we are 
using task, we are not using 
success, we are using the 
complete listener, which means 
that the task itself can throw 
an exception.  It can fail for 
whatever reason, so you don't 
have location permission or 
whatever it be.  It will raise 
an exception on you, so you can 
populate that back up to the 
call, which is done with the 
review and exception method.
And finally, because we are 
using suspend cancelable 
Coroutine, we need to tell the 
play services API that we have 
been canceled, so therefore it 
should cancel.  So we did that 
with a call back, which is 
invoke on completion, and once 
you know it has been canceled, 
you have play services.  And 
play services does not have the 
cancel method, but imagine it 
exists.
  And now Adam is going to talk 
about observing. 
SPEAKER: Sure.  So what happens 
when you want to observe a 
sequence of events over time? 
This is what RX is good at, if 
you are using it for anything t 
, it should be this.  People 
compare RX and coroutines quite 
a bit, so what does it look like
if we emulate this using 
coroutines as a primitive.  So 
it is an API in addition to 
letting you get a one-shot, it  
the current location, you have 
the up adapts updates.  So this 
is a prime candidate to be an 
observable.  And we get this 
composable control over shutting
it down the  down the updates 
cleanly.  It offers a lot of 
functionality to build things 
like this.  So how many similar 
benefits can we get if we base 
this off of suspending 
functions?
So let's again start just 
writing a simple function, or it
is going to start out simple.  
Suspending functions do not 
return until the work is done.  
There is no disposable or 
closeable return, since the 
calling scope itself is 
cancelable, we do not return 
until we are done.  So the 
observer in this case can be a 
Lambda that accepts a location 
result, we will call it whenever
a new location is reported 
without returning from the 
observed location.  So if you 
take in the giant pile of code 
here, some of you may mote  
notice that it looks like an 
observable.create.  We will 
create the done signal that we 
can await on later, so is this 
like the observable completion. 
If you have a stream with a 
well-defined end signal, you can
clean this up to let it clean up
and return and we will see that 
in a bit.  So the next piece 
here, we are creating a location
call-back, we need to know when 
to receive updates from the 
provider.  We use launch to
get the -- the Coroutine scope 
that we opened here carries the 
dispatcher with it that it was 
called with.  So we know that we
are going to call the observer 
in the same place that the 
caller wanted the information 
reported.
So we cancel the old job, and we
call the observer when holding 
the suspending new text, it 
keeps it serialized so we don't 
have too many at once.  
And it will not have multiple 
calls open at once either.  And 
this is an example of some of 
the things that if you are 
building one of these things 
yourself, this is a comparison 
that RX Java does a lot of these
things for you.  With 
coroutines, we have the 
primitives to build it, but we 
need to do a little bit more by 
hand.  And so we register the 
call back, and then we await on 
the done signal.  Since we never
complete it, when is the 
location stream complete anyway?
This waits for the calling job 
to be canceled.  So we have it 
on the blog, and request 
location updates takes a looper 
instead of an executor, which is
unfortunate.  So if you can use 
a direct executor, it will just 
go ahead and run the observer 
wherever the update happens on 
the incoming binding thread, it 
shines when you can avoid the 
extra hops.  You get the idea.
And so here is what it looks 
like in use, and it looks a lot 
like a four H call in a 
collection and behaves the same 
way.  If we use launch to call 
the observer, if the location 
call itself will
throw in itself.  So it is a 
child job, it will chancel the 
parent scope with the exception 
as well, what wraps the observed
location function body.  It will
have the await, unregister it 
from the final block from above 
and all of this composes so you 
can lean on the constructs of 
the language that you know by 
adding some of these suspending
primitives.
SPEAKER: So we will wrap up a 
little bit.  What is next?
Well, the first is that, as you 
saw thin keynote earlier, we 
have a code lab for coroutines, 
that was released three weeks 
ago, a really good introduction 
into coroutines and how to use 
it in your app.  Read the 
manual, and the docs are really,
really good.
And you can edit them how you 
want to, they are use-case 
based, so I need to do X, how do
I do it? Make sure to check it 
out if you are using coroutines.
That is it, thank you very much.
[ Applause.] 
Coming up next: Modern WebView 
Best Practices by Nate Fischer, 
Richard Coles, Toby Sargeant.
Coming up next: Modern WebView 
Coming up next: Modern WebView 
Best Practices by Nate Fischer, 
Coming up next: Modern WebView 
Best Practices by Nate Fischer, 
Coming up next: Modern WebView 
Coming up next: Modern WebView 
Best Practices by Nate Fischer, 
Coming up next: Modern WebView 
Best Practices by Nate Fischer, 
Coming up next: Modern WebView 
Best Practices by Nate Fischer, 
Richard Coles, Toby Sargeant.
1XZ
Coming up next: Modern WebView 
Best Practices by Nate Fischer, 
Richard Coles, Toby Sargeant. [ 
Applause ]. 
SPEAKER: Hi, everyone.  My name 
is Nate, I'm here from the 
WebView team, I would like to 
talk about some modern WebView 
best practices or, as I like to 
call it, using WebView like it 
is 2018.  Because
it is.
Before I dive into what is 
modern with WebView, we will 
talk about what is old.
So WebView has been around since
the very beginning, added in API
level 1, and it changed in a 
significant way, starting with 
Lollipop.  It became updateable,
the implementation was.  And 
this was great, it meant that 
users could benefit from 
security fixes and bug fixes, it
would update every six weeks, 
just like your browser.  And a 
lot has changed since then, too.
We added 40 new APIs, ZUST  -- 
just to make it easier to work 
with WebView to help developers.
But what has really changed? 
Well, when we look at the 
ecosystem, it seems like apps 
are kind of using WebView the 
same way they have always used 
it.  And when you look at 
StackOverflow, the answers are 
outdated at best.  They are 
certainly not best practices 
and, often times, they are just 
wrong.  But some of the blame is
on our shoulders, too.  A lot of
the doc is still written like it
is API level one and a lot has 
changed since then.  So we 
looked over the past year and we
looked out at the Android 
ecosystem and the devices that 
are out there today. And what we
found is that, although we 
added, you know, on nougat, all 
of these great APIs, a lot of 
apps cannot take advantage of 
them because they run on the
nougat devices, less than 5 
percent of devices today and it 
has been two years since it came
out.  And a lot of devices are 
running lollipop and the 
implementation of the APIs are 
not exposed on older platform 
levels.  We thought, can we do 
better?
So, over the past year, we 
worked on our AndroidX library. 
We launched a new AndroidX 
library, and we are pretty 
excited about it.  The basic 
idea is we will bring in all of 
these brand new developer APIs, 
but we are going to try to give 
you the device coverage you 
need, and we are going to 
support lollipop and above.  We 
will leverage the update cycle, 
and make sure that it is usable 
so you can use the APIs to do 
productive things.  We designed 
them to be straightforward to 
swap out from the framework's 
APIs.  So this all sounds fine, 
but how can we use it to make 
your apps better?
So let's take an example.  Since
the very beginning of Android, 
we have given apps a lot of 
power to customize WebView's 
behavior.  And, in particular, 
we added a call back called 
override loading, and the idea 
of this call back, for search 
and navigations, you can cancel 
things in the WebView and 
dispatch them to a different 
thread instead.  Joy can have a 
YouTube url that is better 
suited in the YouTube app. And 
this is great, a lot of apps 
took advantage of this.  But 
there was a problem with the 
API, we did not get it right the
first time.  And the issues that
JavaScript can trigger 
navigations, there is malicious 
JavaScript out in the wild that 
actually tries to exploit this 
app behavior.  And, from the 
useruser's perspective, they 
might be reading web content and
without their interaction, it 
starts opening up some new 
Android app that they are not 
trying to open.
And so we actually already fixed
this issue.  We fixed it back in
Nougat, where the idea is that 
we exposed this notion of user 
gesture.  Did the user trigger 
this navigation?
And it actually works really, 
really well, but it only works 
on the Nougat and above devices,
and these -- before, Nougat 
devices are still vulnerable.  
Weal thought it was a candidate 
for AndroidX library, we will do
this gesture all the way back to
loll
Pop for devices and you can make
it a safe experience for all of 
your users, and we can make it 
easy for apps to override T. 
There is no confusion.  I think 
we succeeded, but we will look 
at the code.  Before Nougat this
is what a lot of app code looked
like,
we are doing overview coding, 
and in the 
coding, and this is the insecure
version of the API but in the 
past the best we could do.  Some
better apps that were out there 
is something like this, you are 
overriding the old implement 
from before, the before Nougat 
devices, and the newer devices, 
you have this implementation, we
are choosing user gesture, we 
not launching intent if we don't
have user gesture.
So this seems great, but it only
runs on a small number of 
devices, even today, only 50 
percent.
So here's how it looks like with
AndroidX.  And the first thing I
want to point out is that almost
nothing changed on this slide.  
I think that is really 
beautiful, it means all the code
that you already wrote to 
handle, you know, the old 
framework APIs, that code is all
the same.
The only difference here is that
we are employing -- we are 
importing our WebView client 
compat class from the AndroidX 
library, we setting this compat 
client, and the idea is that we 
are using the compat client,
instead of invoked on Nougat and
involved, they are invoked to 
Lollipop so you can provide your
users a safer experience without
changing a lot of code.
  This is one example we have to
everyone in AndroidX. O I I 
would like you to check it out 
it  to so how it can make your 
app better.  We are giving you 
the device coverage you need to 
use these APIs, but we will have
a lot more APIs available.  Some
of these are small improvements 
on classic APIs, like we've 
seen, and some them are for en 
tirely new features, like safe 
browsing. 
And had point is this is not in 
a soon to be released library, 
it is out there, ready to go, so
you can try to 1.0 release.  I 
would like to shift gears a 
little bit.  We looked at the 
AndroidX APIs, these same great.
You are going to give us new 
APIs with, you know, pretty good
device coverage, almost ninety 
90 percent.  What about the APIs
around forever, they have 100 
percent device coverage, but 
some of them are hard to use.  
Even I struggle to use them 
correctly and I working on the 
team.  A common use cay we have 
seen is loading in-app content. 
And the idea is that you want to
display some content in your 
application, but you don't 
necessarily want to fetch it 
over the internet, you want to 
just compile it right into your 
app.
But you also want to continue to
build this content with web 
technology.  You want to use 
html, JavaScript, CSS.  And 
WebView has had pretty good 
support for this, in fact, we 
almost have too much support for
this.  We have so many APIs that
it is hard to figure out which 
one is actually the right thing 
to use.
And some of them have some weird
gotchas that make them kind of 
hard to use.
And so I thought maybe we could 
take a look at some of these 
APIs and talk about what is so 
tough about them and recommend 
some best practices.  You don't 
have to start from scratch with 
a new API, but you can kind of 
tweak how you are using these.  
So the first API that we can 
look at is Load Data, and the 
basic idea is that this is going
to accept some html content.  It
accepts it as a string, and it 
is supposed to display this in 
the WebView.
And but one of the gotchas is it
does not really accept html 
content, but encoded content.  
The idea is that you need to 
escape special characters, 
replace them with a percent sign
and the code following it.
And we call this percent in 
coding, this is the default 
configuration for the API.  But 
there is actually no framework 
API to do the percent in coding 
for you.
It is kind of an oversight.
But the end result is that 
developers, what we've seen, is 
that developers tend to actually
do this percent in coding by 
hand themselves.  And this is 
manual, it leads to bugs, and 
these bugs can have significant 
impacts for your application.  
You know, one small bug might 
seem okay today, but it might 
break in a future WebView update
if you forget to encode a 
particular character.
The other issue with load data 
is this thing called an opaque 
origin.
So when your content is loaded 
with what is called an opaque 
origin, this means that it is 
going to fail all the same 
origin checks in the web.
And these same origin checks are
actually chit  critical to 
providing powerful web APIs
securely.  Without these, you 
cannot provide great APIs, like 
html http requests.
So what can you do with this 
APIAPI? You can escape the 
coding-related problems.  This 
API has always accepted an 
alternate encoding scheme, base 
64.  This is not that special of
a coding scheme, it is just a 
different scheme.  It is not 
necessarily better, but it is 
kind of nice because there is 
actually a framework API that 
will do the encoding for you, 
and it does it correctly.  
Great.
So the base64, encode to string,
it will take the content, spit 
out the right answer.  And the 
only reason it is not documented
is because this came out in API 
level 8, which today is ancient 
history, but was still in the 
future at the time of writing
load data.
But we can also take a look at 
the same origin restrictions.  
So the way we recommend to get 
around this is to use something 
called load data with base url. 
And one of the nice things about
this, I think of it as a 
feature, not a bug, is that it 
actually accepts the content as 
is.  You can give it content 
that is totally unencoded, you 
don't have to worry about the 
base 64 stuff if you use this 
API.
The other really nice thing 
about it is it has this thing 
called a base url.  You are 
displaying this as you pass in 
as a string, the base url 
configures the origin that it 
operates it.  You can control 
the origin that you get without 
disabling the important security
settings, just to make the APIs 
work.  How do we choose the 
right base url?
So this is something that even I
struggle with when I try to use 
this API, I know it is the right
thing,
but I don't know what the right 
thing is to pass to it.  
So we will go through the common
use cases.  So something that 
we've seen in a lut  lot of apps
use cached content, it is 
downloaded from the web over the
internet, but they are saving it
for later.  Now when they show 
it, they need to show it with 
the right base url.  And the url
you choose is just the original 
url it came from.  So if it 
worked originally, it has the 
same origin and all the APIs are
going to continue to work.  The 
other use case that we have 
noticed is that apps tend to 
shift the own content and 
display it this way, which is 
great.
And we recommend that you choose
a real internet-type url, and it
should use your organization's 
real domain.  The reason for 
this is so you can import other 
resources from the servers and 
use this content without 
worrying about same origin 
checks, it will all work.  And 
the question is, do we use https
or http? Here is the rule of 
thumb, you want to use https as 
the secure protocol.  If you 
need to share insecure 
resources, we recommend that you
use the html scheme as opposed 
to disabling important security 
settings just to get your app 
working.
And as a last point I want to 
urge apps to avoid curs  custom 
url schemes.  This is something 
that cropped up, they make up 
their own scheme and use that.  
But the problem is that the web 
standards don't really expect 
custom url schemes.  They are 
very vague about how to handle 
this, and it turns out that they
wind up getting handled very 
inconsistently, and this can 
lead to surprising app breakage.
So if you can stick to one of 
the internet url schemes you 
will have a much better
time.
So hopefully I have expressed 
that we care
about developers at the WebView 
team and we are working hard to 
make sure THLT  that you have 
powerful new APIs, paying 
attention to the old APIs, and 
explaining how they need to be 
used and they are actually 
usable.  If you have any 
questions, me and my colleagues 
will be around for the rest of 
today as well as tomorrow and 
would be more than happy to talk
to you about WebView usage and 
what you need for your 
application.  Thank you very 
much.
[ Applause.] 
Coming up next: Low Latency 
Audio - Because Your Ears Are 
Worth It by Don Turner.
Coming up next: Low Latency 
Audio - Because Your Ears Are 
Worth It by Don Turner.
SPEAKER: Hello, hello.  My name 
is Don, I work in the Android 
developer team, I'm here today 
to talk to you about low latency
audio on Android.
So why is audio latency 
important? Well, there are many 
use cases where the audio 
latency is directly proportional
to the user experience.
So this can be in games, where 
you tap on the screen and you 
hear a sound, particularly in 
rhythm games, like Guitar 
Hero-style games, you are 
tapping in response to some 
rhythmic event and you need to 
have audible feedback as quickly
as possible, like the longer the
delay between tapping on the 
screen and hearing a sound, the 
worse the user experience.
And also in DJing apps, you are 
tapping on the screen and 
manipulating odd we and you 
expect that audio manipulation 
to happen in realtime.  
 Karaoke, you have the input, 
your voice, against a backing 
track and also your own voice.  
So if the delay between you 
singing and hearing your own 
voice is too long, then it 
sounds awful.
And also, in VR, we have objects
that make sound in a virtual 
world and, if that sound doesn't
follow you around as your head 
moves in this environment, then 
it kind of distorts the reality.
And lastly, of course, theres a 
whole load of apps for live 
performance, sin 
hesizers, drumming apps, 
anything where you press a key 
and make a sound, you need 
low-latency audio.
So, with this in mind, we built 
a library to help developers 
build these kind of apps.  It is
called Oboe, and it is available
on GitHub now.  We just launched
version 1, it is 
production-ready, to be included
in your apps today.
And the way it works is, under 
the hood, it uses the audio API 
on API 27 and above, which is 
the new high performance, low 
latency, audio introduced
in Oreo.  And on all the 
devices, it uses open SLES. It 
provides a simple, easy-to-use 
API that works across the widest
range of devices.
  So rather than me talking 
about this API, I thought it 
would be fun to build a musical 
instrument in 17 minutes and 23 
seconds.
So, before I start that, I'm 
going to explain the 
architecture so it makes sense 
when I'm in the code.  So we 
have an app, and I'm going to 
build the audioengine part of 
this app.
This audioengine is going to be 
responsible for creating an 
audiostream that is provided by 
the Oboe library, we're going to
be passing audio frames of data 
into this audiostream.  
Ultimately, this stream is 
responsible for putting data out
of the audio device.  In this 
case, it will be the 3 and a 
half millimeter jack on this 
pixel XL phone.  And every time 
the audio device needs more 
information, it is going to give
us a call-back.  So we get this 
call-back loop of, hey, I need 
more audio data, and our 
audioengine is going to be 
passing frames of audio data 
into the audiostream.
For some form of control, we're 
going to monitor tap events in 
that screen.  When you tap down,
the sound will go on.  When you 
lift up, the sound will go off. 
This works about 50 percent of 
the time in rehearsal, we will 
see what happens.
Okay.
First, I need to log in.
Okay, and
can you see my screen?
Fantastic.
Okay, so I'm just going to run 
the app.  So I have a very 
simple shell of an app.  It 
doesn't really do anything at 
the moment, but it has a few 
little shortcuts that make it 
possible for me to do this in a 
short amount of time.
So I would just run the app on 
this pixel XL, and hopefully you
will be able to see that it does
nothing.
So here we go.  Here is the app,
when I tap on the screen, 
nothing happens.  No sound comes
out, it is non-functional.  I 
want you to know that there is 
no smoke and mir and mirrorers, 
it is jen  genuinely 
live.
[ Laughter ].
Thank you, GLRKS  Glenn.
  I have a few methods I will 
use, and I will implement them 
in a second.
So we will create an 
audioengine, and we will start 
by calling start engine.
So we will just jump into our 
J&I code.
So this is in native lib.CVP 
here.  So I'm going to define an
audioengine up here.
I will call it engine.
And then I'm going to call a 
method on my engine called 
start.
And now, I have already created 
the header and implementation 
files, just the blank files for 
this audioengine class.  So I 
will go ahead now and write the 
class.
So audioengine.
And I'm going to have one method
called start.
Okay, now I can use option enter
to generate the definition for 
this in my
implementation and I'm in the 
start method.  Before I use the 
Oboe library, I need to include 
the Oboe
header.
There we
go.
And the other thing I need to 
do, where it makes it easier for
me, to use the Oboe name space. 
And this avoids me having to 
prefix the objects with the word
Oboe.
So, in our start method, I will 
create an audiostream.  To do 
that, we use an audiostream 
builder.
That builder allows us to set 
properties on the stream.  That 
is like the format of the 
stream.
And now, when I set the format, 
there are two choices I can 
choose from, 16-bit integers, or
floats.  I will use floats.  I 
can also set the number of 
channels, so that is two for 
stereo, or one for mono.
And I can also set properties 
which inform the stream of my 
latency requirements.  So the 
most important one here is set 
performance mode.
And there's a number of options,
but the one I want is the low 
latency one.
The second thing I can do is set
the sharing mode on the stream. 
We will set that.  I will set it
to an exclusive sharing mode.  
So that means that I'm 
requesting the audio device give
me an exclusive stream, that 
means that my app's audio is not
mixed with any other audio in 
the system.  If the device 
supports it, I can avoid having 
my odd ye -- audio mixed with 
anything else and I can cut a 
few milliseconds of latency in 
the output.
So that's all I need to do to 
open the stream, Joe can go 
ahead now and call open stream, 
this takes a reference now to an
audiostream pointer.  I can use 
option enter to create a new 
field called stream.  So back in
the header, it is done 
automatically for me.  And once 
the stream is open, there is one
final step I need to take which,
is to set the buffer size on the
stream.  I can do this by 
setting buffer size in frames.  
To get the smallest possible 
buffer size, we have to 
interrogate the audio device for
the minimal amount of data it 
will read in one operation, a 
discrete chunk of audio data, 
and we call it a burst.  So we 
want to obtain the burst size 
from the
odd  -- audio device.  
We will use stream, gets per 
burst.  And that's the minimum 
number of buffers we can set our
stream to have, but we to not 
recommend that you use this 
absolute minimum.  We recognize 
that you use double this, 
because it provides a good 
protection against under runs, 
and it is a good tradeoff with
latency.
That's all I need to do to 
create a low latency stream.  So
I can go ahead and start the 
stream, which will do nothing 
because we have not found a way 
of putting data into the stream.
To get the data into the stream,
we use a call back.  I'm back in
the builder, I will send the 
call back
method.  It will take the call 
back object, and using this, I 
will use my this object, which 
means that my audioengine needs 
to implement this
interface.
So I will do this, and I will 
use control O to show me the 
methods I can override in this 
interface.  I want on radio 
ready, the method that is called
every time the audio device 
needs more data.
So inside here, I will look at 
what this method signature is.  
So on audio ready is called, it 
tells me the stream that wants 
more data, and it gives me a 
container array.  So this 
container array, which is of 
type void star, because it can 
be either 16 bit integers, or 
floating point samples is 
something that I'm going to 
write my audio data into.  SoI 
so I write that, that is passed 
to the audio device.  And next 
is num frames, it tells me the 
number of num frames that need 
to be populated in this 
container array.  I need an 
audio source, I will cheat a 
little bit here.  I created an 
oscillator in advance, we will 
take a quick look at it, and it 
is going to generate a square 
wave here.  So that's a periodic
signal, varying between two 
points to create
a square wave. 
We will create an oscillator.
So this is the template object, 
I need to tell it
what type.
I will include the oscillator 
header.  And now I have the 
oscillator, I will do ask
render, so Android Studio is 
complaining about the signature.
I will build, and that will 
normally sort it
out.
Ignore the arrows.  So on my 
oscillatoroscillator, I have a 
render method to put the audio 
frames from the oscillator into 
the audio data array.  So the 
first thing I need to do is to 
cast it to this -- an array of 
floats.  
So audio data.
And pass in the number of 
frames.
So the last thing I need to do 
on audio ready is return a 
result.  This can be either to 
continue where the call backs 
will continue, or it can be 
stopped and the call backs will 
stop.  So, in my case, I'm going
to continue.  And the final 
thing I want to do is set some 
things up on my oscillator.
And soso I'm going to set the 
amplitude, which is the volume, 
and I'm also going to set the 
frequency, set that at 80 hertz,
a nice base freaks  frequency, 
and the sample rate, which tells
the oscillator how frequently 
these samples should be 
produced.
And I can get that from the 
stream, get sample rate there.
Okay, I know that you are all 
desperate to hear a sound.  
There is a final thing I need to
do here, I need to respond to 
tap events.  So I will just go 
back into the main activity and 
I will override the on touch 
event so that
if the event is down, then I'm 
going to call this native method
tap, to make 
that true.
Otherwise, if the event is up, 
I'm lifting my finger off the 
screen, then I will pass in 
false.  Okay, let's have a look 
at the tap method.
This needs 
implementing.
So I will pass in the true or 
false value, create the new 
method, and they will just call 
ask, set, wave on, and that is 
going to pass that in there.
Now, a moment of truth.
So I'm going to run this and, in
theory, you should hear a
sound.
And when I tap on the screen, we
should hear an 80 hertz square 
wave.
The pressure.
[Tone].
There we go.
[ Applause ].
[Buzzing noise].  
SPEAKER: So you can see, it is 
the lowest possible latency 
here, and it is actually -- 
[series of beeps] -- pretty 
responsive.  So we have a 
musical instrument.  Admittedly,
it is not the best instrument in
the world.  It is a little bit 
to be desired on the sound.  So 
what I thought would be nice is 
if you could add a little bit 
more control.  So for the last 
four minutes and 30 seconds, I 
will tie the motion sensor to
the pitch of the oscillator.  To
do this, I'm going to cheap.  I 
will call in some code that I 
did earlier, and it will 
register a list now that will 
listen to the rotation vector 
sensor.
So to listen to these events, I 
need to implement the sensor 
eventevent listener interface, 
implement the methods, 
onsensorchanged.  So what I want
to do is set the frequency based
on the event values of the X 
axis.  And I also need to scale 
this value.
So I want to have it from, let's
say, from 20 hertz, the limit of
human hearing, and we will go
up to, like,- this will give me 
up to around 100  hertz.
So, yep, that looks good.
So again, I need to implement 
this.
Frequency... so we will just go 
ask set frequency, and there we 
go.
Okay, so we are good to go on 
that.  Brief interlude.  Very 
brief, in fact.  Has
anyone heard of the R man break?
One person.  So the R man break 
comes from a song in the '60s, R
man brother, 4bar four bars of 
the most incredible drum solo, 
the most sampled loop in 
history, but nobody has heard of
it evidently, apart from one 
guy.  I thought it would be nice
if I can play my new musical 
instrument over this 
loop.
So here's the loop, I need to 
run the app.  We will give it a
go.
We will make sure
it is here.
So, with a bit of luck [drum 
sample].
[Oscillating
tone].
[Drumming and oscillating tone].
SPEAKER: Right.
[ Applause ]. 
SPEAKER: Okay, so that is about 
it from me.  If you can go back 
to the
slides.
And, yes, the library is 
available on GitHub.  There's a 
documentation, codelabs, all 
sort saidsses of stuff in there.
We would love for you to build 
awesome experiences on Android, 
and I'm here for any questions. 
Thank you all very 
much.
[ Applause ]. 
SPEAKER: Thanks, Don.  With the 
end of that talk, that is the 
end of the day.
That means that it is time for a
par  party.  So the theory is we
will have food, drinks, and a 
DJ.  I vote for Don do the DJ.  
I wanted  want to hear that tune
all night long, or a two-minute 
loop and that is probably about 
enough.  So 6:20, the party 
starts....
