add Tasks API App Engine sample
diff --git a/samples/tasks_appengine/README b/samples/tasks_appengine/README
new file mode 100644
index 0000000..56da2e8
--- /dev/null
+++ b/samples/tasks_appengine/README
@@ -0,0 +1,2 @@
+Sample code for Getting Started with Tasks API on App Engine article.
+http://code.google.com/appengine/articles/python/getting_started_with_tasks_api.html
diff --git a/samples/tasks_appengine/app.yaml b/samples/tasks_appengine/app.yaml
new file mode 100644
index 0000000..35a7dc6
--- /dev/null
+++ b/samples/tasks_appengine/app.yaml
@@ -0,0 +1,12 @@
+application: mytasks
+version: 1
+runtime: python
+api_version: 1
+
+handlers:
+- url: /oauth2callback
+  script: oauth2client/appengine.py
+- url: /css
+  static_dir: css
+- url: .*
+  script: main.py
diff --git a/samples/tasks_appengine/css/style.css b/samples/tasks_appengine/css/style.css
new file mode 100644
index 0000000..cef1a0b
--- /dev/null
+++ b/samples/tasks_appengine/css/style.css
@@ -0,0 +1,48 @@
+body {
+    width: 600px;
+    margin: auto;
+    margin-top: 48px;
+    margin-bottom: 48px;
+    font-family: 'Coming Soon', serif;
+    font-size: 35px;
+}
+
+ul {
+    -moz-border-radius: 20px;
+    -webkit-border-radius: 20px;
+    border-radius: 20px;
+    background-color: #f8f6c6;
+    background-image: -webkit-linear-gradient(#fdfce8, #f8f6c6);
+    list-style-type: none;
+    padding: 0;
+    margin: 0;
+    border: 5px solid #e3e2be;
+    -moz-box-shadow: 8px 8px 8px #ccc;
+    -webkit-box-shadow: 8px 8px 8px #ccc;
+    box-shadow: 8px 8px 8px #ccc;
+}
+
+li {
+    padding-left: 20px;
+    border-bottom: 4px solid #e3e2be;
+}
+
+li .check {
+    display: block;
+    float: left;
+    border-right: 8px solid #e3e2be;
+    padding-right: 20px;
+    width: 20px;
+}
+
+li .title {
+    margin-left: 4px;
+    padding-left: 20px;
+    border-left: 4px solid #e3e2be;
+    color: black;
+    text-decoration: none;
+}
+
+li:last-child {
+    border-bottom: none;
+}
diff --git a/samples/tasks_appengine/main.py b/samples/tasks_appengine/main.py
new file mode 100644
index 0000000..54260bd
--- /dev/null
+++ b/samples/tasks_appengine/main.py
@@ -0,0 +1,57 @@
+# Copyright (C) 2011 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from google.appengine.dist import use_library
+use_library('django', '1.2')
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import template
+from google.appengine.ext.webapp.util import run_wsgi_app
+from apiclient.discovery import build
+import httplib2
+from oauth2client.appengine import OAuth2Decorator
+import settings
+
+decorator = OAuth2Decorator(client_id=settings.CLIENT_ID,
+                            client_secret=settings.CLIENT_SECRET,
+                            scope=settings.SCOPE,
+                            user_agent='mytasks')
+
+
+class MainHandler(webapp.RequestHandler):
+
+  @decorator.oauth_aware
+  def get(self):
+    if decorator.has_credentials():
+      service = build('tasks', 'v1', http=decorator.http())
+      result = service.tasks().list(tasklist='@default').execute()
+      tasks = result.get('items', [])
+      for task in tasks:
+        task['title_short'] = truncate(task['title'], 26)
+      self.response.out.write(template.render('templates/index.html',
+                                              {'tasks': tasks}))
+    else:
+      url = decorator.authorize_url()
+      self.response.out.write(template.render('templates/index.html',
+                                              {'tasks': [],
+                                               'authorize_url': url}))
+
+
+def truncate(s, l):
+  return s[:l] + '...' if len(s) > l else s
+
+application = webapp.WSGIApplication([('/', MainHandler)], debug=True)
+
+
+def main():
+  run_wsgi_app(application)
diff --git a/samples/tasks_appengine/settings.py.sample b/samples/tasks_appengine/settings.py.sample
new file mode 100644
index 0000000..ee76355
--- /dev/null
+++ b/samples/tasks_appengine/settings.py.sample
@@ -0,0 +1,3 @@
+CLIENT_ID='PASTE_YOUR_GENERATED_CLIENT_ID_HERE'
+CLIENT_SECRET='PASTE_YOUR GENERATED_CLIENT_SECRET_HERE'
+SCOPE='https://www.googleapis.com/auth/tasks.readonly'
diff --git a/samples/tasks_appengine/templates/index.html b/samples/tasks_appengine/templates/index.html
new file mode 100644
index 0000000..7160f0c
--- /dev/null
+++ b/samples/tasks_appengine/templates/index.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+  <head>
+    <link href="/css/style.css" rel="stylesheet" type="text/cs">
+    <link href="http://fonts.googleapis.com/css?family=Coming+Soon:regular" rel="stylesheet" type="text/css">
+  </head>
+  <body>
+    <ul id="tasklist">
+      {% if tasks|length == 0 %}
+      <li>
+        <span class="check">
+          <span>
+            &nbsp;
+          </span>
+        </span>
+      {% if authorize_url %}
+        <a class="title" title="Grant read access to your tasks" href="{{ authorize_url }}">Grant Access</a>
+      {% else %}
+        <a class="title" title="Add more tasks in Gmail" href="http://mail.google.com/">Add More Tasks</a>
+      {% endif %}
+      </li>
+      {% endif %}
+      {% for task in tasks %}
+      <li class="{{ task.status }}">
+        <span class="check">
+          <span>
+            {% if task.status == "completed" %}
+            &#x2713;
+            {% else %}
+            &nbsp;
+            {% endif %}
+          </span>
+        </span>
+        <a class="title" title="{{ task.title }}">{{ task.title_short }}</a>
+      </li>
+      {% endfor %}
+    </ul>
+  </body>
+</html>