Tip:
Highlight text to annotate it
X
Onorato: I'm Joe Onorato. This is Romain Guy.
We're both framework engineers on the Android team.
We're gonna talk about supporting multiple devices.
Guy: But not quite. Onorato: But not quite.
Well, if the code's gonna run on more than one device,
we've got some--well, we're gonna do it on the emulator.
And the reason I sort of didn't want
to talk too much about multiple devices
is all the devices we have now are pretty much the same.
There's--
Guy: I also didn't really know the subject, so.
Onorato: Well, that too.
[laughter]
So it's--
There's the Dre--the G1.
I'll probably call it the Dream at some point.
Guy: That's okay, you can call it the Dream.
Onorato: We're used to that.
Then there's the Ion, or the Magic,
or whatever we're calling that,
and, um...
they're almost identical in terms of hardware.
One has a keyboard, obviously, but other than that,
the screen size is the same.
That's far and away the hardest thing
that you guys have to deal with.
Guy: The same physical screen size and physi--
resolution and same density, so the same screen.
Onorato: Same part number for the screen, so.
Okay.
Oh, I'm supposed to say that if you want to provide feedback--
Or please provide feedback on this presentation
at haveasec.com/IO.
Guy: And I was supposed to say that for my previous session,
so now you know.
[laughter]
Onorato: How many of you--
How many of you guys came from Romain's previous--oh, God.
Okay. [laughter]
Well, good-- good plugging, I guess.
All right, so our motto is "write once."
No, that's not.
But we do have--
We do want you to not have to write
a different version of your app for every single thing.
Guy: Except we don't promise that you will have nothing to do
to make your app work across the bases.
You can write it once,
but you might have more work the first time.
Onorato: In fact, right now the market--
You can actually load-- upload one copy of your app,
so you actually do have to have it working
with one binary for the whole--whole world.
But I know they're working on that too.
So here's some--
some of the things you have to worry about.
There's OS versions 1.0, 1.1,
it's on there, Cupcake, Donut, so.
I think we let it loose that it's called Eclair last night.
Guy: Not just last night. It's public.
Do you guys know what Eclair is?
Onorato: It's E.
Guy: I'm not talking about the pastry.
Okay, good.
Onorato: They had eclairs at the party last night too.
So I think that that gave it away too.
The...
So there's Donut, and luckily most people--
All the devices, as far as I know of,
are getting updated to cupcake.
So we don't have too much of this
multiple version thing to worry about yet,
but this is the mobile industry; the phones don't last forever.
So at some point people will have old versions.
Guy: Well, and let's admit it.
You like to change phones every now and then.
Onorato: You know, I have one every, like, two months or so.
Then I drop it or break it or something.
Guy: Yeah, you drop it.
Onorato: Drop it.
Cupc--
Okay, so the other thing we have is configuration.
So there's--
Let me get the--
There's locales, orientations, which operator--that's carrier.
A lot of other stuff.
Guy: Actually, we have good documentation on that.
Onorato: We do, actually.
That's the other reason
I didn't want to talk too much about this.
Because the docs on this are actually pretty good.
I have a slide for that later, so.
As the URL.
You know, these animations are, like, killer.
Guy: Powerpoint.
Onorato: So this is configuration object.
It's "android.content. res.configuration"
in the docs.
And it's got these different parameters here.
These are the--
I'm not gonna read them all, but you can--
you can see them whether it's a portrait, or a landscape,
or just density of the screen; we'll get to density.
Guy: Yes, basically what you see here
like the values on the right, yeah, like you said,
it's for the G1, but that should give you a fair--
a fairly good idea of, you know,
the kind of things that might change
from one device to the other.
So you know, obviously maybe you don't care,
but when another keyboard is visible
when you want to display button on screen,
but then maybe the resolution will matter.
Onorato: Might need to make a smaller button.
Guy: Yeah. Onorato: Or a bigger button.
Guy: It depends on your app.
And we're gonna focus primarily on density and screen resolution
'cause those are the ones we think that, you know,
will be the most difficult for you guys to handle.
Onorato: Yeah, so for the stuff like locale,
just read the docs; it's in there.
And I have a URL for that later.
All right, so this is the app that we're gonna work with.
Looks kind of like the launcher.
It's based on the code that Romain had--
Guy: It's a very useful app.
Onorato: Well, it has little bug droids on there.
All right, so this is running in HVGA.
That's 320x480.
Guy: So that's G1. Onorato: This is what G1 is.
Guy: On the phone that you got yesterday.
Onorato: And so what if it ran on a QVG device?
That's...
Oh, AbsoluteLayout. Okay.
You could've written that with AbsoluteLayout.
I don't even know why we have AbsoluteLayout--
Guy: But if you do that, I'm gonna hurt you, so don't.
[laughter]
Onorato: The worst part about it, though,
is that it's got this--if you see down there at the bottom,
you have to list the position of every single view.
So that's tedious, right?
For this view here, that's-- what is that--
12 different coordinates you have to come up
just for that one thing.
This file's sort of annoying to write.
All right, so this is what it would look like with QVGA
if you used AbsoluteLayout.
You can see that we're missing one of them.
So that's pretty much what we're gonna talk about
is how to write-- how to write this view,
and have all of your bug droids show up.
Guy: Yeah, how to do it correctly.
Onorato: How to do it correctly.
Another thing.
For Donut, one of the--
Well, for Donut we're gonna be
supporting three different screen resolutions.
Guy: Well, we haven't decided yet.
Let's say densities.
Onorato: Densities. Okay.
But for all these, the screen is gonna be
about the same physical size,
so we're not gonna--
If we have a WVGA device that's 480x800,
it's, you know, gonna be about this big still.
It might be--it'll be prettier because it's got more pixels,
but it's--it's not gonna be this big.
So if you use that AbsoluteLayout,
what you'd see is tiny, little ones
that might be too small to hit with your finger.
And they didn't wrap correctly, either.
Guy: Yeah, and if you use a QVGA device,
then you have the opposite problem
where everything is too big.
So if you think the buttons are too big already on the G1,
wait till you try that on a QVGA device--
Onorato: You get two emails per screen, yeah.
Guy: So screen density is-- is a complex issue.
And actually in the framework, we built a bunch of things
to help you deal with that.
You probably already encountered something called DIPs
that we're gonna talk about.
The DIPs are--are our special way to define pixels
that will scale correctly across all the densities,
and everything will look the same size--
the same physical size, across the devices.
But let's get going.
Onorato: All right. Absolutely, that sucks, okay.
Guy: Yeah, and actually you can't really use AbsoluteLayout
anymore 'cause I duplicated it in Cupcake,
and people already hate me for that.
The layout will not disappear, so you can keep using it,
but it's there to show you that it's probably not what you want.
Onorato: All right.
So Romain talked a bit in his last talk
about writing a custom-- custom view.
He called it custom view layout.
Or what did you call it?
Guy: Custom view, and then custom ViewGroup.
So here it's a custom ViewGroup.
Onorato: This is actually gonna be a custom ViewGroup.
Guy: Or custom layout.
Onorato: They're all subclasses of views,
so what are you gonna do?
All right, these are the links.
These are all on developer.android.com,
which is the homepage for all the developer--
all the SDK docs and stuff.
And you have a pretty much complete copy of that
in the SDK itself, too, so if you're on a plane or something
it still works.
Guy: And also if you look, we did--
sometimes we do write good documentation.
So if you look at the Java doc for the view class,
there's actually a huge block of text at the beginning,
and it explains many things about layouts and custom views.
But apparently people ignore that kind of stuff.
So that's why we have our docs. Onorato: But it is there, yeah.
All right, so...
All right, so we're gonna go and write our custom view.
Four things we're gonna worry about.
The first thing,
we got to figure out how big our view wants to be.
How big it's gonna tell its parent that it wants to be.
Although its parent is free to ignore that.
Guy: Yeah, often they do. Onorato: Often they do.
The lay--Then we're gonna do the layout,
and the first iteration here we're actually gonna
ignore what our children said and just do what we want,
which is valid.
Then there's units, and the last thing,
we want to make sure that you guys can use your view--
this custom view from an XML file.
It's not hard,
it's actually less lines of code than I thought it was.
Guy: It's not hard, you just--It's--
Onorato: It's just ten lines of code
that you have to know what they are.
Guy: They're confusing.
Even, you know, ourselves, like, we can't remember what they are,
and we always have to look at samples that we wrote.
Onorato: But luckily now we can watch the YouTube video of it.
Guy: Yeah, or other sites.
Onorato: Okay, let's talk about units.
Actually, no, let's not talk about units.
Let's write the code.
Guy: Yeah, units are boring. Onorato: Okay.
All right.
So first thing we're gonna do-- I've got my little script here--
is go into our SDK folder, which is--
it's one on my desktop right here.
And create the project...
Guy: So we're gonna take you through the different steps
of creating a new layout, and we'll start with the--you know,
The basic, dumb layout, and we'll iterate on that
until we get something that actually works
and that can be reused, and that works across devices.
And at the end we're gonna submit that
to the Android open source project,
and we're gonna commit it in the actual source code base.
So you can see how it's done,
and also see what kind of stuff you have
to do--if you ever want to, you know,
commit code that ends up in your project,
like, shat kind of things you have to support.
Onorato: So, uh...
It didn't work. Perfect.
Guy: [clicks tongue]
Onorato: "-Target."
Is it working? audience member: Can't see.
Onorato: You can't see that?
Is it too low or is it too small?
Guy: Too low.
Onorato: How's that?
There we go. Okay. We created our file.
So we're gonna go in there. That's back here.
I'll move that up for you.
All right, do you want to talk while I'm typing?
Guy: Yeah. Onorato: Okay.
Guy: So yeah, so Joe--
I don't know if you've used the Generate 1.5 SDK
You probably used the Eclipse plug-in 'cause, you know,
it's easy, it's shiny, you click buttons and stuff.
But Joe just created the project from the comment line,
so we have this historical Android,
a simple executable that will generate the project for you.
Or the directories, the ant build file,
uh, the Manifest, and even, I think, a couple of Java files.
So now that we have the project created,
we can, you know, go ahead and just add back ant install
or entry install and it's gonna compile the project,
ship it to whatever device you have connected to the computer.
And when I say device, it can also be the emulator.
Like anything we do, it's the same
for the actual physical phones and the emulator.
Onorato: So I just started the emulator, here.
Guy: So we're starting the emulator.
And hopefully it's not gonna take too long.
'Cause we have nice machines at Google.
Next time start it before the talk.
Onorato: I know, I meant to. Sorry.
And once--See, the command here is "ant reinstall."
I don't know why it's reinstall.
Guy: It is reinstall because--
Okay, there's a difference between installing the APK
and reinstalling it.
If you try to install an APK that's already on the phone
it's gonna fail, so you have to use a flag called -r
if you use the ADB install command and reinstall.
Make sure that even if the application
is already on the phone, the install will work.
Usually when you're in Eclipse and you click the "Run" button,
that's actually what Eclipse does.
It trans--it's reinstall command behind the scene.
Onorato: All right, well, I was gonna run
the project we just created, but it's taking its time to boot up.
So it's the hello world one that you guys have probably all seen.
Guy: Yeah, let's go to next step.
Onorato: Yeah, let's just go to the next step.
I'm gonna get--I'm gonna open up my file, here.
This is the image. Our little, like, bug droid guy.
And I'm gonna copy that as "drawable."
Copy that into my "drawable."
Into there.
And...
Same thing with the...
XML layout file.
So you guys have seen this, probably from the GUI, there.
Let's open that up now.
And for this-- just to get started,
I always like to start with just a, you know,
some simple layout that's already there.
So we picked linear layout.
It's got these...
Should be up by now. Good.
We can run it, so let's ant reinstall.
It's compiling it.
And it wor--failed.
Guy: Device not found. Interesting.
Try again. Onorato: Perfect.
Oh, you know what, let's do...
Guy: Yeah, that always works. Onorato: This always happens.
I don't know why ADB doesn't work, but it doesn't, so.
Let's--let's kill the server.
Guy: But you--At least you get to learn, you know, how--
Onorato: This is my life.
Guy: Yeah, this is actually how we do things on the Android team
'cause we don't have the luxury of using the Eclipse plug-in.
Pardon me? audience member: [indistinct]
Onorato: That's what I'm doing. Oh, there we go. Kill all ADB.
Guy: Or ADB kill-server. Your best friend.
So, yeah, I was saying, we don't have the--
you know, the fancy Eclipse plug-in,
and I'm always jealous of you guys whenever, you know,
I see a demo of the Eclipse plug-in
'cause it's so easy.
And we have to do all that crap in the command line.
But it works. Onorato: Okay.
Should be here now.
Guy: Yay.
Onorato: Let's put it on our desktop.
There it is. They're in a row. All right.
That took a while, but...
Okay, so the next-- the next thing--
Guy: You wanted a grid, not a column.
Onorato: Yeah, we want a grid.
So let me--
Let me copy that other file over.
Guy: Now you get to see the code.
Onorato: Yeah, now you get to see the code.
That was--Maybe we should've done that beforehand. Sorry.
We wanted to show you the-- sort of the whole step.
I mean, it's a little boring, but...
All right, here's the view.
Guy: Feel our pain, damn it.
Onorato: You want to talk about these?
Guy: Yeah, so that's-- for those of you
who were at the previous session,
this is, like, how you create a layout.
So you extend the ViewGroup class.
So for some weird reason-- it's probably his fault--
we have this classical ViewGroup,
and the children--I mean, the subclasses of ViewGroup
are called BlahLayout.
That makes no sense at all, but that's how it is.
So you extend the ViewGroup class
when you want to create your layout class--
Onorato: That is actually my fault. Yeah, sorry.
Guy: Yeah, I'm pretty sure it is.
[laughter]
And you need at least two constructors
if you want to use the layout from XML.
Which is what we want 'cause we all love XML.
It's so easy and nice.
The first constructor is the one you have to use
when you want to instantiate a view or ViewGroup from code.
But we don't care about that. Like, you know.
Writing code for UI is bad; we want everything in XML
because from XML we can support different configurations.
And, you know, if you don't know what I'm talking about,
refer to the documentation to the links that Joe talked about.
So the constructor that you're interested in is the second one.
So you have a second parameter called the attribute set,
and it's basically just a big bag of values
that come from XML.
So later we're gonna use those attributes
to extract the information we want from the XML file.
Right now we're gonna, you know,
keep using the attributes from the parent class,
so we have nothing to do, which is always nice.
And that fixed grid layout is pretty dumb.
You have to specify the width and the height of each cell,
and we're gonna force the children
to have those dimensions.
So here you have two methods.
Set cell width, and set cell height.
You specify the dimension pixel, and you can see that we have
this request layout call.
So whenever you change the size of the cells,
we want the screen to be refreshed
so we call a request layout that will trigger
a re-layout of the whole screen,
and of that layout in particular.
And if we scroll down, we have--
We have the fun part.
So onMeasure--
We said before that before you want to do a layout,
you have to measure the children
because you want to know how big they want to be.
It can actually matter.
In that case, you can see the middle with the loop
that go through all the children
and call measure on the children.
So we--we-- So we said that before.
Then at the end we have the usual
setMeasuredDimension that tells our parents
how big we want to be.
And you can see a lot of, you know,
weird stuff going on with the MeasureSpec
and the makeMeasureSpec.
And do we want to go into details
about that right now or later?
So basically-- So let's talk about--
a little bit about the MeasureSpec.
So when you-- In the onMeasure method,
what you get are actually not just dimensions.
Like the two parameters widthMeasureSpec
and heightMeasureSpec,
are not how many pixels your parents want you to be.
They're hints from your parent
telling you what size you should be.
So--And we have to do the same with our children.
So let's ignore the parameter for now,
and look at the-- look at the first two lines.
So we create two new specs.
So we use the makeMeasureSpec method
and we use the cellWidth and the cellHeight
that are defined by the two setter methods
that we just saw before.
And we pass--
as a second parameter to the makeMeasureSpec method,
the operation we want to use with these dimensions,
there are three in Android.
That's three, that's two.
Three operations in Android.
We have EXACTLY, AT_MOST, and UNDEFINED.
So here, we know the size of the cells,
so we want to tell our children
that they can be AT_MOST 80 pixels, for instance,
or AT_MOST, you know, whatever the user specified.
If we wanted to force the children
to be exactly 80 pixels,
we would say, "MeasureSpec.EXACTLY."
And if we don't know what we want--
it actually happens sometimes;
the Internet loves to do that kind of stuff--
we would just say, "UNSPECIFIED,"
and then the size you pass doesn't matter.
So here we just create our MeasureSpec,
and we say, please be AT_MOST, you know,
whatever cell width and cell height have.
We call measure on the children,
the children will look at those units,
and will set their dimension according to that.
And the way you do it is at the end of your onMeasure
you can see that we call resolveSize.
So resolveSize is a method of the view class.
You pass it the size that you want to be,
so here the grid layout is saying, okay,
I have a number of-- I have N children,
so that's the count variable--
I know that each child can be AT_MOST cell width in pixels,
and so that's the size I want to have.
Onorato: What we're telling here, though,
is we're telling the children that--that--
that--what would they like--
We're asking them, "What would you guys like
"if these were your constraints?
"So if you could be AT_MOST 80 pixels, in this case,
what would you do?"
And we're giving them a chance to sort of
traverse down the hierarchy and figure out
what they would do in that case, and then we'll bubble up.
Guy: Yeah, but so at the end we do what our children do with us.
Like we tell our parents, you know, what size we want to be.
And the resolveSize--
Yeah, I know, it's...
Onorato: So the one-- So this is a simple one
'cause we're sort of ignoring all the details,
but for a linear layout it's actually--
takes into consideration what its children say,
and comes up with a size for itself.
And that's-- that's how you--
That's how the wrap content mode is implemented.
Guy: And so this resolveSize thing--
we pass the widths that we think we want to be,
and we also pass the MeasureSpec that our parent gave us.
And that MeasureSpec can be AT_MOST, EXACTLY, whatever.
And the resolveSize, we simply look at those two values
and figure out what we should be.
So you can ignore the magic that's inside,
basically always use resolveSize,
pass the MeasureSpec that your parent gave you,
and just indicate what you think you would like to be.
It will, you know, figure out some magic value that works.
Onorato: So if they said EXACTLY,
you'll get exactly what-- it'll, you know, use that one.
Guy: So when you're done with the measurements--
and I know the measurements is very confusing.
Believe me, the first two months I was on Android,
my first task was to write layouts,
and I was like, oh, God, I don't understand.
So when you're done with the measurement
we can do the layout,
so we need to position all the children on screen,
and we need to give them a size
so it can be the size they wanted,
or, you know, the size that we think is best for them.
So here it's a very simple algorithm.
We go through all the children,
and we simply--
we simply wrap the children.
So we put as many children as we can on the row,
and at the end of the row we go to the next row.
Actually, here we only put three children per row.
So you just call layout on the child,
you give it--you give the child the X and Y coordinates,
and you also give it the size you want the child to be.
And here, as you can see,
we're passing the cell width
that we got from the setter methods at the beginning,
so we are totally ignoring the size
that the children figured out when we called Measure.
So that layout will always lay out the children
with a very--with an exact size no matter what the child wanted.
Onorato: But keep in mind that that might be okay.
If all of these views are your views,
and you know exactly how big the bitmaps are,
like, just go ahead and use it.
Guy: Yes, like I was saying before, like,
if you can, if you want, take shortcuts.
Like, if that's all you need, then stop right there,
and, you know, your code will be more efficient
because it does less work.
So that's actually okay to, you know,
fix the size of the children and ignore what they told you.
But that's not correct, like, we can't cheat that on Android,
so we need to improve it.
And also that's not gonna work really well
across different resolutions and different screens.
So let's see the-- the next step.
Onorato: The other thing we're missing here,
I actually realized during your talk,
is we're not skipping the gone ones.
But we don't have any gone ones, so that's not a problem.
Guy: We need bugs, you know.
Onorato: All right.
So the other thing to get this actually running
is we have to go into our-- our view here, and use--
and use the right one, com.example.android...
Guy: So if you ever wondered how to use
your custom views in the XML, that's how you do it.
Onorato: You type in class name.
Guy: Yeah, but the fully qualified class name--
how do they say in Java--
so you need to prefix with the packaging.
And to make things easier, when you use the standard views,
you don't have to say android.widget.ListView.
But you can if you want.
Onorato: All we do is search in those packages for those things.
Makes it a little bit slower, actually,
but this thing isn't linear layout anymore,
so we'll take that out.
Guy: So let's run them.
Onorato: Get a-- down here too.
This is one thing I always forget to do, actually, in XML.
Guy: Ah, the beauty of XML. Onorato: Every single time.
And one more thing.
In here-- Shouldn't be too bad.
Is we actually have to set the size of the cell.
It starts out at zero.
So we'll d-- Guy: Oh, yeah.
You want to say the-- Onorato: Yeah.
Guy: So here, because we can't specify
the width and the height of the cells in XML yet,
we're doing that from code.
So the usual, you know, boilerplate of finding
the ViewById, casting it,
and then calling some methods on it.
Onorato: Okay.
So we'll run it again.
Guy: [imitates small explosion]
You did not specify the ID in the XML file.
Onorato: That is what I did.
So the syntax--
Don't forget android.id, too, that's another--
that's another common one.
If you just say ID, it doesn't--
Guy: Well, at least it's not too painful
when you do that in a project
'cause when you forget the ID
and you're working on the source code of Android itself,
you have to reveal the whole tree.
You know, take some time, and your ID goes all crazy
because it has to refresh all the, like,
tens of thousands of files we have.
So usually we're better at not forgetting that.
Onorato: Yeah.
Guy: Okay. Onorato: So here we have it.
Guy: But, uh, so they are exactly 80 pixels across--
Onorato: And ImageView stretches by default.
Guy: Yeah, so I don't know
if it's really visible on the projector,
but you can see that the droids are much bigger
than the original picture.
And they don't look really good, like, you know, their scale.
And we don't want that, so we need to honor
the measured dimensions from the children.
Onorato: So we'll do that. This is a little bit of typing.
Guy: As long as you do the typing, it's okay.
Onorato: That's fine.
All right, so this is all, uh, all in a layout here.
Guy: Yeah, so we don't have to chance the measure--
the measure method because we've already done it.
And usually, actually, you will probably end up
not doing much work in the measure method
because all you want to do, really,
is you measure the children.
The hardest parts usually is to figure out
whether you want the children to be AT_MOST, EXACTLY,
UNSPECIFIED, and all that stuff.
And if you're really courageous,
you can go look at the source code
of LinearLayout.
And if you're still reading it after a couple minutes,
then please apply for a job and we'd love to give you
LinearLayout to maintain.
And so here, Droid's modifying the layout method
so we're actually asking the children
"What are the measured widths and measured height?"
And that's something that you have to keep in mind.
When you want to know the width and the height
of a view for some reason, you're on your Y,
you have two different methods.
You have get Width and get measuredWidth.
And they can actually be different.
In many situations, the value will be the same,
but they may be different.
So the one you want most of the time is get Width
because it gives you the actual size on screen.
Uh, get measuredWidths is really useful
only for layouts
because they're the ones who care
about what the child thinks--
thinks it wants to be.
Yeah, so here, Joe is just updating
the call to layouts.
Onorato: I'm going to have it centered
in the view too.
Guy: Yeah, and you're actually centering it.
So because we said to the children, "okay,
we want you to be at most 80 pixels large,"
they might be smaller.
So we have a couple lines of code here
"int left" and "int top" =
that center the child within the cell
if they're smaller than the 80 pixels
that we specified.
Onorato: And we'll make it wrap correctly
instead of hardcoding it to four columns too.
Guy: Which is always nice. So let's give that a try.
Onorato: See if that actually runs.
Guy: Just "ant reinstall." Onorato: Yeah.
Guy: The padding. Onorato: Again?
Guy: Okay, no, you don't take care of the padding
of your children in the layout
'cause the padding is inside the child.
So it's part of the measured width.
Onorato: Well, if the layout itself had padding,
then we would.
Guy: Yeah, if the layout has padding,
you have to, you know, deal with it.
What you will deal with is the margin of the children.
'Cause the margins are outside of the bounds
of the children.
If you want a good example of that,
you can look at FrameLayout, LinearLayout,
RelativeLayout, I think,
to deal with that kind of stuff.
And usually, you know, if you don't care
about padding and margin,
just forget about them.
Like, take the shortcut.
'Cause as soon as you start taking the padding
in the margin into account--
Onorato: It doubles the amount of code.
Guy: Yeah, the code becomes much more complicated.
So be glad that we do that kind of stuff for you.
Onorato: So here they are, the right size and centered
in their cells.
Guy: Okay, and, yeah, and if we're to inspect
that layout in Hierarchy Viewer,
we would see that the items are smaller
than the cells in which they're contained.
Onorato: You want to do that?
Guy: Yeah, no. Let's go ahead.
Onorato: Okay.
All right, so next, we're going to actually make the density.
So if we ran this in wide VGA,
what we would see--
I won't do it 'cause it takes so long to start the emulator--
but what we would see is all six of these androids
right in a line.
Guy: Yeah.
Yeah, that's exactly--
the screenshots that you saw at the beginning,
we specified--
in this example, we specified the size of the cells in pixels.
So there are actually 80 pixels.
And if you want to devise it as a higher density,
then 80 pixels are much smaller, physically,
for you as the user.
And that's what we want to fix.
It's not an issue right now,
but it's going to become an issue
for all of your applications really quickly.
We have stuff in the framework that take care of compatibility
for older applications.
But it's best if you guys do it.
Onorato: So we were going-- what we're going to do
is sort of show you how to hardcode it.
But I think I want to just skip that.
Guy: You want to skip that? Okay.
Onorato: 'Cause it takes a little while.
So while this is going,
I'll--when I restart the emulator here...
Guy: Yeah, I'm pretty sure you will have the slides
available somewhere.
So we can-- it's not much code.
It's like two lines of code.
Onorato: It's like 54 lines of code.
Guy: Yeah, and if you want to know more
about supporting the density
and how to compute the right size,
then, you know, just send us emails.
We'll point you to the examples.
And we'll probably talk about it really soon
on the blog, on the forums.
Onorato: So I think what well do
is show you how to use resources then instead.
Guy: Yeah. The attributes one?
Onorato: Yeah. Attributes.
Guy: So let's see-- so again, so let's see
how to get the attributes from XML.
Onorato: So I said it was about ten lines of code.
It's really about that.
So there's res--this is-- you have to add a file
called res/values/attrs.xml.
Guy: It doesn't have to be called attrs.xml.
Onorato: Really? Guy: Yeah. It's a convention.
Onorato: Okay.
Guy: And I would love that we change this convention
'cause I can never pronounce attrs.xml.
[laughter]
I--you should try with a French accent.
You'll see.
[laughter]
Onorato: You don't want me to do my French accent.
Guy: No, you don't.
Onorato: So there's-- go ahead.
Guy: Yeah. Well, yeah, so Joe here is just creating
a new resources-- new series of resources.
And something that called the "styleable"
is basically a set of resources that can be modified
by the theme.
So if you ever created a theme and you changed, you know,
like, the fullscreen mode, the window background,
the appearance of your text views or stuff like that,
that's because they are declared as styleables in the framework.
And then in the styleable, you define all the attributes
that you want to be able to use in XML.
So just a name and a format.
So here dimension.
So dimension is a fancy name for all the units we support.
So that means that when you have a dimension attribute,
you can say 1 px for pixel,
1 dip for device independent pixel.
You can say 1 mm for millimeter.
You can use 1 inch.
We have a bunch of different units.
And we support a bunch of formats
so you can-- you can have floats,
you can have enums, you can have flags,
integers, booleans, strings, references.
Onorato: Enums are kind of fun, actually.
They--they let you--they-- I mean, they do exactly
what enums would do.
They give you a name in the XML file,
but it becomes an integer.
Actually becom--yeah, becomes any integer
in the-- in the data structure.
Guy: So if you ever use a LinearLayout, for instance,
and you specify the orientation,
you say orientation equals vertical or horizontal,
that's an enum defined as a--as an attribute.
So here we just defined the two attributes
we need to specify the width and the height of our cells.
And let's see how to use that from code.
Onorato: Well, we'll add to the XML too.
Guy: Yeah, the XML.
Onorato: So this is the-- this is the magic part that I...
Guy: Always forget about. Onorato: Always forget.
So, yeah, you-- you probably always--
you've already seen that namespace in the root tag
of all your XML layout files.
And you probably wonder,
like, "why do I have to have that thing
that, you know, it's pretty weird to look at?"
Onorato: So there's actually two parts to it.
This is just the prefix that tells us
that this is going to be an Android...
Guy: Attribute. Onorato: Attribute, yeah.
And then there's your packaging.
You know, Android is the one that it's sort of regularly in.
Guy: Yeah. And you want to change the namespace now.
Onorato: Oh, yeah.
Guy: And so when you create your own attributes from XML,
because the name of your attributes
could potentially, you know,
get in conflict with the attributes
that we define,
you have to specify a namespace for your application
just like you have to use the Android namespace.
So, you know, does--
this will prefix then the package name of your app.
Then when you specify the attribute,
instead of using android:whatever,
you use the name of the namespace
you just used.
Here Joe just declared the widths of the cells
to be 80 device independent pixels,
which will scale automatically with the density of the device.
That's some framework magic.
And the height is just 100.
Onorato: Yeah.
Guy: So it's, you know, like we said,
it's actually pretty easy
to create your own XML attributes.
The only thing is that you have to remember the syntax.
And I don't know how you can--
you might not even remember the syntax.
So it's nice to have-- to have examples.
In the--actually, in the SDK, you have a bunch
of examples that--that use that.
So if you look at the home sample,
for instance, we use heav--
we use custom attributes heavily.
Have you recompiled the...
Onorato: I haven't yet 'cause we needed
to put the Java code in too.
Guy: Okay.
Onorato: So I'm just going to copy and paste it.
Guy: So now this--yeah, there's some more width syntax.
Onorato: So what--what this--
so we're going to add this big hunk of code
to the constructor here.
And what that-- what all that XML did,
there's your-- your R.java file.
and that generated-- inside--inside that,
it generated a static, final array
of resource IDs
that can then be used
to this obtainStyledAttributes function.
And it--and it'll get you this thing
called the typed array, which is--is magic.
Guy: So the typed array is--is basically
all the attributes and all the values
that you've defined in XML.
Onorato: But just for this one-- just for this one thing.
Guy: Styleable.
Onorato: One-- for this one styleable, yeah.
And then we're going to get the width and the height
out of that.
The thing we skipped was the pixel size thing.
Just briefly, there's--there's-- this typed array thing
has four or so different versions
of getPixel-- or getDimension.
And the one we have here is used for widths.
So if you--if the rounding would cause your pixel
to go down to zero,
it actually...
Guys: Forces it to be--
Onorato: It actually forces it to be 1
just so your widget doesn't completely disappear.
It's--it's not so much useful for the width
of your actual widget.
But if you're going to do something
like draw a border,
you probably still want to have a border
even if it's a really small border.
Guy: Yeah, it's the second-worst API name ever.
Onorato: Yes. What's the...
Guy: [indistinct]
That's the first one.
Again, so you saw that it's pretty easy
to get the attributes from XML.
But we have to admit that, you know, the API
like TypedArray, getStyleable,
it's under intuitive,
so we wanted to--to show you guys,
you know, the code that you have to type
so that at least you can remember
what it looks like and, you know,
so that you can know what to look at
in the source code of the examples
when you want to do the same in your applications.
Onorato: So here, while we were doing all that,
we restarted the emulator here.
And it's running at wide VGA, which is 400--480x800.
And you can see that Android isn't really ready
for it yet either.
The home screen sort of ends.
[laughter]
And the icons are small.
So here's our little button.
Guy: My androids are small too. Onorato: What?
Guy: The androids are small too.
Onorato: Yeah, the androids are small too.
Well, shouldn't be, actually.
Guy: You have to modify the DPI property of the system.
Onorato: I should've done that. Guy: Yeah.
Onorato: Crap. Guy: That's okay.
Onorato: All right.
Well, if this were to run on a real device,
which don't exist,
which is why this talk is all theoretical anyway,
then this would've looked right.
There--there--there's a way to get the emulator
to build it.
I'm not going to show it
'cause it took me about 20 minutes to figure out.
Guy: Yeah, so one of these days,
I'll write an article on the blog
to show you how you can start the emulator
with the appropriate density.
And you will see that things actually work correctly
so that you can at least test your application.
It's not that hard.
It's another one of those things
where you have to find a weird configuration file
somewhere in the system
and change one of the properties.
So we'll document that.
Onorato: And if--if somebody were actually making a device
that was-- that was that size,
they would've done that, obviously.
Guy: Yes.
If they want to ship with device.
So we're done with the code?
Onorato: So I think we're done with the code, yeah.
Guy: So let's go to the fun part.
Onorato: The fun part. Yeah.
Guy: Okay.
So now we'll show you how--
you know, like, this code is awesome, obviously,
'cause we just wrote it.
Joe's a great developer.
And we're going to now com-- you know, submit that for review
to the Android Open Source Project.
So Joe will show you the commands
that you have to do on your side
to submit a patch.
And then I will show you how on our side,
we look at your patches and how we approve them.
And the cool thing about it is everything that we do right--
that we're going to do right now,
it's exactly what we do every day, several times a day,
and so on.
So we use the exact same tools.
We have to do the exact same process.
The only difference between you and us
is that you don't have the right to submit something
in the tree whenever you want and we do.
You'll see. It will be obvious.
We have a cool button that you will never see
on your screen.
[laughter]
We--we got to have some perks, you know,
working on the Android team.
Onorato: So we're going to check this
into the samples directory on the tree.
And we're actually really going to check it in right now.
So if we break everything, then--the build is broken?
Guy: I think the build is broken already.
So that's okay.
Onorato: I think the public one isn't.
All right. So we copied it in there.
And now we're going to go
into development/samples/
This whole thing is completely command line
in our--in our world.
And we're going to show you Git.
Guy: I won't say anything bad about Git today.
Onorato: Git is great.
Actually, you know what,
Linus had a nice talk at Google
where he slammed everybody but Git.
Linus is a jerk and Git is annoying.
Guy: Can I say that Git is a pain in the butt?
Onorato: Yeah. Cool.
Guy: So Git is a pain in the butt.
No, it is actually awesome.
Let's just say that to me, you know,
as a user that likes Macs,
they forgot about the user part.
So it's not very user friendly.
And we're currently transitioning internally.
We used to be on Perforce and we're switching to Git
so that we use the same tools that we--you do.
You know, so that we can also feel your pain.
And believe me, we do feel it.
Onorato: So I just deleted--
what I had just done is deleted all the files
that we don't actually want to check in.
Our sample structure doesn't have all the build...
Guy: Yeah.
Onorato: 'Cause they have hardcoded paths and everything.
Guy: Okay, let's send the repo.
Onorato: Yeah.
Guy: So we have this tool called repo.
It's basically a smart wrapper on Git
because the Android project is, you know--
we like to make things complicated.
So instead of having one repository,
we have currently 152 or something like that.
Oh, and obviously, you know,
handling 152 Git repositories by hand would be,
you know, quite tedious to really impolite.
So we created this tool called repo
that takes care of mo--most of the work for you.
So when you type "repo sync,"
it goes through the hundreds of repositories
and syncs them.
So when you do "repo start"-- let's back up a little.
Onorato: It's way back up here, yeah.
Guy: Yeah. Stop typing stuff.
So when you do "repo start,"
you're just starting a new branch
shown here called "simple."
So when you want to commit something
to the Android Project,
you have to start a new branch.
Don't ask me why.
Onorato: This is all documented somewhere.
Guy: Yeah, it's because of the way Git works.
Anyway, so you have to start a branch.
And if you don't, I can't help you.
Onorato: It's about ten commands
and you have to rebase something somewhere.
Guy: Yeah, so just create the branch.
Never forget about creating a branch.
Onorato: So we're going to add the file to Git.
Guy: Yeah, so now that we--
Onorato: Or add the directory rather.
Guy: Yeah, so--
Onorato: Status shows us all our new files.
Guy: Let's back up right now.
Onorato: Oh, we didn't do that. Guy: No.
Oh, did you show the [indistinct]?
Onorato: Yeah, we did the commit.
It's just that's all we had to do.
Guy: Okay. Anyway, for those of you who know Git,
we just staged the files in UNIX.
For those who don't, just, I don't know,
go read a book about Git and you'll understand.
Anyway, so the--the point is, you know,
in Git, there are several stages
where your files live.
So you modify the files.
Then you have to go Git add to put them in something
called the index.
So you repo start, my branch,
then you--you--you modify your files.
You do Git add.
You put them in the index.
And when you're ready, you do a Git commit.
The fun part about a Git commit
is that it commits the files on your machine.
So they're still on your machine
for some reason.
So here, Joe is just adding the commitments edge
and you can see the comments at the bottom,
everything that we did.
So we just added a bunch of files.
That's all the files in the project.
And when we're ready, we can finish the commit.
So now files are committed locally.
And we need to upload them to the Android Project.
So for that, we use the comment called "repo upload."
And repo upload is really smart and figures out
that you have commits in your current branch.
It backs-- Onorato: It asks you--
it says, "would you like to upload those?"
Guy: Yes. So yes.
And then--go ahead.
Onorato: That's interesting.
Guy: The remote--oh, do you have the SSH key?
Onorato: Yeah. Guy: Huh. Try again.
[laughter]
Isn't working out.
Onorato: Huh. All right. Well, uh...
Guy: Uh, you don't have the key.
Onorato: I don't have the key.
Guy: Well, let's do that right now.
Go to the web site and put the key in.
Onorato: Oh, yeah. Guy: Uh...
Onorato: Here's my public key if you guys all want to see it.
[laughter]
Guy: I'm pretty sure the country--
Oh, but they will have the video.
They'll show your key.
Onorato: I'm not going to open my private key.
Just the public one.
I hope that's the public one. Yeah.
[laughter]
Guy: You'll have to do that yourself.
So when you want to--to--to deal with repo--
and I'm sure there's a good reason,
but I was never told the reason--
you need to put your public SSH key
in your account.
So let's do that.
Onorato: So, yeah, if you go to the settings tab...
Guy: So let's review the-- Onorato: SSH Keys...
Guy: Yeah, review.source.android.com.
Onorato: Oh, yeah, or r.android.com also works too.
Paste that in there.
Click "Add."
Let's go back and try that again.
Guy: So all the-- all of that, like, you know,
the SSH Keys that explain--
if you go to source.android.com
they explain everything.
Onorato: Here we go. Okay, it worked.
Guy: Okay, it worked. So now the code review is online.
Onorato: The code review's online, yeah.
If anybody here wants to go comment on it,
you can heckle me.
Guy: Actually you can.
Onorato: Yeah, you really can. This is live.
Guy: We welcome comments.
You know, you can look at changes from other people
and say that you disagree with them, that you agree with them,
so, you know, if we see, like,
that 20 people from the outside said yes to a change,
then, you know, maybe it's a good idea to accept the change.
So now it's my turn. Joe's work is done.
Onorato: Actually I need to add you as a reviewer.
Guy: Oh, yeah.
Well-- Onorato: So this--
Guy: You don't re-- Okay, you can.
Onorato: Yeah, let me do it.
Because this is what you guys would do.
You'd see it's up-- I've uploaded it.
The owner is me.
And I'm gonna put in...
in here.
Guy: And if you don't know who should be reviewing your change,
just don't put anything.
We do receive emails, we know that, you know,
new changes are coming in.
And from time to time we go look at them,
and we add ourselves as reviewers.
Anyway, so--
Onorato: I'll add him; it'll send him an email.
Guy: So add reviewer. There you go.
Onorato: All right. Go ahead.
Guy: I'm now a reviewer.
So I switch to my machine.
I'm already logged in on Gerrit,
so the exact same tool.
And if I go to my changes...
Reviewable...
Yeah, it doesn't like Safari sometimes.
Anyway.
Onorato: Oh, this whole thing, by the way,
is written using Google Web Toolkit, too.
It's by a guy, Sean Pierce.
He's on our team, he's fantastic.
Guy: Okay, so where is your change?
Here we go.
So that's Joe's change, and you can see
that all those other changes are changes
that people made to the framework
that I could review,
but it's not necessarily my job to review those changes.
But they still appear for me as changes I can review.
So let's go look at Joe's change.
And it looks exactly the same as Joe's UIs.
And actually, someone commented on the--on the change
and gave it a +1.
Onorato: Good for you.
Guy: Thanks, whoever you are, Frank.
[laughter]
But actually, you know, that's nice 'cause--
Oh, you can see that--
So his comment says,
"Looks good to me, but someone else must approve."
It's a canned comment.
So that means he thinks that, you know,
it's a good thing to have in the platform,
but he doesn't have the approval rights.
So somebody like, you know, Joe or I,
have to go ahead and give the approval.
So here now I can look at all the files I have--
Looks good.
But actually I see here that Joe forgot to put the makefile.
'Cause we use Make in the open source project,
so it probably tells me
that Joe never built that code in our platform.
[laughter]
I promise we never do that.
So let's--
I will say "no score" because I do appreciate the patch,
I want the patch to end up in the--in the buil--
in the framework, so I say "no score,"
and I will leave a comment.
Here I could say that I would prefer
that you didn't submit this.
That means I disagree with you, but, you know,
you could convince me otherwise.
"Do not submit"--
You're really wrong and I don't want to hear about it anymore.
Onorato: And actually, the "do not submit" actually force--
the system won't allow it to be submitted
if somebody's reviewed it that way.
Guy: It's gone.
We have total control over your stuff.
Okay.
Joe,
geez, you didn't even compile your stuff.
Add the makefile.
So I publish my comment.
You can see here that my comment was added.
And the score is still zero. Let's go back to Joe's machine.
Onorato: So I've got my makefile all ready.
Guy: Yeah, especially because
maybe you know how to write a makefile, but I have no idea.
Onorato: If you guys use the build system that's my fault.
If home screen is his fault, the build scr--
build system is my fault.
Here's what the app make files look like.
This is sample code, here's the name of the app.
This one here says it's being built against the SDK
as opposed to the private APIs.
Guy: Which is a good thing. Onorato: Which is a good thing.
Guy: Okay, so now we've done that.
We need to modify the-- the patch that Joe applauded,
and that's where many people make the mistake.
And I do the same quite often, actually.
Because of the way Git works, if you create--
if you do a new commit to commit the changes you just did,
then repo will treat the old commit and the new one
as two different changes, which is not what we want.
Onorato: So we're gonna amend this.
We're just gonna do git--
Guy: So we add the file to the staging area,
and then we do commit with the magic flag called "--amend."
And that modifies the previous commit with those new changes.
So we still have only one commit with all of our changes.
And there's still one mistake we can make after that.
And that we often do.
Onorato: We'll see if we make it,
'cause I don't know what he's talking about.
Guy: I'm sure you do. Repo upload replace?
Onorato: Oh, yeah.
Guy: So if you do a repo upload right now,
it will create another change,
and, you know, that will *** us off
'cause now we have two changes that are pretty much the same
and we have to go ahead and--
Onorato: Figure out what the other one is.
Guy: Yeah, figure out what the other one is, you know,
take care of rejecting one of them.
So what you have to do is "repo upload --replace."
That means that you want to modify the current change
that you-- that you have on repo.
Onorato: And you say which project you want,
which is the one we're in right now.
Guy: So here you can indicate--
You can see that the number between brackets
is the number of the change
in the Gerrit tool on the web.
It's prepopulated for you.
Just make sure that it's the right number.
Onorato: It usually is. Guy: It usually is.
Onorato: It guesses, though.
It's heuristic, so it's not 100%.
Now it's re-uploaded. Guy: Now it's re-uploaded.
My turn.
And if I refresh the page...
If Gerrit refreshes.
Popular. A bunch of people like to comment on bugs.
Onorato: You can see it reset the--
Go back up to the grid, there.
You can see it reset the-- all the statuses.
Guy: Yeah, because we changed the code,
we contrast, you know, what people said the last time.
And here you can see that we have the patch set number 2,
so we can look at the patch set number 1,
and we can look at the difference between the two.
So now the patch set number two has the makefile,
so I am happy with it.
I won't go through the whole code.
So when I'm happy with it, I--
I publish comments, and I say, "Looks good to me, approved."
Onorato: We're gonna have to verify it too.
Guy: Yes, but let's approve first.
So now it's approved. It just means we can submit it.
That doesn't mean that the code will compile, or, you know,
that--that it's not gonna do bad things to the build.
So at this stage, what we're doing internally
is we want to try the patch locally.
So here we have those two comments we can use,
either repo download or the Git pull.
And all I would have to do is copy that,
paste into my shell...
Like, for instance, here I'm in my source tree,
I just paste the comment I executed.
This will bring the change that Joe made into my source tree.
I can compile it, I can run it, I can see if it works,
I can see if the build is still going.
And when it does, I go back to "Publish Comment,"
and I say "Verified."
So I verified that the change is correct.
Onorato: This is actually the big bottleneck process.
It's not looking at the code, it's actually downloading it all
and building it.
Guy: And now that the change is both approved and verified,
I have a new, fancy button here
that you will never see on your machine,
unless we start, you know,
trusting you guys to become contributors.
Proof contributors.
So this is a new submit button.
So the cool thing here is that once we are on the website,
I don't have to, you know, grab the code,
do the patches locally on my machine to patch our tree
and then, you know, do another commit.
Joe doesn't have anything to do.
I just press this button and that's it.
The code is in the tree.
We can see here,
"Change has been successfully merged,"
and Joe's code is now available on source.android.com.
There's one last thing that you want to do on your machine.
The abandon. Onorato: The abandon. Oh, yes.
So, remember, we started a branch.
and if I continue working on that branch,
Oh, God, I don't even know what happens. It just doesn't work.
What happens is that I work for about two weeks
and then realize that I'm on a branch
and try to submit and...
Guy: And you're screwed.
Onorato: Then I'm completely screwed, yeah.
So this is repo abandon--this project was called "sample,"
and it's in this project, so dot.
And it did it, I guess.
Guy: And now-- Can you go to the Git web?
To see your code? Onorato: Uh, yeah.
Guy: So now everything's fine on Joe's machine,
we accepted the change.
When you abandon your change,
the change really disappears from your machine,
so you want to do a repo sync
to bring back the change that you just sent,
and remove from your machines.
It's weird at first, but that's how it works.
So if we go to development samples...
So that's the Web interface to Git.
And you want to look at the master tree.
Onorato: This is master, I think.
Guy: Okay.
Onorato: Tree, samples.
And, uh...
Guy: Are you sure you're in master?
Onorato: No.
Well, anyway, it's probably there.
Guy: Let's prove it's there.
Onorato: Do you know how to find it?
Guy: Go back.
Scroll down.
Uh...
Are you in master?
Onorato: So this is another thing.
I worked for about three weeks, and I was submitting to master,
and I thought I was working on Donut.
So that was a little bit hard, too.
Guy: There we go, master. Onorato: Oh, look at that.
Guy: Doesn't even know how to use a tool he wrote.
Uh, well, somewhere.
Onorato: Well, there's some code somewhere.
Guy: [laughs]
This may be in Donut, this may be in...
Onorato: I wonder if I put it in Donut.
[laughter]
Onorato: Huh. Well, I have some homework to do after this.
And find out what build I broke.
Guy: Well, like we said,
we are in the transition to the new tools,
but at least, you know, that gives you an idea
of what the workflow is.
And again, this is exactly what we do internally.
The only difference is that we don't use the public website.
We have another instance of this Gerrit thing,
and we have another Git tree internally,
but the tools are the same, the workflow's the same...
Onorato: The scratching of our heads is the same, so...
Guy: The only difference is that, you know,
Joe could just upload his patch and say, "Hey, I trust myself,"
and submit it.
Onorato: Yeah, and then I break some branch. Okay.
Guy: Like I did today.
Onorato: So we're just about out of time,
but if you guys have any questions...
Guy: Why has nobody left us? Onorato: Come up to the mic.
Guy: Yeah, go to the mic for the questions.
[man speaking indistinctly]
Okay, so it's--
Yeah, it's a Git rip, so use Git-fetch.
[man speaking indistinctly]
Tesh. Stage? Stash?
[man speaking indistinctly]
Onorato: Oh, you mean you saw how I was doing that there.
Yeah, yeah.
Guy: Okay.
man: Um, when you've submitted your first patch,
and you're waiting to get approved or not,
is there a way that you can do additional work,
or are you kind of frozen in case you have to amend it later?
Onorato: That's actually-- Guy: The nice thing about Git.
Onorato: What you don't have to do in that case
is do repo abandon.
You can continue working on top of your changes.
The only thing is it gets a little bit hairy
if you have to make changes.
Guy: Well, you can switch to another branch.
Onorato: Or you could switch-- If it's totally unrelated,
you can switch to another branch.
man: Okay, I just want to make sure
you get in a state where you can amend without adding...
Onorato: Yeah, you can--
What you end up having to do-- and I've done this--
is you--
instead of doing amend,
you make changes and you rebase them into a different order
and squash them and everything and it works.
All right. Thank you.
[applause]