슬라이드 : http://io2011-zerotohero.appspot.com/index.html
0:02>>Michael Mahemoff: Thanks for coming today. My name is Mike, and this is Paul.
0:07>>Paul Kinlan: Hi. >>Michael Mahemoff: We're talking today about
0:10mobile web development, how you can go from a zero -- that's your very basic raw web app
0:15-- all the way up to a hero on the various mobile devices.
0:19You can see up there we've got feedback links and on the bottom of each slide we've got
0:23the link to Moderator, too, if you want to ask any questions.
0:26So let's see. So HTML5 is here and it's mobile. You've heard a lot about HTML5 today, of course,
0:37in the keynote and some of the other talks. I'm not going to spend ages convincing you
0:42about the benefits and the power of HTML5. What I will say, though, is that HTML5 is
0:47also here on the various mobile form factors. Many phones today, and tablets and so on,
0:53they all support quite advanced HTML5 browsers and run times, and we think that mobile is
0:59an awesome platform to be looking at if you want to reach many of these users.
1:04Firstly, mobile is a very convenient form factor for people. It's something that people
1:10can actually use when they're standing in a line, when they're sitting on the couch
1:15or in the pool, as the case may be, and they've always got it. And if you want your people
1:20to be always using your app, that's a great way is to have it somewhere where it's always
1:24at the person's fingertips. It's also a very personal sort of form factor.
1:30People tend to have one particular device that they've always got with them at all times
1:35and they've got all their personal data and all their friends and so on on it, so you
1:40get that kind of relationship with the user by being on the device.
1:44Mobiles are also a very natural sort of form factor these days. That wasn't always the
1:50case, but now the kind of device that we're carrying around with us happens to be a device
1:55that's very much touch-based, and that's the way that humans have always worked. We like
1:59to manipulate things with our hands. Much more natural than being hunched over with
2:04this superficial kind of very recent -- in the scale of things -- invention of a keyboard.
2:11So actually being able to hook into that and actually working our apps to be natural and
2:17feel, that's sort of our tactile user interface, is a big win.
2:23And probably the most important thing for you guys as developers is the fact that mobile
2:28is an insanely popular platform at this time. Many people here -- probably actually everyone
2:33here actually has at least two mobile devices, as of yesterday, because you almost all have
2:39a mobile phone and you all now have a tablet as well. And I'm guessing there are probably
2:44people here with five, six, and twelve mobile devices. So that's just individuals, but there's
2:49also people across the whole world who don't have any other computer device. The mobile
2:54is the main way they actually get connected with the rest of the world.
3:00The flip side to all these great awesome benefits of mobile is the development side of things
3:06can be a little bit of work. You've got all these different platforms.
3:12We love Android, but it's not the only mobile platform out there. There's still iOS, BlackBerry,
3:15and many more in this, you know, huge long tail of mobile platforms out there.
3:24And especially so when you consider the various form factors.
3:27This is not just about the phone that you carry with you anymore. This is about the
3:32older phones that other people might be carrying around, the traditional feature phones, and
3:37many of them do have some basic browsing capability. We want to be able to reach those users too.
3:42And of course we want to scale up to those very advanced mobile platforms that we have
3:47now like tablets. HTML5 then presents itself as a promising
3:54opportunity, because the web runs on, as you know, traditional form factors like desktops
4:00and notebooks and laptops. We've been running browsers on these things for many years. But
4:06more recently, we've had the web running on all these mobile form factors as well, from
4:11phones and the much more advanced smartphones and tablets to the more exotic sort of devices
4:16like music players and eBook readers too. Most of these kinds of devices, and certainly
4:21any phone that you would actually get these days, any tablet, you would be astonished
4:26if it didn't have a browser, and in fact, many of these browsers are based on engines
4:31like Webkit, very sophisticated, advanced capabilities, and some of the benefits of
4:37performance and so on that we're actually starting to see in the desktop as well.
4:43So HTML5 is a platform. It does all these things. It does amazing user interfaces like
4:48you've seen today in the keynote. It does -- the capabilities are actually behind of
4:52scenes as well, the sort of networking and offline capabilities that have only been available
4:58to native apps until recently. We're starting to get to a point where we're actually -- where
5:02we've got these comparable features in the HTML5 stack.
5:08So it is an awesome opportunity to actually take advantage of HTML5 to reach all of those
5:13mobile users and get all those benefits, but we have to think about these two aspects,
5:18which is firstly user experience. We want to hold our heads up high, as HTML developers.
5:24We want to actually be able to say, you know, "Yeah, this is actually a really compelling,
5:27rich, multimedia experience, just like you're used to on modern mobile devices," and at
5:33the same time, we're developers, right? We're all developers here. We really care about
5:37making our life easier too. We don't want to have to end up writing a million and one
5:42different apps for all of these different form factors. We want to think about ways
5:47to optimize between these two forces of user experience and developer experience.
5:52>>Paul Kinlan: So we've identified these kind of six fundamental challenges that you as
5:59developers today face building these rich, awesome user experiences for the mobile web.
6:05The first and probably one of the most important is, you know, different interaction styles.
6:09How do you build applications and can you build applications that work across all the
6:14different types of form factors that we mentioned earlier? Can you build an application that
6:17works across the Opera Mini style devices or tablets, smartphones, you know, the whole
6:23range? Is it possible just to build one application that is deployed across all these different
6:28types of devices? Users expect their applications to load in
6:32like pretty much instantly and respond quickly to their, you know, gestures and commands.
6:37Is it possible to build applications that -- you know, with the types of speed and interactivity
6:42that we get from the native platforms on the mobile web?
6:46And we're all used to this. We've used our web browser and we've been on a train or kind
6:51of a semipermanent Internet connection. We just can't use our web browser. But is it
6:58actually possible to build applications that work entirely when you're offline or in the
7:03cases where you've got this kind of intermittent Internet connection?
7:07Each of these devices has a marketplace or a store built directly into them, so, you
7:12know, how can we get our applications discovered when we're building mobile Web applications?
7:16Is it possible? If you look at the -- kind of the trend of
7:21browser development, you'll notice that the primary input mechanisms has always been keys
7:27and mouse, but with all these new types of devices primarily being touch-based, can we
7:32build applications that aren't just responsive to kind of the single presses but the rich
7:37gestures that we expect from native applications. So is that possible on the mobile web today?
7:42And we kind of have to forget about the different form factors. Is it possible to build applications
7:48that are responsive to the variety of different screen sizes that we see? In just one class
7:53of device -- say smartphones or even tablets -- they've all got different -- fundamentally
7:56different size screens and resolutions. Can we build applications that, you know, look
8:03great on all these types of devices? So I have a question for you all. Raise your
8:07hand if you think we can solve every single one of these challenges today on the mobile
8:11web. I've got a couple of people.
8:15>>Michael Mahemoff: A lot of confidence here. >>Paul Kinlan: So we can accept that. We can
8:18expand that a little bit easier. Can we solve any of these challenges today on the mobile
8:24web? A couple more people.
8:27Well, no, we can't. >>Michael Mahemoff: Sorry.
8:30[Laughter] >>Paul Kinlan: So we have 45 minutes for questions.
8:34Has anyone got any? [Laughter]
8:36>>Paul Kinlan: You know, of course we can solve these problems, and we're here today
8:41to try and educate you in ways that, you know, we think we can build these applications to
8:46take advantage of all the latest modern advancements in web browse technology, but also kind of
8:52give that, you know, user experience that users expect from applications, regardless
8:56of whether they're native or whether they're actually on the web platform.
8:59So I would like to introduce an application we've written today called IO Reader. It's
9:03a special demo application just for today. The code will be live at the end of this presentation.
9:09And essentially it's a news reading application. It embodies all of the -- kind of the problems
9:14that we face on the mobile web today. We want our applications to work offline but most
9:19Web applications don't, and we think we've built our application to solve all these challenges.
9:24It's built using all the modern web technologies that we as, you know, kind of progressive
9:28web developers use today, so things like node and Modernizer and everything. And without
9:34much further ado, we're going to jump into a demo.
9:37So we've built this application. It's on the mobile -- the smartphone form factor, and
9:43if you've seen -- oh, here we go. Move it up.
9:46If you've ever seen the news and weather native application, you'll notice it looks quite
9:49familiar. We have the traditional kind of scroll inside
9:52the application. And then we can also embed your click-on and read an article. We have
9:58the nice gesture support so we can fling articles. The really cool thing about this is we're
10:03actually using consistent URLs throughout this whole application structure, so we can
10:09take advantage of some native capabilities such as share. So we can share individual
10:17pieces of functionality or pages from our Web app, the kind of things that everyone
10:21expects from Web apps. We just built this application to take advantage of that.
10:26We have nice usage of the browser-based back button, and as you'll see, we didn't actually
10:32reload the page. The whole user experience was contained inside our application. It just
10:36kind of works really nicely. We've got the ability, also, to take advantage
10:40of gestures like the gestures that we see on these native platforms like swipes.
10:46And then we've also got some nice little piece of -- hopefully this will work -- yeah. So
10:51we've got a refresh functionality. The kind of things that we're starting to see in the
10:55-- say the mobile native platforms for, say, refreshing and reloading data, we've built
11:00that into this application, too. It also supports device orientation changes,
11:03so again, we can still swipe between the articles. It's just a really nice example of an application
11:10which we've built just using plain HTML JavaScript that works across smartphones.
11:17But as we said before, the mobile web isn't just about smartphones and building applications
11:23that work on phones. It's also about tablets. So the same application with the exact same
11:29HTML, 90% of the same JavaScript, is targeted to the tablet form factor, and as you can
11:35see, it's -- well, the functionality is equivalent to the application that we had. We had the
11:40categories and articles. We had the dynamic loading of data into the articles, so it's
11:46quick and responsive to your commands. But it's definitely optimized for the tablet experience
11:52so we can go through these articles. The really cool thing is, we had an engineer from the
11:58Mountain View office build this application. Mike actually built the smartphones application
12:01as well. And we didn't have to talk to each other too much.
12:08But, yeah, we have this tablet application. It looks absolutely beautiful.
12:13>>Michael Mahemoff: So I'm going to shift back here.
12:13So we actually just went through those six challenges that we've identified as the main
12:18challenges we think HTML5 developers need to solve if they want to actually make their
12:22apps work nicely on the mobile, and we've shown you these apps here, smartphone and
12:30tablet. So we want to take you through now, how have we solved each of these six challenges.
12:37And the first of those challenges is dealing with different interaction styles.
12:42We want to actually make sure that we're actually looking at different form factors, like the
12:46phone versus the tablet, as not just about the fact that we've got extra screen width
12:52or extra resolution, but actually about the sorts of interactions people have come to
12:56expect. I think you've already seen that with the phone, that it's the sort of thing you
13:00can just get quick information, click and do something. The tablet sort of interface
13:03is more about exploring and you get pictures and it's that sort of more browsing experience,
13:09a bit more of a relaxed experience. And our intuition is, we want to go across
13:13the board with each of our form factors writing separate UIs and separate actually mini applications
13:18based on the common core. We start with the principle that we just have
13:25HTML. This is really how the web is supposed to work. It's supposed to work as structured,
13:30semantic content, and with HTML5 we get extra structure. We can actually have these tags
13:35like header and section, which are -- give a bit more meaning to what's actually on the
13:40page. And this, in itself, is a great thing. This is actually what is sitting on the Internet
13:45for this app. And when you've got that content sitting on the Internet, people can make use
13:48of it. You've already got a kind of API without having to go and deliberately create one,
13:54and it's something that people can make use of and it's great for SEO and for accessibility.
13:58And it's also great for people who have these older kind of browsers as well. Anyone here
14:03use Lynx? Okay. A few fans. So this is the real version of Lynx here,
14:11so we've got our app sitting in Lynx and let me see. I can open up an article and it's
14:16quite readable from just a very basic browser. So our idea is that we can go all the way
14:21up from the zero browser to the hero browser. Likewise, we can actually do this with mobile
14:28as well, so we've got another demo here on Opera Mini.
14:33And Opera Mini, if you don't know, is a very basic sort of browser that does a lot of image
14:40compression and strips a lot of JavaScript to make it a very minimal experience for people
14:44who don't have high bandwidth. So our app works nicely on that too.
14:49Sorry. It's not very well lit up there, but hopefully you can see that.
14:55And same thing. You can then open up individual articles.
14:59So all based on that principle that you have just some bit of common markup and the whole
15:04thing works nicely without any JavaScript at all.
15:10But that's not -- the benefit of that is not just about the fact that there are people
15:14using these browsers like Lynx and Opera Mini, it is also about just the fact that that's
15:18how we've architected our applications. We actually start with that common base and then
15:23each of those layers can actually build on top of that. So, you know, that's our idea.
15:28Here we use this principle of progressive enhancement from the basic app. So we go from
15:34that very basic app, add these extra layers, to actually make it a compelling experience
15:39and one that's relevant to each of the form factors.
15:41We use the principle of feature detection to actually see if a single feature is there.
15:48If we can use it, then we will actually go ahead and use it on this browser.
15:52That's kind of the point about when we say that HTML5 runs so-called everywhere, it is
15:57not a magic bullet much. It is not like saying you just create your app once and it will
16:01magically work everywhere. That can sometimes be the case. It is more about recognizing
16:06that you have got this common stack of technologies running on all of these different devices.
16:11And if you can put a little bit of intelligence in your app about dealing with these different
16:16types of devices, then you get the benefit of all the re-use of that common code base
16:20and the fact that you only have to learn about one set of widgets and one language.
16:26But we're going beyond the idea of feature detection in this talk, and we are introducing
16:30a concept that we're calling form factor detection. And our intuition being, that you can't just
16:37algorithm away the distinction between a mobile and a desktop and a TV. You want just say,
16:42Oh, we are dealing with a TV now, so we will just add an extra 27 columns to our reader
16:46app. It is more about understanding what are the
16:50users trying to do with a TV. It is a lean-back, more of a lazy, perhaps a social experience.
16:55With a phone, it is, I want the information now. With a desktop, it is something you might
17:00spend hours and hours working on. So you have to have qualitatively different sorts of interfaces
17:06for this but you don't want to have a million different interfaces for every model and every
17:10device. We are trying to go for this happy medium
17:14here with the idea of a form factor and actually acknowledging that that's really the thing
17:18that we want to be targeting. So we created this library called formfactorjs
17:24which, basically, does the best job it can to detect what's the current form factor this
17:28app is running in. If it is a phone, then it will run -- it will
17:33load these libraries and JavaScript and CSS. And if it is a desktop, it will load these
17:38other ones so you can build these small layers. That's what we're aiming for, is a big core
17:44of common logic. You have already seen it with the phone and the tablet. They are using
17:48the same markup. They have the same content for doing things -- or the same code for doing
17:54things like retrieving the actual articles that you have seen on the reader, retrieving
17:59the categories, caching them, actually being aware of what the user is doing. All of that
18:05is actually common code, and we create these small layers -- we love to call them awesome
18:09layers -- for just very specific code that's relevant to the user interface.
18:16So this is the kind of thing you'll see if you actually look at one of those awesome
18:19layers. It is the phone, JavaScript and it is able to respond to semantic events. That's
18:26the contract that you have to conform to in our architecture if you want to actually respond
18:32to events like when the users just change the category, when the users change the article.
18:36You can't actually jump in. You can't actually provide some custom JavaScript. But a lot
18:40of that is taken care by the common code on both the client and the server.
18:46On the HTML and CSS side, we also have a patent we use where we actually identify that there
18:52are particular states in our apps. As well as having distinct form factors, within each
18:58-- within our app, we have distinct states. We go from a menu state where you're seeing
19:03all of the categories and maybe a welcome screen to a category state where you are looking
19:07at a single category like music and technology. And then we go to an individual article when
19:12you are actually reading the article. Whether you are looking at the phone or the
19:17tablet or even the very basic interfaces, you've always got those three states. Again,
19:22the contract is if you are writing a layer, you're going to fill in the CSS for how the
19:27app should look in that state. And by putting that class on the route element, you can actually
19:32define the entire CSS for your app, the entire style of each component in the CSS for that
19:39particular user interface for that particular form factor.
19:44And we see that here. This is the kind of CSS you'll see for the phone, where it's doing
19:49things like if we're in the article state, then we'll show the active article with overflow
19:53visible. So we actually show the extra content and so on and so forth. We create all that
19:58style for each of those states. That's what you can do.
20:02If you have already written the phone layer and you are going on to create the tablet
20:05layer, it is very straightforward. You know exactly what you have to do. You just basically
20:09fill in what do I need to do when I'm responding to those events on the JavaScript slide and
20:14what's the CSS like on the HTML and CSS side. We actually use a framework called LESS instead
20:22of raw CSS. LESS is one of several frameworks going around right now that's a kind of CSS++.
20:28It is not a complete new language. It adds a little bit of syntactic sugar that we found
20:33very convenient for this sort of architecture. You can see here, you can do what you can't
20:41do in normal CSS, which is you can actually have a hierarchy, pretty intuitive. It is
20:48just the same as having article state before all of those things.
20:48Another benefit of these high-level CSS frameworks is we have the idea of macros. So no more
20:54repeating the same color code 27 times in your style sheet. We can actually have a single
20:58color palette style sheet. We can then reuse that across all of the different form factors.
21:03If we want to tweak our branding, we can just do that all in one place.
21:07>>Paul Kinlan: How do we build applications that are quick, nearly instant to load and
21:13then respond rapidly to the user's gestures and commands? Well, we built our application
21:18as a single-page app. The theory being behind this is we don't want
21:23to have the user transition between Article A, Article B and then have a five-second delay
21:28with a white screen of the application doing nothing. It kind of really breaks that whole
21:32user experience and isn't something that you see in native applications.
21:37Single-page apps aren't actually a new concept. If you have been doing a lot of kind of AJAX
21:41development recently in the past ten years, eight years, you will be used to this type
21:45of thing. It is really good on mobile-style devices because you don't exactly know what
21:50type of network you are dealing with it. It would be a low-latency network, WiFi connection,
21:54or a high-latency network. Single-page applications, they keep all the
21:58logic and functionality inside that one page, but incrementally they are all data. You just
22:05manage it through JavaScript. It is nothing amazingly special, but it really improves
22:08that whole user experience. As you saw earlier on from the Lynx demo,
22:14our whole DOM, our whole object model for this application is -- it is semantically
22:21defined. So it is consistent across all these browsers.
22:25The main thing that you will notice is when we saw in the Lynx demo you navigate from
22:29article to another article. That navigation is just handled by a normal Lynx.
22:35So what you do with a browser that supports JavaScript and you want to keep them inside
22:39this single-page app, again it is a very similar thing. If you have ever used a JQuery, you
22:44basically just hijack the browser click, do some of your custom business logic, so in
22:50this case a fetch article, and then just prevent the default action from occurring.
22:53I mean, we have been doing these things for quite a while. It is just one of those nice
22:57little things that we did inside our application that really just makes this whole experience
23:01kind of cohesive and inside that one page. But the question that everyone asks when you
23:07build these single-page apps is, well, single-page apps break the Web because they don't allow
23:11you to deep link. They very rarely keep the state inside the URL. We kind of solved that
23:18by playing -- expanding our use of the history API. So it is kind of worth going through
23:24the history -- a brief history of history, I suppose, of how actual browsers have managed
23:28navigation over the last couple of years. In 2,000 when we were all building our Pearl
23:33apps and everything, we had a URL structure that normally closely mimicked the directory
23:38and file -- the directory and file structure. So you would have a category and inside that
23:44category you might say, Well, this is a list of technology articles and the server would
23:47actually render that out and send it to the client. As AJAX took off and we started building
23:52applications which were pretty much inside this single-page container, we still wanted
23:56to have the ability to bookmark individual pieces of state. So you would use the hash-tag
24:03or -- the hash-tag, sorry, that's a Twitterrism. You would use a document fragment syntax that's
24:07there to kind of indicate some state and make it bookmarkable.
24:11But the problem obviously with using the document fragment is that the server never sees this
24:15data so your application can't be built by the server in its entirety. You will load
24:19it in the browser and then the browser will go and fetch some data.
24:24Again, not only because the server can't respond with all the data upfront, the search engines
24:29are having a whole lot of trouble actually being able to actually parse the data in a
24:33consistent manner. So some search engines try to standardize on a common format for
24:41using a document fragment syntax to indicate a particular bit of state inside your browser.
24:46But that wasn't great. But if you look at the URLs that, say, modern Web developers
24:51are using today, they look pretty much exactly the same as they did in 2000. What is the
24:58difference between now and then? Well, the main difference is that use of the HTML5 history
25:02API. It is a really simple API. It, basically, allows you to change the URL of the browser
25:09purely from the client's side. As you can see from this code here, it is
25:15a simple command called push state. Basically you give the browser the URL you want it to
25:18be on the screen, the title so it looks nice when you bookmark it, and maybe some application-specific
25:24states. We didn't need any particular JavaScript objects inside our app pushed into the history
25:30but you can do that. And then as the user is navigating around
25:33the application using forwards and backwards buttons, there is an event that fires pop
25:37state, which essentially you just hook into here. And in our application, we just chose
25:41to listen to the document location. You can actually do whatever you want inside this
25:45function. We just said, Well, we're going to look at document location, parse it out
25:50and then go and fetch the category data. So if the URL match business, we would fetch
25:54the business data. But especially in user application, you never
25:59know all of the URLs that your application will encompass. You might know all the categories,
26:04but you won't know every single article. So managing your code like this isn't great.
26:09So we made a framework called Leviroutes. It is on Getup as well.
26:12We made a framework called Leviroutes, which is basically a URL routing framework. If you
26:19have ever used App Engine or Ruby On Rails, the syntax is relatively similar. It, basically,
26:24allows to you define specific URL types inside your JavaScript, inside your client and then
26:30respond to them with your own kind of custom business logic. So in our case if the article
26:35looks like a category -- or the URL, sorry, looks like a category, we go and fetch the
26:39category data. And then if the article -- sorry, I keep saying this. If the URL looks like
26:43an article, we then go and fetch the article data. It's actually pretty simple, but it
26:48allows you to keep -- keep your application-specific logic to a minimum and just make it very readable.
26:55So we saw from the Lynx demo we were rendering data from the server. All the application
27:02data, in our case categories and articles is being generated by the server. But also
27:07at the same time, we had these really rich interactive applications on, say, the smartphone
27:12and tablet where all the data was located into the client at one time. So that presents
27:17a problem. You need to have potentially two templates,
27:20manage them in potentially different languages, whether it is Python and Pearl and then on
27:25the client side in JavaScript. How did you go about doing it?
27:29We used a basic technique called dual-side templating, and we combine that with a framework
27:35called Mustache which, basically, allows you to maintain one common template across client
27:40and server. As long as your data format is consistent
27:43throughout your entire application, you can basically just render the same data. It substitutes
27:47all the variable names, renders some nice HTML and then you present that to the client.
27:52Sorry. >>Michael Mahemoff: Okay. So we are actually
28:02talking about offline here. You already saw some of the offline apps mentioned in the
28:07keynote today, like "New York Times" reader and "Sports Illustrated" was mentioned. So
28:13this is possible now on the Web. There are technologies that let us do it. And fortunately
28:16enough for us, there are technologies that allow us to do it in the phones and mobile
28:22devices as well. You've actually just seen these apps actually
28:25work offline on the various devices. The first of those technologies -- And there
28:31is really two of them that work side by side. The first is AppCache. And this is the ability
28:36to actually store the entire application logic on the device, just like you would install
28:41an app from the Android market. It would be sitting there on your device. It will be there
28:47even when the user is not connected. To use AppCache, it is really straightforward
28:52actually. In the basic case, you just link to the cache manifest file from your HTML
28:57tag. And in the cache manifest, you simply list all of the assets of your app. And those
29:04-- that's the JavaScript, the CSS images and so on.
29:07The first time the user visits your app, the browser will download all of the assets you've
29:13listed in the manifest. And the next time they visit it, it will immediately serve those
29:18assets. It won't even check to the server at all. It will actually work offline.
29:23Every time you do visit, if you are online, it will keep checking and it will download
29:28in the background all the new assets whenever that one file changes. So if you are connected,
29:33all that ever happens is just a single request to see if the cache manifest has changed.
29:37And if you are offline, the application keeps working as normal.
29:42The other side of this is storing actually data which might be, for instance, an email
29:47the user is composing. In this case, it is actually articles you've read. We've kept
29:51it pretty simple here. We've used LocalStorage, which is a very basic, easy-to-use API. It
29:58gives you, basically, just a JavaScript object. LocalStorage, you can use it. You can treat
30:03it as just a JavaScript object where you can say, localstorage.food equals bar and later
30:07on you retrieve it with localstorage.food. And that's an object that only your domain
30:13can see. No other Web apps can get hold of that, read it and write it, and it's a very
30:19simple interface. That's why there are also these other ones that have actually got more
30:24complexity to them. They give you the benefits of speed and so on, but not quite as simple.
30:29So we've got that with Web SQL database. It's basically an entire SQL database sitting in
30:34the browser. You can imagine that's going to be quite complex. It does have great performance
30:39characteristics in terms of being able to search for a lot of data or being able to
30:44just do things asynchronously, which is the way a lot of the Web apps work.
30:50But that's actually being deprecated, so it is actually quite widely supported as well
30:55in the current -- because it's in Webkit, it's actually in the two apps that you've
31:03actually -- or the two devices you've already seen here, but at the same time it is being
31:04deprecated. It's never made it to mainstream use across all of the desktop browsers.
31:07So what now seems to be the way of the future is indexed database. It's already there in
31:11Chrome and in FireFox on the desktop and hopefully it will make its way to the mobile browsers
31:18soon, and it kind of gives you the best of both worlds. A lot of simplicity you get from
31:21local storage, but the speed and performance characteristics of Web SQL.
31:27>>Paul Kinlan: So we said for each of these devices they have a marketplace, a store built
31:33in, for users to go and find applications. Luckily, the web is no different. But let's
31:39just kind of jump back to our idea of having a consistent common URL structure.
31:45It's really important. We can't stress it enough. If you combine a consistent URL structure
31:49across your app on the client side and the server side using the HTML history and potentially
31:54the dual side templating, it's really easy for search engines to go and aggregate your
32:00content. If it's entirely client side application, it's really hard for search engines to be
32:03able to go and do the job that they do best and deliver users to your application.
32:09But it's not always about just search engines. You know, each of these devices do have native
32:14marketplaces on. You can use services such as Phonegap, for
32:18instance, to actually just package your application -- package your application up into, you know
32:25-- using an existing Web application, you can use it, package it up, and then deliver
32:29it into these native marketplaces. But again, it's not just about native devices.
32:34There are a lot of different web stores. If you're building a desktop application -- I
32:37think Seth was talking about it yesterday, or there was a talk about it yesterday about
32:40the Web Store. Chrome Web Store is a great place for you to go and get your applications
32:44discovered. But from the mobile context, there are a lot of third-party, you know, web stores
32:50available. It's just relatively easily to get into them as well.
32:53So we have a whole range, a whole spectrum of different ways to get your application
32:56discovered from traditional search engines. The power of the web is that these search
33:00engines can index your data, but then you can also go a little bit more native.
33:03>>Michael Mahemoff: Touch. We want to get our apps working with touch the same way the
33:10native apps do, and it's amazing when you think about these gestures, things like swiping
33:13and pinch-to-zoom. When you use a device these days, they feel
33:17so natural but they've only been around for a couple of years, so it's -- it is something
33:22that actually just works with the way we think as humans, and I think we want to be able
33:24to cooperate with that thinking and actually implement that in our apps.
33:30Fortunately, we have this, again, with HTML5, and you've seen it in the IO Reader layers,
33:34the ones that are actually specifically related to touch devices.
33:38We have this with touch events. So we don't have to work around this like the earlier
33:45browsers were doing with your old-school mouse events. You've actually got built-in touch
33:51support with events like touchstart and touchend. As far as those gestures, those things like
33:57swiping and fling and pinch zoom, those are starting to come into the standard. They're
34:02not all there yet. So what we did in in this app is we created a little library to help
34:07with it that would actually detect when the user's done a significant swipe action, significant
34:12enough to actually register and respond to it.
34:16So this is actually a little library where you attach it to an object, also jQuery based,
34:21and it actually will fire when the user's actually done that swipe action in one direction
34:24and let go, whether it's horizontal or vertical. It also fires as the user is moving, moving
34:28their fingers back and forth, so that you actually saw the stories being swiped back
34:35and forth on the phone, or up and down, so we actually can respond to that as the user
34:40is doing it, which is a much better way to design user interfaces than five seconds later.
34:45And we can also do click, which is what happens when the user opens the article, and long
34:50hold. We need to implement those things, because if we're capturing all the touch events, they
34:54won't always fire, the actual built-in click event won't always fire, so we need the library
35:01to do the whole thing. We actually found it really convenient to
35:06develop a lot of this app on the desktop, even in the case of the phone API -- or the
35:11phone form factor, because the desktop is where you're actually coding, so it's really
35:15easy to be able to switch windows and see how the app is working. And it's also where
35:20you can actually take advantage of tools like the Chrome dev tools to actually do your debugging.
35:26So to that end, we actually built into this library a few switches to make sure that it
35:32will work nicely with mouse events as well, if there is no touch support there.
35:38And also, just a tip about debugging is you can actually debug on the device. We were
35:40doing that in the later stages of this project and with Android it's actually really easy.
35:46There's an URL up there where I've just put a few steps in. Just to attach your device
35:49by USB or you can do the same thing on the emulator and you just have to run a couple
35:56of commands and in a couple of minutes you've actually got console logging there. You can
36:00log your own messages and you can also see error messages coming from the browsing environment.
36:04Just a final note and a bit of a caveat on this whole area of touch interfaces.
36:11It's really easy to fall into the so-called uncanny valley. It's a term from robotics
36:18that means you've got kind of close to there, but eerily slightly not there and something's
36:22just a little bit off. So the recommendation here is: Try to stay
36:27right away from that. If you don't think you're going to get very close, you know, just make
36:32something that's very different. There are many successful apps you can see on the various
36:36mobile marketplaces which are a very idiosyncratic interface. You see a lot of games that devise
36:42their own mechanisms for menus and so on. It is actually possible to do that. You don't
36:45have to emulate the native look and feel. But -- so if you're going to do that, you
36:50know, stay clear away from it. If you're going to do what we've tried to do here, we recommend,
36:54you know, trying to take the time to make it as close as possible so you don't end up
36:58in that weird middle ground. >>Paul Kinlan: So we mentioned early on about
37:04the form factor detection and how the differing like interaction styles that you have for
37:08the different types of devices is really important. However, in each of those classes of devices,
37:14there are a whole multitude of -- you know, of different screen sizes and, you know, different
37:19capabilities, and how do you actually build your applications to cater to that.
37:23It's actually quite simple in the end. The first thing that we did in our application
37:26was we defined a viewport. The viewport is basically there to stop the existing web from
37:32completely breaking in mobile devices. So it defines this kind of -- well, it basically
37:39tends to be like say a 1024-by-768 browser so that the pages can render correctly, and
37:44then it will scale the page down to fit the screen or allow you to zoom in and zoom out,
37:48so that you can pan around it, and not actually have to have the web developer of the existing
37:54site, you know, cater especially to your device. But when we're building these applications,
37:58we don't want that functionality, so it's -- it's relatively easy. We're basically saying
38:03this virtual viewport has to be the exact same size as the device width. And that basically
38:08allows you to get sometimes a near one-to-one correspondence between physical device screen
38:13size and, you know, what's reported to your application.
38:16But you still have the ability to enable zoom and zoom-out and all the kind of traditional-based
38:23pinch-based gestures inside your application. We chose not to do it in our application.
38:27We said, you know, we're like specifically targeting smartphones. We're going to have
38:32to design our application to work well across all these devices. We don't need to have zoom
38:37in and zoom out and the traditional, you know, pinch gesture, so we've disabled it.
38:41But it's -- how do I say this? It's entirely up to you. If you have an application which
38:47is suited to zoom in/zoom out and the native -- you know, those native abilities, keep
38:52them in. We chose not to do it in our app. And the next is media queries.
38:57With form factor detection, we're definitely not trying to get rid of the idea of media
39:02queries. We're just trying to make it more manageable for us to build applications that
39:05are responsive to the different screen sizes. It's a lot easier to build an application
39:09that will respond to different smartphone screen sizes or different tablet screen sizes
39:15than have to have one style that responds to all of them at the same time.
39:20And media queries are just a great way to build applications that are at least aware
39:23of the type of screen, the size screen, that they're on, and in our case, when we showed
39:28the demo of the smartphone, we had two different orientations, a portrait and a landscape.
39:32You can just target different CSS to say the landscape mode. Maybe hide some images because
39:38you value vertical space. And then in the portrait mode, you just go and show all the
39:42images because you've got a lot of vertical space and there's a lot more freedom in that
39:47mode. So we're actually getting quite close to the
39:49end. It's -- so we're just kind of going -- all
39:54those images are gone. Cool. [Laughter]
39:58So they're supposed to be nice text. We've solved them. So we think we've done an okay
39:59job at solving, you know, these six different interaction styles -- or six different problems
40:04of -- you know, that we face as mobile web developers.
40:07The different interaction styles are managed through form factor detection, so you can
40:11target specific user interaction styles to the different types of devices.
40:15We've shown you how, you know, single-page apps, traditional AJAX methods, can really
40:19help improve your application just by incrementally loading the data and not having this jarring
40:24experience when you're navigating from one page to the next. And then with --
40:33>>Michael Mahemoff: Are we going to do a relay --
40:33>>Paul Kinlan: Yeah. No. Then using appcache, not only can you make new application load,
40:36you know, instantly, you can just make it work offline as well, and then if you combine
40:40that with local storage, you can at least keep the data, you know, in memory and in
40:44browser, so that the user doesn't have that experience of being completely offline.
40:49We can make our applications discoverable. Things like the server side templating and
40:52the client side -- the dual side templating technique we talked about allow you to render
40:56and present data to search engines really efficiently, get that -- and get discovered.
41:02And then you can obviously use the native frameworks as well to actually package your
41:06Web application. We've shown you who you can build applications
41:09that are responsive not just to basic touch commands but the gestures as well. There's
41:13a nice little library Mike wrote. And then just using the viewport and media queries,
41:16we can actually build applications that are responsive to the different screen sizes.
41:21>>Michael Mahemoff: So the idea here has not been just about targeting the mobile devices,
41:26but about using this technology of HTML5 to reach all of the different types of devices
41:31you might care for, to expose your services to, to users, and to that end we've also got
41:38these other types of apps as well. So we've got a desktop app. So this is not
41:44the desktop app. That's the TV app here, so I'll show you the TV first.
41:47So this is actually where we've got the TV where it's that experience where you're 10
41:54foot back, you want to just get a bit of an overview about what the stories are. You can
41:59see them easily. And taking -- taking into account the user
42:01interface on the input side and the output side.
42:05So on the input side, I'm able to control all of this. I can use a pointing device if
42:10I want, a mouse, but I'm able to control all of it just with the keyboard with just the
42:14arrow keys, you know, which you would have on a very basic remote control. So that's
42:18the input side only. Output side, you can see how it gives you
42:21that very big overview. And in the case of the desktop app -- ooooh.
42:27Have we got a problem? Okay. I'll leave it to the screen shot. You can see that later.
42:36Some problems. Sometimes with the offlining, it can be a problem where if it's a slow connection,
42:39which seems to be the case here, I think, you can actually get something going, so -- oh,
42:43yeah, we can switch over to here. So sometimes you'll get a situation where
42:47it downloads some of it but not all of it, which we haven't optimized for, but you can
42:51see it here. So you get -- again, you get the ability to
42:55use keyboard shortcuts which kind of fits in with the natural style that people assume
42:58should be there on the desktop, as well as the mouse.
43:01So again, it's that input style of keyboard shortcuts and mouse, and the output style
43:05just taking advantage of all the features of a standard desktop browser.
43:10Let's jump back. And the point about this has been that we're
43:13able to develop this all quite independently from one another, so we actually -- Paul did
43:18most of the work on the architecture and the core logic of the app and each of us wrote
43:23the separate layers. Two of us were in London, two of us were over here in Mountain View,
43:27and we didn't have to do that much communication between each other, because it was just a
43:31standard contract for how we'd actually write these apps.
43:35So you can see here, you know, the vast majority of the logic in IO Reader is able to be reused.
43:42And I think that's about it. So you can see the code's going online. The
43:46actual -- the actual news source that you see, it's coming from the guardian API. It's
43:51probably going to be one too many hits for their API if we leave that running, so it's
44:02really something where you can actually plug in your own data source and you could actually
44:06do, you know, something for your own apps or for any other kind of services that are
44:10on the web. That's a separate module that's part of IO Reader on the server side. It's
44:12a node component that you can actually write your own to get different sort of data. And
44:13there are various open source libraries as well that we've used in here that you'll be
44:15able to see. When the slides come online, you'll have those links.
44:20But for right now, we've got a link to Moderator. I don't know, has anyone -- what we did. There's
44:26another one that doesn't work online. Maybe we should switch to the link on there.
44:27>>Paul Kinlan: Should we give it a go? >>Michael Mahemoff: Yeah. We're just going
44:30to jump to the link that we've got in the slides.
44:31But meanwhile, we do have time for questions and we're interested to know like if you've
44:39got any challenges you've come across or any other tips. We'd like to hear about them.
44:42So please, if you've got questions, come up. Otherwise, we'll look at Moderator as well
44:46in a moment, once we're back online. >> Hi.
44:48>>Michael Mahemoff: Hi. >> Can you talk about the interaction between
44:53the manifest file and which files are downloaded compared to the script tags and the style
45:00tags that are in the head compared to the -- that slide you showed with Modernizer which
45:07actually selects which styles -- >>Paul Kinlan: Yeah. So we actually -- we
45:12didn't have an amazing amount of scripts inside our application, so each individual form factor
45:17is actually embedded in the one sheet. So if you look at the code, it's maybe two
45:22lines -- two bits of JavaScript for smartphone, two files for desktop, and we just downloaded
45:27all of them because it's relatively low latency at the moment. This actually allows you to
45:33quickly flip in between the form factors and not have to re-download any new assets and
45:37then validate your appcache. >> And so does the manifest file -- all of
45:45those files get downloaded and they're all referenced in the head as well?
45:47>>Paul Kinlan: Yeah, they're referenced -- well, there are some core assets that are referenced
45:49from the head. The common controller is in the head, and then each of the -- the -- like
45:55the phone JavaScript and the iPad -- or the tablet JavaScript, at least, is referenced
46:01in the form factor loader. Because it's on the server side, we have control
46:04over, you know, what's gets rendered where, so we know all the files that are needed for
46:07the whole entire application, so they are always available at the moment.
46:10>>Michael Mahemoff: Take one from Moderator. >>Paul Kinlan: Yeah. Should we just do one
46:16from Moderator? Just pick the first one. JQuery, mobile, SproutCore mobile, Sencha Touch, what's
46:21being used? In our application, we didn't use anything specific. It was just all normal
46:27JavaScript. Did you want to explain the differences?
46:31>>Michael Mahemoff: Briefly, the differences between these three frameworks, I guess get
46:34JQuery mobile, it is a very new framework. It has got a very permissive license. And
46:39it is very much like JQuery, very much a microkernel with some very basic sort of widgets at the
46:45moment and the ability to do theming as well. But it is very much a standard sort of open
46:50source project like JQuery. The other two are more commercially backed.
46:56In fact, both of those, SproutCore and Sencha, are here at I/O and Sandbox is, if you want
47:05to talk to them. SproutCore is aiming to be an app where you
47:10can sort of write just HTML, focus on something that might even be desktop-related or actually
47:19take advantage of some of their widgets and then they are aiming to offer services on
47:31top of that. So I think it is a little bit of a less commercial license because it is
47:41not jewel license like Sencha Touch. It also has got the MIT license like JQuery. So you
47:57can use it however you want. And then they are planning to sort of add
48:07extra services to it like being able to package things up and distribute to stores and do
48:17caching and so on. Sencha Touch is starting to become a very big commercial operation.
48:23You can still use it as a GPL3 license. They have got some very sophisticated widgets available.
48:31So as always, horses to courses and check them all out.
48:33>>> My question is in regards to the offline of a Web app. If I had an app where a user
48:39could post, let's say, information through the Web app and they were offline, is there
48:43a method or a technology that then the device or the app would recognize they are online
48:49and push that back to my site or the app? >>Paul Kinlan: So that means each of the devices
48:54normal has an event, I think -- online is an event on the document so you can at least
49:01tell when you're on. It doesn't work across all devices because it depends on whether
49:11the network interface is up. >>Michael Mahemoff: Actually, that originally
49:20wasn't supported on Chrome because they had the intuition that you should actually just
49:34be trying to push it online. And if it doesn't work, you handle the exception.
49:46>>Paul Kinlan: One thing we do do in our application at least is we don't necessarily post any
49:57data, but we intercept every request as best we can for fetching the data so we can then
50:08cache it. I think the same thing applies when you are trying to post data. You can basically
50:15get the on-error states on XML HTTP request and then respond to it that way and then just
50:21make sure the data is cached. >>Michael Mahemoff: It makes a good point
50:29because we need more libraries to do this sort of thing. We are starting to get some
50:36libraries like Lawn Chair and PersistJS that will eventually have different layers probably
50:38to sort of fall back depending on what mechanism is there for LocalStorage.
50:41What we need is actually higher-level libraries that will do things like caching and syncing.
50:47They are not really there yet in JavaScript. >>> Thank you.
50:53>>> This isn't a question. It is just a comment about console.log. Console.log on Android
51:09is not universal to every single Android device. For example, the HTC -- mostly HTC lines,
51:27not the dex one but the commercially available HTC lines do not have console.log. That's
51:33why it is a good idea to actually develop your mobile application in the emulator instead
51:39of the device in this one case. >>Paul Kinlan: It is actually interesting.
51:44You can do About call on some of the Android devices, and you can get -- if it supports
51:51console, you can actually get a console log inside the application on the device as well.
52:00>>Michael Mahemoff: Right now you type about.debug and use it to toggle or not.
52:04(Speaker off microphone.) >>Michael Mahemoff: Let's take one from the
52:08Moderator. Next one: Is what's your take on app versus mobile Web sites?
52:11>>Paul Kinlan: Depends on the content, I think. >>Michael Mahemoff: It does, yeah.
52:14>>Paul Kinlan: If it is content-based, it is a site. If it is functionality, then it
52:18is more app-like. >>Michael Mahemoff: I think we are seeing
52:23the same thing. Paul and I have worked a lot on the Chrome Web store. I think it is also
52:32about being aware of the fact that apps do have special privileges. They can do things
52:57like inapp payments easily and maybe they've got other ability to get involved with the
53:10hardware and so on. They can also communicate back and forth.
53:14There are techniques that you can use on the various platforms like the kinds of things
53:23Phone Gap does that let you communicate with native capabilities. So if you actually do
53:47need to tune into those native capabilities. You can do that with apps as well. I think
53:56there is always a role for Web sites. And actually with phones, you got the ability
54:06to send a SMS with a link in it so it is really convenient for people to just click on a link
54:17from an SMS as well as they might see it in Twitter or something. All those things don't
54:21open in apps. They open in phones, so you still need at least some mobile Web presence.
54:27>>> I have a couple of questions. What's the best way -- Normally you detect a difference
54:33between the phone and desktop. Like, usually you use the user agent. So is that the best
54:37way or you go -- what is the best way to handle it?
54:40>>Paul Kinlan: Yes, that's actually a very good question. So how do you actually detect
54:42the differences between the devices? We've actually used a combination of user agent
54:45string check-in in the JavaScript side of things. But then specific media queries as
54:47well. So the formfactorjs can take a whole lot of
54:51different expressions that will determine whether it is a particular device class or
54:57not. >>Michael Mahemoff: In the case of formfactorjs,
55:00we have left that kind of modular so it is actually down to a community that doesn't
55:05yet exist. That would be the concept, you would be able to have a whole grassroots collection.
55:09>>Paul Kinlan: Yeah. We have media queries like TV and pretty much only TV support the
55:11media TV query. So you can basically use that media query to say we definitely think you
55:16are on TV and then download these assets. It is a combination.
55:20>>Michael Mahemoff: It is always a messy approach. It is not very clean cut. That's why we also
55:27provide the ability to jump back to the standard desktop app, and it's something that's a bit
55:34controversial doing this sort of thing. We feel like the standard approaches that
55:41people talk about in terms of feature detection and responsive Web design, they are just not
55:44enough to have quite distinct user interfaces for these different form factors. Even if
55:45it is messy, we would rather have some sort of guess about the form factor.
55:48>>> Okay. And another question was, like, normally in applications, if you have sort
55:52of an event where you click it, like it is a map coordinate, you click it and it will
55:52launch a map UI. So say, for example, if I were to develop something for picture, if
55:53someone clicks and holds on the picture, if I want to raise to set as a a wallpaper or
55:54something like that, is there a way possible right now?
55:54>>Paul Kinlan: That's kind of more Android specific. I'm under the impression that the
55:54applications respond to when those intents broadcast from inside within your Web application.
55:54The other way around, I don't think you can -- I don't think -- it won't launch the browser
55:54naturally unless the browser is actually listening to that intent.
55:54>>> Maybe I didn't clarify. If I have a Web application and I'm browsing, like, pictures,
55:54from that, is there a way, like, to have new intents as to -- if some application is registered
55:54for, say, if you click on it, in the browser itself and then you want to set it as -- you
55:55want to crop that wallpaper or set that as a wallpaper, can you do that?
55:57>>Michael Mahemoff: Not that I know of. You just have the ability what we show, which
55:57is the share feature. That's as far as I know. There are standards so there are standard
55:58protocols for doing things like launching the phone, if you want to show a phone number,
55:59you can do a tel prefix and so on but not in a general sense as far as I know.
56:00>>> Thank you. >>Michael Mahemoff: Thank you. Maybe time
56:00for one more question. >>Paul Kinlan: Should we do one more question
56:01from here. >>Michael Mahemoff: And then we'll wrap up.
56:01>>Paul Kinlan: A good example out there of a real-life HTML5 implementation that runs
56:01on desktop, phone and tablet with offline capability? Gmail seems like a poster child
56:01more than any others. So Gmail doesn't. IO Reader? That's a great
56:01-- >>Michael Mahemoff: Yeah. I think the "real
56:02life" was intended there for a reason. >>Paul Kinlan: Yeah. Exactly. So this is one
56:04of the things that we're trying to do. We're trying to reach developers and help them build
56:06applications this way because I don't think that many people are building those types
56:07of apps at the moment. >>Michael Mahemoff: I'll give you a couple
56:07of examples. One interesting one to look at is bit.ly, bit.ly's own app. It works nicely
56:07on the mobiles. It does canvas graphics to actually generate charts, which is basically
56:08the same thing it does on the desktop, and it seems to be quite natural on both of those.
56:08And also look at Netflix. There's a long article on Function Source Log recently about how
56:09Netflix is targeting over 400 different types of devices with HTML5. It's not really the
56:09same app but it is actually the fact that they are using the same technology stack,
56:09again and again, based on HTML5 and actually they're specifically aiming it keep it within
56:09Webkit. >>Paul Kinlan: Actually, can I answer one
56:09about the pushstate? We've got a question about is the window.pushstate history manipulation
56:09supported on all major platforms or is it just a feature for future applications?
56:09It's not supported on every single platform at the moment. Webkit has got a really good
56:09coverage of pushstate. The reason why we created a wrapper library is because we can't always
56:10rely on the fact -- or this routes framework, we can't always rely on the fact that pushstate
56:11is supported. So the application -- if you use Lynx, you'd
56:12see it use the document fragment to indicate active articles and things like that, so the
56:12whole routing framework can take advantage of on-hash change or just normal on-load events
56:12as well to actually detect the current state of the application. So we haven't got full
56:12coverage. I think more browsers are going to support it because this -- I mean, literally
56:12it's the fact that you can have a single consistent URL structure on the server side. On the client
56:17side, it just makes it so powerful. >>Michael Mahemoff: Okay. So let's take one
56:19final question from the audience here and then we'll wrap up.
56:21>> Thanks. How are you using Mustache? Were you actually sending different markup to each
56:28client and like transporting JSON or was it all CSS based?
56:31>>Paul KinlanActually, we've got the same consistent HTML DOM structure throughout the
56:35whole of the application and across all the different types of devices.
56:40Moustache was just there to -- when we get data back from the article. So we will go
56:45and make a request to the guardian API, and it's in its own format, and we -- we've like
56:49munched that into a consistent object model for our side.
56:53If it's an AJAX request, it's JSON to the client and then rendered using that same template
56:57that is held on the server side, and the server is -- again, because it's all node and it's
57:01a JSON object, we just basically render the same thing.
57:05>>Michael Mahemoff: But the reason we use Mustache -- because it's all node, we could
57:08have used anything, right? Because it's actually Javascript on either side.
57:12We just used Mustache really as a proof of concept. We wanted to show that even if we
57:16did our server in Python or whatever, we'd still be able to use that same pattern of
57:20taking the same data structure, but on the server the data structure is like a raw data
57:25structure. It's not really represented as a string of JSON. It's just a data structure
57:32that gets pushed into the Mustache library. Whereas on the browser, it is actually JSON
57:32that gets pulled down by XHR and processed on demand as the user actually requests that
57:40content. Yeah. I think we're done. Okay.
57:41>>Paul Kinlan: Thank you. >>Michael Mahemoff: Thank you.
57:43%%%Chuck3end
'Google' 카테고리의 다른 글
구글이 몇 가지 개발툴을 무료로 오픈했습니다 (0) | 2010.09.29 |
---|---|
YouTube 동영상 다운로드 (0) | 2010.07.23 |
새로운 언어 Google Go (0) | 2010.01.20 |