auto import from //depot/cupcake/@135843
diff --git a/tools/scripts/AndroidManifest.alias.template b/tools/scripts/AndroidManifest.alias.template
new file mode 100644
index 0000000..b1be4c8
--- /dev/null
+++ b/tools/scripts/AndroidManifest.alias.template
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="PACKAGE"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <application android:hasCode="false">
+        <activity android:name="android.app.AliasActivity" android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <meta-data android:name="android.app.alias" android:resource="@xml/alias" />
+        </activity>
+    </application>
+</manifest> 
diff --git a/tools/scripts/AndroidManifest.template b/tools/scripts/AndroidManifest.template
new file mode 100644
index 0000000..2b06e76
--- /dev/null
+++ b/tools/scripts/AndroidManifest.template
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="PACKAGE"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <application android:label="@string/app_name">
+        <activity android:name=".ACTIVITY_NAME"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest> 
diff --git a/tools/scripts/AndroidManifest.tests.template b/tools/scripts/AndroidManifest.tests.template
new file mode 100644
index 0000000..1f7d827
--- /dev/null
+++ b/tools/scripts/AndroidManifest.tests.template
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="PACKAGE.tests"
+          android:versionCode="1"
+          android:versionName="1.0">
+    <!-- We add an application tag here just so that we can indicate that
+         this package needs to link against the android.test library,
+         which is needed when building test cases. -->
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <!--
+    This declares that this application uses the instrumentation test runner targeting
+    the package of PACKAGE.  To run the tests use the command:
+    "adb shell am instrument -w PACKAGE.tests/android.test.InstrumentationTestRunner"
+    -->
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+                     android:targetPackage="PACKAGE"
+                     android:label="Tests for ACTIVITY_NAME"/>
+</manifest>
diff --git a/tools/scripts/README_add-ons.txt b/tools/scripts/README_add-ons.txt
new file mode 100644
index 0000000..b8eb1d6
--- /dev/null
+++ b/tools/scripts/README_add-ons.txt
@@ -0,0 +1,2 @@
+Add-on folder.
+Drop vendor supplied SDK add-on in this folder.
\ No newline at end of file
diff --git a/tools/scripts/add-accounts b/tools/scripts/add-accounts
new file mode 100755
index 0000000..d2cddc0
--- /dev/null
+++ b/tools/scripts/add-accounts
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# 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.
+
+"""
+A faux Setup Wizard.  Stuffs one or two usernames + passwords into the
+database on the device.
+"""
+
+import sys
+if sys.hexversion < 0x02040000:
+  print "This script requires python 2.4 or higher."
+  sys.exit(1)
+
+import getpass
+import subprocess
+import time
+import sha
+
+DB = "/data/data/com.google.android.googleapps/databases/accounts.db"
+
+def RunCmd(args):
+  proc = subprocess.Popen(args, stdout=subprocess.PIPE)
+  out = proc.stdout.read()
+  if proc.wait():
+    print
+    print "failed: %s" % " ".join(args)
+    return None
+  return out
+
+def GetProp(adb_flags, name):
+  args = ("adb",) + adb_flags + ("shell", "su", "root",
+                                 "/system/bin/getprop", name)
+  return RunCmd(args)
+
+def SetProp(adb_flags, name, value):
+  args = ("adb",) + adb_flags + ("shell", "su", "root",
+                                 "/system/bin/setprop", name, value)
+  return RunCmd(args)
+
+def DbExists(adb_flags):
+  args = ("adb",) + adb_flags + ("shell", "su", "root",
+                                 "/system/bin/ls", DB)
+  result = RunCmd(args)
+  if result is None: return None
+  return "No such file" not in result
+
+def main(argv):
+  if len(argv) == 1:
+    print ("usage: %s [adb flags] "
+           "[<dasher address[:password]>] "
+           "[<gmail address[:password]>]") % (argv[0],)
+    sys.exit(2)
+
+  argv = argv[1:]
+
+  gmail = None
+  dasher = None
+  while argv and "@" in argv[-1]:
+    addr = argv.pop()
+    if "@gmail.com" in addr or "@googlemail.com" in addr:
+      gmail = addr
+    else:
+      dasher = addr
+
+  adb_flags = tuple(argv)
+
+  while True:
+    db = DbExists(adb_flags)
+    if db is None:
+      print "failed to contact device; will retry in 3 seconds"
+      time.sleep(3)
+      continue
+
+    if db:
+      print
+      print "GoogleLoginService has already started on this device;"
+      print "it's too late to use this script to add accounts."
+      print
+      print "This script only works on a freshly-wiped device (or "
+      print "emulator) while booting for the first time."
+      print
+      break
+
+    hosted_account = GetProp(adb_flags, "ro.config.hosted_account").strip()
+    google_account = GetProp(adb_flags, "ro.config.google_account").strip()
+
+    if dasher and hosted_account:
+      print
+      print "A dasher account is already configured on this device;"
+      print "can't add", hosted_account
+      print
+      dasher = None
+
+    if gmail and google_account:
+      print
+      print "A google account is already configured on this device;"
+      print "can't add", google_account
+      print
+      gmail = None
+
+    if not gmail and not dasher: break
+
+    if dasher:
+      SetProp(adb_flags, "ro.config.hosted_account", dasher)
+      print "set hosted_account to", dasher
+    if gmail:
+      SetProp(adb_flags, "ro.config.google_account", gmail)
+      print "set google_account to", gmail
+
+    break
+
+
+
+
+
+
+if __name__ == "__main__":
+  main(sys.argv)
diff --git a/tools/scripts/add-accounts-sdk b/tools/scripts/add-accounts-sdk
new file mode 100755
index 0000000..bb3447f
--- /dev/null
+++ b/tools/scripts/add-accounts-sdk
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# 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.
+
+"""
+A faux Setup Wizard.  Stuffs one or two usernames + passwords into the
+database on the device.
+"""
+
+import sys
+if sys.hexversion < 0x02040000:
+  print "This script requires python 2.4 or higher."
+  sys.exit(1)
+
+import getpass
+import subprocess
+import time
+import sha
+
+DB = "/data/data/com.google.android.googleapps/databases/accounts.db"
+
+def RunCmd(args):
+  proc = subprocess.Popen(args, stdout=subprocess.PIPE)
+  out = proc.stdout.read()
+  if proc.wait():
+    print
+    print "failed: %s" % " ".join(args)
+    return None
+  return out
+
+def GetProp(adb_flags, name):
+  args = ("adb",) + adb_flags + ("shell", "/system/bin/getprop", name)
+  return RunCmd(args)
+
+def SetProp(adb_flags, name, value):
+  args = ("adb",) + adb_flags + ("shell", "/system/bin/setprop", name, value)
+  return RunCmd(args)
+
+def DbExists(adb_flags):
+  args = ("adb",) + adb_flags + ("shell", "/system/bin/ls", DB)
+  result = RunCmd(args)
+  if result is None: return None
+  return "No such file" not in result
+
+def main(argv):
+  if len(argv) == 1:
+    print ("usage: %s [adb flags] "
+           "[<hosted address[:password]>] "
+           "[<gmail address[:password]>]") % (argv[0],)
+    sys.exit(2)
+
+  argv = argv[1:]
+
+  gmail = None
+  hosted = None
+  while argv and "@" in argv[-1]:
+    addr = argv.pop()
+    if "@gmail.com" in addr or "@googlemail.com" in addr:
+      gmail = addr
+    else:
+      hosted = addr
+
+  adb_flags = tuple(argv)
+
+  while True:
+    db = DbExists(adb_flags)
+    if db is None:
+      print "failed to contact device; will retry in 3 seconds"
+      time.sleep(3)
+      continue
+
+    if db:
+      print
+      print "GoogleLoginService has already started on this device;"
+      print "it's too late to use this script to add accounts."
+      print
+      print "This script only works on a freshly-wiped device (or "
+      print "emulator) while booting for the first time."
+      print
+      break
+
+    hosted_account = GetProp(adb_flags, "ro.config.hosted_account").strip()
+    google_account = GetProp(adb_flags, "ro.config.google_account").strip()
+
+    if hosted and hosted_account:
+      print
+      print "A hosted account is already configured on this device;"
+      print "can't add", hosted_account
+      print
+      hosted = None
+
+    if gmail and google_account:
+      print
+      print "A google account is already configured on this device;"
+      print "can't add", google_account
+      print
+      gmail = None
+
+    if not gmail and not hosted: break
+
+    if hosted:
+      SetProp(adb_flags, "ro.config.hosted_account", hosted)
+      print "set hosted_account to", hosted
+    if gmail:
+      SetProp(adb_flags, "ro.config.google_account", gmail)
+      print "set google_account to", gmail
+
+    break
+
+
+
+
+
+
+if __name__ == "__main__":
+  main(sys.argv)
diff --git a/tools/scripts/alias.template b/tools/scripts/alias.template
new file mode 100644
index 0000000..8be355b
--- /dev/null
+++ b/tools/scripts/alias.template
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<alias xmlns:android="http://schemas.android.com/apk/res/android">
+    <intent android:action="android.intent.action.VIEW"
+        android:data="ALIASDATA"/>
+</alias>
\ No newline at end of file
diff --git a/tools/scripts/alias_rules.xml b/tools/scripts/alias_rules.xml
new file mode 100644
index 0000000..bc7de7c
--- /dev/null
+++ b/tools/scripts/alias_rules.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="alias_rules" default="package">
+
+    <!-- No user servicable parts below. -->
+
+    <!-- Input directories -->
+    <property name="resource-dir" value="res" />
+
+    <!-- The final package file to generate -->
+    <property name="out-package" value="${ant.project.name}.apk" />
+
+    <!-- Tools -->
+    <condition property="aapt" value="${android-tools}/aapt.exe" else="${android-tools}/aapt" >
+        <os family="windows"/>
+    </condition>
+    <condition property="adb" value="${android-tools}/adb.exe" else="${android-tools}/adb" >
+        <os family="windows"/>
+    </condition>
+    <property name="android-jar" value="${sdk-folder}/android.jar" />
+
+    <!-- Rules -->
+
+    <!-- Packages the manifest and the resource files -->
+    <target name="package-res">
+        <echo>Packaging resources...</echo>
+        <exec executable="${aapt}" failonerror="true">
+            <arg value="package" />
+            <arg value="-f" />
+            <arg value="-M" />
+            <arg value="AndroidManifest.xml" />
+            <arg value="-S" />
+            <arg value="${resource-dir}" />
+            <arg value="-I" />
+            <arg value="${android-jar}" />
+            <arg value="-F" />
+            <arg value="${out-package}" />
+        </exec>
+    </target>
+
+    <!-- Create the package file for this project from the sources. -->
+    <target name="package" depends="package-res" />
+
+    <!-- Create the package and install package on the default emulator -->
+    <target name="install" depends="package">
+        <echo>Sending package to default emulator...</echo>
+        <exec executable="${adb}" failonerror="true">
+            <arg value="install" />
+            <arg value="${out-package}" />
+        </exec>
+    </target>
+
+</project>
diff --git a/tools/scripts/android.el b/tools/scripts/android.el
new file mode 100644
index 0000000..49f2f1e
--- /dev/null
+++ b/tools/scripts/android.el
@@ -0,0 +1,131 @@
+;;;; Copyright 2007 The Android Open Source Project
+
+;;; Set up GUD+JDB to attach to a Java process running on the phone or
+;;; under the emulator.
+
+(defvar android-jdb-port-history '("8700")
+ "history of ports supplied to `android-jdb'")
+
+(defvar android-jdb-project-root-history '()
+ "history of project roots supplied to `android-jdb'")
+(defvar android-jdb-history nil
+ "history of commands supplied to `android-jdb'")
+
+(defvar android-jdb-activity-class-history ()
+ "history of activity classes supplied to `start-android-activity'")
+
+(defcustom  android-jdb-command-name "jdb"
+  "Name of Java debugger."
+  :type 'string
+  :group 'android)
+
+(defgroup android nil
+  "Android Applications."
+  :group 'applications)
+
+(defcustom android-project-root nil
+ "This is where your Android project root is stored."
+  :type 'directory
+ :group 'android )
+
+(defcustom android-apk nil
+ "This is where your Android Application Package is stored."
+ :type 'string
+ :group 'android)
+
+(defcustom android-activity-class nil
+ "This is where your Android Activity class is stored."
+ :type 'string
+ :group 'android)
+
+(defun android-read-project-root ()
+ (if (or (string-match "XEmacs" emacs-version)
+         (>= emacs-major-version 22))
+     (read-file-name "Android project root: "
+                     android-project-root
+                     nil
+                     t
+                     nil
+                     'file-directory-p)
+   (labels ((read-directory ()
+                            (read-file-name "Android project root: "
+                                            android-project-root
+                                            nil
+                                            t
+                                            nil)))
+     (do ((entered-root (read-directory) (read-directory)))
+         ((and entered-root
+               (file-directory-p entered-root))
+          (expand-file-name entered-root))))))
+
+(defun android-jdb (port root)
+ "Set GUD+JDB up to run against Android on PORT in directory ROOT."
+ (interactive
+  (list
+   (read-from-minibuffer "Activity's JDWP DDMS port: "
+                     (car android-jdb-port-history)
+                     nil
+                     t
+                     'android-jdb-port-history)
+                    (android-read-project-root)))
+ (setq android-project-root root)
+ (let ((jdb-command
+        (format "%s -attach localhost:%s -sourcepath%s"
+                android-jdb-command-name
+                port
+                (format "%s/src" root))))
+   (if (not (string= jdb-command (car android-jdb-history)))
+       (push jdb-command android-jdb-history))
+   (jdb jdb-command)))
+
+(defun android-emulate ()
+ "Run the Android emulator. This expects the SDK tools directory to be in the current path."
+ (interactive)
+ (compile "emulator"))
+
+(defun android-install-app (apk)
+  "Install an Android application package APK in the Android emulator. This expects the SDK tools directory to be in the current path."
+  (interactive (list (expand-file-name
+                      (read-file-name "Android Application Package (.apk): "
+                                      nil
+                                      android-apk
+                                      t
+                                      nil
+                                      nil))))
+  (setq android-apk apk)
+  (compile (format "adb install -r %s" apk)))
+
+(defun android-uninstall-app (package-name)
+  "Uninstall an Android application package APK in the Android emulator. This expects the SDK tools directory to be in the current path.
+Specify the package name --- and not the name of the application e.g., com.android.foo."
+  (interactive
+   (list
+    (read-from-minibuffer "Package: ")))
+  (compile (format "adb install -r %s" package)))
+
+(defun android-start-activity (package class)
+ "Start the activity PACKAGE/CLASS in the Android emulator. This expects the SDK tools directory to be in the current path."
+ (interactive
+  (list
+   (read-from-minibuffer "Package: ")
+   (read-from-minibuffer "Activity Java class: "
+         (car android-jdb-activity-class-history)
+         nil
+         t
+         'android-jdb-activity-class-history)))
+ (compile (format "adb shell am start -n %s/%s" package class)))
+
+(defun android-debug-activity (package class)
+ "Start the activity PACKAGE/CLASS within the debugger in the Android emulator. This expects the SDK tools directory to be in the current path."
+ (interactive
+  (list
+   (read-from-minibuffer "Package: ")
+   (read-from-minibuffer "Activity Java class: "
+         (car android-jdb-activity-class-history)
+         nil
+         t
+         'android-jdb-activity-class-history)))
+ (compile (format "adb shell am start -D -n %s/%s" package class)))
+
+(provide 'android)
+
diff --git a/tools/scripts/android_rules.xml b/tools/scripts/android_rules.xml
new file mode 100644
index 0000000..799aa0b
--- /dev/null
+++ b/tools/scripts/android_rules.xml
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="android_rules" default="debug">
+
+    <!--
+        This rules file is meant to be imported by the custom Ant task:
+            com.android.ant.AndroidInitTask
+
+        The following properties are put in place by the importing task:
+            android-jar, android-aidl, aapt, aidl, and dx
+
+        Additionnaly, the task sets up the following classpath reference:
+            android.target.classpath
+        This is used by the compiler task as the boot classpath.
+    -->
+
+    <!-- Custom tasks -->
+    <taskdef name="aaptexec"
+        classname="com.android.ant.AaptExecLoopTask"
+        classpathref="android.antlibs"/>
+
+    <taskdef name="apkbuilder"
+        classname="com.android.ant.ApkBuilderTask"
+        classpathref="android.antlibs"/>
+
+    <!-- Properties -->
+
+    <property name="android-tools" value="${sdk-location}/tools" />
+
+    <!-- Input directories -->
+    <property name="source-folder" value="src" />
+    <property name="resource-folder" value="res" />
+    <property name="asset-folder" value="assets" />
+    <property name="source-location" value="${basedir}/${source-folder}" />
+
+    <!-- folder for the 3rd party java libraries -->
+    <property name="external-libs-folder" value="libs" />
+
+    <!-- folder for the native libraries -->
+    <property name="native-libs-folder" value="libs" />
+
+    <!-- Output directories -->
+    <property name="out-folder" value="bin" />
+    <property name="out-classes" value="${out-folder}/classes" />
+    <property name="out-classes-location" value="${basedir}/${out-classes}"/>
+    <!-- out folders for a parent project if this project is an instrumentation project -->
+    <property name="main-out-folder" value="../${out-folder}" />
+    <property name="main-out-classes" value="${main-out-folder}/classes"/>
+
+    <!-- Create R.java in the source directory -->
+    <property name="r-folder" value="${source-folder}" />
+
+    <!-- Intermediate files -->
+    <property name="dex-file" value="classes.dex" />
+    <property name="intermediate-dex" value="${out-folder}/${dex-file}" />
+    <!-- dx does not properly support incorrect / or \ based on the platform
+         and Ant cannot convert them because the parameter is not a valid path.
+         Because of this we have to compute different paths depending on the platform. -->
+    <condition property="intermediate-dex-location"
+            value="${basedir}\${intermediate-dex}"
+            else="${basedir}/${intermediate-dex}" >
+        <os family="windows"/>
+    </condition>
+
+    <!-- The final package file to generate -->
+    <property name="out-debug-package" value="${out-folder}/${ant.project.name}-debug.apk"/>
+
+    <!-- Tools -->
+    <condition property="exe" value="exe" else=""><os family="windows"/></condition>
+    <property name="adb" value="${android-tools}/adb${exe}"/>
+
+    <!-- rules -->
+
+    <!-- Create the output directories if they don't exist yet. -->
+    <target name="dirs">
+        <echo>Creating output directories if needed...</echo>
+        <mkdir dir="${out-folder}" />
+        <mkdir dir="${out-classes}" />
+    </target>
+
+    <!-- Generate the R.java file for this project's resources. -->
+    <target name="resource-src" depends="dirs">
+        <echo>Generating R.java / Manifest.java from the resources...</echo>
+        <exec executable="${aapt}" failonerror="true">
+            <arg value="package" />
+            <arg value="-m" />
+            <arg value="-J" />
+            <arg path="${r-folder}" />
+            <arg value="-M" />
+            <arg path="AndroidManifest.xml" />
+            <arg value="-S" />
+            <arg path="${resource-folder}" />
+            <arg value="-I" />
+            <arg path="${android-jar}" />
+        </exec>
+    </target>
+
+    <!-- Generate java classes from .aidl files. -->
+    <target name="aidl" depends="dirs">
+        <echo>Compiling aidl files into Java classes...</echo>
+        <apply executable="${aidl}" failonerror="true">
+            <arg value="-p${android-aidl}" />
+            <arg value="-I${source-folder}" />
+            <fileset dir="${source-folder}">
+                <include name="**/*.aidl"/>
+            </fileset>
+        </apply>
+    </target>
+
+    <!-- Compile this project's .java files into .class files. -->
+    <target name="compile" depends="dirs, resource-src, aidl">
+        <javac encoding="ascii" target="1.5" debug="true" extdirs=""
+                srcdir="${source-folder}"
+                destdir="${out-classes}"
+                bootclasspathref="android.target.classpath">
+            <classpath>
+                <fileset dir="${external-libs-folder}" includes="*.jar"/>
+                <pathelement path="${main-out-classes}"/>
+            </classpath>
+         </javac>
+    </target>
+
+    <!-- Convert this project's .class files into .dex files. -->
+    <target name="dex" depends="compile">
+        <echo>Converting compiled files and external libraries into ${out-folder}/${dex-file}...</echo>
+        <apply executable="${dx}" failonerror="true" parallel="true">
+            <arg value="--dex" />
+            <arg value="--output=${intermediate-dex-location}" />
+            <arg path="${out-classes-location}" />
+            <fileset dir="${external-libs-folder}" includes="*.jar"/>
+        </apply>
+    </target>
+
+    <!-- Put the project's resources into the output package file
+         This actually can create multiple resource package in case
+         Some custom apk with specific configuration have been
+         declared in default.properties.
+         -->
+    <target name="package-resources">
+        <echo>Packaging resources</echo>
+        <aaptexec executable="${aapt}"
+                command="package"
+                manifest="AndroidManifest.xml"
+                resources="${resource-folder}"
+                assets="${asset-folder}"
+                androidjar="${android-jar}"
+                outfolder="${out-folder}"
+                basename="${ant.project.name}" />
+    </target>
+
+    <!-- Package the application and sign it with a debug key.
+         This is the default target when building. It is used for debug. -->
+    <target name="debug" depends="dex, package-resources">
+        <apkbuilder
+                outfolder="${out-folder}"
+                basename="${ant.project.name}"
+                signed="true"
+                verbose="false">
+            <file path="${intermediate-dex}" />
+            <sourcefolder path="${source-folder}" />
+            <jarfolder path="${external-libs-folder}" />
+            <nativefolder path="${native-libs-folder}" />
+        </apkbuilder>
+    </target>
+
+    <!-- Package the application without signing it.
+         This allows for the application to be signed later with an official publishing key. -->
+    <target name="release" depends="dex, package-resources">
+        <apkbuilder
+                outfolder="${out-folder}"
+                basename="${ant.project.name}"
+                signed="false"
+                verbose="false">
+            <file path="${intermediate-dex}" />
+            <sourcefolder path="${source-folder}" />
+            <jarfolder path="${external-libs-folder}" />
+            <nativefolder path="${native-libs-folder}" />
+        </apkbuilder>
+        <echo>All generated packages need to be signed with jarsigner before they are published.</echo>
+    </target>
+
+    <!-- Install the package on the default emulator -->
+    <target name="install" depends="debug">
+        <echo>Installing ${out-debug-package} onto default emulator...</echo>
+        <exec executable="${adb}" failonerror="true">
+            <arg value="install" />
+            <arg path="${out-debug-package}" />
+        </exec>
+    </target>
+
+    <target name="reinstall" depends="debug">
+        <echo>Installing ${out-debug-package} onto default emulator...</echo>
+        <exec executable="${adb}" failonerror="true">
+            <arg value="install" />
+            <arg value="-r" />
+            <arg path="${out-debug-package}" />
+        </exec>
+    </target>
+
+    <!-- Uinstall the package from the default emulator -->
+    <target name="uninstall">
+        <echo>Uninstalling ${application-package} from the default emulator...</echo>
+        <exec executable="${adb}" failonerror="true">
+            <arg value="uninstall" />
+            <arg path="${application-package}" />
+        </exec>
+    </target>
+    
+    <target name="help">
+        <!-- displays starts at col 13
+              |13                                                              80| -->
+        <echo>Android Ant Build. Available targets:</echo>
+        <echo>   help:      Displays this help.</echo>
+        <echo>   debug:     Builds the application and sign it with a debug key.</echo>
+        <echo>   release:   Builds the application. The generated apk file must be</echo>
+        <echo>              signed before it is published.</echo>
+        <echo>   install:   Installs the debug package onto a running emulator or</echo>
+        <echo>              device. This can only be used if the application has </echo>
+        <echo>              not yet been installed.</echo>
+        <echo>   reinstall: Installs the debug package on a running emulator or</echo>
+        <echo>              device that already has the application.</echo>
+        <echo>              The signatures must match.</echo>
+        <echo>   uninstall: uninstall the application from a running emulator or</echo>
+        <echo>              device.</echo>
+    </target>
+</project>
diff --git a/tools/scripts/app_engine_server/LICENSE b/tools/scripts/app_engine_server/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/tools/scripts/app_engine_server/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
diff --git a/tools/scripts/app_engine_server/app.yaml b/tools/scripts/app_engine_server/app.yaml
new file mode 100755
index 0000000..1fb50c7
--- /dev/null
+++ b/tools/scripts/app_engine_server/app.yaml
@@ -0,0 +1,16 @@
+application: androidappdocs-staging
+version: 1
+runtime: python
+api_version: 1
+
+handlers:
+- url: /gae_shell/static
+  static_dir: gae_shell/static
+  expiration: 1d
+
+- url: /gae_shell/.*
+  script: /gae_shell/shell.py
+  login: admin
+
+- url: .*
+  script: main.py
diff --git a/tools/scripts/app_engine_server/gae_shell/README b/tools/scripts/app_engine_server/gae_shell/README
new file mode 100644
index 0000000..5b0089f
--- /dev/null
+++ b/tools/scripts/app_engine_server/gae_shell/README
@@ -0,0 +1,17 @@
+An interactive, stateful AJAX shell that runs Python code on the server.
+
+Part of http://code.google.com/p/google-app-engine-samples/.
+
+May be run as a standalone app or in an existing app as an admin-only handler.
+Can be used for system administration tasks, as an interactive way to try out
+APIs, or as a debugging aid during development.
+
+The logging, os, sys, db, and users modules are imported automatically.
+
+Interpreter state is stored in the datastore so that variables, function
+definitions, and other values in the global and local namespaces can be used
+across commands.
+
+To use the shell in your app, copy shell.py, static/*, and templates/* into
+your app's source directory. Then, copy the URL handlers from app.yaml into
+your app.yaml.
diff --git a/tools/scripts/app_engine_server/gae_shell/__init__.py b/tools/scripts/app_engine_server/gae_shell/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/scripts/app_engine_server/gae_shell/__init__.py
diff --git a/tools/scripts/app_engine_server/gae_shell/__init__.pyc b/tools/scripts/app_engine_server/gae_shell/__init__.pyc
new file mode 100644
index 0000000..84951e9
--- /dev/null
+++ b/tools/scripts/app_engine_server/gae_shell/__init__.pyc
Binary files differ
diff --git a/tools/scripts/app_engine_server/gae_shell/shell.py b/tools/scripts/app_engine_server/gae_shell/shell.py
new file mode 100755
index 0000000..df2fb17
--- /dev/null
+++ b/tools/scripts/app_engine_server/gae_shell/shell.py
@@ -0,0 +1,308 @@
+#!/usr/bin/python
+#
+# Copyright 2007 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.
+
+"""
+An interactive, stateful AJAX shell that runs Python code on the server.
+
+Part of http://code.google.com/p/google-app-engine-samples/.
+
+May be run as a standalone app or in an existing app as an admin-only handler.
+Can be used for system administration tasks, as an interactive way to try out
+APIs, or as a debugging aid during development.
+
+The logging, os, sys, db, and users modules are imported automatically.
+
+Interpreter state is stored in the datastore so that variables, function
+definitions, and other values in the global and local namespaces can be used
+across commands.
+
+To use the shell in your app, copy shell.py, static/*, and templates/* into
+your app's source directory. Then, copy the URL handlers from app.yaml into
+your app.yaml.
+
+TODO: unit tests!
+"""
+
+import logging
+import new
+import os
+import pickle
+import sys
+import traceback
+import types
+import wsgiref.handlers
+
+from google.appengine.api import users
+from google.appengine.ext import db
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import template
+
+
+# Set to True if stack traces should be shown in the browser, etc.
+_DEBUG = True
+
+# The entity kind for shell sessions. Feel free to rename to suit your app.
+_SESSION_KIND = '_Shell_Session'
+
+# Types that can't be pickled.
+UNPICKLABLE_TYPES = (
+  types.ModuleType,
+  types.TypeType,
+  types.ClassType,
+  types.FunctionType,
+  )
+
+# Unpicklable statements to seed new sessions with.
+INITIAL_UNPICKLABLES = [
+  'import logging',
+  'import os',
+  'import sys',
+  'from google.appengine.ext import db',
+  'from google.appengine.api import users',
+  ]
+
+
+class Session(db.Model):
+  """A shell session. Stores the session's globals.
+
+  Each session globals is stored in one of two places:
+
+  If the global is picklable, it's stored in the parallel globals and
+  global_names list properties. (They're parallel lists to work around the
+  unfortunate fact that the datastore can't store dictionaries natively.)
+
+  If the global is not picklable (e.g. modules, classes, and functions), or if
+  it was created by the same statement that created an unpicklable global,
+  it's not stored directly. Instead, the statement is stored in the
+  unpicklables list property. On each request, before executing the current
+  statement, the unpicklable statements are evaluated to recreate the
+  unpicklable globals.
+
+  The unpicklable_names property stores all of the names of globals that were
+  added by unpicklable statements. When we pickle and store the globals after
+  executing a statement, we skip the ones in unpicklable_names.
+
+  Using Text instead of string is an optimization. We don't query on any of
+  these properties, so they don't need to be indexed.
+  """
+  global_names = db.ListProperty(db.Text)
+  globals = db.ListProperty(db.Blob)
+  unpicklable_names = db.ListProperty(db.Text)
+  unpicklables = db.ListProperty(db.Text)
+
+  def set_global(self, name, value):
+    """Adds a global, or updates it if it already exists.
+
+    Also removes the global from the list of unpicklable names.
+
+    Args:
+      name: the name of the global to remove
+      value: any picklable value
+    """
+    blob = db.Blob(pickle.dumps(value))
+
+    if name in self.global_names:
+      index = self.global_names.index(name)
+      self.globals[index] = blob
+    else:
+      self.global_names.append(db.Text(name))
+      self.globals.append(blob)
+
+    self.remove_unpicklable_name(name)
+
+  def remove_global(self, name):
+    """Removes a global, if it exists.
+
+    Args:
+      name: string, the name of the global to remove
+    """
+    if name in self.global_names:
+      index = self.global_names.index(name)
+      del self.global_names[index]
+      del self.globals[index]
+
+  def globals_dict(self):
+    """Returns a dictionary view of the globals.
+    """
+    return dict((name, pickle.loads(val))
+                for name, val in zip(self.global_names, self.globals))
+
+  def add_unpicklable(self, statement, names):
+    """Adds a statement and list of names to the unpicklables.
+
+    Also removes the names from the globals.
+
+    Args:
+      statement: string, the statement that created new unpicklable global(s).
+      names: list of strings; the names of the globals created by the statement.
+    """
+    self.unpicklables.append(db.Text(statement))
+
+    for name in names:
+      self.remove_global(name)
+      if name not in self.unpicklable_names:
+        self.unpicklable_names.append(db.Text(name))
+
+  def remove_unpicklable_name(self, name):
+    """Removes a name from the list of unpicklable names, if it exists.
+
+    Args:
+      name: string, the name of the unpicklable global to remove
+    """
+    if name in self.unpicklable_names:
+      self.unpicklable_names.remove(name)
+
+
+class FrontPageHandler(webapp.RequestHandler):
+  """Creates a new session and renders the shell.html template.
+  """
+
+  def get(self):
+    # set up the session. TODO: garbage collect old shell sessions
+    session_key = self.request.get('session')
+    if session_key:
+      session = Session.get(session_key)
+    else:
+      # create a new session
+      session = Session()
+      session.unpicklables = [db.Text(line) for line in INITIAL_UNPICKLABLES]
+      session_key = session.put()
+
+    template_file = os.path.join(os.path.dirname(__file__), 'templates',
+                                 'shell.html')
+    session_url = '/?session=%s' % session_key
+    vars = { 'server_software': os.environ['SERVER_SOFTWARE'],
+             'python_version': sys.version,
+             'session': str(session_key),
+             'user': users.get_current_user(),
+             'login_url': users.create_login_url(session_url),
+             'logout_url': users.create_logout_url(session_url),
+             }
+    rendered = webapp.template.render(template_file, vars, debug=_DEBUG)
+    self.response.out.write(rendered)
+
+
+class StatementHandler(webapp.RequestHandler):
+  """Evaluates a python statement in a given session and returns the result.
+  """
+
+  def get(self):
+    self.response.headers['Content-Type'] = 'text/plain'
+
+    # extract the statement to be run
+    statement = self.request.get('statement')
+    if not statement:
+      return
+
+    # the python compiler doesn't like network line endings
+    statement = statement.replace('\r\n', '\n')
+
+    # add a couple newlines at the end of the statement. this makes
+    # single-line expressions such as 'class Foo: pass' evaluate happily.
+    statement += '\n\n'
+
+    # log and compile the statement up front
+    try:
+      logging.info('Compiling and evaluating:\n%s' % statement)
+      compiled = compile(statement, '<string>', 'single')
+    except:
+      self.response.out.write(traceback.format_exc())
+      return
+
+    # create a dedicated module to be used as this statement's __main__
+    statement_module = new.module('__main__')
+
+    # use this request's __builtin__, since it changes on each request.
+    # this is needed for import statements, among other things.
+    import __builtin__
+    statement_module.__builtins__ = __builtin__
+
+    # load the session from the datastore
+    session = Session.get(self.request.get('session'))
+
+    # swap in our custom module for __main__. then unpickle the session
+    # globals, run the statement, and re-pickle the session globals, all
+    # inside it.
+    old_main = sys.modules.get('__main__')
+    try:
+      sys.modules['__main__'] = statement_module
+      statement_module.__name__ = '__main__'
+
+      # re-evaluate the unpicklables
+      for code in session.unpicklables:
+        exec code in statement_module.__dict__
+
+      # re-initialize the globals
+      for name, val in session.globals_dict().items():
+        try:
+          statement_module.__dict__[name] = val
+        except:
+          msg = 'Dropping %s since it could not be unpickled.\n' % name
+          self.response.out.write(msg)
+          logging.warning(msg + traceback.format_exc())
+          session.remove_global(name)
+
+      # run!
+      old_globals = dict(statement_module.__dict__)
+      try:
+        old_stdout = sys.stdout
+        old_stderr = sys.stderr
+        try:
+          sys.stdout = self.response.out
+          sys.stderr = self.response.out
+          exec compiled in statement_module.__dict__
+        finally:
+          sys.stdout = old_stdout
+          sys.stderr = old_stderr
+      except:
+        self.response.out.write(traceback.format_exc())
+        return
+
+      # extract the new globals that this statement added
+      new_globals = {}
+      for name, val in statement_module.__dict__.items():
+        if name not in old_globals or val != old_globals[name]:
+          new_globals[name] = val
+
+      if True in [isinstance(val, UNPICKLABLE_TYPES)
+                  for val in new_globals.values()]:
+        # this statement added an unpicklable global. store the statement and
+        # the names of all of the globals it added in the unpicklables.
+        session.add_unpicklable(statement, new_globals.keys())
+        logging.debug('Storing this statement as an unpicklable.')
+
+      else:
+        # this statement didn't add any unpicklables. pickle and store the
+        # new globals back into the datastore.
+        for name, val in new_globals.items():
+          if not name.startswith('__'):
+            session.set_global(name, val)
+
+    finally:
+      sys.modules['__main__'] = old_main
+
+    session.put()
+
+
+def main():
+  application = webapp.WSGIApplication(
+    [('/gae_shell/', FrontPageHandler),
+     ('/gae_shell/shell.do', StatementHandler)], debug=_DEBUG)
+  wsgiref.handlers.CGIHandler().run(application)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/scripts/app_engine_server/gae_shell/shell.py~ b/tools/scripts/app_engine_server/gae_shell/shell.py~
new file mode 100755
index 0000000..dee9fdb
--- /dev/null
+++ b/tools/scripts/app_engine_server/gae_shell/shell.py~
@@ -0,0 +1,308 @@
+#!/usr/bin/python
+#
+# Copyright 2007 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.
+
+"""
+An interactive, stateful AJAX shell that runs Python code on the server.
+
+Part of http://code.google.com/p/google-app-engine-samples/.
+
+May be run as a standalone app or in an existing app as an admin-only handler.
+Can be used for system administration tasks, as an interactive way to try out
+APIs, or as a debugging aid during development.
+
+The logging, os, sys, db, and users modules are imported automatically.
+
+Interpreter state is stored in the datastore so that variables, function
+definitions, and other values in the global and local namespaces can be used
+across commands.
+
+To use the shell in your app, copy shell.py, static/*, and templates/* into
+your app's source directory. Then, copy the URL handlers from app.yaml into
+your app.yaml.
+
+TODO: unit tests!
+"""
+
+import logging
+import new
+import os
+import pickle
+import sys
+import traceback
+import types
+import wsgiref.handlers
+
+from google.appengine.api import users
+from google.appengine.ext import db
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import template
+
+
+# Set to True if stack traces should be shown in the browser, etc.
+_DEBUG = True
+
+# The entity kind for shell sessions. Feel free to rename to suit your app.
+_SESSION_KIND = '_Shell_Session'
+
+# Types that can't be pickled.
+UNPICKLABLE_TYPES = (
+  types.ModuleType,
+  types.TypeType,
+  types.ClassType,
+  types.FunctionType,
+  )
+
+# Unpicklable statements to seed new sessions with.
+INITIAL_UNPICKLABLES = [
+  'import logging',
+  'import os',
+  'import sys',
+  'from google.appengine.ext import db',
+  'from google.appengine.api import users',
+  ]
+
+
+class Session(db.Model):
+  """A shell session. Stores the session's globals.
+
+  Each session globals is stored in one of two places:
+
+  If the global is picklable, it's stored in the parallel globals and
+  global_names list properties. (They're parallel lists to work around the
+  unfortunate fact that the datastore can't store dictionaries natively.)
+
+  If the global is not picklable (e.g. modules, classes, and functions), or if
+  it was created by the same statement that created an unpicklable global,
+  it's not stored directly. Instead, the statement is stored in the
+  unpicklables list property. On each request, before executing the current
+  statement, the unpicklable statements are evaluated to recreate the
+  unpicklable globals.
+
+  The unpicklable_names property stores all of the names of globals that were
+  added by unpicklable statements. When we pickle and store the globals after
+  executing a statement, we skip the ones in unpicklable_names.
+
+  Using Text instead of string is an optimization. We don't query on any of
+  these properties, so they don't need to be indexed.
+  """
+  global_names = db.ListProperty(db.Text)
+  globals = db.ListProperty(db.Blob)
+  unpicklable_names = db.ListProperty(db.Text)
+  unpicklables = db.ListProperty(db.Text)
+
+  def set_global(self, name, value):
+    """Adds a global, or updates it if it already exists.
+
+    Also removes the global from the list of unpicklable names.
+
+    Args:
+      name: the name of the global to remove
+      value: any picklable value
+    """
+    blob = db.Blob(pickle.dumps(value))
+
+    if name in self.global_names:
+      index = self.global_names.index(name)
+      self.globals[index] = blob
+    else:
+      self.global_names.append(db.Text(name))
+      self.globals.append(blob)
+
+    self.remove_unpicklable_name(name)
+
+  def remove_global(self, name):
+    """Removes a global, if it exists.
+
+    Args:
+      name: string, the name of the global to remove
+    """
+    if name in self.global_names:
+      index = self.global_names.index(name)
+      del self.global_names[index]
+      del self.globals[index]
+
+  def globals_dict(self):
+    """Returns a dictionary view of the globals.
+    """
+    return dict((name, pickle.loads(val))
+                for name, val in zip(self.global_names, self.globals))
+
+  def add_unpicklable(self, statement, names):
+    """Adds a statement and list of names to the unpicklables.
+
+    Also removes the names from the globals.
+
+    Args:
+      statement: string, the statement that created new unpicklable global(s).
+      names: list of strings; the names of the globals created by the statement.
+    """
+    self.unpicklables.append(db.Text(statement))
+
+    for name in names:
+      self.remove_global(name)
+      if name not in self.unpicklable_names:
+        self.unpicklable_names.append(db.Text(name))
+
+  def remove_unpicklable_name(self, name):
+    """Removes a name from the list of unpicklable names, if it exists.
+
+    Args:
+      name: string, the name of the unpicklable global to remove
+    """
+    if name in self.unpicklable_names:
+      self.unpicklable_names.remove(name)
+
+
+class FrontPageHandler(webapp.RequestHandler):
+  """Creates a new session and renders the shell.html template.
+  """
+
+  def get(self):
+    # set up the session. TODO: garbage collect old shell sessions
+    session_key = self.request.get('session')
+    if session_key:
+      session = Session.get(session_key)
+    else:
+      # create a new session
+      session = Session()
+      session.unpicklables = [db.Text(line) for line in INITIAL_UNPICKLABLES]
+      session_key = session.put()
+
+    template_file = os.path.join(os.path.dirname(__file__), 'templates',
+                                 'shell.html')
+    session_url = '/?session=%s' % session_key
+    vars = { 'server_software': os.environ['SERVER_SOFTWARE'],
+             'python_version': sys.version,
+             'session': str(session_key),
+             'user': users.get_current_user(),
+             'login_url': users.create_login_url(session_url),
+             'logout_url': users.create_logout_url(session_url),
+             }
+    rendered = webapp.template.render(template_file, vars, debug=_DEBUG)
+    self.response.out.write(rendered)
+
+
+class StatementHandler(webapp.RequestHandler):
+  """Evaluates a python statement in a given session and returns the result.
+  """
+
+  def get(self):
+    self.response.headers['Content-Type'] = 'text/plain'
+
+    # extract the statement to be run
+    statement = self.request.get('statement')
+    if not statement:
+      return
+
+    # the python compiler doesn't like network line endings
+    statement = statement.replace('\r\n', '\n')
+
+    # add a couple newlines at the end of the statement. this makes
+    # single-line expressions such as 'class Foo: pass' evaluate happily.
+    statement += '\n\n'
+
+    # log and compile the statement up front
+    try:
+      logging.info('Compiling and evaluating:\n%s' % statement)
+      compiled = compile(statement, '<string>', 'single')
+    except:
+      self.response.out.write(traceback.format_exc())
+      return
+
+    # create a dedicated module to be used as this statement's __main__
+    statement_module = new.module('__main__')
+
+    # use this request's __builtin__, since it changes on each request.
+    # this is needed for import statements, among other things.
+    import __builtin__
+    statement_module.__builtins__ = __builtin__
+
+    # load the session from the datastore
+    session = Session.get(self.request.get('session'))
+
+    # swap in our custom module for __main__. then unpickle the session
+    # globals, run the statement, and re-pickle the session globals, all
+    # inside it.
+    old_main = sys.modules.get('__main__')
+    try:
+      sys.modules['__main__'] = statement_module
+      statement_module.__name__ = '__main__'
+
+      # re-evaluate the unpicklables
+      for code in session.unpicklables:
+        exec code in statement_module.__dict__
+
+      # re-initialize the globals
+      for name, val in session.globals_dict().items():
+        try:
+          statement_module.__dict__[name] = val
+        except:
+          msg = 'Dropping %s since it could not be unpickled.\n' % name
+          self.response.out.write(msg)
+          logging.warning(msg + traceback.format_exc())
+          session.remove_global(name)
+
+      # run!
+      old_globals = dict(statement_module.__dict__)
+      try:
+        old_stdout = sys.stdout
+        old_stderr = sys.stderr
+        try:
+          sys.stdout = self.response.out
+          sys.stderr = self.response.out
+          exec compiled in statement_module.__dict__
+        finally:
+          sys.stdout = old_stdout
+          sys.stderr = old_stderr
+      except:
+        self.response.out.write(traceback.format_exc())
+        return
+
+      # extract the new globals that this statement added
+      new_globals = {}
+      for name, val in statement_module.__dict__.items():
+        if name not in old_globals or val != old_globals[name]:
+          new_globals[name] = val
+
+      if True in [isinstance(val, UNPICKLABLE_TYPES)
+                  for val in new_globals.values()]:
+        # this statement added an unpicklable global. store the statement and
+        # the names of all of the globals it added in the unpicklables.
+        session.add_unpicklable(statement, new_globals.keys())
+        logging.debug('Storing this statement as an unpicklable.')
+
+      else:
+        # this statement didn't add any unpicklables. pickle and store the
+        # new globals back into the datastore.
+        for name, val in new_globals.items():
+          if not name.startswith('__'):
+            session.set_global(name, val)
+
+    finally:
+      sys.modules['__main__'] = old_main
+
+    session.put()
+
+
+def main():
+  application = webapp.WSGIApplication(
+    [('/', FrontPageHandler),
+     ('/shell.do', StatementHandler)], debug=_DEBUG)
+  wsgiref.handlers.CGIHandler().run(application)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/scripts/app_engine_server/gae_shell/static/shell.js b/tools/scripts/app_engine_server/gae_shell/static/shell.js
new file mode 100644
index 0000000..4aa1583
--- /dev/null
+++ b/tools/scripts/app_engine_server/gae_shell/static/shell.js
@@ -0,0 +1,195 @@
+// Copyright 2007 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.
+
+/**
+ * @fileoverview
+ * Javascript code for the interactive AJAX shell.
+ *
+ * Part of http://code.google.com/p/google-app-engine-samples/.
+ *
+ * Includes a function (shell.runStatement) that sends the current python
+ * statement in the shell prompt text box to the server, and a callback
+ * (shell.done) that displays the results when the XmlHttpRequest returns.
+ *
+ * Also includes cross-browser code (shell.getXmlHttpRequest) to get an
+ * XmlHttpRequest.
+ */
+
+/**
+ * Shell namespace.
+ * @type {Object}
+ */
+var shell = {}
+
+/**
+ * The shell history. history is an array of strings, ordered oldest to
+ * newest. historyCursor is the current history element that the user is on.
+ *
+ * The last history element is the statement that the user is currently
+ * typing. When a statement is run, it's frozen in the history, a new history
+ * element is added to the end of the array for the new statement, and
+ * historyCursor is updated to point to the new element.
+ *
+ * @type {Array}
+ */
+shell.history = [''];
+
+/**
+ * See {shell.history}
+ * @type {number}
+ */
+shell.historyCursor = 0;
+
+/**
+ * A constant for the XmlHttpRequest 'done' state.
+ * @type Number
+ */
+shell.DONE_STATE = 4;
+
+/**
+ * A cross-browser function to get an XmlHttpRequest object.
+ *
+ * @return {XmlHttpRequest?} a new XmlHttpRequest
+ */
+shell.getXmlHttpRequest = function() {
+  if (window.XMLHttpRequest) {
+    return new XMLHttpRequest();
+  } else if (window.ActiveXObject) {
+    try {
+      return new ActiveXObject('Msxml2.XMLHTTP');
+    } catch(e) {
+      return new ActiveXObject('Microsoft.XMLHTTP');
+    }
+  }
+
+  return null;
+};
+
+/**
+ * This is the prompt textarea's onkeypress handler. Depending on the key that
+ * was pressed, it will run the statement, navigate the history, or update the
+ * current statement in the history.
+ *
+ * @param {Event} event the keypress event
+ * @return {Boolean} false to tell the browser not to submit the form.
+ */
+shell.onPromptKeyPress = function(event) {
+  var statement = document.getElementById('statement');
+
+  if (this.historyCursor == this.history.length - 1) {
+    // we're on the current statement. update it in the history before doing
+    // anything.
+    this.history[this.historyCursor] = statement.value;
+  }
+
+  // should we pull something from the history?
+  if (event.ctrlKey && event.keyCode == 38 /* up arrow */) {
+    if (this.historyCursor > 0) {
+      statement.value = this.history[--this.historyCursor];
+    }
+    return false;
+  } else if (event.ctrlKey && event.keyCode == 40 /* down arrow */) {
+    if (this.historyCursor < this.history.length - 1) {
+      statement.value = this.history[++this.historyCursor];
+    }
+    return false;
+  } else if (!event.altKey) {
+    // probably changing the statement. update it in the history.
+    this.historyCursor = this.history.length - 1;
+    this.history[this.historyCursor] = statement.value;
+  }
+
+  // should we submit?
+  var ctrlEnter = (document.getElementById('submit_key').value == 'ctrl-enter');
+  if (event.keyCode == 13 /* enter */ && !event.altKey && !event.shiftKey &&
+      event.ctrlKey == ctrlEnter) {
+    return this.runStatement();
+  }
+};
+
+/**
+ * The XmlHttpRequest callback. If the request succeeds, it adds the command
+ * and its resulting output to the shell history div.
+ *
+ * @param {XmlHttpRequest} req the XmlHttpRequest we used to send the current
+ *     statement to the server
+ */
+shell.done = function(req) {
+  if (req.readyState == this.DONE_STATE) {
+    var statement = document.getElementById('statement')
+    statement.className = 'prompt';
+
+    // add the command to the shell output
+    var output = document.getElementById('output');
+
+    output.value += '\n>>> ' + statement.value;
+    statement.value = '';
+
+    // add a new history element
+    this.history.push('');
+    this.historyCursor = this.history.length - 1;
+
+    // add the command's result
+    var result = req.responseText.replace(/^\s*|\s*$/g, '');  // trim whitespace
+    if (result != '')
+      output.value += '\n' + result;
+
+    // scroll to the bottom
+    output.scrollTop = output.scrollHeight;
+    if (output.createTextRange) {
+      var range = output.createTextRange();
+      range.collapse(false);
+      range.select();
+    }
+  }
+};
+
+/**
+ * This is the form's onsubmit handler. It sends the python statement to the
+ * server, and registers shell.done() as the callback to run when it returns.
+ *
+ * @return {Boolean} false to tell the browser not to submit the form.
+ */
+shell.runStatement = function() {
+  var form = document.getElementById('form');
+
+  // build a XmlHttpRequest
+  var req = this.getXmlHttpRequest();
+  if (!req) {
+    document.getElementById('ajax-status').innerHTML =
+        "<span class='error'>Your browser doesn't support AJAX. :(</span>";
+    return false;
+  }
+
+  req.onreadystatechange = function() { shell.done(req); };
+
+  // build the query parameter string
+  var params = '';
+  for (i = 0; i < form.elements.length; i++) {
+    var elem = form.elements[i];
+    if (elem.type != 'submit' && elem.type != 'button' && elem.id != 'caret') {
+      var value = escape(elem.value).replace(/\+/g, '%2B'); // escape ignores +
+      params += '&' + elem.name + '=' + value;
+    }
+  }
+
+  // send the request and tell the user.
+  document.getElementById('statement').className = 'prompt processing';
+  req.open(form.method, form.action + '?' + params, true);
+  req.setRequestHeader('Content-type',
+                       'application/x-www-form-urlencoded;charset=UTF-8');
+  req.send(null);
+
+  return false;
+};
diff --git a/tools/scripts/app_engine_server/gae_shell/static/spinner.gif b/tools/scripts/app_engine_server/gae_shell/static/spinner.gif
new file mode 100644
index 0000000..3e58d6e
--- /dev/null
+++ b/tools/scripts/app_engine_server/gae_shell/static/spinner.gif
Binary files differ
diff --git a/tools/scripts/app_engine_server/gae_shell/templates/shell.html b/tools/scripts/app_engine_server/gae_shell/templates/shell.html
new file mode 100644
index 0000000..123b200
--- /dev/null
+++ b/tools/scripts/app_engine_server/gae_shell/templates/shell.html
@@ -0,0 +1,122 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<title> Interactive Shell </title>
+<script type="text/javascript" src="/gae_shell/static/shell.js"></script>
+<style type="text/css">
+body {
+  font-family: monospace;
+  font-size: 10pt;
+}
+
+p {
+  margin: 0.5em;
+}
+
+.prompt, #output {
+  width: 45em;
+  border: 1px solid silver;
+  background-color: #f5f5f5;
+  font-size: 10pt;
+  margin: 0.5em;
+  padding: 0.5em;
+  padding-right: 0em;
+  overflow-x: hidden;
+}
+
+#toolbar {
+  margin-left: 0.5em;
+  padding-left: 0.5em;
+}
+
+#caret {
+  width: 2.5em;
+  margin-right: 0px;
+  padding-right: 0px;
+  border-right: 0px;
+}
+
+#statement {
+  width: 43em;
+  margin-left: -1em;
+  padding-left: 0px;
+  border-left: 0px;
+  background-position: top right;
+  background-repeat: no-repeat;
+}
+
+.processing {
+  background-image: url("/gae_shell/static/spinner.gif");
+}
+
+#ajax-status {
+  font-weight: bold;
+}
+
+.message {
+  color: #8AD;
+  font-weight: bold;
+  font-style: italic;
+}
+
+.error {
+  color: #F44;
+}
+
+.username {
+  font-weight: bold;
+}
+
+</style>
+</head>
+
+<body>
+
+<p> Interactive server-side Python shell for
+<a href="http://code.google.com/appengine/">Google App Engine</a>.
+(<a href="http://code.google.com/p/google-app-engine-samples/">source</a>)
+</p>
+
+<textarea id="output" rows="22" readonly="readonly">
+{{ server_software }}
+Python {{ python_version }}
+</textarea>
+
+<form id="form" action="shell.do" method="get">
+  <nobr>
+  <textarea class="prompt" id="caret" readonly="readonly" rows="4"
+            onfocus="document.getElementById('statement').focus()"
+            >&gt;&gt;&gt;</textarea>
+  <textarea class="prompt" name="statement" id="statement" rows="4"
+            onkeypress="return shell.onPromptKeyPress(event);"></textarea>
+  </nobr>
+  <input type="hidden" name="session" value="{{ session }}" />
+  <input type="submit" style="display: none" />
+</form>
+
+<p id="ajax-status"></p>
+
+<p id="toolbar">
+{% if user %}
+  <span class="username">{{ user.nickname }}</span>
+  (<a href="{{ logout_url }}">log out</a>)
+{% else %}
+  <a href="{{ login_url }}">log in</a>
+{% endif %}
+ | Ctrl-Up/Down for history |
+<select id="submit_key">
+  <option value="enter">Enter</option>
+  <option value="ctrl-enter" selected="selected">Ctrl-Enter</option>
+</select>
+<label for="submit_key">submits</label>
+</p>
+
+<script type="text/javascript">
+document.getElementById('statement').focus();
+</script>
+
+</body>
+</html>
+
diff --git a/tools/scripts/app_engine_server/index.yaml b/tools/scripts/app_engine_server/index.yaml
new file mode 100644
index 0000000..8e6046d
--- /dev/null
+++ b/tools/scripts/app_engine_server/index.yaml
@@ -0,0 +1,12 @@
+indexes:
+
+# AUTOGENERATED
+
+# This index.yaml is automatically updated whenever the dev_appserver
+# detects that a new type of query is run.  If you want to manage the
+# index.yaml file manually, remove the above marker line (the line
+# saying "# AUTOGENERATED").  If you want to manage some indexes
+# manually, move them above the marker line.  The index.yaml file is
+# automatically uploaded to the admin console when you next deploy
+# your application using appcfg.py.
+
diff --git a/tools/scripts/app_engine_server/memcache_zipserve.py b/tools/scripts/app_engine_server/memcache_zipserve.py
new file mode 100644
index 0000000..e11cfc5
--- /dev/null
+++ b/tools/scripts/app_engine_server/memcache_zipserve.py
@@ -0,0 +1,412 @@
+#!/usr/bin/env python
+#
+# Copyright 2009 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.
+#
+
+"""A class to serve pages from zip files and use memcache for performance.
+
+This contains a class and a function to create an anonymous instance of the
+class to serve HTTP GET requests. Memcache is used to increase response speed
+and lower processing cycles used in serving. Credit to Guido van Rossum and
+his implementation of zipserve which served as a reference as I wrote this.
+
+  MemcachedZipHandler: Class that serves request
+  create_handler: method to create instance of MemcachedZipHandler
+"""
+
+__author__ = 'jmatt@google.com (Justin Mattson)'
+
+import email.Utils
+import logging
+import mimetypes
+import time
+import zipfile
+
+from google.appengine.api import memcache
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import util
+
+
+def create_handler(zip_files, max_age=None, public=None):
+  """Factory method to create a MemcachedZipHandler instance.
+
+  Args:
+    zip_files: A list of file names, or a list of lists of file name, first
+        member of file mappings. See MemcachedZipHandler documentation for
+        more information about using the list of lists format
+    max_age: The maximum client-side cache lifetime
+    public: Whether this should be declared public in the client-side cache
+  Returns:
+    A MemcachedZipHandler wrapped in a pretty, anonymous bow for use with App
+    Engine
+
+  Raises:
+    ValueError: if the zip_files argument is not a list
+  """
+  # verify argument integrity. If the argument is passed in list format,
+  # convert it to list of lists format
+  
+  if zip_files and type(zip_files).__name__ == 'list':
+    num_items = len(zip_files)
+    while num_items > 0:
+      if type(zip_files[num_items - 1]).__name__ != 'list':
+        zip_files[num_items - 1] = [zip_files[num_items-1]]
+      num_items -= 1
+  else:
+    raise ValueError('File name arguments must be a list')
+
+  class HandlerWrapper(MemcachedZipHandler):
+    """Simple wrapper for an instance of MemcachedZipHandler.
+
+    I'm still not sure why this is needed
+    """
+    
+    def get(self, name):
+      self.zipfilenames = zip_files
+      self.TrueGet(name)
+      if max_age is not None:
+        MAX_AGE = max_age
+      if public is not None:
+        PUBLIC = public
+
+  return HandlerWrapper
+
+
+class MemcachedZipHandler(webapp.RequestHandler):
+  """Handles get requests for a given URL.
+
+  Serves a GET request from a series of zip files. As files are served they are
+  put into memcache, which is much faster than retreiving them from the zip
+  source file again. It also uses considerably fewer CPU cycles.
+  """
+  zipfile_cache = {}                # class cache of source zip files
+  MAX_AGE = 600                     # max client-side cache lifetime
+  PUBLIC = True                     # public cache setting
+  CACHE_PREFIX = 'cache://'         # memcache key prefix for actual URLs
+  NEG_CACHE_PREFIX = 'noncache://'  # memcache key prefix for non-existant URL
+
+  def TrueGet(self, name):
+    """The top-level entry point to serving requests.
+
+    Called 'True' get because it does the work when called from the wrapper
+    class' get method
+
+    Args:
+      name: URL requested
+
+    Returns:
+      None
+    """
+    name = self.PreprocessUrl(name)
+
+    # see if we have the page in the memcache
+    resp_data = self.GetFromCache(name)
+    if resp_data is None:
+      logging.info('Cache miss for %s', name)
+      resp_data = self.GetFromNegativeCache(name)
+      if resp_data is None:
+        resp_data = self.GetFromStore(name)
+
+        # IF we have the file, put it in the memcache
+        # ELSE put it in the negative cache
+        if resp_data is not None:
+          self.StoreOrUpdateInCache(name, resp_data)
+        else:
+          logging.info('Adding %s to negative cache, serving 404', name)
+          self.StoreInNegativeCache(name)
+          self.Write404Error()
+          return
+      else:
+        self.Write404Error()
+        return
+
+    content_type, encoding = mimetypes.guess_type(name)
+    if content_type:
+      self.response.headers['Content-Type'] = content_type
+    self.SetCachingHeaders()
+    self.response.out.write(resp_data)
+
+  def PreprocessUrl(self, name):
+    """Any preprocessing work on the URL when it comes it.
+
+    Put any work related to interpretting the incoming URL here. For example,
+    this is used to redirect requests for a directory to the index.html file
+    in that directory. Subclasses should override this method to do different
+    preprocessing.
+
+    Args:
+      name: The incoming URL
+
+    Returns:
+      The processed URL
+    """
+    # handle special case of requesting the domain itself
+    if not name:
+      name = 'index.html'
+
+    # determine if this is a request for a directory
+    final_path_segment = name
+    final_slash_offset = name.rfind('/')
+    if final_slash_offset != len(name) - 1:
+      final_path_segment = name[final_slash_offset + 1:]
+      if final_path_segment.find('.') == -1:
+        name = ''.join([name, '/'])
+
+    # if this is a directory, redirect to index.html
+    if name[len(name) - 1:] == '/':
+      return '%s%s' % (name, 'index.html')
+    else:
+      return name
+
+  def GetFromStore(self, file_path):
+    """Retrieve file from zip files.
+
+    Get the file from the source, it must not have been in the memcache. If
+    possible, we'll use the zip file index to quickly locate where the file
+    should be found. (See MapToFileArchive documentation for assumptions about
+    file ordering.) If we don't have an index or don't find the file where the
+    index says we should, look through all the zip files to find it.
+
+    Args:
+      file_path: the file that we're looking for
+
+    Returns:
+      The contents of the requested file
+    """
+    resp_data = None
+    file_itr = iter(self.zipfilenames)
+
+    # check the index, if we have one, to see what archive the file is in
+    archive_name = self.MapFileToArchive(file_path)
+    if not archive_name:
+      archive_name = file_itr.next()[0]
+    
+    while resp_data is None and archive_name:
+      zip_archive = self.LoadZipFile(archive_name)
+      if zip_archive:
+
+        # we expect some lookups will fail, and that's okay, 404s will deal
+        # with that
+        try:
+          resp_data = zip_archive.read(file_path)
+        except (KeyError, RuntimeError), err:
+          # no op
+          x = False
+        if resp_data is not None:
+          logging.info('%s read from %s', file_path, archive_name)
+          
+      try:
+        archive_name = file_itr.next()[0]
+      except (StopIteration), err:
+        archive_name = False
+
+    return resp_data
+
+  def LoadZipFile(self, zipfilename):
+    """Convenience method to load zip file.
+
+    Just a convenience method to load the zip file from the data store. This is
+    useful if we ever want to change data stores and also as a means of
+    dependency injection for testing. This method will look at our file cache
+    first, and then load and cache the file if there's a cache miss
+
+    Args:
+      zipfilename: the name of the zip file to load
+
+    Returns:
+      The zip file requested, or None if there is an I/O error
+    """
+    zip_archive = None
+    zip_archive = self.zipfile_cache.get(zipfilename)
+    if zip_archive is None:
+      try:
+        zip_archive = zipfile.ZipFile(zipfilename)
+        self.zipfile_cache[zipfilename] = zip_archive
+      except (IOError, RuntimeError), err:
+        logging.error('Can\'t open zipfile %s, cause: %s' % (zipfilename,
+                                                             err))
+    return zip_archive
+
+  def MapFileToArchive(self, file_path):
+    """Given a file name, determine what archive it should be in.
+
+    This method makes two critical assumptions.
+    (1) The zip files passed as an argument to the handler, if concatenated
+        in that same order, would result in a total ordering
+        of all the files. See (2) for ordering type.
+    (2) Upper case letters before lower case letters. The traversal of a
+        directory tree is depth first. A parent directory's files are added
+        before the files of any child directories
+
+    Args:
+      file_path: the file to be mapped to an archive
+
+    Returns:
+      The name of the archive where we expect the file to be
+    """
+    num_archives = len(self.zipfilenames)
+    while num_archives > 0:
+      target = self.zipfilenames[num_archives - 1]
+      if len(target) > 1:
+        if self.CompareFilenames(target[1], file_path) >= 0:
+          return target[0]
+      num_archives -= 1
+
+    return None
+
+  def CompareFilenames(self, file1, file2):
+    """Determines whether file1 is lexigraphically 'before' file2.
+
+    WARNING: This method assumes that paths are output in a depth-first,
+    with parent directories' files stored before childs'
+
+    We say that file1 is lexigraphically before file2 if the last non-matching
+    path segment of file1 is alphabetically before file2.
+    
+    Args:
+      file1: the first file path
+      file2: the second file path
+
+    Returns:
+      A positive number if file1 is before file2
+      A negative number if file2 is before file1
+      0 if filenames are the same
+    """
+    f1_segments = file1.split('/')
+    f2_segments = file2.split('/')
+
+    segment_ptr = 0
+    while (segment_ptr < len(f1_segments) and
+           segment_ptr < len(f2_segments) and
+           f1_segments[segment_ptr] == f2_segments[segment_ptr]):
+      segment_ptr += 1
+
+    if len(f1_segments) == len(f2_segments):
+
+      # we fell off the end, the paths much be the same
+      if segment_ptr == len(f1_segments):
+        return 0
+
+      # we didn't fall of the end, compare the segments where they differ
+      if f1_segments[segment_ptr] < f2_segments[segment_ptr]:
+        return 1
+      elif f1_segments[segment_ptr] > f2_segments[segment_ptr]:
+        return -1
+      else:
+        return 0
+
+      # the number of segments differs, we either mismatched comparing
+      # directories, or comparing a file to a directory
+    else:
+
+      # IF we were looking at the last segment of one of the paths,
+      # the one with fewer segments is first because files come before
+      # directories
+      # ELSE we just need to compare directory names
+      if (segment_ptr + 1 == len(f1_segments) or
+          segment_ptr + 1 == len(f2_segments)):
+        return len(f2_segments) - len(f1_segments)
+      else:
+        if f1_segments[segment_ptr] < f2_segments[segment_ptr]:
+          return 1
+        elif f1_segments[segment_ptr] > f2_segments[segment_ptr]:
+          return -1
+        else:
+          return 0
+
+  def SetCachingHeaders(self):
+    """Set caching headers for the request."""
+    max_age = self.MAX_AGE
+    self.response.headers['Expires'] = email.Utils.formatdate(
+        time.time() + max_age, usegmt=True)
+    cache_control = []
+    if self.PUBLIC:
+      cache_control.append('public')
+    cache_control.append('max-age=%d' % max_age)
+    self.response.headers['Cache-Control'] = ', '.join(cache_control)
+
+  def GetFromCache(self, filename):
+    """Get file from memcache, if available.
+
+    Args:
+      filename: The URL of the file to return
+
+    Returns:
+      The content of the file
+    """
+    return memcache.get('%s%s' % (self.CACHE_PREFIX, filename))
+
+  def StoreOrUpdateInCache(self, filename, data):
+    """Store data in the cache.
+
+    Store a piece of data in the memcache. Memcache has a maximum item size of
+    1*10^6 bytes. If the data is too large, fail, but log the failure. Future
+    work will consider compressing the data before storing or chunking it
+
+    Args:
+      filename: the name of the file to store
+      data: the data of the file
+
+    Returns:
+      None
+    """
+    try:
+      if not memcache.add('%s%s' % (self.CACHE_PREFIX, filename), data):
+        memcache.replace('%s%s' % (self.CACHE_PREFIX, filename), data)
+    except (ValueError), err:
+      logging.warning('Data size too large to cache\n%s' % err)
+
+  def Write404Error(self):
+    """Ouptut a simple 404 response."""
+    self.error(404)
+    self.response.out.write(
+        ''.join(['<html><head><title>404: Not Found</title></head>',
+                 '<body><b><h2>Error 404</h2><br/>',
+                 'File not found</b></body></html>']))
+
+  def StoreInNegativeCache(self, filename):
+    """If a non-existant URL is accessed, cache this result as well.
+
+    Future work should consider setting a maximum negative cache size to
+    prevent it from from negatively impacting the real cache.
+
+    Args:
+      filename: URL to add ot negative cache
+
+    Returns:
+      None
+    """
+    memcache.add('%s%s' % (self.NEG_CACHE_PREFIX, filename), -1)
+
+  def GetFromNegativeCache(self, filename):
+    """Retrieve from negative cache.
+
+    Args:
+      filename: URL to retreive
+
+    Returns:
+      The file contents if present in the negative cache.
+    """
+    return memcache.get('%s%s' % (self.NEG_CACHE_PREFIX, filename))
+
+
+def main():
+  application = webapp.WSGIApplication([('/([^/]+)/(.*)',
+                                         MemcachedZipHandler)])
+  util.run_wsgi_app(application)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/scripts/build.alias.template b/tools/scripts/build.alias.template
new file mode 100644
index 0000000..b605295
--- /dev/null
+++ b/tools/scripts/build.alias.template
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="PROJECT_NAME" default="package">
+
+    <!-- The build.properties file can be created by you and is never touched
+         by activitycreator. If you want to manually set properties, this is
+         the best place to set them. -->
+    <property file="build.properties"/>
+
+    <!-- The default.properties file is created and updated by activitycreator.
+         It will set any properties not already defined by build.properties. -->
+    <property file="default.properties"/>
+
+    <!-- ************************************************************************************* -->
+    <!-- Import the default Android build rules. 
+         This requires ant 1.6.0 or above. -->
+
+    <import file="${sdk-folder}/tools/lib/alias_rules.xml" />
+
+</project>
diff --git a/tools/scripts/build.template b/tools/scripts/build.template
new file mode 100644
index 0000000..7939e6c
--- /dev/null
+++ b/tools/scripts/build.template
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="PROJECT_NAME" default="help">
+
+    <!-- The local.properties file is created and updated by the 'android' tool.
+         It contain the path to the SDK. It should *NOT* be checked in in Version
+         Control Systems. -->
+    <property file="local.properties"/>
+
+    <!-- The build.properties file can be created by you and is never touched
+         by the 'android' tool. This is the place to change some of the default property values
+         used by the Ant rules.
+         Here are some properties you may want to change/update:
+
+         application-package
+             the name of your application package as defined in the manifest. Used by the
+             'uninstall' rule.
+         source-folder
+             the name of the source folder. Default is 'src'.
+         out-folder
+             the name of the output folder. Default is 'bin'.
+
+         Properties related to the SDK location or the project target should be updated
+          using the 'android' tool with the 'update' action.
+
+         This file is an integral part of the build system for your application and
+         should be checked in in Version Control Systems.
+
+         -->
+    <property file="build.properties"/>
+
+    <!-- The default.properties file is created and updated by the 'android' tool, as well
+         as ADT. 
+         This file is an integral part of the build system for your application and
+         should be checked in in Version Control Systems. -->
+    <property file="default.properties"/>
+
+    <!-- Custom Android task to deal with the project target, and import the proper rules.
+         This requires ant 1.6.0 or above. -->
+    <path id="android.antlibs">
+        <pathelement path="${sdk-location}/tools/lib/anttasks.jar" />
+        <pathelement path="${sdk-location}/tools/lib/sdklib.jar" />
+        <pathelement path="${sdk-location}/tools/lib/androidprefs.jar" />
+        <pathelement path="${sdk-location}/tools/lib/apkbuilder.jar" />
+        <pathelement path="${sdk-location}/tools/lib/jarutils.jar" />
+    </path>
+
+    <taskdef name="setup"
+        classname="com.android.ant.SetupTask"
+        classpathref="android.antlibs"/>
+
+    <!-- Execute the Android Setup task that will setup some properties specific to the target,
+         and import the rules files.
+         To customize the rules, copy/paste them below the task, and disable import by setting
+         the import attribute to false:
+            <setup import="false" />
+         
+         This will ensure that the properties are setup correctly but that your customized
+         targets are used.
+    -->
+    <setup />
+</project>
diff --git a/tools/scripts/combine_sdks.sh b/tools/scripts/combine_sdks.sh
new file mode 100755
index 0000000..ebaa1c6
--- /dev/null
+++ b/tools/scripts/combine_sdks.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+function replace()
+{
+        echo replacing $1
+        rm $V -rf "$UNZIPPED_BASE_DIR"/$1
+        cp $V -rf "$UNZIPPED_IMAGE_DIR"/$1 "$UNZIPPED_BASE_DIR"/$1
+}
+
+V=""
+Q="-q"
+if [ "$1" == "-v" ]; then
+    V="-v"
+    Q=""
+    shift
+fi
+
+NOZIP=""
+if [ "$1" == "-nozip" ]; then
+    NOZIP="1"
+    shift
+fi
+
+BASE="$1"
+IMAGES="$2"
+OUTPUT="$3"
+
+if [[ -z "$BASE" || -z "$IMAGES" || -z "$OUTPUT" ]] ; then
+    echo "usage: combine_sdks.sh [-v] [-nozip] BASE IMAGES OUTPUT"
+    echo
+    echo "  BASE and IMAGES should be sdk zip files.  The system image files,"
+    echo "  emulator and other runtime files will be copied from IMAGES and"
+    echo "  everything else will be copied from BASE.  All of this will be"
+    echo "  bundled into OUTPUT and zipped up again (unless -nozip is specified)."
+    echo
+    exit 1
+fi
+
+TMP=$(mktemp -d)
+
+TMP_ZIP=tmp.zip
+
+# determine executable extension
+case `uname -s` in
+    *_NT-*)  # for Windows
+        EXE=.exe
+        ;;
+    *)
+        EXE=
+        ;;
+esac
+
+BASE_DIR="$TMP"/base
+IMAGES_DIR="$TMP"/images
+OUTPUT_TMP_ZIP="$BASE_DIR/$TMP_ZIP"
+
+unzip $Q "$BASE"   -d "$BASE_DIR"
+unzip $Q "$IMAGES" -d "$IMAGES_DIR"
+
+UNZIPPED_BASE_DIR=$(echo "$BASE_DIR"/*)
+UNZIPPED_IMAGE_DIR=$(echo "$IMAGES_DIR"/*)
+
+#
+# The commands to copy over the files that we want
+#
+
+# replace tools/emulator # at this time we do not want the exe from SDK1.x
+replace tools/lib/images
+replace tools/lib/res
+replace tools/lib/fonts
+replace tools/lib/layoutlib.jar
+replace docs
+replace android.jar
+
+for i in widgets categories broadcast_actions service_actions; do
+    replace tools/lib/$i.txt
+done
+
+if [ -d "$UNZIPPED_BASE_DIR"/usb_driver ]; then
+    replace usb_driver
+fi
+
+#
+# end
+#
+
+if [ -z "$NOZIP" ]; then
+    pushd "$BASE_DIR" &> /dev/null
+        # rename the directory to the leaf minus the .zip of OUTPUT
+        LEAF=$(echo "$OUTPUT" | sed -e "s:.*\.zip/::" | sed -e "s:.zip$::")
+        mv * "$LEAF"
+        # zip it
+        zip $V -qr "$TMP_ZIP" "$LEAF"
+    popd &> /dev/null
+
+    cp $V "$OUTPUT_TMP_ZIP" "$OUTPUT"
+    echo "Combined SDK available at $OUTPUT"
+else
+    OUT_DIR="${OUTPUT//.zip/}"
+    mv $V "$BASE_DIR"/* "$OUT_DIR"
+    echo "Unzipped combined SDK available at $OUT_DIR"
+fi
+
+rm $V -rf "$TMP"
+
diff --git a/tools/scripts/divide_and_compress.py b/tools/scripts/divide_and_compress.py
new file mode 100755
index 0000000..d369be4
--- /dev/null
+++ b/tools/scripts/divide_and_compress.py
@@ -0,0 +1,352 @@
+#!/usr/bin/python2.4
+#
+# Copyright (C) 2008 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.
+#
+
+"""Module to compress directories in to series of zip files.
+
+This module will take a directory and compress all its contents, including
+child directories into a series of zip files named N.zip where 'N' ranges from
+0 to infinity. The zip files will all be below a certain specified maximum
+threshold.
+
+The directory is compressed with a depth first traversal, each directory's
+file contents being compressed as it is visisted, before the compression of any
+child directory's contents. In this way the files within an archive are ordered
+and the archives themselves are ordered.
+
+The class also constructs a 'main.py' file intended for use with Google App
+Engine with a custom App Engine program not currently distributed with this
+code base. The custom App Engine runtime can leverage the index files written
+out by this class to more quickly locate which zip file to serve a given URL
+from.
+"""
+
+__author__ = 'jmatt@google.com (Justin Mattson)'
+
+from optparse import OptionParser
+import os
+import stat
+import sys
+import zipfile
+from zipfile import ZipFile
+import divide_and_compress_constants
+
+
+def Main(argv):
+  parser = CreateOptionsParser()
+  (options, args) = parser.parse_args()
+  VerifyArguments(options, parser)
+  zipper = DirectoryZipper(options.destination, 
+                           options.sourcefiles, 
+                           ParseSize(options.filesize),
+                           options.compress)
+  zipper.StartCompress()
+  
+
+def CreateOptionsParser():
+  rtn = OptionParser()
+  rtn.add_option('-s', '--sourcefiles', dest='sourcefiles', default=None,
+                 help='The directory containing the files to compress')
+  rtn.add_option('-d', '--destination', dest='destination', default=None,
+                 help=('Where to put the archive files, this should not be'
+                       ' a child of where the source files exist.'))
+  rtn.add_option('-f', '--filesize', dest='filesize', default='1M',
+                 help=('Maximum size of archive files. A number followed by' 
+                       'a magnitude indicator, eg. 1000000B == one million '
+                       'BYTES, 500K == five hundred KILOBYTES, 1.2M == one '
+                       'point two MEGABYTES. 1M == 1048576 BYTES'))
+  rtn.add_option('-n', '--nocompress', action='store_false', dest='compress',
+                 default=True, 
+                 help=('Whether the archive files should be compressed, or '
+                       'just a concatenation of the source files'))
+  return rtn
+
+
+def VerifyArguments(options, parser):
+  try:
+    if options.sourcefiles is None or options.destination is None:
+      parser.print_help()
+      sys.exit(-1)
+  except (AttributeError), err:
+    parser.print_help()
+    sys.exit(-1)
+
+
+def ParseSize(size_str):
+  if len(size_str) < 2:
+    raise ValueError(('filesize argument not understood, please include'
+                      ' a numeric value and magnitude indicator'))
+  magnitude = size_str[len(size_str)-1:]
+  if not magnitude in ('K', 'B', 'M'):
+    raise ValueError(('filesize magnitude indicator not valid, must be \'K\','
+                      '\'B\', or \'M\''))
+  numeral = float(size_str[0:len(size_str)-1])
+  if magnitude == 'K':
+    numeral *= 1024
+  elif magnitude == 'M':
+    numeral *= 1048576
+  return int(numeral)
+
+
+class DirectoryZipper(object):
+  """Class to compress a directory and all its sub-directories."""  
+  current_archive = None
+  output_dir = None
+  base_path = None
+  max_size = None
+  compress = None
+  index_fp = None
+
+  def __init__(self, output_path, base_dir, archive_size, enable_compression):
+    """DirectoryZipper constructor.
+
+    Args:
+      output_path: the path to write the archives and index file to
+      base_dir: the directory to compress
+      archive_size: the maximum size, in bytes, of a single archive file
+      enable_compression: whether or not compression should be enabled, if
+        disabled, the files will be written into an uncompresed zip
+    """
+    self.output_dir = output_path
+    self.current_archive = '0.zip'
+    self.base_path = base_dir
+    self.max_size = archive_size
+    self.compress = enable_compression
+
+  def StartCompress(self):
+    """Start compress of the directory.
+
+    This will start the compression process and write the archives to the
+    specified output directory. It will also produce an 'index.txt' file in the
+    output directory that maps from file to archive.
+    """
+    self.index_fp = open(''.join([self.output_dir, 'main.py']), 'w')
+    self.index_fp.write(divide_and_compress_constants.file_preamble)
+    os.path.walk(self.base_path, self.CompressDirectory, 1)
+    self.index_fp.write(divide_and_compress_constants.file_endpiece)
+    self.index_fp.close()
+
+  def RemoveLastFile(self, archive_path=None):
+    """Removes the last item in the archive.
+
+    This removes the last item in the archive by reading the items out of the
+    archive, adding them to a new archive, deleting the old archive, and
+    moving the new archive to the location of the old archive.
+
+    Args:
+      archive_path: Path to the archive to modify. This archive should not be
+        open elsewhere, since it will need to be deleted.
+    Return:
+      A new ZipFile object that points to the modified archive file
+    """
+    if archive_path is None:
+      archive_path = ''.join([self.output_dir, self.current_archive])
+
+    # Move the old file and create a new one at its old location
+    ext_offset = archive_path.rfind('.')
+    old_archive = ''.join([archive_path[0:ext_offset], '-old',
+                           archive_path[ext_offset:]])
+    os.rename(archive_path, old_archive)
+    old_fp = self.OpenZipFileAtPath(old_archive, mode='r')
+
+    if self.compress:
+      new_fp = self.OpenZipFileAtPath(archive_path,
+                                      mode='w',
+                                      compress=zipfile.ZIP_DEFLATED)
+    else:
+      new_fp = self.OpenZipFileAtPath(archive_path,
+                                      mode='w',
+                                      compress=zipfile.ZIP_STORED)
+    
+    # Read the old archive in a new archive, except the last one
+    zip_members = enumerate(old_fp.infolist())
+    num_members = len(old_fp.infolist())
+    while num_members > 1:
+      this_member = zip_members.next()[1]
+      new_fp.writestr(this_member.filename, old_fp.read(this_member.filename))
+      num_members -= 1
+
+    # Close files and delete the old one
+    old_fp.close()
+    new_fp.close()
+    os.unlink(old_archive)
+
+  def OpenZipFileAtPath(self, path, mode=None, compress=zipfile.ZIP_DEFLATED):
+    """This method is mainly for testing purposes, eg dependency injection."""
+    if mode is None:
+      if os.path.exists(path):
+        mode = 'a'
+      else:
+        mode = 'w'
+
+    if mode == 'r':
+      return ZipFile(path, mode)
+    else:
+      return ZipFile(path, mode, compress)
+
+  def CompressDirectory(self, irrelevant, dir_path, dir_contents):
+    """Method to compress the given directory.
+
+    This method compresses the directory 'dir_path'. It will add to an existing
+    zip file that still has space and create new ones as necessary to keep zip
+    file sizes under the maximum specified size. This also writes out the
+    mapping of files to archives to the self.index_fp file descriptor
+
+    Args:
+      irrelevant: a numeric identifier passed by the os.path.walk method, this
+        is not used by this method
+      dir_path: the path to the directory to compress
+      dir_contents: a list of directory contents to be compressed
+    """
+    
+    # construct the queue of files to be added that this method will use
+    # it seems that dir_contents is given in reverse alphabetical order,
+    # so put them in alphabetical order by inserting to front of the list
+    dir_contents.sort()
+    zip_queue = []
+    if dir_path[len(dir_path) - 1:] == os.sep:
+      for filename in dir_contents:
+        zip_queue.append(''.join([dir_path, filename]))
+    else:
+      for filename in dir_contents:
+        zip_queue.append(''.join([dir_path, os.sep, filename]))
+    compress_bit = zipfile.ZIP_DEFLATED
+    if not self.compress:
+      compress_bit = zipfile.ZIP_STORED
+
+    # zip all files in this directory, adding to existing archives and creating
+    # as necessary
+    while len(zip_queue) > 0:
+      target_file = zip_queue[0]
+      if os.path.isfile(target_file):
+        self.AddFileToArchive(target_file, compress_bit)
+        
+        # see if adding the new file made our archive too large
+        if not self.ArchiveIsValid():
+          
+          # IF fixing fails, the last added file was to large, skip it
+          # ELSE the current archive filled normally, make a new one and try
+          #  adding the file again
+          if not self.FixArchive('SIZE'):
+            zip_queue.pop(0)
+          else:
+            self.current_archive = '%i.zip' % (
+                int(self.current_archive[
+                    0:self.current_archive.rfind('.zip')]) + 1)
+        else:
+
+          # if this the first file in the archive, write an index record
+          self.WriteIndexRecord()
+          zip_queue.pop(0)
+      else:
+        zip_queue.pop(0)
+
+  def WriteIndexRecord(self):
+    """Write an index record to the index file.
+
+    Only write an index record if this is the first file to go into archive
+
+    Returns:
+      True if an archive record is written, False if it isn't
+    """
+    archive = self.OpenZipFileAtPath(
+        ''.join([self.output_dir, self.current_archive]), 'r')
+    archive_index = archive.infolist()
+    if len(archive_index) == 1:
+      self.index_fp.write(
+          '[\'%s\', \'%s\'],\n' % (self.current_archive,
+                                   archive_index[0].filename))
+      archive.close()
+      return True
+    else:
+      archive.close()
+      return False
+
+  def FixArchive(self, problem):
+    """Make the archive compliant.
+
+    Args:
+      problem: the reason the archive is invalid
+
+    Returns:
+      Whether the file(s) removed to fix the archive could conceivably be
+      in an archive, but for some reason can't be added to this one.
+    """
+    archive_path = ''.join([self.output_dir, self.current_archive])
+    rtn_value = None
+    
+    if problem == 'SIZE':
+      archive_obj = self.OpenZipFileAtPath(archive_path, mode='r')
+      num_archive_files = len(archive_obj.infolist())
+      
+      # IF there is a single file, that means its too large to compress,
+      # delete the created archive
+      # ELSE do normal finalization
+      if num_archive_files == 1:
+        print ('WARNING: %s%s is too large to store.' % (
+            self.base_path, archive_obj.infolist()[0].filename))
+        archive_obj.close()
+        os.unlink(archive_path)
+        rtn_value = False
+      else:
+        self.RemoveLastFile(''.join([self.output_dir, self.current_archive]))
+        archive_obj.close()
+        print 'Final archive size for %s is %i' % (
+            self.current_archive, os.stat(archive_path)[stat.ST_SIZE])
+        rtn_value = True
+    return rtn_value
+
+  def AddFileToArchive(self, filepath, compress_bit):
+    """Add the file at filepath to the current archive.
+
+    Args:
+      filepath: the path of the file to add
+      compress_bit: whether or not this fiel should be compressed when added
+
+    Returns:
+      True if the file could be added (typically because this is a file) or
+      False if it couldn't be added (typically because its a directory)
+    """
+    curr_archive_path = ''.join([self.output_dir, self.current_archive])
+    if os.path.isfile(filepath):
+      if os.stat(filepath)[stat.ST_SIZE] > 1048576:
+        print 'Warning: %s is potentially too large to serve on GAE' % filepath
+      archive = self.OpenZipFileAtPath(curr_archive_path,
+                                       compress=compress_bit)
+      # add the file to the archive
+      archive.write(filepath, filepath[len(self.base_path):])
+      archive.close()
+      return True
+    else:
+      return False
+
+  def ArchiveIsValid(self):
+    """Check whether the archive is valid.
+
+    Currently this only checks whether the archive is under the required size.
+    The thought is that eventually this will do additional validation
+
+    Returns:
+      True if the archive is valid, False if its not
+    """
+    archive_path = ''.join([self.output_dir, self.current_archive])
+    if os.stat(archive_path)[stat.ST_SIZE] > self.max_size:
+      return False
+    else:
+      return True
+
+if __name__ == '__main__':
+  Main(sys.argv)
diff --git a/tools/scripts/divide_and_compress_constants.py b/tools/scripts/divide_and_compress_constants.py
new file mode 100644
index 0000000..4e11b6f
--- /dev/null
+++ b/tools/scripts/divide_and_compress_constants.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python2.4
+#
+# Copyright (C) 2008 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.
+#
+
+"""Constants for the divide_and_compress script and DirectoryZipper class."""
+
+__author__ = 'jmatt@google.com (Justin Mattson)'
+
+file_preamble = ('#!/usr/bin/env python\n'
+                 '#\n'
+                 '# Copyright 2008 Google Inc.\n'
+                 '#\n'                                                       
+                 '# Licensed under the Apache License, Version 2.0 (the' 
+                 '\"License");\n'                               
+                 '# you may not use this file except in compliance with the '
+                 'License.\n'                                                 
+                 '# You may obtain a copy of the License at\n'           
+                 '#\n'
+                 '#     http://www.apache.org/licenses/LICENSE-2.0\n'
+                 '#\n'
+                 '# Unless required by applicable law or agreed to in writing,'
+                 ' software\n'                                              
+                 '# distributed under the License is distributed on an \"AS' 
+                 'IS\" BASIS,\n'
+                 '# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either '
+                 'express or implied.\n'
+                 '# See the License for the specific language governing' 
+                 ' permissions and\n'
+                 '# limitations under the License.\n'
+                 '#\n\n'
+                 'import wsgiref.handlers\n'
+                 'from google.appengine.ext import zipserve\n'
+                 'from google.appengine.ext import webapp\n'
+                 'import memcache_zipserve\n\n\n'
+                 'class MainHandler(webapp.RequestHandler):\n\n'
+                 '  def get(self):\n'
+                 '    self.response.out.write(\'Hello world!\')\n\n'
+                 'def main():\n'
+                 '  application = webapp.WSGIApplication([(\'/(.*)\','
+                 ' memcache_zipserve.create_handler([')
+
+file_endpiece = ('])),\n'
+                 '],\n'
+                 'debug=False)\n'
+                 '  wsgiref.handlers.CGIHandler().run(application)\n\n'
+                 'if __name__ == \'__main__\':\n'
+                 '  main()')
diff --git a/tools/scripts/iml.template b/tools/scripts/iml.template
new file mode 100644
index 0000000..c4fe3a3
--- /dev/null
+++ b/tools/scripts/iml.template
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module version="4" relativePaths="true" type="JAVA_MODULE">
+  <component name="ModuleRootManager" />
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+    </content>
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="android" level="project" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntryProperties />
+  </component>
+</module>
diff --git a/tools/scripts/ipr.template b/tools/scripts/ipr.template
new file mode 100644
index 0000000..cb3d65e
--- /dev/null
+++ b/tools/scripts/ipr.template
@@ -0,0 +1,232 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4" relativePaths="false">
+  <component name="AntConfiguration">
+    <defaultAnt bundledAnt="true" />
+    <buildFile url="file://$PROJECT_DIR$/build.xml">
+      <additionalClassPath />
+      <antReference projectDefault="true" />
+      <customJdkName value="" />
+      <maximumHeapSize value="128" />
+      <properties />
+    </buildFile>
+  </component>
+  <component name="BuildJarProjectSettings">
+    <option name="BUILD_JARS_ON_MAKE" value="false" />
+  </component>
+  <component name="CodeStyleProjectProfileManger">
+    <option name="PROJECT_PROFILE" />
+    <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
+  </component>
+  <component name="CodeStyleSettingsManager">
+    <option name="PER_PROJECT_SETTINGS" />
+    <option name="USE_PER_PROJECT_SETTINGS" value="false" />
+  </component>
+  <component name="CompilerConfiguration">
+    <option name="DEFAULT_COMPILER" value="Javac" />
+    <option name="DEPLOY_AFTER_MAKE" value="0" />
+    <resourceExtensions>
+      <entry name=".+\.(properties|xml|html|dtd|tld)" />
+      <entry name=".+\.(gif|png|jpeg|jpg)" />
+    </resourceExtensions>
+    <wildcardResourcePatterns>
+      <entry name="?*.properties" />
+      <entry name="?*.xml" />
+      <entry name="?*.gif" />
+      <entry name="?*.png" />
+      <entry name="?*.jpeg" />
+      <entry name="?*.jpg" />
+      <entry name="?*.html" />
+      <entry name="?*.dtd" />
+      <entry name="?*.tld" />
+    </wildcardResourcePatterns>
+  </component>
+  <component name="DataSourceManagerImpl" />
+  <component name="DependenciesAnalyzeManager">
+    <option name="myForwardDirection" value="false" />
+  </component>
+  <component name="DependencyValidationManager" />
+  <component name="EclipseCompilerSettings">
+    <option name="DEBUGGING_INFO" value="true" />
+    <option name="GENERATE_NO_WARNINGS" value="true" />
+    <option name="DEPRECATION" value="false" />
+    <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+    <option name="MAXIMUM_HEAP_SIZE" value="128" />
+  </component>
+  <component name="EclipseEmbeddedCompilerSettings">
+    <option name="DEBUGGING_INFO" value="true" />
+    <option name="GENERATE_NO_WARNINGS" value="true" />
+    <option name="DEPRECATION" value="false" />
+    <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+    <option name="MAXIMUM_HEAP_SIZE" value="128" />
+  </component>
+  <component name="EntryPointsManager">
+    <entry_points />
+  </component>
+  <component name="ExportToHTMLSettings">
+    <option name="PRINT_LINE_NUMBERS" value="false" />
+    <option name="OPEN_IN_BROWSER" value="false" />
+    <option name="OUTPUT_DIRECTORY" />
+  </component>
+  <component name="GUI Designer component loader factory" />
+  <component name="IdProvider" IDEtalkID="F6EC4D80E2C03FEF19EDD201903A6DFE" />
+  <component name="InspectionProjectProfileManager">
+    <option name="PROJECT_PROFILE" value="Project Default" />
+    <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
+    <scopes />
+    <profiles>
+      <profile version="1.0" is_locked="false">
+        <option name="myName" value="Project Default" />
+        <option name="myLocal" value="false" />
+        <used_levels>
+          <error>
+            <option name="myName" value="ERROR" />
+            <option name="myVal" value="400" />
+          </error>
+          <warning>
+            <option name="myName" value="WARNING" />
+            <option name="myVal" value="300" />
+          </warning>
+          <information>
+            <option name="myName" value="INFO" />
+            <option name="myVal" value="200" />
+          </information>
+          <server>
+            <option name="myName" value="SERVER PROBLEM" />
+            <option name="myVal" value="100" />
+          </server>
+        </used_levels>
+        <inspection_tool class="ClassReferencesSubclass" level="WARNING" enabled="true" />
+        <inspection_tool class="MissingOverrideAnnotation" level="WARNING" enabled="true" />
+        <inspection_tool class="Finalize" level="WARNING" enabled="true" />
+        <inspection_tool class="UnusedImport" level="WARNING" enabled="true" />
+        <inspection_tool class="StaticInheritance" level="WARNING" enabled="true" />
+        <inspection_tool class="RedundantMethodOverride" level="WARNING" enabled="true" />
+        <inspection_tool class="AbstractMethodCallInConstructor" level="WARNING" enabled="true" />
+        <inspection_tool class="RawUseOfParameterizedType" level="WARNING" enabled="true">
+          <option name="ignoreObjectConstruction" value="true" />
+          <option name="ignoreTypeCasts" value="false" />
+        </inspection_tool>
+        <inspection_tool class="SystemGC" level="WARNING" enabled="true" />
+        <inspection_tool class="ConstantNamingConvention" level="WARNING" enabled="true">
+          <option name="m_regex" value="[A-Z_\d]*" />
+          <option name="m_minLength" value="5" />
+          <option name="m_maxLength" value="32" />
+        </inspection_tool>
+        <inspection_tool class="EnumeratedConstantNamingConvention" level="WARNING" enabled="true">
+          <option name="m_regex" value="[A-Z][A-Za-z\d]*" />
+          <option name="m_minLength" value="5" />
+          <option name="m_maxLength" value="32" />
+        </inspection_tool>
+        <inspection_tool class="DivideByZero" level="WARNING" enabled="true" />
+        <inspection_tool class="CloneCallsConstructors" level="WARNING" enabled="true" />
+        <inspection_tool class="CloneDeclaresCloneNotSupported" level="WARNING" enabled="false" />
+        <inspection_tool class="CloneInNonCloneableClass" level="WARNING" enabled="true" />
+        <inspection_tool class="UtilityClassWithoutPrivateConstructor" level="WARNING" enabled="true">
+          <option name="ignoreClassesWithOnlyMain" value="false" />
+        </inspection_tool>
+        <inspection_tool class="UtilityClassWithPublicConstructor" level="WARNING" enabled="true" />
+        <inspection_tool class="ConditionalExpressionWithIdenticalBranches" level="WARNING" enabled="true" />
+        <inspection_tool class="CanBeFinal" level="WARNING" enabled="false">
+          <option name="REPORT_CLASSES" value="false" />
+          <option name="REPORT_METHODS" value="false" />
+          <option name="REPORT_FIELDS" value="true" />
+        </inspection_tool>
+        <inspection_tool class="ThisEscapedInConstructor" level="WARNING" enabled="true" />
+        <inspection_tool class="NonThreadSafeLazyInitialization" level="WARNING" enabled="true" />
+        <inspection_tool class="FieldMayBeStatic" level="WARNING" enabled="true" />
+        <inspection_tool class="InnerClassMayBeStatic" level="WARNING" enabled="true" />
+        <inspection_tool class="MethodMayBeStatic" level="WARNING" enabled="true">
+          <option name="m_onlyPrivateOrFinal" value="false" />
+          <option name="m_ignoreEmptyMethods" value="true" />
+        </inspection_tool>
+        <inspection_tool class="ComponentRegistrationProblems" level="ERROR" enabled="false">
+          <option name="CHECK_PLUGIN_XML" value="true" />
+          <option name="CHECK_JAVA_CODE" value="true" />
+          <option name="CHECK_ACTIONS" value="true" />
+        </inspection_tool>
+        <inspection_tool class="ComponentNotRegistered" level="WARNING" enabled="false">
+          <option name="CHECK_ACTIONS" value="true" />
+          <option name="IGNORE_NON_PUBLIC" value="true" />
+        </inspection_tool>
+        <inspection_tool class="BusyWait" level="WARNING" enabled="true" />
+        <inspection_tool class="UnconditionalWait" level="WARNING" enabled="true" />
+        <inspection_tool class="WaitNotInLoop" level="WARNING" enabled="true" />
+      </profile>
+    </profiles>
+  </component>
+  <component name="JavacSettings">
+    <option name="DEBUGGING_INFO" value="true" />
+    <option name="GENERATE_NO_WARNINGS" value="false" />
+    <option name="DEPRECATION" value="true" />
+    <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+    <option name="MAXIMUM_HEAP_SIZE" value="128" />
+  </component>
+  <component name="JavadocGenerationManager">
+    <option name="OUTPUT_DIRECTORY" />
+    <option name="OPTION_SCOPE" value="protected" />
+    <option name="OPTION_HIERARCHY" value="true" />
+    <option name="OPTION_NAVIGATOR" value="true" />
+    <option name="OPTION_INDEX" value="true" />
+    <option name="OPTION_SEPARATE_INDEX" value="true" />
+    <option name="OPTION_DOCUMENT_TAG_USE" value="false" />
+    <option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false" />
+    <option name="OPTION_DOCUMENT_TAG_VERSION" value="false" />
+    <option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true" />
+    <option name="OPTION_DEPRECATED_LIST" value="true" />
+    <option name="OTHER_OPTIONS" value="" />
+    <option name="HEAP_SIZE" />
+    <option name="LOCALE" />
+    <option name="OPEN_IN_BROWSER" value="true" />
+  </component>
+  <component name="JikesSettings">
+    <option name="JIKES_PATH" value="" />
+    <option name="DEBUGGING_INFO" value="true" />
+    <option name="DEPRECATION" value="true" />
+    <option name="GENERATE_NO_WARNINGS" value="false" />
+    <option name="IS_EMACS_ERRORS_MODE" value="true" />
+    <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+  </component>
+  <component name="LogConsolePreferences">
+    <option name="FILTER_ERRORS" value="false" />
+    <option name="FILTER_WARNINGS" value="false" />
+    <option name="FILTER_INFO" value="true" />
+    <option name="CUSTOM_FILTER" />
+  </component>
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/ACTIVITY_NAME.iml" filepath="$PROJECT_DIR$/ACTIVITY_NAME.iml" />
+    </modules>
+  </component>
+  <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.5" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/bin" />
+  </component>
+  <component name="ProjectRunConfigurationManager" />
+  <component name="RmicSettings">
+    <option name="IS_EANABLED" value="false" />
+    <option name="DEBUGGING_INFO" value="true" />
+    <option name="GENERATE_NO_WARNINGS" value="false" />
+    <option name="GENERATE_IIOP_STUBS" value="false" />
+    <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+  </component>
+  <component name="StarteamVcsAdapter" />
+  <component name="XSLT-Support.FileAssociationsManager" />
+  <component name="com.intellij.jsf.UserDefinedFacesConfigs">
+    <option name="USER_DEFINED_CONFIGS">
+      <value>
+        <list size="0" />
+      </value>
+    </option>
+  </component>
+  <component name="libraryTable">
+    <library name="android">
+      <CLASSES>
+        <root url="jar://ANDROID_SDK_FOLDER/android.jar!/" />
+      </CLASSES>
+      <JAVADOC>
+        <root url="file://ANDROID_SDK_FOLDER/docs/reference" />
+      </JAVADOC>
+      <SOURCES />
+    </library>
+  </component>
+  <UsedPathMacros />
+</project>
diff --git a/tools/scripts/iws.template b/tools/scripts/iws.template
new file mode 100644
index 0000000..67d2053
--- /dev/null
+++ b/tools/scripts/iws.template
@@ -0,0 +1,470 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4" relativePaths="false">
+  <component name="AntConfiguration">
+    <defaultAnt bundledAnt="true" />
+    <buildFile url="file://$PROJECT_DIR$/build.xml">
+      <additionalClassPath />
+      <antReference projectDefault="true" />
+      <customJdkName value="" />
+      <maximumHeapSize value="128" />
+      <properties />
+    </buildFile>
+  </component>
+  <component name="BookmarkManager" />
+  <component name="ChangeBrowserSettings">
+    <option name="MAIN_SPLITTER_PROPORTION" value="0.3" />
+    <option name="MESSAGES_SPLITTER_PROPORTION" value="0.8" />
+    <option name="USE_DATE_BEFORE_FILTER" value="false" />
+    <option name="USE_DATE_AFTER_FILTER" value="false" />
+    <option name="USE_CHANGE_BEFORE_FILTER" value="false" />
+    <option name="USE_CHANGE_AFTER_FILTER" value="false" />
+    <option name="DATE_BEFORE" value="" />
+    <option name="DATE_AFTER" value="" />
+    <option name="CHANGE_BEFORE" value="" />
+    <option name="CHANGE_AFTER" value="" />
+    <option name="USE_USER_FILTER" value="false" />
+    <option name="USER" value="" />
+  </component>
+  <component name="ChangeListManager">
+    <list default="true" name="Default" comment="" />
+  </component>
+  <component name="ChangeListSynchronizer" />
+  <component name="ChangesViewManager" flattened_view="true" />
+  <component name="CheckinPanelState" />
+  <component name="Commander">
+    <leftPanel />
+    <rightPanel />
+    <splitter proportion="0.5" />
+  </component>
+  <component name="CompilerWorkspaceConfiguration">
+    <option name="COMPILE_IN_BACKGROUND" value="false" />
+    <option name="AUTO_SHOW_ERRORS_IN_EDITOR" value="true" />
+    <option name="CLOSE_MESSAGE_VIEW_IF_SUCCESS" value="true" />
+    <option name="COMPILE_DEPENDENT_FILES" value="false" />
+    <option name="CLEAR_OUTPUT_DIRECTORY" value="false" />
+    <option name="ASSERT_NOT_NULL" value="true" />
+  </component>
+  <component name="CoverageDataManager" />
+  <component name="Cvs2Configuration">
+    <option name="PRUNE_EMPTY_DIRECTORIES" value="true" />
+    <option name="MERGING_MODE" value="0" />
+    <option name="MERGE_WITH_BRANCH1_NAME" value="HEAD" />
+    <option name="MERGE_WITH_BRANCH2_NAME" value="HEAD" />
+    <option name="RESET_STICKY" value="false" />
+    <option name="CREATE_NEW_DIRECTORIES" value="true" />
+    <option name="DEFAULT_TEXT_FILE_SUBSTITUTION" value="kv" />
+    <option name="PROCESS_UNKNOWN_FILES" value="false" />
+    <option name="PROCESS_DELETED_FILES" value="false" />
+    <option name="PROCESS_IGNORED_FILES" value="false" />
+    <option name="RESERVED_EDIT" value="false" />
+    <option name="CHECKOUT_DATE_OR_REVISION_SETTINGS">
+      <value>
+        <option name="BRANCH" value="" />
+        <option name="DATE" value="" />
+        <option name="USE_BRANCH" value="false" />
+        <option name="USE_DATE" value="false" />
+      </value>
+    </option>
+    <option name="UPDATE_DATE_OR_REVISION_SETTINGS">
+      <value>
+        <option name="BRANCH" value="" />
+        <option name="DATE" value="" />
+        <option name="USE_BRANCH" value="false" />
+        <option name="USE_DATE" value="false" />
+      </value>
+    </option>
+    <option name="SHOW_CHANGES_REVISION_SETTINGS">
+      <value>
+        <option name="BRANCH" value="" />
+        <option name="DATE" value="" />
+        <option name="USE_BRANCH" value="false" />
+        <option name="USE_DATE" value="false" />
+      </value>
+    </option>
+    <option name="SHOW_OUTPUT" value="false" />
+    <option name="ADD_WATCH_INDEX" value="0" />
+    <option name="REMOVE_WATCH_INDEX" value="0" />
+    <option name="UPDATE_KEYWORD_SUBSTITUTION" />
+    <option name="MAKE_NEW_FILES_READONLY" value="false" />
+    <option name="SHOW_CORRUPTED_PROJECT_FILES" value="0" />
+    <option name="TAG_AFTER_PROJECT_COMMIT" value="false" />
+    <option name="OVERRIDE_EXISTING_TAG_FOR_PROJECT" value="true" />
+    <option name="TAG_AFTER_PROJECT_COMMIT_NAME" value="" />
+    <option name="CLEAN_COPY" value="false" />
+  </component>
+  <component name="DaemonCodeAnalyzer">
+    <disable_hints />
+  </component>
+  <component name="DebuggerManager">
+    <breakpoint_any>
+      <breakpoint>
+        <option name="NOTIFY_CAUGHT" value="true" />
+        <option name="NOTIFY_UNCAUGHT" value="true" />
+        <option name="ENABLED" value="false" />
+        <option name="SUSPEND_POLICY" value="SuspendAll" />
+        <option name="LOG_ENABLED" value="false" />
+        <option name="LOG_EXPRESSION_ENABLED" value="false" />
+        <option name="COUNT_FILTER_ENABLED" value="false" />
+        <option name="COUNT_FILTER" value="0" />
+        <option name="CONDITION_ENABLED" value="false" />
+        <option name="CLASS_FILTERS_ENABLED" value="false" />
+        <option name="INSTANCE_FILTERS_ENABLED" value="false" />
+        <option name="CONDITION" value="" />
+        <option name="LOG_MESSAGE" value="" />
+      </breakpoint>
+      <breakpoint>
+        <option name="NOTIFY_CAUGHT" value="true" />
+        <option name="NOTIFY_UNCAUGHT" value="true" />
+        <option name="ENABLED" value="false" />
+        <option name="SUSPEND_POLICY" value="SuspendAll" />
+        <option name="LOG_ENABLED" value="false" />
+        <option name="LOG_EXPRESSION_ENABLED" value="false" />
+        <option name="COUNT_FILTER_ENABLED" value="false" />
+        <option name="COUNT_FILTER" value="0" />
+        <option name="CONDITION_ENABLED" value="false" />
+        <option name="CLASS_FILTERS_ENABLED" value="false" />
+        <option name="INSTANCE_FILTERS_ENABLED" value="false" />
+        <option name="CONDITION" value="" />
+        <option name="LOG_MESSAGE" value="" />
+      </breakpoint>
+    </breakpoint_any>
+    <breakpoint_rules />
+    <ui_properties />
+  </component>
+  <component name="ErrorTreeViewConfiguration">
+    <option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
+    <option name="HIDE_WARNINGS" value="false" />
+  </component>
+  <component name="FavoritesManager">
+    <favorites_list name="LunarLander" />
+  </component>
+  <component name="FavoritesProjectViewPane" />
+	<component name="FileEditorManager">
+    <leaf>
+      <file leaf-file-name="ACTIVITY_NAME.java" pinned="false" current="true" current-in-tab="true">
+        <entry file="file://$PROJECT_DIR$/src/PACKAGE_PATH/ACTIVITY_NAME.java">
+          <provider selected="true" editor-type-id="text-editor">
+            <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.08211144">
+              <folding />
+            </state>
+          </provider>
+        </entry>
+      </file>
+    </leaf>
+  </component>
+  <component name="FindManager">
+    <FindUsagesManager>
+      <setting name="OPEN_NEW_TAB" value="false" />
+    </FindUsagesManager>
+  </component>
+  <component name="HierarchyBrowserManager">
+    <option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
+    <option name="SORT_ALPHABETICALLY" value="false" />
+    <option name="HIDE_CLASSES_WHERE_METHOD_NOT_IMPLEMENTED" value="false" />
+  </component>
+  <component name="InspectionManager">
+    <option name="AUTOSCROLL_TO_SOURCE" value="false" />
+    <option name="SPLITTER_PROPORTION" value="0.5" />
+    <option name="GROUP_BY_SEVERITY" value="false" />
+    <option name="FILTER_RESOLVED_ITEMS" value="true" />
+    <option name="ANALYZE_TEST_SOURCES" value="true" />
+    <option name="SHOW_DIFF_WITH_PREVIOUS_RUN" value="false" />
+    <option name="SCOPE_TYPE" value="1" />
+    <option name="CUSTOM_SCOPE_NAME" value="" />
+    <option name="SHOW_ONLY_DIFF" value="false" />
+    <option name="myCurrentProfileName" value="Default" />
+  </component>
+  <component name="J2EEProjectPane" />
+  <component name="JspContextManager" />
+  <component name="ModuleEditorState">
+    <option name="LAST_EDITED_MODULE_NAME" />
+    <option name="LAST_EDITED_TAB_NAME" />
+  </component>
+  <component name="NamedScopeManager" />
+  <component name="PackagesPane">
+    <subPane>
+      <PATH>
+        <PATH_ELEMENT>
+          <option name="myItemId" value="ACTIVITY_NAME.ipr" />
+          <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewProjectNode" />
+        </PATH_ELEMENT>
+        <PATH_ELEMENT>
+          <option name="myItemId" value="ACTIVITY_NAME" />
+          <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PackageViewModuleNode" />
+        </PATH_ELEMENT>
+      </PATH>
+    </subPane>
+  </component>
+  <component name="PerforceChangeBrowserSettings">
+    <option name="USE_CLIENT_FILTER" value="true" />
+    <option name="CLIENT" value="" />
+  </component>
+  <component name="PerforceDirect.Settings">
+    <option name="useP4CONFIG" value="true" />
+    <option name="port" value="&lt;perforce_server&gt;:1666" />
+    <option name="client" value="" />
+    <option name="user" value="" />
+    <option name="passwd" value="" />
+    <option name="showCmds" value="false" />
+    <option name="useNativeApi" value="false" />
+    <option name="pathToExec" value="p4" />
+    <option name="useCustomPathToExec" value="false" />
+    <option name="SYNC_FORCE" value="false" />
+    <option name="SYNC_RUN_RESOLVE" value="true" />
+    <option name="REVERT_UNCHANGED_FILES" value="true" />
+    <option name="CHARSET" value="none" />
+    <option name="SHOW_BRANCHES_HISTORY" value="true" />
+    <option name="ENABLED" value="true" />
+    <option name="USE_LOGIN" value="false" />
+    <option name="LOGIN_SILENTLY" value="false" />
+    <option name="INTEGRATE_RUN_RESOLVE" value="true" />
+    <option name="INTEGRATE_REVERT_UNCHANGED" value="true" />
+    <option name="SERVER_TIMEOUT" value="20000" />
+  </component>
+  <component name="ProjectLevelVcsManager">
+    <OptionsSetting value="true" id="Add" />
+    <OptionsSetting value="true" id="Remove" />
+    <OptionsSetting value="true" id="Checkin" />
+    <OptionsSetting value="true" id="Checkout" />
+    <OptionsSetting value="true" id="Update" />
+    <OptionsSetting value="true" id="Status" />
+    <OptionsSetting value="true" id="Edit" />
+    <ConfirmationsSetting value="0" id="Add" />
+    <ConfirmationsSetting value="0" id="Remove" />
+  </component>
+  <component name="ProjectPane">
+    <subPane>
+      <PATH>
+        <PATH_ELEMENT>
+          <option name="myItemId" value="ACTIVITY_NAME.ipr" />
+          <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+        </PATH_ELEMENT>
+        <PATH_ELEMENT>
+          <option name="myItemId" value="ACTIVITY_NAME" />
+          <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewModuleNode" />
+        </PATH_ELEMENT>
+      </PATH>
+    </subPane>
+  </component>
+  <component name="ProjectReloadState">
+    <option name="STATE" value="0" />
+  </component>
+  <component name="ProjectView">
+    <navigator currentView="ProjectPane" proportions="0.1" version="1" splitterProportion="0.5">
+      <flattenPackages />
+      <showMembers />
+      <showModules />
+      <showLibraryContents />
+      <hideEmptyPackages />
+      <abbreviatePackageNames />
+      <showStructure PackagesPane="false" ProjectPane="false" />
+      <autoscrollToSource />
+      <autoscrollFromSource />
+      <sortByType />
+    </navigator>
+  </component>
+  <component name="PropertiesComponent">
+    <property name="MemberChooser.copyJavadoc" value="false" />
+    <property name="GoToClass.includeLibraries" value="false" />
+    <property name="MemberChooser.showClasses" value="true" />
+    <property name="MemberChooser.sorted" value="false" />
+    <property name="GoToFile.includeJavaFiles" value="false" />
+    <property name="GoToClass.toSaveIncludeLibraries" value="false" />
+  </component>
+  <component name="ReadonlyStatusHandler">
+    <option name="SHOW_DIALOG" value="true" />
+  </component>
+  <component name="RecentsManager" />
+  <component name="RestoreUpdateTree" />
+  <component name="RunManager">
+    <configuration default="true" type="Application" factoryName="Application" enabled="false" merge="false">
+      <option name="MAIN_CLASS_NAME" />
+      <option name="VM_PARAMETERS" />
+      <option name="PROGRAM_PARAMETERS" />
+      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+      <option name="ALTERNATIVE_JRE_PATH" />
+      <option name="ENABLE_SWING_INSPECTOR" value="false" />
+      <module name="" />
+    </configuration>
+    <configuration default="true" type="Applet" factoryName="Applet">
+      <module name="" />
+      <option name="MAIN_CLASS_NAME" />
+      <option name="HTML_FILE_NAME" />
+      <option name="HTML_USED" value="false" />
+      <option name="WIDTH" value="400" />
+      <option name="HEIGHT" value="300" />
+      <option name="POLICY_FILE" value="/Developer/Applications/IntelliJ IDEA 6.0.4.app/bin/appletviewer.policy" />
+      <option name="VM_PARAMETERS" />
+      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+      <option name="ALTERNATIVE_JRE_PATH" />
+    </configuration>
+    <configuration default="true" type="JUnit" factoryName="JUnit" enabled="false" merge="false">
+      <module name="" />
+      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+      <option name="ALTERNATIVE_JRE_PATH" />
+      <option name="PACKAGE_NAME" />
+      <option name="MAIN_CLASS_NAME" />
+      <option name="METHOD_NAME" />
+      <option name="TEST_OBJECT" value="class" />
+      <option name="VM_PARAMETERS" />
+      <option name="PARAMETERS" />
+      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+      <option name="ADDITIONAL_CLASS_PATH" />
+      <option name="TEST_SEARCH_SCOPE">
+        <value defaultName="wholeProject" />
+      </option>
+    </configuration>
+    <configuration default="true" type="Remote" factoryName="Remote">
+      <option name="USE_SOCKET_TRANSPORT" value="true" />
+      <option name="SERVER_MODE" value="false" />
+      <option name="SHMEM_ADDRESS" value="javadebug" />
+      <option name="HOST" value="localhost" />
+      <option name="PORT" value="5005" />
+    </configuration>
+  </component>
+  <component name="ScopeViewComponent" />
+  <component name="SelectInManager" />
+  <component name="StarteamConfiguration">
+    <option name="SERVER" value="" />
+    <option name="PORT" value="49201" />
+    <option name="USER" value="" />
+    <option name="PASSWORD" value="" />
+    <option name="PROJECT" value="" />
+    <option name="VIEW" value="" />
+    <option name="ALTERNATIVE_WORKING_PATH" value="" />
+    <option name="LOCK_ON_CHECKOUT" value="false" />
+    <option name="UNLOCK_ON_CHECKIN" value="false" />
+  </component>
+  <component name="StructuralSearchPlugin" />
+  <component name="StructureViewFactory">
+    <option name="AUTOSCROLL_MODE" value="true" />
+    <option name="AUTOSCROLL_FROM_SOURCE" value="false" />
+    <option name="ACTIVE_ACTIONS" value="" />
+  </component>
+  <component name="Struts Assistant">
+    <option name="showInputs" value="true" />
+    <option name="resources">
+      <value>
+        <option name="strutsPath" />
+        <option name="strutsHelp" />
+      </value>
+    </option>
+    <option name="selectedTaglibs" />
+    <option name="selectedTaglibs" />
+    <option name="myStrutsValidationEnabled" value="true" />
+    <option name="myTilesValidationEnabled" value="true" />
+    <option name="myValidatorValidationEnabled" value="true" />
+    <option name="myReportErrorsAsWarnings" value="true" />
+  </component>
+  <component name="SvnChangesBrowserSettings">
+    <option name="USE_AUTHOR_FIELD" value="true" />
+    <option name="AUTHOR" value="" />
+    <option name="LOCATION" value="" />
+    <option name="USE_PROJECT_SETTINGS" value="true" />
+    <option name="USE_ALTERNATE_LOCATION" value="false" />
+  </component>
+  <component name="SvnConfiguration">
+    <option name="USER" value="" />
+    <option name="PASSWORD" value="" />
+    <option name="PROCESS_UNRESOLVED" value="false" />
+    <option name="LAST_MERGED_REVISION" />
+    <option name="UPDATE_RUN_STATUS" value="false" />
+    <option name="UPDATE_RECURSIVELY" value="true" />
+    <option name="MERGE_DRY_RUN" value="false" />
+    <upgradeMode>auto</upgradeMode>
+  </component>
+  <component name="TodoView" selected-index="0">
+    <todo-panel id="selected-file">
+      <are-packages-shown value="false" />
+      <are-modules-shown value="false" />
+      <flatten-packages value="false" />
+      <is-autoscroll-to-source value="true" />
+    </todo-panel>
+    <todo-panel id="all">
+      <are-packages-shown value="true" />
+      <are-modules-shown value="false" />
+      <flatten-packages value="false" />
+      <is-autoscroll-to-source value="true" />
+    </todo-panel>
+  </component>
+  <component name="ToolWindowManager">
+    <frame x="0" y="22" width="1440" height="834" extended-state="0" />
+    <editor active="false" />
+    <layout>
+      <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
+      <window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
+      <window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
+      <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="7" />
+      <window_info id="Project" active="true" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.24946082" order="0" />
+      <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="1" />
+      <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="1" />
+      <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
+      <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" order="6" />
+      <window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
+      <window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
+      <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
+      <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="1" />
+      <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
+      <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="2" />
+      <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="2" />
+      <window_info id="File View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
+      <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" order="4" />
+      <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" order="0" />
+      <window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
+      <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
+      <window_info id="Web" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="2" />
+      <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="0" />
+      <window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="3" />
+      <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="5" />
+    </layout>
+  </component>
+  <component name="VCS.FileViewConfiguration">
+    <option name="SELECTED_STATUSES" value="DEFAULT" />
+    <option name="SELECTED_COLUMNS" value="DEFAULT" />
+    <option name="SHOW_FILTERS" value="true" />
+    <option name="CUSTOMIZE_VIEW" value="true" />
+    <option name="SHOW_FILE_HISTORY_AS_TREE" value="true" />
+  </component>
+  <component name="VcsManagerConfiguration">
+    <option name="OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT" value="true" />
+    <option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="true" />
+    <option name="PERFORM_UPDATE_IN_BACKGROUND" value="false" />
+    <option name="PERFORM_COMMIT_IN_BACKGROUND" value="false" />
+    <option name="PUT_FOCUS_INTO_COMMENT" value="false" />
+    <option name="FORCE_NON_EMPTY_COMMENT" value="false" />
+    <option name="LAST_COMMIT_MESSAGE" />
+    <option name="SAVE_LAST_COMMIT_MESSAGE" value="true" />
+    <option name="CHECKIN_DIALOG_SPLITTER_PROPORTION" value="0.8" />
+    <option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="false" />
+    <option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="false" />
+    <option name="REFORMAT_BEFORE_FILE_COMMIT" value="false" />
+    <option name="FILE_HISTORY_DIALOG_COMMENTS_SPLITTER_PROPORTION" value="0.8" />
+    <option name="FILE_HISTORY_DIALOG_SPLITTER_PROPORTION" value="0.5" />
+    <option name="ERROR_OCCURED" value="false" />
+    <option name="ACTIVE_VCS_NAME" value="CVS" />
+    <option name="UPDATE_GROUP_BY_PACKAGES" value="false" />
+    <option name="SHOW_FILE_HISTORY_AS_TREE" value="false" />
+    <option name="FILE_HISTORY_SPLITTER_PROPORTION" value="0.6" />
+  </component>
+  <component name="XPathView.XPathProjectComponent">
+    <history />
+    <find-history />
+  </component>
+  <component name="XSLT-Support.FileAssociationsSettings" />
+  <component name="antWorkspaceConfiguration">
+    <option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
+    <option name="FILTER_TARGETS" value="false" />
+  </component>
+  <component name="com.intellij.ide.util.scopeChooser.ScopeChooserConfigurable" proportions="" version="1">
+    <option name="myLastEditedConfigurable" />
+  </component>
+  <component name="com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectRootMasterDetailsConfigurable" proportions="0.1" version="1">
+    <option name="myPlainMode" value="false" />
+    <option name="myLastEditedConfigurable" value="android" />
+  </component>
+  <component name="com.intellij.profile.ui.ErrorOptionsConfigurable" proportions="" version="1">
+    <option name="myLastEditedConfigurable" />
+  </component>
+  <component name="editorHistoryManager" />
+</project>
\ No newline at end of file
diff --git a/tools/scripts/java_file.template b/tools/scripts/java_file.template
new file mode 100644
index 0000000..aeb541f
--- /dev/null
+++ b/tools/scripts/java_file.template
@@ -0,0 +1,15 @@
+package PACKAGE;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class ACTIVITY_NAME extends Activity
+{
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
diff --git a/tools/scripts/java_tests_file.template b/tools/scripts/java_tests_file.template
new file mode 100644
index 0000000..7781a33
--- /dev/null
+++ b/tools/scripts/java_tests_file.template
@@ -0,0 +1,21 @@
+package PACKAGE;
+
+import android.test.ActivityInstrumentationTestCase;
+
+/**
+ * This is a simple framework for a test of an Application.  See
+ * {@link android.test.ApplicationTestCase ApplicationTestCase} for more information on
+ * how to write and extend Application tests.
+ * <p/>
+ * To run this test, you can type:
+ * adb shell am instrument -w \
+ * -e class PACKAGE.ACTIVITY_NAMETest \
+ * PACKAGE.tests/android.test.InstrumentationTestRunner
+ */
+public class ACTIVITY_NAMETest extends ActivityInstrumentationTestCase<ACTIVITY_NAME> {
+
+    public ACTIVITY_NAMETest() {
+        super("PACKAGE", ACTIVITY_NAME.class);
+    }
+
+}
\ No newline at end of file
diff --git a/tools/scripts/layout.template b/tools/scripts/layout.template
new file mode 100644
index 0000000..864e997
--- /dev/null
+++ b/tools/scripts/layout.template
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+<TextView  
+    android:layout_width="fill_parent" 
+    android:layout_height="wrap_content" 
+    android:text="Hello World, ACTIVITY_NAME"
+    />
+</LinearLayout>
+
diff --git a/tools/scripts/plugin.prop b/tools/scripts/plugin.prop
new file mode 100644
index 0000000..99dba4a
--- /dev/null
+++ b/tools/scripts/plugin.prop
@@ -0,0 +1,4 @@
+# begin plugin.prop
+plugin.version=0.9.0
+plugin.platform=android
+# end plugin.prop
\ No newline at end of file
diff --git a/tools/scripts/sdk_clean.sh b/tools/scripts/sdk_clean.sh
new file mode 100755
index 0000000..467d560
--- /dev/null
+++ b/tools/scripts/sdk_clean.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+#
+# This script processes a set of files given as arguments as sample code to be  released
+# in the SDK.
+#
+# Note that these files are modified in-place.
+#
+
+DIR=$1
+
+#
+# Remove BEGIN_INCLUDE and END_INCLUDE lines used by the javadoc.
+#
+# This does it by replacing these lines with blank lines so line numbers aren't
+# changed in the process, making it easier to match 3rd party complaints/questions
+# with the source tree.
+#
+# sed on Mac OS takes -i SUFFIX and sed on Linux takes -iSUFFIX
+#
+if [ $HOST_OS = darwin ] ; then
+find $DIR -name "*.java" -o -name "*.xml" | xargs -n 1 \
+    sed \
+        -e "s/.*BEGIN_INCLUDE(.*//" \
+        -e "s/.*END_INCLUDE(.*//" \
+        -i ""
+else
+find $DIR -name "*.java" -o -name "*.xml" | xargs -n 1 \
+    sed \
+        -e "s/.*BEGIN_INCLUDE(.*//" \
+        -e "s/.*END_INCLUDE(.*//" \
+        -i
+fi
+
+#
+# Fix up the line endings of all text files
+#
+if [ $HOST_OS = windows ] ; then
+    ENDING_TYPE=dos
+else
+    ENDING_TYPE=unix
+fi
+find $DIR -name "*.aidl" -o -name "*.css" -o -name "*.html" -o -name "*.java" \
+                     -o -name "*.js" -o -name "*.prop" -o -name "*.py" \
+                     -o -name "*.template" -o -name "*.txt" -o -name "*.windows" \
+                     -o -name "*.xml" \
+        | xargs $HOST_OUT_EXECUTABLES/line_endings $ENDING_TYPE
+
+
diff --git a/tools/scripts/strings.template b/tools/scripts/strings.template
new file mode 100644
index 0000000..acc28e2
--- /dev/null
+++ b/tools/scripts/strings.template
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">ACTIVITY_NAME</string>
+</resources>
diff --git a/tools/scripts/test_divide_and_compress.py b/tools/scripts/test_divide_and_compress.py
new file mode 100755
index 0000000..d0d27b3
--- /dev/null
+++ b/tools/scripts/test_divide_and_compress.py
@@ -0,0 +1,490 @@
+#!/usr/bin/python2.4
+#
+# Copyright (C) 2008 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.
+#
+
+"""Tests for divide_and_compress.py.
+
+TODO: Add tests for module methods.
+"""
+
+__author__ = 'jmatt@google.com (Justin Mattson)'
+
+import os
+import stat
+import unittest
+import zipfile
+from zipfile import ZipFile
+
+import divide_and_compress
+from mox import mox
+
+
+class BagOfParts(object):
+  """Just a generic class that I can use to assign random attributes to."""
+
+  def NoOp(self):
+    x = 1
+
+    
+class ValidAndRemoveTests(unittest.TestCase):
+  """Test the ArchiveIsValid and RemoveLastFile methods."""
+  
+  def setUp(self):
+    """Prepare the test.
+
+    Construct some mock objects for use with the tests.
+    """
+    self.my_mox = mox.Mox()
+    file1 = BagOfParts()
+    file1.filename = 'file1.txt'
+    file1.contents = 'This is a test file'
+    file2 = BagOfParts()
+    file2.filename = 'file2.txt'
+    file2.contents = ('akdjfk;djsf;kljdslkfjslkdfjlsfjkdvn;kn;2389rtu4i'
+                      'tn;ghf8:89H*hp748FJw80fu9WJFpwf39pujens;fihkhjfk'
+                      'sdjfljkgsc n;iself')
+    self.files = {'file1': file1, 'file2': file2}
+
+  def testArchiveIsValid(self):
+    """Test the DirectoryZipper.ArchiveIsValid method.
+
+    Run two tests, one that we expect to pass and one that we expect to fail
+    """
+    test_file_size = 1056730
+    self.my_mox.StubOutWithMock(os, 'stat')
+    os.stat('/foo/0.zip').AndReturn([test_file_size])
+    self.my_mox.StubOutWithMock(stat, 'ST_SIZE')
+    stat.ST_SIZE = 0
+    os.stat('/baz/0.zip').AndReturn([test_file_size])
+    mox.Replay(os.stat)
+    test_target = divide_and_compress.DirectoryZipper('/foo/', 'bar', 
+                                                      test_file_size - 1, True)
+
+    self.assertEqual(False, test_target.ArchiveIsValid(),
+                     msg=('ERROR: Test failed, ArchiveIsValid should have '
+                          'returned false, but returned true'))
+
+    test_target = divide_and_compress.DirectoryZipper('/baz/', 'bar',
+                                                      test_file_size + 1, True)
+    self.assertEqual(True, test_target.ArchiveIsValid(),
+                     msg=('ERROR: Test failed, ArchiveIsValid should have'
+                          ' returned true, but returned false'))
+
+  def testRemoveLastFile(self):
+    """Test DirectoryZipper.RemoveLastFile method.
+
+    Construct a ZipInfo mock object with two records, verify that write is
+    only called once on the new ZipFile object.
+    """
+    source = self.CreateZipSource()
+    dest = self.CreateZipDestination()
+    source_path = ''.join([os.getcwd(), '/0-old.zip'])
+    dest_path = ''.join([os.getcwd(), '/0.zip'])
+    test_target = divide_and_compress.DirectoryZipper(
+        ''.join([os.getcwd(), '/']), 'dummy', 1024*1024, True)
+    self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath')
+    test_target.OpenZipFileAtPath(source_path, mode='r').AndReturn(source)
+    test_target.OpenZipFileAtPath(dest_path,
+                                  compress=zipfile.ZIP_DEFLATED,
+                                  mode='w').AndReturn(dest)
+    self.my_mox.StubOutWithMock(os, 'rename')
+    os.rename(dest_path, source_path)
+    self.my_mox.StubOutWithMock(os, 'unlink')
+    os.unlink(source_path)
+    
+    self.my_mox.ReplayAll()
+    test_target.RemoveLastFile()
+    self.my_mox.VerifyAll()    
+
+  def CreateZipSource(self):
+    """Create a mock zip sourec object.
+
+    Read should only be called once, because the second file is the one
+    being removed.
+
+    Returns:
+      A configured mocked
+    """
+    
+    source_zip = self.my_mox.CreateMock(ZipFile)
+    source_zip.infolist().AndReturn([self.files['file1'], self.files['file1']])
+    source_zip.infolist().AndReturn([self.files['file1'], self.files['file1']])
+    source_zip.read(self.files['file1'].filename).AndReturn(
+        self.files['file1'].contents)
+    source_zip.close()
+    return source_zip
+
+  def CreateZipDestination(self):
+    """Create mock destination zip.
+
+    Write should only be called once, because there are two files in the
+    source zip and we expect the second to be removed.
+
+    Returns:
+      A configured mocked
+    """
+    
+    dest_zip = mox.MockObject(ZipFile)
+    dest_zip.writestr(self.files['file1'].filename,
+                      self.files['file1'].contents)
+    dest_zip.close()
+    return dest_zip
+
+  def tearDown(self):
+    """Remove any stubs we've created."""
+    self.my_mox.UnsetStubs()
+
+
+class FixArchiveTests(unittest.TestCase):
+  """Tests for the DirectoryZipper.FixArchive method."""
+  
+  def setUp(self):
+    """Create a mock file object."""
+    self.my_mox = mox.Mox()
+    self.file1 = BagOfParts()
+    self.file1.filename = 'file1.txt'
+    self.file1.contents = 'This is a test file'
+
+  def _InitMultiFileData(self):
+    """Create an array of mock file objects.
+
+    Create three mock file objects that we can use for testing.
+    """
+    self.multi_file_dir = []
+    
+    file1 = BagOfParts()
+    file1.filename = 'file1.txt'
+    file1.contents = 'kjaskl;jkdjfkja;kjsnbvjnvnbuewklriujalvjsd'
+    self.multi_file_dir.append(file1)
+
+    file2 = BagOfParts()
+    file2.filename = 'file2.txt'
+    file2.contents = ('He entered the room and there in the center, it was.'
+                      ' Looking upon the thing, suddenly he could not remember'
+                      ' whether he had actually seen it before or whether'
+                      ' his memory of it was merely the effect of something'
+                      ' so often being imagined that it had long since become '
+                      ' manifest in his mind.')
+    self.multi_file_dir.append(file2)
+
+    file3 = BagOfParts()
+    file3.filename = 'file3.txt'
+    file3.contents = 'Whoa, what is \'file2.txt\' all about?'
+    self.multi_file_dir.append(file3)
+    
+  def testSingleFileArchive(self):
+    """Test behavior of FixArchive when the archive has a single member.
+
+    We expect that when this method is called with an archive that has a
+    single member that it will return False and unlink the archive.
+    """
+    test_target = divide_and_compress.DirectoryZipper(
+        ''.join([os.getcwd(), '/']), 'dummy', 1024*1024, True)
+    self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath')
+    test_target.OpenZipFileAtPath(
+        ''.join([os.getcwd(), '/0.zip']), mode='r').AndReturn(
+            self.CreateSingleFileMock())
+    self.my_mox.StubOutWithMock(os, 'unlink')
+    os.unlink(''.join([os.getcwd(), '/0.zip']))
+    self.my_mox.ReplayAll()
+    self.assertEqual(False, test_target.FixArchive('SIZE'))
+    self.my_mox.VerifyAll()
+
+  def CreateSingleFileMock(self):
+    """Create a mock ZipFile object for testSingleFileArchive.
+
+    We just need it to return a single member infolist twice
+
+    Returns:
+      A configured mock object
+    """
+    mock_zip = self.my_mox.CreateMock(ZipFile)
+    mock_zip.infolist().AndReturn([self.file1])
+    mock_zip.infolist().AndReturn([self.file1])
+    mock_zip.close()
+    return mock_zip
+
+  def testMultiFileArchive(self):
+    """Test behavior of DirectoryZipper.FixArchive with a multi-file archive.
+
+    We expect that FixArchive will rename the old archive, adding '-old' before
+    '.zip', read all the members except the last one of '-old' into a new
+    archive with the same name as the original, and then unlink the '-old' copy
+    """
+    test_target = divide_and_compress.DirectoryZipper(
+        ''.join([os.getcwd(), '/']), 'dummy', 1024*1024, True)
+    self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath')
+    test_target.OpenZipFileAtPath(
+        ''.join([os.getcwd(), '/0.zip']), mode='r').AndReturn(
+            self.CreateMultiFileMock())
+    self.my_mox.StubOutWithMock(test_target, 'RemoveLastFile')
+    test_target.RemoveLastFile(''.join([os.getcwd(), '/0.zip']))
+    self.my_mox.StubOutWithMock(os, 'stat')
+    os.stat(''.join([os.getcwd(), '/0.zip'])).AndReturn([49302])
+    self.my_mox.StubOutWithMock(stat, 'ST_SIZE')
+    stat.ST_SIZE = 0
+    self.my_mox.ReplayAll()
+    self.assertEqual(True, test_target.FixArchive('SIZE'))
+    self.my_mox.VerifyAll()
+
+  def CreateMultiFileMock(self):
+    """Create mock ZipFile object for use with testMultiFileArchive.
+
+    The mock just needs to return the infolist mock that is prepared in
+    InitMultiFileData()
+
+    Returns:
+      A configured mock object
+    """
+    self._InitMultiFileData()
+    mock_zip = self.my_mox.CreateMock(ZipFile)
+    mock_zip.infolist().AndReturn(self.multi_file_dir)
+    mock_zip.close()
+    return mock_zip
+
+  def tearDown(self):
+    """Unset any mocks that we've created."""
+    self.my_mox.UnsetStubs()
+
+
+class AddFileToArchiveTest(unittest.TestCase):
+  """Test behavior of method to add a file to an archive."""
+
+  def setUp(self):
+    """Setup the arguments for the DirectoryZipper object."""
+    self.my_mox = mox.Mox()
+    self.output_dir = '%s/' % os.getcwd()
+    self.file_to_add = 'file.txt'
+    self.input_dir = '/foo/bar/baz/'
+
+  def testAddFileToArchive(self):
+    """Test the DirectoryZipper.AddFileToArchive method.
+
+    We are testing a pretty trivial method, we just expect it to look at the
+    file its adding, so that it possible can through out a warning.
+    """
+    test_target = divide_and_compress.DirectoryZipper(self.output_dir,
+                                                      self.input_dir,
+                                                      1024*1024, True)
+    self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath')
+    archive_mock = self.CreateArchiveMock()
+    test_target.OpenZipFileAtPath(
+        ''.join([self.output_dir, '0.zip']),
+        compress=zipfile.ZIP_DEFLATED).AndReturn(archive_mock)
+    self.StubOutOsModule()
+    self.my_mox.ReplayAll()
+    test_target.AddFileToArchive(''.join([self.input_dir, self.file_to_add]),
+                                 zipfile.ZIP_DEFLATED)
+    self.my_mox.VerifyAll()
+
+  def StubOutOsModule(self):
+    """Create a mock for the os.path and os.stat objects.
+
+    Create a stub that will return the type (file or directory) and size of the
+    object that is to be added.
+    """
+    self.my_mox.StubOutWithMock(os.path, 'isfile')
+    os.path.isfile(''.join([self.input_dir, self.file_to_add])).AndReturn(True)
+    self.my_mox.StubOutWithMock(os, 'stat')
+    os.stat(''.join([self.input_dir, self.file_to_add])).AndReturn([39480])
+    self.my_mox.StubOutWithMock(stat, 'ST_SIZE')
+    stat.ST_SIZE = 0
+    
+  def CreateArchiveMock(self):
+    """Create a mock ZipFile for use with testAddFileToArchive.
+
+    Just verify that write is called with the file we expect and that the
+    archive is closed after the file addition
+
+    Returns:
+      A configured mock object
+    """
+    archive_mock = self.my_mox.CreateMock(ZipFile)
+    archive_mock.write(''.join([self.input_dir, self.file_to_add]),
+                       self.file_to_add)
+    archive_mock.close()
+    return archive_mock
+
+  def tearDown(self):
+    self.my_mox.UnsetStubs()
+
+
+class CompressDirectoryTest(unittest.TestCase):
+  """Test the master method of the class.
+
+  Testing with the following directory structure.
+  /dir1/
+  /dir1/file1.txt
+  /dir1/file2.txt
+  /dir1/dir2/
+  /dir1/dir2/dir3/
+  /dir1/dir2/dir4/
+  /dir1/dir2/dir4/file3.txt
+  /dir1/dir5/
+  /dir1/dir5/file4.txt
+  /dir1/dir5/file5.txt
+  /dir1/dir5/file6.txt
+  /dir1/dir5/file7.txt
+  /dir1/dir6/
+  /dir1/dir6/file8.txt
+
+  file1.txt., file2.txt, file3.txt should be in 0.zip
+  file4.txt should be in 1.zip
+  file5.txt, file6.txt should be in 2.zip
+  file7.txt will not be stored since it will be too large compressed
+  file8.txt should b in 3.zip
+  """
+
+  def setUp(self):
+    """Setup all the mocks for this test."""
+    self.my_mox = mox.Mox()
+
+    self.base_dir = '/dir1'
+    self.output_path = '/out_dir/'
+    self.test_target = divide_and_compress.DirectoryZipper(
+        self.output_path, self.base_dir, 1024*1024, True)
+    
+    self.InitArgLists()
+    self.InitOsDotPath()
+    self.InitArchiveIsValid()
+    self.InitWriteIndexRecord()
+    self.InitAddFileToArchive()
+
+  def tearDown(self):
+    self.my_mox.UnsetStubs()
+
+  def testCompressDirectory(self):
+    """Test the DirectoryZipper.CompressDirectory method."""
+    self.my_mox.ReplayAll()
+    for arguments in self.argument_lists:
+      self.test_target.CompressDirectory(None, arguments[0], arguments[1])
+    self.my_mox.VerifyAll()
+
+  def InitAddFileToArchive(self):
+    """Setup mock for DirectoryZipper.AddFileToArchive.
+
+    Make sure that the files are added in the order we expect.
+    """
+    self.my_mox.StubOutWithMock(self.test_target, 'AddFileToArchive')
+    self.test_target.AddFileToArchive('/dir1/file1.txt', zipfile.ZIP_DEFLATED)
+    self.test_target.AddFileToArchive('/dir1/file2.txt', zipfile.ZIP_DEFLATED)
+    self.test_target.AddFileToArchive('/dir1/dir2/dir4/file3.txt',
+                                      zipfile.ZIP_DEFLATED)
+    self.test_target.AddFileToArchive('/dir1/dir5/file4.txt',
+                                      zipfile.ZIP_DEFLATED)
+    self.test_target.AddFileToArchive('/dir1/dir5/file4.txt',
+                                      zipfile.ZIP_DEFLATED)
+    self.test_target.AddFileToArchive('/dir1/dir5/file5.txt',
+                                      zipfile.ZIP_DEFLATED)
+    self.test_target.AddFileToArchive('/dir1/dir5/file5.txt',
+                                      zipfile.ZIP_DEFLATED)
+    self.test_target.AddFileToArchive('/dir1/dir5/file6.txt',
+                                      zipfile.ZIP_DEFLATED)
+    self.test_target.AddFileToArchive('/dir1/dir5/file7.txt',
+                                      zipfile.ZIP_DEFLATED)
+    self.test_target.AddFileToArchive('/dir1/dir5/file7.txt',
+                                      zipfile.ZIP_DEFLATED)
+    self.test_target.AddFileToArchive('/dir1/dir6/file8.txt',
+                                      zipfile.ZIP_DEFLATED)
+  
+  def InitWriteIndexRecord(self):
+    """Setup mock for DirectoryZipper.WriteIndexRecord."""
+    self.my_mox.StubOutWithMock(self.test_target, 'WriteIndexRecord')
+
+    # we are trying to compress 8 files, but we should only attempt to
+    # write an index record 7 times, because one file is too large to be stored
+    self.test_target.WriteIndexRecord().AndReturn(True)
+    self.test_target.WriteIndexRecord().AndReturn(False)
+    self.test_target.WriteIndexRecord().AndReturn(False)
+    self.test_target.WriteIndexRecord().AndReturn(True)
+    self.test_target.WriteIndexRecord().AndReturn(True)
+    self.test_target.WriteIndexRecord().AndReturn(False)
+    self.test_target.WriteIndexRecord().AndReturn(True)
+
+  def InitArchiveIsValid(self):
+    """Mock out DirectoryZipper.ArchiveIsValid and DirectoryZipper.FixArchive.
+
+    Mock these methods out such that file1, file2, and file3 go into one
+    archive. file4 then goes into the next archive, file5 and file6 in the
+    next, file 7 should appear too large to compress into an archive, and
+    file8 goes into the final archive
+    """
+    self.my_mox.StubOutWithMock(self.test_target, 'ArchiveIsValid')
+    self.my_mox.StubOutWithMock(self.test_target, 'FixArchive')
+    self.test_target.ArchiveIsValid().AndReturn(True)
+    self.test_target.ArchiveIsValid().AndReturn(True)
+    self.test_target.ArchiveIsValid().AndReturn(True)
+
+    # should be file4.txt
+    self.test_target.ArchiveIsValid().AndReturn(False)
+    self.test_target.FixArchive('SIZE').AndReturn(True)
+    self.test_target.ArchiveIsValid().AndReturn(True)
+
+    # should be file5.txt
+    self.test_target.ArchiveIsValid().AndReturn(False)
+    self.test_target.FixArchive('SIZE').AndReturn(True)
+    self.test_target.ArchiveIsValid().AndReturn(True)
+    self.test_target.ArchiveIsValid().AndReturn(True)
+
+    # should be file7.txt
+    self.test_target.ArchiveIsValid().AndReturn(False)
+    self.test_target.FixArchive('SIZE').AndReturn(True)
+    self.test_target.ArchiveIsValid().AndReturn(False)
+    self.test_target.FixArchive('SIZE').AndReturn(False)
+    self.test_target.ArchiveIsValid().AndReturn(True)
+    
+  def InitOsDotPath(self):
+    """Mock out os.path.isfile.
+
+    Mock this out so the things we want to appear as files appear as files and
+    the things we want to appear as directories appear as directories. Also
+    make sure that the order of file visits is as we expect (which is why
+    InAnyOrder isn't used here).
+    """
+    self.my_mox.StubOutWithMock(os.path, 'isfile')
+    os.path.isfile('/dir1/dir2').AndReturn(False)
+    os.path.isfile('/dir1/dir5').AndReturn(False)
+    os.path.isfile('/dir1/dir6').AndReturn(False)
+    os.path.isfile('/dir1/file1.txt').AndReturn(True)
+    os.path.isfile('/dir1/file2.txt').AndReturn(True)
+    os.path.isfile('/dir1/dir2/dir3').AndReturn(False)
+    os.path.isfile('/dir1/dir2/dir4').AndReturn(False)
+    os.path.isfile('/dir1/dir2/dir4/file3.txt').AndReturn(True)
+    os.path.isfile('/dir1/dir5/file4.txt').AndReturn(True)
+    os.path.isfile('/dir1/dir5/file4.txt').AndReturn(True)
+    os.path.isfile('/dir1/dir5/file5.txt').AndReturn(True)
+    os.path.isfile('/dir1/dir5/file5.txt').AndReturn(True)
+    os.path.isfile('/dir1/dir5/file6.txt').AndReturn(True)
+    os.path.isfile('/dir1/dir5/file7.txt').AndReturn(True)
+    os.path.isfile('/dir1/dir5/file7.txt').AndReturn(True)
+    os.path.isfile('/dir1/dir6/file8.txt').AndReturn(True)
+
+  def InitArgLists(self):
+    """Create the directory path => directory contents mappings."""
+    self.argument_lists = []
+    self.argument_lists.append(['/dir1',
+                                ['file1.txt', 'file2.txt', 'dir2', 'dir5',
+                                 'dir6']])
+    self.argument_lists.append(['/dir1/dir2', ['dir3', 'dir4']])
+    self.argument_lists.append(['/dir1/dir2/dir3', []])
+    self.argument_lists.append(['/dir1/dir2/dir4', ['file3.txt']])
+    self.argument_lists.append(['/dir1/dir5',
+                                ['file4.txt', 'file5.txt', 'file6.txt',
+                                 'file7.txt']])
+    self.argument_lists.append(['/dir1/dir6', ['file8.txt']])
+      
+if __name__ == '__main__':
+  unittest.main()