diff --git a/.svn/all-wcprops b/.svn/all-wcprops
new file mode 100644
index 0000000..aa561d4
--- /dev/null
+++ b/.svn/all-wcprops
@@ -0,0 +1,41 @@
+K 25
+svn:wc:ra_dav:version-url
+V 21
+/svn/!svn/ver/2/trunk
+END
+default.properties
+K 25
+svn:wc:ra_dav:version-url
+V 40
+/svn/!svn/ver/2/trunk/default.properties
+END
+local.properties
+K 25
+svn:wc:ra_dav:version-url
+V 38
+/svn/!svn/ver/2/trunk/local.properties
+END
+AndroidManifest.xml
+K 25
+svn:wc:ra_dav:version-url
+V 41
+/svn/!svn/ver/2/trunk/AndroidManifest.xml
+END
+COPYING
+K 25
+svn:wc:ra_dav:version-url
+V 29
+/svn/!svn/ver/2/trunk/COPYING
+END
+build.properties
+K 25
+svn:wc:ra_dav:version-url
+V 38
+/svn/!svn/ver/2/trunk/build.properties
+END
+build.xml
+K 25
+svn:wc:ra_dav:version-url
+V 31
+/svn/!svn/ver/2/trunk/build.xml
+END
diff --git a/.svn/entries b/.svn/entries
new file mode 100644
index 0000000..576a195
--- /dev/null
+++ b/.svn/entries
@@ -0,0 +1,244 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+default.properties
+file
+
+
+
+
+2010-06-03T15:23:16.985646Z
+e02739cf613b14b831da735ddfaafff7
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+450
+
+assets
+dir
+
+tests
+dir
+
+local.properties
+file
+
+
+
+
+2010-06-03T15:23:16.985646Z
+89e0b3a2c2aafb770e7788b734db6e93
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+42
+
+AndroidManifest.xml
+file
+
+
+
+
+2010-06-03T15:23:16.985646Z
+f2abf1f6972713ea84249f740b37b785
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8037
+
+src
+dir
+
+COPYING
+file
+
+
+
+
+2010-06-03T15:23:16.985646Z
+3b83ef96387f14655fc854ddc3c6bd57
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+11358
+
+res
+dir
+
+build.properties
+file
+
+
+
+
+2010-06-03T15:23:16.985646Z
+d5c7126f99cb6d80e4da6504570ca968
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+848
+
+build.xml
+file
+
+
+
+
+2010-06-03T15:23:16.985646Z
+1eea952ca3383e2e95622f508a5f2ba6
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2867
+
diff --git a/.svn/text-base/AndroidManifest.xml.svn-base b/.svn/text-base/AndroidManifest.xml.svn-base
new file mode 100644
index 0000000..e421bee
--- /dev/null
+++ b/.svn/text-base/AndroidManifest.xml.svn-base
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.svn/text-base/COPYING.svn-base b/.svn/text-base/COPYING.svn-base
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/.svn/text-base/COPYING.svn-base
@@ -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/.svn/text-base/build.properties.svn-base b/.svn/text-base/build.properties.svn-base
new file mode 100644
index 0000000..873bd38
--- /dev/null
+++ b/.svn/text-base/build.properties.svn-base
@@ -0,0 +1,21 @@
+# This file is used to override default values used by the Ant build system.
+#
+# This file must be checked in Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# The name of your application package as defined in the manifest.
+# Used by the 'uninstall' rule.
+application-package=com.google.android.apps.iosched
+
+# You can use this to override default values such as
+# 'source.dir' for the location of your java source folder and
+# 'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+# 'key.store' for the location of your keystore and
+# 'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+
diff --git a/.svn/text-base/build.xml.svn-base b/.svn/text-base/build.xml.svn-base
new file mode 100644
index 0000000..ee2bd56
--- /dev/null
+++ b/.svn/text-base/build.xml.svn-base
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.svn/text-base/default.properties.svn-base b/.svn/text-base/default.properties.svn-base
new file mode 100644
index 0000000..d84434c
--- /dev/null
+++ b/.svn/text-base/default.properties.svn-base
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Indicates whether an apk should be generated for each density.
+split.density=false
+
+# Project target.
+target=android-5
diff --git a/.svn/text-base/local.properties.svn-base b/.svn/text-base/local.properties.svn-base
new file mode 100644
index 0000000..8c4030b
--- /dev/null
+++ b/.svn/text-base/local.properties.svn-base
@@ -0,0 +1,2 @@
+sdk.dir=/home/jsharkey/android-sdk-linux
+
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 0000000..e421bee
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/COPYING
@@ -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/README b/README
new file mode 100644
index 0000000..fe8f218
--- /dev/null
+++ b/README
@@ -0,0 +1,9 @@
+Google I/O is a developer conference held each year with two days of deep technical content featuring over 90 sessions, more than 5,000 developers, and over 180 demonstrations from developers showcasing their technologies.
+
+This project is the Android app for the conference, including these features and more:
+
+Conference schedule with calendar-block overview.
+Detailed session info that is full-text searchable.
+Info about companies in the Developer Sandbox.
+Star sessions and Sandbox companies.
+Conference map and take notes on sessions.
diff --git a/assets/.svn/all-wcprops b/assets/.svn/all-wcprops
new file mode 100644
index 0000000..4569cdf
--- /dev/null
+++ b/assets/.svn/all-wcprops
@@ -0,0 +1,23 @@
+K 25
+svn:wc:ra_dav:version-url
+V 28
+/svn/!svn/ver/2/trunk/assets
+END
+cache-vendors.xml
+K 25
+svn:wc:ra_dav:version-url
+V 46
+/svn/!svn/ver/2/trunk/assets/cache-vendors.xml
+END
+cache-sessions.xml
+K 25
+svn:wc:ra_dav:version-url
+V 47
+/svn/!svn/ver/2/trunk/assets/cache-sessions.xml
+END
+cache-speakers.xml
+K 25
+svn:wc:ra_dav:version-url
+V 47
+/svn/!svn/ver/2/trunk/assets/cache-speakers.xml
+END
diff --git a/assets/.svn/entries b/assets/.svn/entries
new file mode 100644
index 0000000..123c148
--- /dev/null
+++ b/assets/.svn/entries
@@ -0,0 +1,130 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/assets
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+cache-vendors.xml
+file
+
+
+
+
+2010-06-03T15:23:14.661840Z
+fb744cd01e2eefe8fca1e97046640086
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+220296
+
+cache-sessions.xml
+file
+
+
+
+
+2010-06-03T15:23:14.665646Z
+140643be8c95d71a8591ddc41e7f33fd
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+133455
+
+cache-speakers.xml
+file
+
+
+
+
+2010-06-03T15:23:14.665646Z
+737be814f4213fb1b315dfcdd11c0f6e
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+178047
+
diff --git a/assets/.svn/text-base/cache-sessions.xml.svn-base b/assets/.svn/text-base/cache-sessions.xml.svn-base
new file mode 100644
index 0000000..10ba965
--- /dev/null
+++ b/assets/.svn/text-base/cache-sessions.xml.svn-base
@@ -0,0 +1,32 @@
+http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic2010-05-10T18:39:11.857Zsessionschristine.leechristine.lee@gmail.com821http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cokwr2010-05-10T18:39:11.857ZRow: 2sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 6, product: Android, track: Android, sessiontype: 101, sessiontitle: A beginner's guide to Android, tags: Android, Mobile, Java, sessionspeakers: Reto Meier, speakers: retomeier, sessionabstract: This session will introduce some of the basic concepts involved in Android development. Starting with an overview of the SDK APIs available to developers, we will work through some simple code examples that explore some of the more common user features including using sensors, maps, and geolocation., sessionrequirements: Proficiency in Java and a basic understanding of embedded environments like mobile phones , sessionlink: beginners-guide-android, sessionhashtag: #android1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.45, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B-Xhdu7ZkBHw, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B-Xhdu7ZkBHw, waveid: w+-Xhdu7ZkBHwhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ckd7g2010-05-10T18:39:11.857ZRow: 3sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: Writing real-time games for Android redux, tags: Android, Mobile, Java, Games, 2D, 3D, C++, sessionspeakers: Chris Pruett, speakers: pruett, sessionabstract: This session is a crash course in Android game development: everything you need to know to get started writing 2D and 3D games, as well as tips, tricks, and benchmarks to help your code reach optimal performance. In addition, we'll discuss hot topics related to game development, including hardware differences across devices, using C++ to write Android games, and the traits of the most popular games on Market., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: writing-real-time-games-android, sessionhashtag: #android2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.9B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BWDgRyLZkaR, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BWDgRyLZkaR, waveid: w+WDgRyLZkaRhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/clrrx2010-05-10T18:39:11.857ZRow: 4sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: The world of ListView, tags: Android, Mobile, Java, sessionspeakers: Romain Guy, Adam Powell, speakers: romainguy, adamp, sessionabstract: ListView is one of the most widely used Android widgets but also the most complex one. Join us to learn how to master ListView and learn all about its features, optimizations, quirks and limitations.
+, sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: world-of-listview-android, sessionhashtag: #android3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.47, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv7BXJrZkBJT, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv7BXJrZkBJT, waveid: w+v7BXJrZkBJThttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d180g2010-05-10T18:39:11.857ZRow: 5sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: Casting a wide net: how to target all Android devices, tags: Android, Mobile, Java, sessionspeakers: Justin Mattson, speakers: jmatt, sessionabstract: One of Android's strengths is its flexibility to run on a wide variety of devices. In this session, we will explore the facilities the Android resource system provides to developers to make supporting many devices from one application binary easier, as well as common pitfalls. In addition to hardware heterogeneity, more than one version of Android may exist in the wild at any given time. We will go over strategies for providing cross-version compatibility., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: casting-wide-net-android-devices, sessionhashtag: #android4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.48, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BfZ2urbZk7o, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BfZ2urbZk7o, waveid: w+fZ2urbZk7ohttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d2mkx2010-05-10T18:39:11.857ZRow: 6sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: Android UI design patterns, tags: Android, Mobile, Java, sessionspeakers: Chris Nesladek, German Bauer, Richard Fulcher, Christian Robertson, Jim Palmer, speakers: cnesladek, germanb, rfulcher, robertsonc, jimpalmer, sessionabstract: In this session, the Android User Experience team will show the types of patterns you can use to build a great Android application. We'll cover things like how to use Interactive Titlebars, Quick Contacts, and Bottom bars as well some new patterns which will get an I/O-only preview. The team will be also available for a no holds barred Q&A session., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: android-ui-design-patterns, sessionhashtag: #android5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.49, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkP1, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkP1, waveid: w+e6j2obZkP1http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cssly2010-05-10T18:39:11.857ZRow: 7sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 6, product: Android, track: Android, sessiontype: 301, sessiontitle: Developing Android REST client applications, tags: Android, Mobile, Java, sessionspeakers: Virgil Dobjanschi, speakers: virgild, sessionabstract: This session will present architectural considerations for developing RESTful applications on the Android platform. It focuses on design patterns, platform integration and performance issues specific to the Android platform.
+, sessionrequirements: Advanced knowledge of Java & Android concepts, sessionlink: developing-RESTful-android-apps, sessionhashtag: #android6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6C, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkNc, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkNc, waveid: w+e6j2obZkNchttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cu76f2010-05-10T18:39:11.857ZRow: 8sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: 6, product: Android, track: Android, sessiontype: 301, sessiontitle: A JIT Compiler for Android's Dalvik VM, tags: Android, Mobile, Java, sessionspeakers: Ben Cheng, Bill Buzbee, speakers: bccheng, buzbee, sessionabstract: In this session we will outline the design of a JIT Compiler suitable for embedded Android devices. Topics will include an architectural overview, the rationale for design decisions and the special support for JIT verification, testing and tuning., sessionrequirements: Advanced knowledge of Java & Android concepts, sessionlink: jit-compiler-androids-dalvik-vm, sessionhashtag: #android7, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6D, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkRw, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkRw, waveid: w+e6j2obZkRwhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cvlqs2010-05-10T18:39:11.857ZRow: 9sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 3, product: App Engine, track: App Engine, sessiontype: 201, sessiontitle: Appstats - RPC instrumentation and optimizations for App Engine, tags: App Engine, Python, Java, sessionspeakers: Guido van Rossum, speakers: guido, sessionabstract: Appstats is a pure userland library (for Python and Java) that inserts instrumentation hooks into the App Engine runtime at the interface between the runtime and services like the datastore. The collected statistics can be browsed in a rich UI which allows drilling down to various levels of detail. The talk will also discuss common optimizations to address typical findings., sessionrequirements: Intermediate knowledge of App Engine (Python or Java), sessionlink: appstatsrpc-appengine, sessionhashtag: #appengine1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.4A, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BeRiTZrZkC22, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BeRiTZrZkC22, waveid: w+eRiTZrZkC22http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/dcgjs2010-05-10T18:39:11.857ZRow: 10sessiondate: Thursday May 20, sessiontime: 4:45pm-5:45pm, room: 3, product: App Engine, track: App Engine, sessiontype: 301, sessiontitle: Next gen queries, tags: App Engine, Datastore, Query, Cursor, Zigzag Merge Join, sessionspeakers: Alfred Fuller, speakers: arfuller, sessionabstract: This session will discuss the design and implications of improvements to the Datastore query engine including support for AND, OR and NOT query operators, the solution to exploding indexes and paging backwards with Cursors. Specific technologies discussed will be an improved zigzag merge join algorithm, a new extensible multiquery framework (with geo-query support) and a smaller more versatile Cursor design., sessionrequirements: Intermediate knowledge of Google App Engine, specifically the datastore., sessionlink: next-gen-queries-appengine, sessionhashtag: #appengine10, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.71, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BY_40V7ZkB3Z, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BY_40V7ZkB3Z, waveid: w+Y_40V7ZkB3Zhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ddv492010-05-10T18:39:11.857ZRow: 11sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 3, product: App Engine, track: App Engine, sessiontype: 201, sessiontitle: Data migration in App Engine, tags: App Engine, Data Migration, sessionspeakers: Matthew Blain, speakers: mblain, sessionabstract: Learn about the App Engine bulk loader and see an example of migrating data from an external data source into the app engine datastore--and back out. Do you have data stored in a traditional, relational DB which you'd like to upload to App Engine? This session will teach you how., sessionrequirements: Basic knowledge of Google App Engine and database concepts., sessionlink: data-migration-appengine, sessionhashtag: #appengine4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.4D, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BeRiTZrZkC29, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BeRiTZrZkC29, waveid: w+eRiTZrZkC29http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d415a2010-05-10T18:39:11.857ZRow: 12sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: 3, product: App Engine, track: App Engine, sessiontype: 201, sessiontitle: What's hot in Java for App Engine, tags: App Engine, Java, sessionspeakers: Toby Reyelts, Don Schwarz, speakers: tobyr, schwardo, sessionabstract: Learn what's new with Java on App Engine. We'll take a whirlwind tour through the changes since last year, walk through a code sample for task queues and the new blobstore service, and demonstrate techniques for improving your application's performance. We'll top it off with a glimpse into some new features that we've planned for the year ahead., sessionrequirements: Intermediate knowledge of App Engine for Java, sessionlink: whats-hot-in-java-for-app-engine, sessionhashtag: #appengine6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6E, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BO4sGNLZkCGh, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BO4sGNLZkCGh, waveid: w+O4sGNLZkCGhhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d5fpr2010-05-10T18:39:11.857ZRow: 13sessiondate: Thursday May 20, sessiontime: 1:00pm-2:00pm, room: 3, product: App Engine, track: App Engine, sessiontype: 301, sessiontitle: Building high-throughput data pipelines with Google App Engine, tags: App Engine, Data processing, Mapping on data, sessionspeakers: Brett Slatkin, speakers: bslatkin, sessionabstract: This session will cover how to build, test, and maintain large-scale data pipelines on Google App Engine. It will cover maximizing efficiency, productionization, and how to deal with changing requirements., sessionrequirements: Advanced knowledge of Google App Engine, sessionlink: high-throughput-data-pipelines-appengine, sessionhashtag: #appengine7, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6F, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BO4sGNLZkCHO, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BO4sGNLZkCHO, waveid: w+O4sGNLZkCHOhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/df9om2010-05-10T18:39:11.857ZRow: 14sessiondate: Thursday May 20, sessiontime: 3:30pm-4:30pm, room: 3, product: App Engine, track: App Engine, sessiontype: 201, sessiontitle: Testing techniques for Google App Engine, tags: App Engine, Unit Testing, sessionspeakers: Max Ross, speakers: maxr, sessionabstract: Google App Engine is designed to help developers more easily create and manage scalable web applications. But what about testing? We typically write tests under the assumption that our development stack closely resembles our production stack, so what do we do when our target environment only lives in the cloud? In this talk, we will highlight the key differences between typical testing techniques and Google App Engine testing techniques. We will also present concrete strategies for testing against local and cloud-based implementations of App Engine services like the datastore and the task queue. Finally, we will explain how to use App Engine as a highly parallel test harness that runs existing test suites without modification., sessionrequirements: Intermediate knowledge of Google App Engine and unit testing concepts., sessionlink: testing-techniques-app-engine, sessionhashtag: #appengine9, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.70, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BeRiTZrZkCzy, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BeRiTZrZkCzy, waveid: w+eRiTZrZkCzyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/dgo932010-05-10T18:39:11.857ZRow: 15sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 5, product: Chrome Dev Tools, track: Chrome, sessiontype: 101, sessiontitle: Google Chrome's Developer Tools, tags: Chrome, sessionspeakers: Pavel Feldman, Anders Sandholm, speakers: pfeldman, sandholm, sessionabstract: In this session we'll give an overview of Developer Tools for Google Chrome that is a part of the standard Chrome distribution. Chrome Developer Tools allow inspecting, debugging and tuning the web applications and many more. In addition to this overview we would like to share some implementation details of the Developer Tools features and call for your contribution., sessionrequirements: Web developer background, sessionlink: chrome-developer-tools, sessionhashtag: #chrome1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.4E, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv_K9zbZkBY_, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv_K9zbZkBY_, waveid: w+v_K9zbZkBY_http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/djhdx2010-05-10T18:39:11.857ZRow: 16sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 5, product: Chrome, track: Chrome, sessiontype: 201, sessiontitle: Beyond JavaScript: programming the web with native code, tags: Chrome, Native Client, sessionspeakers: Dave Springer, Ian Lewis, speakers: dspringer, ilewis, sessionabstract: Although JavaScript performance is rapidly increasing, there are still applications for which native code is a better choice. Learn about Native Client and how you can use it to build rich applications with all of the advantages and power of the web., sessionlink: native-code-chrome, sessionhashtag: #chrome2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.4F, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkKS, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkKS, waveid: w+e6j2obZkKShttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/dyxo82010-05-10T18:39:11.857ZRow: 17sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 5, product: Chrome OS, track: Chrome, sessiontype: 201, sessiontitle: Developing With HTML5, tags: HTML5, Chrome, Chrome OS, sessionspeakers: Mihai Ionescu, Arne Roomann-Kurrik, speakers: mihai, kurrik, sessionabstract: This session covers the HTML5 APIs available to Google Chrome and Google Chrome OS applications and Google Chrome extensions. Learn how to design web applications for a Google Chrome OS netbook using the latest web technologies., sessionrequirements: Familiarity with HTML and JavaScript, sessionlink: developing-with-html5, sessionhashtag: #chrome3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.51, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQ9WoGLZkBlw, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQ9WoGLZkBlw, waveid: w+Q9WoGLZkBlwhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/c4wx52010-05-10T18:39:11.857ZRow: 18sessiondate: Thursday May 20, sessiontime: 1:00pm-2:00pm, room: 5, product: Chrome, track: Chrome, sessiontype: 101, sessiontitle: Chrome Extensions - how-to, tags: Chrome, Chrome Extensions, sessionspeakers: Brian Kennish, speakers: bkennish, sessionabstract: Google Chrome shipped an extensions API in version 4.0. Since last year, new capabilites have been added to the extensions framework, and many people have already written powerful extensions with minimal effort. Find out how to write an extension, and what's coming next in Chrome Extensions., sessionrequirements: Familiarity with HTML and JavaScript, sessionlink: chrome-extensions, sessionhashtag: #chrome6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.72, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BetojYbZkBGj, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BetojYbZkBGj, waveid: w+etojYbZkBGjhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/drwu72010-05-10T18:39:11.857ZRow: 19sessiondate: Thursday May 20, sessiontime: 2:15pm-3:15pm, room: 5, product: Google Chrome Frame, track: Chrome, sessiontype: 201, sessiontitle: Using Google Chrome Frame, tags: HTML5, Chrome, Chrome Frame, sessionspeakers: Alex Russell, speakers: slightlyoff, sessionabstract: Google Chrome Frame brings the HTML5 platform and fast Javascript performance to IE6, 7 & 8. This session will cover the latest on Google Chrome Frame, what it can do for you and your customers, how it can be used, and a sneak peak into what's planned next., sessionrequirements: Web developer backgrounds, as well as enterprise IT, sessionlink: using-chrome-frame, sessionhashtag: #chrome7, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.73, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B9PNCdLZkBxY, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B9PNCdLZkBxY, waveid: w+9PNCdLZkBxYhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/dtbek2010-05-10T18:39:11.857ZRow: 20sessiondate: Thursday May 20, sessiontime: 4:45pm-5:45pm, room: 5, product: Chrome, track: Chrome, sessiontype: 201, sessiontitle: HTML5 status update, tags: HTML5, Chrome, sessionspeakers: Ian Fette, Jeff Chang, speakers: ifette, jeffreyc, sessionabstract: Where is HTML5 today? What new features are now broadly supported, what features are on the horizon, and what features are on the chopping block? How do we decide what to implement, what to propose, and what to drop? Find out., sessionrequirements: None, sessionlink: html5-status-chrome, sessionhashtag: #chrome8, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.75, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B9PNCdLZkBxf, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B9PNCdLZkBxf, waveid: w+9PNCdLZkBxfhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/dupz12010-05-10T18:39:11.857ZRow: 21sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 8, product: Apps Marketplace, Enterprise, track: Enterprise, sessiontype: 201, sessiontitle: Reach new customers fast: Learn how to sell your cloud app on the Google Apps Marketplace, tags: Enterprise, Google Apps, Customization, Apps Marketplace, ISV, SaaS, sessionspeakers: Scott McMullan, Jay Simmons (Atlassian), Chuck Dietrich (Sliderocket), Amit Kulkarni (Manymoon), speakers: scottmc, sessionabstract: In this introductory session we'll provide an overview of the Google Apps Marketplace and learn product and marketing best practices directly from 3 Marketplace ISVs., sessionrequirements: None, sessionlink: reach-new-customers-apps-marketplace, sessionhashtag: #enterprise1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.52, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BkrgNvLZkBkL, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BkrgNvLZkBkL, waveid: w+krgNvLZkBkLhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/e35dj2010-05-10T18:39:11.857ZRow: 22sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm
+, room: 4, product: Enterprise, track: Enterprise, sessiontype: 101, sessiontitle: Making Freemium work - converting free users to paying customers, tags: Enterprise, Monetization, Google Apps, SaaS, VC, sessionspeakers: Don Dodge (moderator), Brad Feld, Dave McClure, Jeff Clavier, Matt Holleran, Joe Kraus, speakers: dondodge, sessionabstract: A panel of prominent venture capital leaders will help you understand how to build free apps that can be upgraded to paid & how to build products that can be profitable., sessionrequirements: None, sessionlink: freemium-vc-panel-enterprise, sessionhashtag: #enterprise2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.53, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BY_40V7ZkB7J, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BY_40V7ZkB7J, waveid: w+Y_40V7ZkB7Jhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/e4jxw2010-05-10T18:39:11.857ZRow: 23sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 5, product: Apps Marketplace, Enterprise, track: Enterprise, sessiontype: 201, sessiontitle: Integrating your app with the Google Apps Marketplace: Navigation, SSO, Data APIs and manifests, tags: Enterprise, SSO, APIs, Apps Marketplace, ISV, SaaS, sessionspeakers: Ryan Boyd, Steve Bazyl, speakers: ryanboyd, sbazyl, sessionabstract: In this fast-paced, demo-focused session, you'll learn how to build, integrate, and sell a web app on the Google Apps Marketplace. We'll go end-to-end in 40 minutes with time left for Q&A., sessionrequirements: None, sessionlink: integrating-cloud-app-with-google-apps, sessionhashtag: #enterprise3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.54, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BTeHZ97ZkBAg, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BTeHZ97ZkBAg, waveid: w+TeHZ97ZkBAghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/e5yid2010-05-10T18:39:11.857ZRow: 24sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 7, product: Enterprise, track: Enterprise, sessiontype: 201, sessiontitle: Customizing Google Apps & integrating with customer environments, tags: Enterprise, Google Apps, Customization, sessionspeakers: Mike O'Brien, Matt Pruden (Appirio), Adam Graff (Genentech), Don Dodge (moderator), sessionabstract: Learn from real life examples of customizing Google Apps to meet customer requirements. Hear from the customer (Genentech) and the System Integrator (Appirio). Explore integration issues and deployment best practices with people who have done it. Get your questions answered in this session., sessionrequirements: None, sessionlink: customizing-google-apps-partners-customers, sessionhashtag: #enterprise4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.55, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BSVy-sLZkBYe, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BSVy-sLZkBYe, waveid: w+SVy-sLZkBYehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cg5gh2010-05-10T18:39:11.857ZRow: 25sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: 4, product: Docs API, Sites API, Secure Data Connector, SDC, track: Enterprise, sessiontype: 201, sessiontitle: Connecting your enterprise applications with Google Docs and Sites, tags: Enterprise, Docs API, Sites API, Apps, Google Data APIs, Secure Data Connector, SDC, sessionspeakers: Eric Bidelman, Vijay Bangaru, Matthew Tonkin (Memeo Inc), speakers: ericbidelman, vijayb
+, sessionabstract: Learn how your organization can harness the power of Google Docs and Sites directly from within your existing enterprise systems using our extensive APIs. Integrate with data from behind the firewall using Secure Data Connector. Upload, share, collaborate, and sync any file to Docs. Even automate the creation of project and team workspaces in a single click in Sites from within your CRM., sessionrequirements: Knowledge of Google Data APIs and top-level understanding of Docs and Sites, sessionlink: connecting-enterprise-apps-docs-sites, sessionhashtag: #enterprise6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.77, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bo01RhLZkBGa, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bo01RhLZkBGa, waveid: w+o01RhLZkBGahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/eilm22010-05-10T18:39:11.857ZRow: 26sessiondate: Thursday May 20, sessiontime: 2:15pm-3:15pm, room: 4, product: Enterprise, track: Enterprise, sessiontype: 201, sessiontitle: Launch your app inside of Google Apps with gadgets, tags: Gadgets, Enterprise, Google Apps, Gmail, Calendar, Sites, sessionspeakers: Dan Holevoet, speakers: danielholevoet, sessionabstract: Gadgets represent a valuable opportunity to get in front of the many Google Apps users who use Gmail, Google Calendar, and Google Sites throughout the day. This session will talk about how you can write gadgets as natural extensions of your existing products and take advantage of the unique opportunities available to gadgets in Google Apps., sessionrequirements: Basic familiarity with gadgets and/or HTML/JS., sessionlink: launch-your-app-google-apps-gadgets, sessionhashtag: #enterprise7, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.78, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BSVy-sLZkBX4, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BSVy-sLZkBX4, waveid: w+SVy-sLZkBX4http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ek06j2010-05-10T18:39:11.857ZRow: 27sessiondate: Thursday May 20, sessiontime: 3:30pm-4:30pm, room: 4, product: Google Apps Script, track: Enterprise, sessiontype: 201, sessiontitle: Scripting Google Apps for business process automation, tags: Apps Script, Enterprise, Google Apps, Spreadsheets, Calendar, Sites, Document List, sessionspeakers: Evin Levey, speakers: evin, sessionabstract: Learn how to use Google Apps for business process automation, and custom work-flow. We'll introduce the powerful scripting service along with several easy-to-use interfaces including Spreadsheets, Calendar, Sites and the Document List. We'll also demonstrate interoperability with third party web services and showcase exciting new developments in Google Apps Script., sessionrequirements: Basic knowledge of Google Apps, and an interest in learning how to automate processes using scripts., sessionlink: scripting-google-apps-for-business-process-automation, sessionhashtag: #enterprise8, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.79, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv7BXJrZkBOq, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv7BXJrZkBOq, waveid: w+v7BXJrZkBOqhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/eleqw2010-05-10T18:39:11.857ZRow: 28sessiondate: Thursday May 20, sessiontime: 4:45pm-5:45pm, room: 4, product: Enterprise, Google Apps, track: Enterprise, sessiontype: 201, sessiontitle: Building context-aware extensions for Gmail - Deep dive on Gmail contextual gadgets, tags: Enterprise, Google Apps, Gadgets, Gmail, ISV, SaaS, sessionspeakers: Dan Holevoet, speakers: danielholevoet, sessionabstract: How much time do your users spend in email everyday? Wouldn't it be nice if you could seamlessly integrate your apps into the rich context offered by their email and allow them to avoid shifting to new applications for various tasks? Gmail contextual gadgets allow you to register regular expressions and insert gadgets into e-mail messages based on their content. In this session, you'll learn how to create and distribute these powerful gadgets., sessionrequirements: Basic familiarity with gadgets and/or HTML/JS., sessionlink: deep-dive-gmail-contextual-gadgets, sessionhashtag: #enterprise9, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.7A, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B9PNCdLZkBt_, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B9PNCdLZkBt_, waveid: w+9PNCdLZkBt_http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/eedwv2010-05-10T18:39:11.857ZRow: 29sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 3, product: App Engine, track: Enterprise, App Engine, sessiontype: 201, sessiontitle: Run corporate applications on Google App Engine? Yes we do., tags: Enterprise, SaaS, PaaS, Hosting, App Engine, Java, sessionspeakers: Ben Fried, Irwin Boutboul, Justin McWilliams, Matthew Simmons, speakers: bf, irwin, ogle, mpsimmon , sessionabstract: Come hear Google's CIO Ben Fried and his team of engineers describe how Google builds real-world applications on App Engine. If you're interested in building corporate applications that run on Google's cloud, this team has been doing exactly that using App Engine, SDC, GWT, Django, Closure, Google Analytics, Bulkloader, cron server, and friends. In this session, you'll hear usable recipes for uniting Google's cloud technologies with firewalls, legacy databases, proprietary servers, legacy systems, and other realities in the life of an IT developer and learn how these teams have been able to respond more quickly to business needs while reducing operational burden. , sessionrequirements: None, sessionlink: run-corp-apps-on-app-engine, sessionhashtag: #appengine2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.4B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bo01RhLZkBJ0, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bo01RhLZkBJ0, waveid: w+o01RhLZkBJ0http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/efsh82010-05-10T18:39:11.857ZRow: 30sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 3, track: Enterprise, App Engine, sessiontype: 201, sessiontitle: It’s 2010: How is your move to the cloud doing?, tags: Enterprise, Saas, Paas, Hosting, App Engine, Google Apps, Java, sessionspeakers: David Glazer, speakers: dglazer, sessionabstract: Come discover the latest innovations from Google enabling IT and ISV developers to build on Google’s cloud-based storage and computing offerings. This talk will give a complete overview of Google’s commercial developer products and provide insights and best practices so enterprise developers can take more advantage of the cloud., sessionrequirements: None, sessionlink: its-2010-move-to-the-cloud, sessionhashtag: #appengine3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.4C, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BtaQlR7ZkBIS, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BtaQlR7ZkBIS, waveid: w+taQlR7ZkBIShttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/eh71p2010-05-10T18:39:11.857ZRow: 31sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 1, product: Auth, Google Data Protocol, OpenID, SSO, track: Enterprise, Google APIs, sessiontype: 201, sessiontitle: OpenID-based single sign on and OAuth data access for Google Apps, tags: Authentication, Google Data APIs, OAuth, AuthSub, Enterprise, OpenID, SSO, sessionspeakers: Ryan Boyd, David Primmer, speakers: ryanboyd, primmer, sessionabstract: A discussion of all the auth tangles you've encountered so far -- OpenID, SSO, 2-Legged OAuth, 3-Legged OAuth, and Hybrid OAuth. We'll show you when and where to use the APIs, code some example apps, and demonstrate how they all integrate with Google APIs and other developer products. We'll also talk about how these technologies relate to apps sold on the Google Apps Marketplace., sessionrequirements: None, sessionlink: untangling-auth-enterprise, sessionhashtag: #enterprise5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.76, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BY_40V7ZkB0w, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BY_40V7ZkB0w, waveid: w+Y_40V7ZkB0whttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bwhin2010-05-10T18:39:11.857ZRow: 32sessiondate: Thursday May 20, sessiontime: 4:45pm-5:45pm, room: 6, product: Android, track: Fireside Chats, Android, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the Android team, tags: Android, Mobile, Java, sessionspeakers: The Android team with Chris DiBona moderating, sessionabstract: Pull up a chair and join the Android team at Google for a fireside chat. It's your opportunity to ask us about the platform and to tell us where you'd like to see it go in the future., sessionlink: fireside-chat-android-team, sessionhashtag: #fireside-android, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.93, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv7BXJrZkBPC, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv7BXJrZkBPC, waveid: w+v7BXJrZkBPChttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bxw302010-05-10T18:39:11.857ZRow: 33sessiondate: Thursday May 20, sessiontime: 1:00pm-2:00pm, room: 8, product: Android, track: Fireside Chats, Android, sessiontype: Fireside Chat, sessiontitle: Fireside chat with Android handset manufacturers, tags: Android, Mobile, Java, OHA, Open Handset Alliance, sessionspeakers: Dino Brusco (Motorola), Erik Hellman (Sony Ericsson), Joon Kang (LGE), Ciaran Rochford (Samsung), Eric Chu (Google; moderator), sessionabstract: Come join us for a fireside chat with the top Android handset manufacturers. Hear about the types of devices being planned for 2010 and get your device-specific questions answered., sessionlink: fireside-chat-android-handset-manufacturers, sessionhashtag: #fireside-androidhandset, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.92, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQhu3aLZknM, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQhu3aLZknM, waveid: w+Qhu3aLZknMhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bv2y62010-05-10T18:39:11.857ZRow: 34sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: Fireside Chat Room, product: App Engine, track: Fireside Chats, App Engine, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the App Engine team, tags: App Engine, sessionspeakers: Brett Slatkin, Guido van Rossum, Matt Blain, Max Ross, Don Schwarz, Alfred Fuller, Kevin Gibbs, Sean Lynch
+, speakers: bslatkin, guido, mblain, maxr, schwardo, arfuller, kgibbs, slynch, sessionabstract: It's been an busy year for the App Engine team with lots of new features and lots of new developers. Come tell us about what you've loved and what still bugs you. With several members of the App Engine team on deck, you'll get the answers to your questions straight from the source., sessionlink: fireside-chat-app-engine, sessionhashtag: #fireside-appengine, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6A, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B9PNCdLZkBuX, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B9PNCdLZkBuX, waveid: w+9PNCdLZkBuXhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/etu5e2010-05-10T18:39:11.857ZRow: 35sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: Fireside Chat Room, product: Chrome, Chrome OS, track: Fireside Chats, Chrome, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the Google Chrome and Chrome OS teams, tags: Chrome, Chrome OS, sessionspeakers: Ian Fette, Brian Rakowski, Linus Upson, Caesar Sengupta, Matt Papakipos, speakers: ifette, brakowski, linus, caesars, papakipos, sessionabstract: Curious about what's new in Google Chrome, or what makes Google Chrome OS so exciting? We'll talk briefly about the major developments over the past year, and then field questions from the audience. If you're dying to know something, this is the place to find an answer., sessionlink: fireside-chat-chrome-chrome-os, sessionhashtag: #fireside-chrome, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.69, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B1g8YLbZkBFj, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B1g8YLbZkBFj, waveid: w+1g8YLbZkBFjhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ceqw02010-05-10T18:39:11.857ZRow: 36sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: Fireside Chat Room, product: Enterprise, Apps Marketplace, track: Fireside Chats, Enterprise, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the Enterprise team, tags: Enterprise, Google Apps, Apps Marketplace, sessionspeakers: Chris Vander Mey, Scott McMullan, Ryan Boyd, David Glazer, Ken Lin, speakers: cvandermey, scottmc, dglazer, ryanboyd, kenlin, sessionabstract: With the launch of the Google Apps Marketplace, we've introduced a new way to expose your software to businesses - and a new way to extend Google Apps. If you're interested in building apps, what we're thinking about, or if you have other questions about the Marketplace, pull up a chair., sessionlink: fireside-chat-enterprise-apps, sessionhashtag: #fireside-enterprise, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B1g8YLbZkBBh, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B1g8YLbZkBBh, waveid: w+1g8YLbZkBBhhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cdcbn2010-05-10T18:39:11.857ZRow: 37sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: Fireside Chat Room, product: Geo, track: Fireside Chats, Geo, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the Geo team, tags: Geo, Earth, Maps, Maps API, Maps Data API, Earth API, sessionspeakers: Thor Mitchell, Peter Birch, Matt Holden, Ben Appleton, Bart Locanthi, Thatcher Ulrich, speakers: thor, pbirch, mholden, appleton, bnl, tulrich, sessionabstract: Here's your opportunity to pick the brains of the people behind the Maps, Earth, and Maps Data APIs! We'll take a quick walk through the milestones of the last year, and then open it up to your questions. Don't miss your opportunity to get the straight scoop on all that's new in the world of Google Geo APIs., sessionlink: fireside-chat-geo, sessionhashtag: #fireside-geo, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.67, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BY_40V7ZkB74, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BY_40V7ZkB74, waveid: w+Y_40V7ZkB74http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ev8pv2010-05-10T18:39:11.857ZRow: 38sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: Fireside Chat Room, product: GWT, track: Fireside Chats, GWT, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the GWT team, tags: GWT, Google Web Toolkit, Java, JavaScript, AJAX, sessionspeakers: Bruce Johnson, Joel Webber, Ray Ryan, Amit Manjhi, Jaime Yap, Kathrin Probst, Eric Ayers, speakers: bruce, jgw, rjrjr, amitmanjhi, jaimeyap, kprobst, zundel, sessionabstract: If you're interested in what the GWT team has been up to since 2.0, here's your chance. We'll have several of the core engineers available to discuss the new features and frameworks in GWT, as well as to answer any questions that you might have., sessionlink: fireside-chat-gwt, sessionhashtag: #fireside-gwt, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.91, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQhu3aLZknM, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQhu3aLZknM, waveid: w+Qhu3aLZknMhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ewna82010-05-10T18:39:11.857ZRow: 39sessiondate: Thursday May 20, sessiontime: 2:15pm-3:15pm, room: Fireside Chat Room, product: Social, Open Web, Buzz, track: Fireside Chats, Social Web, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the Social Web team, tags: Social, Social Web, Buzz, sessionspeakers: David Glazer, DeWitt Clinton, John Panzer, Joseph Smarr, Sami Shalabi, Todd Jackson, speakers: dglazer, dewitt, jpanzer, jsmarr, shalabi, toddj, sessionabstract: Social is quickly becoming an integral part of how we experience the web, and this is your chance to pick the brains of the people who are working on Buzz, the Buzz API and the underlying open protocols such as Activity Streams and OAuth which are an essential component of a truly open & social web., sessionlink: fireside-chat-social-web, sessionhashtag: #fireside-socialweb, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.94, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BY_40V7ZkB1C, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BY_40V7ZkB1C, waveid: w+Y_40V7ZkB1Chttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ey1up2010-05-10T18:39:11.857ZRow: 40sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: Fireside Chat Room, product: Google Wave, track: Fireside Chats, Wave, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the Google Wave team, tags: Wave, sessionspeakers: Lars Rasmussen, Douwe Osinga, Jochen Bekmann, Alan Green, Pamela Fox, Dan Peterson, Stephanie Hannon, sessionabstract: Join the Google Wave team around the campfire to chat about all things Wave: the product, the API platform, and the wave federation protocol. Come to learn about the new Wave API features, get tips on how to build the best extensions, discuss how to take advantage of the open source code available and hear more about what users are doing with the product. This is an excellent opportunity to ask the engineering team questions directly, and learn more about where Wave is heading., sessionrequirements: None, sessionlink: fireside-chat-wave, sessionhashtag: #fireside-wave, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.9F, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BsErKcbZkjh.4, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BsErKcbZkjh.4, waveid: w+sErKcbZkjh.4http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/epmg72010-05-10T18:39:11.857ZRow: 41sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 4, product: Maps API, track: Geo, sessiontype: 201, sessiontitle: Stepping up: Porting v2 JavaScript Maps API applications to v3, tags: Maps, Maps API, JavaScript, sessionspeakers: Daniel Lee, speakers: daniellee, sessionabstract: The JavaScript Maps API v3 is the future of the Google Maps API. To take advantage of the many great features coming to the API you will need to migrate existing v2 applications to v3. This session will guide you through the process, illustrating how easy it is to start reaping the benefits in features and performance., sessionrequirements: Experience developing applications with the Maps JavaScript API v2, sessionlink: v2-javascript-maps-api-v3, sessionhashtag: #geo1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.56, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BU2jwj7ZkBfu, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BU2jwj7ZkBfu, waveid: w+U2jwj7ZkBfuhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/c6bhi2010-05-10T18:39:11.857ZRow: 42sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 1, product: Maps API, track: Geo, sessiontype: 201, sessiontitle: Map once, map anywhere: Developing geospatial applications for both desktop and mobile, tags: Maps, Maps API, Mobile, JavaScript, Geospatial, sessionspeakers: Mano Marks, speakers: mmarks, sessionabstract: As the number of desktop and mobile platforms proliferates the cost of developing and maintaining multiple versions of an application continues to increase. This session illustrates how the JS Maps API can be used to simplify cross platform geospatial application development by enabling a single implementation to be shared across multiple platforms, while maintaining a native look and feel., sessionrequirements: Web application development, sessionlink: map-once-map-anywhere-geospatial-apps, sessionhashtag: #geo2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.57, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkOR, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkOR, waveid: w+e6j2obZkORhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cbxr62010-05-10T18:39:11.857ZRow: 43sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 4, product: Maps Data API, track: Geo, sessiontype: 101, sessiontitle: Unleash your map data: Cloud computing for geospatial applications, tags: Maps, Maps API, Data, Cloud, Geospatial, sessionspeakers: Tom Manshreck, speakers: shreck, sessionabstract: The Google Maps API made geospatial development accessible to all but hosting your data remains complex and time consuming. This session will detail the services Google offers for storing your geospatial data in the cloud, illustrate the ways in which that data can be accessed and visualized, and walk through development of a retail store finder using these technologies., sessionrequirements: Experience developing web based applications that search over or visualise geospatial data is helpful but not essential., sessionlink: unleash-map-data-cloud-computing-geospatial-apps, sessionhashtag: #geo4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.58, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BTeHZ97ZkBDO, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BTeHZ97ZkBDO, waveid: w+TeHZ97ZkBDOhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/a59y22010-05-10T18:39:11.857ZRow: 44sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: 9, product: SketchUp, Building Maker, 3D Warehouse, track: Geo, sessiontype: 201, sessiontitle: The SketchUp 3D API: Working with 3D geospatial data, tags: SketchUp, Building Maker, 3D Warehouse, 3D, sessionspeakers: Matt Lowrie, speakers: mlowrie, sessionabstract: The world is a three dimensional space. Your geospatial applications should be showing it that way. This session will show how to create 3D data in Building Maker and then use the SketchUp API to customize that data to fit your needs., sessionrequirements: Basic understanding of 3D is helpful, but not essential, sessionlink: sketchup-3d-api, sessionhashtag: #geo5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.7B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv_K9zbZkBY1, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv_K9zbZkBY1, waveid: w+v_K9zbZkBY1http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/a6oij2010-05-10T18:39:11.857ZRow: 45sessiondate: Thursday May 20, sessiontime: 2:15pm-3:15pm, room: 9, product: KML, Earth API, Maps Data API, Geospatial, 3D, track: Geo, sessiontype: 201, sessiontitle: Mapping in 3D: Tips and tricks for Google Earth API and KML, tags: KML, Earth API, Maps Data API, Geospatial, 3D, sessionspeakers: Josh Livni, Mano Marks, speakers: jlivni, mmarks, sessionabstract: Google Earth and the Earth API can handle a tremendous amount of data. But you always have more. We will talk about integrating large datasets efficiently, coding for optimal performance, and taking advantage of advanced features in KML and the Earth API., sessionrequirements: Attendees should have a basic knowledge of Google Earth, and KML or Javascript.
+, sessionlink: mapping-in-3d-earth-api-kml, sessionhashtag: #geo6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.7C, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQhu3aLZkgD, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQhu3aLZkgD, waveid: w+Qhu3aLZkgDhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/a832w2010-05-10T18:39:11.857ZRow: 46sessiondate: Thursday May 20, sessiontime: 3:30pm-4:30pm, room: 1, product: Maps API, track: Geo, sessiontype: 301, sessiontitle: Moving beyond markers: Advanced Maps API customization, tags: Maps, Maps API, sessionspeakers: Jez Fletcher, David Day, speakers: jez, davidday, sessionabstract: With such a large number of Google Maps API sites online, it can be hard to make your site stand out from the crowd. This session covers ways in which you can enhance your Maps API application to truly differentiate it, including customizing your overlays, controls, and map., sessionrequirements: Experience developing applications with the Google Maps APIs, sessionlink: moving-beyond-markers-advanced-maps-api, sessionhashtag: #geo7, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.7D, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BTeHZ97ZkBCl, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BTeHZ97ZkBCl, waveid: w+TeHZ97ZkBClhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/a9hnd2010-05-10T18:39:11.857ZRow: 47sessiondate: Thursday May 20, sessiontime: 4:45pm-5:45pm, room: 1, product: Maps API, track: Geo, Tech Talks, sessiontype: Tech Talk, sessiontitle: How Maps API v3 came to be: Tips, tricks, and lessons learned in developing a cross platform desktop and mobile API, tags: Maps, Maps API, JavaScript, Mobile, sessionspeakers: Susannah Raub, Marc Ridey, speakers: sraub, mridey, sessionabstract: The Google JavaScript Maps API v3 celebrates its one year anniversary at this year's Google I/O. In this session, we reveal the reasons for embarking on a new API, the challenges we faced in developing a truly cross platform and cross device framework, and the lessons learned on the way., sessionrequirements: Experience developing desktop web based applications, and an interest in porting such applications to new mobile platforms.
+, sessionlink: maps-api-v3-api, sessionhashtag: #geo8, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.7F, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BkrgNvLZkBmC, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BkrgNvLZkBmC, waveid: w+krgNvLZkBmChttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/akq6p2010-05-10T18:39:11.857ZRow: 48sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 2, product: AJAX APIs, track: Google APIs, sessiontype: 101, sessiontitle: Bringing Google to your site, tags: AJAX APIs, Friend Connect, Ads APIs, Web Elements, Analytics, sessionspeakers: DeWitt Clinton, Jeff Scudder, speakers: dewitt, jscudder, sessionabstract: This is an overview session about some of the many ways that a developer can enrich their site and more fully engage their visitors using Google products. We will cover a variety of products and APIs designed to quickly and easily improve and monetize your site, from AdSense and Custom Search to Feeds and Web Elements. We'll include announcements for several eye-popping new features., sessionrequirements: Basic HTML and JavaScript familiarity, sessionlink: bringing-google-to-your-site-googleapis, sessionhashtag: #googleapis1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.98, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bo01RhLZkBDwC, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bo01RhLZkBDwC, waveid: w+o01RhLZkBDwChttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/aaw7q2010-05-10T18:39:11.857ZRow: 49sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 2, product: PowerMeter API, track: Google APIs, sessiontype: 101, sessiontitle: Knowledge is (less) power: Exploring the Google PowerMeter API, tags: PowerMeter API, APIs, OAuth, Java, Python, Energy, sessionspeakers: Srikanth Rajagopalan, Rus Heywood, speakers: srikanth, rheywood, sessionabstract: In this session we will discuss interesting ways to make users understand and manage their home energy use through Google PowerMeter. The Google PowerMeter API currently available allows devices to integrate with Google PowerMeter. Come learn how you can build with the API and about exciting developments ahead. We will dig into the implementation details for integrations and open up the floor for other ideas that may be relevant., sessionrequirements: Web developer background, hardware enthusiasts, sessionlink: google-powermeter-api, sessionhashtag: #googleapis2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.59, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQhu3aLZkkc, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQhu3aLZkkc, waveid: w+Qhu3aLZkkchttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/acas72010-05-10T18:39:11.857ZRow: 50sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 2, product: Google Charts Toolkit, Visualization API, Chartserver
+, track: Google APIs, sessiontype: 201, sessiontitle: Google Charts Toolkit: Google's new unified approach for creating dynamic charts on the web, tags: Charts, Toolkit, Visualization API, JavaScript, Images, Data source, sessionspeakers: Michael Fink, Amit Weinstein, speakers: fink, amitw, sessionabstract: Google Charts Toolkit is Google's unified approach for creating charts on the web. It provides a rich gallery spanning from pie charts to interactive heat-maps and from organizational trees to motion charts. The toolkit lets developers choose between JavaScript based client-side rendering and image based server-side rendering. We will present the relative strengths of these two approaches, and unveil the future visual design of Google Charts. , sessionrequirements: Basic knowledge of Google Visualization API and Google Charts API, sessionlink: google-charts-toolkit-googleapis, sessionhashtag: #googleapis4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.5A, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BSVy-sLZkBX8, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BSVy-sLZkBX8, waveid: w+SVy-sLZkBX8http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/adpck2010-05-10T18:39:11.857ZRow: 51sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 2, product: Google Analytics API, track: Google APIs, sessiontype: 201, sessiontitle: Google Analytics APIs: End to end, tags: Google Analytics, JavaScript, Java, Gadgets, Mobile, ga.js, Video, sessionspeakers: Nick Mihailovski, speakers: nm, sessionabstract: Google Analytics measures performance of your website. Learn advanced techniques on how to use our tracking, processing and data export APIs as we walk you through an example of creating a most visited pages web element for your website., sessionrequirements: Intermediate Knowledge of Google Analytics, Javascript, sessionlink: google-analytics-apis-end-to-end, sessionhashtag: #googleapis5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.7E, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BtaQlR7ZkBF9, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BtaQlR7ZkBF9, waveid: w+taQlR7ZkBF9http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/af3x12010-05-10T18:39:11.857ZRow: 52sessiondate: Thursday May 20, sessiontime: 2:15pm-3:15pm, room: 2, product: YouTube API, track: Google APIs, sessiontype: 201, sessiontitle: YouTube API uploads: Tools, tips, and best practices, tags: Google Data APIs, Android, iPhone, YouTube, YouTube APIs, Mobile, sessionspeakers: Jeffrey Posnick, Gareth McSorley, Kuan Yong, speakers: jeffy, gmcsorley, kuanyong, sessionabstract: Are you integrating YouTube upload functionality into your mobile, desktop, or web app? Learn about Android and iPhone upload best practices, resuming interrupted YouTube uploads, and the YouTube Direct embeddable iframe for soliciting uploads on your existing web pages., sessionrequirements: Interest in adding YouTube upload functionality to mobile, desktop, or web-based applications, sessionlink: youtube-api-uploads, sessionhashtag: #googleapis7, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.80, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bo01RhLZkBHy, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bo01RhLZkBHy, waveid: w+o01RhLZkBHyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/auk5k2010-05-10T18:39:11.857ZRow: 53sessiondate: Thursday May 20, sessiontime: 3:30pm-4:30pm, room: 2, product: Google APIs, track: Google APIs, sessiontype: 201, sessiontitle: How Google builds APIs, tags: APIs, Atom, JSON, Java, Python, sessionspeakers: Zach Maier, Joseph Schorr, Mark Stahl, speakers: zpm, jschorr, mstahl, sessionabstract: Dive into the API development process at Google, and discover how we routinely take our full-featured APIs from design to testing to production in less than an hour. We'll present Google API design goals and best practices, showcase our new API infrastructure, and demonstrate how you can take advantage of the secret "power" features hiding in every Google API., sessionrequirements: A basic understanding of web APIs is helpful but not necessarily required, sessionlink: how-google-builds-apis, sessionhashtag: #googleapis8, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.81, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B9rTSb7ZkBm1, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B9rTSb7ZkBm1, waveid: w+9rTSb7ZkBm1http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/avyq12010-05-10T18:39:11.857ZRow: 54sessiondate: Thursday May 20, sessiontime: 4:45pm-5:45pm, room: 2, product: AdSense for Mobile Apps, Google Analytics, track: Google APIs, Android, sessiontype: 201, sessiontitle: Analyzing and monetizing your Android & iPhone apps, tags: AdSense for Mobile Apps, Google Analytics, Android, Mobile, Java, sessionspeakers: Chrix Finne, Jim Kelm, speakers: chrix, kelm, sessionabstract: If you've got a great mobile app, you're likely interested to know how users interact with your app and how to build a business from it. In this session you'll learn how you can drive awareness and earn revenue for your app using AdSense for Mobile Apps. We'll also discuss how using Google Analytics can help with your app development by providing insights into where your app users are coming from and how they're engaging with your app. We'll share tips, tricks, and examples of real-world mobile apps that have found success., sessionrequirements: Attendees should have an existing iPhone or Android app, but not necessary. Familiarity and current account setup on AdSense and Google Analytics useful but not necessary, sessionlink: analyzing-monetizing-mobile-apps, sessionhashtag: #googleapis9, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.82, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQ9WoGLZkBoo, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQ9WoGLZkBoo, waveid: w+Q9WoGLZkBoohttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/am4r22010-05-10T18:39:11.857ZRow: 55sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 201, sessiontitle: Measure in milliseconds redux: Meet Speed Tracer, tags: GWT, Google Web Toolkit, AJAX, Speed Tracer, Chrome, Java, JavaScript, sessionspeakers: Kelly Norton, speakers: knorton, sessionabstract: It turns out that web apps can be slow for all sorts of opaque and unintuitive reasons. Don't be fooled into thinking that bloated, slow JavaScript is the only culprit. This session introduces you to Speed Tracer, a new GWT tool that can tell you exactly where time is going within the browser., sessionrequirements: Intermediate knowledge of Javascript and DOM concepts., sessionlink: measure-in-milliseconds-speed-tracer-gwt, sessionhashtag: #gwt1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.5B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BeRiTZrZkC3Z, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BeRiTZrZkC3Z, waveid: w+eRiTZrZkC3Zhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/anjbj2010-05-10T18:39:11.857ZRow: 56sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 201, sessiontitle: Faster apps faster: Optimizing apps with the GWT Compiler, tags: GWT, Google Web Toolkit, AJAX, Eclipse, IDE, Java, JavaScript, sessionspeakers: Ray Cromwell, speakers: cromwellian, sessionabstract: The GWT compiler isn't just a Java to JavaScript transliterator. It performs many optimizations along the way. In this session, we'll show you not only the optimizations performed, but how you can get more out of the compiler itself. Learn how to speed up compiles, use -draftCompile, compile for only one locale/browser permutation, and more., sessionrequirements: Intermediate knowledge of Java, Google Web Toolkit, and compiler optimization strategies., sessionlink: faster-apps-faster-gwt-compiler, sessionhashtag: #gwt2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.5C, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B9rTSb7ZkBlL, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B9rTSb7ZkBlL, waveid: w+9rTSb7ZkBlLhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/aoxvw2010-05-10T18:39:11.857ZRow: 57sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 201, sessiontitle: Architecting for performance with GWT, tags: GWT, Google Web Toolkit, AJAX, Java, JavaScript, Enterprise, sessionspeakers: Joel Webber, Adam Schuck, speakers: jgw, schuck, sessionabstract: Modern web applications are quickly evolving to an architecture that has to account for the performance characteristics of the client, the server, and the global network connecting them. Should you render HTML on the server or build DOM structures with JS in the browser, or both? This session discusses this, as well as several other key architectural considerations to keep in mind when building your Next Big Thing., sessionrequirements: Intermediate knowledge of client/server relationships, Java, and Google Web Toolkit, sessionlink: architecting-performance-gwt, sessionhashtag: #gwt3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.5D, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQhu3aLZker, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQhu3aLZker, waveid: w+Qhu3aLZkerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/aqcgd2010-05-10T18:39:11.857ZRow: 58sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 301, sessiontitle: GWT Linkers target HTML5 Web Workers, Chrome Extensions, and more, tags: GWT, Google Web Toolkit, AJAX, Java, JavaScript, HTML5, sessionspeakers: Matt Mastracci, sessionabstract: At its core GWT has a well-defined and customizable mechanism -- called Linkers -- that controls exactly how GWT's compiled JavaScript should be packaged, served, and run. This session will describe how to create linkers and explains some of the linkers we've created, including a linker that turns a GWT module into an HTML5 Web Worker and one that generates an HTML App Cache manifest automatically., sessionrequirements: Advanced knowledge of Google Web Toolkit. Intermediate knowledge of Java., sessionlink: gwt-linkers-webworkers-extensions, sessionhashtag: #gwt4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.5E, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BetojYbZkBEt, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BetojYbZkBEt, waveid: w+etojYbZkBEthttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/b4e4j2010-05-10T18:39:11.857ZRow: 59sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 201, sessiontitle: GWT's UI overhaul: UiBinder, ClientBundle, and Layout Panels, tags: GWT, Google Web Toolkit, AJAX, Java, JavaScript, sessionspeakers: Joel Webber, Ray Ryan, speakers: jgw, rjrjr, sessionabstract: There have been some really huge improvements in GWT's UI fundamentals over the past year. We've introduced features such as UiBinder, ClientBundle, CssResource, and uber layout panels that allow you to build fast UIs in a sane manner. Come see how fun/easy/fast it can be to use these technologies in harmony to overhaul your UI., sessionrequirements: Intermediate knowledge of Google Web Toolkit., sessionlink: gwt-ui-overhaul, sessionhashtag: #gwt5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.83, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQ9WoGLZkBpL, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQ9WoGLZkBpL, waveid: w+Q9WoGLZkBpLhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/b5sow2010-05-10T18:39:11.857ZRow: 60sessiondate: Thursday May 20, sessiontime: 1:00pm-2:00pm, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 201, sessiontitle: GWT + HTML5 can do what?!, tags: GWT, Google Web Toolkit, AJAX, HTML5, Java, JavaScript, Enterprise, sessionspeakers: Joel Webber, Ray Cromwell, Stefan Haustein, speakers: jgw, cromwellian, haustein, sessionabstract: How can you take advantage of new HTML5 features in your GWT applications? In this session, we answer that question in the form of demos -- lots and lots of demos. We'll cover examples of how to use Canvas for advanced graphics, CSS3 features, Web Workers, and more within your GWT applications., sessionrequirements: Baseline knowledge of HTML5 as well as intermediate knowledge of Java and Google Web Toolkit, sessionlink: gwt-html5, sessionhashtag: #gwt6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.84, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BetojYbZkBDR, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BetojYbZkBDR, waveid: w+etojYbZkBDRhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/b779d2010-05-10T18:39:11.857ZRow: 61sessiondate: Thursday May 20, sessiontime: 2:15pm-3:15pm, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 301, sessiontitle: GWT testing best practices, tags: GWT, Google Web Toolkit, AJAX, Testing, Java, JavaScript, sessionspeakers: Daniel Danilatos, speakers: danilatos, sessionabstract: GWT has a lot of little-publicized infrastructure that can help you build apps The Right Way: test-driven development, code coverage, comprehensive unit tests, and integration testing using Selenium or WebDriver. This session will survey GWT's testing infrastructure, describe some best practices we've developed at Google, and help you avoid common pitfalls., sessionrequirements: Advanced knowledge of Java, Google Web Toolkit, and current testing frameworks., sessionlink: gwt-continuous-build-testing, sessionhashtag: #gwt7, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.85, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BeRiTZrZkC31, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BeRiTZrZkC31, waveid: w+eRiTZrZkC31http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/axdae2010-05-10T18:39:11.857ZRow: 62sessiondate: Thursday May 20, sessiontime: 3:30pm-4:30pm, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 301, sessiontitle: Architecting GWT applications for production at Google, tags: GWT, Google Web Toolkit, AJAX, Java, JavaScript, Enterprise, sessionspeakers: Ray Ryan, speakers: rjrjr, sessionabstract: For large GWT applications, there's a lot you should think about early in the design of your project. GWT has a variety of technologies to help you, but putting it all together can be daunting. This session walks you through how teams at Google architect production-grade apps, from design to deployment, using GWT., sessionrequirements: Advanced knowledge of Google Web Toolkit. Intermediate knowledge of design patterns and Java., sessionlink: architecting-production-gwt, sessionhashtag: #gwt8, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.86, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BeRiTZrZkC31, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BeRiTZrZkC31, waveid: w+eRiTZrZkC31http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ayruv2010-05-10T18:39:11.857ZRow: 63sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 1, product: Social, OpenSocial, Social Web, track: Social Web, sessiontype: 101, sessiontitle: The open & social web, tags: ActivityStrea.ms, Salmon, WebFinger, PubSubHubbub, PortableContacts, OpenID, OAuth, OpenSocial, sessionspeakers: Chris Messina, speakers: messina, sessionabstract: This session will cover the latest and most important trends of the Social Web and dive deep into where this is all going, at both technical and conceptual levels. From the concepts of digital identity, relationships, and social objects, this session will cover emerging technologies like WebFinger, Salmon, ActivityStrea.ms, OpenID, OAuth and OpenSocial., sessionrequirements: A web developer background and an interest in the social web., sessionlink: open-and-social-web, sessionhashtag: #googlesocial, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.5F, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BfZ2urbZk6F, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BfZ2urbZk6F, waveid: w+fZ2urbZk6Fhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/c7q1z2010-05-10T18:39:11.857ZRow: 64sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 8, product: Google Buzz APIs, track: Social Web, sessiontype: 101, sessiontitle: What's the hubbub about Google Buzz APIs?, tags: Buzz, ActivityStrea.ms, OpenID, Salmon, PubSubHubbub, OAuth, sessionspeakers: Chris Chabot, Loic Le Meur (Seesmic), Ming Yong (Socialwok), speakers: chabotc, sessionabstract: Google Buzz is a new way to share updates, photos, videos and more, and start conversations about the things you find interesting. In this session, we'll take a deep dive into building with the Buzz APIs and the open standards it uses, such as ActivityStrea.ms, PubSubHubbub, OAuth, Salmon and WebFinger., sessionrequirements: A web developer background and an interest in the social web, sessionlink: google-buzz-apis, sessionhashtag: #buzzapi, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.99, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bqlswu7ZkBSc, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bqlswu7ZkBSc, waveid: w+qlswu7ZkBSchttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/b06f82010-05-10T18:39:11.857ZRow: 65sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 9, product: Social Web, Social, track: Social Web, sessiontype: 201, sessiontitle: Bridging the islands: Building fluid social experiences across websites, tags: OpenID, OAuth, Portable Contacts, ActivityStrea.ms, Salmon, sessionspeakers: John Panzer, Joseph Smarr, speakers: jpanzer, jsmarr, sessionabstract: As more sites add social functionality, profiles, friends, and conversations are becoming increasingly fragmented and redundant. But an emerging collection of open technologies aim to help bridge these social islands, allowing users to seamlessly move between sites, bring their friends along, and have unified conversations that span multiple web sites. Come learn how OpenID, OAuth, Portable Contacts, ActivityStrea.ms, and Salmon can help you connect your site to the rest of the Social Web, increasing your traffic, engagement, and relevance to your users., sessionlink: building-fluid-social-experiences-across-websites, sessionhashtag: #islandsio, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.60, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQhu3aLZkgi, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQhu3aLZkgi, waveid: w+Qhu3aLZkgihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/b1kzp2010-05-10T18:39:11.857ZRow: 66sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 9, product: PubSubHubbub, track: Social Web, sessiontype: 201, sessiontitle: Make your application real-time with PubSubHubbub, tags: PubSubHubbub, real-time, Atom, RSS, sessionspeakers: Brett Slatkin, speakers: bslatkin, sessionabstract: This session will go over how to add support for the PubSubHubbub protocol to your website. You'll learn how to turn Atom and RSS feeds into real-time streams. We'll go over how to consume real-time data streams and how to make your website reactive to what's happening on the web right now., sessionrequirements: Knowledge of web technologies (Atom, RSS, HTTP, HTML) and content-mangement systems (WordPress, etc)., sessionlink: real-time-apps-pubsubhubbub, sessionhashtag: #PuSHio, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.61, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BeRiTZrZkC0N, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BeRiTZrZkC0N, waveid: w+eRiTZrZkC0Nhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/be8562010-05-10T18:39:11.857ZRow: 67sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 9, product: iGoogle, OpenSocial, track: Social Web, sessiontype: 201, sessiontitle: iGoogle developer portal and tools, tags: iGoogle, OpenSocial, OSDE, OpenSocial Development Environment, HTML, JavaScript, sessionspeakers: Shih-chia Cheng, Albert Cheng, speakers: shihchia, albertcheng, sessionabstract: Learn how to build and maintain better OpenSocial gadgets for iGoogle. Two major applications will be introduced. The first one is iGoogle Gadget Dashboard for managing gadgets created by you. The second one is OSDE (OpenSocial Development Environment) which is an Eclipse plugin for developers to easily implement gadgets., sessionrequirements: Knowledge of web technology such as HTML and JavaScript., sessionlink: igoogle-developer-portal, sessionhashtag: #igoogleio, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.87, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BkrgNvLZkBhk, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BkrgNvLZkBhk, waveid: w+krgNvLZkBhkhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bfmpn2010-05-10T18:39:11.857ZRow: 68sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: 1, product: Google Buzz APIs, track: Social Web, sessiontype: 201, sessiontitle: Surf the stream: Google Buzz, location, and social gaming, tags: Buzz, Social, mobile, HTML5, App Engine, sessionspeakers: Bob Aman, Timothy Jordan, speakers: bobaman, timothyjordan, sessionabstract: Google Buzz has a feature-rich API that allows you to do all kinds of interesting things with conversations and location. In this session we'll build a Buzz-tastic mobile game using App Engine, HTML5, and the Buzz API for social awesomeness. , sessionrequirements: Knowledge of Python, the web and a good spirited nature., sessionlink: surf-the-stream-google-buzz-social-gaming, sessionhashtag: #buzzbingo, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.88, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bo01RhLZkBJ6, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bo01RhLZkBJ6, waveid: w+o01RhLZkBJ6http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bh1a02010-05-10T18:39:11.857ZRow: 69sessiondate: Thursday May 20, sessiontime: 4:45pm-5:45pm, room: 7, product: Social Web, Social, track: Social Web, sessiontype: 201, sessiontitle: Where is the social web going next?, tags: ActivityStrea.ms, Salmon, WebFinger, PubSubHubbub, PortableContacts, OpenID, OAuth, OpenSocial, sessionspeakers: Adam Nash, Daniel Raffel, Chris Messina, Angus Logan, Ryan Sarver, Chris Cole, Kara Swisher (moderator), speakers: messina, sessionabstract: With the advent of social protocols like OAuth, OpenID and ActivityStrea.ms, it's clear that the web has gone social and is becoming more open. Adam Nash (LinkedIn), Daniel Raffel (Yahoo), Chris Messina (Google), Angus Logan (Microsoft), Ryan Sarver (Twitter), and Chris Cole (MySpace) will discuss the importance of such emerging technologies, how they've adopted them in their products and debate what's next. Kara Swisher will moderate., sessionrequirements: An interest in the social web, sessionlink: where-is-the-social-web-going-next, sessionhashtag: #socialfuture, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.8A, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BYTqpC7ZkBXT, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BYTqpC7ZkBXT, waveid: w+YTqpC7ZkBXThttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bifuh2010-05-10T18:39:11.857ZRow: 70sessiondate: Thursday May 20, sessiontime: 1:00pm-2:00pm, room: 9, product: OpenSocial, track: Social Web, Enterprise, sessiontype: 201, sessiontitle: Best practices for implementing OpenSocial in the Enterprise, tags: OpenSocial, Enterprise, sessionspeakers: Mark Weitzel, Matt Tucker, Mark Halvorson, Helen Chen, Chris Schalk, speakers: cschalk, sessionabstract: Enterprise deployments of OpenSocial technologies brings an additional set of considerations that may not be apparent in a traditional social network implementation. In this session, several enterprise vendors will demonstrate how they've been working together to address these issues in a collection of "Best Practices". This session will also provide a review of existing challenges for enterprise implementations of OpenSocial., sessionrequirements: General understanding of OpenSocial technologies. Some Enterprise experience is also recommended., sessionlink: opensocial-enterprise-panel, sessionhashtag: #opensocial, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.89, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BWDgRyLZkfF, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BWDgRyLZkfF, waveid: w+WDgRyLZkfFhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ba0fz2010-05-10T18:39:11.857ZRow: 71sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 5, track: Tech Talks, sessiontype: Tech Talk, sessiontitle: Ignite Google I/O, tags: Ignite, geek culture, sessionspeakers: Brady Forrest, Annalee Newitz, Ben Huh, Matt Harding, Clay Johnson, Bradley Vickers, Aaron Koblin, Michael Van Riper, Anne Veling, James Young, sessionabstract: <p><a href="http://ignite.oreilly.com">Ignite</a> captures the best of geek culture in a series of five-minute speed presentations. Each speaker gets 20 slides that auto-advance after 15 seconds. Check out <a href="http://code.google.com/events/io/2009/sessions/IgniteGoogleIO.html">last year's Ignite Google I/O</a>.</p>
+<p>
+Presenting this year's Ignite Google I/O speakers:
+</p>
+<p>
+<a href="http://www.techsploitation.com/">Annalee Newitz</a> (<a href="http://io9.com">io9</a>) - Weird Science
+ </p>
+<p>
+<a href="http://www.benhuh.com/">Ben Huh</a> (<a href="http://icanhascheezburger.com/">I Can has Cheez Burger</a>) - <strong>Evolution of the Meme</strong>
+</p><p>
+<a href="http://en.wikipedia.org/wiki/Matt_Harding">Matt Harding</a> (<a href="http://www.wherethehellismatt.com/">Where The Hell Is Matt?</a>) - <strong>The Imaginary Line of Ancient Cosmic Weirdness </strong>
+</p><p>
+<a href="http://twitter.com/cjoh">Clay Johnson</a> (<a href="http://sunlightfoundation.com/">Sunlight Foundation</a>) - <strong>Tales from the District: How Washington Publishes Data</strong>
+</p><p>
+<a href="http://www.flickr.com/nwrower">Bradley Vickers</a> - <strong>How to Row across the North Atlantic, Ration Food and Not Have Your Teammates Eat You</strong>
+</p><p>
+<a href="http://www.aaronkoblin.com/">Aaron Koblin</a> (Google/<a href="http://www.thesheepmarket.com/">Sheep Market</a>) - <strong>Digital Art</strong>
+</p>
+<p>
+<a href="http://van-riper.blogspot.com/">Michael Van Riper</a> - <strong>Confessions of a Serial Community Organizer</strong>
+</p><p>
+<a href="http://www.beyondtrees.com/weblog/">Anne Veling</a> - <strong>Feeling Lonely on Google Wave</strong></p>
+<p><a href="http://twitter.com/jamesyoung6979">James Young</a> - <strong>You Sank My Battleship!</strong></p>, sessionrequirements: Affinity for geek culture, sessionlink: ignite-google-io, sessionhashtag: #ignitegoogleio, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BO4sGNLZkCK4, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BO4sGNLZkCK4, waveid: w+O4sGNLZkCK4http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bbf0c2010-05-10T18:39:11.857ZRow: 72sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 9, product: Closure Library, track: Tech Talks, sessiontype: 201, sessiontitle: Opening up Closure Library, tags: Closure Tools, Closure Library, JavaScript, sessionspeakers: Nathan Naze, speakers: nnaze, sessionabstract: Closure Library is the open-source JavaScript library behind some of Google's big web apps like Gmail and Google Docs. This session will tour the broad library, its object-oriented design, and its namespaced organization. We'll explain how it works and how to integrate it in your setup, both for development and optimized for a live application using Closure Compiler., sessionrequirements: The session assumes JavaScript and web development experience., sessionlink: closure-library, sessionhashtag: #techtalks-closure, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.64, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv7BXJrZkBME, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv7BXJrZkBME, waveid: w+v7BXJrZkBMEhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bctkt2010-05-10T18:39:11.857ZRow: 73sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 4, product: Go Programming Language, track: Tech Talks, sessiontype: Tech Talk, sessiontitle: Go programming, tags: Go, open source, language, C, C++, Java, Python, sessionspeakers: Rob Pike, Russ Cox, speakers: r, rsc, sessionabstract: The Go programming language was released as an open source project in late 2009. This session will illustrate how programming in Go differs from other languages through a set of examples demonstrating features particular to Go. These include concurrency, embedded types, methods on any type, and program construction using interfaces. Very little time will be spent waiting for compilation., sessionrequirements: Programming experience in a language such as C, C++, Java, or Python. Some familiarity with Go helpful but not required., sessionlink: go-programming, sessionhashtag: #techtalks-go, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.8D, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BWDgRyLZkb2, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BWDgRyLZkb2, waveid: w+WDgRyLZkb2http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bpgoi2010-05-10T18:39:11.857ZRow: 74sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: 2, track: Tech Talks, sessiontype: Tech Talk, sessiontitle: How to lose friends and alienate people: The joys of engineering leadership, tags: Engineering, leadership, management, storytelling, sessionspeakers: Brian W. Fitzpatrick, Ben Collins-Sussman, speakers: fitz, sussman, sessionabstract: Are you considered the 'point' person for your team? Do you have sweaty palms, headaches, and a calendar full of meetings? You may have an affliction called 'manager'. 3 out of 4 engineers aren't aware of this condition, but it is treatable through careful analysis and therapy. We'll examine how you may have arrived at this state and how you can once again regain your self-respect and the respect of your peers. Hear real-life stories of both good and bad leadership. Learn to lead by following., sessionrequirements: Sense of humor, sessionlink: lose-friends-alienate-people-engineering-leadership, sessionhashtag: #techtalks-leadership, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.8E, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B9rTSb7ZkBlS, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B9rTSb7ZkBlS, waveid: w+9rTSb7ZkBlShttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bqv8z2010-05-10T18:39:11.857ZRow: 75sessiondate: Thursday May 20, sessiontime: 2:15pm-3:15pm, room: 8, product: Web Search, track: Tech Talks, sessiontype: Tech Talk, sessiontitle: SEO site advice from the experts, tags: Search, SEO, site architecture, sessionspeakers: Matt Cutts, Greg Grothaus, Evan Roseman, speakers: matt, greggrothaus, eroseman, sessionabstract: A perfect opportunity to get your website reviewed by the experts in the Google Search Quality team. Attendees can get concrete search engine optimization (SEO) feedback on their own sites. We'll also answer real-life questions that affect developers when it comes to optimizing their websites for search., sessionrequirements: Perfect for web developers who want to learn more about search engine optimization (SEO), sessionlink: seo-site-review-from-experts, sessionhashtag: #techtalks-seo, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.8F, wavelink: https://wave.google.com/wave/#restored:search,restored:wave:googlewave.com!w%252B-Xhdu7ZkBLf, _e8rn7: https://wave.google.com/wave/#restored:search,restored:wave:googlewave.com, _dmair: w%252B-Xhdu7ZkBLf, waveid: w+-Xhdu7ZkBLfhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bs9tc2010-05-10T18:39:11.857ZRow: 76sessiondate: Thursday May 20, sessiontime: 3:30pm-4:30pm, room: 9, product: Page Speed, track: Tech Talks, sessiontype: 101, sessiontitle: Optimize every bit of your site serving and web pages with Page Speed, tags: Design, Web development, Website optimization, Page Speed, Analytics, sessionspeakers: Richard Rabbat, Bryan McQuade, speakers: rabbat, bmcquade, sessionabstract: Page Speed is an open-source Firefox/Firebug Add-on. Webmasters and web developers can use Page Speed to evaluate the performance of their web pages and to get suggestions on how to improve them. Learn about the latest rules of web development we've added, optimizations that we've updated, go over a new refreshed UI, see how to collect data through beacons to track progress over time, cut and paste fixes, and how to work with 3rd party libraries more effectively, including Google Analytics., sessionlink: optimize-site-serving-page-speed, sessionhashtag: #techtalks-speed, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.90, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BU2jwj7ZkBer, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BU2jwj7ZkBer, waveid: w+U2jwj7ZkBerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/btodt2010-05-10T18:39:11.857ZRow: 77sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 9, track: Tech Talks, sessiontype: Tech Talk, sessiontitle: Beyond design: Creating positive user experiences, tags: UI, UX, design, user experience, users, sessionspeakers: John Zeratsky, Matt Shobe, speakers: jazer, shobe, sessionabstract: Good user experience isn't just about good design. Learn how to create a positive user experience by being fast, open, engaged, surprising, polite, and, well... being yourself. Chock full of examples from the web and beyond, this talk is a practical introduction for developers who are passionate about user experience but may not have a background in design., sessionrequirements: Willingness to let old assumptions about their inability to solve certain design challenges get a bit banged up., sessionlink: beyond-design-user-experience, sessionhashtag: #techtalks-ux, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.97, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BtaQlR7ZkBhw, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BtaQlR7ZkBhw, waveid: w+taQlR7ZkBhwhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bjueu2010-05-10T18:39:11.857ZRow: 78sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 4, track: Tech Talks, sessiontype: Tech Talk, sessiontitle: Technology, innovation, computer science, & more: VC panel, tags: VC, venture capital, technology, sessionspeakers: Albert Wenger, Chris Dixon, Dave McClure, Brad Feld, Paul Graham, Dick Costolo (moderator), sessionabstract: What do notable tech-minded VCs think about big trends happening today? In this session, you'll get to hear from and ask questions to a panel of well-respected investors, all of whom are programmers by trade. Albert Wenger, Chris Dixon, Dave McClure, Paul Graham, and Brad Feld will duke it out on a number of hot tech topics with Dick Costolo moderating. , sessionrequirements: None, sessionlink: tech-innovation-cs-vc-panel, sessionhashtag: #techtalks-vc, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.65, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv_K9zbZkBXX, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv_K9zbZkBXX, waveid: w+v_K9zbZkBXXhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bo2452010-05-10T18:39:11.857ZRow: 79sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 8, product: Google Wave, track: Wave, sessiontype: 201, sessiontitle: Google Wave API design principles: Anatomy of a great extension, tags: Wave, sessionspeakers: Pamela Fox, Michael Goderbauer (Hasso Plattner Institute), speakers: pamelafox, sessionabstract: Google Wave is all about collaboration, and the most successful extensions are user-friendly and collaborative. Wave robots should be as intuitive to communicate with as a human, and play well with other robots; Wave gadgets should extend the metaphors of the textual collaboration into the visual. In this talk, we'll discuss the design and privacy principles you should consider while building extensions, and show examples of extensions that demonstrate these principles. , sessionrequirements: Basic understanding of the Google Wave extensions APIs (gadgets and robots), sessionlink: wave-api-design-extensions, sessionhashtag: #wave1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.62, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BkrgNvLZkBh6, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BkrgNvLZkBh6, waveid: w+krgNvLZkBh6http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bmnjo2010-05-10T18:39:11.857ZRow: 80sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 8, product: Google Wave, track: Wave, sessiontype: 101, sessiontitle: Waving across the web, tags: Wave, sessionspeakers: Dhanji Prasanna, Douwe Osinga, speakers: dhanji, douwe, sessionabstract: This talk focuses on using the Google Wave APIs outside of the Google Wave product. We'll show how to take advantage of embedded waves for commenting and discussions on your website, how to integrate with WaveThis to make your website more collaborative, and how to use the wave data APIs to get access to wave content from your website., sessionrequirements: JavaScript. Basic understanding of Google Wave is helpful., sessionlink: waving-across-the-web, sessionhashtag: #wave2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.9C
+, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BuH6OdLZkCY3.7, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BuH6OdLZkCY3.7, waveid: w+uH6OdLZkCY3.7http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/c0p7u2010-05-10T18:39:11.857ZRow: 81sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 8, product: Google Wave, track: Wave, sessiontype: 101, sessiontitle: Open source Google Wave: Building your own wave provider, tags: Wave, Open Source, sessionspeakers: Dan Peterson, Jochen Bekmann, J.D. Zamfirescu Pereira, David LaPalomento (Novell), speakers: dpeterson, jochen, zamfi, sessionabstract: Learn how to build your own wave service. Google is open sourcing the lion's share of the code that went into creating Google Wave to help bootstrap a network of federated providers. This talk will discuss the state of the reference implementation: the software architecture, how you can plug it into your own use cases -- and how you can contribute to the code and definition of the underlying specification., sessionrequirements: XMPP knowledge is useful. Some Java proficiency is helpful, sessionlink: open-source-wave-provider, sessionhashtag: #wave3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.63, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BTeHZ97ZkBEG, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BTeHZ97ZkBEG, waveid: w+TeHZ97ZkBEGhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bl8zb2010-05-10T18:39:11.857ZRow: 82sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: 8, product: Google Wave, track: Wave, sessiontype: 201, sessiontitle: Making smart & scalable Wave robots, tags: Wave, App Engine, sessionspeakers: David Byttow, Marcel Prasetya, speakers: davidbyttow, mprasetya, sessionabstract: A smart robot must be able to store persistent data. Wave robots can store data in wave structures, like wavelets, datadocs, and annotations, instead of traditional datastores. A scalable robot must perform operations with minimal bandwidth. Wave robots can optimize by selecting the appropriate amount of context, the optimal events, and narrow filters for events. In this talk, we'll share best practices on data storage and scaling. , sessionrequirements: Understanding of Google Wave robots, and Google App Engine, the ideal attendee has already built robots in the past, sessionlink: smart-scalable-wave-robots, sessionhashtag: #wave4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.8B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BSVy-sLZkBYM, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BSVy-sLZkBYM, waveid: w+SVy-sLZkBYMhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/c3ico2010-05-10T18:39:11.857ZRow: 83sessiondate: Thursday May 20, sessiontime: 3:30pm-4:30pm, room: 8, product: Google Wave, track: Wave, Enterprise, sessiontype: 101, sessiontitle: Google Wave and the enterprise environment, tags: Wave, Enterprise, sessionspeakers: Greg D'alesandre, Alan Green, Kris Muller (Salesforce.com), speakers: gregd, alangreen, sessionabstract: Google Wave provides enterprises with an opportunity to streamline a variety of communications. One of the main ways this occurs is through building generalized and custom extensions. This session will cover some of the existing enterprise extensions that have been built as well as give a sneak peak as to what's in progress. If you're wondering how Wave can be used to help build your business, you'll want to attend this session. , sessionrequirements: Basic understanding of Google Wave is helpful, sessionlink: google-wave-enterprise, sessionhashtag: #wave5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.8C, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bo01RhLZkBH7, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bo01RhLZkBH7, waveid: w+o01RhLZkBH7
\ No newline at end of file
diff --git a/assets/.svn/text-base/cache-speakers.xml.svn-base b/assets/.svn/text-base/cache-speakers.xml.svn-base
new file mode 100644
index 0000000..70dbf42
--- /dev/null
+++ b/assets/.svn/text-base/cache-speakers.xml.svn-base
@@ -0,0 +1,5 @@
+http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic2010-05-10T18:39:11.857Zspeakerschristine.leechristine.lee@gmail.com1971http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cpzh42010-05-10T18:39:11.857ZRow: 2speakertitle: , speakerabstract: , speakerldap: http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cre1l2010-05-10T18:39:11.857ZRow: 3speakertitle: Aaron Koblin, speakercompany: Google, speakerabstract: Aaron takes social and infrastructural data and uses it to depict cultural trends and emergent patterns. He received the National Science foundation’s first place award for scientific visualization and is part of the permanent collection of the Museum of Modern Art. Currently, Aaron is part of Google’s Creative Lab in San Francisco., speakerldap: AaronKoblinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/chk2m2010-05-10T18:39:11.857ZRow: 4speakertitle: Adam Graff, speakercompany: Genentech, speakerabstract: Adam has worked in various roles at Genentech in software development, support and management while contributing to saving the lives of patients with unmet medical needs. Adam leads a team that is implementing Google Apps for 18,000 employees. They are also delivering innovative integrations between the Google Apps platform and other critical business systems that delight users, foster collaboration and increase productivity., speakerldap: AdamGraffhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ciyn32010-05-10T18:39:11.857ZRow: 5speakertitle: Adam Nash, speakercompany: LinkedIn, speakerabstract: Adam joined LinkedIn in 2007 and has helped build a number of product & technology teams. After taking over LinkedIn platform efforts in 2009, he led the effort to launch their first open platform, <a href="http://developer.linkedin.com">developer.linkedin.com</a>, and their full support for OAuth. He currently spends his time focused on how the next generation of business applications will leverage social platforms., speakerldap: AdamNashhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ckd7g2010-05-10T18:39:11.857ZRow: 6speakertitle: Adam Powell, speakercompany: Google, speakerabstract: Adam is a software engineer at Google with a background in small-scale games and a fascination with UI. As a member of the Android framework team, Adam is working to turn mobile devices into more approachable windows to the world's information., speakerldap: AdamPowellhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/clrrx2010-05-10T18:39:11.857ZRow: 7speakertitle: Adam Schuck, speakercompany: Google, speakerabstract: Adam is the technical lead for the Google Wave client. Since commencing work at Google Sydney in February 2006, Adam worked on Google Maps, having co-created Mapplets in 2007. Having achieved a black-belt as a JavaScript ninja, Adam dusted off his Java textbook, threw away his ninja uniform, and embraced the new art of GWT. He received the University Medal for his B.S. with Honours in Computer Science from the University of New South Wales., speakerldap: AdamSchuckhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cyevm2010-05-10T18:39:11.857ZRow: 8speakertitle: Alan Green, speakercompany: Google, speakerabstract: Alan is a software engineer building enterprise and privacy features for Google Wave. Prior to joining Google, Alan consulted in a range of industries, including government, banking, insurance, airlines and telecommunications. His code still dispatches fire engines in at least one major city., speakerldap: AlanGreenhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cztg32010-05-10T18:39:11.857ZRow: 9speakertitle: Albert Cheng, speakercompany: Google, speakerabstract: Albert is a software engineer on the iGoogle team. He currently leads the Gadget Development Tools projects. Interests include mountain climbing, enjoying free food, and fixing bugs., speakerldap: AlbertChenghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/d2mkx2010-05-10T18:39:11.857ZRow: 10speakertitle: Albert Wenger, speakercompany: Union Square Ventures, speakerabstract: Albert is a managing partner at Union Square Ventures. He currently serves on the boards of Twilio, Foursquare, 10gen, AMEE, Covestor, and Clickable. Albert has a Bachelors degree from Harvard and a PhD from MIT. He is married with three kids and lives outside New York City. Albert blogs at <a href="http://continuations.com">http://continuations.com</a>, speakerldap: AlbertWengerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cssly2010-05-10T18:39:11.857ZRow: 11speakertitle: Alex Russell, speakercompany: Google, speakerabstract: Alex is a member of the Google Chrome Frame team and a believer in the web as a democratizing platform. Prior to his work on Google Chrome Frame, Alex was Project Lead for the Dojo Toolkit JavaScript framework., speakerldap: AlexRussellhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cu76f2010-05-10T18:39:11.857ZRow: 12speakertitle: Alfred Fuller, speakercompany: Google, speakerabstract: Alfred recently joined Google after getting his Ph.D. in Computer Science from the University of California, Davis. Since joining Google's App Engine team, he has worked primarily on improving the Datastore., speakerldap: AlfredFullerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cx0b92010-05-10T18:39:11.857ZRow: 13speakertitle: Amit Agarwal, speakercompany: Google, speakerabstract: Amit is a product manager at Google. He has spent the last 10+ years designing, developing and deploying infrastructure., speakerldap: AmitAgarwalhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/d9ney2010-05-10T18:39:11.857ZRow: 14speakertitle: Amit Kulkarni, speakercompany: Manymoon, speakerabstract: Amit is the co-founder and CEO of Manymoon, a social productivity tool that helps people get work done by leveraging their connections and existing cloud platforms. He has a passion for building applications that make people more productive. Amit has a BS in Electrical Engineering and minor in Computer Science from the University of Illinois, Urbana-Champaign., speakerldap: AmitKulkarnihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/db1zf2010-05-10T18:39:11.857ZRow: 15speakertitle: Amit Manjhi, speakercompany: Google, speakerabstract: Amit is a software engineer on the Google Web Toolkit team, where he works towards GWT delivering "productivity for developers, performance for users." In particular, he has contributed to improving GWT's development mode, testability of GWT applications, and resource selection in GWT. He holds a Ph.D. degree in Computer Science from Carnegie Mellon University, has founded a VC-backed Web startup, and enjoys mathematical puzzles., speakerldap: AmitManjhihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dcgjs2010-05-10T18:39:11.857ZRow: 16speakertitle: Amit Weinstein, speakercompany: Google, speakerabstract: Amit is a developer on the Google Chart Tools team based in Israel. The team is now working on unifying the available chart tools in Google and enhancing them. When not at work, Amit is also working on his PhD in Computer Science at Tel Aviv University., speakerldap: AmitWeinsteinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ddv492010-05-10T18:39:11.857ZRow: 17speakertitle: Anders Sandholm, speakercompany: Google, speakerabstract: Anders is a Product Manager at Google. He launched the V8 JavaScript engine and Developer Tools in Google Chrome. Before coming to Google, Anders was a management consultant with McKinsey & Company. Anders holds a PhD in computer science from the University of Aarhus and an MBA from INSEAD. , speakerldap: AndersSandholmhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/iow8x2010-05-10T18:39:11.857ZRow: 18speakertitle: Angus Logan, speakercompany: Microsoft, speakerabstract: Angus helps websites connect with the 500 million people who use Windows Live including products such as Messenger., speakerldap: AngusLoganhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/d415a2010-05-10T18:39:11.857ZRow: 19speakertitle: Anne Veling, speakercompany: BeyondTrees, speakerabstract: After a M.Sc. in Computer Science/Artificial Intelligence, Anne worked for several years in the search engine industry, designing highly scalable knowledge extraction, clustering and visualization modules for search applications. Currently self-employed and helping out global companies deal with large amounts of loosely structured information as an internet application architect. Married, father of 3, theatresports addict, Lucene trainer., speakerldap: AnneVelinghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/d5fpr2010-05-10T18:39:11.857ZRow: 20speakertitle: Arne Roomann-Kurrik, speakercompany: Google, speakerabstract: Arne is a Developer Programs Engineer at Google, working with the Chrome team. Just like the rest of you, he puts his pants on one leg at a time; except that once his pants are on, he makes excellent samples, developer tools, and documentation., speakerldap: ArneRoomann-Kurrikhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/d6ua42010-05-10T18:39:11.857ZRow: 21speakertitle: Bart Locanthi, speakercompany: Google, speakerabstract: Bart joined Google in 2005 and most recently has worked on MyMaps and the Maps Data API. Prior to Google, he did freelance work on embedded languages and XML app servers. In ancient times, he was at AT&T Bell Labs, where he worked on the Blit and Gnot (Plan9) terminals, CAD tools, and CS in general., speakerldap: BartLocanthihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/d88ul2010-05-10T18:39:11.857ZRow: 22speakertitle: Ben Appleton, speakercompany: Google, speakerabstract: Ben is the tech lead manager for Google's Maps APIs. He is interested in randomized algorithms and computational geometry. Ben has a PhD in image analysis and degrees in electrical engineering and mathematics from the University of Queensland., speakerldap: BenAppletonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dkvya2010-05-10T18:39:11.857ZRow: 23speakertitle: Ben Cheng, speakercompany: Google, speakerabstract: Ben is a software engineer in the Android team working on Dalvik. His primary project is to develop a JIT compiler that improves the efficiency of the VM. He also spends time developing tools for performance tuning and code verification. Before Google, Ben worked at various companies on virtual machines, including Transmeta, Azul, and PeakStream. Ben got a PhD degree in Computer Science from University of Illinois at Urbana-Champaign., speakerldap: BenChenghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dmair2010-05-10T18:39:11.857ZRow: 24speakertitle: Ben Collins-Sussman, speakercompany: Google, speakerabstract: Ben is a tech lead and manager for Google Code's project hosting service. He was one of the founding developers of Subversion, and later helped port it to Google's Bigtable infrastructure. He also has a degree in mathematics, plays banjo, writes musicals, and takes lots of photos., speakerldap: BenCollins-Sussmanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dnp342010-05-10T18:39:11.857ZRow: 25speakertitle: Ben Fried, speakercompany: Google, speakerabstract: Ben is Chief Information Officer at Google and oversees Google's global technology systems. His extensive hands-on experience in technology includes stints as a dBASE II programmer, front-line support manager, Macintosh developer, Windows 1.0 programmer, and Unix systems programmer. Prior to joining Google, he spent more than 13 years in Morgan Stanley's technology department., speakerldap: BenFriedhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/if29y2010-05-10T18:39:11.857ZRow: 26speakertitle: Ben Huh, speakercompany: Cheezburger Network, speakerabstract: Ben is a former journalist turned dot com entrepreneur who has a knack for nailing the zeitgeist. He has been credited with bringing Internet memes to the mainstream and popularizing Internet culture. The success of his business is attributed to his knowledge of memes, viral content, and crowd sourcing. Ben graduated with a BSJ from Northwestern University’s Medill School of Journalism., speakerldap: BenHuhhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dp3nl2010-05-10T18:39:11.857ZRow: 27speakertitle: Bill Buzbee, speakercompany: Google, speakerabstract: Bill is a software engineer on Google's Android team, where he works on the Dalvik JIT Compiler. Prior to Google, Bill worked on dynamic compilation at Hewlett-Packard and Transmeta. He lives in Half Moon Bay with his wife, three kids and a dog., speakerldap: BillBuzbeehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/df9om2010-05-10T18:39:11.857ZRow: 28speakertitle: Bob Aman, speakercompany: Google, speakerabstract: Bob works on the Developer Relations team at Google in Mountain View. He loves contributing to open source, open web standards, and generally making the web a friendlier, more useful place for everybody. Bob was once saved from a volcanic mishap by a bad case of malaria. True story., speakerldap: BobAmanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dgo932010-05-10T18:39:11.857ZRow: 29speakertitle: Brad Feld, speakercompany: Foundry Group, speakerabstract: Brad is a managing director at the Foundry Group located in Boulder, Colorado. He has been an early stage investor and entrepreneur for over 20 years. Brad is also an avid art collector and long-distance runner. He has completed fifteen marathons as part of his mission to run a marathon in each of the 50 states., speakerldap: BradFeldhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dw4je2010-05-10T18:39:11.857ZRow: 30speakertitle: Brad Fitzpatrick, speakercompany: Google, speakerabstract: Brad is a software engineer on Google's Android team, where he works on performance. Brad's also worked Google's social efforts and, prior to Google, founded Danga.com where he developed OpenID, LiveJournal and created open source server software such as memcached, Gearman, MogileFS, djabberd, etc. Originally from Portland and Seattle, Brad now lives in San Francisco., speakerldap: BradFitzpatrickhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dxj3v2010-05-10T18:39:11.857ZRow: 31speakertitle: Bradley Vickers, speakercompany: OAR Northwest, speakerabstract: Bradley obtained a Guinness World Record over the course of 71 days as part of the first crew to Row across the North Atlantic from New York, USA to Falmouth, England. He co-founded OAR Northwest and is on the board of Around-n-Over. Bradley’s shares his adventures in presentations that focus on achieving goals and developing leadership skills., speakerldap: BradleyVickershttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dyxo82010-05-10T18:39:11.857ZRow: 32speakertitle: Brady Forrest, speakercompany: O'Reilly Media/Ignite, speakerabstract: Brady co-created <a href="http://igniteshow.com">Ignite</a> and is the Chair for O'Reilly's Where 2.0. Additionally, he co-Chairs the <a href="http://web2expo.com">Web 2.0 Expo</a> in San Francisco and NYC. Brady writes for <a href="http://radar.oreilly.com/">O'Reilly Radar</a> tracking changes in technology. Brady lives in Seattle, where he builds <a href="http://heaid.com">music-making robots</a> for Burning Man and runs <a href="http://igniteseattle.com">Ignite Seattle</a>. You can track his web travels at <a href="http://trufflehoney.com/">TruffleHoney</a>., speakerldap: BradyForresthttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/e0c8p2010-05-10T18:39:11.857ZRow: 33speakertitle: Brett Bavar, speakercompany: Google, speakerabstract: Brett is a software engineer working on Google's AJAX APIs. Brett has worked on a few other projects during his time at Google, including the integration of Google Reader with Google Translate, which was his first 20% project., speakerldap: BrettBavarhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dqi9q2010-05-10T18:39:11.857ZRow: 34speakertitle: Brett Slatkin, speakercompany: Google, speakerabstract: Brett is the co-creator of the PubSubHubbub protocol and a Software Engineer on the Google App Engine team. He joined Google in 2005. He earned his B.S. in Computer Engineering from Columbia University in the City of New York. He lives in San Francisco., speakerldap: BrettSlatkinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/drwu72010-05-10T18:39:11.857ZRow: 35speakertitle: Brian Kennish, speakercompany: Google, speakerabstract: Brian was the first support engineer at Google back when the AdWords API was launched. Since then, he’s helped launch four more Google developer products - Gadget Ads, the Analytics API, Wave, and Chrome Extensions. Brian is currently a developer advocate for Google Chrome., speakerldap: BrianKennishhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dtbek2010-05-10T18:39:11.857ZRow: 36speakertitle: Brian Rakowski, speakercompany: Google, speakerabstract: Brian joined Google as the company's first Associate Product Manager in 2002. He worked on Gmail through launch until 2005 when he moved to Google's Zurich office. Brian has since returned to Google headquarters and has been working on Google Chrome since the project began. , speakerldap: BrianRakowskihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dupz12010-05-10T18:39:11.857ZRow: 37speakertitle: Brian Swetland, speakercompany: Google, speakerabstract: Brian has been the Android Systems/Kernel technical lead since Android's startup days in 2005. Prior to Android, Brian was responsible for the Hiptop OS and the 1.0 virtual machine at Danger, Inc. Over ten years of mobile/embedded OS development has yet to completely destroy his sanity., speakerldap: BrianSwetlandhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/e7d2q2010-05-10T18:39:11.857ZRow: 38speakertitle: Brian W. Fitzpatrick, speakercompany: Google, speakerabstract: Brian started Google's Chicago engineering office in 2005. An open source contributor for over 10 years, Brian is the engineering manager for Google's Data Liberation Front, a member of the Apache Software Foundation, a former engineer at Apple and CollabNet, a Subversion developer, and a co-author of "Version Control with Subversion"., speakerldap: BrianWFitzpatrickhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/e8rn72010-05-10T18:39:11.857ZRow: 39speakertitle: Bruce Johnson, speakercompany: Google, speakerabstract: Bruce founded the Google Web Toolkit project and Google's engineering office in Atlanta, Georgia., speakerldap: BruceJohnsonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ea67k2010-05-10T18:39:11.857ZRow: 40speakertitle: Bryan McQuade, speakercompany: Google, speakerabstract: During his time at Google, Bryan has contributed to projects that make the web faster, including Shared Dictionary Compression over HTTP, optimizing web servers to better utilize HTTP, and most recently, the Page Speed web performance tool. Prior to working on web performance, Bryan was the first full time engineer on the Google TV Ads team, where he helped to build some of Google’s TV ad auction and video management systems., speakerldap: BryanMcQuadehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ebks12010-05-10T18:39:11.857ZRow: 41speakertitle: Chris Chabot, speakercompany: Google, speakerabstract: Chris is a Developer Advocate at Google, who's interested in Open Source, OpenSocial, and trying to do the impossible. Most recently he's been the driving force behind PHP Shindig, the reference OpenSocial server implementation, Partuza a popular open-source example social network site that shows how to use OpenID, OAuth and OpenSocial, and the OpenSocial PHP client libraries., speakerldap: ChrisChabothttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ihves2010-05-10T18:39:11.857ZRow: 42speakertitle: Chris Cole, speakercompany: MySpace, speakerabstract: Chris focuses on guiding and implementing open solutions for MySpace as a software architect and a core contributor and committer to the OpenSocial specification. Recently he co-authored the book “Building OpenSocial Apps.” He’s been an Internet developer from the gopher and Mosaic days, through the dot-com era, and into today’s social web and beyond., speakerldap: ChrisColehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/e1qt22010-05-10T18:39:11.857ZRow: 43speakertitle: Chris DiBona, speakercompany: Google, speakerabstract: Chris is the open source and public sector programs manager at Google. His team oversees license compliance and supports the open source developer community through programs such as the Google Summer of Code and through the release of open source software projects and patches. In the public sector space, he looks after Google Moderator and the polling locations API., speakerldap: ChrisDiBonahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/e35dj2010-05-10T18:39:11.857ZRow: 44speakertitle: Chris Dixon, speakercompany: Hunch, speakerabstract: Chris is co-founder/CEO of Hunch and co-founder of Founder Collective, a $40M early-stage VC fund. Previously he was co-founder/CEO of SiteAdvisor which was acquired by McAfee. As a software developer, Chris worked on high speed trading algorithms and later on multimedia software. He is a personal investor in startups including Skype, Gerson Lerhman Group, TrialPay, Invite Media, OMGPOP, DocVerse and Knewton. He has a BA & MA from Columbia and Harvard., speakerldap: ChrisDixonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/e4jxw2010-05-10T18:39:11.857ZRow: 45speakertitle: Chris Lambert, speakercompany: Google, speakerabstract: Chris is a software engineer working on location based services, including Google Latitude and the HTML5 network location provider in Chrome and Firefox. Previously, Chris spent some time in Google Research working on data mining for natural language translation. Chris graduated from Northeastern University in Boston with his BS in Computer Science., speakerldap: ChrisLamberthttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/e5yid2010-05-10T18:39:11.857ZRow: 46speakertitle: Chris Messina, speakercompany: Google, speakerabstract: Chris got his start in 2004 by leading community marketing through the launch of Firefox. He is a board member of the OpenID and Open Web Foundations and plays an instrumental role in advancing OAuth and safer online computing. In 2008, Chris received the Google Open Source Award recognizing his community work on initiatives like microformats. He also co-founded the coworking and BarCamp communities and introduced hashtags on Twitter., speakerldap: ChrisMessinahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/inhog2010-05-10T18:39:11.857ZRow: 47speakertitle: Chris Nesladek, speakercompany: Google, speakerabstract: Chris is a lead interaction designer on the Android project, with a focus on social. His work can be seen across the platform with the Quick Contact widget as well as the Contacts and Facebook applications. Prior to Android, Chris worked on projects with Danger, Intuit, and Sony Design Center. In addition to staying in shape through tennis and triathlon-related sports, Chris is an avid pizza aficionado., speakerldap: ChrisNesladekhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/eilm22010-05-10T18:39:11.857ZRow: 48speakertitle: Chris Pruett, speakercompany: Google, speakerabstract: Chris works from Google's Japan office to support local Android application development. Prior to joining the Android team he worked as an engineer on Lively, Google's 3D online world. Chris worked for several years at a subsidiary of Activision, Inc developing video games. In his free time Chris enjoys dissecting horror games. Read about his research at <a href="http://www.dreamdawn.com/sh">http://www.dreamdawn.com/sh</a>., speakerldap: ChrisPruetthttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ek06j2010-05-10T18:39:11.857ZRow: 49speakertitle: Chris Schalk, speakercompany: Google, speakerabstract: Chris is a Developer Advocate at Google who engages both the App Engine and Enterprise OpenSocial communities. He has also worked on Google Friend Connect, Google AJAX APIs, Maps, Gears and Google Web Toolkit. Prior to Google, Chris was a Principal Product Manager at Oracle in the development tools group as well as co-author of "JavaServer Faces: The Complete Reference". In his spare time, he plays trumpet in local Bay Area symphonies., speakerldap: ChrisSchalkhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/eleqw2010-05-10T18:39:11.857ZRow: 50speakertitle: Chris Vander Mey, speakercompany: Google, speakerabstract: Chris is a Sr. Product Manager at Google where he works on Google Apps. He's shipped the Google Apps Marketplace, Google Apps Sync for Microsoft Outlook, elements of Chrome, and Google Pack. Before Google, Chris was an engineering manager at Amazon and an early member of multiple startups. He earned a Masters of Engineering Management from Dartmouth and BS in Aerospace Engineering from the University of Virgina., speakerldap: ChrisVanderMeyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/emtbd2010-05-10T18:39:11.857ZRow: 51speakertitle: Chrix Finne, speakercompany: Google, speakerabstract: Chrix (with an 'x'!) is a Product Manager at Google focused on Mobile Ads, where he is responsible for search ads and ads quality as well as mobile features in Google Analytics. Previously, he was Product Manager for Google Reader, where he helped develop some of Google's first social sharing features. Chrix is a graduate of Harvard University, and has also worked at Microsoft and Intellectual Ventures., speakerldap: ChrixFinnehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/eczce2010-05-10T18:39:11.857ZRow: 52speakertitle: Chuck Dietrich, speakercompany: Sliderocket, speakerabstract: Chuck is the CEO of SlideRocket. Thousands of companies use SlideRocket’s web based presentation application to create stunning and interactive presentations that can be centrally managed and measured. Previously, Chuck spent 9 years at Salesforce.com and was the General Manager & Vice President for Salesforce.com Mobile. Chuck holds a BA from University of Colorado in Economics and an MBA from University of Utah. , speakerldap: ChuckDietrichhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/eedwv2010-05-10T18:39:11.857ZRow: 53speakertitle: Ciaran Rochford, speakercompany: Samsung, speakerabstract: Ciaran is Director of Software Engineering at Samsung Mobile R&D lab in San Jose, California, where he leads the Android R&D effort. Since joining Samsung he has helped form a team experts focusing on JME and Android. Prior to joining Samsung, Ciaran worked at Sun Microsystems on the JME stack from its inception and related mobile platform technologies., speakerldap: CiaranRochfordhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ij9z92010-05-10T18:39:11.857ZRow: 54speakertitle: Dan Bornstein, speakercompany: Google, speakerabstract: Dan joined Google in 2005 to join the Android project, continuing his work on virtual machines and related technology. He designed and co-developed the Dalvik virtual machine and core libraries, and he continues to lead the Dalvik team today. Dan earned an ScB in Cognitive Science from Brown University, and just for the record, he does not believe that he has any Icelandic ancestors., speakerldap: DanBornsteinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/efsh82010-05-10T18:39:11.857ZRow: 55speakertitle: Dan Holevoet, speakercompany: Google, speakerabstract: Dan Holevoet is a Developer Programs Engineer at Google, focused on Gadgets and Apps. In his free time he likes gardening, playing video games, and recycling speaker bios., speakerldap: DanHolevoet
+
+http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/eh71p2010-05-10T18:39:11.857ZRow: 56speakertitle: Dan Peterson, speakercompany: Google, speakerabstract: Dan is a Product Manager on Google Wave and President of the OpenSocial Foundation. Previously, Dan led the Google Web Toolkit (GWT) team as it became an open source project and worked on Google's infrastructure team on web search and data center management. Dan earned a B.S. in Computer Science from the University of Illinois at Urbana-Champaign, as well as minors in Technology & Management and philosophy. , speakerldap: DanPetersonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/etu5e2010-05-10T18:39:11.857ZRow: 57speakertitle: Daniel Danilatos, speakercompany: Google, speakerabstract: Dan is the tech lead for the Google Wave editor and has been using GWT for two years. Dan lives in Sydney, Australia. He earned a B.S. in Computer Science from the University of New South Wales. , speakerldap: DanielDanilatoshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ewna82010-05-10T18:39:11.857ZRow: 58speakertitle: Daniel Lee, speakercompany: Google, speakerabstract: Daniel joined the Developer Relations team at Google over 3 years ago. He dedicated most of his time supporting and building up the community around the Google Gadgets API for iGoogle. After spending over two years on the product, he shifted focus onto supporting the Google Maps APIs., speakerldap: DanielLeehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ey1up2010-05-10T18:39:11.857ZRow: 59speakertitle: Daniel Raffel, speakercompany: Yahoo!, speakerabstract: Daniel is a Product Manager at Yahoo! on the Open Strategy team. He launched Yahoo! Pipes and Yahoo! Messenger with Voice. Previously, he worked at Songbird, ABC News Online, and CondeNet. Earlier in his career his passion for food led him to pursue a culinary degree which culminated in a stint as a chef in the kitchen of Thomas Keller’s NYC restaurant per se. Daniel received a Masters from the Interactive Telecommunications Program at NYU. , speakerldap: DanielRaffelhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/eo7vq2010-05-10T18:39:11.857ZRow: 60speakertitle: Dave Day, speakercompany: Google, speakerabstract: Dave works in the Sydney office as the Tech Lead of the Maps Javascript API v2, as well as Maps Labs. Before joining Google, Dave studied electrical engineering and computer science at Sydney University and worked in the power industry on simulation and database software., speakerldap: DaveDayhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/epmg72010-05-10T18:39:11.857ZRow: 61speakertitle: Dave McClure, speakercompany: 500 Hats, speakerabstract: Dave likes to hang out with entrepreneurs, and occasionally help or invest in their startups if they let him. Dave has been geeking out in Silicon Valley for over twenty years, and has worked with companies such as PayPal, Mint, Founders Fund, Facebook, LinkedIn, SlideShare, Twilio, Simply Hired, O'Reilly Media, Intel, & Microsoft. Dave also likes to play ultimate frisbee when his knees don't hurt., speakerldap: DaveSparkshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/er10k2010-05-10T18:39:11.857ZRow: 62speakertitle: Dave Sparks, speakercompany: Google, speakerabstract: Dave is the technical lead for the Android media framework. He was the principal author of the MMA's Downloadable Sounds specification (DLS) now part of the MPEG-4 and 3GPP standards., speakerldap: DaveMcClurehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/esfl12010-05-10T18:39:11.857ZRow: 63speakertitle: Dave Springer, speakercompany: Google, speakerabstract: David joined Google in 2008 and currently works on developer-facing tools for the Google Chrome and Native Client application development environment. Prior to Google, David was at Apple where he developed debugging and performance measuring tools for OpenGL. He also worked at NeXT Computer where he developed Interactive RenderMan and the 3D aspects of NeXTSTEP. David was a founder of Alias Research, Inc. and received an Academy Award for his work on Alias Power Animator., speakerldap: DaveSpringerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/a59y22010-05-10T18:39:11.857ZRow: 64speakertitle: Dave Wang, speakercompany: Booyah, speakerabstract: Dave is a veteran of the exploding geo-location space with a proven track record in both the application and technology-enabling layers. Dave has been integral to driving the product roadmap, design, and rapid user growth of the MyTown franchise. Prior to joining Booyah, Dave was the Sr. Director of Marketing at SiRF/Centrality, focusing on location platforms and services. Dave graduated from Stanford University with a dual Masters in EE/MS&E Dave and received a BS in EE/CS from UC Berkeley., speakerldap: DaveWanghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/a6oij2010-05-10T18:39:11.857ZRow: 65speakertitle: David Byttow, speakercompany: Google, speakerabstract: David has been a software engineer for 8 years and is currently working on Google Wave out of Mountain View, CA where he has focused his efforts primarily on Google Wave APIs. Prior to joining Google in 2008, David developed video games for popular gaming consoles and has since hung up his C++ hat to embrace all sorts of newfangled languages and technologies., speakerldap: DavidByttowhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/a832w2010-05-10T18:39:11.857ZRow: 66speakertitle: David Erb, speakercompany: Google, speakerabstract: David is a technical lead at Google in Seattle. Since joining Google in 2005, David has also worked on projects related to mobile phones and telephone systems. Prior to Google, David worked at Microsoft, Borland, SRI International, and numerous start-ups., speakerldap: DavidErbhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/a9hnd2010-05-10T18:39:11.857ZRow: 67speakertitle: David Glazer, speakercompany: Google, speakerabstract: David is an Engineering Director at Google where he leads the Developer Platforms team and is responsible for Google's developer-facing APIs, tools, and hosting offerings. He is active in Google's ongoing contributions to the social web, including being on the board of the OpenSocial Foundation. Prior to Google, David was a founder of Eloquent (later acquired by Open Text) and Verity., speakerldap: DavidGlazerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/9znoe2010-05-10T18:39:11.857ZRow: 68speakertitle: David LaPalomento, speakercompany: Novell, Inc., speakerabstract: David is the lead frontend developer for the Novell Pulse project. He is interested in the challenges of building large-scale and concurrent systems, test-driven development and delivering innovative user experiences through emerging web standards. He has worked on Pulse since its inception. , speakerldap: DavidLaPalomentohttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/a128v2010-05-10T18:39:11.857ZRow: 69speakertitle: David Primmer, speakercompany: Google, speakerabstract: David is a software engineer working on the security technologies used in Google Developer Platforms, including OAuth, OpenID and Secure Data Connector. David is a former systems and database administrator with a wide background managing the business computing environment., speakerldap: DavidPrimmerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/a2gt82010-05-10T18:39:11.857ZRow: 70speakertitle: Debajit Ghosh, speakercompany: Google, speakerabstract: Debajit is the tech lead manager for the Android Sync Team and has worked on Android through all of its major releases. He previously worked on Google’s mobile efforts, focusing on server-side infrastructure for browser-based properties. Prior to Google, Debajit worked on speech recognition technology at Nuance Communications and has Bachelor’s and Master’s degrees from MIT., speakerldap: DebajitGhoshhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/a3vdp2010-05-10T18:39:11.857ZRow: 71speakertitle: DeWitt Clinton, speakercompany: Google, speakerabstract: DeWitt is a technical lead on Google's developer team, focused on building product APIs, growing the developer ecosystem, and defending the open web. Previously, DeWitt held engineering and product management roles at Amazon, Travelocity, Microsoft, several successful early Internet ventures, and several not-quite-as-successful Internet adventures. DeWitt holds a BA in Computer Science and Political Science from Williams College. He enjoys each and every day., speakerldap: DeWittClintonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ivx4q2010-05-10T18:39:11.857ZRow: 72speakertitle: Dhanji Prasanna, speakercompany: Google, speakerabstract: Dhanji contributes to open source projects in his free time such as Google Guice, Google Wave, Sitebricks and the MVEL language. Dhanji is the author of "Dependency Injection: Design Patterns", a book published by Manning in 2009. He represents Google on several Java expert groups including Servlet and JAX-RS. Say hi to him at: <a href="http://twitter.com/dhanji">http://twitter.com/dhanji</a>., speakerldap: DhanjiPrasannahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/agihe2010-05-10T18:39:11.857ZRow: 73speakertitle: Dick Costolo, speakercompany: Twitter, speakerabstract: Dick is the COO of Twitter, Inc. Prior to Twitter, he worked in the Ads product group at Google. Dick was co-founder and CEO of FeedBurner, which was acquired by Google in June 2007. , speakerldap: DickCostolohttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ahx1v2010-05-10T18:39:11.857ZRow: 74speakertitle: Dino Brusco, speakercompany: Motorola, speakerabstract: Dino is the Senior Director of Android Product Management at Motorola. In the past, he has held key management positions with Palmsource, Turbolinux, Hewlett-Packard and Concurrent Computer Corp. Dino has led product development, software developer communities, marketing, software support and services organizations., speakerldap: DinoBruscohttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ajbm82010-05-10T18:39:11.857ZRow: 75speakertitle: Don Dodge, speakercompany: Google, speakerabstract: Don is a Developer Advocate focusing on helping developers work with Google Apps. Don recently joined Google after 5 years as a developer evangelist at Microsoft. Don is a veteran of five start-ups including Forte Software, AltaVista, Napster, Bowstreet, and Groove Networks and has been in the software business for more than 25 years. Don spent 5 years at Microsoft working with VCs and start-ups in the greater Boston area., speakerldap: DonDodgehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/akq6p2010-05-10T18:39:11.857ZRow: 76speakertitle: Don Schwarz, speakercompany: Google, speakerabstract: Don is a software engineer in Google's Chicago engineering office. He has worked on a number of projects including Google Mashup Editor and most recently as one of the co-creators of Google App Engine for Java. Prior to joining Google, Don wrote distributed systems for a large financial institution., speakerldap: DonSchwarzhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/aaw7q2010-05-10T18:39:11.857ZRow: 77speakertitle: Douwe Osinga, speakercompany: Google, speakerabstract: Douwe is the technical lead for the Google Wave APIs. After studying Computer Science and Philosophy at the Free University in Amsterdam, Douwe started various companies with widely differing amounts of success. After becoming bored with talking to customers, he joined Google in 2004, working on Search Quality, Google Trends, and on SMS Channels in India. , speakerldap: DouweOsingahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/acas72010-05-10T18:39:11.857ZRow: 78speakertitle: Eric Ayers, speakercompany: Google, speakerabstract: Eric is a Software Engineer on the Speed Tracer team and is also the maintainer of the Google API Library for GWT. He holds a Master's Degree in Computer Science from the Georgia Institute of Technology. Eric enjoys hobby electronics and is a 5 year veteran mentor of the FIRST program, inspiring elementary through high school students to develop interest in pursuing careers in technology through building robots. , speakerldap: EricAyershttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/adpck2010-05-10T18:39:11.857ZRow: 79speakertitle: Eric Bidelman, speakercompany: Google, speakerabstract: Eric is an engineer and developer advocate at Google. He works on the Google Data APIs, primarily on the Google Documents List, Google Health, and the Authentication APIs. Prior to Google, Eric worked as a software engineer at the University of Michigan where he designed rich web applications and APIs for the university's 19 libraries. Eric holds a B.S.E in Computer Engineering and a B.S.E in Electrical Engineering from the University of Michigan, Ann Arbor., speakerldap: EricBidelmanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/af3x12010-05-10T18:39:11.857ZRow: 80speakertitle: Erik Hellman, speakercompany: Sony Ericsson, speakerabstract: Erik has worked on projects ranging from large-scale telecom systems to mobile application development; now he is one of the principal Software Architects for Android at Sony Ericsson. Despite being severely colorblind, he manages to oversee most of the architectural aspects of Android application development within Sony Ericsson while still having time to write code himself., speakerldap: ErikHellmanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/arr0q2010-05-10T18:39:11.857ZRow: 81speakertitle: Erik Kay, speakercompany: Google, speakerabstract: Erik is an engineer on the Google Chrome team. He's currently a lead on the extensions project and also designed Chrome's autoupdate system., speakerldap: ErikKayhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/at5l72010-05-10T18:39:11.857ZRow: 82speakertitle: Evan Roseman, speakercompany: Google, speakerabstract: Evan joined Google in 2005 and is a software engineer in Google's Search Quality Team. His primary focus is on Webspam in Google's search index. Prior to joining Google, Evan studied Computer Science at Rice University., speakerldap: EvanRosemanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/auk5k2010-05-10T18:39:11.857ZRow: 83speakertitle: Evin Levey, speakercompany: Google, speakerabstract: Evin is a Product Manager on the Google Apps team, managing the Docs Platform and Google Apps Script. Before joining Google three years ago, he spent more than eight years at a video game middleware startup, working variously as coder, manager and salesman, focused exclusively on the developer community. Evin has a Masters in Computer Science from Trinity College, Dublin., speakerldap: EvinLeveyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/avyq12010-05-10T18:39:11.857ZRow: 84speakertitle: Gareth McSorley, speakercompany: Google, speakerabstract: Gareth is a Software Engineer with Google Switzerland who spends most of his time tinkering with the YouTube APIs. His main focus is the handling of video uploads., speakerldap: GarethMcSorleyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/am4r22010-05-10T18:39:11.857ZRow: 85speakertitle: German Bauer, speakercompany: Google, speakerabstract: German joined Google in 2006, working on the Android mobile operating system UI on both interaction and visual design. Before Google, he worked at Adobe, Apple, Claris, Netscape and Motorola on mobile and desktop products. German has a background in Industrial Design working in medical and consumer areas., speakerldap: GermanBauerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/anjbj2010-05-10T18:39:11.857ZRow: 86speakertitle: Gideon Mann, speakercompany: Google, speakerabstract: Gideon has worked as a research scientist at Google in New York City since 2007. His recent published work has been in weakly supervised learning and large-scale distributed training., speakerldap: GideonMannhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/aoxvw2010-05-10T18:39:11.857ZRow: 87speakertitle: Greg D'alesandre, speakercompany: Google, speakerabstract: Greg is a Product Manager on Google Wave based in Sydney, Australia. Before heading down under he was a PM for Google Sites and Google Groups. Pre-Google Greg worked at a variety of start-ups in a variety of roles, most recently as a Product Manager for JotSpot leading up to the Google Acquisition. Eons ago, when the sun was still young, Greg received his ScB and ScM in Electrical Engineering from Brown University. , speakerldap: GregD'alesandrehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/aqcgd2010-05-10T18:39:11.857ZRow: 88speakertitle: Greg Grothaus, speakercompany: Google, speakerabstract: Greg is a Staff Software Engineer who joined Google's Search Quality team in 2005, where he is responsible for maintaining the quality of the search results in Google's search index. Prior to Google, he studied Computer Science with an interest in Bioinformatics at Virginia Tech., speakerldap: GregGrothaushttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/b2zk22010-05-10T18:39:11.857ZRow: 89speakertitle: Guido van Rossum, speakercompany: Google, speakerabstract: Guido is the creator of the Python programming language. He joined Google in 2005. In 2007 he joined the Google App Engine team, where he has been working on Python language support, API design, UI programming, the Admin Console, and developer tools., speakerldap: GuidovanRossumhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/b5sow2010-05-10T18:39:11.857ZRow: 90speakertitle: Helen Chen, speakercompany: Cisco, speakerabstract: Helen is a software development manager at Emerging Technologies Group with Cisco Systems, Inc., where she leads OpenSocial enterprise adoption at Cisco Pulse and web application development. Her expertise includes web 2.0, User Experience, and social applications., speakerldap: HelenChenhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/b779d2010-05-10T18:39:11.857ZRow: 91speakertitle: Ian Fette, speakercompany: Google, speakerabstract: Ian joined the Google Chrome team as a product manager in 2007, focused on security, browser infrastructure, and the team's implementation and development of new web standards. Prior to joining Google, Ian worked for the U.S. Government. Ian has degrees in Computer Science from the University of Michigan and Carnegie Mellon University., speakerldap: IanFettehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/axdae2010-05-10T18:39:11.857ZRow: 92speakertitle: Ian Lewis, speakercompany: Google, speakerabstract: Ian has spent many years in the computer game industry. He was part of the launch team for the Xbox 360 and is credited on numerous titles such as Halo 3 and Grand Theft Auto 4. He joined Google in 2009 and is currently working on the Native Client team to make Chrome the ultimate platform for developers., speakerldap: IanLewishttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ayruv2010-05-10T18:39:11.857ZRow: 93speakertitle: Ian Taylor, speakercompany: Google, speakerabstract: Ian works on the Go compiler. He is the primary author of the gold linker and (years ago) Taylor UUCP, and has worked on a number of other free software projects., speakerldap: IanTaylorhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/b06f82010-05-10T18:39:11.857ZRow: 94speakertitle: Irwin Boutboul, speakercompany: Google, speakerabstract: Irwin is a software engineer with Google NYC. He works on building enterprise applications on App Engine. Before joining Google, Irwin worked on grid computing, VoIP and social networks at IBM. He then enjoyed going bankrupt with Lehman Brothers building real time trading systems in Java., speakerldap: IrwinBoutboulhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/b1kzp2010-05-10T18:39:11.857ZRow: 95speakertitle: J.D. Zamfirescu Pereira, speakercompany: Google, speakerabstract: J.D. is a software engineer focused on open sourcing the Google Wave code. J.D. left Google to become CTO at AppJet, the makers of EtherPad. AppJet was acquired by Google in December 2009, and J.D. is excited to be back -- this time, in sunny Sydney, Australia. He holds S.B. and M.Eng. degrees from MIT, speakerldap: J.D.ZamfirescuPereirahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/be8562010-05-10T18:39:11.857ZRow: 96speakertitle: Jaime Yap, speakercompany: Google, speakerabstract: Jaime is a software engineer on the Google Speed Tracer team. He is a contributor to the Google Web Toolkit project, and loves expanding the boundaries for what can be accomplished in the browser. Jaime earned his Masters degree in Computer Science from the Georgia Institute of Technology in 2007, and has been with Google since 2008. He currently resides in Atlanta and enjoys long walks on the beach., speakerldap: JaimeYaphttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bfmpn2010-05-10T18:39:11.857ZRow: 97speakertitle: James Kelm, speakercompany: Google, speakerabstract: Jim is a Product Manager at Google responsible for AdSense for Mobile Applications. He previously worked on a variety of payments, commerce, shopping, and mobile projects at Google. Jim holds degrees from Columbia and MIT. Before Google he worked at Sun Microsystems on Solaris and Apple on consumer portables. Outside work, Jim has run or consulted on Internet strategy in several high profile political campaigns., speakerldap: JamesKelmhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bh1a02010-05-10T18:39:11.857ZRow: 98speakertitle: James Young, speakercompany: Local Credit Union, speakerabstract: James is a happy father, husband, and geek. He voraciously read books and will read anything. James also builds (and fights) model warships. He belongs to northern Utah and feels that life isn't complete without mountains. James goes backpacking in the summer and snowboarding in the winter., speakerldap: JamesYounghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ba0fz2010-05-10T18:39:11.857ZRow: 99speakertitle: Jay Simons, speakercompany: Atlassian Software, speakerabstract: Jay leads sales & marketing for Atlassian, who sell ~$60M annually in software development tools directly to developers over the Web. Atlassian's tools include JIRA, an issue tracker for IT project management, Confluence, an advanced enterprise wiki, and Studio, a hosted software development suite integrated with Google Apps., speakerldap: JaySimonshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/b8lvi2010-05-10T18:39:11.857ZRow: 100speakertitle: Jeff Chang, speakercompany: Google, speakerabstract: Jeff joined the Google Chrome team as a product manager in September 2009. He currently focuses on the web platform, plug-ins, and Chrome's user-facing features. Prior to joining Google, Jeff received a B.S. in Computer Science and Engineering from MIT., speakerldap: JeffChanghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bifuh2010-05-10T18:39:11.857ZRow: 101speakertitle: Jeff Clavier, speakercompany: SoftTech VC, speakerabstract: Jeff is the Founder and Managing Partner of SoftTech VC, one of the most active seed stage investors in Web 2.0 startups. Since 2004, Jeff has invested in more than 60 consumer Internet companies in areas like social media, communities, search, gaming or consumer infrastructure, almost exclusively in Silicon Valley., speakerldap: JeffClavierhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bpgoi2010-05-10T18:39:11.857ZRow: 102speakertitle: Jeff Scudder, speakercompany: Google, speakerabstract: Jeff has been an engineer with Google since 2006 and has worked on products including the AdWords API, Google Data APIs, App Engine, Google AJAX APIs, and Google Web Elements. After graduating from Baylor with a CS degree, Jeff worked on web services at Boeing. When not crafting code, Jeff's been known to strum the guitar and be otherwise musical., speakerldap: JeffScudderhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bbf0c2010-05-10T18:39:11.857ZRow: 103speakertitle: Jeffrey Posnick, speakercompany: Google, speakerabstract: Jeff is a member of Google's Developer Relations team. He currently helps developers navigate the ins and outs of the YouTube APIs. Based in New York City, Jeff is happy to be making his first trip out to Google I/O in San Francisco., speakerldap: JeffreyPosnickhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/btodt2010-05-10T18:39:11.857ZRow: 104speakertitle: Jez Fletcher, speakercompany: Google, speakerabstract: Jez joined Google in 2008 after deciding academia wasn't the thrill-ride he'd expected. Since joining Google, Jez has worked on just about everything in the Google Geo APIs spectrum, including the v2 and v3 JS API, the Maps API for Flash and the Static Map API., speakerldap: JezFletcherhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bqv8z2010-05-10T18:39:11.857ZRow: 105speakertitle: Jim Palmer, speakercompany: Google, speakerabstract: Jim is the head of the Android UX team. Prior to joining Google, he worked on consumer experiences for Motorola, Good Technology, Liberate, and Apple. , speakerldap: JimPalmerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bl8zb2010-05-10T18:39:11.857ZRow: 106speakertitle: Jimin Li, speakercompany: Google, speakerabstract: Jimin is a Software Engineer on the Google Wave team working on Wave Media and API Projects. Prior to Google Wave, Jimin led Revenue and Reporting team of Google Relationship Manager. Prior to Google, Jimin was a Software Engineer in the Customer Relationship Management industry., speakerldap: JiminLihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bo2452010-05-10T18:39:11.857ZRow: 107speakertitle: Jochen Bekmann, speakercompany: Google, speakerabstract: Jochen is Tech Lead for federation on Google Wave. Prior to that, he worked on concurrency control and the wave server and on Google Maps at some point. He worked on TCP/IP at Microsoft and was part of a few start-ups before that. The dazzling allure of academia didn't entirely pass him by (PhD(UNSW), BScHons,BCom(UCT)). , speakerldap: JochenBekmannhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/c0p7u2010-05-10T18:39:11.857ZRow: 108speakertitle: Joe Kraus, speakercompany: Google, speakerabstract: Joe is a partner at Google Ventures focused on mobile, gaming and local services. Previously, he was a two-time entrepreneur. In 1993, he co-founded Excite.com, an early Internet search engine. In 2004, he co-founded JotSpot, a wiki company that was acquired by Google in 2006. While at Google, Joe held multiple product management roles. He is also on the board of the EFF., speakerldap: JoeKraushttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bmnjo2010-05-10T18:39:11.857ZRow: 109speakertitle: Joel Webber, speakercompany: Google, speakerabstract: Joel is a software engineer at Google and the co-creator of the Google Web Toolkit. Before working at Google, Joel spent his time banging bits and building cross-platform UI frameworks for mobile devices at AppForge. Still earlier, he was a game engine developer, but oddly enough finds working at Google to be more fun than writing games., speakerldap: JoelWebberhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bv2y62010-05-10T18:39:11.857ZRow: 110speakertitle: John Panzer, speakercompany: Google, speakerabstract: John hacks social software, standards, and products. He has participated in the Atom, AtomPub, OpenID, OAuth, OpenSocial, and Salmon standards efforts. He has helped to launch groups and blogging products at AOL and most recently managed Blogger. He is now working on open standards for the Social Web., speakerldap: JohnPanzerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/c4wx52010-05-10T18:39:11.857ZRow: 111speakertitle: John Zeratsky, speakercompany: YouTube, speakerabstract: John is a senior designer at YouTube. Prior to that he was a user experience lead at Google, focused on the company's suite of advertising products for agencies. From 2005 through 2007, he helped create FeedBurner, which was acquired by Google in June 2007., speakerldap: JohnZeratskyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/c3ico2010-05-10T18:39:11.857ZRow: 112speakertitle: Joon Kang, speakercompany: LG Electronics Mobile Research, speakerabstract: Joon has a broad and unique combination of experience in new business development, mobile and web technology and product strategy development. Prior to joining LG, Joon has been playing a pivotal role at Yahoo Search Marketing where he led global technology development and partner management. Joon studies at POSTECH, Korea, Columbia University and U.C. Berkeley, received Bachelor of Science and MBA emphasis on international business and technology management., speakerldap: JoongKanghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bwhin2010-05-10T18:39:11.857ZRow: 113speakertitle: Joseph Schorr, speakercompany: Google, speakerabstract: Joey is a software engineer on the Google Data team and is responsible for all API configuration development, including API automated configuration tools. Joey has worked at Google for almost two years and is a graduate of the University of Pennsylvania, with degrees in Computer Science and Business., speakerldap: JosephSchorrhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bxw302010-05-10T18:39:11.857ZRow: 114speakertitle: Joseph Smarr, speakercompany: Google, speakerabstract: Joseph is a software engineer at Google, focused on socially enabling the web using open standards. Previously, he was Plaxo's Chief Technology Officer. He is also a member of the Board of Directors of the OpenID Foundation. A frequent speaker and community participant in the social networking and web development communities, Joseph has built web applications for many years., speakerldap: JosephSmarrhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ceqw02010-05-10T18:39:11.857ZRow: 115speakertitle: Josh Livni, speakercompany: Google, speakerabstract: Josh is a Developer Programs Engineer, currently focusing on KML and the Earth API. Before joining Google, he worked as a consultant, integrating analytic, cartographic, and statistical tools on the web. Josh is an active member of the open source geospatial community, and plans to spend more time paragliding right after I/O., speakerldap: JoshLivnihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cdcbn2010-05-10T18:39:11.857ZRow: 116speakertitle: Justin Mattson, speakercompany: Google, speakerabstract: Justin is a Developer Advocate at Google. For the past year he has devoted his time to Android. Before that he spent time on some of Google's ads products. He has a strong interest in operating system architectures, macro economics, and advanced armchair physics., speakerldap: JustinMattsonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/c6bhi2010-05-10T18:39:11.857ZRow: 117speakertitle: Justin McWilliams, speakercompany: Google, speakerabstract: Justin is a developer in Corporate Engineering based in Google NYC. He has been building enterprise web applications on App Engine for the past year, previously filling a desktop support role since he joined Google in 2006. Justin graduated from the University of Michigan with a BA in 2005., speakerldap: JustinMcWilliamshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cg5gh2010-05-10T18:39:11.857ZRow: 118speakertitle: Kara Swisher, speakercompany: Wall Street Journal, speakerabstract: Kara started covering digital issues for The Wall Street Journal's San Francisco bureau in 1997 and also wrote the BoomTown column about the sector. With Walt Mossberg, she co-produces and co-hosts D: All Things Digital, a major high-tech and media conference., speakerldap: KaraSwisherhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cbxr62010-05-10T18:39:11.857ZRow: 119speakertitle: Kathrin Probst, speakercompany: Google, speakerabstract: Kathrin is a software engineer on the Google Web Toolkit team. Most recently, she has worked on making Ajax applications crawlable, and is now contributing to the next generation of the Google Plugin for Eclipse. Kathrin earned her Ph.D. in Computer Science in 2005, and has been with Google since 2008. She lives in Atlanta with her family, which she loves because she gets to spend lots of time outside., speakerldap: KathrinProbsthttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bzanh2010-05-10T18:39:11.857ZRow: 120speakertitle: Kelly Norton, speakercompany: Google, speakerabstract: Kelly writes software for Google and enjoys working in the gooey area between humans and technology. He has a range of quirky experience from network hardware to graphic design and is a contributor to the Google Web Toolkit project. Kelly is the proud owner of two degrees both with ornate lettering, the first in electrical engineering from Georgia Tech and the second from the MIT Media Lab., speakerldap: KellyNortonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hmyxm2010-05-10T18:39:11.857ZRow: 121speakertitle: Kevin Gibbs, speakercompany: Google, speakerabstract: Kevin joined Google in 2004 and is the technical lead and manager of the Google App Engine project. Prior to his work on Google App Engine, Kevin worked for a number of years in Google's systems infrastructure group, where he worked on the cluster management systems that underlie Google's products and services. Kevin created Google Suggest, the product which provides interactive search suggestions as you type on the Google homepage., speakerldap: KevinGibbshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/caj6t2010-05-10T18:39:11.857ZRow: 122speakertitle: Kris Muller, speakercompany: Salesforce.com, speakerabstract: Kris is a product marketer with Salesforce.com. He joined the company in 2006 and enjoyed several years of selling the Force.com platform and CRM apps as a sales engineer. He's now pushing to make the workplace more collaborative and is fired up by the collective intelligence that Salesforce Chatter delivers. , speakerldap: KrisMullerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/c94mc2010-05-10T18:39:11.857ZRow: 123speakertitle: Kuan Yong, speakercompany: YouTube, speakerabstract: Kuan is the Product Manager for the YouTube developer platform. Prior to working on the YouTube APIs, he worked on enterprise video hosting, mobile syndication and monetization, launching Google Video for business and AdSense for mobile content. Kuan loves his iPhone and writes iPhone apps in his spare time., speakerldap: KuanYonghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hps2g2010-05-10T18:39:11.857ZRow: 124speakertitle: Lars Rasmussen, speakercompany: Google, speakerabstract: Lars is a member of Google's technical staff, based in the Sydney office, and with his brother Jens is co-founder of the Google Wave effort. In early 2003, the brothers co-founded a mapping start-up, Where 2 Technologies, which was acquired by Google in October of 2004. Lars joined Google and worked as one of the lead engineers in the team that turned this acquisition into Google Maps, now used by millions of people around the world. Lars holds a Ph.D. in theoretical computer science from the University of California at Berkeley. Lars has possibly the world's least developed sense of direction, and consistently types faster than he can spell. , speakerldap: LarsRasmussenhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hodi32010-05-10T18:39:11.857ZRow: 125speakertitle: Linus Upson, speakercompany: Google, speakerabstract: Linus joined Google in 2005 and is currently a vice president of engineering overseeing Google's browser products including Chrome and Chrome OS. , speakerldap: LinusUpsonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hhcny2010-05-10T18:39:11.857ZRow: 126speakertitle: Loic Le Meur, speakercompany: Seesmic, speakerabstract: Loic is Founder and CEO of Seesmic, one of the most popular Twitter and Facebook social apps. He also founded and hosts the #1 European internet conference, LeWeb, with his wife Geraldine. Loic was named one of the "25 Most Influential People on the Web" and 'Young Global Leader' by Business Week and the World Economic Forum, respectively. He lives in San Francisco and has three boys., speakerldap: LoicLeMeurhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hr6mx2010-05-10T18:39:11.857ZRow: 127speakertitle: Mano Marks, speakercompany: Google, speakerabstract: Mano joined Google's Geo API team in 2006. He helps large companies, small startups, and international aid organizations all over the world develop and deploy their content in KML and Google Maps. Before Google, Mano had an eclectic career that involved getting a Masters in History, a Masters in Information Management and Systems, and working as a data manager in small and mid-sized organizations for over a decade., speakerldap: ManoMarkshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/c7q1z2010-05-10T18:39:11.857ZRow: 128speakertitle: Marc Ridey, speakercompany: Google, speakerabstract: Marc is a Software Engineer working on Google Maps JavaScript API v3. His focus for the past year has been on mobile and performance. Marc graduated as a Computer Engineer in France before moving to Australia. Before joining Google, Marc worked as a Senior Consultant on Microsoft .NET and SQL Technologies., speakerldap: MarcRideyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hlkd92010-05-10T18:39:11.857ZRow: 129speakertitle: Marcel Prasetya, speakercompany: Google, speakerabstract: Marcel is a software engineer on Google Wave, focusing on the Google Wave Robot API. He joined Google in early 2006, and worked for the internal system team for three years before joining Google Wave. Marcel graduated from the University of Texas at Austin, with a bachelor's degree in Computer Science., speakerldap: MarcelPrasetyahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hk5ss2010-05-10T18:39:11.857ZRow: 130speakertitle: Marcelo Camelo, speakercompany: Google, speakerabstract: Marcelo is a software engineer with Google Australia, where he works in the Maps API team, with a focus on latency and mobile. Previously, Marcelo worked developing massively multiplayer games and data visualization software. Marcelo holds a B.Eng. in Metallurgical Engineering and a Masters in Industrial Engineering., speakerldap: MarceloCamelohttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hir8f2010-05-10T18:39:11.857ZRow: 131speakertitle: Mark Halvorson, speakercompany: Atlassian Software, speakerabstract: Mark is the Chief Imagineer and OpenSocial Evangelist at Atlassian Software - the makers of JIRA and Confluence. He has a long history working with portals and social enterprise software having worked in various capacities from software developer to technical marketer for Firepond, Plumtree, BEA and Oracle., speakerldap: MarkHalvorsonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hzm1f2010-05-10T18:39:11.857ZRow: 132speakertitle: Mark Stahl, speakercompany: Google, speakerabstract: Mark is a technical lead on the Google Data APIs clients team. After starting his career at Bell Labs, Mark was lured into a small networking startup just in time for the dot-com crash. In 2005, he took his experiences to Google, where he started working on the API infrastructure. He spends his time trying to figure out how to make it easy to build great APIs., speakerldap: MarkStahlhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hsl7a2010-05-10T18:39:11.857ZRow: 133speakertitle: Mark Weitzel, speakercompany: International Business Machines (IBM), speakerabstract: Mark is a Senior Technical Staff Member in IBM Software Group’s Emerging Standards and Open Source team focusing on social networking and is also Secretary of the OpenSocial Foundation. Prior to this, Mark, as part of Tivoli’s Autonomic Computing team, was responsible for several open source systems management initiatives at Eclipse and Apache. , speakerldap: MarkWeitzelhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/i2f692010-05-10T18:39:11.857ZRow: 134speakertitle: Matt Cutts, speakercompany: Google, speakerabstract: Matt is the head of the webspam team at Google where he specializes in search engine optimization (SEO) issues. He is known in the webmaster and SEO community for applying Google's Quality Guidelines. Matt sometimes discusses search issues and offers advice on website visibility in Google on his personal blog at http://www.mattcutts.com/blog/ . You can also catch him on Twitter at http://twitter.com/mattcutts, speakerldap: MattCuttshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/i10ls2010-05-10T18:39:11.857ZRow: 135speakertitle: Matt Harding, speakerabstract: Matt travels around the world and films himself dancing badly in exotic places. That's basically his job. God bless the internet., speakerldap: MattHardinghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hy7gy2010-05-10T18:39:11.857ZRow: 136speakertitle: Matt Holleran, speakercompany: Emergence Capital Partners, speakerabstract: Matt is a Venture Partner at Emergence Capital Partners. He is an expert in cloud application business models and cloud ecosystems. Prior to joining the firm, Matt’s team started and built the AppExchange and Force.com partner ecosystem at salesforce.com. He advises cloud platform companies and helps ISVs leverage them. He is interested in freemium business application companies., speakerldap: MattHolleranhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/i9g0a2010-05-10T18:39:11.857ZRow: 137speakertitle: Matt Lowrie, speakercompany: Google, speakerabstract: Matt is a Test Engineer who has worked at Google since 2006. He ensures product quality on several Geo products, including Building Maker and SketchUp., speakerldap: MattLowriehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ic9542010-05-10T18:39:11.857ZRow: 138speakertitle: Matt Mastracci, speakercompany: DotSpots, speakerabstract: Matt is a founder and CTO of DotSpots. He is currently leading the efforts to build better ways to allow users to attach images, videos and blog posts to the news in real-time using a platform based on Google Web Toolkit. Prior to DotSpots, he worked on building rich, scalable, browser-based applications for the energy sector and micropayments., speakerldap: MattMastraccihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/iaukr2010-05-10T18:39:11.857ZRow: 139speakertitle: Matt Pruden, speakercompany: Appirio, speakerabstract: Matt is a Technical Architect at Appirio who has helped transition over 200,000 users at 40 companies to Google Apps. His expertise is in enterprise use of the Google Apps platform. Prior to Appirio, Matt spent seven years in Intel IT, providing perspective into large scale technology transitions. Matt's technical repertoire includes Python, Twisted, Ruby, Java, etc., speakerldap: MattPrudenhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hwswl2010-05-10T18:39:11.857ZRow: 140speakertitle: Matt Shobe, speakercompany: Google, speakerabstract: Matt is a staff user experience designer at Google whose work centers on services for publishers. Prior to Google, Matt was a co-founder of FeedBurner and led user experience design efforts there and on two prior startups with the same team. Matt is an avid distance runner, private pilot, and skier, though no such triathlon exists (yet)., speakerldap: MattShobehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hvec42010-05-10T18:39:11.857ZRow: 141speakertitle: Matt Tucker, speakercompany: Jive Software, speakerabstract: Matt is the CTO and Co-Founder of Jive Software. He leads technology efforts such as Jive's adoption of cloud technologies and open standards. He's also an active member of the OpenSocial and XMPP communities., speakerldap: MattTuckerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/htzrr2010-05-10T18:39:11.857ZRow: 142speakertitle: Matthew Blain, speakercompany: Google, speakerabstract: Matthew is a software engineer on the Google App Engine team working on tools. Prior to joining App Engine, he worked on Google Toolbar. Prior to Google, Matthew had stints as a software consultant and as a developer on Microsoft Internet Explorer and InfoPath., speakerldap: MatthewBlainhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/i58b32010-05-10T18:39:11.857ZRow: 143speakertitle: Matthew Holden, speakercompany: Google, speakerabstract: Matthew is a Product Manager for the Google Maps Data API, Google Earth vector layers, and global problem reporting on Google Maps. He joined Google in 2008, and recently helped launch spatial + attribute search in the Maps Data API. The Maps Data API provides scalable hosting and searching of geographic content in Google's cloud., speakerldap: MatthewHoldenhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/i3tqm2010-05-10T18:39:11.857ZRow: 144speakertitle: Matthew Papakipos, speakercompany: Google, speakerabstract: Matthew is an Engineering Director at Google, where he runs the Chrome OS and Chrome Graphics efforts. Prior to working at Google, he was the CTO of PeakStream and Architecture Director at NVIDIA. His interests include computer hardware and software, interactive computer graphics, and games. He has over forty awarded patents in processor design and software architecture. He received his BS in Mathematics and Computer Science from Brown University., speakerldap: MatthewPapakiposhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/im3432010-05-10T18:39:11.857ZRow: 145speakertitle: Matthew Simmons, speakercompany: Google, speakerabstract: Matthew is a developer working in Google Corporate Engineering. His work centers on developing and evaluating knowledge management systems. In his spare time he can be found hanging out with his wife and daughters or doing research at the University of Michigan in pursuit of his Ph.D. in Information., speakerldap: MatthewSimmonshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ikojm2010-05-10T18:39:11.857ZRow: 146speakertitle: Matthew Tonkin, speakercompany: Memeo Inc, speakerabstract: Matt is the Mac Applications Architect at Memeo and technical lead for Memeo Connect, a client for syncing files between the desktop and Google Docs by utilizing the new API's for storing any file type. Memeo is extending Connect and Google Docs beyond the desktop to mobile platforms including Android and iPad. Matt originates from Australia and is currently living and working in Orange County, CA., speakerldap: MatthewTonkinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/i81fx2010-05-10T18:39:11.857ZRow: 147speakertitle: Max Lin, speakercompany: Google, speakerabstract: Max is a software engineer with Google Research in New York City office. Prior to Google, he published research work in video content analysis, sentiment analysis, machine learning, and cross-lingual information retrieval. He has a PhD in Computer Science from Carnegie Mellon University., speakerldap: MaxLinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/i6mvg2010-05-10T18:39:11.857ZRow: 148speakertitle: Max Ross, speakercompany: Google, speakerabstract: Max is a test-driven Software Engineer on the App Engine team where he works on the datastore and the Java runtime. He is also the founder of the Hibernate Shards project. Prior to joining Google he was a software engineer in the supply chain management and travel technology industries. He holds a BA in American Studies and Computer Science from Williams College., speakerldap: MaxRosshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/idnpl2010-05-10T18:39:11.857ZRow: 149speakertitle: Michael Fink, speakercompany: Google, speakerabstract: Michael joined Google Research in 2005, where he started the “mass personalization” effort. Since then, Michael Fink initiated several media-based projects, including YouTube's Interactive Video Annotations, which enables embedding of hyperlinks onto objects in YouTube videos. When he's not at work, you'll probably find him scuba diving in the Red Sea., speakerldap: MichaelFinkhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ixbp72010-05-10T18:39:11.857ZRow: 150speakertitle: Michael Goderbauer , speakercompany: Hasso Plattner Institute, speakerabstract: Michael is a student of Germany's #1 ranked software engineering school, the Hasso Plattner Institute in Potsdam. Right now he is in his sixth semester and he will finish his bachelor’s degree this summer. About a year ago, he and six fellow students started the processWave.org project to create a collaborative diagram editor for Google Wave. , speakerldap: MichaelGoderbauer http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/iyq9k2010-05-10T18:39:11.857ZRow: 151speakertitle: Michael Morrissey, speakercompany: Google, speakerabstract: Michael runs the Android services team at Google, which includes Market, sync, and device management. Prior to Android, he built the Sidekick/Hiptop service platform at Danger and worked on mobile products at Microsoft., speakerldap: MichaelMorrisseyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j04u12010-05-10T18:39:11.857ZRow: 152speakertitle: Michael Schwartz, speakercompany: Google, speakerabstract: Mike is a software engineer in Google's Seattle office. Before Google he worked at a number of start-up companies, and was an Associate Professor of Computer Science at the University of Colorado - Boulder., speakerldap: MichaelSchwartzhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/iqav22010-05-10T18:39:11.857ZRow: 153speakertitle: Michael Van Riper, speakercompany: Krillion, Inc., speakerabstract: Van is Chief Web Technologist at Krillion and a Java Champion. He leads the largest Java and Google Technology User Groups in Silicon Valley. Van is also a serial community organizer., speakerldap: MichaelVanRiperhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/irpfj2010-05-10T18:39:11.857ZRow: 154speakertitle: Mihai Ionescu, speakercompany: Google, speakerabstract: Since starting at Google in 2003, Mihai has worked on Google Desktop, O3D and Chrome. Lately, he is focusing on HTML5 and particularly on Chrome as a developer platform for rich web applications. Prior to Google, Mihai worked at Microsoft on Internet Explorer, Java VM, and .NET. He holds an MBA from the Berkeley Haas School of Business and an MS in Computer Science from UC Santa Barbara., speakerldap: MihaiIonescuhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/it3zw2010-05-10T18:39:11.857ZRow: 155speakertitle: Mike Aizatsky, speakercompany: Google, speakerabstract: Mike is a software engineer working on Google App Engine. He worked on Google Code Search prior to joining the App Engine team. Before joining, Google he was a Project Manager at JetBrains working on developer tools.
+, speakerldap: MikeAizatskyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/iuikd2010-05-10T18:39:11.857ZRow: 156speakertitle: Mike O'Brien, speakercompany: Appirio, speakerabstract: Mike manages Appirio's growing enterprise Google business. He has responsibility for sales and delivery of Appirio's Google customers with a focus on large global enterprise implementations. He brings over 15 years of experience leading large enterprise software deployments that involve complex business requirements for global businesses., speakerldap: MikeOBrienhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j75o22010-05-10T18:39:11.857ZRow: 157speakertitle: Ming Yong, speakercompany: Voiceroute Pte Ltd (Socialwok)
+, speakerabstract: Ming is Founder and CEO of Socialwok (http://socialwok.com), one of the most popular business social productivity service on the web for group collaboration and enterprise microblogging. Ming previously founded Druid, a popular open source project on Unified Communications for small medium businesses.He is also one of the committee member of the Singapore Google Technology User group (SG-GTUG)., speakerldap: MingYonghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j8k8j2010-05-10T18:39:11.857ZRow: 158speakertitle: Moishe Lettvin, speakercompany: Google, speakerabstract: Moishe is a software engineer working in Google's Kirkland and Seattle offices. In his time at Google, he's worked on many aspects of Google Talk and is currently working on Google App Engine., speakerldap: MoisheLettvinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j9ysw2010-05-10T18:39:11.857ZRow: 159speakertitle: Nathan Naze, speakercompany: Google, speakerabstract: Nathan is a software engineer at Google. He works on user interface for Google Books, one of many projects using Closure Library. Open sourcing Closure Library was his 20% project with Daniel Nadasi. He joined Google in 2005 after graduating from the University of Wisconsin with majors in computer science, political science, and economics. He lives in San Francisco., speakerldap: NathanNazehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jbddd2010-05-10T18:39:11.857ZRow: 160speakertitle: Nick Johnson, speakercompany: Google, speakerabstract: Nick is a Developer Programs Engineer on the App Engine team. He spends his days helping people write code for App Engine. His nights are spent writing code about App Engine and then blogging about it., speakerldap: NickJohnsonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j1jee2010-05-10T18:39:11.857ZRow: 161speakertitle: Nick Mihailovski, speakercompany: Google, speakerabstract: Nick has been working on Google Analytics for over 5 years and is currently working as a developer programs engineer to help developers create better tools, integrations and automation with Google Analytics and 3rd party systems., speakerldap: NickMihailovskihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j2xyv2010-05-10T18:39:11.857ZRow: 162speakertitle: Pamela Fox, speakercompany: Google, speakerabstract: Pamela has worked in the Developer Relations group at Google for the last 3 years. She spent 3 years supporting the Maps API community, and now is focusing her time on helping the blossoming Wave APIs community. In her free time, she likes to play around with any one of Google's 60+ APIs. , speakerldap: PamelaFoxhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j4cj82010-05-10T18:39:11.857ZRow: 163speakertitle: Paul Graham, speakercompany: Y Combinator, speakerabstract: Paul is a partner at Y Combinator and the author of On Lisp (1993), ANSI Common Lisp (1995), and Hackers & Painters (2004). In 1995, he and Robert Morris started Viaweb, the first ASP, which in 1998 became Yahoo! Store. In 2002 he discovered a simple spam filtering algorithm that inspired the current generation of filters. He has an AB from Cornell and a PhD in Computer Science from Harvard., speakerldap: PaulGrahamhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j5r3p2010-05-10T18:39:11.857ZRow: 164speakertitle: Pavel Feldman, speakercompany: Google, speakerabstract: Pavel is a software engineer working on Google Chrome Developer Tools and WebKit's Inspector. He worked on Google Calendar prior to joining the Chrome team. Before joining Google, he was Chief Scientist at Borland working on modeling tools., speakerldap: PavelFeldmanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jie7e2010-05-10T18:39:11.857ZRow: 165speakertitle: Peter Birch, speakercompany: Google, speakerabstract: Peter joined Google in 2006 as Product Manager for Google Earth, Google's 3D earth visualization tool that combines the power of Google Search with satellite imagery, maps, terrain and 3D buildings. He is responsible for the direction, growth, and success of the Google Earth product family including Google Earth Free, Pro, and Enterprise, the Google Earth API and browser plug-in, and Google Earth for the iPhone., speakerldap: PeterBirchhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jjsrv2010-05-10T18:39:11.857ZRow: 166speakertitle: Phil Liao, speakercompany: Google, speakerabstract: Phil is a Software Engineer on the Google Wave team. Prior to joining Google, he built software in areas of education, supply chain execution, brain imaging and finance. Phil holds computer science degrees from MIT and Berkeley., speakerldap: PhilLiaohttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jl7c82010-05-10T18:39:11.857ZRow: 167speakertitle: Ray Cromwell, speakercompany: Timefire, speakerabstract: Ray is CTO and co-founder of Timefire where he dreams of flying over vast historical data landscapes. These days, he works on visualization software using Google Web Toolkit. Prior to Timefire, he led Oracle's efforts to build mobile XForms implementations and draft IETF standards for push email., speakerldap: RayCromwellhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jmlwp2010-05-10T18:39:11.857ZRow: 168speakertitle: Ray Ryan, speakercompany: Google, speakerabstract: Ray is part of the Google Web Toolkit team, and has been goading people into writing software the way he thinks they should since the 1980s. He has worked at Lighthouse Design, Sun, AOL, and has spent the last four years at Google. Recently he had a strong hand in shaping the architecture of the new GWT-based user interface for AdWords., speakerldap: RayRyanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jcrxq2010-05-10T18:39:11.857ZRow: 169speakertitle: Reto Meier, speakercompany: Google, speakerabstract: Reto is a Developer Advocate for Android at Google and is the author of 'Professional Android 2 Application Development'. As the EMEA Android Advocate he works closely with Android developers, helping them make the most of the platform to bring rich, compelling apps to the Android Market. Before Google he worked in various industries, including offshore oil and gas while in Australia and the London finance market. , speakerldap: RetoMeierhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/je6i72010-05-10T18:39:11.857ZRow: 170speakertitle: Richard Fulcher, speakercompany: Google, speakerabstract: Richard Fulcher is a senior user interface designer on the Android team, focused on communication applications. Prior to joining Google, he designed consumer experiences for TiVo, Sirius, Dell and AOL. He freely admits to playing lots of board games., speakerldap: RichardFulcherhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jfl2k2010-05-10T18:39:11.857ZRow: 171speakertitle: Richard Rabbat, speakercompany: Google, speakerabstract: Richard is a product manager at Google. He recently released Page Speed, a Firefox add-on that analyzes web pages and gives suggestions on how to improve them in addition to doing some of the optimizations itself. Richard manages the Google program to make the web faster and works on projects that power Google’s infrastructure including latency measurements for Google apps., speakerldap: RichardRabbathttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jgzn12010-05-10T18:39:11.857ZRow: 172speakertitle: Rob Pike, speakercompany: Google, speakerabstract: Rob works on distributed systems, programming languages, and software development tools. Before Google, Rob was at Bell Labs Research. He was an architect of the Plan 9 and Inferno operating systems and is the co-author with Brian Kernighan of The Unix Programming Environment and The Practice of Programming., speakerldap: RobPikehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jtmqq2010-05-10T18:39:11.857ZRow: 173speakertitle: Romain Guy, speakercompany: Google, speakerabstract: Romain is a software engineer at Google. After spending years having fun with large UIs on the desktop and talking about them at conferences, in blogs, magazines and books, Romain decided to go for the small screen and joined the Android project, an Open Source operating system for mobile phones. He’s now trying to make mobile phone UIs as fun and exciting as desktop ones., speakerldap: RomainGuyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jv1b72010-05-10T18:39:11.857ZRow: 174speakertitle: Rus Heywood, speakercompany: Google, speakerabstract: Rus is the lead engineer for the Google PowerMeter project, which is part of Google.org. Rus came to Google through Jotspot, a collaboration tool company that Google acquired in 2006., speakerldap: RusHeywoodhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jwfvk2010-05-10T18:39:11.857ZRow: 175speakertitle: Russ Cox, speakercompany: Google, speakerabstract: Russ is a software engineer working on the Go team. Before working on Go, Russ led the creation of Code Search, Google's only regular expression-based search engine., speakerldap: RussCoxhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jxug12010-05-10T18:39:11.857ZRow: 176speakertitle: Ryan Boyd, speakercompany: Google, speakerabstract: Ryan is a developer advocate at Google focused on enabling developers to extend Google Apps and build businesses on top of Google technology. He previously worked on OpenSocial, Google Friend Connect and Google's AtomPub APIs. Prior to joining Google, Ryan worked in higher education as a web architect for RIT's web hosting environment and as web app developer., speakerldap: RyanBoydhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jo0h22010-05-10T18:39:11.857ZRow: 177speakertitle: Ryan Sarver, speakercompany: Twitter, speakerabstract: Ryan is currently the Director of Platform at Twitter where he works with developers building myriad of experiences on top of 140 characters. Previously he was the Director of Consumer Products at Skyhook Wireless where he led product initiatives that leveraged the WPS platform in consumer applications and experiences. Ryan was also one of the founding members of the W3C Geolocation API Working Group., speakerldap: RyanSarverhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jpf1j2010-05-10T18:39:11.857ZRow: 178speakertitle: Sami Shalabi, speakercompany: Google, speakerabstract: Sami is a Technical Lead Manager at Google. He is leading the engineering teams responsible for Google Friend Connect and Google Buzz APIs. Prior to Google, Sami was a co-founder of Zingku (acquired by Google), and YallaStartup. Sami holds a Bachelor’s and Master’s degrees from MIT., speakerldap: SamiShalabihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jqtlw2010-05-10T18:39:11.857ZRow: 179speakertitle: Scott McMullan, speakercompany: Google, speakerabstract: Scott is a Senior Enterprise Program Manager focused on helping 3rd parties grow their business with Google Apps. A startup guy at heart, Scott joined Google from JotSpot, where he ran developer programs, and previously founded Inovie Software, maker of early Internet collaboration service TeamCenter. Scott holds bachelor's and master's degrees in computer science from UCSD and was a PhD student at the university's Computer Systems Lab., speakerldap: ScottMcMullanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/js86d2010-05-10T18:39:11.857ZRow: 180speakertitle: Sean Lynch, speakercompany: Google, speakerabstract: Sean is a Product Manager for Google App Engine. Since joining Google in 2008, Sean worked on Google core infrastructure before moving to the App Engine team in 2009. Sean holds a B.S. in Computer Science from the University of Waterloo., speakerldap: SeanLynchhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/f52je2010-05-10T18:39:11.857ZRow: 181speakertitle: Seth Covitz, speakercompany: Google, speakerabstract: Seth is a software engineer on Google Wave. He enjoys innovating and pushing the boundaries of the web each and every day. Prior to Google, he held positions as CTO, VP of Development, and Software Architect for companies ranging from startups to the Fortune 50. Seth holds a B.S. degree in Math and Computer Science from Carnegie Mellon University. , speakerldap: SethCovitzhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/f6h3v2010-05-10T18:39:11.857ZRow: 182speakertitle: Shih-chia Cheng, speakercompany: Google, speakerabstract: Shih-Chia is a software engineer of JAPAC iGoogle team. Prior to iGoogle, he worked on Google Maps. Shih-Chia has a Masters degree from Stanford University. His interests include social dance, games, and Chinese chess., speakerldap: Shih-chiaChenghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/f9a8p2010-05-10T18:39:11.857ZRow: 183speakertitle: Siddartha Naidu, speakercompany: Google, speakerabstract: Siddartha has been crunching large data sets at Google since 2005 for a variety of products and as a physics grad student before that. He has worked with or on almost every data processing framework at Google and is still looking for ways to make his job easier., speakerldap: SiddarthaNaiduhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ezg9q2010-05-10T18:39:11.857ZRow: 184speakertitle: Srikanth Rajagopalan, speakercompany: Google, speakerabstract: Srikanth is a Product Manager for Infrastructure/Advanced Projects at Google. He has tackled responsibilities ranging from rolling out Google PowerMeter to making Google’s Cloud Computing Infrastructure more efficient and scaling Google Book Search towards scanning the world's offline content., speakerldap: SrikanthRajagopalanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/f0uu72010-05-10T18:39:11.857ZRow: 185speakertitle: Stefan Haustein, speakercompany: Google, speakerabstract: Stefan is a software engineer at Google in London and is responsible for the WebOS port of Google Maps. Prior to joining Google, he worked as an engineering director at Interact!v GmbH in Cologne. Stefan has a PhD in Computer Science from the University of Dortmund. , speakerldap: Stefan Hausteinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/f29ek2010-05-10T18:39:11.857ZRow: 186speakertitle: Stephanie Hannon, speakercompany: Google, speakerabstract: Stephanie, the lead product manager for Google Wave, is based in Google's Sydney office. Prior to Sydney she worked in Google's Zurich office, leading the product team for Google Maps for Europe, Middle East and Africa, and before this she was product manager for Gmail in at Google in Mountain View, CA. Stephanie holds a B.S. Computer Systems Engineering and M.S.E.E. from Stanford University and an M.B.A. from Harvard Business School. , speakerldap: StephanieHannonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/f3nz12010-05-10T18:39:11.857ZRow: 187speakertitle: Steve Bazyl, speakercompany: Google, speakerabstract: Steve is a Developer Advocate at Google focused on developers integrating with Google Apps. Prior to joining Google, Steve was an architect at IGT and RSA Security where he worked on building scalable and secure web based systems. , speakerldap: Steve Bazylhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fgb2q2010-05-10T18:39:11.857ZRow: 188speakertitle: Susannah Raub, speakercompany: Google, speakerabstract: Susannah is the tech lead for the Maps API v3 team in Sydney, focused on latency and mobile. Prior to moving to Australia, she worked on Local Search in New York and Google Desktop in Mountain View. She earned her bachelors in Mathematics-Computer Science at Brown University., speakerldap: SusannahRaubhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fhpn72010-05-10T18:39:11.857ZRow: 189speakertitle: Thatcher Ulrich, speakercompany: Google, speakerabstract: Thatcher is a founding member of the Google Earth API and Browser Plugin team. Prior to joining Google he worked on games and graphics at Oddworld Inhabitants and a variety of startups., speakerldap: ThatcherUlrichhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fj47k2010-05-10T18:39:11.857ZRow: 190speakertitle: Thor Mitchell, speakercompany: Google, speakerabstract: Thor is a Product Manager for the Google Maps Developer Platform, based in Sydney, Australia. Although originally from the U.K., he does not have a strong preference between Marmite and Vegemite, despising both equally., speakerldap: ThorMitchellhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fkis12010-05-10T18:39:11.857ZRow: 191speakertitle: Timothy Jordan, speakercompany: Google, speakerabstract: Timothy is a Developer Advocate at Google working on the social web. He also teaches New Media at the Academy of Art University and announces Roller Derby. As an engineer he enjoys solving problems for the benefit of fellow humans. As an artist he enjoys the search for truth and beauty., speakerldap: TimothyJordanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/faot22010-05-10T18:39:11.857ZRow: 192speakertitle: Toby Reyelts, speakercompany: Google, speakerabstract: Toby is a progenitor of App Engine for Java and a member of the Google Web Toolkit team. Toby's current areas of interest are performance (45.32us is too slow) and improving dynalangs on the JVM and App Engine for Java., speakerldap: TobyReyeltshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fc3dj2010-05-10T18:39:11.857ZRow: 193speakertitle: Todd Jackson, speakercompany: Google, speakerabstract: Todd is a product manager on Gmail and the product lead for Google Buzz. He has worked at Google since 2004. Todd holds an MS and BS in Computer Science from Stanford University., speakerldap: ToddJacksonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fdhxw2010-05-10T18:39:11.857ZRow: 194speakertitle: Tom Manshreck, speakercompany: Google, speakerabstract: Tom is a senior technical writer and has worked on the Maps API since 2007, where he writes and maintains the Maps API documentation set. Prior to Google, Tom was a managing editor for computer science at Prentice Hall/Pearson. He enjoys driving around the States with his cocker spaniel, Faulkner., speakerldap: TomManshreckhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fewid2010-05-10T18:39:11.857ZRow: 195speakertitle: Vic Gundotra, speakercompany: Google, speakerabstract: Vic joined Google in 2007 as VP of Engineering, responsible for mobile applications and developer evangelism. Vic spent 15 years at Microsoft, working on products and operating systems including Windows 3.0, NT, Windows XP, and Vista. He was recognized by MIT as a "Young Innovator under 35" for his work in sparking Microsoft's change from Win32 to the .NET programming model. Vic holds two patents in the area of distributed computing and identity-based access to cloud resources., speakerldap: VicGundotrahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/frjm22010-05-10T18:39:11.857ZRow: 196speakertitle: Vijay Bangaru, speakercompany: Google, speakerabstract: Vijay is a product manager on the Google Docs team. Previously at Google, he ran core infrastructure teams for storage, object transport, indexing and crawling. Prior to Google, he worked at Microsoft where he drove product strategy and led development teams for SQL Server, WinFS and the .NET Framework. Vijay holds bachelor's degrees in computer engineering and electrical engineering from Washington University of St. Louis, where he graduated summa cum laude., speakerldap: VijayBangaruhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fsy6j2010-05-10T18:39:11.857ZRow: 197speakertitle: Virgil Dobjanschi, speakercompany: Google, speakerabstract: Virgil is an Android application developer at Google. He focuses on Social applications development. Prior to Android, Virgil was the President of Multiple Facets and Chief Architect at AudioTalk Networks and Essentel., speakerldap: VirgilDobjanschihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fucqw2010-05-10T18:39:11.857ZRow: 198speakertitle: Zach Maier, speakercompany: Google, speakerabstract: Zach is the product manager for the Google Data Protocol, and his team is responsible for building the infrastructure behind most of Google's APIs. Rumor has it that Zach invented the hashtag #myjobisawesome after he started working on developer products at Google. Before APIs, Zach attended Kansas State University where he graduated summa cum laude with a degree in computer engineering and a specialty in machine intelligence., speakerldap: ZachMaier
\ No newline at end of file
diff --git a/assets/.svn/text-base/cache-vendors.xml.svn-base b/assets/.svn/text-base/cache-vendors.xml.svn-base
new file mode 100644
index 0000000..e824e87
--- /dev/null
+++ b/assets/.svn/text-base/cache-vendors.xml.svn-base
@@ -0,0 +1,20 @@
+http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic2010-05-10T18:39:11.857Zsandboxchristine.leechristine.lee@gmail.com1831http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cpzh42010-05-10T18:39:11.857ZRow: 2companyname: 12wiki, companylocation: Breda, Noord-Brabant, The Netherlands, companydesc: 12wiki is a webdevelopment company specialised in open-source software., companyurl: www.12wiki.eu, productdesc: Currently we are working on MediawikiWave, which will allow you to edit Mediawiki articles (i.e. Wikipedia) in a Wave Enviroment, solving a lot of the issues with the current editor and making contributing content a pain-free experience., companylogo: 12wiki.png, companypod: Wave, companytags: Wave, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cre1l2010-05-10T18:39:11.857ZRow: 3companyname: 280 North, Inc., companylocation: San Francisco, California, companydesc: Creators of 280 Slides, a web based presentation editor; the Cappuccino Web Framework, for building desktop-class applications that run in any browser; and Atlas, a powerful visual editor for building applications with Cappuccino., companyurl: www.280north.com, productdesc: 280 Slides relies on the Google AJAX APIs to provide a rich media experience to users. Without leaving the site, users can integrate relevant pictures and videos from the web in just a few clicks., companylogo: 280north.png, companypod: Google APIs, companytags: Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/b8lvi2010-05-10T18:39:11.857ZRow: 4companyname: 6rounds, companylocation: Tel Aviv, Israel, companydesc: 6rounds is a social entertainment video chat platform. The platform combines interactive video chats, co-browsing activities and real-time games to create a fun, playful and thrilling place for people of all ages., companyurl: www.6rounds.com, productdesc: 6rounds created a Google Wave extension which enables Wave users to enjoy instant one-on-one video chats and collaborate together in real-time using webcams, various rich activities and different interactive games., companylogo: 6rounds.png, companypod: Wave, companytags: Wave, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/chk2m2010-05-10T18:39:11.857ZRow: 5companyname: Adobe Systems Incorporated, companylocation: San Jose, California, companydesc: Adobe revolutionizes how the world engages with ideas and information - anytime, anywhere and through any medium., companyurl: www.adobe.com, companylogo: adobe.png, companypod: Chrome, companytags: Chrome, adddate: 5/7/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ciyn32010-05-10T18:39:11.857ZRow: 6companyname: Aloqa, companylocation: Palo Alto, California, companydesc: Aloqa is a mobile service that proactively notifies you of interesting places, events, music, movies and other activities near you. Instead of having to search, you can just look at your phone and see your favorite hotspots, activities, events of interest, and recommended bargains close by., companyurl: www.aloqa.com, productdesc: We use several Google products: the Android SDK for building the Android client, the Google Static Maps API for maps on WinMo and J2ME phones, the Google Visualization API for our internal analytics, Google Local Search for our Pull-based Search Channel, Google AdSense for Mobile, and Google Web Toolkit for some of our websites., companylogo: aloqa.png, companypod: Android, companytags: Android, Geo, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ckd7g2010-05-10T18:39:11.857ZRow: 7companyname: Amazon, companylocation: Seattle, Washington, companydesc: Amazon.com, Inc., seeks to be Earth's most customer-centric company, where customers can find and discover anything they might want to buy online, and endeavors to offer its customers the lowest possible prices., companyurl: www.amazon.com, companylogo: amazon.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/clrrx2010-05-10T18:39:11.857ZRow: 8companyname: Appirio, companylocation: San Mateo, California, companydesc: Appirio is a cloud solution provider, delivering both products and professional services that help enterprises accelerate their adoption of cloud computing., companyurl: www.appirio.com, productdesc: Appirio uses Google App Engine, GWT, GData, OpenID/oAuth and OpenSocial Gadgets to build custom productivity extensions to Google Apps which help a wide range of customers increase productivity and enrich their experience with existing SaaS investments. For example, our popular "connector" products have helped almost 5,000 customers worldwide synchronize their contacts and calendar events between Salesforce CRM and Google Apps. , companylogo: appirio.png, companypod: Enterprise, companytags: Enterprise, App Engine, GWT, Google APIs, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cztg32010-05-10T18:39:11.857ZRow: 9companyname: Atlassian, companylocation: San Francisco, California, companydesc: Atlassian is an Australian software company specialising in collaboration and development tools to help technical teams build better software. Atlassian's flagship products include JIRA, the popular issue tracker, Confluence, the enterprise wiki, and JIRA Studio, a hosted development suite., companyurl: www.atlassian.com, productdesc: Atlassian makes use of many different Google products and technologies in its products. JIRA and Confluence are both OpenSocial containers and publishers, and Atlassian uses OpenSocial gadgets across its product portfolio to integrate and share information between products. JIRA Studio, Atlassian's hosted development suite, integrates with Google Apps, and several Atlassian products use Google Web Toolkit., companylogo: atlassian.png, companypod: Social Web, companytags: Social Web, GWT, Enterprise, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cssly2010-05-10T18:39:11.857ZRow: 10companyname: Aviary, companylocation: New York, New York, companydesc: Aviary has created a suite of powerful, free web apps for designers and multimedia creators. Whether you are trying to edit a screen capture of your website, remix a music track or create a logo for your new business, Aviary has the right tool for you., companyurl: www.aviary.com, productdesc: Aviary has developed for many of Google's APIs including: the Android SDK for our Android app, integration with OpenID and OAuth to let people login to Aviary using their Google account, Picasa's API to import people's photos, Google's Web Toolkit to build our website and a plugin for Google Chrome. We also use Google Apps for email, documents and collaboration and Google Analytics for traffic monitoring., companylogo: aviary.png, companypod: Enterprise, companytags: Enterprise, Android, Google APIs, Social Web, GWT, Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cu76f2010-05-10T18:39:11.857ZRow: 11companyname: BeeBole, companylocation: Wallonia, Belgium, companydesc: BeeBole builds web apps for small and medium businesses. The current core features are: contact manager, timesheet and reporting, but the main objective is to let anyone add modules, either from the community or their own. BeeBole is also the author of the popular open source JavaScript templates engine PURE., companyurl: www.beebole.com, productdesc: Google APIs fit very well with our integration design. BeeBole is using: Google Chart Tools, Google Maps, Google Calendar, Google Docs, Google Login Authentication., companylogo: beebole.png, companypod: Google APIs, companytags: Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ba0fz2010-05-10T18:39:11.857ZRow: 12companyname: Big Ladder, companylocation: Denver, Colorado, companydesc: Big Ladder is a software and consulting company offering services at the intersection of programming, engineering, and energy simulation. We specialize in developing open source tools for modeling energy usage in the built environment., companyurl: www.bigladdersoftware.com, productdesc: Big Ladder uses the Google SketchUp API to couple building energy simulation with SketchUp's intuitive 3-D drawing tools., companylogo: bigladder.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cvlqs2010-05-10T18:39:11.857ZRow: 13companyname: bit.ly, companylocation: New York, New York, companydesc: bit.ly shortens, shares, and tracks the social propagation of links on the Internet., companyurl: www.bit.ly, productdesc: We make use of Google code, perftools, Closure, Google Apps, the Google Safebrowsing, and other Google APIs in various roles for development and management of our product., companylogo: bitly.png, companypod: Chrome, companytags: Chrome, Enterprise, Google APIs, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cx0b92010-05-10T18:39:11.857ZRow: 14companyname: Black Tonic, companylocation: Portland, Oregon, companydesc: Present. Demo. Share. Whatever you do, be on the same page at the same time with Black Tonic - the only web based tool providing real-time presentation delivery and control without using plugins or flash. Changes can be made on the fly, delivery is non-linear, and content can be added while the conversation is taking place., companyurl: www.blacktonic.com, productdesc: Black Tonic uses Google Web Toolkit (GWT) to serve the client side interface of our web-page html synchronization solution, FLOW. , companylogo: blacktonic.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/d9ney2010-05-10T18:39:11.857ZRow: 15companyname: Box.net, companylocation: Palo Alto, California, companydesc: Box.net is a Cloud Content Management provider that makes it simple for businesses to access, manage, and share all of their content online. , companyurl: www.box.net, productdesc: Box.net’s Cloud Content Management system adds workflow and content-based administrative oversight to Google Apps. This integration provides access to Gmail, Google Calendar, Google Sites, and Google Docs within Box.net, and likewise makes Box.net's service accessible through Google’s universal navigation links and Google Gadgets., companylogo: boxnet.png, companypod: Google APIs, companytags: Google APIs, Enterprise, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/db1zf2010-05-10T18:39:11.857ZRow: 16companyname: BTBuckets, companylocation: São Paulo, SP, Brazil, companydesc: BTBuckets is a free profiling and behavioral targeting tool that aims to provide a complete solution for websites to deliver more relevant content, products and advertising to its users in an easy and low cost way., companyurl: www.btbuckets.com, productdesc: We use App Engine as our segmentation and targeting engine., companylogo: btbuckets.png, companypod: App Engine, companytags: App Engine, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dcgjs2010-05-10T18:39:11.857ZRow: 17companyname: Bump, companylocation: Mountain View, California, companydesc: Bump Technologies is building a mobile technology platform and apps to allow phones to connect and interact by simply bumping them together. Share photos, contacts, and even apps with just a bump of your phones., companyurl: www.bu.mp
+, productdesc: Android features used: Location services, Accelerometer, and Custom Intent to share via Bump., companylogo: bump.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ddv492010-05-10T18:39:11.857ZRow: 18companyname: BusyCal, companylocation: Seattle, Washington, companydesc: BusyCal is a desktop calendar for Mac OS X that provides seamless calendar sharing and enhanced productivity tools for workgroups and individuals. BusyCal syncs with Google Calendar offering online access to your calendar on any platform and the ability to share calendars with other Google Calendar users., companyurl: www.busycal.com, productdesc: BusyCal uses the Google Calendar Data API for performing two-way synchronization with Google Calendar., companylogo: busycal.png, companypod: Google APIs, companytags: Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/d415a2010-05-10T18:39:11.857ZRow: 19companyname: Caseish, companylocation: Copenhagen, Denmark, companydesc: Caseish helps organize your Waves to be more organized and helps you control the flow of every case that you’re managing. Caseish also collects metrics on your processes and visualizes your pain points., companyurl: www.caseish.com, productdesc: We are using Google Wave Robots and API to integrate with Wave, AppEngine to control the robot and Google Visualization API to display graphs., companylogo: caseish.png, companypod: Wave, companytags: Wave, Google APIs, App Engine, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/d5fpr2010-05-10T18:39:11.857ZRow: 20companyname: CELLULAR GmbH, companylocation: Hamburg, Germany, companydesc: CELLULAR is a leading international, full-service agencies for mobile solutions and services. We support customers with conceptualization and programming of mobile portals and applications for cell phones, smart phones and other web compatible devices. We develop solutions for market-leading mobile operators, brands, agencies and mass media., companyurl: www.cellular.de, productdesc: Android features used: Animations, lists, customized ListViews, customized toggle-buttons, customized tabs, customized video player, customized menus, exception reporting, LayoutInflater, SaxParser, ContentProvider, SQLite, and JSON., companylogo: cellular.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/d6ua42010-05-10T18:39:11.857ZRow: 21companyname: Chromed Bird, companylocation: Fortaleza, CE, Brazil, companydesc: I came from a web and mobile applications background and started to dive into Chrome extensions since early dev channel releases. Right now I'm working on maintaining and expanding Chromed Bird extension and trying to contribute further with Chromium community., companyurl: www.chromedbird.com, productdesc: Chromed Bird uses HTML5 and Chromium extensions API to build a powerful yet simple Twitter client., companylogo: chromedbird.png, companypod: Chrome, companytags: Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/d88ul2010-05-10T18:39:11.857ZRow: 22companyname: Cisco, companylocation: San Jose, California, companydesc: Cisco Systems, Inc. designs, manufactures, and sells Internet Protocol (IP)-based networking solutions, including Routers, Switches, IP Communications Equipment, Unified I/O Compute Systems and network management software and other products to the communications and IT industry worldwide. The company also offers optical networking products, cable access, and service provider VoIP services., companyurl: www.cisco.com, productdesc: Cisco built its north bound API in OpenSocial complaint way. It uses Google Map API and service for people location, Google Guice for backend dependency injection., and Android SDK for building the Android client (in process)., companylogo: cisco.png, companypod: Social Web, companytags: Social Web, Geo, Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dkvya2010-05-10T18:39:11.857ZRow: 23companyname: Civio, companylocation: Palo Alto, California, companydesc: Civio provides the platform to orchestrate interactions that target and engage people in meaningful ways. Customers use Civio to engage people in actions online and offline and receive the benefit of increased participation, affinity and contribution levels. , companyurl: www.civio.com, productdesc: We've built our platform on top of Google App Engine for our hosting and web-based infrastructure. We’re also using Google Maps and the Geocoding Service to provide geographical overlays to our users., companylogo: civio.png, companypod: App Engine, companytags: App Engine, Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dmair2010-05-10T18:39:11.857ZRow: 24companyname: Clicker, companylocation: Los Angeles, California, companydesc: Clicker is the complete programming guide for the new era of Internet television. Clicker catalogues all TV shows, movies and Web originals available on the Web in one place, delivering a seamless, organized experience so you can easily discover what’s available, where you can watch it, and get suggestions for other content you might find worth watching. , companyurl: www.clicker.com, productdesc: We use Chrome for HTML5 support and Javascript/UI debugging; the YouTube API to programmatically control YouTube embeds; Page speed to analyze our UI speed; and the Notifications API to serve up notifications when new shows are online., companylogo: clicker.png, companypod: Chrome, companytags: Chrome, Google APIs, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dnp342010-05-10T18:39:11.857ZRow: 25companyname: Cloud Sherpas, companylocation: Atlanta, Georgia, companydesc: Cloud Sherpas develops software and provides professional services to help enterprises leverage Google Apps and adopt cloud computing. As one of the early Google Apps partners, Cloud Sherpas has migrated tens of thousands of users to Google Apps and developed SherpaTools, an valuable toolset that enhances the functionality and ease-of-use of Google Apps for both IT administrators and end users., companyurl: www.cloudsherpas.com, productdesc: SherpaTools for Google Apps is built entirely on Google App Engine and makes heavy use of Google Apps APIs and Google Web Toolkit. SherpaTools also relies on reusable open standards such as OpenID and OAuth and leverages Google Apps as its OpenID identity provider., companylogo: cloudsherpas.png, companypod: Enterprise, companytags: Enterprise, App Engine, Google APIs, GWT, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dp3nl2010-05-10T18:39:11.857ZRow: 26companyname: Columbus Internet GmbH, companylocation: Berlin, Germany, companydesc: Columbus Internet provides two acclaimed gadgets for Google Wave: Travel WithMe and Hostel WithMe. Travel WithMe allows groups of people to plan trips together in real time, presenting content from Lonely Planet and hotel information from Expedia in a map and calendar-based GUI, while Hostel WithMe lets groups of users choose and book the perfect budget accomodation using a simple but powerful GUI., companyurl: www.rucksack.com, productdesc: The WithMe gadget family harnesses the collaborative power of Google Wave to allow users to work and play together and make informed collective decisions. Travel WithMe makes innovative use of Google Maps, Street View and Google Local and allows the planned trip to be exported to Google Calendar., companylogo: columbusinternet.png, companypod: Wave, companytags: Wave, Geo, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/df9om2010-05-10T18:39:11.857ZRow: 27companyname: Com2Us, companylocation: Seoul, South Korea, companydesc: Com2uS is a fun and creative mobile game company with various award-winning games in Korea and abroad. Founded in 1998, Com2us continues to bring innovative gameplay beyond mobile to multi-platform as a worldwide leader in mobile gaming with overseas offices in US, Japan and China., companyurl: www.com2us.com, productdesc: Android features used: multi-touch, gyro sensor, vibration, network connectivity using 3G or WiFi, and Developed by NDK(with C and C++ game code)., companylogo: com2us.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dgo932010-05-10T18:39:11.857ZRow: 28companyname: ConnectWise, companylocation: Tampa, Florida, companydesc: ConnectWise is a leading web-based professional service automation software designed exclusively for IT solutions providers by an IT solution provider. More than 37,000 IT professionals rely on us to more efficiently integrate key business operations, lower costs, increase revenue and improve customer satisfaction., companyurl: www.connectwise.com, productdesc: We’re using Google Web Toolkit to help build our community platform and working to embed Google Apps into our management software., companylogo: connectwise.png, companypod: Enterprise, companytags: Enterprise, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/di2tg2010-05-10T18:39:11.857ZRow: 29companyname: Cordys, companylocation: Palo Alto, California, companydesc: Cordys is a global provider of software for business process innovation and offers Cordys Process Factory, a simple, effective mechanism for anyone in the world to create and deploy Mashup Applications (MashApps®). These web-based, process-centric applications are ideal for SME’s and departmental deployments to create applications and business processes on the fly, at low cost and without IT involvement., companyurl: www.cordys.com, productdesc: Cordys Process Factory has out-of-the box integration with Google Apps allowing users of Google Apps to easily enrich their offering., companylogo: cordys.jpg, companypod: Enterprise, companytags: Enterprise, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/djhdx2010-05-10T18:39:11.857ZRow: 30companyname: Coupons.com, companylocation: Mountain View, California, companydesc: Coupons.com Inc. is a leader in digital coupons, including mobile, online printable, and save-to-loyalty card promotions., companyurl: www.coupons.com, productdesc: Grocery iQ for Android makes full use of the Android platform including native UI widgets, Voice Recognition to add new items to your list, dynamically resizing and re-scaling the UI and images based on hardware model, etc. In addition, Grocery iQ supports a unique intent that allows other applications to easily add items to a user's grocery list., companylogo: coupons.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dw4je2010-05-10T18:39:11.857ZRow: 31companyname: CS Odessa, companylocation: Odessa, Ukraine, companydesc: Founded in 1993, CS Odessa supplies productivity tools and graphics technologies to professional and corporate users. With headquarters in Odessa, Ukraine and an office in California, USA, CS Odessa sells products in over 120 countries., companyurl: www.csodessa.com, productdesc: We are building a mind mapping gadget for Google Wave using Google Wave's Extensions API and OpenSocial Gadgets API together to help people view real time the structure of their collaboration., companylogo: csodessa.png, companypod: Wave, companytags: Wave, Social Web, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dxj3v2010-05-10T18:39:11.857ZRow: 32companyname: DataViz, companylocation: Milford, Connecticut, companydesc: DataViz is an industry leader in developing and marketing Office compatibility solutions across a variety of platforms including Android, BlackBerry, Java®, Linux, Palm OS®, Symbian OS™, Windows Mobile®, Windows® and Macintosh®., companyurl: www.dataviz.com, productdesc: Key Android-related features include: Open files and attachments via intents; run in the background; multi-touch and pinch-to-zoom; rotate page; and live folders., companylogo: dataviz.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/e0c8p2010-05-10T18:39:11.857ZRow: 33companyname: Digg, companylocation: San Francisco, California, companydesc: Digg is focused on building the best internet platform for news. Digg is building a platform to enable social curation of the world's content and the conversation around it. It's Digg's belief that people across the world have a constant thirst for finding relevant news in a timely manner., companyurl: www.digg.com, productdesc: Digg develops for the Android SDK for our Android client and the Chrome extension platform., companylogo: digg.png, companypod: Chrome, companytags: Chrome, Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/drwu72010-05-10T18:39:11.857ZRow: 34companyname: Dito, companylocation: Manassas, Virginia, companydesc: Dito provides Google Apps deployment, development, training, and supporting services to businesses and organizations of all sizes. Our mission is to provide a complete solution for our clients that are seeking to maximize the potential of Google technologies., companyurl: www.ditoweb.com, productdesc: Dito Directory for Google Apps provides external contact management for organizations and is built on Google App Engine with Google Web Toolkit. Dito Directory integrates seamlessly with Google Apps using OpenID for authentication and multiple Google Apps APIs., companylogo: dito.png, companypod: Enterprise, companytags: Enterprise, App Engine, GWT, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dtbek2010-05-10T18:39:11.857ZRow: 35companyname: DotSpots, companylocation: Los Angeles, California, companydesc: DotSpots enables anyone to create dots or 'distributed objects of thought.' Dots are bigger than tweets, smaller than blogs, allow wiki-like collaboration and can be easily distributed as global annotations on text in webpages or via social media. By allowing dots on their pages, publishers enable the wisdom of crowds to interactively enrich their content., companyurl: www.dotspots.com, productdesc: DotSpots uses Google Web Toolkit to build their website, and Firefox and Chrome extensions. They're also using the YouTube and Picasa APIs to integrate video and photos into user-generated content., companylogo: dotspots.png, companypod: GWT, companytags: GWT, Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dupz12010-05-10T18:39:11.857ZRow: 36companyname: Drawloop Technologies, companylocation: Irvine, California, companydesc: Drawloop Technologies delivers powerful on-demand document creation services to your organization. We provide a simple business process for users to merge and automate dynamic document creation using any combination of Google Docs as well as other documents like Microsoft Word, PowerPoint and Excel., companyurl: www.drawloop.com, productdesc: Drawloop uses many Google technologies. For automating document creation, we utilize the Google Docs API as well as Google's two-legged OAuth. In addition, we use Google Gadgets within Gmail and Google Sites to deploy Dynamic Document Packages (DDPs)., companylogo: drawloop.png, companypod: Enterprise, companytags: Enterprise, Google APIs, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/e7d2q2010-05-10T18:39:11.857ZRow: 37companyname: EA, companylocation: Redwood City, California, companydesc: EA Mobile is a division of Electronic Arts Inc. (EA), a leading publisher and developer of mobile games. Award-winning hits from EA Mobile include Tetris, Bejeweled, The Sims titles, and Spore Origins. With its strong ties to EA's overall franchises, EA Mobile continues to develop and produce innovative products across all major mobile platforms, including Android.
+, companyurl: www.ea.com, productdesc: Rock Band is now on Android. Rock out on guitar, bass, drums or vocals to MP3-quality songs. Fully optimized for Android 2.0 devices.
+, companylogo: ea.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/e8rn72010-05-10T18:39:11.857ZRow: 38companyname: eBay, companylocation: San Jose, California, companydesc: Founded in 1995, eBay Inc. connects hundreds of millions of people around the world every day by providing the Internet platforms of choice for global commerce, payments and communications. Since its inception, eBay Inc. has expanded to include some of the strongest brands in the world, including eBay, PayPal, Skype, StubHub, Shopping.com, and others., companyurl: www.ebay.com, productdesc: eBay uses OpenSocial and Gadget standard to power its Selling Manager application platform marketplace. We also developed a prototype Gadget using Google Friend Connect that distributes eBay inventory across the GFC network of publishers., companylogo: ebay.png, companypod: Social Web, companytags: Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ea67k2010-05-10T18:39:11.857ZRow: 39companyname: EchoSign, companylocation: Palo Alto, California, companydesc: EchoSign is a leading electronic signature service with over 10,000 customers and over 1 million users and is used by medium-sized businesses, enterprises, and Fortune 500 companies alike. EchoSign natively integrates with your Google Apps account, including Google docs, spreadsheets, etc. for instant on-line review, approval, and eSignatures., companyurl: www.echosign.com, productdesc: EchoSign integrates Google as an OpenID provider for easy login across both standard Google accounts as well as for Google Apps for businesses and accesses Google Docs via OAuth as a content source allowing customers to easily send any Google Doc for e-Signature., companylogo: echosign.png, companypod: Enterprise, companytags: Enterprise, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ebks12010-05-10T18:39:11.857ZRow: 40companyname: Eclipse, companylocation: Ottawa, Ontario Canada, companydesc: Eclipse is an open source community, whose projects are focused on building an open development platform comprised of extensible frameworks, tools and runtimes for building, deploying and managing software across the lifecycle. A large, vibrant ecosystem of major technology vendors, innovative start-ups, universities and research institutions and individuals extend, complement and support the Eclipse Platform., companyurl: www.eclipse.org, productdesc: The Eclipse community is introducing the next generation of the Eclipse platform, code name e4, that includes the ability to integrate OpenSocial gadgets into Eclipse based applications., companylogo: eclipse.png, companypod: Social Web, companytags: Social Web, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/e1qt22010-05-10T18:39:11.857ZRow: 41companyname: Entrinsik, companylocation: Raleigh, North Carolina, companydesc: Entrinsik's Informer Web Reporting software delivers true real-time access to operational data simply and easily using a built-in Web-based query engine. Informer leverages a metadata model that creates consistency among data descriptions and structures to provide a single point of access to disparate data sources for ad hoc report customization and analysis., companyurl: www.entrinsik.com, productdesc: Google Web Toolkit is the engine behind Informer’s drag-and-drop analytics, one click charting, and WYSIWYG criteria builder., companylogo: entrinsik.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/e35dj2010-05-10T18:39:11.857ZRow: 42companyname: Epocrates, companylocation: San Mateo, California, companydesc: Epocrates, Inc. develops clinical information and decision support tools that enable healthcare professionals to find answers more quickly and confidently at the point of care. More than 950,000 healthcare professionals worldwide and 40% of U.S. physicians use Epocrates’ innovative mobile and web-based products to help them reduce medical errors, improve patient care and increase productivity.
+, companyurl: www.epocrates.com, productdesc: You can search for a drug without launching the Epocrates application using the Quick Search box on Android devices., companylogo: epocrates.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/e4jxw2010-05-10T18:39:11.857ZRow: 43companyname: ESPN EMEA, companylocation: London, United Kingdom, companydesc: ESPN EMEA is the European digital arm of ESPN international, building and running Cricinfo.com, Soccernet.com, Scrum.com, ESPNF1.com and espn.co.uk, as well as the UK’s ESPN TV channel and other digital properties., companyurl: international.espn.com, productdesc: We are currently using google's Chrome Extension support to engage with cricket fans from all over the world. We use Background Pages and Notification Badges to update our users the moment cricketing news breaks on our website. We’re doing this with Scrum.com and plan on rolling it out for our other properties too. A project is underway to localize our scorecards to Hindi. Google's Transliteration APIs are playing a key role in getting our comprehensive database of over 50,000 players localized., companylogo: espn.png, companypod: Chrome, companytags: Chrome, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/e5yid2010-05-10T18:39:11.857ZRow: 44companyname: Exceptional Software, companylocation: Linthicum, Maryland, companydesc: Exceptional Software Strategies, Inc. is a leading provider of information technology solutions and services to the U.S. Government, educational institutions, and private industry. Exceptional Software can meet all your information technology needs – from customized software products and solutions such as Geographic Information Systems, enterprise-level portals, and information management, to services such as network administration, security design, project management, and interactive training., companyurl: www.exceptionalsoftware.com, productdesc: EarthSpector was developed using the Google Web Toolkit (GWT), and brings GIS analysis capabilities found in heavier desktop products to the flexible platform of the Google Earth Plug-in. EarthSpector is a product of Exceptional Software Strategies, Inc., companylogo: exceptionalsoftware.png, companypod: Geo, companytags: Geo, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/emtbd2010-05-10T18:39:11.857ZRow: 45companyname: eXo Platform, companylocation: San Francisco, California, companydesc: eXo provides a new way to extend, build and deploy applications, complete with rich social computing capabilities, by reusing existing services and components. An integrated framework and user experience platform in one, the eXo Platform extends and enables rapid development of Java applications with modern APIs and tooling such as OpenSocial, gadgets, REST, Groovy, and mashups., companyurl: www.exoplatform.com, productdesc: Google Web Toolkit is an important part of the eXo Platform, as a part of our IDE and CMIS implementation. We also use the Google Gadget API in GateIn, the open source portal framework that serves as the foundation of eXo Platform. Finally, we support OpenSocial in eXo Enterprise Social, an extended service for eXo Platform., companylogo: exoplatform.png, companypod: Social Web, companytags: Social Web, GWT, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/eczce2010-05-10T18:39:11.857ZRow: 46companyname: ExtensionFM, companylocation: New York, New York, companydesc: ExtensionFM turns the entire web into your personal music library. Discovering new music has never been easier: simply browse to your music site of choice and ExtensionFM will automatically add any music it finds into your library and keep you up to date with any new music the site publishes., companyurl: www.extension.fm, productdesc: ExtensionFM is a Google Chrome extension that makes heavy use of HTML5., companylogo: extensionfm.png, companypod: Chrome, companytags: Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/eedwv2010-05-10T18:39:11.857ZRow: 47companyname: Eye-Fi, companylocation: Mountain View, California, companydesc: Founded in 2005, Eye-Fi is dedicated to building products and services that help consumers navigate, nurture and share their visual memories. Eye-Fi’s patent-pending technology works with Wi-Fi networks to automatically send photos from a digital camera to online, in-home and retail destinations., companyurl: www.eye.fi, productdesc: Eye-Fi Cards upload images and videos to Picasa Web Albums and to YouTube. We use the API's to enable uploads from our servers, to Google's servers., companylogo: eyefi.png, companypod: Google APIs, companytags: Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/efsh82010-05-10T18:39:11.857ZRow: 48companyname: EyeCon, companylocation: Palo Alto, California, companydesc: Eyecon allows you to search through all your media simultaneously and play it to any device, without cluttering the screen, without getting up, and with zero hassle., companyurl: www.eyecontec.com, productdesc: The Eyecon application was developed using the Android SDK, companylogo: eyecon.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/eh71p2010-05-10T18:39:11.857ZRow: 49companyname: EZasset, companylocation: Columbus, Ohio, companydesc: EZasset is an asset management application for home and business. EZasset is a simple and safe way to record and manage data on all your assets. EZasset provides all of the tools needed to manage assets efficiently online., companyurl: www.ezasset.com, productdesc: EZasset utilizes several Google APIs such as Google Calendar for maintenance tracking for assets and Google Docs for easy importing and exporting of information for assets. The Google Maps API is integrated to give visual representation of a physical location where assets are stored. We are running both our home and business applications on App Engine., companylogo: ezasset.png, companypod: Enterprise, companytags: Enterprise, Google APIs, Geo, App Engine, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/etu5e2010-05-10T18:39:11.857ZRow: 50companyname: Facebook, companylocation: Palo Alto, California, companydesc: Facebook is all about connections—whether it’s to a friend, or a school, or a community of people. Connections help Facebook create a relevant and meaningful experience for users, and deliver an engaging interaction for those who want to learn more about each other, companyurl: www.facebook.com, productdesc: Facebook for Android lets other Android applications leverage the content of Facebook by allowing direct linking to relevant contact. Facebook also offers Connect to Android applications., companylogo: facebook.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ev8pv2010-05-10T18:39:11.857ZRow: 51companyname: Fandango, companylocation: Los Angeles, California, companydesc: Fandango, a leading moviegoer destination, sells tickets to more than 16,000 screens. Fandango entertains and informs consumers with exclusive film clips, trailers, celebrity interviews, fan reviews and news, while offering them the ability to quickly select a film and conveniently buy tickets in advance., companyurl: www.fandango.com, productdesc: Android technologies used: widgets, intents, voice search, Quick Search integration and more., companylogo: fandango.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ewna82010-05-10T18:39:11.857ZRow: 52companyname: Farmigo, companylocation: San Rafael, California, companydesc: Our mission is to make small farming more sustainable so that many more farms can join the growing trend of farms selling direct. Farmigo CSA System is a web-based service created for small farms to promote, collaborate, sell and explain their food items online. Each farm on the Farmigo network receives farm management software for tracking harvest, processing orders, managing logistics, and servicing customers., companyurl: www.farmigo.com, productdesc: Farmigo has built a multi-tenant enterprise solution on Google App Engine enabling small organic farms to network with one another while directly selling to their local communities., companylogo: farmigo.png, companypod: App Engine, companytags: App Engine, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ey1up2010-05-10T18:39:11.857ZRow: 53companyname: FortiusOne, companylocation: Arlington, Virginia, companydesc: FortiusOne makes it possible to visualize and analyze massive amounts of data, make decisions and solve problems without extensive training using traditional mapping tools by delivering easy-to-use, browser-based solutions. FortiusOne's GeoIQ is a complete data management, visualization, and analysis platform that enables intuitive exploration, sharing and collaboration around geospatial information., companyurl: www.fortiusone.com, productdesc: FortiusOne has integrated with both Google Earth Fusion and the Google Maps API in order to visualize geospatial analysis with GeoIQ Enterprise, on field laptop using GeoIQ Portable, or at the community GeoCommons.com., companylogo: fortiusone.png, companypod: Geo, companytags: Geo, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/eo7vq2010-05-10T18:39:11.857ZRow: 54companyname: Gameloft, companylocation: San Francisco, California, companydesc: Gameloft is an international publisher and developer of downloadable video games. Gameloft is present on all the continents with its own production studios, employing over 3,500 developers, and distributes its games in over 100 countries., companyurl: www.gameloft.com, productdesc: Asphalt is rocketing onto Android! Get behind the wheel of the best vehicles from prestigious manufacturers (Lamborghini, Kawasaki, Ford, etc) and make a name for yourself in amazing 3D surroundings.
+, companylogo: gameloft.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/epmg72010-05-10T18:39:11.857ZRow: 55companyname: GetGlue, companylocation: New York, New York, companydesc: GetGlue is an innovative social recommendation network for movies, books, and music. The GetGlue website provides a recommendation stream based on personal tastes, what friends like, and what's most popular right now. The GetGlue browser extension brings filtered friend reviews, personal recommendations, and contextual content to popular sites around the web, such as Wikipedia, Amazon, IMDB, and hundreds more., companyurl: www.getglue.com, productdesc: GetGlue makes use of the following Google APIs: Search to retrieve site and page specific information, Contacts to surface the existing social graph, and Books, YouTube, and Images to retrieve rich content. AdaptiveBlue also uses Google Web Toolkit to build their website, and Chrome and Firefox extensions. , companylogo: getglue.png, companypod: Chrome, companytags: Chrome, Google APIs, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/er10k2010-05-10T18:39:11.857ZRow: 56companyname: Glu, companylocation: San Mateo, California, companydesc: Glu is a global publisher of mobile games for feature phones and smartphones. Its portfolio of top-rated games includes original titles: Beat It!, Bonsai Blast, Brain Genius, Glyder, Stranded, and Super K.O. Boxing! Consumers can find high-quality, fresh entertainment created exclusively for their mobile phones wherever they see the 'g' character logo., companyurl: www.glu.com, productdesc: Super KO Boxing 2 takes full advantage of the Android Native Development Kit (NDK) to bring the highest quality visuals and experience to Android Market. This title features touch screen controls and uses the SD Card to store the 86MB resource file required to run., companylogo: glu.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/esfl12010-05-10T18:39:11.857ZRow: 57companyname: Google Checkout, companylocation: Mountain View, California, companydesc: Google Checkout is an alternative payment flow that allows consumers to use a single account to purchase from multiple sites across the web., companyurl: code.google.com/apis/checkout, companylogo: google.png, companypod: Google APIs, companytags: Google APIs, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/a6oij2010-05-10T18:39:11.857ZRow: 58companyname: Google Code Labs, companylocation: Mountain View, California, companydesc: Google Code Labs is home to developer products that are still in their formative stages., companyurl: code.google.com/labs, companylogo: google.png, companypod: App Engine, companytags: App Engine, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bbf0c2010-05-10T18:39:11.857ZRow: 59companyname: Google Corporate Engineering, companylocation: Mountain View, California, companydesc: Much like Google's mission, Corporate Engineering is about solving real problems that affect a large number of Googlers. Our goal is to provide the internal technology, infrastructure and support that allow Googlers to be the happiest and most productive workforce on the planet. , companylogo: google.png, companypod: App Engine, companytags: App Engine, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/a832w2010-05-10T18:39:11.857ZRow: 60companyname: Google Customer Solutions Engineering, companylocation: Mountain View, California, companydesc: The Customer Solutions Engineering team works with select Google customers to promote consumer product and API adoption. We'll be demoing real-world examples of websites enhanced by Google's APIs., companylogo: google.png, companypod: Google APIs, companytags: Google APIs, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/9znoe2010-05-10T18:39:11.857ZRow: 61companyname: Google Earth, companylocation: Mountain View, California, companydesc: Google Earth takes advantage of the Android NDK and the OpenGL ES1.1 module. The main application is written in Java, and communicates to the native engine using JNI. Several open source modules in the NDK are used, including libcurl, freeimage, libjpeg, and others. , companyurl: www.google.com/mobile/earth, companylogo: google.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/a128v2010-05-10T18:39:11.857ZRow: 62companyname: Google Goggles, companylocation: Mountain View, California, companydesc: Use pictures to search the web. Photograph objects or places to learn more about them. Photograph text to translate it to another language., companyurl: www.google.com/mobile/goggles, companylogo: google.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/a2gt82010-05-10T18:39:11.857ZRow: 63companyname: Google Native Client, companylocation: Mountain View, California, companydesc: Native Client is an open-source technology for running native code in web applications, with the goal of maintaining the browser neutrality, portability, and safety that people expect from web apps., companyurl: code.google.com/p/nativeclient, companylogo: google.png, companypod: Chrome, companytags: Chrome, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/a3vdp2010-05-10T18:39:11.857ZRow: 64companyname: Google SkyMap, companylocation: Mountain View, California, companydesc: Google Sky Map turns your Android-powered mobile phone into a window on the night sky. It will identify objects that appear in the sky and allow users to search for various objects. By using your Android phone's orientation sensors, we can show you a star map for your location. You can search for a variety of objects and Google Sky Map direct you to the object's location in the sky., companyurl: www.google.com/sky/skymap, companylogo: google.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/agihe2010-05-10T18:39:11.857ZRow: 65companyname: Google Wave, companylocation: Mountain View, California, companydesc: Google Wave is an online tool for real-time communication and collaboration, built with GWT. A wave can be both a conversation and a document where people can discuss and work together using richly formatted text, photos, videos, maps, and more, including developer-built extensions (robots and gadgets) with the Google Wave APIs., companyurl: code.google.com/apis/wave, companylogo: google.png, companypod: GWT, companytags: GWT, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ahx1v2010-05-10T18:39:11.857ZRow: 66companyname: Handmark, companylocation: Kansas City, Missouri, companydesc: Handmark is a leading developer and distributor of mobile applications, specializing in mobile solutions for publishers and media companies. Trusted by many of the world’s leading media brands, Handmark delivers a full-service mobile publishing solution including development, distribution, monetization and on-going innovation.
+, companyurl: www.handmark.com, productdesc: Goal.com 3.0, developed on the Handmark Mobile Publishing Platform, provides users with live scores, tables, fixtures, and editorial content for football matches around the world. The Mobile Publishing Platform uses the embedded Media Player to present high quality video content, leverages linkify to tightly integrate content into Android’s platform services, and brings timely content to the home screen through Android App Widgets., companylogo: handmark.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ajbm82010-05-10T18:39:11.857ZRow: 67companyname: Hasbro, companylocation: Pawtucket, Rhode Island, companydesc: Hasbro, Inc. is a worldwide leader in children's and family leisure time products and services with a rich portfolio of brands and entertainment properties that provides some of the highest quality and most recognizable play and recreational experiences in the world., companyurl: www.hasbro.com/monopoly/en_US/discover/news/Monopoly-City-Streets-Has-Ended.cfm, productdesc: MONOPOLY City Streets used Google Maps API, as a way to excite consumers and demonstrate the new game play features to launch the MONOPOLY City board game. To promote the “try before you buy” experience we also worked with Google Sketch Up and ran a competition to design new buildings of which the top 3 were featured in MONOPOLY City Streets., companylogo: hasbro.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/akq6p2010-05-10T18:39:11.857ZRow: 68companyname: HP, companylocation: Palo Alto, California, companydesc: HP creates new possibilities for technology to have a meaningful impact on people, businesses and society. HP brings together a portfolio that spans printing, personal computing, software, services and IT infrastructure to solve customer problems., companyurl: www.hp.com, productdesc: Used Google Maps API to create an HP Print App that allows users to print a map and point to point directions directly from HP printers., companylogo: hp.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/aaw7q2010-05-10T18:39:11.857ZRow: 69companyname: HTC, companylocation: Taoyuan, Taiwan, companydesc: HTC Corporation (HTC) is one of the fastest growing companies in the mobile phone industry and continues to pioneer industry-leading mobile experiences through design, usability and innovation that are sparked by how the mobile phone can improve how people live and communicate. The company is listed on the Taiwan Stock Exchange under ticker 2498., companyurl: developer.htc.com, productdesc: As a founding member of the OHA and the first to ship Android on a phone, HTC has continued to introduce new Android phones around the world including the recently unveiled HTC Evo 4G, Droid Incredible by HTC, HTC Desire and HTC Legend., companylogo: htc.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/acas72010-05-10T18:39:11.857ZRow: 70companyname: Hydro4GE Inc., companylocation: Scranton, Pennsylvania, companydesc: Hydro4GE (pronounced hy-dro-forge) is a Platform-as-a-Service (PaaS) that enables developers to rapidly develop complex and adaptable software systems using an online, fourth generation development environment. Hydro4GE shortens the traditional software development life cycle and accelerates time to client-ready solutions by eliminating the most time-consuming and expensive steps for a developer: low-level coding and unit testing., companyurl: www.hydro4ge.com, productdesc: The Hydro4GE user interface is built entirely using the Google Web Toolkit (GWT), relying on a single external dependency, RaphaelJS, for interactive SVG diagrams. GWT allowed us to create a highly-interactive, rich user interface (UI) with emerging features such as database schema visualization via interactive, scalable graphics., companylogo: hydro4ge.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/adpck2010-05-10T18:39:11.857ZRow: 71companyname: IBM, companylocation: Armonk, New York, companydesc: For nearly 100 years, IBM has served clients in more than 170 countries around the world. Today, IBM’s portfolio is built around networked, modularized and embedded technologies, such as service-oriented architecture (SOA), business intelligence and analytics. IBM is working with our clients to make many aspects of our world "smarter": roadways, health care, power grids, food production for a better way of living. , companyurl: www.ibm.com, productdesc: IBM is partnering with Google and others in the industry to develop emerging open standards (i.e. OpenSocial) and open source (i.e. Apache Shindig) for the advancement of social networking technologies., companylogo: ibm.png, companypod: Social Web, companytags: Social Web, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/af3x12010-05-10T18:39:11.857ZRow: 72companyname: Instantiations, Inc., companylocation: Portland, Oregon, companydesc: Instantiations develops innovative software products for the Eclipse Java open source platform with a wide range of tools for Eclipse, IBM Rational,® JBuilder® and MyEclipse. Instantiations’ AJAX offering, GWT Designer,™ is the leading GUI building tool for GWT and is based on Instantiations’ award-winning WindowBuilder product, named Best Commercial Eclipse-Based Developer Tool of 2009 by the Eclipse Foundation., companyurl: www.instantiations.com, productdesc: GWT Designer helps developers rapidly create GUIs in Eclipse and GWT/Java before deploying them as AJAX applications – using wizards, drag-and-drop visual design, bi-directional code generation, internationalization, a multitude of layout managers, and support for popular add-on widget packages such as Ext GWT (GXT) and GWT-Ext., companylogo: instantiations.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/arr0q2010-05-10T18:39:11.857ZRow: 73companyname: Instituto Federal Electoral, companylocation: Mexico Distrito Federal, Mexico, companydesc: Instituto Federal Electoral (IFE) is the government agency in Mexico in charge of Elections., companyurl: www.ife.org.mx/portal/site/ifev2, productdesc: We are using Google Maps APIs to provide our website with the precise locations of voting sites so all of our citizens can locate the precise place where they can vote, and access data of the available candidates, also our voters roll registering offices location., companylogo: ife.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/at5l72010-05-10T18:39:11.857ZRow: 74companyname: InterContinental Hotels Group , companylocation: Atlanta, Georgia, companydesc: IHG is an international hotel company that welcomes over 180 million guests each year in nearly 4,400 hotels (that's over 640,000 rooms!) in 100 countries. We operate seven hotel brands – InterContinental, Crowne Plaza, Hotel Indigo, Holiday Inn, Holiday Inn Express, Staybridge Suites, Candlewood Suites and our Priority Club Loyalty Program., companyurl: www.ichotelsgroup.com, productdesc: We utilize GWT for our public facing websites and many of our internal applications. We use Google Maps on our public facing website and Wallboard application located in our hotel lobbies. InterContinetal.com will soon be launching a new Google maps interface that allows our guests to find nearby concierge suggested attractions, restaurants and services., companylogo: ihg.png, companypod: Geo, companytags: Geo, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/auk5k2010-05-10T18:39:11.857ZRow: 75companyname: JetBrains, companylocation: Prague, Czech Republic, companydesc: JetBrains is a software development company that's crazy-passionate about developer productivity. Want more intelligent development tools? Consider it done. How about more efficient development processes? We've got you covered. What about shorter development cycles? Uh huh, we do that too. Simply put, our tools automate your routine tasks and help you effectively solve the challenging ones., companyurl: www.jetbrains.com, productdesc: We use Google Web Toolkit for several internal systems, and we use Google AJAX APIs on our web sites. But most importantly, we build developer tools for GWT, Google App Engine and Android SDK that allow other developers to build great apps using all these technologies., companylogo: jetbrains.png, companypod: GWT, companytags: GWT, Google APIs, App Engine, Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/avyq12010-05-10T18:39:11.857ZRow: 76companyname: Jive Software, companylocation: Portland, Oregon, companydesc: Jive SBS takes all the great things people love about social networking software, collaboration software, and community software and makes those work for business. Jive is a leader in Social Business Software, with an extensive solution, large implementations, and expertise in delivering value., companyurl: www.jivesoftware.com, productdesc: Jive uses OpenSocial in our Jive SBS product to host OpenSocial and Google gadgets., companylogo: jive.png, companypod: Social Web, companytags: Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/am4r22010-05-10T18:39:11.857ZRow: 77companyname: Juice Analytics, companylocation: Reston, Virginia, companydesc: Juice crafts easy-to-use web applications that engage users with your data. Unlike static dashboards and time-consuming reports, our solutions help your data tell a story., companyurl: www.juiceanalytics.com, productdesc: We use Google Analytics and Website Optimizer to improve our website. Google Analytics API and AppEngine power our analytics visualizations., companylogo: juiceanalytics.png, companypod: Google APIs, companytags: Google APIs, App Engine, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/anjbj2010-05-10T18:39:11.857ZRow: 78companyname: Kaazing, companylocation: Mountain View, California, companydesc: Kaazing is a web infrastructure software company that enables companies to quickly deliver massively scalable real-time web applications and significantly simplify their back-end server infrastructure. Its flagship product, Kaazing WebSockets Gateway, can connect hundreds of thousands of concurrent users directly to the real-time data flowing through an organization., companyurl: www.kaazing.com, productdesc: By using the HTML5 WebSockets standard and avoiding the overhead and latency inherent in HTTP, Kaazing WebSockets Gateway extends the use of any TCP-based messaging format to any browser, delivering ultra-high performance bi-directional communication over the Web without the use of browser plug-ins., companylogo: kaazing.png, companypod: Chrome, companytags: Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/aoxvw2010-05-10T18:39:11.857ZRow: 79companyname: Kashoo, companylocation: Vancouver, BC, Canada, companydesc: Kashoo is online accounting for small business. Designed with simplicity and ease-of-use in mind, Kashoo allows you to take control of your finances and record keeping without having to install and learn complicated software., companyurl: www.kashoo.com, productdesc: We're using Google Web Toolkit to build our online software., companylogo: kashoo.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/aqcgd2010-05-10T18:39:11.857ZRow: 80companyname: Korem, companylocation: Quebec City, QC, Canada, companydesc: Korem is a North American geospatial system integrator that has been at the forefront of geospatial technology, business Intelligence and location-oriented demographic analysis since 1993. We create and implement integrated geospatial solutions based on your business needs, and we enable you to better utilize your data and improve your business decision-making capabilities., companyurl: www.korem.com, productdesc: Korem develops and implements geospatial solutions using the Google Maps API and Google Earth Enterprise., companylogo: korem.png, companypod: Geo, companytags: Geo, Enterprise, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/b2zk22010-05-10T18:39:11.857ZRow: 81companyname: Kuali Foundation, Inc., companylocation: Bloomington, Indiana, companydesc: Kuali Student is a next-generation student system currently under development. We will offer a flexible, scalable, cost-effective system that can be configured to meet the business requirements of any higher education institution. Our student-centric design will help students, faculty, and institutions deal with the dynamic learning environment., companyurl: student.kuali.org, productdesc: Kuali Student is using GWT to building its rich user interface which will be used by faculty, students and staff., companylogo: kuali.png, companypod: GWT, companytags: GWT, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/b4e4j2010-05-10T18:39:11.857ZRow: 82companyname: Laminar Research, companylocation: Columbia, South Carolina, companydesc: Laminar Research is the brains behind X-Plane, the most realistic flight simulator available for person computers. Started in 1995 by Austin Meyer, Laminar Research is well known to be the company behind the most physically accurate games and simulations available in the consumer market., companyurl: www.x-plane.com, productdesc: Android technologies used: OpenGL ES for drawing; Accelerometers and multi-touch for input; and sound streaming interface for sound, companylogo: laminarresearch.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/b5sow2010-05-10T18:39:11.857ZRow: 83companyname: LastPass, companylocation: Vienna, Virginia, companydesc: LastPass provides a free password manager that works across all major platforms and browsers including Google Chrome, Firefox, Safari and Internet Explorer on Windows, Mac and Linux. Multi-factor authentication solutions and mobile solutions on Android, iPhone, Blackberry, Symbian, webOS and Windows Mobile are also available., companyurl: www.lastpass.com, productdesc: We are using Chrome Extension APIs to build a native chrome password manager extension, and the AndroidSDK to build the LastPass for Android mobile app., companylogo: lastpass.png, companypod: Chrome, companytags: Chrome, Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/b779d2010-05-10T18:39:11.857ZRow: 84companyname: LGE, companylocation: Seoul, Korea, companydesc: Founded in 1958, LG Electronics has led the way in bringing advanced digital products and applied technologies to our customers. With our commitment to innovation and assertive global business policies, we aim to become a worldwide leader in advanced digital technology., companyurl: www.lge.com, productdesc: We are using Google Mobile Services on our mobile devices to enhance user value., companylogo: lge.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/axdae2010-05-10T18:39:11.857ZRow: 85companyname: LinkedIn, companylocation: Mountain View, CA, companydesc: LinkedIn takes your professional network online, giving you access to people, jobs and opportunities like never before. Built upon trusted connections and relationships, LinkedIn has established the world’s largest and most powerful professional network. Currently, more than 55 million professionals are on LinkedIn., companyurl: www.linkedin.com, productdesc: LinkedIn uses many Google technologies. For Java development we use Google Collections and Guice to speed development. Our OpenSocial-based InApps platform uses Apache Shindig, which powers LinkedIn Polls, Events, Company Buzz and Tweets. Shindig OAuth technology is also used in our Open API Program., companylogo: linkedin.png, companypod: Social Web, companytags: Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ayruv2010-05-10T18:39:11.857ZRow: 86companyname: Lombardi, An IBM Company, companylocation: Austin, Texas, companydesc: Lombardi believes that a Process-Driven approach is the best way to win. We deliver a suite of Business Process Management (BPM) software and services that enable organizations to quickly become Process-Driven., companyurl: www.lombardi.com, productdesc: Lombardi has been using GWT since 2007. We use it to develop all the user interface for Lombardi Blueprint, our software as a service process discovery and documentation product, and the web based portions of the developer interface for Lombardi Teamworks 7, our process automation, simulation and management suite., companylogo: lombardi.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/b06f82010-05-10T18:39:11.857ZRow: 87companyname: LTech, companylocation: Bridgewater, New Jersey , companydesc: LTech is a provider of enterprise cloud computing services and broad mix of cloud enablement products for the Google Apps platform. As an early Google Enterprise Partner, LTech has successfully completed dozens of Google Apps deployments and helped develop best-practices for adopting and successfully scaling cloud computing programs., companyurl: www.ltech.com, productdesc: LTech relies heavily on the Google Apps APIs and OAuth for several products. Our LTech Power Panel for Google Apps product is built on top of Google App Engine, leverages Google Web Toolkit and uses many of the Google Apps Management and Application APIs., companylogo: ltech.png, companypod: Enterprise, companytags: Enterprise, Google APIs, Social Web, App Engine, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/b1kzp2010-05-10T18:39:11.857ZRow: 88companyname: Manymoon, companylocation: San Francisco, California, companydesc: Manymoon is the simplest way to get work done online with your contacts. Track projects, complete tasks and share information with no training or setup. Easy enough for anyone to use, Manymoon provides the privacy required for work and the simplicity of social applications. Manymoon is a top-rated application for Google Apps and is trusted by tens of thousands of businesses., companyurl: www.manymoon.com/auth/login?v=googleIOsandbox, productdesc: Manymoon uses the Google Documents Data List API, Calendar API and Google Charts to work with tasks, projects and work activity. Google Gadgets API and OpenSocial are used to access Manymoon within Google Apps. Customers can securely login using OpenID. The Provisioning API is used for administration., companylogo: manymoon.png, companypod: Enterprise, companytags: Enterprise, Google APIs, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/be8562010-05-10T18:39:11.857ZRow: 89companyname: MediaBeacon, companylocation: Minneapolis, Minnesota, companydesc: MediaBeacon is a Digital Asset Management and Enterprise Search company that provides rich media management and intelligent content discovery solutions to enterprises. , companyurl: www.mediabeacon.com, productdesc: We use Google Web Toolkit to power the entire interface layer of our enterprise digital asset management system., companylogo: mediabeacon.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bfmpn2010-05-10T18:39:11.857ZRow: 90companyname: Meebo, companylocation: Mountain View, California, companydesc: Meebo enables over 100 million users to instantly connect, share, and communicate with all of their friends. Because Meebo integrates all social network and communication channels into a single, simple-to-use interface, users can easily share content and communicate in real time with the people who matter to them., companyurl: www.meebo.com, productdesc: Meebo has been developing XAuth as an open standard for simplifying access to users' online services anywhere on the web. Together with the Google Buzz APIs, we are making our products smarter and more personalized for visitors as they navigate across the social web., companylogo: meebo.png, companypod: Social Web, companytags: Social Web, Google APIs, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bh1a02010-05-10T18:39:11.857ZRow: 91companyname: Memeo, companylocation: Aliso Viejo, California, companydesc: Memeo Inc. is a privately-held software company focused on helping consumers and businesses share, manage, and protect their digital content. The company was founded in 2003 and has shipped 45 million software licenses to over 150 countries. , companyurl: www.memeo.com, productdesc: Memeo Connect bridges Google Docs cloud storage with desktop software. With Memeo Connect, instant access to your important documents while online or offline is now very possible. Memeo Connect is available on both Windows and Mac platforms., companylogo: memeo.png, companypod: Enterprise, companytags: Enterprise, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bifuh2010-05-10T18:39:11.857ZRow: 92companyname: Mibbit, companylocation: London, United Kingdom, companydesc: Mibbit gives anyone the capability to host fast, flexible group chat on their website through the use of its customisable Javascript widget. Mibbit currently connects to IRC networks and Twitter, YouTube and other Google services giving people the ability to collaborate around content and data in real-time., companyurl: www.mibbit.com, productdesc: We use several Google APIs: the YouTube data API lets users preview and play content inside chat; the Maps API shows maps inside chat, helping people explore locations; the Google Translate API translates chats across different languages in real-time; and the AdSense API displays contextually targeted ads that reflect users' preferences., companylogo: mibbit.png, companypod: Google APIs, companytags: Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bctkt2010-05-10T18:39:11.857ZRow: 93companyname: MindMeister, companylocation: Munich, Germany, companydesc: MindMeister allows your team to be more innovative by providing a shared collaboration and brainstorming environment using the proven mind mapping technique. Plan projects, manage meetings and sketch out business plans online with partners and colleagues, all in real time!, companyurl: www.mindmeister.com, productdesc: MindMeister uses the Google Documents Data List API and Contacts API to export mind maps to Google Docs and allow sharing with Google contacts. Customers can securely login using OpenID. The Provisioning API is used for administration., companylogo: mindmeister.png, companypod: Enterprise, companytags: Enterprise, Google APIs, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bpgoi2010-05-10T18:39:11.857ZRow: 94companyname: Mint.com, companylocation: Mountain View, California, companydesc: Mint.com lets you see all of your accounts in one place, set budgets, see where you spend, and find personalized savings – all for free.
+, companyurl: www.mint.com, productdesc: Mint.com for Android helps you track all of your accounts, budget, and manage your money right from your phone, updated automatically. The app takes advantage of key Android features such as widgets, smart folders, and system searches of Mint transactions.
+, companylogo: mint.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bqv8z2010-05-10T18:39:11.857ZRow: 95companyname: MLB, companylocation: New York, New York, companydesc: MLB Advanced Media, the interactive media company of Major League Baseball, manages the official league site and each of the 30 individual Club sites to create the most comprehensive Major League Baseball resource on the Internet.
+, companyurl: www.mlb.com, productdesc: MLB.com At Bat 2010 delivers an exclusive and reliable mobile experience for fans wherever they go with up-to-the-minute box scores and statistics, live audio with home and away broadcast feeds, in-game video highlights, MLB.com Gameday pitch speed and location tracking, and more on Android., companylogo: mlb.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bs9tc2010-05-10T18:39:11.857ZRow: 96companyname: Mobiata, companylocation: Ann Arbor, Michigan, companydesc: Mobiata, founded in November 2008, is a leader in mobile travel applications through innovative and beautifully designed products., companyurl: www.mobiata.com, productdesc: Track all your flights on beautiful satellite maps or elegant flight cards that update in real-time. An Android exclusive is a home screen widget that provides the latest information on your flight regardless of which app is running.
+, companylogo: mobiata.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/btodt2010-05-10T18:39:11.857ZRow: 97companyname: MobiTV, companylocation: Emeryville, California, companydesc: MobiTV, Inc. is a provider of technology solutions that deliver media over wireless and broadband networks. MobiTV’s flexible platform powers an end-to-end managed service for content ingestion, encoding and optimized delivery of media for major mobile operators., companyurl: www.mobitv.com, productdesc: MobiTV has taken advantage of Android’s open platform to create a unique media player that supports download of high quality video content with digital rights management for offline playback., companylogo: mobitv.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bjueu2010-05-10T18:39:11.857ZRow: 98companyname: Motorola, companylocation: Schaumburg, Illinois, companydesc: Motorola is known around the world for innovation in communications and focused on advancing the way the world connects. The Motorola MOTODEV™ Developer Program provides developers access to a wealth of services, support, and information to help design, develop and market wireless applications., companyurl: developer.motorola.com, productdesc: Motorola has chosen Android as a simple, powerful means to bring a whole new generation of phenomenal mobile experiences to Motorola users and has already announced an extensive portfolio of Android based handsets around the world. MOTODEV, Motorola's developer network, offers the one, easy-to-access, internet-enabled, open environment developers need to succeed in the future., companylogo: motorola.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bl8zb2010-05-10T18:39:11.857ZRow: 99companyname: mSpot, companylocation: Redwood City, California, companydesc: mSpot is free and the simplest way to get your entire music collection to your Android phone and listen from other internet-connected devices. mSpot is a mobile and PC entertainment company that delivers music, movies, and radio to more than six million mobile customers across 10 wireless carriers., companyurl: www.mspot.com, productdesc: Android technologies used: Android SDK and Eclipse Plug-in for app development; and Google Closure Tools to verify our JavaScript code used on website
+, companylogo: mspot.png, companypod: Android, companytags: Android, GWT, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bmnjo2010-05-10T18:39:11.857ZRow: 100companyname: myERP, companylocation: San Francisco, California, companydesc: myERP.com is a free online business suite that allows small businesses to manage their business in the cloud. myERP.com streamlines small businesses: sales, purchases, CRM, accounting, inventory, and logistics., companyurl: www.myerp.com, productdesc: myERP.com is developed using Google Web Toolkit on the client-side. GWT converts the Java code into a user interface of the AJAX type. While the user interfaces display in a few milliseconds, the application is just as user-friendly and intuitive as a locally installed application. myERP.com is also integrated with Google Apps, Gmail, Google Calendar and Contacts., companylogo: myerp.png, companypod: Enterprise, companytags: Enterprise, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bo2452010-05-10T18:39:11.857ZRow: 101companyname: MySpace, companylocation: Beverly Hills, California, companydesc: MySpace is a technology company connecting people through personal expression, content, and culture. MySpace empowers its global community to experience the Internet through a social lens by integrating personal profiles, photos, videos, mobile, messaging, games, and the world's largest music community., companyurl: www.myspace.com, productdesc: MySpace supports the development of applications based on OpenSocial, allowing developers to have a standardized way to develop great social apps across platforms. As a founding partner of OpenSocial with Google, MySpace is dedicated to open standards and specifications., companylogo: myspace.png, companypod: Social Web, companytags: Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/c0p7u2010-05-10T18:39:11.857ZRow: 102companyname: NaturalMotion , companylocation: San Francisco, California, companydesc: In addition to developing games, NaturalMotion provides advanced animation technologies for titles such as GTA IV, Red Dead Redemption and Star Wars.
+, companyurl: www.naturalmotion.com, productdesc: Backbreaker is an Android game with Android-specific features including uprated graphics and effects optimised for Android devices., companylogo: naturalmotion.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/c23sb2010-05-10T18:39:11.857ZRow: 103companyname: nextstop, companylocation: San Francisco, California, companydesc: nextstop is a community effort to build a catalog of all the best things to do, places to go, and experiences to try anywhere in the world. Our long term goal is to help you find something great to get out and do — whatever you are interested in, whatever language you speak, and wherever you are., companyurl: www.nextstop.com, productdesc: nextstop's web app runs on smartphones like Android, iPhone and modern browsers by using the latest HTML5 features such as localStorage, AppCache and GeoLocation. We also extensively use the Google AJAX APIs and Google Maps APIs., companylogo: nextstop.png, companypod: Chrome, companytags: Chrome, Android, Geo, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/c3ico2010-05-10T18:39:11.857ZRow: 104companyname: Ning, companylocation: Palo Alto, California, companydesc: Ning is a social platform for the world’s interests and passions online, offering an easy-to-use service that allows people to join and create Ning Networks. With more than 1.9 million Ning Networks created and 40 million registered users, millions of people every day are coming together across Ning to explore and express their interests, discover new passions, and meet new people around shared pursuits., companyurl: www.ning.com, productdesc: Ning Apps empower Ning Network Creators with a greater set of choices over the functionality of their Ning Network and ways they can increase engagement by their members. We support a subset of OpenSocial 0.8 APIs., companylogo: ning.png, companypod: Social Web, companytags: Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/c4wx52010-05-10T18:39:11.857ZRow: 105companyname: NordicTrack, companylocation: Logan, Utah, companydesc: NordicTrack is the premium brand more people buy for treadmills, ellipticals and other fitness products. Our consumers want to stay motivated and engaged for fitness, so we launched the interactive website iFit.com. The flagship feature is iFit Live powered by Google Maps, where you can draw a custom running route anywhere in the world there’s a map available., companyurl: www.ifit.com, productdesc: Using Wi-Fi, iFit.com sends a workout to your equipment that simulates the distance and the terrain of the route you’ve drawn. You can watch your real-time progress on Google Maps with satellite, 3D, and Street views. We’re bringing our NordicTrack X7i Incline Trainer – an extreme treadmill that inclines to 40% - so come by and experience the combination of NordicTrack, iFit, the Google Static Maps, JavaScript Maps and Earth API!, companylogo: nordictrack.png, companypod: Geo, companytags: Geo, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bv2y62010-05-10T18:39:11.857ZRow: 106companyname: Novell, companylocation: Waltham, Massachusetts, companydesc: Through our infrastructure software and ecosystem of partnerships, Novell harmoniously integrates mixed IT environments, allowing people and technology to work as one. Novell® Making IT Work As One™., companyurl: www.novell.com, productdesc: Novell Pulse is a new real-time collaboration environment for enterprises that's being built with Google Wave Federation Protocol so people can co-edit messages together across Novell Pulse and Google Wave in real-time. Novell Pulse also supports the Wave Gadgets API and Wave Robots API so extensions built by 3rd parties work seamlessly with a mix of users on Novell Pulse and Google Wave., companylogo: novell.png, companypod: Wave, companytags: Wave, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bwhin2010-05-10T18:39:11.857ZRow: 107companyname: NPR, companylocation: Washington, D.C., companydesc: NPR is an award-winning, multimedia news organization and an influential force in American life. In collaboration with more than 800 independent public radio stations nationwide, NPR strives to create a more informed public, one challenged and invigorated by a deeper understanding and appreciation of events, ideas, and cultures. NPR content is also available on other platforms such as satellite radio, an expansive web site, and mobile devices., companyurl: www.npr.org, productdesc: NPR news and content is available as an Android app (developed using the Android SDK), a Chrome Extension, and iGoogle Gadget. Additionally, search for NPR.org and the NPR API are powered by Google Search Appliances., companylogo: npr.png, companypod: Chrome, companytags: Chrome, Android, Enterprise, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bxw302010-05-10T18:39:11.857ZRow: 108companyname: NYSTROM Herff Jones Education Division, companylocation: Indianapolis, Indiana, companydesc: NYSTROM Herff Jones Education Division is the leading publisher of maps, globes, atlases, hands-on programs, and technology for the K-12 market. Introduced by NYSTROM in September 2009, StrataLogicaTM is a revolutionary, web-based product that delivers layers of age-appropriate, curriculum-based Social Studies content for K-12., companyurl: www.herffjones.com, productdesc: NYSTROM Herff Jones Education Division is using the Google Earth API to power the 3-D educational content available through StrataLogica. By using the Google Earth API, Nystrom and Roundarch have partnered to reinvent the classroom map and globe and present educational content in a groundbreaking, interactive format. , companylogo: herffjones.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bzanh2010-05-10T18:39:11.857ZRow: 109companyname: OffiSync, companylocation: Seattle, Washington, companydesc: OffiSync Supercharges Microsoft Office, enabling users to significantly improve the way they create, collaborate and share their documents by integrating Microsoft Office with Google Docs, and Google Apps., companyurl: www.offisync.com, productdesc: OffiSync uses the Google Doclist API and Google Search API to extend the core functionality of Microsoft OFfice and introduce new innovative ways of collaboration in the cloud., companylogo: offisync.png, companypod: Enterprise, companytags: Enterprise, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cbxr62010-05-10T18:39:11.857ZRow: 110companyname: OnStar, companylocation: Detroit, Michigan, companydesc: OnStar, a leading provider of in-vehicle safety, security and communication services, has nearly 5.5 million subscribers, and is available on more than 30 MY 2010 GM models., companyurl: www.onstar.com, productdesc: The Chevrolet Volt Mobile App is powered by OnStar technology and is available on Android phones. Features include the ability to lock/unlock, remotely start your vehicle, view state of charge and remotely control when your Volt draws power from the grid., companylogo: onstar.png, companypod: Android, companytags: Android, adddate: 5/4/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cdcbn2010-05-10T18:39:11.857ZRow: 111companyname: OpenTable, companylocation: San Francisco, California, companydesc: OpenTable is a leading online provider of free, real-time online restaurant reservations for diners and reservation and guest management solutions for restaurants. OpenTable has more than 11,000 restaurant customers, and since its inception in 1998, has seated more than 120 million diners around the world., companyurl: www.opentable.com, productdesc: We use Google Maps and two versions of the Geolocation API to display real-time restaurant availability around a user’s current location on opentable.com and the Android smartphone platform., companylogo: opentable.png, companypod: Geo, companytags: Geo, Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ceqw02010-05-10T18:39:11.857ZRow: 112companyname: Oracle, companylocation: Redwood Shores, California, companydesc: Oracle provides a complete, open, and integrated business software and hardware systems to more than 370,000 customers—including 100 of the Fortune 100—that represent a variety of sizes and industries in more than 145 countries around the globe., companyurl: www.oracle.com, productdesc: Demonstrating Oracle CRM OnDemand and OnPremise Collaboration with Google Wave, Google Docs, and OpenSocial gadgets. Integration developed using Google Web Toolkit, Google App Engine and CRM OnDemand and OnPremise Web Services., companylogo: oracle.png, companypod: Wave, companytags: Wave, Social Web, App Engine, GWT, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cg5gh2010-05-10T18:39:11.857ZRow: 113companyname: Orange Honey, companylocation: Seattle, Washington, companydesc: Orange Honey has developed online photo editing and vector sketching applications that bring desktop application quality to the browser and
+mobile experience., companyurl: www.orangehoney.com, productdesc: Orange Honey products utilize Google Chrome and the HTML5 Canvas standard to support cross-platform image sharing and accessibility, providing a rich image creation and sharing experience for all users., companylogo: orangehoney.png, companypod: Chrome, companytags: Chrome, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/c6bhi2010-05-10T18:39:11.857ZRow: 114companyname: OrangeScape, companylocation: Chennai, Tamilnadu, India, companydesc: OrangeScape is a Plaform-as-a-Service for building business applications that can run across cloud platforms - Google App Engine, IBM SmartCloud, Amazon EC2 or your own data center., companyurl: www.orangescape.com, productdesc: The development environment - OrangeScape Studio - runs on App Engine and the application developed on OrangeScape Studio can be deployed on App Engine., companylogo: orangescape.png, companypod: App Engine, companytags: App Engine, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/c7q1z2010-05-10T18:39:11.857ZRow: 115companyname: Oxylabs Networks, companylocation: Gurgaon, Haryana, India, companydesc: Oxylabs develops cross-platform, cross-device social games., companyurl: www.oxylabs.com, productdesc: We use several Google technologies to develop our platform: App Engine for a scalable back end; OpenSocial for distribution; Google Charts for our API; Google Translate for our feedback management system; and Google Apps for email, collaboration and documents., companylogo: oxylabs.png, companypod: App Engine, companytags: App Engine, Social Web, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/c94mc2010-05-10T18:39:11.857ZRow: 116companyname: Palm, companylocation: Sunnyvale, California, companydesc: Palm creates intuitive and powerful mobile experiences that enable consumers and businesses to connect to their information in more useful and usable ways., companyurl: www.palm.com, productdesc: Palm has a long history of working closely with Google, building services like Google Search and Google Maps directly into Palm devices. Palm and Google share the same belief in the Web and the opportunities it provides developers. The native platform for Palm's groundbreaking webOS is the Web, allowing you to write real applications for mobile as easily as you can write them for the Web. , companylogo: palm.png, companypod: Chrome, companytags: Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/caj6t2010-05-10T18:39:11.857ZRow: 117companyname: Pandora, companylocation: Oakland, California, companydesc: Pandora is the world’s largest internet radio service available through personal computers, mobile phones, and a wide array of consumer electronic devices. More than 45 million listeners enjoy personalized radio stations based on Pandora’s Music Genome Project™, which enables Pandora to quickly and easily create relevant playlists, full of discovery., companyurl: www.pandora.com, productdesc: We use a number of Google products: the Android SDK for creating the Pandora app, Google Analytics, Google Webmaster Tools, AdSense and DoubleClick., companylogo: pandora.png, companypod: Android, companytags: Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hmyxm2010-05-10T18:39:11.857ZRow: 118companyname: Parrot, companylocation: Paris, France, companydesc: Parrot, a global leader in wireless devices for mobile phones, stands on the cutting edge of innovation. In January 2010, Parrot introduced the Parrot AR.Drone, a flying Wi-Fi quadricopter, which allows users to play video games in Augmented Reality., companyurl: www.parrot.com, productdesc: The Parrot AR.Drone utilizes wifi connectivity, OpenGL ES for video streaming, and the accelerometer and trackball for navigation controls., companylogo: parrot.png, companypod: Android, companytags: Android, adddate: 5/4/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hodi32010-05-10T18:39:11.857ZRow: 119companyname: PayPal, companylocation: San Jose, California, companydesc: PayPal is the faster, safer way to pay and get paid online. The service allows members to send money without sharing financial information, with the flexibility to pay using their account balances, bank accounts, credit cards or promotional financing., companyurl: www.paypal.com, productdesc: With the PayPal Mobile Payments Library, Android developers will be able to start accepting in-app payments for physical goods, services, donations and personal payments in a matter of minutes. Collecting financial information or requiring the consumer to fill long forms to complete a purchase is no longer needed, the PayPal library takes care of the payment flow and developers can keep focusing on building great apps.
+, companylogo: paypal.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hps2g2010-05-10T18:39:11.857ZRow: 120companyname: PlanetInAction, companylocation: Auckland, New Zealand, companydesc: Dinther Product Design Ltd showcases their browser-based games on PlanetInAction.com, an example of what can be done with the Google Earth API. We also handle commercial projects where we develop innovative software solutions using Google Earth and other Google products., companyurl: www.planetinaction.com, productdesc: We mainly use the Google Maps and Earth API. Our solutions tend to be low tech, because we focus on results and short development times., companylogo: planetinaction.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hr6mx2010-05-10T18:39:11.857ZRow: 121companyname: Plaxo, companylocation: Mountain View, California, companydesc: Plaxo is working to deliver on the vision of a truly smart, socially-aware address book that lives securely in the cloud, while interacting with all of the applications, services, and devices in your life where contact information matters., companyurl: www.plaxo.com, productdesc: We are using Google Contacts Data APIs to build two-way contact sync between Plaxo and Google., companylogo: plaxo.png, companypod: Social Web, companytags: Social Web, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hhcny2010-05-10T18:39:11.857ZRow: 122companyname: Playfish, companylocation: San Francisco, California, companydesc: Playfish is a leader in the social gaming industry in innovation and creativity with award-winning, category-defining games designed for friends to play together. The company has changed the way people play games by creating more social and connected experiences., companyurl: www.playfish.com, productdesc: Playfish has brought some of its most iconic social games to Open Social communities around the world, from Who Has The Biggest Brain? on iGoogle, to Pet Society on Cyworld, and Word Challenge on Netlog., companylogo: playfish.png, companypod: Social Web, companytags: Social Web, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hir8f2010-05-10T18:39:11.857ZRow: 123companyname: PolarBit, companylocation: Stockholm, Sweden, companydesc: Polarbit is a leading developer of high-end 3D games and middleware for the mobile and handheld markets worldwide. Our dedication to our work and uncompromising attitude towards quality ensure customers and users that Polarbit technology is always synonymous with world class technology.
+, companyurl: www.polarbit.com, productdesc: Polarbit games are built using the Android NDK, companylogo: polarbit.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hk5ss2010-05-10T18:39:11.857ZRow: 124companyname: Prezi, companylocation: San Francisco, CA, companydesc: Prezi is a web-based presentation application and storytelling tool that uses a single canvas instead of traditional slides. The canvas allows users to create non-linear presentations, where they can zoom in and out of a visual map., companyurl: www.prezi.com, productdesc: Prezi uses the Google Wave Gadgets API to create new features and functionality for our presentation editor., companylogo: prezi.png, companypod: Wave, companytags: Wave, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hlkd92010-05-10T18:39:11.857ZRow: 125companyname: ProcessOne, companylocation: Paris, France, companydesc: ProcessOne provides real-time interpersonal web communications for telecommunications organisations, ISPs, social networking sites and large enterprises. Well-known businesses benefit from our expertise in real-time instant messaging., companyurl: www.process-one.net, productdesc: As an extension to its XMPP server, ProcessOne uses the Google Wave Federation Protocol in its own "WaveOne" server, federated with Google's WaveSandbox and FedOne., companylogo: processone.png, companypod: Wave, companytags: Wave, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hy7gy2010-05-10T18:39:11.857ZRow: 126companyname: processWave.org, companylocation: Potsdam, Germany, companydesc: processWave.org is the project of seven students from Germany's #1 ranked software engineering school, the Hasso Plattner Institute in Potsdam, Germany. We are working on a collaborative diagram editor that supports software and business process modeling with UML, BPMN and EPC. It allows multiple people to edit the same diagram at the same time., companyurl: www.processwave.org, productdesc: Our diagram editor is realized as a gadget for Google Wave, which provides the collaborative basis for our product., companylogo: processwave.png, companypod: Wave, companytags: Wave, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hzm1f2010-05-10T18:39:11.857ZRow: 127companyname: Qik, companylocation: Redwood City, California, companydesc: Qik lets people capture and share live and recorded video with their friends, family, and the world, right from their phone., companyurl: www.qik.com, productdesc: Qik uses the Android SDK to produce video recording and broadcasting software for all Android phones, and Google Maps for website video location information, companylogo: qik.png, companypod: Android, companytags: Android, Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/i10ls2010-05-10T18:39:11.857ZRow: 128companyname: Red Hat, companylocation: Raleigh, North Carolina, companydesc: JBoss Enterprise Middleware is a broad portfolio of enterprise-class open source middleware, delivered by Red Hat, a leader in open source solutions. The portfolio includes application, portal, data integration, rules management and ESB-based SOA platforms. JBoss Enterprise Middleware is available via subscriptions that include patches, updates, support, multi-year maintenance policies, and software assurance., companyurl: www.redhat.com, productdesc: We use GWT for several of our products, including the JBoss Web Framwork Kit, the JBoss Business Rules Management System, and development frameworks like Seam, which provides GWT integration layer. GateIn, our new portal project, includes the ability for end users to easily create GWT widgets and portlet-based applications and dashboards within a secure, personalized site experience., companylogo: redhat.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/i2f692010-05-10T18:39:11.857ZRow: 129companyname: Remember The Milk, companylocation: Sydney, NSW, Australia, companydesc: Remember The Milk is an online task management service with more than 2 million users. With products available in 31 languages, Remember The Milk helps people all over the world be more organized and productive., companyurl: www.rememberthemilk.com, productdesc: Remember The Milk makes extensive use of Google technologies. Products include an Android app, offline web app access with Gears, a Google Maps mashup to visualize task locations, Google Talk reminders, Google Calendar sidebar and event gadgets, an iGoogle gadget, a Gmail sidebar gadget, and Firefox and Google Chrome extensions that integrate with Gmail., companylogo: rememberthemilk.png, companypod: Google APIs, companytags: Google APIs, Android, Geo, Chrome, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hsl7a2010-05-10T18:39:11.857ZRow: 130companyname: Rent.com, companylocation: Santa Monica, California, companydesc: With Rent.com, property owners and managers can create rich listings that include photos, floorplans, amenities, move-in specials, and detailed descriptions, while renters can search millions of units, filtering by number of beds/bath, city, neighborhood, price and amenities in order to find their perfect home., companyurl: www.rent.com, productdesc: We are using the Google maps API V3.0 on the Android and iPhone OS for mapping the location of, and providing driving directions to, rental properties that match a user's specific search criteria. We are also providing a search nearby feature that makes use of the Google Maps API V3.0., companylogo: rent.png, companypod: Geo, companytags: Geo, Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/htzrr2010-05-10T18:39:11.857ZRow: 131companyname: Roambi, companylocation: San Diego, California, companydesc: Roambi, from MeLLmo, is the next generation mobile business application. Roambi puts the pulse of your business in the palm of your hand by transforming existing business reports and data into rich, interactive visualizations and mobile dashboards, delivered to any iPhone. Roambi lets busy professionals track the status of their project, department or company – anytime and anywhere – and make faster, smarter decisions on-the-go., companyurl: www.roambi.com, productdesc: Roambi utilizes the Google Docs and Spreadsheet APIs as well as OAuth to give users the ability to convert data from Google spreadsheets into our interactive views., companylogo: roambi.png, companypod: Enterprise, companytags: Enterprise, Social Web, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hwswl2010-05-10T18:39:11.857ZRow: 132companyname: RockYou, companylocation: Redwood City, California, companydesc: RockYou is a leading innovator, creator, and distributor of content on the social web, with over 90M monthly US users, 250M users worldwide, and 15 billion global impressions., companyurl: www.rockyou.com, productdesc: We utilize Open Social to implement our applications for many of the large social networks out there including MySpace in the US, Mixi in Japan, and Cyworld in Korea. We've been big proponents for the Open Social platform and were key in pushing adoption by all of the Asian platforms., companylogo: rockyou.png, companypod: Social Web, companytags: Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/i9g0a2010-05-10T18:39:11.857ZRow: 133companyname: Rosetta, companylocation: Hamilton, New Jersey, companydesc: Rosetta is the largest independent interactive agency in the country whose Personality-based segmentations uncover the drivers of brand choice and product usage. Rosetta's industry-focused marketers, world-class technology teams and creative teams translate those insights into relevant marketing solutions that attract, retain and deepen a brand's best customer relationships., companyurl: www.rosetta.com, productdesc: We use Google Web Toolkit to create the user interface for a number of our different ecommerce clients. The toolkit flexibility and ease of use enables us to create a cutting edge, brilliant, web 2.0 feel for our websites., companylogo: rosetta.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/iaukr2010-05-10T18:39:11.857ZRow: 134companyname: RunMyProcess, companylocation: Paris, France, companydesc: RunMyProcess is the platform as a service of choice for workflows and integration in the Cloud. It offers a complete integration with Google Apps, as well as a set of 1,000+ connectors to link it in a few clicks to most traditionnal or SaaS softwares., companyurl: www.runmyprocess.com, productdesc: We provide an abstraction layer on almost all of Google's web service API (Calendar, Sites, Spreadsheet, Docs, Analytics, etc) allowing users to use any API easily within workflows or business process designed on our platform. We use Google Web Toolkit for dynamically generating web applications and gadgets, as well as GViz for reporting dashboard on workflows embedded within gadgets. W also use Google's enablement of OAuth, OpenID and SAMLv2 for unified authentication capabilities., companylogo: runmyprocess.png, companypod: Enterprise, companytags: Enterprise, Google APIs, GWT, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ic9542010-05-10T18:39:11.857ZRow: 135companyname: SADA Systems, companylocation: Los Angeles, California, companydesc: One of the first 10 Google Apps for Enterprise Partners worldwide and initial contributor to the Google Apps Open Source provisioning tool kit, SADA Systems, Inc. has developed a proven track record in successful implementations of Google Apps solutions. SADA works closely with its clients to create custom, leading edge solutions that strategically meet their business and institutional objectives., companyurl: www.sadasystems.com, productdesc: At SADA, we not only resell Google Apps, but use Google Apps internally for collaboration along with the GData API to develop migration tools. We utilize App Engine to host custom applications, all of which allow us to more efficiently develop custom products for our clients such as our Single Sign On (SSO) tool, User Provisioning, On-premise and hosted Sync Tools., companylogo: sada.png, companypod: Enterprise, companytags: Enterprise, Google APIs, App Engine, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/idnpl2010-05-10T18:39:11.857ZRow: 136companyname: Safari Books Online, companylocation: Sebastopol, California, companydesc: Safari Books Online is an on-demand digital library that provides searchable, online access to the full content of thousands of books, training videos and pre-published manuscripts from the world's leading authors and publishers. For one low monthly price, Safari Books Online delivers access to the most current and critical technology, digital media and business titles, as well as time-saving tools that make it easy to organize information and personalize your learning experience., companyurl: www.safaribooksonline.com, productdesc: To deliver an improved mobile reading experience, we are implementing HTML5 features such as local storage for storing offline books and CSS transitions., companylogo: safaribooksonline.png, companypod: Chrome, companytags: Chrome, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/i3tqm2010-05-10T18:39:11.857ZRow: 137companyname: Salesforce.com, companylocation: San Francisco, California, companydesc: Salesforce.com is an enterprise cloud computing company. Based on Salesforce.com's real-time, multitenant architecture, the company's platform and CRM applications have revolutionized the way companies collaborate and communicate with their customers., companyurl: www.salesforce.com, productdesc: The Force.com Toolkit for Google Data APIs provides a set of tools and services that developers can use to take advantage of Google Data APIs from within Force.com. The Force.com for Google App Engine library enables you to access the Force.com platform from Google App Engine Apps - Cloud to Cloud. Salesforce.com has done recent prototyping with Google Wave APIs, and the company also offers Google Apps and Google Adwords integration., companylogo: salesforce.png, companypod: Wave, companytags: Wave, Google APIs, App Engine, Enterprise, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/i58b32010-05-10T18:39:11.857ZRow: 138companyname: Samsung, companylocation: San Jose, California, companydesc: Samsung Electronics Co., Ltd. is a global leader in semiconductor, telecommunication, digital media and digital convergence technologies with 2009 consolidated sales of US$116.7 billion. Recognized as one of the fastest growing global brands, Samsung Electronics is a leading producer of digital TVs, memory chips, mobile phones and TFT- LCDs., companyurl: www.samsung.com, productdesc: As a member of Open Handset Alliance, Samsung develops mobile phones based on Android for markets across the world. Including the recently announced Samsung Galaxy S., companylogo: samsung.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/g2s5e2010-05-10T18:39:11.857ZRow: 139companyname: SAP, companylocation: Palo Alto, California, companydesc: As a leading provider of business software, SAP delivers products and services that help accelerate business innovation for our customers. Today, customers in more than 120 countries run SAP applications -- from distinct solutions addressing the needs of small businesses and midsize companies to suite offerings for global organizations., companyurl: www.sap.com, companylogo: sap.png, companypod: Wave, companytags: Wavehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/i81fx2010-05-10T18:39:11.857ZRow: 140companyname: Seesmic, companylocation: San Francisco, California, companydesc: Seesmic has become one of the most popular Twitter and Facebook clients that allow users to easily access their social networks in one application and successfully manage their online presence. From web to mobile or email to chat, Seesmic looks to be the gateway to all your social services on any online platform., companyurl: www.seesmic.com, productdesc: Seesmic is available on Android, taking advantage of the unique features of the mobile platform, thus making a powerful, yet nevertheless simple and easy to use application. Seesmic also relies on Google Web Toolkit as the main foundation technology for its rich and appreciated web application: Seesmic Web., companylogo: seesmic.png, companypod: Android, companytags: Android, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ikojm2010-05-10T18:39:11.857ZRow: 141companyname: SGN, companylocation: Palo Alto, California, companydesc: SGN is a publisher of mobile social games that emphasize console-quality graphics and social engagement., companyurl: www.sgn.com, productdesc: Android Platform technologies used: NDK r3 based game engine; fully support android multi-tasking, resuming right in the middle of a multiplayer game; OpenGL ES 1.1 graphics; high detailed terrain and models, water effects; AudioPool sound system bridge from NDK to JAVA Classes; and Linux polling and sockets for multiplayer., companylogo: sgn.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/im3432010-05-10T18:39:11.857ZRow: 142companyname: SheepDog Inc., companylocation: Halifax, Nova Scotia, Canada, companydesc: SheepDog, like its namesake, is reliable, loyal, protective and highly trained to manage the heart of business today: information and connectivity. SheepDog facilitates migration projects to the Google Apps environment as well as showing clients how to fully leverage the most valuable and powerful aspects of the Google cloud., companyurl: www.sheepdoginc.ca, productdesc: Gtrax is an application that is built on App Engine with Google Web Toolkit. Its key advantage is that it has been designed to tightly integrate with Google Apps APIs so that it simplifies use. It's fully integrated with OpenID, MVP design pattern, command design pattern, and DI design pattern. , companylogo: sheepdog.png, companypod: Enterprise, companytags: Enterprise, App Engine, GWT, Google APIs, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/inhog2010-05-10T18:39:11.857ZRow: 143companyname: shopkick Inc., companylocation: Palo Alto, California, companydesc: shopkick turns the physical retail world into an interactive world for consumers, through the use of their cell phones. Its first app is CauseWorld - the first app that lets you do good deeds simply for walking into a store. Sponsors like Citibank and Kraft Foods provide donation money; consumers decide where it goes., companyurl: www.shopkick.com, productdesc: The CauseWorld mobile application runs on the Google Android platform., companylogo: shopkick.png, companypod: Android, companytags: Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/iow8x2010-05-10T18:39:11.857ZRow: 144companyname: ShufflePoint, companylocation: Newark, Delaware, companydesc: ShufflePoint transforms Web Analytics data into PowerPoint, Excel or Google Gadgets. We provide powerful solutions for Web Business Analysts that eliminate tedious cut-and-paste work so they can instead spend their time creating business value., companyurl: www.shufflepoint.com, productdesc: We use the Google Analytics API to retrieve data used for report generation. We also use the Google Visualization API and gadgets to create web dashboards., companylogo: shufflepoint.png, companypod: Google APIs, companytags: Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/if29y2010-05-10T18:39:11.857ZRow: 145companyname: Skytap, companylocation: Seattle, Washington, companydesc: Skytap a leading provider of self-service cloud automation solutions for dynamic workloads. Skytap enables users to run enterprise apps unchanged in the cloud, collaborate securely with global teams, and gain unparalleled business productivity. IT organizations can use Skytap for application development, testing, virtual training, ERP migration and sales demonstration projects and reduce costs by up to 70%., companyurl: www.skytap.com, productdesc: Skytap uses Google Analytics APIs and Google Apps as its OpenID identity provider., companylogo: skytap.png, companypod: Enterprise, companytags: Enterprise, Social Web, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/igguf2010-05-10T18:39:11.857ZRow: 146companyname: SlideRocket, companylocation: San Francisco, California, companydesc: SlideRocket is reinventing presentations with its feature rich, innovative platform that lets you create, manage, share and measure your presentations in one complete and integrated application., companyurl: www.sliderocket.com, productdesc: SlideRocket uses Google Apps API's to provide seamless integration between Google Docs and SlideRocket. We use: Google's user provisioning API for integrating accounts; the Apps licensing API and federated login (OpenID + OAuth) for enabling single sign-on; the Google Docs read/write API for importing dynamic tables, charts and presentation from Google Docs to SlideRocket; and the Contacts read/write API for integrating Google Contacts into SlideRocket., companylogo: sliderocket.png, companypod: Enterprise, companytags: Enterprise, Google APIs, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ihves2010-05-10T18:39:11.857ZRow: 147companyname: Smartsheet, companylocation: Bellevue, Washington, companydesc: Smartsheet provides an online collaboration tool used by thousands of small businesses and teams in larger corporations for project and task management, sales pipeline management, crowdsourcing and other types of work management., companyurl: www.smartsheet.com, productdesc: Smartsheet enables single sign in for Googe Apps and Google Accounts using Google Account Authentication. Smartsheet uses the Google Documents List Data API to enable customers to attach Google Docs to any task and the Google Spreadsheets Data API for import/export between Smartsheet and Google spreadsheets. We also use the Google Gadgets API for our Smartsheet iGoogle gadgets., companylogo: smartsheet.png, companypod: Enterprise, companytags: Enterprise, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ij9z92010-05-10T18:39:11.857ZRow: 148companyname: SnapABug, companylocation: Huntsville, Alabama, companydesc: The SnapABug Help widget reduces companies QA and support cycle. SnapABug can easily be embedded on any website and provides all the information necessary to debug the situation quickly, including a snapshot of the remote visitor webpage. SnapABug live also lets you chat with your websites visitors while they browse, and does all this with no software install., companyurl: www.snapabug.com, productdesc: SnapABug is a 100% Google App Engine Java application. It interfaces with several Google services such as Google Talk and Google Maps API. SnapABug also relies on Google Web Toolkit as the technology behind its Widget Configurator user interface., companylogo: snapabug.png, companypod: App Engine, companytags: App Engine, Geo, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ivx4q2010-05-10T18:39:11.857ZRow: 149companyname: Socialwok, companylocation: Singapore, companydesc: Voiceroute Pte Ltd is the company behind Socialwok, a feed-based, business, social networking service that help thousands of small and medium businesses to collaborate on Google Apps. Socialwok is the winner of the Techcrunch 2009 demopit award., companyurl: www.socialwok.com, productdesc: Socialwok is a Google App Engine Java application that is tightly integrated with Google Apps using Google Data APIs. Our user interface uses Google Web Toolkit., companylogo: socialwok.png, companypod: App Engine, companytags: App Engine, Enterprise, Google APIs, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ixbp72010-05-10T18:39:11.857ZRow: 150companyname: Sony Ericsson, companylocation: Lund, Sweden, companydesc: Sony Ericsson is a 50:50 joint venture by Sony and Ericsson established in October 2001, with global corporate functions located in London and operations in all major markets. Sony Ericsson vision is to become the industry leader in Communication Entertainment; where new styles of communicating through the internet and social media, become entertainment. Sony Ericsson offers exciting consumer experiences through phones, accessories, content and applications., companyurl: www.sonyericsson.com, productdesc: As one of the leading OEMs of Android phones, Sony Ericsson makes use of Android code management and review tools built around git, including Google-created gerrit and repo tools. Sony Ericsson uses the Android SDK and related tools, as well as creating new additions to Android which are in many cases contributed to the Android platform., companylogo: sonyericsson.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/iyq9k2010-05-10T18:39:11.857ZRow: 151companyname: Spanning, companylocation: Austin, Texas, companydesc: Spanning extends the Google Apps collaboration and messaging suite. Spanning Sync 3 syncs both Google Calendar with Apple iCal and Google Contacts with the OS X Address Book. Spanning Backup for Google Apps is a new cloud-based application that continually backs up Google Calendar, Contacts, and Docs., companyurl: www.spanning.com, productdesc: Our products use many of the Google Data APIs, including those for Calendar, Contacts, Docs, Spreadsheets, and Provisioning. Plus, we use Google Apps, Groups, Analytics, Feedburner, YouTube, and Picasa Web Albums to run our business., companylogo: spanningsync.png, companypod: Enterprise, companytags: Enterprise, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/j04u12010-05-10T18:39:11.857ZRow: 152companyname: Spiral Universe, companylocation: White Plains, New York, companydesc: Spiral is a powerful information management system and learning platform used by schools and colleges in 60 countries around the world. Spiral uses the power of modern technologies to improve the learning experience and provide valuable tools to school administrators, teachers, students, and parents. Our mission is to bring the best information technology to all schools regardless of their means., companyurl: www.spiraluniverse.com, productdesc: Spiral is one of the largest GWT-based applications ever built, offering hundreds of tools for school administrators, teachers, students, and parents through a cloud-based webtop user interface. We recommend Chrome to our users because of its speed and support for HTML5 features used in our system., companylogo: spiraluniverse.png, companypod: GWT, companytags: GWT, Chrome, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/iqav22010-05-10T18:39:11.857ZRow: 153companyname: Springpad, companylocation: Charlestown, Massachusetts, companydesc: Springpad is a free web app that gives you many ways to save and access anything you want to remember. It automatically organizes and enhances the data you've stored with useful links and offers to save you time and money., companyurl: www.springpadit.com, productdesc: Springpad was built from the ground up using Google Web Toolkit. Springpad also uses Google's AJAX api's (search, maps, local, gcal), and OpenID/OAuth. An Android application is in the works., companylogo: springpad.png, companypod: GWT, companytags: GWT, Google APIs, Social Web, Android, Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/irpfj2010-05-10T18:39:11.857ZRow: 154companyname: Square, companylocation: San Francisco, California, companydesc: Square is focused on bringing immediacy, transparency, and approachability to the world of payments: an inherently social interaction each of us participates in daily., companyurl: www.squareup.com, productdesc: Android features used: Intent API for 3rd party developers; background processing; and notifications., companylogo: square.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/iuikd2010-05-10T18:39:11.857ZRow: 155companyname: StudyBlue, companylocation: Madison, Wisconsin, companydesc: StudyBlue is a student-driven study network. Students using our services cut their study time dramatically while performing better, consistently, with less stress regardless of proficiency. We offer class and subject-specific content coupled with sophisticated tools that provide real-time adaptive feedback into the most effective methods of learning and retention., companyurl: www.studyblue.com, productdesc: Our website is built entirely with Google Web Toolkit (GWT). GWT allows for complete AJAX integration without sacrificing usability or integration capabilities. Our system architecture enables rapid evolution and adaptation to changing user expectations/needs, and delivery/consumption alternatives., companylogo: studyblue.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/j75o22010-05-10T18:39:11.857ZRow: 156companyname: The Huffington Post, companylocation: New York, New York, companydesc: The Huffington Post is a leading social news and opinion site, which in four and a half years has become an influential media brand -- "The Internet Newspaper." The site offers coverage of politics, media, business, entertainment, living, style, sustainable “green” living, world news, technology, and comedy, and is a top destination for news, blogs and original content., companyurl: www.huffingtonpost.com, productdesc: We use a number of Google products, including Google Apps for messaging and collaboration, Google Analytics for web trends, and the Search and Trends APIs in our CMS. Our website lets users sign in via Google Friend Connect, Google Site Search powers searches on our website, and we've built a HuffPost Google gadget and Android app. We're using the YouTube Video Uploader to help cover citizen journalism and allow our community to participate. HuffPost content is also featured on Google’s Fast Flip that is integrated within Google News pages., companylogo: huffingtonpost.png, companypod: Google APIs, companytags: Google APIs, Enterprise, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/j8k8j2010-05-10T18:39:11.857ZRow: 157companyname: The New York Times, companylocation: New York, New York, companydesc: The New York Times Company includes The New York Times, the International Herald Tribune, The Boston Globe, 15 other daily newspapers and more than 50 web sites, including NYTimes.com, Boston.com and About.com. The Company’s core purpose is to enhance society by creating, collecting and distributing high-quality news, information and entertainment., companyurl: www.nytimes.com, productdesc: The New York Times is using the latest in HTML5 to advance our online products, including video, Application Cache, DOM storage, and advanced CSS3 features. The result is exciting new Desktop-class apps that will run great on mobile phones, Chrome, and the forthcoming Chrome OS., companylogo: nytimes.png, companypod: Chrome, companytags: Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jbddd2010-05-10T18:39:11.857ZRow: 158companyname: TheDeadline, companylocation: Hamburg, Germany, companydesc: TheDeadline is an intelligent Todo-Manager. Adding and tagging todos is as easy as writing a Twitter post. TheDeadline provides a novel way to share todos fast and easy with co-workers to collaborate in small and large projects. An integrated artificial intelligence gives status updates, asks for decisions and automatically re-submits overdue todos in a smart way without annoying the user., companyurl: the-deadline.appspot.com, productdesc: We are using Google App Engine and Google Closure. Integration with Google Apps will be available by the time of Google I/O 2010., companylogo: thedeadline.png, companypod: App Engine, companytags: App Engine, Enterprise, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/j1jee2010-05-10T18:39:11.857ZRow: 159companyname: Thermopylae Sciences and Technology, companylocation: Arlington, Virginia, companydesc: Thermopylae Sciences + Technology is non-traditional services and software company that harnesses new and emerging concepts and technologies to rapidly deliver solutions to customers in Federal/DoD/commercial sectors. These specific solutions include products like iSpatial and iHarvest, as well as custom solution development built specifically around a customer or problem set., companyurl: www.t-sciences.com, productdesc: Thermopylae uses the Google Maps API, Earth API, Google Earth Enterprise, as well as the Android SDK in both our iSpatial & UBIQUITY products and within our custom solution development efforts for customers like the Secretary of State, US State Department and US Army, SOUTHCOM for Haiti Relief effort., companylogo: tsciences.png, companypod: Geo, companytags: Geo, Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/j2xyv2010-05-10T18:39:11.857ZRow: 160companyname: Thumbplay, companylocation: New York, New York, companydesc: Thumbplay, a leading provider of mobile entertainment in the U.S., has built an award-winning service centered on delivering millions of pieces of mobile content to 95 percent of U.S. devices among every major carrier. Thumbplay’s latest offering is Thumbplay Music, a next generation, cloud-based music service that delivers millions of songs on-demand to smartphones and PCs/Macs., companyurl: www.thumbplay.com, productdesc: Thumbplay Music is delivering cloud-based access to millions of songs via Android and soon via Chrome OS using HTML5., companylogo: thumbplay.png, companypod: Chrome, companytags: Chrome, Android, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/j4cj82010-05-10T18:39:11.857ZRow: 161companyname: TimeBridge Inc., companylocation: Berkeley, California, companydesc: TimeBridge is an easy-to-use web app that helps busy professionals schedule and run great meetings across companies and calendaring systems. TimeBridge helps you quickly find a time to meet, add a phone or web conference, and collaborate on the agenda, action items, meeting documents and notes., companyurl: www.timebridge.com, productdesc: TimeBridge created extensions to Calendar and Wave, using GData and OpenID/oAuth; and supports Google Apps Marketplace with Universal Navigation integration that allows for the enablement of whole domains by administrators. Our web app fully supports Calendar and Contact integration for seamless experience and Single Sign-on., companylogo: timebridge.png, companypod: Wave, companytags: Wave, Google APIs, Social Web, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jie7e2010-05-10T18:39:11.857ZRow: 162companyname: TribalDDB London, companylocation: London, United Kingdom, companydesc: We are a full service digital agency based in London. Our approach to digital communications is invention, we invent based on human insight, we build and produce never-seen-before digital experiences which enthral, excite, provoke action and emotion, thereby providing new ways for people to engage with our clients' brands., companyurl: blogs.tribalddb.co.uk, productdesc: We use the Google Maps API to produce compelling geo-aware Web applications., companylogo: tribalddb.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jjsrv2010-05-10T18:39:11.857ZRow: 163companyname: TripIt, companylocation: San Francisco, California, companydesc: TripIt helps people organize and share their travel plans no matter where they book. Travelers simply forward their travel confirmation emails from over 1,000 sites to plans@tripit.com and TripIt automatically creates a master itinerary that combines all their travel plans in one place plus weather, maps, restaurants and more, and can be accessed anytime online or through a mobile device. , companyurl: www.tripit.com, productdesc: We use Open Social for our LinkedIn app, we use the Android Software Development Kit for our Android app, and we use Google Geo APIs for maps and directions in our trip itineraries., companylogo: tripit.png, companypod: Android, companytags: Android, Social Web, Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jl7c82010-05-10T18:39:11.857ZRow: 164companyname: Trulia, companylocation: San Francisco, California, companydesc: Trulia is a real estate site focused on empowering you with smarter tools to help you find the right home. Whether you are an active buyer, seller or real estate enthusiast, Trulia gives you all the information you care about, from rich, property data to a personalized search experience. Trulia brings together local information, community insights, market data and national listings all in one place., companyurl: www.trulia.com, productdesc: We use the Google Maps API to enrich Trulia’s search result pages and residential real estate property pages., companylogo: trulia.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jcrxq2010-05-10T18:39:11.857ZRow: 165companyname: TweakerSoft, companylocation: Torino, Italy, companydesc: TweakerSoft originated from more than a decade of software development in CAD/CAM, graphics and mobile applications. They specialize in the development of innovative Mac and iPhone software., companyurl: www.tweakersoft.com, productdesc: Our mobile application, AroundMe, uses the Google Maps API and Local Search API. AroundMe is also available for Android devices., companylogo: tweakersoft.png, companypod: Google APIs, companytags: Google APIs, Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/je6i72010-05-10T18:39:11.857ZRow: 166companyname: TweetDeck, companylocation: London, United Kingdom, companydesc: TweetDeck is a leading browser for the real-time and social web, allowing users to connect with Twitter, Facebook, LinkedIn and MySpace. Our advanced user interface gives you more power in managing your social stream across desktop and mobile platforms. , companyurl: www.tweetdeck.com, productdesc: Our upcoming product is being developed on Android and we're also using AppEngine to power various parts of our backend infrastructure. In addition, we consume a variety of APIs: Google Analytics for traffic monitoring, the Visualization API for internal statistics tracking systems, and the Social Graph API to help model the social web., companylogo: tweetdeck.png, companypod: App Engine, companytags: App Engine, Android, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jfl2k2010-05-10T18:39:11.857ZRow: 167companyname: Twilio, companylocation: San Francisco, California, companydesc: Twilio provides a web-service API for businesses to build scalable, reliable communication apps that make and receive phone calls and and send and receive SMS messages., companyurl: www.twilio.com, productdesc: Twilio uses the Google Wave API to build a robot extension called twiliobot that enables users to make and receive phone calls from inside Google Wave. Calls are recorded, transcribed, and posted back to the wave., companylogo: twilio.png, companypod: Wave, companytags: Wave, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jgzn12010-05-10T18:39:11.857ZRow: 168companyname: Ubisoft, companylocation: San Francisco, California, companydesc: Ubisoft is a leading producer, publisher and distributor of interactive entertainment products worldwide and has grown considerably through a strong and diversified line-up of products and partnerships. Ubisoft is present in 28 countries and has sales in more than 55 countries around the globe., companyurl: www.ubisoft.com, productdesc: Ubisoft is using Google App Engine to host TickTock and other future social games the company is developing., companylogo: ubisoft.png, companypod: App Engine, companytags: App Engine, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jtmqq2010-05-10T18:39:11.857ZRow: 169companyname: UnaWave, companylocation: Los Angeles, California, companydesc: UnaWave is a simple and elegant work management application that works on top of Google Wave and fully leverages its rich real time collaboration and communications framework. UnaWave enables managers, team leaders, and team members to easily assign, track and report on work status, reducing the complexity, cost and effort required to manage short term and long term work assignments., companyurl: www.unawave.com, productdesc: We're using Google Web Toolkit, OpenSocial Gadgets, and the Google Wave real-time communications and collaboration platform to enable customers to create, assign, share and report on tasks for team work-assignments of all types of durations., companylogo: unawave.png, companypod: Wave, companytags: GWT, Wave, Social Web, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jv1b72010-05-10T18:39:11.857ZRow: 170companyname: Unity, companylocation: San Francisco, California, companydesc: Unity Technologies is an emerging market leader in mobile and online 3D technology. It's flagship product Unity is revolutionizing the game industry and beyond, delivering great power, workflows and platform reach.
+, companyurl: www.unity3d.com, productdesc: Unity Android enables developers to create multiplatform 3D games and experiences, while also allowing them to harness the specific strengths of the individual hardware and OS platform., companylogo: unity.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jxug12010-05-10T18:39:11.857ZRow: 171companyname: Ushahidi, companylocation: Orlando, Florida, companydesc: The Ushahidi Platform allows anyone to gather distributed data via SMS, email or web and visualize it on a map or timeline. Our goal is to create the simplest way of aggregating information from the public for use in crisis response., companyurl: www.ushahidi.com, productdesc: We've built an Android-based mobile app that syncs up with our web service; we use Google Maps to display geo-tagged locations and media in our open source platform; and we use Google Geocoding web service to geo-tag text messages in our open source platform., companylogo: ushahidi.png, companypod: Geo, companytags: Geo, Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jo0h22010-05-10T18:39:11.857ZRow: 172companyname: VendAsta, companylocation: Saskatoon, Saskatchewan, Canada, companydesc: VendAsta is sowing trust by making social context portable. With our applications StepRep (for small businesses) and MashedIn (for bloggers and website owners), the networks you've built up on Facebook, Twitter, etc. can extend to you and your social connections all over the web., companyurl: www.vendasta.com, productdesc: We use App Engine to host our core products. We leverage Open Social, Maps, and Social Graph API in our products. In addition, VendAsta uses Google Apps and Analytics extensively for running its core business. VendAsta is the developer of the open source library asynctools, which allows developers to perform App Engine API calls in parallel, most notably datastore queries., companylogo: vendasta.png, companypod: App Engine, companytags: App Engine, Social Web, Google APIs, Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jpf1j2010-05-10T18:39:11.857ZRow: 173companyname: Voxeo, companylocation: Orlando, Florida, companydesc: Voxeo unlocks the value of communications with open standards, disruptive innovation and a passion for problem solving. More than 100,000 developers, 45,000 companies and half of the Fortune 100 use Voxeo to deliver speech-enabled inbound and outbound IVR, call control, conferencing, and SIP-based VoIP applications, including multi-channel applications that span voice, SMS, IM, the mobile web and social networking., companyurl: www.voxeo.com/GoogleIO, productdesc: Voxeo's collaboration and communication platforms can be tightly integrated with Google technologies. Build and deploy rich multi-modal communications applications on Google App Engine. Organize a conference call within Google Wave, then call in with your phone or participate directly from your browser. Embed collaboration and web meeting spaces into any Google Friend Connect site. Or use Google Talk to interact with your users through any application., companylogo: voxeo.png, companypod: Social Web, companytags: Social Web, App Engine, Wave, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jqtlw2010-05-10T18:39:11.857ZRow: 174companyname: Walk Score, companylocation: Seattle, Washington, companydesc: Walk Score measures the walkability of any address and through integrations with real estate sites helps millions of homebuyers find a walkable place to live. We believe walkable neighborhoods are one of the simplest and most effective solutions to halt climate change, improve our health, and strengthen our communities., companyurl: www.walkscore.com, productdesc: The Walk Score API on Google App Engine serves 3 million Walk Scores a day to thousands of real estate partner sites. We also use Google Maps, Google AJAX APIs for geocoding and local search, Django on App Engine to host our site City-Go-Round.com, and we are developing a Transit API and Transit Score on App Engine using public GTFS data from transit agencies around the world., companylogo: walkscore.png, companypod: App Engine, companytags: App Engine, Geo, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/js86d2010-05-10T18:39:11.857ZRow: 175companyname: wave.to, companylocation: Bangor, North Wales, United Kingdom, companydesc: wave.to integrates existing products and services into the Google Wave platform. We're a small dedicated development team who have been involved in extending the platform since its announcement and have integrated Google Wave into social networking, instant messaging, CRM platforms, mobile devices and e-mail. Our e-mail platform Mr-Ray won the Judges Choice award in Mashable's Google Wave API Challenge., companyurl: www.wave.to, productdesc: We use a mixture of Google App Engine and the V8 javascript engine (node.js) to host our services that communicate with the Google Wave servers using the robots and gadget APIs. We are developing clients for our services using the Android SDK to extend them beyond desktop browsers., companylogo: waveto.png, companypod: Wave, companytags: Wave, App Engine, Android, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/f52je2010-05-10T18:39:11.857ZRow: 176companyname: WebFilings, companylocation: Los Altos, California, companydesc: WebFilings delivers a revolution in collaboration software for regulatory compliance — providing the only end-to-end solution to meet SEC reporting requirements. Our intuitive user interface integrates word processing and spreadsheet editing capabilities into a single, rich internet application - streamlining the entire reporting process with features and document controls fine-tuned to the needs of SEC reporting professionals. WebFilings helps companies reduce the time, risk and costs associated with creating and filing SEC reports., companyurl: www.webfilings.com, productdesc: WebFilings uses web services built on the App Engine python SDK. Our use of App Engine and its datastore provides our customers a secure, reliable and scalable platform for their mission-critical SEC report management process., companylogo: webfilings.png, companypod: App Engine, companytags: App Engine, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/f7vo82010-05-10T18:39:11.857ZRow: 177companyname: WHERE, companylocation: Boston, Massachusetts, companydesc: WHERE is a leading local search and recommendation service. WHERE puts the best local information at users’ finger tips with real-time reviews and special offers from local merchants. WHERE is available on over one hundred mobile devices and a leading application on all major smartphone platforms and carriers., companyurl: www.where.com, productdesc: We're using the Android SDK to build the WHERE mobile application for Android. , companylogo: where.png, companypod: Android, companytags: Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/f9a8p2010-05-10T18:39:11.857ZRow: 178companyname: WithWaves, companylocation: Seattle, Washington, companydesc: WithWaves is the exploration of Google Wave utilizing technology developed under the Streamd.in umbrella. WithWaves is a small team of developers focusing on providing simple collaborative applications and tools. We have developed http://streamd.in, amazon.withwaves.com, ebay.withwaves.com and run the withwaves.com blog, all of them with support to run standalone and as gadgets in the Google Wave ecosystem., companyurl: www.withwaves.com, productdesc: We use Google Wave to deploy our robot and gadgets and App Engine to host most of them. We also use the Google Maps API V.3 in our latest project http://streamd.in., companylogo: withwaves.png, companypod: Wave, companytags: Wave, App Engine, Geo, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ezg9q2010-05-10T18:39:11.857ZRow: 179companyname: WorkSmart Labs, companylocation: New York, New York, companydesc: The WorkSmart Labs mission is to use technology to make fitness more motivating and effective. We make CardioTrainer mobile wellness software on the Android, as well as provide many gym entertainment solutions., companyurl: www.worksmartlabs.com, productdesc: We are using the Android SDK and a large set of other Google technologies (Protocol Buffers, Build Tools, KML, etc) to create our mobile applications and website., companylogo: worksmartlabs.png, companypod: Android, companytags: Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/f0uu72010-05-10T18:39:11.857ZRow: 180companyname: WOT, companylocation: Helsinki, Finland, companydesc: Web of Trust is a community-based safe surfing tool that uses an intuitive traffic-light style rating system to help you avoid risky sites when you search, browse and shop online., companyurl: www.mywot.com, productdesc: We develop extensions for Google Chrome., companylogo: wot.png, companypod: Chrome, companytags: Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/f29ek2010-05-10T18:39:11.857ZRow: 181companyname: Yahoo! Inc., companylocation: Sunnyvale, California, companydesc: Yahoo! Inc. offers a robust set of developer tools, APIs, and resources through the Yahoo! Developer Network., companyurl: www.yahoo.com, productdesc: The Yahoo! Application Platform (YAP) provides an OpenSocial container on the Yahoo! homepage and is the largest user of the Google Caja project. Yahoo! uses iGoogle as a canvas to publish Yahoo! user updates through its Updates API and integrates numerous open Google APIs and feeds within the Yahoo! Query Language (YQL). Yahoo! is a charter member of the OpenSocial Foundation, on the Board of OpenID, and a key contributor to the OAuth spec., companylogo: yahoo.png, companypod: Social Web, companytags: Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/f3nz12010-05-10T18:39:11.857ZRow: 182companyname: Yelp, companylocation: San Francisco, California, companydesc: Yelp is a website that connects people with great local businesses in the US, Canada, UK and Ireland. Yelpers have written more than 10 million local reviews, making Yelp a local guide for real word-of-mouth on everything from boutiques and mechanics to restaurants and dentists.
+, companyurl: www.yelp.com, productdesc: Yelp for Android helps you find great businesses around you. Using location services, Google Maps, shared Intents and a host of other features in the Android SDK, the app delivers a simple-yet-powerful Yelp experience on Android devices.
+, companylogo: yelp.png, companypod: Android, companytags: Android, Geo, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/fgb2q2010-05-10T18:39:11.857ZRow: 183companyname: Zillow, companylocation: Seattle, Washington, companydesc: Zillow.com is an online real estate marketplace where homeowners, buyers, sellers, renters, real estate agents and mortgage professionals find and share vital information about homes and mortgages.
+, companyurl: www.zillow.com, productdesc: Zillow.com uses Google Maps in our mobile applications, including our Android application. Zillow.com uses Google Street View on our main website as well as in our Android application, companylogo: zillow.png, companypod: Android, companytags: Android, Geo, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/fhpn72010-05-10T18:39:11.857ZRow: 184companyname: Zoho, companylocation: Pleasanton, California, companydesc: Zoho offers a broad suite of Online Business, Productivity & Collaboration Applications for Small and Medium Organizations. , companyurl: www.zoho.com, productdesc: We use a number of Google products, including Google Web Toolkit. We also build extensions for Chrome, solutions for Google Apps, OpenSocial gadgets, and Google OpenID/OAuth., companylogo: zoho.png, companypod: Enterprise, companytags: Enterprise, GWT, Social Web, Chrome, adddate: x
\ No newline at end of file
diff --git a/assets/cache-sessions.xml b/assets/cache-sessions.xml
new file mode 100644
index 0000000..10ba965
--- /dev/null
+++ b/assets/cache-sessions.xml
@@ -0,0 +1,32 @@
+http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic2010-05-10T18:39:11.857Zsessionschristine.leechristine.lee@gmail.com821http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cokwr2010-05-10T18:39:11.857ZRow: 2sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 6, product: Android, track: Android, sessiontype: 101, sessiontitle: A beginner's guide to Android, tags: Android, Mobile, Java, sessionspeakers: Reto Meier, speakers: retomeier, sessionabstract: This session will introduce some of the basic concepts involved in Android development. Starting with an overview of the SDK APIs available to developers, we will work through some simple code examples that explore some of the more common user features including using sensors, maps, and geolocation., sessionrequirements: Proficiency in Java and a basic understanding of embedded environments like mobile phones , sessionlink: beginners-guide-android, sessionhashtag: #android1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.45, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B-Xhdu7ZkBHw, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B-Xhdu7ZkBHw, waveid: w+-Xhdu7ZkBHwhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ckd7g2010-05-10T18:39:11.857ZRow: 3sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: Writing real-time games for Android redux, tags: Android, Mobile, Java, Games, 2D, 3D, C++, sessionspeakers: Chris Pruett, speakers: pruett, sessionabstract: This session is a crash course in Android game development: everything you need to know to get started writing 2D and 3D games, as well as tips, tricks, and benchmarks to help your code reach optimal performance. In addition, we'll discuss hot topics related to game development, including hardware differences across devices, using C++ to write Android games, and the traits of the most popular games on Market., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: writing-real-time-games-android, sessionhashtag: #android2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.9B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BWDgRyLZkaR, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BWDgRyLZkaR, waveid: w+WDgRyLZkaRhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/clrrx2010-05-10T18:39:11.857ZRow: 4sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: The world of ListView, tags: Android, Mobile, Java, sessionspeakers: Romain Guy, Adam Powell, speakers: romainguy, adamp, sessionabstract: ListView is one of the most widely used Android widgets but also the most complex one. Join us to learn how to master ListView and learn all about its features, optimizations, quirks and limitations.
+, sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: world-of-listview-android, sessionhashtag: #android3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.47, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv7BXJrZkBJT, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv7BXJrZkBJT, waveid: w+v7BXJrZkBJThttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d180g2010-05-10T18:39:11.857ZRow: 5sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: Casting a wide net: how to target all Android devices, tags: Android, Mobile, Java, sessionspeakers: Justin Mattson, speakers: jmatt, sessionabstract: One of Android's strengths is its flexibility to run on a wide variety of devices. In this session, we will explore the facilities the Android resource system provides to developers to make supporting many devices from one application binary easier, as well as common pitfalls. In addition to hardware heterogeneity, more than one version of Android may exist in the wild at any given time. We will go over strategies for providing cross-version compatibility., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: casting-wide-net-android-devices, sessionhashtag: #android4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.48, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BfZ2urbZk7o, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BfZ2urbZk7o, waveid: w+fZ2urbZk7ohttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d2mkx2010-05-10T18:39:11.857ZRow: 6sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: Android UI design patterns, tags: Android, Mobile, Java, sessionspeakers: Chris Nesladek, German Bauer, Richard Fulcher, Christian Robertson, Jim Palmer, speakers: cnesladek, germanb, rfulcher, robertsonc, jimpalmer, sessionabstract: In this session, the Android User Experience team will show the types of patterns you can use to build a great Android application. We'll cover things like how to use Interactive Titlebars, Quick Contacts, and Bottom bars as well some new patterns which will get an I/O-only preview. The team will be also available for a no holds barred Q&A session., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: android-ui-design-patterns, sessionhashtag: #android5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.49, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkP1, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkP1, waveid: w+e6j2obZkP1http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cssly2010-05-10T18:39:11.857ZRow: 7sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 6, product: Android, track: Android, sessiontype: 301, sessiontitle: Developing Android REST client applications, tags: Android, Mobile, Java, sessionspeakers: Virgil Dobjanschi, speakers: virgild, sessionabstract: This session will present architectural considerations for developing RESTful applications on the Android platform. It focuses on design patterns, platform integration and performance issues specific to the Android platform.
+, sessionrequirements: Advanced knowledge of Java & Android concepts, sessionlink: developing-RESTful-android-apps, sessionhashtag: #android6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6C, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkNc, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkNc, waveid: w+e6j2obZkNchttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cu76f2010-05-10T18:39:11.857ZRow: 8sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: 6, product: Android, track: Android, sessiontype: 301, sessiontitle: A JIT Compiler for Android's Dalvik VM, tags: Android, Mobile, Java, sessionspeakers: Ben Cheng, Bill Buzbee, speakers: bccheng, buzbee, sessionabstract: In this session we will outline the design of a JIT Compiler suitable for embedded Android devices. Topics will include an architectural overview, the rationale for design decisions and the special support for JIT verification, testing and tuning., sessionrequirements: Advanced knowledge of Java & Android concepts, sessionlink: jit-compiler-androids-dalvik-vm, sessionhashtag: #android7, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6D, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkRw, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkRw, waveid: w+e6j2obZkRwhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cvlqs2010-05-10T18:39:11.857ZRow: 9sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 3, product: App Engine, track: App Engine, sessiontype: 201, sessiontitle: Appstats - RPC instrumentation and optimizations for App Engine, tags: App Engine, Python, Java, sessionspeakers: Guido van Rossum, speakers: guido, sessionabstract: Appstats is a pure userland library (for Python and Java) that inserts instrumentation hooks into the App Engine runtime at the interface between the runtime and services like the datastore. The collected statistics can be browsed in a rich UI which allows drilling down to various levels of detail. The talk will also discuss common optimizations to address typical findings., sessionrequirements: Intermediate knowledge of App Engine (Python or Java), sessionlink: appstatsrpc-appengine, sessionhashtag: #appengine1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.4A, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BeRiTZrZkC22, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BeRiTZrZkC22, waveid: w+eRiTZrZkC22http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/dcgjs2010-05-10T18:39:11.857ZRow: 10sessiondate: Thursday May 20, sessiontime: 4:45pm-5:45pm, room: 3, product: App Engine, track: App Engine, sessiontype: 301, sessiontitle: Next gen queries, tags: App Engine, Datastore, Query, Cursor, Zigzag Merge Join, sessionspeakers: Alfred Fuller, speakers: arfuller, sessionabstract: This session will discuss the design and implications of improvements to the Datastore query engine including support for AND, OR and NOT query operators, the solution to exploding indexes and paging backwards with Cursors. Specific technologies discussed will be an improved zigzag merge join algorithm, a new extensible multiquery framework (with geo-query support) and a smaller more versatile Cursor design., sessionrequirements: Intermediate knowledge of Google App Engine, specifically the datastore., sessionlink: next-gen-queries-appengine, sessionhashtag: #appengine10, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.71, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BY_40V7ZkB3Z, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BY_40V7ZkB3Z, waveid: w+Y_40V7ZkB3Zhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ddv492010-05-10T18:39:11.857ZRow: 11sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 3, product: App Engine, track: App Engine, sessiontype: 201, sessiontitle: Data migration in App Engine, tags: App Engine, Data Migration, sessionspeakers: Matthew Blain, speakers: mblain, sessionabstract: Learn about the App Engine bulk loader and see an example of migrating data from an external data source into the app engine datastore--and back out. Do you have data stored in a traditional, relational DB which you'd like to upload to App Engine? This session will teach you how., sessionrequirements: Basic knowledge of Google App Engine and database concepts., sessionlink: data-migration-appengine, sessionhashtag: #appengine4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.4D, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BeRiTZrZkC29, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BeRiTZrZkC29, waveid: w+eRiTZrZkC29http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d415a2010-05-10T18:39:11.857ZRow: 12sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: 3, product: App Engine, track: App Engine, sessiontype: 201, sessiontitle: What's hot in Java for App Engine, tags: App Engine, Java, sessionspeakers: Toby Reyelts, Don Schwarz, speakers: tobyr, schwardo, sessionabstract: Learn what's new with Java on App Engine. We'll take a whirlwind tour through the changes since last year, walk through a code sample for task queues and the new blobstore service, and demonstrate techniques for improving your application's performance. We'll top it off with a glimpse into some new features that we've planned for the year ahead., sessionrequirements: Intermediate knowledge of App Engine for Java, sessionlink: whats-hot-in-java-for-app-engine, sessionhashtag: #appengine6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6E, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BO4sGNLZkCGh, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BO4sGNLZkCGh, waveid: w+O4sGNLZkCGhhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d5fpr2010-05-10T18:39:11.857ZRow: 13sessiondate: Thursday May 20, sessiontime: 1:00pm-2:00pm, room: 3, product: App Engine, track: App Engine, sessiontype: 301, sessiontitle: Building high-throughput data pipelines with Google App Engine, tags: App Engine, Data processing, Mapping on data, sessionspeakers: Brett Slatkin, speakers: bslatkin, sessionabstract: This session will cover how to build, test, and maintain large-scale data pipelines on Google App Engine. It will cover maximizing efficiency, productionization, and how to deal with changing requirements., sessionrequirements: Advanced knowledge of Google App Engine, sessionlink: high-throughput-data-pipelines-appengine, sessionhashtag: #appengine7, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6F, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BO4sGNLZkCHO, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BO4sGNLZkCHO, waveid: w+O4sGNLZkCHOhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/df9om2010-05-10T18:39:11.857ZRow: 14sessiondate: Thursday May 20, sessiontime: 3:30pm-4:30pm, room: 3, product: App Engine, track: App Engine, sessiontype: 201, sessiontitle: Testing techniques for Google App Engine, tags: App Engine, Unit Testing, sessionspeakers: Max Ross, speakers: maxr, sessionabstract: Google App Engine is designed to help developers more easily create and manage scalable web applications. But what about testing? We typically write tests under the assumption that our development stack closely resembles our production stack, so what do we do when our target environment only lives in the cloud? In this talk, we will highlight the key differences between typical testing techniques and Google App Engine testing techniques. We will also present concrete strategies for testing against local and cloud-based implementations of App Engine services like the datastore and the task queue. Finally, we will explain how to use App Engine as a highly parallel test harness that runs existing test suites without modification., sessionrequirements: Intermediate knowledge of Google App Engine and unit testing concepts., sessionlink: testing-techniques-app-engine, sessionhashtag: #appengine9, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.70, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BeRiTZrZkCzy, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BeRiTZrZkCzy, waveid: w+eRiTZrZkCzyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/dgo932010-05-10T18:39:11.857ZRow: 15sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 5, product: Chrome Dev Tools, track: Chrome, sessiontype: 101, sessiontitle: Google Chrome's Developer Tools, tags: Chrome, sessionspeakers: Pavel Feldman, Anders Sandholm, speakers: pfeldman, sandholm, sessionabstract: In this session we'll give an overview of Developer Tools for Google Chrome that is a part of the standard Chrome distribution. Chrome Developer Tools allow inspecting, debugging and tuning the web applications and many more. In addition to this overview we would like to share some implementation details of the Developer Tools features and call for your contribution., sessionrequirements: Web developer background, sessionlink: chrome-developer-tools, sessionhashtag: #chrome1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.4E, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv_K9zbZkBY_, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv_K9zbZkBY_, waveid: w+v_K9zbZkBY_http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/djhdx2010-05-10T18:39:11.857ZRow: 16sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 5, product: Chrome, track: Chrome, sessiontype: 201, sessiontitle: Beyond JavaScript: programming the web with native code, tags: Chrome, Native Client, sessionspeakers: Dave Springer, Ian Lewis, speakers: dspringer, ilewis, sessionabstract: Although JavaScript performance is rapidly increasing, there are still applications for which native code is a better choice. Learn about Native Client and how you can use it to build rich applications with all of the advantages and power of the web., sessionlink: native-code-chrome, sessionhashtag: #chrome2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.4F, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkKS, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkKS, waveid: w+e6j2obZkKShttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/dyxo82010-05-10T18:39:11.857ZRow: 17sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 5, product: Chrome OS, track: Chrome, sessiontype: 201, sessiontitle: Developing With HTML5, tags: HTML5, Chrome, Chrome OS, sessionspeakers: Mihai Ionescu, Arne Roomann-Kurrik, speakers: mihai, kurrik, sessionabstract: This session covers the HTML5 APIs available to Google Chrome and Google Chrome OS applications and Google Chrome extensions. Learn how to design web applications for a Google Chrome OS netbook using the latest web technologies., sessionrequirements: Familiarity with HTML and JavaScript, sessionlink: developing-with-html5, sessionhashtag: #chrome3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.51, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQ9WoGLZkBlw, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQ9WoGLZkBlw, waveid: w+Q9WoGLZkBlwhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/c4wx52010-05-10T18:39:11.857ZRow: 18sessiondate: Thursday May 20, sessiontime: 1:00pm-2:00pm, room: 5, product: Chrome, track: Chrome, sessiontype: 101, sessiontitle: Chrome Extensions - how-to, tags: Chrome, Chrome Extensions, sessionspeakers: Brian Kennish, speakers: bkennish, sessionabstract: Google Chrome shipped an extensions API in version 4.0. Since last year, new capabilites have been added to the extensions framework, and many people have already written powerful extensions with minimal effort. Find out how to write an extension, and what's coming next in Chrome Extensions., sessionrequirements: Familiarity with HTML and JavaScript, sessionlink: chrome-extensions, sessionhashtag: #chrome6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.72, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BetojYbZkBGj, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BetojYbZkBGj, waveid: w+etojYbZkBGjhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/drwu72010-05-10T18:39:11.857ZRow: 19sessiondate: Thursday May 20, sessiontime: 2:15pm-3:15pm, room: 5, product: Google Chrome Frame, track: Chrome, sessiontype: 201, sessiontitle: Using Google Chrome Frame, tags: HTML5, Chrome, Chrome Frame, sessionspeakers: Alex Russell, speakers: slightlyoff, sessionabstract: Google Chrome Frame brings the HTML5 platform and fast Javascript performance to IE6, 7 & 8. This session will cover the latest on Google Chrome Frame, what it can do for you and your customers, how it can be used, and a sneak peak into what's planned next., sessionrequirements: Web developer backgrounds, as well as enterprise IT, sessionlink: using-chrome-frame, sessionhashtag: #chrome7, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.73, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B9PNCdLZkBxY, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B9PNCdLZkBxY, waveid: w+9PNCdLZkBxYhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/dtbek2010-05-10T18:39:11.857ZRow: 20sessiondate: Thursday May 20, sessiontime: 4:45pm-5:45pm, room: 5, product: Chrome, track: Chrome, sessiontype: 201, sessiontitle: HTML5 status update, tags: HTML5, Chrome, sessionspeakers: Ian Fette, Jeff Chang, speakers: ifette, jeffreyc, sessionabstract: Where is HTML5 today? What new features are now broadly supported, what features are on the horizon, and what features are on the chopping block? How do we decide what to implement, what to propose, and what to drop? Find out., sessionrequirements: None, sessionlink: html5-status-chrome, sessionhashtag: #chrome8, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.75, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B9PNCdLZkBxf, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B9PNCdLZkBxf, waveid: w+9PNCdLZkBxfhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/dupz12010-05-10T18:39:11.857ZRow: 21sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 8, product: Apps Marketplace, Enterprise, track: Enterprise, sessiontype: 201, sessiontitle: Reach new customers fast: Learn how to sell your cloud app on the Google Apps Marketplace, tags: Enterprise, Google Apps, Customization, Apps Marketplace, ISV, SaaS, sessionspeakers: Scott McMullan, Jay Simmons (Atlassian), Chuck Dietrich (Sliderocket), Amit Kulkarni (Manymoon), speakers: scottmc, sessionabstract: In this introductory session we'll provide an overview of the Google Apps Marketplace and learn product and marketing best practices directly from 3 Marketplace ISVs., sessionrequirements: None, sessionlink: reach-new-customers-apps-marketplace, sessionhashtag: #enterprise1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.52, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BkrgNvLZkBkL, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BkrgNvLZkBkL, waveid: w+krgNvLZkBkLhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/e35dj2010-05-10T18:39:11.857ZRow: 22sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm
+, room: 4, product: Enterprise, track: Enterprise, sessiontype: 101, sessiontitle: Making Freemium work - converting free users to paying customers, tags: Enterprise, Monetization, Google Apps, SaaS, VC, sessionspeakers: Don Dodge (moderator), Brad Feld, Dave McClure, Jeff Clavier, Matt Holleran, Joe Kraus, speakers: dondodge, sessionabstract: A panel of prominent venture capital leaders will help you understand how to build free apps that can be upgraded to paid & how to build products that can be profitable., sessionrequirements: None, sessionlink: freemium-vc-panel-enterprise, sessionhashtag: #enterprise2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.53, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BY_40V7ZkB7J, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BY_40V7ZkB7J, waveid: w+Y_40V7ZkB7Jhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/e4jxw2010-05-10T18:39:11.857ZRow: 23sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 5, product: Apps Marketplace, Enterprise, track: Enterprise, sessiontype: 201, sessiontitle: Integrating your app with the Google Apps Marketplace: Navigation, SSO, Data APIs and manifests, tags: Enterprise, SSO, APIs, Apps Marketplace, ISV, SaaS, sessionspeakers: Ryan Boyd, Steve Bazyl, speakers: ryanboyd, sbazyl, sessionabstract: In this fast-paced, demo-focused session, you'll learn how to build, integrate, and sell a web app on the Google Apps Marketplace. We'll go end-to-end in 40 minutes with time left for Q&A., sessionrequirements: None, sessionlink: integrating-cloud-app-with-google-apps, sessionhashtag: #enterprise3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.54, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BTeHZ97ZkBAg, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BTeHZ97ZkBAg, waveid: w+TeHZ97ZkBAghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/e5yid2010-05-10T18:39:11.857ZRow: 24sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 7, product: Enterprise, track: Enterprise, sessiontype: 201, sessiontitle: Customizing Google Apps & integrating with customer environments, tags: Enterprise, Google Apps, Customization, sessionspeakers: Mike O'Brien, Matt Pruden (Appirio), Adam Graff (Genentech), Don Dodge (moderator), sessionabstract: Learn from real life examples of customizing Google Apps to meet customer requirements. Hear from the customer (Genentech) and the System Integrator (Appirio). Explore integration issues and deployment best practices with people who have done it. Get your questions answered in this session., sessionrequirements: None, sessionlink: customizing-google-apps-partners-customers, sessionhashtag: #enterprise4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.55, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BSVy-sLZkBYe, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BSVy-sLZkBYe, waveid: w+SVy-sLZkBYehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cg5gh2010-05-10T18:39:11.857ZRow: 25sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: 4, product: Docs API, Sites API, Secure Data Connector, SDC, track: Enterprise, sessiontype: 201, sessiontitle: Connecting your enterprise applications with Google Docs and Sites, tags: Enterprise, Docs API, Sites API, Apps, Google Data APIs, Secure Data Connector, SDC, sessionspeakers: Eric Bidelman, Vijay Bangaru, Matthew Tonkin (Memeo Inc), speakers: ericbidelman, vijayb
+, sessionabstract: Learn how your organization can harness the power of Google Docs and Sites directly from within your existing enterprise systems using our extensive APIs. Integrate with data from behind the firewall using Secure Data Connector. Upload, share, collaborate, and sync any file to Docs. Even automate the creation of project and team workspaces in a single click in Sites from within your CRM., sessionrequirements: Knowledge of Google Data APIs and top-level understanding of Docs and Sites, sessionlink: connecting-enterprise-apps-docs-sites, sessionhashtag: #enterprise6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.77, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bo01RhLZkBGa, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bo01RhLZkBGa, waveid: w+o01RhLZkBGahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/eilm22010-05-10T18:39:11.857ZRow: 26sessiondate: Thursday May 20, sessiontime: 2:15pm-3:15pm, room: 4, product: Enterprise, track: Enterprise, sessiontype: 201, sessiontitle: Launch your app inside of Google Apps with gadgets, tags: Gadgets, Enterprise, Google Apps, Gmail, Calendar, Sites, sessionspeakers: Dan Holevoet, speakers: danielholevoet, sessionabstract: Gadgets represent a valuable opportunity to get in front of the many Google Apps users who use Gmail, Google Calendar, and Google Sites throughout the day. This session will talk about how you can write gadgets as natural extensions of your existing products and take advantage of the unique opportunities available to gadgets in Google Apps., sessionrequirements: Basic familiarity with gadgets and/or HTML/JS., sessionlink: launch-your-app-google-apps-gadgets, sessionhashtag: #enterprise7, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.78, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BSVy-sLZkBX4, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BSVy-sLZkBX4, waveid: w+SVy-sLZkBX4http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ek06j2010-05-10T18:39:11.857ZRow: 27sessiondate: Thursday May 20, sessiontime: 3:30pm-4:30pm, room: 4, product: Google Apps Script, track: Enterprise, sessiontype: 201, sessiontitle: Scripting Google Apps for business process automation, tags: Apps Script, Enterprise, Google Apps, Spreadsheets, Calendar, Sites, Document List, sessionspeakers: Evin Levey, speakers: evin, sessionabstract: Learn how to use Google Apps for business process automation, and custom work-flow. We'll introduce the powerful scripting service along with several easy-to-use interfaces including Spreadsheets, Calendar, Sites and the Document List. We'll also demonstrate interoperability with third party web services and showcase exciting new developments in Google Apps Script., sessionrequirements: Basic knowledge of Google Apps, and an interest in learning how to automate processes using scripts., sessionlink: scripting-google-apps-for-business-process-automation, sessionhashtag: #enterprise8, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.79, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv7BXJrZkBOq, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv7BXJrZkBOq, waveid: w+v7BXJrZkBOqhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/eleqw2010-05-10T18:39:11.857ZRow: 28sessiondate: Thursday May 20, sessiontime: 4:45pm-5:45pm, room: 4, product: Enterprise, Google Apps, track: Enterprise, sessiontype: 201, sessiontitle: Building context-aware extensions for Gmail - Deep dive on Gmail contextual gadgets, tags: Enterprise, Google Apps, Gadgets, Gmail, ISV, SaaS, sessionspeakers: Dan Holevoet, speakers: danielholevoet, sessionabstract: How much time do your users spend in email everyday? Wouldn't it be nice if you could seamlessly integrate your apps into the rich context offered by their email and allow them to avoid shifting to new applications for various tasks? Gmail contextual gadgets allow you to register regular expressions and insert gadgets into e-mail messages based on their content. In this session, you'll learn how to create and distribute these powerful gadgets., sessionrequirements: Basic familiarity with gadgets and/or HTML/JS., sessionlink: deep-dive-gmail-contextual-gadgets, sessionhashtag: #enterprise9, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.7A, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B9PNCdLZkBt_, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B9PNCdLZkBt_, waveid: w+9PNCdLZkBt_http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/eedwv2010-05-10T18:39:11.857ZRow: 29sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 3, product: App Engine, track: Enterprise, App Engine, sessiontype: 201, sessiontitle: Run corporate applications on Google App Engine? Yes we do., tags: Enterprise, SaaS, PaaS, Hosting, App Engine, Java, sessionspeakers: Ben Fried, Irwin Boutboul, Justin McWilliams, Matthew Simmons, speakers: bf, irwin, ogle, mpsimmon , sessionabstract: Come hear Google's CIO Ben Fried and his team of engineers describe how Google builds real-world applications on App Engine. If you're interested in building corporate applications that run on Google's cloud, this team has been doing exactly that using App Engine, SDC, GWT, Django, Closure, Google Analytics, Bulkloader, cron server, and friends. In this session, you'll hear usable recipes for uniting Google's cloud technologies with firewalls, legacy databases, proprietary servers, legacy systems, and other realities in the life of an IT developer and learn how these teams have been able to respond more quickly to business needs while reducing operational burden. , sessionrequirements: None, sessionlink: run-corp-apps-on-app-engine, sessionhashtag: #appengine2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.4B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bo01RhLZkBJ0, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bo01RhLZkBJ0, waveid: w+o01RhLZkBJ0http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/efsh82010-05-10T18:39:11.857ZRow: 30sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 3, track: Enterprise, App Engine, sessiontype: 201, sessiontitle: It’s 2010: How is your move to the cloud doing?, tags: Enterprise, Saas, Paas, Hosting, App Engine, Google Apps, Java, sessionspeakers: David Glazer, speakers: dglazer, sessionabstract: Come discover the latest innovations from Google enabling IT and ISV developers to build on Google’s cloud-based storage and computing offerings. This talk will give a complete overview of Google’s commercial developer products and provide insights and best practices so enterprise developers can take more advantage of the cloud., sessionrequirements: None, sessionlink: its-2010-move-to-the-cloud, sessionhashtag: #appengine3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.4C, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BtaQlR7ZkBIS, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BtaQlR7ZkBIS, waveid: w+taQlR7ZkBIShttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/eh71p2010-05-10T18:39:11.857ZRow: 31sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 1, product: Auth, Google Data Protocol, OpenID, SSO, track: Enterprise, Google APIs, sessiontype: 201, sessiontitle: OpenID-based single sign on and OAuth data access for Google Apps, tags: Authentication, Google Data APIs, OAuth, AuthSub, Enterprise, OpenID, SSO, sessionspeakers: Ryan Boyd, David Primmer, speakers: ryanboyd, primmer, sessionabstract: A discussion of all the auth tangles you've encountered so far -- OpenID, SSO, 2-Legged OAuth, 3-Legged OAuth, and Hybrid OAuth. We'll show you when and where to use the APIs, code some example apps, and demonstrate how they all integrate with Google APIs and other developer products. We'll also talk about how these technologies relate to apps sold on the Google Apps Marketplace., sessionrequirements: None, sessionlink: untangling-auth-enterprise, sessionhashtag: #enterprise5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.76, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BY_40V7ZkB0w, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BY_40V7ZkB0w, waveid: w+Y_40V7ZkB0whttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bwhin2010-05-10T18:39:11.857ZRow: 32sessiondate: Thursday May 20, sessiontime: 4:45pm-5:45pm, room: 6, product: Android, track: Fireside Chats, Android, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the Android team, tags: Android, Mobile, Java, sessionspeakers: The Android team with Chris DiBona moderating, sessionabstract: Pull up a chair and join the Android team at Google for a fireside chat. It's your opportunity to ask us about the platform and to tell us where you'd like to see it go in the future., sessionlink: fireside-chat-android-team, sessionhashtag: #fireside-android, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.93, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv7BXJrZkBPC, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv7BXJrZkBPC, waveid: w+v7BXJrZkBPChttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bxw302010-05-10T18:39:11.857ZRow: 33sessiondate: Thursday May 20, sessiontime: 1:00pm-2:00pm, room: 8, product: Android, track: Fireside Chats, Android, sessiontype: Fireside Chat, sessiontitle: Fireside chat with Android handset manufacturers, tags: Android, Mobile, Java, OHA, Open Handset Alliance, sessionspeakers: Dino Brusco (Motorola), Erik Hellman (Sony Ericsson), Joon Kang (LGE), Ciaran Rochford (Samsung), Eric Chu (Google; moderator), sessionabstract: Come join us for a fireside chat with the top Android handset manufacturers. Hear about the types of devices being planned for 2010 and get your device-specific questions answered., sessionlink: fireside-chat-android-handset-manufacturers, sessionhashtag: #fireside-androidhandset, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.92, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQhu3aLZknM, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQhu3aLZknM, waveid: w+Qhu3aLZknMhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bv2y62010-05-10T18:39:11.857ZRow: 34sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: Fireside Chat Room, product: App Engine, track: Fireside Chats, App Engine, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the App Engine team, tags: App Engine, sessionspeakers: Brett Slatkin, Guido van Rossum, Matt Blain, Max Ross, Don Schwarz, Alfred Fuller, Kevin Gibbs, Sean Lynch
+, speakers: bslatkin, guido, mblain, maxr, schwardo, arfuller, kgibbs, slynch, sessionabstract: It's been an busy year for the App Engine team with lots of new features and lots of new developers. Come tell us about what you've loved and what still bugs you. With several members of the App Engine team on deck, you'll get the answers to your questions straight from the source., sessionlink: fireside-chat-app-engine, sessionhashtag: #fireside-appengine, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6A, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B9PNCdLZkBuX, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B9PNCdLZkBuX, waveid: w+9PNCdLZkBuXhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/etu5e2010-05-10T18:39:11.857ZRow: 35sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: Fireside Chat Room, product: Chrome, Chrome OS, track: Fireside Chats, Chrome, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the Google Chrome and Chrome OS teams, tags: Chrome, Chrome OS, sessionspeakers: Ian Fette, Brian Rakowski, Linus Upson, Caesar Sengupta, Matt Papakipos, speakers: ifette, brakowski, linus, caesars, papakipos, sessionabstract: Curious about what's new in Google Chrome, or what makes Google Chrome OS so exciting? We'll talk briefly about the major developments over the past year, and then field questions from the audience. If you're dying to know something, this is the place to find an answer., sessionlink: fireside-chat-chrome-chrome-os, sessionhashtag: #fireside-chrome, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.69, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B1g8YLbZkBFj, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B1g8YLbZkBFj, waveid: w+1g8YLbZkBFjhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ceqw02010-05-10T18:39:11.857ZRow: 36sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: Fireside Chat Room, product: Enterprise, Apps Marketplace, track: Fireside Chats, Enterprise, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the Enterprise team, tags: Enterprise, Google Apps, Apps Marketplace, sessionspeakers: Chris Vander Mey, Scott McMullan, Ryan Boyd, David Glazer, Ken Lin, speakers: cvandermey, scottmc, dglazer, ryanboyd, kenlin, sessionabstract: With the launch of the Google Apps Marketplace, we've introduced a new way to expose your software to businesses - and a new way to extend Google Apps. If you're interested in building apps, what we're thinking about, or if you have other questions about the Marketplace, pull up a chair., sessionlink: fireside-chat-enterprise-apps, sessionhashtag: #fireside-enterprise, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B1g8YLbZkBBh, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B1g8YLbZkBBh, waveid: w+1g8YLbZkBBhhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cdcbn2010-05-10T18:39:11.857ZRow: 37sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: Fireside Chat Room, product: Geo, track: Fireside Chats, Geo, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the Geo team, tags: Geo, Earth, Maps, Maps API, Maps Data API, Earth API, sessionspeakers: Thor Mitchell, Peter Birch, Matt Holden, Ben Appleton, Bart Locanthi, Thatcher Ulrich, speakers: thor, pbirch, mholden, appleton, bnl, tulrich, sessionabstract: Here's your opportunity to pick the brains of the people behind the Maps, Earth, and Maps Data APIs! We'll take a quick walk through the milestones of the last year, and then open it up to your questions. Don't miss your opportunity to get the straight scoop on all that's new in the world of Google Geo APIs., sessionlink: fireside-chat-geo, sessionhashtag: #fireside-geo, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.67, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BY_40V7ZkB74, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BY_40V7ZkB74, waveid: w+Y_40V7ZkB74http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ev8pv2010-05-10T18:39:11.857ZRow: 38sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: Fireside Chat Room, product: GWT, track: Fireside Chats, GWT, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the GWT team, tags: GWT, Google Web Toolkit, Java, JavaScript, AJAX, sessionspeakers: Bruce Johnson, Joel Webber, Ray Ryan, Amit Manjhi, Jaime Yap, Kathrin Probst, Eric Ayers, speakers: bruce, jgw, rjrjr, amitmanjhi, jaimeyap, kprobst, zundel, sessionabstract: If you're interested in what the GWT team has been up to since 2.0, here's your chance. We'll have several of the core engineers available to discuss the new features and frameworks in GWT, as well as to answer any questions that you might have., sessionlink: fireside-chat-gwt, sessionhashtag: #fireside-gwt, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.91, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQhu3aLZknM, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQhu3aLZknM, waveid: w+Qhu3aLZknMhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ewna82010-05-10T18:39:11.857ZRow: 39sessiondate: Thursday May 20, sessiontime: 2:15pm-3:15pm, room: Fireside Chat Room, product: Social, Open Web, Buzz, track: Fireside Chats, Social Web, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the Social Web team, tags: Social, Social Web, Buzz, sessionspeakers: David Glazer, DeWitt Clinton, John Panzer, Joseph Smarr, Sami Shalabi, Todd Jackson, speakers: dglazer, dewitt, jpanzer, jsmarr, shalabi, toddj, sessionabstract: Social is quickly becoming an integral part of how we experience the web, and this is your chance to pick the brains of the people who are working on Buzz, the Buzz API and the underlying open protocols such as Activity Streams and OAuth which are an essential component of a truly open & social web., sessionlink: fireside-chat-social-web, sessionhashtag: #fireside-socialweb, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.94, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BY_40V7ZkB1C, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BY_40V7ZkB1C, waveid: w+Y_40V7ZkB1Chttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ey1up2010-05-10T18:39:11.857ZRow: 40sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: Fireside Chat Room, product: Google Wave, track: Fireside Chats, Wave, sessiontype: Fireside Chat, sessiontitle: Fireside chat with the Google Wave team, tags: Wave, sessionspeakers: Lars Rasmussen, Douwe Osinga, Jochen Bekmann, Alan Green, Pamela Fox, Dan Peterson, Stephanie Hannon, sessionabstract: Join the Google Wave team around the campfire to chat about all things Wave: the product, the API platform, and the wave federation protocol. Come to learn about the new Wave API features, get tips on how to build the best extensions, discuss how to take advantage of the open source code available and hear more about what users are doing with the product. This is an excellent opportunity to ask the engineering team questions directly, and learn more about where Wave is heading., sessionrequirements: None, sessionlink: fireside-chat-wave, sessionhashtag: #fireside-wave, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.9F, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BsErKcbZkjh.4, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BsErKcbZkjh.4, waveid: w+sErKcbZkjh.4http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/epmg72010-05-10T18:39:11.857ZRow: 41sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 4, product: Maps API, track: Geo, sessiontype: 201, sessiontitle: Stepping up: Porting v2 JavaScript Maps API applications to v3, tags: Maps, Maps API, JavaScript, sessionspeakers: Daniel Lee, speakers: daniellee, sessionabstract: The JavaScript Maps API v3 is the future of the Google Maps API. To take advantage of the many great features coming to the API you will need to migrate existing v2 applications to v3. This session will guide you through the process, illustrating how easy it is to start reaping the benefits in features and performance., sessionrequirements: Experience developing applications with the Maps JavaScript API v2, sessionlink: v2-javascript-maps-api-v3, sessionhashtag: #geo1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.56, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BU2jwj7ZkBfu, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BU2jwj7ZkBfu, waveid: w+U2jwj7ZkBfuhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/c6bhi2010-05-10T18:39:11.857ZRow: 42sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 1, product: Maps API, track: Geo, sessiontype: 201, sessiontitle: Map once, map anywhere: Developing geospatial applications for both desktop and mobile, tags: Maps, Maps API, Mobile, JavaScript, Geospatial, sessionspeakers: Mano Marks, speakers: mmarks, sessionabstract: As the number of desktop and mobile platforms proliferates the cost of developing and maintaining multiple versions of an application continues to increase. This session illustrates how the JS Maps API can be used to simplify cross platform geospatial application development by enabling a single implementation to be shared across multiple platforms, while maintaining a native look and feel., sessionrequirements: Web application development, sessionlink: map-once-map-anywhere-geospatial-apps, sessionhashtag: #geo2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.57, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkOR, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkOR, waveid: w+e6j2obZkORhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cbxr62010-05-10T18:39:11.857ZRow: 43sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 4, product: Maps Data API, track: Geo, sessiontype: 101, sessiontitle: Unleash your map data: Cloud computing for geospatial applications, tags: Maps, Maps API, Data, Cloud, Geospatial, sessionspeakers: Tom Manshreck, speakers: shreck, sessionabstract: The Google Maps API made geospatial development accessible to all but hosting your data remains complex and time consuming. This session will detail the services Google offers for storing your geospatial data in the cloud, illustrate the ways in which that data can be accessed and visualized, and walk through development of a retail store finder using these technologies., sessionrequirements: Experience developing web based applications that search over or visualise geospatial data is helpful but not essential., sessionlink: unleash-map-data-cloud-computing-geospatial-apps, sessionhashtag: #geo4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.58, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BTeHZ97ZkBDO, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BTeHZ97ZkBDO, waveid: w+TeHZ97ZkBDOhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/a59y22010-05-10T18:39:11.857ZRow: 44sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: 9, product: SketchUp, Building Maker, 3D Warehouse, track: Geo, sessiontype: 201, sessiontitle: The SketchUp 3D API: Working with 3D geospatial data, tags: SketchUp, Building Maker, 3D Warehouse, 3D, sessionspeakers: Matt Lowrie, speakers: mlowrie, sessionabstract: The world is a three dimensional space. Your geospatial applications should be showing it that way. This session will show how to create 3D data in Building Maker and then use the SketchUp API to customize that data to fit your needs., sessionrequirements: Basic understanding of 3D is helpful, but not essential, sessionlink: sketchup-3d-api, sessionhashtag: #geo5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.7B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv_K9zbZkBY1, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv_K9zbZkBY1, waveid: w+v_K9zbZkBY1http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/a6oij2010-05-10T18:39:11.857ZRow: 45sessiondate: Thursday May 20, sessiontime: 2:15pm-3:15pm, room: 9, product: KML, Earth API, Maps Data API, Geospatial, 3D, track: Geo, sessiontype: 201, sessiontitle: Mapping in 3D: Tips and tricks for Google Earth API and KML, tags: KML, Earth API, Maps Data API, Geospatial, 3D, sessionspeakers: Josh Livni, Mano Marks, speakers: jlivni, mmarks, sessionabstract: Google Earth and the Earth API can handle a tremendous amount of data. But you always have more. We will talk about integrating large datasets efficiently, coding for optimal performance, and taking advantage of advanced features in KML and the Earth API., sessionrequirements: Attendees should have a basic knowledge of Google Earth, and KML or Javascript.
+, sessionlink: mapping-in-3d-earth-api-kml, sessionhashtag: #geo6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.7C, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQhu3aLZkgD, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQhu3aLZkgD, waveid: w+Qhu3aLZkgDhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/a832w2010-05-10T18:39:11.857ZRow: 46sessiondate: Thursday May 20, sessiontime: 3:30pm-4:30pm, room: 1, product: Maps API, track: Geo, sessiontype: 301, sessiontitle: Moving beyond markers: Advanced Maps API customization, tags: Maps, Maps API, sessionspeakers: Jez Fletcher, David Day, speakers: jez, davidday, sessionabstract: With such a large number of Google Maps API sites online, it can be hard to make your site stand out from the crowd. This session covers ways in which you can enhance your Maps API application to truly differentiate it, including customizing your overlays, controls, and map., sessionrequirements: Experience developing applications with the Google Maps APIs, sessionlink: moving-beyond-markers-advanced-maps-api, sessionhashtag: #geo7, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.7D, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BTeHZ97ZkBCl, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BTeHZ97ZkBCl, waveid: w+TeHZ97ZkBClhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/a9hnd2010-05-10T18:39:11.857ZRow: 47sessiondate: Thursday May 20, sessiontime: 4:45pm-5:45pm, room: 1, product: Maps API, track: Geo, Tech Talks, sessiontype: Tech Talk, sessiontitle: How Maps API v3 came to be: Tips, tricks, and lessons learned in developing a cross platform desktop and mobile API, tags: Maps, Maps API, JavaScript, Mobile, sessionspeakers: Susannah Raub, Marc Ridey, speakers: sraub, mridey, sessionabstract: The Google JavaScript Maps API v3 celebrates its one year anniversary at this year's Google I/O. In this session, we reveal the reasons for embarking on a new API, the challenges we faced in developing a truly cross platform and cross device framework, and the lessons learned on the way., sessionrequirements: Experience developing desktop web based applications, and an interest in porting such applications to new mobile platforms.
+, sessionlink: maps-api-v3-api, sessionhashtag: #geo8, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.7F, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BkrgNvLZkBmC, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BkrgNvLZkBmC, waveid: w+krgNvLZkBmChttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/akq6p2010-05-10T18:39:11.857ZRow: 48sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 2, product: AJAX APIs, track: Google APIs, sessiontype: 101, sessiontitle: Bringing Google to your site, tags: AJAX APIs, Friend Connect, Ads APIs, Web Elements, Analytics, sessionspeakers: DeWitt Clinton, Jeff Scudder, speakers: dewitt, jscudder, sessionabstract: This is an overview session about some of the many ways that a developer can enrich their site and more fully engage their visitors using Google products. We will cover a variety of products and APIs designed to quickly and easily improve and monetize your site, from AdSense and Custom Search to Feeds and Web Elements. We'll include announcements for several eye-popping new features., sessionrequirements: Basic HTML and JavaScript familiarity, sessionlink: bringing-google-to-your-site-googleapis, sessionhashtag: #googleapis1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.98, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bo01RhLZkBDwC, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bo01RhLZkBDwC, waveid: w+o01RhLZkBDwChttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/aaw7q2010-05-10T18:39:11.857ZRow: 49sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 2, product: PowerMeter API, track: Google APIs, sessiontype: 101, sessiontitle: Knowledge is (less) power: Exploring the Google PowerMeter API, tags: PowerMeter API, APIs, OAuth, Java, Python, Energy, sessionspeakers: Srikanth Rajagopalan, Rus Heywood, speakers: srikanth, rheywood, sessionabstract: In this session we will discuss interesting ways to make users understand and manage their home energy use through Google PowerMeter. The Google PowerMeter API currently available allows devices to integrate with Google PowerMeter. Come learn how you can build with the API and about exciting developments ahead. We will dig into the implementation details for integrations and open up the floor for other ideas that may be relevant., sessionrequirements: Web developer background, hardware enthusiasts, sessionlink: google-powermeter-api, sessionhashtag: #googleapis2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.59, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQhu3aLZkkc, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQhu3aLZkkc, waveid: w+Qhu3aLZkkchttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/acas72010-05-10T18:39:11.857ZRow: 50sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 2, product: Google Charts Toolkit, Visualization API, Chartserver
+, track: Google APIs, sessiontype: 201, sessiontitle: Google Charts Toolkit: Google's new unified approach for creating dynamic charts on the web, tags: Charts, Toolkit, Visualization API, JavaScript, Images, Data source, sessionspeakers: Michael Fink, Amit Weinstein, speakers: fink, amitw, sessionabstract: Google Charts Toolkit is Google's unified approach for creating charts on the web. It provides a rich gallery spanning from pie charts to interactive heat-maps and from organizational trees to motion charts. The toolkit lets developers choose between JavaScript based client-side rendering and image based server-side rendering. We will present the relative strengths of these two approaches, and unveil the future visual design of Google Charts. , sessionrequirements: Basic knowledge of Google Visualization API and Google Charts API, sessionlink: google-charts-toolkit-googleapis, sessionhashtag: #googleapis4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.5A, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BSVy-sLZkBX8, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BSVy-sLZkBX8, waveid: w+SVy-sLZkBX8http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/adpck2010-05-10T18:39:11.857ZRow: 51sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 2, product: Google Analytics API, track: Google APIs, sessiontype: 201, sessiontitle: Google Analytics APIs: End to end, tags: Google Analytics, JavaScript, Java, Gadgets, Mobile, ga.js, Video, sessionspeakers: Nick Mihailovski, speakers: nm, sessionabstract: Google Analytics measures performance of your website. Learn advanced techniques on how to use our tracking, processing and data export APIs as we walk you through an example of creating a most visited pages web element for your website., sessionrequirements: Intermediate Knowledge of Google Analytics, Javascript, sessionlink: google-analytics-apis-end-to-end, sessionhashtag: #googleapis5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.7E, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BtaQlR7ZkBF9, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BtaQlR7ZkBF9, waveid: w+taQlR7ZkBF9http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/af3x12010-05-10T18:39:11.857ZRow: 52sessiondate: Thursday May 20, sessiontime: 2:15pm-3:15pm, room: 2, product: YouTube API, track: Google APIs, sessiontype: 201, sessiontitle: YouTube API uploads: Tools, tips, and best practices, tags: Google Data APIs, Android, iPhone, YouTube, YouTube APIs, Mobile, sessionspeakers: Jeffrey Posnick, Gareth McSorley, Kuan Yong, speakers: jeffy, gmcsorley, kuanyong, sessionabstract: Are you integrating YouTube upload functionality into your mobile, desktop, or web app? Learn about Android and iPhone upload best practices, resuming interrupted YouTube uploads, and the YouTube Direct embeddable iframe for soliciting uploads on your existing web pages., sessionrequirements: Interest in adding YouTube upload functionality to mobile, desktop, or web-based applications, sessionlink: youtube-api-uploads, sessionhashtag: #googleapis7, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.80, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bo01RhLZkBHy, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bo01RhLZkBHy, waveid: w+o01RhLZkBHyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/auk5k2010-05-10T18:39:11.857ZRow: 53sessiondate: Thursday May 20, sessiontime: 3:30pm-4:30pm, room: 2, product: Google APIs, track: Google APIs, sessiontype: 201, sessiontitle: How Google builds APIs, tags: APIs, Atom, JSON, Java, Python, sessionspeakers: Zach Maier, Joseph Schorr, Mark Stahl, speakers: zpm, jschorr, mstahl, sessionabstract: Dive into the API development process at Google, and discover how we routinely take our full-featured APIs from design to testing to production in less than an hour. We'll present Google API design goals and best practices, showcase our new API infrastructure, and demonstrate how you can take advantage of the secret "power" features hiding in every Google API., sessionrequirements: A basic understanding of web APIs is helpful but not necessarily required, sessionlink: how-google-builds-apis, sessionhashtag: #googleapis8, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.81, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B9rTSb7ZkBm1, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B9rTSb7ZkBm1, waveid: w+9rTSb7ZkBm1http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/avyq12010-05-10T18:39:11.857ZRow: 54sessiondate: Thursday May 20, sessiontime: 4:45pm-5:45pm, room: 2, product: AdSense for Mobile Apps, Google Analytics, track: Google APIs, Android, sessiontype: 201, sessiontitle: Analyzing and monetizing your Android & iPhone apps, tags: AdSense for Mobile Apps, Google Analytics, Android, Mobile, Java, sessionspeakers: Chrix Finne, Jim Kelm, speakers: chrix, kelm, sessionabstract: If you've got a great mobile app, you're likely interested to know how users interact with your app and how to build a business from it. In this session you'll learn how you can drive awareness and earn revenue for your app using AdSense for Mobile Apps. We'll also discuss how using Google Analytics can help with your app development by providing insights into where your app users are coming from and how they're engaging with your app. We'll share tips, tricks, and examples of real-world mobile apps that have found success., sessionrequirements: Attendees should have an existing iPhone or Android app, but not necessary. Familiarity and current account setup on AdSense and Google Analytics useful but not necessary, sessionlink: analyzing-monetizing-mobile-apps, sessionhashtag: #googleapis9, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.82, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQ9WoGLZkBoo, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQ9WoGLZkBoo, waveid: w+Q9WoGLZkBoohttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/am4r22010-05-10T18:39:11.857ZRow: 55sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 201, sessiontitle: Measure in milliseconds redux: Meet Speed Tracer, tags: GWT, Google Web Toolkit, AJAX, Speed Tracer, Chrome, Java, JavaScript, sessionspeakers: Kelly Norton, speakers: knorton, sessionabstract: It turns out that web apps can be slow for all sorts of opaque and unintuitive reasons. Don't be fooled into thinking that bloated, slow JavaScript is the only culprit. This session introduces you to Speed Tracer, a new GWT tool that can tell you exactly where time is going within the browser., sessionrequirements: Intermediate knowledge of Javascript and DOM concepts., sessionlink: measure-in-milliseconds-speed-tracer-gwt, sessionhashtag: #gwt1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.5B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BeRiTZrZkC3Z, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BeRiTZrZkC3Z, waveid: w+eRiTZrZkC3Zhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/anjbj2010-05-10T18:39:11.857ZRow: 56sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 201, sessiontitle: Faster apps faster: Optimizing apps with the GWT Compiler, tags: GWT, Google Web Toolkit, AJAX, Eclipse, IDE, Java, JavaScript, sessionspeakers: Ray Cromwell, speakers: cromwellian, sessionabstract: The GWT compiler isn't just a Java to JavaScript transliterator. It performs many optimizations along the way. In this session, we'll show you not only the optimizations performed, but how you can get more out of the compiler itself. Learn how to speed up compiles, use -draftCompile, compile for only one locale/browser permutation, and more., sessionrequirements: Intermediate knowledge of Java, Google Web Toolkit, and compiler optimization strategies., sessionlink: faster-apps-faster-gwt-compiler, sessionhashtag: #gwt2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.5C, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B9rTSb7ZkBlL, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B9rTSb7ZkBlL, waveid: w+9rTSb7ZkBlLhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/aoxvw2010-05-10T18:39:11.857ZRow: 57sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 201, sessiontitle: Architecting for performance with GWT, tags: GWT, Google Web Toolkit, AJAX, Java, JavaScript, Enterprise, sessionspeakers: Joel Webber, Adam Schuck, speakers: jgw, schuck, sessionabstract: Modern web applications are quickly evolving to an architecture that has to account for the performance characteristics of the client, the server, and the global network connecting them. Should you render HTML on the server or build DOM structures with JS in the browser, or both? This session discusses this, as well as several other key architectural considerations to keep in mind when building your Next Big Thing., sessionrequirements: Intermediate knowledge of client/server relationships, Java, and Google Web Toolkit, sessionlink: architecting-performance-gwt, sessionhashtag: #gwt3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.5D, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQhu3aLZker, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQhu3aLZker, waveid: w+Qhu3aLZkerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/aqcgd2010-05-10T18:39:11.857ZRow: 58sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 301, sessiontitle: GWT Linkers target HTML5 Web Workers, Chrome Extensions, and more, tags: GWT, Google Web Toolkit, AJAX, Java, JavaScript, HTML5, sessionspeakers: Matt Mastracci, sessionabstract: At its core GWT has a well-defined and customizable mechanism -- called Linkers -- that controls exactly how GWT's compiled JavaScript should be packaged, served, and run. This session will describe how to create linkers and explains some of the linkers we've created, including a linker that turns a GWT module into an HTML5 Web Worker and one that generates an HTML App Cache manifest automatically., sessionrequirements: Advanced knowledge of Google Web Toolkit. Intermediate knowledge of Java., sessionlink: gwt-linkers-webworkers-extensions, sessionhashtag: #gwt4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.5E, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BetojYbZkBEt, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BetojYbZkBEt, waveid: w+etojYbZkBEthttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/b4e4j2010-05-10T18:39:11.857ZRow: 59sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 201, sessiontitle: GWT's UI overhaul: UiBinder, ClientBundle, and Layout Panels, tags: GWT, Google Web Toolkit, AJAX, Java, JavaScript, sessionspeakers: Joel Webber, Ray Ryan, speakers: jgw, rjrjr, sessionabstract: There have been some really huge improvements in GWT's UI fundamentals over the past year. We've introduced features such as UiBinder, ClientBundle, CssResource, and uber layout panels that allow you to build fast UIs in a sane manner. Come see how fun/easy/fast it can be to use these technologies in harmony to overhaul your UI., sessionrequirements: Intermediate knowledge of Google Web Toolkit., sessionlink: gwt-ui-overhaul, sessionhashtag: #gwt5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.83, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQ9WoGLZkBpL, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQ9WoGLZkBpL, waveid: w+Q9WoGLZkBpLhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/b5sow2010-05-10T18:39:11.857ZRow: 60sessiondate: Thursday May 20, sessiontime: 1:00pm-2:00pm, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 201, sessiontitle: GWT + HTML5 can do what?!, tags: GWT, Google Web Toolkit, AJAX, HTML5, Java, JavaScript, Enterprise, sessionspeakers: Joel Webber, Ray Cromwell, Stefan Haustein, speakers: jgw, cromwellian, haustein, sessionabstract: How can you take advantage of new HTML5 features in your GWT applications? In this session, we answer that question in the form of demos -- lots and lots of demos. We'll cover examples of how to use Canvas for advanced graphics, CSS3 features, Web Workers, and more within your GWT applications., sessionrequirements: Baseline knowledge of HTML5 as well as intermediate knowledge of Java and Google Web Toolkit, sessionlink: gwt-html5, sessionhashtag: #gwt6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.84, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BetojYbZkBDR, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BetojYbZkBDR, waveid: w+etojYbZkBDRhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/b779d2010-05-10T18:39:11.857ZRow: 61sessiondate: Thursday May 20, sessiontime: 2:15pm-3:15pm, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 301, sessiontitle: GWT testing best practices, tags: GWT, Google Web Toolkit, AJAX, Testing, Java, JavaScript, sessionspeakers: Daniel Danilatos, speakers: danilatos, sessionabstract: GWT has a lot of little-publicized infrastructure that can help you build apps The Right Way: test-driven development, code coverage, comprehensive unit tests, and integration testing using Selenium or WebDriver. This session will survey GWT's testing infrastructure, describe some best practices we've developed at Google, and help you avoid common pitfalls., sessionrequirements: Advanced knowledge of Java, Google Web Toolkit, and current testing frameworks., sessionlink: gwt-continuous-build-testing, sessionhashtag: #gwt7, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.85, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BeRiTZrZkC31, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BeRiTZrZkC31, waveid: w+eRiTZrZkC31http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/axdae2010-05-10T18:39:11.857ZRow: 62sessiondate: Thursday May 20, sessiontime: 3:30pm-4:30pm, room: 7, product: Google Web Toolkit, track: GWT, sessiontype: 301, sessiontitle: Architecting GWT applications for production at Google, tags: GWT, Google Web Toolkit, AJAX, Java, JavaScript, Enterprise, sessionspeakers: Ray Ryan, speakers: rjrjr, sessionabstract: For large GWT applications, there's a lot you should think about early in the design of your project. GWT has a variety of technologies to help you, but putting it all together can be daunting. This session walks you through how teams at Google architect production-grade apps, from design to deployment, using GWT., sessionrequirements: Advanced knowledge of Google Web Toolkit. Intermediate knowledge of design patterns and Java., sessionlink: architecting-production-gwt, sessionhashtag: #gwt8, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.86, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BeRiTZrZkC31, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BeRiTZrZkC31, waveid: w+eRiTZrZkC31http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ayruv2010-05-10T18:39:11.857ZRow: 63sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 1, product: Social, OpenSocial, Social Web, track: Social Web, sessiontype: 101, sessiontitle: The open & social web, tags: ActivityStrea.ms, Salmon, WebFinger, PubSubHubbub, PortableContacts, OpenID, OAuth, OpenSocial, sessionspeakers: Chris Messina, speakers: messina, sessionabstract: This session will cover the latest and most important trends of the Social Web and dive deep into where this is all going, at both technical and conceptual levels. From the concepts of digital identity, relationships, and social objects, this session will cover emerging technologies like WebFinger, Salmon, ActivityStrea.ms, OpenID, OAuth and OpenSocial., sessionrequirements: A web developer background and an interest in the social web., sessionlink: open-and-social-web, sessionhashtag: #googlesocial, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.5F, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BfZ2urbZk6F, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BfZ2urbZk6F, waveid: w+fZ2urbZk6Fhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/c7q1z2010-05-10T18:39:11.857ZRow: 64sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 8, product: Google Buzz APIs, track: Social Web, sessiontype: 101, sessiontitle: What's the hubbub about Google Buzz APIs?, tags: Buzz, ActivityStrea.ms, OpenID, Salmon, PubSubHubbub, OAuth, sessionspeakers: Chris Chabot, Loic Le Meur (Seesmic), Ming Yong (Socialwok), speakers: chabotc, sessionabstract: Google Buzz is a new way to share updates, photos, videos and more, and start conversations about the things you find interesting. In this session, we'll take a deep dive into building with the Buzz APIs and the open standards it uses, such as ActivityStrea.ms, PubSubHubbub, OAuth, Salmon and WebFinger., sessionrequirements: A web developer background and an interest in the social web, sessionlink: google-buzz-apis, sessionhashtag: #buzzapi, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.99, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bqlswu7ZkBSc, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bqlswu7ZkBSc, waveid: w+qlswu7ZkBSchttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/b06f82010-05-10T18:39:11.857ZRow: 65sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 9, product: Social Web, Social, track: Social Web, sessiontype: 201, sessiontitle: Bridging the islands: Building fluid social experiences across websites, tags: OpenID, OAuth, Portable Contacts, ActivityStrea.ms, Salmon, sessionspeakers: John Panzer, Joseph Smarr, speakers: jpanzer, jsmarr, sessionabstract: As more sites add social functionality, profiles, friends, and conversations are becoming increasingly fragmented and redundant. But an emerging collection of open technologies aim to help bridge these social islands, allowing users to seamlessly move between sites, bring their friends along, and have unified conversations that span multiple web sites. Come learn how OpenID, OAuth, Portable Contacts, ActivityStrea.ms, and Salmon can help you connect your site to the rest of the Social Web, increasing your traffic, engagement, and relevance to your users., sessionlink: building-fluid-social-experiences-across-websites, sessionhashtag: #islandsio, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.60, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BQhu3aLZkgi, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BQhu3aLZkgi, waveid: w+Qhu3aLZkgihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/b1kzp2010-05-10T18:39:11.857ZRow: 66sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 9, product: PubSubHubbub, track: Social Web, sessiontype: 201, sessiontitle: Make your application real-time with PubSubHubbub, tags: PubSubHubbub, real-time, Atom, RSS, sessionspeakers: Brett Slatkin, speakers: bslatkin, sessionabstract: This session will go over how to add support for the PubSubHubbub protocol to your website. You'll learn how to turn Atom and RSS feeds into real-time streams. We'll go over how to consume real-time data streams and how to make your website reactive to what's happening on the web right now., sessionrequirements: Knowledge of web technologies (Atom, RSS, HTTP, HTML) and content-mangement systems (WordPress, etc)., sessionlink: real-time-apps-pubsubhubbub, sessionhashtag: #PuSHio, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.61, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BeRiTZrZkC0N, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BeRiTZrZkC0N, waveid: w+eRiTZrZkC0Nhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/be8562010-05-10T18:39:11.857ZRow: 67sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 9, product: iGoogle, OpenSocial, track: Social Web, sessiontype: 201, sessiontitle: iGoogle developer portal and tools, tags: iGoogle, OpenSocial, OSDE, OpenSocial Development Environment, HTML, JavaScript, sessionspeakers: Shih-chia Cheng, Albert Cheng, speakers: shihchia, albertcheng, sessionabstract: Learn how to build and maintain better OpenSocial gadgets for iGoogle. Two major applications will be introduced. The first one is iGoogle Gadget Dashboard for managing gadgets created by you. The second one is OSDE (OpenSocial Development Environment) which is an Eclipse plugin for developers to easily implement gadgets., sessionrequirements: Knowledge of web technology such as HTML and JavaScript., sessionlink: igoogle-developer-portal, sessionhashtag: #igoogleio, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.87, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BkrgNvLZkBhk, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BkrgNvLZkBhk, waveid: w+krgNvLZkBhkhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bfmpn2010-05-10T18:39:11.857ZRow: 68sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: 1, product: Google Buzz APIs, track: Social Web, sessiontype: 201, sessiontitle: Surf the stream: Google Buzz, location, and social gaming, tags: Buzz, Social, mobile, HTML5, App Engine, sessionspeakers: Bob Aman, Timothy Jordan, speakers: bobaman, timothyjordan, sessionabstract: Google Buzz has a feature-rich API that allows you to do all kinds of interesting things with conversations and location. In this session we'll build a Buzz-tastic mobile game using App Engine, HTML5, and the Buzz API for social awesomeness. , sessionrequirements: Knowledge of Python, the web and a good spirited nature., sessionlink: surf-the-stream-google-buzz-social-gaming, sessionhashtag: #buzzbingo, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.88, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bo01RhLZkBJ6, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bo01RhLZkBJ6, waveid: w+o01RhLZkBJ6http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bh1a02010-05-10T18:39:11.857ZRow: 69sessiondate: Thursday May 20, sessiontime: 4:45pm-5:45pm, room: 7, product: Social Web, Social, track: Social Web, sessiontype: 201, sessiontitle: Where is the social web going next?, tags: ActivityStrea.ms, Salmon, WebFinger, PubSubHubbub, PortableContacts, OpenID, OAuth, OpenSocial, sessionspeakers: Adam Nash, Daniel Raffel, Chris Messina, Angus Logan, Ryan Sarver, Chris Cole, Kara Swisher (moderator), speakers: messina, sessionabstract: With the advent of social protocols like OAuth, OpenID and ActivityStrea.ms, it's clear that the web has gone social and is becoming more open. Adam Nash (LinkedIn), Daniel Raffel (Yahoo), Chris Messina (Google), Angus Logan (Microsoft), Ryan Sarver (Twitter), and Chris Cole (MySpace) will discuss the importance of such emerging technologies, how they've adopted them in their products and debate what's next. Kara Swisher will moderate., sessionrequirements: An interest in the social web, sessionlink: where-is-the-social-web-going-next, sessionhashtag: #socialfuture, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.8A, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BYTqpC7ZkBXT, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BYTqpC7ZkBXT, waveid: w+YTqpC7ZkBXThttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bifuh2010-05-10T18:39:11.857ZRow: 70sessiondate: Thursday May 20, sessiontime: 1:00pm-2:00pm, room: 9, product: OpenSocial, track: Social Web, Enterprise, sessiontype: 201, sessiontitle: Best practices for implementing OpenSocial in the Enterprise, tags: OpenSocial, Enterprise, sessionspeakers: Mark Weitzel, Matt Tucker, Mark Halvorson, Helen Chen, Chris Schalk, speakers: cschalk, sessionabstract: Enterprise deployments of OpenSocial technologies brings an additional set of considerations that may not be apparent in a traditional social network implementation. In this session, several enterprise vendors will demonstrate how they've been working together to address these issues in a collection of "Best Practices". This session will also provide a review of existing challenges for enterprise implementations of OpenSocial., sessionrequirements: General understanding of OpenSocial technologies. Some Enterprise experience is also recommended., sessionlink: opensocial-enterprise-panel, sessionhashtag: #opensocial, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.89, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BWDgRyLZkfF, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BWDgRyLZkfF, waveid: w+WDgRyLZkfFhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ba0fz2010-05-10T18:39:11.857ZRow: 71sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 5, track: Tech Talks, sessiontype: Tech Talk, sessiontitle: Ignite Google I/O, tags: Ignite, geek culture, sessionspeakers: Brady Forrest, Annalee Newitz, Ben Huh, Matt Harding, Clay Johnson, Bradley Vickers, Aaron Koblin, Michael Van Riper, Anne Veling, James Young, sessionabstract: <p><a href="http://ignite.oreilly.com">Ignite</a> captures the best of geek culture in a series of five-minute speed presentations. Each speaker gets 20 slides that auto-advance after 15 seconds. Check out <a href="http://code.google.com/events/io/2009/sessions/IgniteGoogleIO.html">last year's Ignite Google I/O</a>.</p>
+<p>
+Presenting this year's Ignite Google I/O speakers:
+</p>
+<p>
+<a href="http://www.techsploitation.com/">Annalee Newitz</a> (<a href="http://io9.com">io9</a>) - Weird Science
+ </p>
+<p>
+<a href="http://www.benhuh.com/">Ben Huh</a> (<a href="http://icanhascheezburger.com/">I Can has Cheez Burger</a>) - <strong>Evolution of the Meme</strong>
+</p><p>
+<a href="http://en.wikipedia.org/wiki/Matt_Harding">Matt Harding</a> (<a href="http://www.wherethehellismatt.com/">Where The Hell Is Matt?</a>) - <strong>The Imaginary Line of Ancient Cosmic Weirdness </strong>
+</p><p>
+<a href="http://twitter.com/cjoh">Clay Johnson</a> (<a href="http://sunlightfoundation.com/">Sunlight Foundation</a>) - <strong>Tales from the District: How Washington Publishes Data</strong>
+</p><p>
+<a href="http://www.flickr.com/nwrower">Bradley Vickers</a> - <strong>How to Row across the North Atlantic, Ration Food and Not Have Your Teammates Eat You</strong>
+</p><p>
+<a href="http://www.aaronkoblin.com/">Aaron Koblin</a> (Google/<a href="http://www.thesheepmarket.com/">Sheep Market</a>) - <strong>Digital Art</strong>
+</p>
+<p>
+<a href="http://van-riper.blogspot.com/">Michael Van Riper</a> - <strong>Confessions of a Serial Community Organizer</strong>
+</p><p>
+<a href="http://www.beyondtrees.com/weblog/">Anne Veling</a> - <strong>Feeling Lonely on Google Wave</strong></p>
+<p><a href="http://twitter.com/jamesyoung6979">James Young</a> - <strong>You Sank My Battleship!</strong></p>, sessionrequirements: Affinity for geek culture, sessionlink: ignite-google-io, sessionhashtag: #ignitegoogleio, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BO4sGNLZkCK4, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BO4sGNLZkCK4, waveid: w+O4sGNLZkCK4http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bbf0c2010-05-10T18:39:11.857ZRow: 72sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 9, product: Closure Library, track: Tech Talks, sessiontype: 201, sessiontitle: Opening up Closure Library, tags: Closure Tools, Closure Library, JavaScript, sessionspeakers: Nathan Naze, speakers: nnaze, sessionabstract: Closure Library is the open-source JavaScript library behind some of Google's big web apps like Gmail and Google Docs. This session will tour the broad library, its object-oriented design, and its namespaced organization. We'll explain how it works and how to integrate it in your setup, both for development and optimized for a live application using Closure Compiler., sessionrequirements: The session assumes JavaScript and web development experience., sessionlink: closure-library, sessionhashtag: #techtalks-closure, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.64, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv7BXJrZkBME, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv7BXJrZkBME, waveid: w+v7BXJrZkBMEhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bctkt2010-05-10T18:39:11.857ZRow: 73sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 4, product: Go Programming Language, track: Tech Talks, sessiontype: Tech Talk, sessiontitle: Go programming, tags: Go, open source, language, C, C++, Java, Python, sessionspeakers: Rob Pike, Russ Cox, speakers: r, rsc, sessionabstract: The Go programming language was released as an open source project in late 2009. This session will illustrate how programming in Go differs from other languages through a set of examples demonstrating features particular to Go. These include concurrency, embedded types, methods on any type, and program construction using interfaces. Very little time will be spent waiting for compilation., sessionrequirements: Programming experience in a language such as C, C++, Java, or Python. Some familiarity with Go helpful but not required., sessionlink: go-programming, sessionhashtag: #techtalks-go, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.8D, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BWDgRyLZkb2, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BWDgRyLZkb2, waveid: w+WDgRyLZkb2http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bpgoi2010-05-10T18:39:11.857ZRow: 74sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: 2, track: Tech Talks, sessiontype: Tech Talk, sessiontitle: How to lose friends and alienate people: The joys of engineering leadership, tags: Engineering, leadership, management, storytelling, sessionspeakers: Brian W. Fitzpatrick, Ben Collins-Sussman, speakers: fitz, sussman, sessionabstract: Are you considered the 'point' person for your team? Do you have sweaty palms, headaches, and a calendar full of meetings? You may have an affliction called 'manager'. 3 out of 4 engineers aren't aware of this condition, but it is treatable through careful analysis and therapy. We'll examine how you may have arrived at this state and how you can once again regain your self-respect and the respect of your peers. Hear real-life stories of both good and bad leadership. Learn to lead by following., sessionrequirements: Sense of humor, sessionlink: lose-friends-alienate-people-engineering-leadership, sessionhashtag: #techtalks-leadership, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.8E, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B9rTSb7ZkBlS, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B9rTSb7ZkBlS, waveid: w+9rTSb7ZkBlShttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bqv8z2010-05-10T18:39:11.857ZRow: 75sessiondate: Thursday May 20, sessiontime: 2:15pm-3:15pm, room: 8, product: Web Search, track: Tech Talks, sessiontype: Tech Talk, sessiontitle: SEO site advice from the experts, tags: Search, SEO, site architecture, sessionspeakers: Matt Cutts, Greg Grothaus, Evan Roseman, speakers: matt, greggrothaus, eroseman, sessionabstract: A perfect opportunity to get your website reviewed by the experts in the Google Search Quality team. Attendees can get concrete search engine optimization (SEO) feedback on their own sites. We'll also answer real-life questions that affect developers when it comes to optimizing their websites for search., sessionrequirements: Perfect for web developers who want to learn more about search engine optimization (SEO), sessionlink: seo-site-review-from-experts, sessionhashtag: #techtalks-seo, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.8F, wavelink: https://wave.google.com/wave/#restored:search,restored:wave:googlewave.com!w%252B-Xhdu7ZkBLf, _e8rn7: https://wave.google.com/wave/#restored:search,restored:wave:googlewave.com, _dmair: w%252B-Xhdu7ZkBLf, waveid: w+-Xhdu7ZkBLfhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bs9tc2010-05-10T18:39:11.857ZRow: 76sessiondate: Thursday May 20, sessiontime: 3:30pm-4:30pm, room: 9, product: Page Speed, track: Tech Talks, sessiontype: 101, sessiontitle: Optimize every bit of your site serving and web pages with Page Speed, tags: Design, Web development, Website optimization, Page Speed, Analytics, sessionspeakers: Richard Rabbat, Bryan McQuade, speakers: rabbat, bmcquade, sessionabstract: Page Speed is an open-source Firefox/Firebug Add-on. Webmasters and web developers can use Page Speed to evaluate the performance of their web pages and to get suggestions on how to improve them. Learn about the latest rules of web development we've added, optimizations that we've updated, go over a new refreshed UI, see how to collect data through beacons to track progress over time, cut and paste fixes, and how to work with 3rd party libraries more effectively, including Google Analytics., sessionlink: optimize-site-serving-page-speed, sessionhashtag: #techtalks-speed, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.90, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BU2jwj7ZkBer, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BU2jwj7ZkBer, waveid: w+U2jwj7ZkBerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/btodt2010-05-10T18:39:11.857ZRow: 77sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 9, track: Tech Talks, sessiontype: Tech Talk, sessiontitle: Beyond design: Creating positive user experiences, tags: UI, UX, design, user experience, users, sessionspeakers: John Zeratsky, Matt Shobe, speakers: jazer, shobe, sessionabstract: Good user experience isn't just about good design. Learn how to create a positive user experience by being fast, open, engaged, surprising, polite, and, well... being yourself. Chock full of examples from the web and beyond, this talk is a practical introduction for developers who are passionate about user experience but may not have a background in design., sessionrequirements: Willingness to let old assumptions about their inability to solve certain design challenges get a bit banged up., sessionlink: beyond-design-user-experience, sessionhashtag: #techtalks-ux, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.97, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BtaQlR7ZkBhw, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BtaQlR7ZkBhw, waveid: w+taQlR7ZkBhwhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bjueu2010-05-10T18:39:11.857ZRow: 78sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 4, track: Tech Talks, sessiontype: Tech Talk, sessiontitle: Technology, innovation, computer science, & more: VC panel, tags: VC, venture capital, technology, sessionspeakers: Albert Wenger, Chris Dixon, Dave McClure, Brad Feld, Paul Graham, Dick Costolo (moderator), sessionabstract: What do notable tech-minded VCs think about big trends happening today? In this session, you'll get to hear from and ask questions to a panel of well-respected investors, all of whom are programmers by trade. Albert Wenger, Chris Dixon, Dave McClure, Paul Graham, and Brad Feld will duke it out on a number of hot tech topics with Dick Costolo moderating. , sessionrequirements: None, sessionlink: tech-innovation-cs-vc-panel, sessionhashtag: #techtalks-vc, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.65, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv_K9zbZkBXX, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv_K9zbZkBXX, waveid: w+v_K9zbZkBXXhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bo2452010-05-10T18:39:11.857ZRow: 79sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 8, product: Google Wave, track: Wave, sessiontype: 201, sessiontitle: Google Wave API design principles: Anatomy of a great extension, tags: Wave, sessionspeakers: Pamela Fox, Michael Goderbauer (Hasso Plattner Institute), speakers: pamelafox, sessionabstract: Google Wave is all about collaboration, and the most successful extensions are user-friendly and collaborative. Wave robots should be as intuitive to communicate with as a human, and play well with other robots; Wave gadgets should extend the metaphors of the textual collaboration into the visual. In this talk, we'll discuss the design and privacy principles you should consider while building extensions, and show examples of extensions that demonstrate these principles. , sessionrequirements: Basic understanding of the Google Wave extensions APIs (gadgets and robots), sessionlink: wave-api-design-extensions, sessionhashtag: #wave1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.62, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BkrgNvLZkBh6, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BkrgNvLZkBh6, waveid: w+krgNvLZkBh6http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bmnjo2010-05-10T18:39:11.857ZRow: 80sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 8, product: Google Wave, track: Wave, sessiontype: 101, sessiontitle: Waving across the web, tags: Wave, sessionspeakers: Dhanji Prasanna, Douwe Osinga, speakers: dhanji, douwe, sessionabstract: This talk focuses on using the Google Wave APIs outside of the Google Wave product. We'll show how to take advantage of embedded waves for commenting and discussions on your website, how to integrate with WaveThis to make your website more collaborative, and how to use the wave data APIs to get access to wave content from your website., sessionrequirements: JavaScript. Basic understanding of Google Wave is helpful., sessionlink: waving-across-the-web, sessionhashtag: #wave2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.9C
+, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BuH6OdLZkCY3.7, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BuH6OdLZkCY3.7, waveid: w+uH6OdLZkCY3.7http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/c0p7u2010-05-10T18:39:11.857ZRow: 81sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 8, product: Google Wave, track: Wave, sessiontype: 101, sessiontitle: Open source Google Wave: Building your own wave provider, tags: Wave, Open Source, sessionspeakers: Dan Peterson, Jochen Bekmann, J.D. Zamfirescu Pereira, David LaPalomento (Novell), speakers: dpeterson, jochen, zamfi, sessionabstract: Learn how to build your own wave service. Google is open sourcing the lion's share of the code that went into creating Google Wave to help bootstrap a network of federated providers. This talk will discuss the state of the reference implementation: the software architecture, how you can plug it into your own use cases -- and how you can contribute to the code and definition of the underlying specification., sessionrequirements: XMPP knowledge is useful. Some Java proficiency is helpful, sessionlink: open-source-wave-provider, sessionhashtag: #wave3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.63, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BTeHZ97ZkBEG, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BTeHZ97ZkBEG, waveid: w+TeHZ97ZkBEGhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/bl8zb2010-05-10T18:39:11.857ZRow: 82sessiondate: Thursday May 20, sessiontime: 11:30am-12:30pm, room: 8, product: Google Wave, track: Wave, sessiontype: 201, sessiontitle: Making smart & scalable Wave robots, tags: Wave, App Engine, sessionspeakers: David Byttow, Marcel Prasetya, speakers: davidbyttow, mprasetya, sessionabstract: A smart robot must be able to store persistent data. Wave robots can store data in wave structures, like wavelets, datadocs, and annotations, instead of traditional datastores. A scalable robot must perform operations with minimal bandwidth. Wave robots can optimize by selecting the appropriate amount of context, the optimal events, and narrow filters for events. In this talk, we'll share best practices on data storage and scaling. , sessionrequirements: Understanding of Google Wave robots, and Google App Engine, the ideal attendee has already built robots in the past, sessionlink: smart-scalable-wave-robots, sessionhashtag: #wave4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.8B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BSVy-sLZkBYM, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BSVy-sLZkBYM, waveid: w+SVy-sLZkBYMhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/c3ico2010-05-10T18:39:11.857ZRow: 83sessiondate: Thursday May 20, sessiontime: 3:30pm-4:30pm, room: 8, product: Google Wave, track: Wave, Enterprise, sessiontype: 101, sessiontitle: Google Wave and the enterprise environment, tags: Wave, Enterprise, sessionspeakers: Greg D'alesandre, Alan Green, Kris Muller (Salesforce.com), speakers: gregd, alangreen, sessionabstract: Google Wave provides enterprises with an opportunity to streamline a variety of communications. One of the main ways this occurs is through building generalized and custom extensions. This session will cover some of the existing enterprise extensions that have been built as well as give a sneak peak as to what's in progress. If you're wondering how Wave can be used to help build your business, you'll want to attend this session. , sessionrequirements: Basic understanding of Google Wave is helpful, sessionlink: google-wave-enterprise, sessionhashtag: #wave5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.8C, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bo01RhLZkBH7, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bo01RhLZkBH7, waveid: w+o01RhLZkBH7
\ No newline at end of file
diff --git a/assets/cache-speakers.xml b/assets/cache-speakers.xml
new file mode 100644
index 0000000..70dbf42
--- /dev/null
+++ b/assets/cache-speakers.xml
@@ -0,0 +1,5 @@
+http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic2010-05-10T18:39:11.857Zspeakerschristine.leechristine.lee@gmail.com1971http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cpzh42010-05-10T18:39:11.857ZRow: 2speakertitle: , speakerabstract: , speakerldap: http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cre1l2010-05-10T18:39:11.857ZRow: 3speakertitle: Aaron Koblin, speakercompany: Google, speakerabstract: Aaron takes social and infrastructural data and uses it to depict cultural trends and emergent patterns. He received the National Science foundation’s first place award for scientific visualization and is part of the permanent collection of the Museum of Modern Art. Currently, Aaron is part of Google’s Creative Lab in San Francisco., speakerldap: AaronKoblinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/chk2m2010-05-10T18:39:11.857ZRow: 4speakertitle: Adam Graff, speakercompany: Genentech, speakerabstract: Adam has worked in various roles at Genentech in software development, support and management while contributing to saving the lives of patients with unmet medical needs. Adam leads a team that is implementing Google Apps for 18,000 employees. They are also delivering innovative integrations between the Google Apps platform and other critical business systems that delight users, foster collaboration and increase productivity., speakerldap: AdamGraffhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ciyn32010-05-10T18:39:11.857ZRow: 5speakertitle: Adam Nash, speakercompany: LinkedIn, speakerabstract: Adam joined LinkedIn in 2007 and has helped build a number of product & technology teams. After taking over LinkedIn platform efforts in 2009, he led the effort to launch their first open platform, <a href="http://developer.linkedin.com">developer.linkedin.com</a>, and their full support for OAuth. He currently spends his time focused on how the next generation of business applications will leverage social platforms., speakerldap: AdamNashhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ckd7g2010-05-10T18:39:11.857ZRow: 6speakertitle: Adam Powell, speakercompany: Google, speakerabstract: Adam is a software engineer at Google with a background in small-scale games and a fascination with UI. As a member of the Android framework team, Adam is working to turn mobile devices into more approachable windows to the world's information., speakerldap: AdamPowellhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/clrrx2010-05-10T18:39:11.857ZRow: 7speakertitle: Adam Schuck, speakercompany: Google, speakerabstract: Adam is the technical lead for the Google Wave client. Since commencing work at Google Sydney in February 2006, Adam worked on Google Maps, having co-created Mapplets in 2007. Having achieved a black-belt as a JavaScript ninja, Adam dusted off his Java textbook, threw away his ninja uniform, and embraced the new art of GWT. He received the University Medal for his B.S. with Honours in Computer Science from the University of New South Wales., speakerldap: AdamSchuckhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cyevm2010-05-10T18:39:11.857ZRow: 8speakertitle: Alan Green, speakercompany: Google, speakerabstract: Alan is a software engineer building enterprise and privacy features for Google Wave. Prior to joining Google, Alan consulted in a range of industries, including government, banking, insurance, airlines and telecommunications. His code still dispatches fire engines in at least one major city., speakerldap: AlanGreenhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cztg32010-05-10T18:39:11.857ZRow: 9speakertitle: Albert Cheng, speakercompany: Google, speakerabstract: Albert is a software engineer on the iGoogle team. He currently leads the Gadget Development Tools projects. Interests include mountain climbing, enjoying free food, and fixing bugs., speakerldap: AlbertChenghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/d2mkx2010-05-10T18:39:11.857ZRow: 10speakertitle: Albert Wenger, speakercompany: Union Square Ventures, speakerabstract: Albert is a managing partner at Union Square Ventures. He currently serves on the boards of Twilio, Foursquare, 10gen, AMEE, Covestor, and Clickable. Albert has a Bachelors degree from Harvard and a PhD from MIT. He is married with three kids and lives outside New York City. Albert blogs at <a href="http://continuations.com">http://continuations.com</a>, speakerldap: AlbertWengerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cssly2010-05-10T18:39:11.857ZRow: 11speakertitle: Alex Russell, speakercompany: Google, speakerabstract: Alex is a member of the Google Chrome Frame team and a believer in the web as a democratizing platform. Prior to his work on Google Chrome Frame, Alex was Project Lead for the Dojo Toolkit JavaScript framework., speakerldap: AlexRussellhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cu76f2010-05-10T18:39:11.857ZRow: 12speakertitle: Alfred Fuller, speakercompany: Google, speakerabstract: Alfred recently joined Google after getting his Ph.D. in Computer Science from the University of California, Davis. Since joining Google's App Engine team, he has worked primarily on improving the Datastore., speakerldap: AlfredFullerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cx0b92010-05-10T18:39:11.857ZRow: 13speakertitle: Amit Agarwal, speakercompany: Google, speakerabstract: Amit is a product manager at Google. He has spent the last 10+ years designing, developing and deploying infrastructure., speakerldap: AmitAgarwalhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/d9ney2010-05-10T18:39:11.857ZRow: 14speakertitle: Amit Kulkarni, speakercompany: Manymoon, speakerabstract: Amit is the co-founder and CEO of Manymoon, a social productivity tool that helps people get work done by leveraging their connections and existing cloud platforms. He has a passion for building applications that make people more productive. Amit has a BS in Electrical Engineering and minor in Computer Science from the University of Illinois, Urbana-Champaign., speakerldap: AmitKulkarnihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/db1zf2010-05-10T18:39:11.857ZRow: 15speakertitle: Amit Manjhi, speakercompany: Google, speakerabstract: Amit is a software engineer on the Google Web Toolkit team, where he works towards GWT delivering "productivity for developers, performance for users." In particular, he has contributed to improving GWT's development mode, testability of GWT applications, and resource selection in GWT. He holds a Ph.D. degree in Computer Science from Carnegie Mellon University, has founded a VC-backed Web startup, and enjoys mathematical puzzles., speakerldap: AmitManjhihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dcgjs2010-05-10T18:39:11.857ZRow: 16speakertitle: Amit Weinstein, speakercompany: Google, speakerabstract: Amit is a developer on the Google Chart Tools team based in Israel. The team is now working on unifying the available chart tools in Google and enhancing them. When not at work, Amit is also working on his PhD in Computer Science at Tel Aviv University., speakerldap: AmitWeinsteinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ddv492010-05-10T18:39:11.857ZRow: 17speakertitle: Anders Sandholm, speakercompany: Google, speakerabstract: Anders is a Product Manager at Google. He launched the V8 JavaScript engine and Developer Tools in Google Chrome. Before coming to Google, Anders was a management consultant with McKinsey & Company. Anders holds a PhD in computer science from the University of Aarhus and an MBA from INSEAD. , speakerldap: AndersSandholmhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/iow8x2010-05-10T18:39:11.857ZRow: 18speakertitle: Angus Logan, speakercompany: Microsoft, speakerabstract: Angus helps websites connect with the 500 million people who use Windows Live including products such as Messenger., speakerldap: AngusLoganhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/d415a2010-05-10T18:39:11.857ZRow: 19speakertitle: Anne Veling, speakercompany: BeyondTrees, speakerabstract: After a M.Sc. in Computer Science/Artificial Intelligence, Anne worked for several years in the search engine industry, designing highly scalable knowledge extraction, clustering and visualization modules for search applications. Currently self-employed and helping out global companies deal with large amounts of loosely structured information as an internet application architect. Married, father of 3, theatresports addict, Lucene trainer., speakerldap: AnneVelinghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/d5fpr2010-05-10T18:39:11.857ZRow: 20speakertitle: Arne Roomann-Kurrik, speakercompany: Google, speakerabstract: Arne is a Developer Programs Engineer at Google, working with the Chrome team. Just like the rest of you, he puts his pants on one leg at a time; except that once his pants are on, he makes excellent samples, developer tools, and documentation., speakerldap: ArneRoomann-Kurrikhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/d6ua42010-05-10T18:39:11.857ZRow: 21speakertitle: Bart Locanthi, speakercompany: Google, speakerabstract: Bart joined Google in 2005 and most recently has worked on MyMaps and the Maps Data API. Prior to Google, he did freelance work on embedded languages and XML app servers. In ancient times, he was at AT&T Bell Labs, where he worked on the Blit and Gnot (Plan9) terminals, CAD tools, and CS in general., speakerldap: BartLocanthihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/d88ul2010-05-10T18:39:11.857ZRow: 22speakertitle: Ben Appleton, speakercompany: Google, speakerabstract: Ben is the tech lead manager for Google's Maps APIs. He is interested in randomized algorithms and computational geometry. Ben has a PhD in image analysis and degrees in electrical engineering and mathematics from the University of Queensland., speakerldap: BenAppletonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dkvya2010-05-10T18:39:11.857ZRow: 23speakertitle: Ben Cheng, speakercompany: Google, speakerabstract: Ben is a software engineer in the Android team working on Dalvik. His primary project is to develop a JIT compiler that improves the efficiency of the VM. He also spends time developing tools for performance tuning and code verification. Before Google, Ben worked at various companies on virtual machines, including Transmeta, Azul, and PeakStream. Ben got a PhD degree in Computer Science from University of Illinois at Urbana-Champaign., speakerldap: BenChenghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dmair2010-05-10T18:39:11.857ZRow: 24speakertitle: Ben Collins-Sussman, speakercompany: Google, speakerabstract: Ben is a tech lead and manager for Google Code's project hosting service. He was one of the founding developers of Subversion, and later helped port it to Google's Bigtable infrastructure. He also has a degree in mathematics, plays banjo, writes musicals, and takes lots of photos., speakerldap: BenCollins-Sussmanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dnp342010-05-10T18:39:11.857ZRow: 25speakertitle: Ben Fried, speakercompany: Google, speakerabstract: Ben is Chief Information Officer at Google and oversees Google's global technology systems. His extensive hands-on experience in technology includes stints as a dBASE II programmer, front-line support manager, Macintosh developer, Windows 1.0 programmer, and Unix systems programmer. Prior to joining Google, he spent more than 13 years in Morgan Stanley's technology department., speakerldap: BenFriedhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/if29y2010-05-10T18:39:11.857ZRow: 26speakertitle: Ben Huh, speakercompany: Cheezburger Network, speakerabstract: Ben is a former journalist turned dot com entrepreneur who has a knack for nailing the zeitgeist. He has been credited with bringing Internet memes to the mainstream and popularizing Internet culture. The success of his business is attributed to his knowledge of memes, viral content, and crowd sourcing. Ben graduated with a BSJ from Northwestern University’s Medill School of Journalism., speakerldap: BenHuhhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dp3nl2010-05-10T18:39:11.857ZRow: 27speakertitle: Bill Buzbee, speakercompany: Google, speakerabstract: Bill is a software engineer on Google's Android team, where he works on the Dalvik JIT Compiler. Prior to Google, Bill worked on dynamic compilation at Hewlett-Packard and Transmeta. He lives in Half Moon Bay with his wife, three kids and a dog., speakerldap: BillBuzbeehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/df9om2010-05-10T18:39:11.857ZRow: 28speakertitle: Bob Aman, speakercompany: Google, speakerabstract: Bob works on the Developer Relations team at Google in Mountain View. He loves contributing to open source, open web standards, and generally making the web a friendlier, more useful place for everybody. Bob was once saved from a volcanic mishap by a bad case of malaria. True story., speakerldap: BobAmanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dgo932010-05-10T18:39:11.857ZRow: 29speakertitle: Brad Feld, speakercompany: Foundry Group, speakerabstract: Brad is a managing director at the Foundry Group located in Boulder, Colorado. He has been an early stage investor and entrepreneur for over 20 years. Brad is also an avid art collector and long-distance runner. He has completed fifteen marathons as part of his mission to run a marathon in each of the 50 states., speakerldap: BradFeldhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dw4je2010-05-10T18:39:11.857ZRow: 30speakertitle: Brad Fitzpatrick, speakercompany: Google, speakerabstract: Brad is a software engineer on Google's Android team, where he works on performance. Brad's also worked Google's social efforts and, prior to Google, founded Danga.com where he developed OpenID, LiveJournal and created open source server software such as memcached, Gearman, MogileFS, djabberd, etc. Originally from Portland and Seattle, Brad now lives in San Francisco., speakerldap: BradFitzpatrickhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dxj3v2010-05-10T18:39:11.857ZRow: 31speakertitle: Bradley Vickers, speakercompany: OAR Northwest, speakerabstract: Bradley obtained a Guinness World Record over the course of 71 days as part of the first crew to Row across the North Atlantic from New York, USA to Falmouth, England. He co-founded OAR Northwest and is on the board of Around-n-Over. Bradley’s shares his adventures in presentations that focus on achieving goals and developing leadership skills., speakerldap: BradleyVickershttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dyxo82010-05-10T18:39:11.857ZRow: 32speakertitle: Brady Forrest, speakercompany: O'Reilly Media/Ignite, speakerabstract: Brady co-created <a href="http://igniteshow.com">Ignite</a> and is the Chair for O'Reilly's Where 2.0. Additionally, he co-Chairs the <a href="http://web2expo.com">Web 2.0 Expo</a> in San Francisco and NYC. Brady writes for <a href="http://radar.oreilly.com/">O'Reilly Radar</a> tracking changes in technology. Brady lives in Seattle, where he builds <a href="http://heaid.com">music-making robots</a> for Burning Man and runs <a href="http://igniteseattle.com">Ignite Seattle</a>. You can track his web travels at <a href="http://trufflehoney.com/">TruffleHoney</a>., speakerldap: BradyForresthttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/e0c8p2010-05-10T18:39:11.857ZRow: 33speakertitle: Brett Bavar, speakercompany: Google, speakerabstract: Brett is a software engineer working on Google's AJAX APIs. Brett has worked on a few other projects during his time at Google, including the integration of Google Reader with Google Translate, which was his first 20% project., speakerldap: BrettBavarhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dqi9q2010-05-10T18:39:11.857ZRow: 34speakertitle: Brett Slatkin, speakercompany: Google, speakerabstract: Brett is the co-creator of the PubSubHubbub protocol and a Software Engineer on the Google App Engine team. He joined Google in 2005. He earned his B.S. in Computer Engineering from Columbia University in the City of New York. He lives in San Francisco., speakerldap: BrettSlatkinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/drwu72010-05-10T18:39:11.857ZRow: 35speakertitle: Brian Kennish, speakercompany: Google, speakerabstract: Brian was the first support engineer at Google back when the AdWords API was launched. Since then, he’s helped launch four more Google developer products - Gadget Ads, the Analytics API, Wave, and Chrome Extensions. Brian is currently a developer advocate for Google Chrome., speakerldap: BrianKennishhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dtbek2010-05-10T18:39:11.857ZRow: 36speakertitle: Brian Rakowski, speakercompany: Google, speakerabstract: Brian joined Google as the company's first Associate Product Manager in 2002. He worked on Gmail through launch until 2005 when he moved to Google's Zurich office. Brian has since returned to Google headquarters and has been working on Google Chrome since the project began. , speakerldap: BrianRakowskihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/dupz12010-05-10T18:39:11.857ZRow: 37speakertitle: Brian Swetland, speakercompany: Google, speakerabstract: Brian has been the Android Systems/Kernel technical lead since Android's startup days in 2005. Prior to Android, Brian was responsible for the Hiptop OS and the 1.0 virtual machine at Danger, Inc. Over ten years of mobile/embedded OS development has yet to completely destroy his sanity., speakerldap: BrianSwetlandhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/e7d2q2010-05-10T18:39:11.857ZRow: 38speakertitle: Brian W. Fitzpatrick, speakercompany: Google, speakerabstract: Brian started Google's Chicago engineering office in 2005. An open source contributor for over 10 years, Brian is the engineering manager for Google's Data Liberation Front, a member of the Apache Software Foundation, a former engineer at Apple and CollabNet, a Subversion developer, and a co-author of "Version Control with Subversion"., speakerldap: BrianWFitzpatrickhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/e8rn72010-05-10T18:39:11.857ZRow: 39speakertitle: Bruce Johnson, speakercompany: Google, speakerabstract: Bruce founded the Google Web Toolkit project and Google's engineering office in Atlanta, Georgia., speakerldap: BruceJohnsonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ea67k2010-05-10T18:39:11.857ZRow: 40speakertitle: Bryan McQuade, speakercompany: Google, speakerabstract: During his time at Google, Bryan has contributed to projects that make the web faster, including Shared Dictionary Compression over HTTP, optimizing web servers to better utilize HTTP, and most recently, the Page Speed web performance tool. Prior to working on web performance, Bryan was the first full time engineer on the Google TV Ads team, where he helped to build some of Google’s TV ad auction and video management systems., speakerldap: BryanMcQuadehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ebks12010-05-10T18:39:11.857ZRow: 41speakertitle: Chris Chabot, speakercompany: Google, speakerabstract: Chris is a Developer Advocate at Google, who's interested in Open Source, OpenSocial, and trying to do the impossible. Most recently he's been the driving force behind PHP Shindig, the reference OpenSocial server implementation, Partuza a popular open-source example social network site that shows how to use OpenID, OAuth and OpenSocial, and the OpenSocial PHP client libraries., speakerldap: ChrisChabothttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ihves2010-05-10T18:39:11.857ZRow: 42speakertitle: Chris Cole, speakercompany: MySpace, speakerabstract: Chris focuses on guiding and implementing open solutions for MySpace as a software architect and a core contributor and committer to the OpenSocial specification. Recently he co-authored the book “Building OpenSocial Apps.” He’s been an Internet developer from the gopher and Mosaic days, through the dot-com era, and into today’s social web and beyond., speakerldap: ChrisColehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/e1qt22010-05-10T18:39:11.857ZRow: 43speakertitle: Chris DiBona, speakercompany: Google, speakerabstract: Chris is the open source and public sector programs manager at Google. His team oversees license compliance and supports the open source developer community through programs such as the Google Summer of Code and through the release of open source software projects and patches. In the public sector space, he looks after Google Moderator and the polling locations API., speakerldap: ChrisDiBonahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/e35dj2010-05-10T18:39:11.857ZRow: 44speakertitle: Chris Dixon, speakercompany: Hunch, speakerabstract: Chris is co-founder/CEO of Hunch and co-founder of Founder Collective, a $40M early-stage VC fund. Previously he was co-founder/CEO of SiteAdvisor which was acquired by McAfee. As a software developer, Chris worked on high speed trading algorithms and later on multimedia software. He is a personal investor in startups including Skype, Gerson Lerhman Group, TrialPay, Invite Media, OMGPOP, DocVerse and Knewton. He has a BA & MA from Columbia and Harvard., speakerldap: ChrisDixonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/e4jxw2010-05-10T18:39:11.857ZRow: 45speakertitle: Chris Lambert, speakercompany: Google, speakerabstract: Chris is a software engineer working on location based services, including Google Latitude and the HTML5 network location provider in Chrome and Firefox. Previously, Chris spent some time in Google Research working on data mining for natural language translation. Chris graduated from Northeastern University in Boston with his BS in Computer Science., speakerldap: ChrisLamberthttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/e5yid2010-05-10T18:39:11.857ZRow: 46speakertitle: Chris Messina, speakercompany: Google, speakerabstract: Chris got his start in 2004 by leading community marketing through the launch of Firefox. He is a board member of the OpenID and Open Web Foundations and plays an instrumental role in advancing OAuth and safer online computing. In 2008, Chris received the Google Open Source Award recognizing his community work on initiatives like microformats. He also co-founded the coworking and BarCamp communities and introduced hashtags on Twitter., speakerldap: ChrisMessinahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/inhog2010-05-10T18:39:11.857ZRow: 47speakertitle: Chris Nesladek, speakercompany: Google, speakerabstract: Chris is a lead interaction designer on the Android project, with a focus on social. His work can be seen across the platform with the Quick Contact widget as well as the Contacts and Facebook applications. Prior to Android, Chris worked on projects with Danger, Intuit, and Sony Design Center. In addition to staying in shape through tennis and triathlon-related sports, Chris is an avid pizza aficionado., speakerldap: ChrisNesladekhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/eilm22010-05-10T18:39:11.857ZRow: 48speakertitle: Chris Pruett, speakercompany: Google, speakerabstract: Chris works from Google's Japan office to support local Android application development. Prior to joining the Android team he worked as an engineer on Lively, Google's 3D online world. Chris worked for several years at a subsidiary of Activision, Inc developing video games. In his free time Chris enjoys dissecting horror games. Read about his research at <a href="http://www.dreamdawn.com/sh">http://www.dreamdawn.com/sh</a>., speakerldap: ChrisPruetthttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ek06j2010-05-10T18:39:11.857ZRow: 49speakertitle: Chris Schalk, speakercompany: Google, speakerabstract: Chris is a Developer Advocate at Google who engages both the App Engine and Enterprise OpenSocial communities. He has also worked on Google Friend Connect, Google AJAX APIs, Maps, Gears and Google Web Toolkit. Prior to Google, Chris was a Principal Product Manager at Oracle in the development tools group as well as co-author of "JavaServer Faces: The Complete Reference". In his spare time, he plays trumpet in local Bay Area symphonies., speakerldap: ChrisSchalkhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/eleqw2010-05-10T18:39:11.857ZRow: 50speakertitle: Chris Vander Mey, speakercompany: Google, speakerabstract: Chris is a Sr. Product Manager at Google where he works on Google Apps. He's shipped the Google Apps Marketplace, Google Apps Sync for Microsoft Outlook, elements of Chrome, and Google Pack. Before Google, Chris was an engineering manager at Amazon and an early member of multiple startups. He earned a Masters of Engineering Management from Dartmouth and BS in Aerospace Engineering from the University of Virgina., speakerldap: ChrisVanderMeyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/emtbd2010-05-10T18:39:11.857ZRow: 51speakertitle: Chrix Finne, speakercompany: Google, speakerabstract: Chrix (with an 'x'!) is a Product Manager at Google focused on Mobile Ads, where he is responsible for search ads and ads quality as well as mobile features in Google Analytics. Previously, he was Product Manager for Google Reader, where he helped develop some of Google's first social sharing features. Chrix is a graduate of Harvard University, and has also worked at Microsoft and Intellectual Ventures., speakerldap: ChrixFinnehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/eczce2010-05-10T18:39:11.857ZRow: 52speakertitle: Chuck Dietrich, speakercompany: Sliderocket, speakerabstract: Chuck is the CEO of SlideRocket. Thousands of companies use SlideRocket’s web based presentation application to create stunning and interactive presentations that can be centrally managed and measured. Previously, Chuck spent 9 years at Salesforce.com and was the General Manager & Vice President for Salesforce.com Mobile. Chuck holds a BA from University of Colorado in Economics and an MBA from University of Utah. , speakerldap: ChuckDietrichhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/eedwv2010-05-10T18:39:11.857ZRow: 53speakertitle: Ciaran Rochford, speakercompany: Samsung, speakerabstract: Ciaran is Director of Software Engineering at Samsung Mobile R&D lab in San Jose, California, where he leads the Android R&D effort. Since joining Samsung he has helped form a team experts focusing on JME and Android. Prior to joining Samsung, Ciaran worked at Sun Microsystems on the JME stack from its inception and related mobile platform technologies., speakerldap: CiaranRochfordhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ij9z92010-05-10T18:39:11.857ZRow: 54speakertitle: Dan Bornstein, speakercompany: Google, speakerabstract: Dan joined Google in 2005 to join the Android project, continuing his work on virtual machines and related technology. He designed and co-developed the Dalvik virtual machine and core libraries, and he continues to lead the Dalvik team today. Dan earned an ScB in Cognitive Science from Brown University, and just for the record, he does not believe that he has any Icelandic ancestors., speakerldap: DanBornsteinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/efsh82010-05-10T18:39:11.857ZRow: 55speakertitle: Dan Holevoet, speakercompany: Google, speakerabstract: Dan Holevoet is a Developer Programs Engineer at Google, focused on Gadgets and Apps. In his free time he likes gardening, playing video games, and recycling speaker bios., speakerldap: DanHolevoet
+
+http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/eh71p2010-05-10T18:39:11.857ZRow: 56speakertitle: Dan Peterson, speakercompany: Google, speakerabstract: Dan is a Product Manager on Google Wave and President of the OpenSocial Foundation. Previously, Dan led the Google Web Toolkit (GWT) team as it became an open source project and worked on Google's infrastructure team on web search and data center management. Dan earned a B.S. in Computer Science from the University of Illinois at Urbana-Champaign, as well as minors in Technology & Management and philosophy. , speakerldap: DanPetersonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/etu5e2010-05-10T18:39:11.857ZRow: 57speakertitle: Daniel Danilatos, speakercompany: Google, speakerabstract: Dan is the tech lead for the Google Wave editor and has been using GWT for two years. Dan lives in Sydney, Australia. He earned a B.S. in Computer Science from the University of New South Wales. , speakerldap: DanielDanilatoshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ewna82010-05-10T18:39:11.857ZRow: 58speakertitle: Daniel Lee, speakercompany: Google, speakerabstract: Daniel joined the Developer Relations team at Google over 3 years ago. He dedicated most of his time supporting and building up the community around the Google Gadgets API for iGoogle. After spending over two years on the product, he shifted focus onto supporting the Google Maps APIs., speakerldap: DanielLeehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ey1up2010-05-10T18:39:11.857ZRow: 59speakertitle: Daniel Raffel, speakercompany: Yahoo!, speakerabstract: Daniel is a Product Manager at Yahoo! on the Open Strategy team. He launched Yahoo! Pipes and Yahoo! Messenger with Voice. Previously, he worked at Songbird, ABC News Online, and CondeNet. Earlier in his career his passion for food led him to pursue a culinary degree which culminated in a stint as a chef in the kitchen of Thomas Keller’s NYC restaurant per se. Daniel received a Masters from the Interactive Telecommunications Program at NYU. , speakerldap: DanielRaffelhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/eo7vq2010-05-10T18:39:11.857ZRow: 60speakertitle: Dave Day, speakercompany: Google, speakerabstract: Dave works in the Sydney office as the Tech Lead of the Maps Javascript API v2, as well as Maps Labs. Before joining Google, Dave studied electrical engineering and computer science at Sydney University and worked in the power industry on simulation and database software., speakerldap: DaveDayhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/epmg72010-05-10T18:39:11.857ZRow: 61speakertitle: Dave McClure, speakercompany: 500 Hats, speakerabstract: Dave likes to hang out with entrepreneurs, and occasionally help or invest in their startups if they let him. Dave has been geeking out in Silicon Valley for over twenty years, and has worked with companies such as PayPal, Mint, Founders Fund, Facebook, LinkedIn, SlideShare, Twilio, Simply Hired, O'Reilly Media, Intel, & Microsoft. Dave also likes to play ultimate frisbee when his knees don't hurt., speakerldap: DaveSparkshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/er10k2010-05-10T18:39:11.857ZRow: 62speakertitle: Dave Sparks, speakercompany: Google, speakerabstract: Dave is the technical lead for the Android media framework. He was the principal author of the MMA's Downloadable Sounds specification (DLS) now part of the MPEG-4 and 3GPP standards., speakerldap: DaveMcClurehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/esfl12010-05-10T18:39:11.857ZRow: 63speakertitle: Dave Springer, speakercompany: Google, speakerabstract: David joined Google in 2008 and currently works on developer-facing tools for the Google Chrome and Native Client application development environment. Prior to Google, David was at Apple where he developed debugging and performance measuring tools for OpenGL. He also worked at NeXT Computer where he developed Interactive RenderMan and the 3D aspects of NeXTSTEP. David was a founder of Alias Research, Inc. and received an Academy Award for his work on Alias Power Animator., speakerldap: DaveSpringerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/a59y22010-05-10T18:39:11.857ZRow: 64speakertitle: Dave Wang, speakercompany: Booyah, speakerabstract: Dave is a veteran of the exploding geo-location space with a proven track record in both the application and technology-enabling layers. Dave has been integral to driving the product roadmap, design, and rapid user growth of the MyTown franchise. Prior to joining Booyah, Dave was the Sr. Director of Marketing at SiRF/Centrality, focusing on location platforms and services. Dave graduated from Stanford University with a dual Masters in EE/MS&E Dave and received a BS in EE/CS from UC Berkeley., speakerldap: DaveWanghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/a6oij2010-05-10T18:39:11.857ZRow: 65speakertitle: David Byttow, speakercompany: Google, speakerabstract: David has been a software engineer for 8 years and is currently working on Google Wave out of Mountain View, CA where he has focused his efforts primarily on Google Wave APIs. Prior to joining Google in 2008, David developed video games for popular gaming consoles and has since hung up his C++ hat to embrace all sorts of newfangled languages and technologies., speakerldap: DavidByttowhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/a832w2010-05-10T18:39:11.857ZRow: 66speakertitle: David Erb, speakercompany: Google, speakerabstract: David is a technical lead at Google in Seattle. Since joining Google in 2005, David has also worked on projects related to mobile phones and telephone systems. Prior to Google, David worked at Microsoft, Borland, SRI International, and numerous start-ups., speakerldap: DavidErbhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/a9hnd2010-05-10T18:39:11.857ZRow: 67speakertitle: David Glazer, speakercompany: Google, speakerabstract: David is an Engineering Director at Google where he leads the Developer Platforms team and is responsible for Google's developer-facing APIs, tools, and hosting offerings. He is active in Google's ongoing contributions to the social web, including being on the board of the OpenSocial Foundation. Prior to Google, David was a founder of Eloquent (later acquired by Open Text) and Verity., speakerldap: DavidGlazerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/9znoe2010-05-10T18:39:11.857ZRow: 68speakertitle: David LaPalomento, speakercompany: Novell, Inc., speakerabstract: David is the lead frontend developer for the Novell Pulse project. He is interested in the challenges of building large-scale and concurrent systems, test-driven development and delivering innovative user experiences through emerging web standards. He has worked on Pulse since its inception. , speakerldap: DavidLaPalomentohttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/a128v2010-05-10T18:39:11.857ZRow: 69speakertitle: David Primmer, speakercompany: Google, speakerabstract: David is a software engineer working on the security technologies used in Google Developer Platforms, including OAuth, OpenID and Secure Data Connector. David is a former systems and database administrator with a wide background managing the business computing environment., speakerldap: DavidPrimmerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/a2gt82010-05-10T18:39:11.857ZRow: 70speakertitle: Debajit Ghosh, speakercompany: Google, speakerabstract: Debajit is the tech lead manager for the Android Sync Team and has worked on Android through all of its major releases. He previously worked on Google’s mobile efforts, focusing on server-side infrastructure for browser-based properties. Prior to Google, Debajit worked on speech recognition technology at Nuance Communications and has Bachelor’s and Master’s degrees from MIT., speakerldap: DebajitGhoshhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/a3vdp2010-05-10T18:39:11.857ZRow: 71speakertitle: DeWitt Clinton, speakercompany: Google, speakerabstract: DeWitt is a technical lead on Google's developer team, focused on building product APIs, growing the developer ecosystem, and defending the open web. Previously, DeWitt held engineering and product management roles at Amazon, Travelocity, Microsoft, several successful early Internet ventures, and several not-quite-as-successful Internet adventures. DeWitt holds a BA in Computer Science and Political Science from Williams College. He enjoys each and every day., speakerldap: DeWittClintonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ivx4q2010-05-10T18:39:11.857ZRow: 72speakertitle: Dhanji Prasanna, speakercompany: Google, speakerabstract: Dhanji contributes to open source projects in his free time such as Google Guice, Google Wave, Sitebricks and the MVEL language. Dhanji is the author of "Dependency Injection: Design Patterns", a book published by Manning in 2009. He represents Google on several Java expert groups including Servlet and JAX-RS. Say hi to him at: <a href="http://twitter.com/dhanji">http://twitter.com/dhanji</a>., speakerldap: DhanjiPrasannahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/agihe2010-05-10T18:39:11.857ZRow: 73speakertitle: Dick Costolo, speakercompany: Twitter, speakerabstract: Dick is the COO of Twitter, Inc. Prior to Twitter, he worked in the Ads product group at Google. Dick was co-founder and CEO of FeedBurner, which was acquired by Google in June 2007. , speakerldap: DickCostolohttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ahx1v2010-05-10T18:39:11.857ZRow: 74speakertitle: Dino Brusco, speakercompany: Motorola, speakerabstract: Dino is the Senior Director of Android Product Management at Motorola. In the past, he has held key management positions with Palmsource, Turbolinux, Hewlett-Packard and Concurrent Computer Corp. Dino has led product development, software developer communities, marketing, software support and services organizations., speakerldap: DinoBruscohttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ajbm82010-05-10T18:39:11.857ZRow: 75speakertitle: Don Dodge, speakercompany: Google, speakerabstract: Don is a Developer Advocate focusing on helping developers work with Google Apps. Don recently joined Google after 5 years as a developer evangelist at Microsoft. Don is a veteran of five start-ups including Forte Software, AltaVista, Napster, Bowstreet, and Groove Networks and has been in the software business for more than 25 years. Don spent 5 years at Microsoft working with VCs and start-ups in the greater Boston area., speakerldap: DonDodgehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/akq6p2010-05-10T18:39:11.857ZRow: 76speakertitle: Don Schwarz, speakercompany: Google, speakerabstract: Don is a software engineer in Google's Chicago engineering office. He has worked on a number of projects including Google Mashup Editor and most recently as one of the co-creators of Google App Engine for Java. Prior to joining Google, Don wrote distributed systems for a large financial institution., speakerldap: DonSchwarzhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/aaw7q2010-05-10T18:39:11.857ZRow: 77speakertitle: Douwe Osinga, speakercompany: Google, speakerabstract: Douwe is the technical lead for the Google Wave APIs. After studying Computer Science and Philosophy at the Free University in Amsterdam, Douwe started various companies with widely differing amounts of success. After becoming bored with talking to customers, he joined Google in 2004, working on Search Quality, Google Trends, and on SMS Channels in India. , speakerldap: DouweOsingahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/acas72010-05-10T18:39:11.857ZRow: 78speakertitle: Eric Ayers, speakercompany: Google, speakerabstract: Eric is a Software Engineer on the Speed Tracer team and is also the maintainer of the Google API Library for GWT. He holds a Master's Degree in Computer Science from the Georgia Institute of Technology. Eric enjoys hobby electronics and is a 5 year veteran mentor of the FIRST program, inspiring elementary through high school students to develop interest in pursuing careers in technology through building robots. , speakerldap: EricAyershttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/adpck2010-05-10T18:39:11.857ZRow: 79speakertitle: Eric Bidelman, speakercompany: Google, speakerabstract: Eric is an engineer and developer advocate at Google. He works on the Google Data APIs, primarily on the Google Documents List, Google Health, and the Authentication APIs. Prior to Google, Eric worked as a software engineer at the University of Michigan where he designed rich web applications and APIs for the university's 19 libraries. Eric holds a B.S.E in Computer Engineering and a B.S.E in Electrical Engineering from the University of Michigan, Ann Arbor., speakerldap: EricBidelmanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/af3x12010-05-10T18:39:11.857ZRow: 80speakertitle: Erik Hellman, speakercompany: Sony Ericsson, speakerabstract: Erik has worked on projects ranging from large-scale telecom systems to mobile application development; now he is one of the principal Software Architects for Android at Sony Ericsson. Despite being severely colorblind, he manages to oversee most of the architectural aspects of Android application development within Sony Ericsson while still having time to write code himself., speakerldap: ErikHellmanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/arr0q2010-05-10T18:39:11.857ZRow: 81speakertitle: Erik Kay, speakercompany: Google, speakerabstract: Erik is an engineer on the Google Chrome team. He's currently a lead on the extensions project and also designed Chrome's autoupdate system., speakerldap: ErikKayhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/at5l72010-05-10T18:39:11.857ZRow: 82speakertitle: Evan Roseman, speakercompany: Google, speakerabstract: Evan joined Google in 2005 and is a software engineer in Google's Search Quality Team. His primary focus is on Webspam in Google's search index. Prior to joining Google, Evan studied Computer Science at Rice University., speakerldap: EvanRosemanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/auk5k2010-05-10T18:39:11.857ZRow: 83speakertitle: Evin Levey, speakercompany: Google, speakerabstract: Evin is a Product Manager on the Google Apps team, managing the Docs Platform and Google Apps Script. Before joining Google three years ago, he spent more than eight years at a video game middleware startup, working variously as coder, manager and salesman, focused exclusively on the developer community. Evin has a Masters in Computer Science from Trinity College, Dublin., speakerldap: EvinLeveyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/avyq12010-05-10T18:39:11.857ZRow: 84speakertitle: Gareth McSorley, speakercompany: Google, speakerabstract: Gareth is a Software Engineer with Google Switzerland who spends most of his time tinkering with the YouTube APIs. His main focus is the handling of video uploads., speakerldap: GarethMcSorleyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/am4r22010-05-10T18:39:11.857ZRow: 85speakertitle: German Bauer, speakercompany: Google, speakerabstract: German joined Google in 2006, working on the Android mobile operating system UI on both interaction and visual design. Before Google, he worked at Adobe, Apple, Claris, Netscape and Motorola on mobile and desktop products. German has a background in Industrial Design working in medical and consumer areas., speakerldap: GermanBauerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/anjbj2010-05-10T18:39:11.857ZRow: 86speakertitle: Gideon Mann, speakercompany: Google, speakerabstract: Gideon has worked as a research scientist at Google in New York City since 2007. His recent published work has been in weakly supervised learning and large-scale distributed training., speakerldap: GideonMannhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/aoxvw2010-05-10T18:39:11.857ZRow: 87speakertitle: Greg D'alesandre, speakercompany: Google, speakerabstract: Greg is a Product Manager on Google Wave based in Sydney, Australia. Before heading down under he was a PM for Google Sites and Google Groups. Pre-Google Greg worked at a variety of start-ups in a variety of roles, most recently as a Product Manager for JotSpot leading up to the Google Acquisition. Eons ago, when the sun was still young, Greg received his ScB and ScM in Electrical Engineering from Brown University. , speakerldap: GregD'alesandrehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/aqcgd2010-05-10T18:39:11.857ZRow: 88speakertitle: Greg Grothaus, speakercompany: Google, speakerabstract: Greg is a Staff Software Engineer who joined Google's Search Quality team in 2005, where he is responsible for maintaining the quality of the search results in Google's search index. Prior to Google, he studied Computer Science with an interest in Bioinformatics at Virginia Tech., speakerldap: GregGrothaushttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/b2zk22010-05-10T18:39:11.857ZRow: 89speakertitle: Guido van Rossum, speakercompany: Google, speakerabstract: Guido is the creator of the Python programming language. He joined Google in 2005. In 2007 he joined the Google App Engine team, where he has been working on Python language support, API design, UI programming, the Admin Console, and developer tools., speakerldap: GuidovanRossumhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/b5sow2010-05-10T18:39:11.857ZRow: 90speakertitle: Helen Chen, speakercompany: Cisco, speakerabstract: Helen is a software development manager at Emerging Technologies Group with Cisco Systems, Inc., where she leads OpenSocial enterprise adoption at Cisco Pulse and web application development. Her expertise includes web 2.0, User Experience, and social applications., speakerldap: HelenChenhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/b779d2010-05-10T18:39:11.857ZRow: 91speakertitle: Ian Fette, speakercompany: Google, speakerabstract: Ian joined the Google Chrome team as a product manager in 2007, focused on security, browser infrastructure, and the team's implementation and development of new web standards. Prior to joining Google, Ian worked for the U.S. Government. Ian has degrees in Computer Science from the University of Michigan and Carnegie Mellon University., speakerldap: IanFettehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/axdae2010-05-10T18:39:11.857ZRow: 92speakertitle: Ian Lewis, speakercompany: Google, speakerabstract: Ian has spent many years in the computer game industry. He was part of the launch team for the Xbox 360 and is credited on numerous titles such as Halo 3 and Grand Theft Auto 4. He joined Google in 2009 and is currently working on the Native Client team to make Chrome the ultimate platform for developers., speakerldap: IanLewishttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ayruv2010-05-10T18:39:11.857ZRow: 93speakertitle: Ian Taylor, speakercompany: Google, speakerabstract: Ian works on the Go compiler. He is the primary author of the gold linker and (years ago) Taylor UUCP, and has worked on a number of other free software projects., speakerldap: IanTaylorhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/b06f82010-05-10T18:39:11.857ZRow: 94speakertitle: Irwin Boutboul, speakercompany: Google, speakerabstract: Irwin is a software engineer with Google NYC. He works on building enterprise applications on App Engine. Before joining Google, Irwin worked on grid computing, VoIP and social networks at IBM. He then enjoyed going bankrupt with Lehman Brothers building real time trading systems in Java., speakerldap: IrwinBoutboulhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/b1kzp2010-05-10T18:39:11.857ZRow: 95speakertitle: J.D. Zamfirescu Pereira, speakercompany: Google, speakerabstract: J.D. is a software engineer focused on open sourcing the Google Wave code. J.D. left Google to become CTO at AppJet, the makers of EtherPad. AppJet was acquired by Google in December 2009, and J.D. is excited to be back -- this time, in sunny Sydney, Australia. He holds S.B. and M.Eng. degrees from MIT, speakerldap: J.D.ZamfirescuPereirahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/be8562010-05-10T18:39:11.857ZRow: 96speakertitle: Jaime Yap, speakercompany: Google, speakerabstract: Jaime is a software engineer on the Google Speed Tracer team. He is a contributor to the Google Web Toolkit project, and loves expanding the boundaries for what can be accomplished in the browser. Jaime earned his Masters degree in Computer Science from the Georgia Institute of Technology in 2007, and has been with Google since 2008. He currently resides in Atlanta and enjoys long walks on the beach., speakerldap: JaimeYaphttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bfmpn2010-05-10T18:39:11.857ZRow: 97speakertitle: James Kelm, speakercompany: Google, speakerabstract: Jim is a Product Manager at Google responsible for AdSense for Mobile Applications. He previously worked on a variety of payments, commerce, shopping, and mobile projects at Google. Jim holds degrees from Columbia and MIT. Before Google he worked at Sun Microsystems on Solaris and Apple on consumer portables. Outside work, Jim has run or consulted on Internet strategy in several high profile political campaigns., speakerldap: JamesKelmhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bh1a02010-05-10T18:39:11.857ZRow: 98speakertitle: James Young, speakercompany: Local Credit Union, speakerabstract: James is a happy father, husband, and geek. He voraciously read books and will read anything. James also builds (and fights) model warships. He belongs to northern Utah and feels that life isn't complete without mountains. James goes backpacking in the summer and snowboarding in the winter., speakerldap: JamesYounghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ba0fz2010-05-10T18:39:11.857ZRow: 99speakertitle: Jay Simons, speakercompany: Atlassian Software, speakerabstract: Jay leads sales & marketing for Atlassian, who sell ~$60M annually in software development tools directly to developers over the Web. Atlassian's tools include JIRA, an issue tracker for IT project management, Confluence, an advanced enterprise wiki, and Studio, a hosted software development suite integrated with Google Apps., speakerldap: JaySimonshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/b8lvi2010-05-10T18:39:11.857ZRow: 100speakertitle: Jeff Chang, speakercompany: Google, speakerabstract: Jeff joined the Google Chrome team as a product manager in September 2009. He currently focuses on the web platform, plug-ins, and Chrome's user-facing features. Prior to joining Google, Jeff received a B.S. in Computer Science and Engineering from MIT., speakerldap: JeffChanghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bifuh2010-05-10T18:39:11.857ZRow: 101speakertitle: Jeff Clavier, speakercompany: SoftTech VC, speakerabstract: Jeff is the Founder and Managing Partner of SoftTech VC, one of the most active seed stage investors in Web 2.0 startups. Since 2004, Jeff has invested in more than 60 consumer Internet companies in areas like social media, communities, search, gaming or consumer infrastructure, almost exclusively in Silicon Valley., speakerldap: JeffClavierhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bpgoi2010-05-10T18:39:11.857ZRow: 102speakertitle: Jeff Scudder, speakercompany: Google, speakerabstract: Jeff has been an engineer with Google since 2006 and has worked on products including the AdWords API, Google Data APIs, App Engine, Google AJAX APIs, and Google Web Elements. After graduating from Baylor with a CS degree, Jeff worked on web services at Boeing. When not crafting code, Jeff's been known to strum the guitar and be otherwise musical., speakerldap: JeffScudderhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bbf0c2010-05-10T18:39:11.857ZRow: 103speakertitle: Jeffrey Posnick, speakercompany: Google, speakerabstract: Jeff is a member of Google's Developer Relations team. He currently helps developers navigate the ins and outs of the YouTube APIs. Based in New York City, Jeff is happy to be making his first trip out to Google I/O in San Francisco., speakerldap: JeffreyPosnickhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/btodt2010-05-10T18:39:11.857ZRow: 104speakertitle: Jez Fletcher, speakercompany: Google, speakerabstract: Jez joined Google in 2008 after deciding academia wasn't the thrill-ride he'd expected. Since joining Google, Jez has worked on just about everything in the Google Geo APIs spectrum, including the v2 and v3 JS API, the Maps API for Flash and the Static Map API., speakerldap: JezFletcherhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bqv8z2010-05-10T18:39:11.857ZRow: 105speakertitle: Jim Palmer, speakercompany: Google, speakerabstract: Jim is the head of the Android UX team. Prior to joining Google, he worked on consumer experiences for Motorola, Good Technology, Liberate, and Apple. , speakerldap: JimPalmerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bl8zb2010-05-10T18:39:11.857ZRow: 106speakertitle: Jimin Li, speakercompany: Google, speakerabstract: Jimin is a Software Engineer on the Google Wave team working on Wave Media and API Projects. Prior to Google Wave, Jimin led Revenue and Reporting team of Google Relationship Manager. Prior to Google, Jimin was a Software Engineer in the Customer Relationship Management industry., speakerldap: JiminLihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bo2452010-05-10T18:39:11.857ZRow: 107speakertitle: Jochen Bekmann, speakercompany: Google, speakerabstract: Jochen is Tech Lead for federation on Google Wave. Prior to that, he worked on concurrency control and the wave server and on Google Maps at some point. He worked on TCP/IP at Microsoft and was part of a few start-ups before that. The dazzling allure of academia didn't entirely pass him by (PhD(UNSW), BScHons,BCom(UCT)). , speakerldap: JochenBekmannhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/c0p7u2010-05-10T18:39:11.857ZRow: 108speakertitle: Joe Kraus, speakercompany: Google, speakerabstract: Joe is a partner at Google Ventures focused on mobile, gaming and local services. Previously, he was a two-time entrepreneur. In 1993, he co-founded Excite.com, an early Internet search engine. In 2004, he co-founded JotSpot, a wiki company that was acquired by Google in 2006. While at Google, Joe held multiple product management roles. He is also on the board of the EFF., speakerldap: JoeKraushttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bmnjo2010-05-10T18:39:11.857ZRow: 109speakertitle: Joel Webber, speakercompany: Google, speakerabstract: Joel is a software engineer at Google and the co-creator of the Google Web Toolkit. Before working at Google, Joel spent his time banging bits and building cross-platform UI frameworks for mobile devices at AppForge. Still earlier, he was a game engine developer, but oddly enough finds working at Google to be more fun than writing games., speakerldap: JoelWebberhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bv2y62010-05-10T18:39:11.857ZRow: 110speakertitle: John Panzer, speakercompany: Google, speakerabstract: John hacks social software, standards, and products. He has participated in the Atom, AtomPub, OpenID, OAuth, OpenSocial, and Salmon standards efforts. He has helped to launch groups and blogging products at AOL and most recently managed Blogger. He is now working on open standards for the Social Web., speakerldap: JohnPanzerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/c4wx52010-05-10T18:39:11.857ZRow: 111speakertitle: John Zeratsky, speakercompany: YouTube, speakerabstract: John is a senior designer at YouTube. Prior to that he was a user experience lead at Google, focused on the company's suite of advertising products for agencies. From 2005 through 2007, he helped create FeedBurner, which was acquired by Google in June 2007., speakerldap: JohnZeratskyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/c3ico2010-05-10T18:39:11.857ZRow: 112speakertitle: Joon Kang, speakercompany: LG Electronics Mobile Research, speakerabstract: Joon has a broad and unique combination of experience in new business development, mobile and web technology and product strategy development. Prior to joining LG, Joon has been playing a pivotal role at Yahoo Search Marketing where he led global technology development and partner management. Joon studies at POSTECH, Korea, Columbia University and U.C. Berkeley, received Bachelor of Science and MBA emphasis on international business and technology management., speakerldap: JoongKanghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bwhin2010-05-10T18:39:11.857ZRow: 113speakertitle: Joseph Schorr, speakercompany: Google, speakerabstract: Joey is a software engineer on the Google Data team and is responsible for all API configuration development, including API automated configuration tools. Joey has worked at Google for almost two years and is a graduate of the University of Pennsylvania, with degrees in Computer Science and Business., speakerldap: JosephSchorrhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bxw302010-05-10T18:39:11.857ZRow: 114speakertitle: Joseph Smarr, speakercompany: Google, speakerabstract: Joseph is a software engineer at Google, focused on socially enabling the web using open standards. Previously, he was Plaxo's Chief Technology Officer. He is also a member of the Board of Directors of the OpenID Foundation. A frequent speaker and community participant in the social networking and web development communities, Joseph has built web applications for many years., speakerldap: JosephSmarrhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ceqw02010-05-10T18:39:11.857ZRow: 115speakertitle: Josh Livni, speakercompany: Google, speakerabstract: Josh is a Developer Programs Engineer, currently focusing on KML and the Earth API. Before joining Google, he worked as a consultant, integrating analytic, cartographic, and statistical tools on the web. Josh is an active member of the open source geospatial community, and plans to spend more time paragliding right after I/O., speakerldap: JoshLivnihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cdcbn2010-05-10T18:39:11.857ZRow: 116speakertitle: Justin Mattson, speakercompany: Google, speakerabstract: Justin is a Developer Advocate at Google. For the past year he has devoted his time to Android. Before that he spent time on some of Google's ads products. He has a strong interest in operating system architectures, macro economics, and advanced armchair physics., speakerldap: JustinMattsonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/c6bhi2010-05-10T18:39:11.857ZRow: 117speakertitle: Justin McWilliams, speakercompany: Google, speakerabstract: Justin is a developer in Corporate Engineering based in Google NYC. He has been building enterprise web applications on App Engine for the past year, previously filling a desktop support role since he joined Google in 2006. Justin graduated from the University of Michigan with a BA in 2005., speakerldap: JustinMcWilliamshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cg5gh2010-05-10T18:39:11.857ZRow: 118speakertitle: Kara Swisher, speakercompany: Wall Street Journal, speakerabstract: Kara started covering digital issues for The Wall Street Journal's San Francisco bureau in 1997 and also wrote the BoomTown column about the sector. With Walt Mossberg, she co-produces and co-hosts D: All Things Digital, a major high-tech and media conference., speakerldap: KaraSwisherhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/cbxr62010-05-10T18:39:11.857ZRow: 119speakertitle: Kathrin Probst, speakercompany: Google, speakerabstract: Kathrin is a software engineer on the Google Web Toolkit team. Most recently, she has worked on making Ajax applications crawlable, and is now contributing to the next generation of the Google Plugin for Eclipse. Kathrin earned her Ph.D. in Computer Science in 2005, and has been with Google since 2008. She lives in Atlanta with her family, which she loves because she gets to spend lots of time outside., speakerldap: KathrinProbsthttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/bzanh2010-05-10T18:39:11.857ZRow: 120speakertitle: Kelly Norton, speakercompany: Google, speakerabstract: Kelly writes software for Google and enjoys working in the gooey area between humans and technology. He has a range of quirky experience from network hardware to graphic design and is a contributor to the Google Web Toolkit project. Kelly is the proud owner of two degrees both with ornate lettering, the first in electrical engineering from Georgia Tech and the second from the MIT Media Lab., speakerldap: KellyNortonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hmyxm2010-05-10T18:39:11.857ZRow: 121speakertitle: Kevin Gibbs, speakercompany: Google, speakerabstract: Kevin joined Google in 2004 and is the technical lead and manager of the Google App Engine project. Prior to his work on Google App Engine, Kevin worked for a number of years in Google's systems infrastructure group, where he worked on the cluster management systems that underlie Google's products and services. Kevin created Google Suggest, the product which provides interactive search suggestions as you type on the Google homepage., speakerldap: KevinGibbshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/caj6t2010-05-10T18:39:11.857ZRow: 122speakertitle: Kris Muller, speakercompany: Salesforce.com, speakerabstract: Kris is a product marketer with Salesforce.com. He joined the company in 2006 and enjoyed several years of selling the Force.com platform and CRM apps as a sales engineer. He's now pushing to make the workplace more collaborative and is fired up by the collective intelligence that Salesforce Chatter delivers. , speakerldap: KrisMullerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/c94mc2010-05-10T18:39:11.857ZRow: 123speakertitle: Kuan Yong, speakercompany: YouTube, speakerabstract: Kuan is the Product Manager for the YouTube developer platform. Prior to working on the YouTube APIs, he worked on enterprise video hosting, mobile syndication and monetization, launching Google Video for business and AdSense for mobile content. Kuan loves his iPhone and writes iPhone apps in his spare time., speakerldap: KuanYonghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hps2g2010-05-10T18:39:11.857ZRow: 124speakertitle: Lars Rasmussen, speakercompany: Google, speakerabstract: Lars is a member of Google's technical staff, based in the Sydney office, and with his brother Jens is co-founder of the Google Wave effort. In early 2003, the brothers co-founded a mapping start-up, Where 2 Technologies, which was acquired by Google in October of 2004. Lars joined Google and worked as one of the lead engineers in the team that turned this acquisition into Google Maps, now used by millions of people around the world. Lars holds a Ph.D. in theoretical computer science from the University of California at Berkeley. Lars has possibly the world's least developed sense of direction, and consistently types faster than he can spell. , speakerldap: LarsRasmussenhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hodi32010-05-10T18:39:11.857ZRow: 125speakertitle: Linus Upson, speakercompany: Google, speakerabstract: Linus joined Google in 2005 and is currently a vice president of engineering overseeing Google's browser products including Chrome and Chrome OS. , speakerldap: LinusUpsonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hhcny2010-05-10T18:39:11.857ZRow: 126speakertitle: Loic Le Meur, speakercompany: Seesmic, speakerabstract: Loic is Founder and CEO of Seesmic, one of the most popular Twitter and Facebook social apps. He also founded and hosts the #1 European internet conference, LeWeb, with his wife Geraldine. Loic was named one of the "25 Most Influential People on the Web" and 'Young Global Leader' by Business Week and the World Economic Forum, respectively. He lives in San Francisco and has three boys., speakerldap: LoicLeMeurhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hr6mx2010-05-10T18:39:11.857ZRow: 127speakertitle: Mano Marks, speakercompany: Google, speakerabstract: Mano joined Google's Geo API team in 2006. He helps large companies, small startups, and international aid organizations all over the world develop and deploy their content in KML and Google Maps. Before Google, Mano had an eclectic career that involved getting a Masters in History, a Masters in Information Management and Systems, and working as a data manager in small and mid-sized organizations for over a decade., speakerldap: ManoMarkshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/c7q1z2010-05-10T18:39:11.857ZRow: 128speakertitle: Marc Ridey, speakercompany: Google, speakerabstract: Marc is a Software Engineer working on Google Maps JavaScript API v3. His focus for the past year has been on mobile and performance. Marc graduated as a Computer Engineer in France before moving to Australia. Before joining Google, Marc worked as a Senior Consultant on Microsoft .NET and SQL Technologies., speakerldap: MarcRideyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hlkd92010-05-10T18:39:11.857ZRow: 129speakertitle: Marcel Prasetya, speakercompany: Google, speakerabstract: Marcel is a software engineer on Google Wave, focusing on the Google Wave Robot API. He joined Google in early 2006, and worked for the internal system team for three years before joining Google Wave. Marcel graduated from the University of Texas at Austin, with a bachelor's degree in Computer Science., speakerldap: MarcelPrasetyahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hk5ss2010-05-10T18:39:11.857ZRow: 130speakertitle: Marcelo Camelo, speakercompany: Google, speakerabstract: Marcelo is a software engineer with Google Australia, where he works in the Maps API team, with a focus on latency and mobile. Previously, Marcelo worked developing massively multiplayer games and data visualization software. Marcelo holds a B.Eng. in Metallurgical Engineering and a Masters in Industrial Engineering., speakerldap: MarceloCamelohttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hir8f2010-05-10T18:39:11.857ZRow: 131speakertitle: Mark Halvorson, speakercompany: Atlassian Software, speakerabstract: Mark is the Chief Imagineer and OpenSocial Evangelist at Atlassian Software - the makers of JIRA and Confluence. He has a long history working with portals and social enterprise software having worked in various capacities from software developer to technical marketer for Firepond, Plumtree, BEA and Oracle., speakerldap: MarkHalvorsonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hzm1f2010-05-10T18:39:11.857ZRow: 132speakertitle: Mark Stahl, speakercompany: Google, speakerabstract: Mark is a technical lead on the Google Data APIs clients team. After starting his career at Bell Labs, Mark was lured into a small networking startup just in time for the dot-com crash. In 2005, he took his experiences to Google, where he started working on the API infrastructure. He spends his time trying to figure out how to make it easy to build great APIs., speakerldap: MarkStahlhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hsl7a2010-05-10T18:39:11.857ZRow: 133speakertitle: Mark Weitzel, speakercompany: International Business Machines (IBM), speakerabstract: Mark is a Senior Technical Staff Member in IBM Software Group’s Emerging Standards and Open Source team focusing on social networking and is also Secretary of the OpenSocial Foundation. Prior to this, Mark, as part of Tivoli’s Autonomic Computing team, was responsible for several open source systems management initiatives at Eclipse and Apache. , speakerldap: MarkWeitzelhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/i2f692010-05-10T18:39:11.857ZRow: 134speakertitle: Matt Cutts, speakercompany: Google, speakerabstract: Matt is the head of the webspam team at Google where he specializes in search engine optimization (SEO) issues. He is known in the webmaster and SEO community for applying Google's Quality Guidelines. Matt sometimes discusses search issues and offers advice on website visibility in Google on his personal blog at http://www.mattcutts.com/blog/ . You can also catch him on Twitter at http://twitter.com/mattcutts, speakerldap: MattCuttshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/i10ls2010-05-10T18:39:11.857ZRow: 135speakertitle: Matt Harding, speakerabstract: Matt travels around the world and films himself dancing badly in exotic places. That's basically his job. God bless the internet., speakerldap: MattHardinghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hy7gy2010-05-10T18:39:11.857ZRow: 136speakertitle: Matt Holleran, speakercompany: Emergence Capital Partners, speakerabstract: Matt is a Venture Partner at Emergence Capital Partners. He is an expert in cloud application business models and cloud ecosystems. Prior to joining the firm, Matt’s team started and built the AppExchange and Force.com partner ecosystem at salesforce.com. He advises cloud platform companies and helps ISVs leverage them. He is interested in freemium business application companies., speakerldap: MattHolleranhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/i9g0a2010-05-10T18:39:11.857ZRow: 137speakertitle: Matt Lowrie, speakercompany: Google, speakerabstract: Matt is a Test Engineer who has worked at Google since 2006. He ensures product quality on several Geo products, including Building Maker and SketchUp., speakerldap: MattLowriehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ic9542010-05-10T18:39:11.857ZRow: 138speakertitle: Matt Mastracci, speakercompany: DotSpots, speakerabstract: Matt is a founder and CTO of DotSpots. He is currently leading the efforts to build better ways to allow users to attach images, videos and blog posts to the news in real-time using a platform based on Google Web Toolkit. Prior to DotSpots, he worked on building rich, scalable, browser-based applications for the energy sector and micropayments., speakerldap: MattMastraccihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/iaukr2010-05-10T18:39:11.857ZRow: 139speakertitle: Matt Pruden, speakercompany: Appirio, speakerabstract: Matt is a Technical Architect at Appirio who has helped transition over 200,000 users at 40 companies to Google Apps. His expertise is in enterprise use of the Google Apps platform. Prior to Appirio, Matt spent seven years in Intel IT, providing perspective into large scale technology transitions. Matt's technical repertoire includes Python, Twisted, Ruby, Java, etc., speakerldap: MattPrudenhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hwswl2010-05-10T18:39:11.857ZRow: 140speakertitle: Matt Shobe, speakercompany: Google, speakerabstract: Matt is a staff user experience designer at Google whose work centers on services for publishers. Prior to Google, Matt was a co-founder of FeedBurner and led user experience design efforts there and on two prior startups with the same team. Matt is an avid distance runner, private pilot, and skier, though no such triathlon exists (yet)., speakerldap: MattShobehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/hvec42010-05-10T18:39:11.857ZRow: 141speakertitle: Matt Tucker, speakercompany: Jive Software, speakerabstract: Matt is the CTO and Co-Founder of Jive Software. He leads technology efforts such as Jive's adoption of cloud technologies and open standards. He's also an active member of the OpenSocial and XMPP communities., speakerldap: MattTuckerhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/htzrr2010-05-10T18:39:11.857ZRow: 142speakertitle: Matthew Blain, speakercompany: Google, speakerabstract: Matthew is a software engineer on the Google App Engine team working on tools. Prior to joining App Engine, he worked on Google Toolbar. Prior to Google, Matthew had stints as a software consultant and as a developer on Microsoft Internet Explorer and InfoPath., speakerldap: MatthewBlainhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/i58b32010-05-10T18:39:11.857ZRow: 143speakertitle: Matthew Holden, speakercompany: Google, speakerabstract: Matthew is a Product Manager for the Google Maps Data API, Google Earth vector layers, and global problem reporting on Google Maps. He joined Google in 2008, and recently helped launch spatial + attribute search in the Maps Data API. The Maps Data API provides scalable hosting and searching of geographic content in Google's cloud., speakerldap: MatthewHoldenhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/i3tqm2010-05-10T18:39:11.857ZRow: 144speakertitle: Matthew Papakipos, speakercompany: Google, speakerabstract: Matthew is an Engineering Director at Google, where he runs the Chrome OS and Chrome Graphics efforts. Prior to working at Google, he was the CTO of PeakStream and Architecture Director at NVIDIA. His interests include computer hardware and software, interactive computer graphics, and games. He has over forty awarded patents in processor design and software architecture. He received his BS in Mathematics and Computer Science from Brown University., speakerldap: MatthewPapakiposhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/im3432010-05-10T18:39:11.857ZRow: 145speakertitle: Matthew Simmons, speakercompany: Google, speakerabstract: Matthew is a developer working in Google Corporate Engineering. His work centers on developing and evaluating knowledge management systems. In his spare time he can be found hanging out with his wife and daughters or doing research at the University of Michigan in pursuit of his Ph.D. in Information., speakerldap: MatthewSimmonshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ikojm2010-05-10T18:39:11.857ZRow: 146speakertitle: Matthew Tonkin, speakercompany: Memeo Inc, speakerabstract: Matt is the Mac Applications Architect at Memeo and technical lead for Memeo Connect, a client for syncing files between the desktop and Google Docs by utilizing the new API's for storing any file type. Memeo is extending Connect and Google Docs beyond the desktop to mobile platforms including Android and iPad. Matt originates from Australia and is currently living and working in Orange County, CA., speakerldap: MatthewTonkinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/i81fx2010-05-10T18:39:11.857ZRow: 147speakertitle: Max Lin, speakercompany: Google, speakerabstract: Max is a software engineer with Google Research in New York City office. Prior to Google, he published research work in video content analysis, sentiment analysis, machine learning, and cross-lingual information retrieval. He has a PhD in Computer Science from Carnegie Mellon University., speakerldap: MaxLinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/i6mvg2010-05-10T18:39:11.857ZRow: 148speakertitle: Max Ross, speakercompany: Google, speakerabstract: Max is a test-driven Software Engineer on the App Engine team where he works on the datastore and the Java runtime. He is also the founder of the Hibernate Shards project. Prior to joining Google he was a software engineer in the supply chain management and travel technology industries. He holds a BA in American Studies and Computer Science from Williams College., speakerldap: MaxRosshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/idnpl2010-05-10T18:39:11.857ZRow: 149speakertitle: Michael Fink, speakercompany: Google, speakerabstract: Michael joined Google Research in 2005, where he started the “mass personalization” effort. Since then, Michael Fink initiated several media-based projects, including YouTube's Interactive Video Annotations, which enables embedding of hyperlinks onto objects in YouTube videos. When he's not at work, you'll probably find him scuba diving in the Red Sea., speakerldap: MichaelFinkhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ixbp72010-05-10T18:39:11.857ZRow: 150speakertitle: Michael Goderbauer , speakercompany: Hasso Plattner Institute, speakerabstract: Michael is a student of Germany's #1 ranked software engineering school, the Hasso Plattner Institute in Potsdam. Right now he is in his sixth semester and he will finish his bachelor’s degree this summer. About a year ago, he and six fellow students started the processWave.org project to create a collaborative diagram editor for Google Wave. , speakerldap: MichaelGoderbauer http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/iyq9k2010-05-10T18:39:11.857ZRow: 151speakertitle: Michael Morrissey, speakercompany: Google, speakerabstract: Michael runs the Android services team at Google, which includes Market, sync, and device management. Prior to Android, he built the Sidekick/Hiptop service platform at Danger and worked on mobile products at Microsoft., speakerldap: MichaelMorrisseyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j04u12010-05-10T18:39:11.857ZRow: 152speakertitle: Michael Schwartz, speakercompany: Google, speakerabstract: Mike is a software engineer in Google's Seattle office. Before Google he worked at a number of start-up companies, and was an Associate Professor of Computer Science at the University of Colorado - Boulder., speakerldap: MichaelSchwartzhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/iqav22010-05-10T18:39:11.857ZRow: 153speakertitle: Michael Van Riper, speakercompany: Krillion, Inc., speakerabstract: Van is Chief Web Technologist at Krillion and a Java Champion. He leads the largest Java and Google Technology User Groups in Silicon Valley. Van is also a serial community organizer., speakerldap: MichaelVanRiperhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/irpfj2010-05-10T18:39:11.857ZRow: 154speakertitle: Mihai Ionescu, speakercompany: Google, speakerabstract: Since starting at Google in 2003, Mihai has worked on Google Desktop, O3D and Chrome. Lately, he is focusing on HTML5 and particularly on Chrome as a developer platform for rich web applications. Prior to Google, Mihai worked at Microsoft on Internet Explorer, Java VM, and .NET. He holds an MBA from the Berkeley Haas School of Business and an MS in Computer Science from UC Santa Barbara., speakerldap: MihaiIonescuhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/it3zw2010-05-10T18:39:11.857ZRow: 155speakertitle: Mike Aizatsky, speakercompany: Google, speakerabstract: Mike is a software engineer working on Google App Engine. He worked on Google Code Search prior to joining the App Engine team. Before joining, Google he was a Project Manager at JetBrains working on developer tools.
+, speakerldap: MikeAizatskyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/iuikd2010-05-10T18:39:11.857ZRow: 156speakertitle: Mike O'Brien, speakercompany: Appirio, speakerabstract: Mike manages Appirio's growing enterprise Google business. He has responsibility for sales and delivery of Appirio's Google customers with a focus on large global enterprise implementations. He brings over 15 years of experience leading large enterprise software deployments that involve complex business requirements for global businesses., speakerldap: MikeOBrienhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j75o22010-05-10T18:39:11.857ZRow: 157speakertitle: Ming Yong, speakercompany: Voiceroute Pte Ltd (Socialwok)
+, speakerabstract: Ming is Founder and CEO of Socialwok (http://socialwok.com), one of the most popular business social productivity service on the web for group collaboration and enterprise microblogging. Ming previously founded Druid, a popular open source project on Unified Communications for small medium businesses.He is also one of the committee member of the Singapore Google Technology User group (SG-GTUG)., speakerldap: MingYonghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j8k8j2010-05-10T18:39:11.857ZRow: 158speakertitle: Moishe Lettvin, speakercompany: Google, speakerabstract: Moishe is a software engineer working in Google's Kirkland and Seattle offices. In his time at Google, he's worked on many aspects of Google Talk and is currently working on Google App Engine., speakerldap: MoisheLettvinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j9ysw2010-05-10T18:39:11.857ZRow: 159speakertitle: Nathan Naze, speakercompany: Google, speakerabstract: Nathan is a software engineer at Google. He works on user interface for Google Books, one of many projects using Closure Library. Open sourcing Closure Library was his 20% project with Daniel Nadasi. He joined Google in 2005 after graduating from the University of Wisconsin with majors in computer science, political science, and economics. He lives in San Francisco., speakerldap: NathanNazehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jbddd2010-05-10T18:39:11.857ZRow: 160speakertitle: Nick Johnson, speakercompany: Google, speakerabstract: Nick is a Developer Programs Engineer on the App Engine team. He spends his days helping people write code for App Engine. His nights are spent writing code about App Engine and then blogging about it., speakerldap: NickJohnsonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j1jee2010-05-10T18:39:11.857ZRow: 161speakertitle: Nick Mihailovski, speakercompany: Google, speakerabstract: Nick has been working on Google Analytics for over 5 years and is currently working as a developer programs engineer to help developers create better tools, integrations and automation with Google Analytics and 3rd party systems., speakerldap: NickMihailovskihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j2xyv2010-05-10T18:39:11.857ZRow: 162speakertitle: Pamela Fox, speakercompany: Google, speakerabstract: Pamela has worked in the Developer Relations group at Google for the last 3 years. She spent 3 years supporting the Maps API community, and now is focusing her time on helping the blossoming Wave APIs community. In her free time, she likes to play around with any one of Google's 60+ APIs. , speakerldap: PamelaFoxhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j4cj82010-05-10T18:39:11.857ZRow: 163speakertitle: Paul Graham, speakercompany: Y Combinator, speakerabstract: Paul is a partner at Y Combinator and the author of On Lisp (1993), ANSI Common Lisp (1995), and Hackers & Painters (2004). In 1995, he and Robert Morris started Viaweb, the first ASP, which in 1998 became Yahoo! Store. In 2002 he discovered a simple spam filtering algorithm that inspired the current generation of filters. He has an AB from Cornell and a PhD in Computer Science from Harvard., speakerldap: PaulGrahamhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/j5r3p2010-05-10T18:39:11.857ZRow: 164speakertitle: Pavel Feldman, speakercompany: Google, speakerabstract: Pavel is a software engineer working on Google Chrome Developer Tools and WebKit's Inspector. He worked on Google Calendar prior to joining the Chrome team. Before joining Google, he was Chief Scientist at Borland working on modeling tools., speakerldap: PavelFeldmanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jie7e2010-05-10T18:39:11.857ZRow: 165speakertitle: Peter Birch, speakercompany: Google, speakerabstract: Peter joined Google in 2006 as Product Manager for Google Earth, Google's 3D earth visualization tool that combines the power of Google Search with satellite imagery, maps, terrain and 3D buildings. He is responsible for the direction, growth, and success of the Google Earth product family including Google Earth Free, Pro, and Enterprise, the Google Earth API and browser plug-in, and Google Earth for the iPhone., speakerldap: PeterBirchhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jjsrv2010-05-10T18:39:11.857ZRow: 166speakertitle: Phil Liao, speakercompany: Google, speakerabstract: Phil is a Software Engineer on the Google Wave team. Prior to joining Google, he built software in areas of education, supply chain execution, brain imaging and finance. Phil holds computer science degrees from MIT and Berkeley., speakerldap: PhilLiaohttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jl7c82010-05-10T18:39:11.857ZRow: 167speakertitle: Ray Cromwell, speakercompany: Timefire, speakerabstract: Ray is CTO and co-founder of Timefire where he dreams of flying over vast historical data landscapes. These days, he works on visualization software using Google Web Toolkit. Prior to Timefire, he led Oracle's efforts to build mobile XForms implementations and draft IETF standards for push email., speakerldap: RayCromwellhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jmlwp2010-05-10T18:39:11.857ZRow: 168speakertitle: Ray Ryan, speakercompany: Google, speakerabstract: Ray is part of the Google Web Toolkit team, and has been goading people into writing software the way he thinks they should since the 1980s. He has worked at Lighthouse Design, Sun, AOL, and has spent the last four years at Google. Recently he had a strong hand in shaping the architecture of the new GWT-based user interface for AdWords., speakerldap: RayRyanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jcrxq2010-05-10T18:39:11.857ZRow: 169speakertitle: Reto Meier, speakercompany: Google, speakerabstract: Reto is a Developer Advocate for Android at Google and is the author of 'Professional Android 2 Application Development'. As the EMEA Android Advocate he works closely with Android developers, helping them make the most of the platform to bring rich, compelling apps to the Android Market. Before Google he worked in various industries, including offshore oil and gas while in Australia and the London finance market. , speakerldap: RetoMeierhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/je6i72010-05-10T18:39:11.857ZRow: 170speakertitle: Richard Fulcher, speakercompany: Google, speakerabstract: Richard Fulcher is a senior user interface designer on the Android team, focused on communication applications. Prior to joining Google, he designed consumer experiences for TiVo, Sirius, Dell and AOL. He freely admits to playing lots of board games., speakerldap: RichardFulcherhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jfl2k2010-05-10T18:39:11.857ZRow: 171speakertitle: Richard Rabbat, speakercompany: Google, speakerabstract: Richard is a product manager at Google. He recently released Page Speed, a Firefox add-on that analyzes web pages and gives suggestions on how to improve them in addition to doing some of the optimizations itself. Richard manages the Google program to make the web faster and works on projects that power Google’s infrastructure including latency measurements for Google apps., speakerldap: RichardRabbathttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jgzn12010-05-10T18:39:11.857ZRow: 172speakertitle: Rob Pike, speakercompany: Google, speakerabstract: Rob works on distributed systems, programming languages, and software development tools. Before Google, Rob was at Bell Labs Research. He was an architect of the Plan 9 and Inferno operating systems and is the co-author with Brian Kernighan of The Unix Programming Environment and The Practice of Programming., speakerldap: RobPikehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jtmqq2010-05-10T18:39:11.857ZRow: 173speakertitle: Romain Guy, speakercompany: Google, speakerabstract: Romain is a software engineer at Google. After spending years having fun with large UIs on the desktop and talking about them at conferences, in blogs, magazines and books, Romain decided to go for the small screen and joined the Android project, an Open Source operating system for mobile phones. He’s now trying to make mobile phone UIs as fun and exciting as desktop ones., speakerldap: RomainGuyhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jv1b72010-05-10T18:39:11.857ZRow: 174speakertitle: Rus Heywood, speakercompany: Google, speakerabstract: Rus is the lead engineer for the Google PowerMeter project, which is part of Google.org. Rus came to Google through Jotspot, a collaboration tool company that Google acquired in 2006., speakerldap: RusHeywoodhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jwfvk2010-05-10T18:39:11.857ZRow: 175speakertitle: Russ Cox, speakercompany: Google, speakerabstract: Russ is a software engineer working on the Go team. Before working on Go, Russ led the creation of Code Search, Google's only regular expression-based search engine., speakerldap: RussCoxhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jxug12010-05-10T18:39:11.857ZRow: 176speakertitle: Ryan Boyd, speakercompany: Google, speakerabstract: Ryan is a developer advocate at Google focused on enabling developers to extend Google Apps and build businesses on top of Google technology. He previously worked on OpenSocial, Google Friend Connect and Google's AtomPub APIs. Prior to joining Google, Ryan worked in higher education as a web architect for RIT's web hosting environment and as web app developer., speakerldap: RyanBoydhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jo0h22010-05-10T18:39:11.857ZRow: 177speakertitle: Ryan Sarver, speakercompany: Twitter, speakerabstract: Ryan is currently the Director of Platform at Twitter where he works with developers building myriad of experiences on top of 140 characters. Previously he was the Director of Consumer Products at Skyhook Wireless where he led product initiatives that leveraged the WPS platform in consumer applications and experiences. Ryan was also one of the founding members of the W3C Geolocation API Working Group., speakerldap: RyanSarverhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jpf1j2010-05-10T18:39:11.857ZRow: 178speakertitle: Sami Shalabi, speakercompany: Google, speakerabstract: Sami is a Technical Lead Manager at Google. He is leading the engineering teams responsible for Google Friend Connect and Google Buzz APIs. Prior to Google, Sami was a co-founder of Zingku (acquired by Google), and YallaStartup. Sami holds a Bachelor’s and Master’s degrees from MIT., speakerldap: SamiShalabihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/jqtlw2010-05-10T18:39:11.857ZRow: 179speakertitle: Scott McMullan, speakercompany: Google, speakerabstract: Scott is a Senior Enterprise Program Manager focused on helping 3rd parties grow their business with Google Apps. A startup guy at heart, Scott joined Google from JotSpot, where he ran developer programs, and previously founded Inovie Software, maker of early Internet collaboration service TeamCenter. Scott holds bachelor's and master's degrees in computer science from UCSD and was a PhD student at the university's Computer Systems Lab., speakerldap: ScottMcMullanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/js86d2010-05-10T18:39:11.857ZRow: 180speakertitle: Sean Lynch, speakercompany: Google, speakerabstract: Sean is a Product Manager for Google App Engine. Since joining Google in 2008, Sean worked on Google core infrastructure before moving to the App Engine team in 2009. Sean holds a B.S. in Computer Science from the University of Waterloo., speakerldap: SeanLynchhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/f52je2010-05-10T18:39:11.857ZRow: 181speakertitle: Seth Covitz, speakercompany: Google, speakerabstract: Seth is a software engineer on Google Wave. He enjoys innovating and pushing the boundaries of the web each and every day. Prior to Google, he held positions as CTO, VP of Development, and Software Architect for companies ranging from startups to the Fortune 50. Seth holds a B.S. degree in Math and Computer Science from Carnegie Mellon University. , speakerldap: SethCovitzhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/f6h3v2010-05-10T18:39:11.857ZRow: 182speakertitle: Shih-chia Cheng, speakercompany: Google, speakerabstract: Shih-Chia is a software engineer of JAPAC iGoogle team. Prior to iGoogle, he worked on Google Maps. Shih-Chia has a Masters degree from Stanford University. His interests include social dance, games, and Chinese chess., speakerldap: Shih-chiaChenghttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/f9a8p2010-05-10T18:39:11.857ZRow: 183speakertitle: Siddartha Naidu, speakercompany: Google, speakerabstract: Siddartha has been crunching large data sets at Google since 2005 for a variety of products and as a physics grad student before that. He has worked with or on almost every data processing framework at Google and is still looking for ways to make his job easier., speakerldap: SiddarthaNaiduhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/ezg9q2010-05-10T18:39:11.857ZRow: 184speakertitle: Srikanth Rajagopalan, speakercompany: Google, speakerabstract: Srikanth is a Product Manager for Infrastructure/Advanced Projects at Google. He has tackled responsibilities ranging from rolling out Google PowerMeter to making Google’s Cloud Computing Infrastructure more efficient and scaling Google Book Search towards scanning the world's offline content., speakerldap: SrikanthRajagopalanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/f0uu72010-05-10T18:39:11.857ZRow: 185speakertitle: Stefan Haustein, speakercompany: Google, speakerabstract: Stefan is a software engineer at Google in London and is responsible for the WebOS port of Google Maps. Prior to joining Google, he worked as an engineering director at Interact!v GmbH in Cologne. Stefan has a PhD in Computer Science from the University of Dortmund. , speakerldap: Stefan Hausteinhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/f29ek2010-05-10T18:39:11.857ZRow: 186speakertitle: Stephanie Hannon, speakercompany: Google, speakerabstract: Stephanie, the lead product manager for Google Wave, is based in Google's Sydney office. Prior to Sydney she worked in Google's Zurich office, leading the product team for Google Maps for Europe, Middle East and Africa, and before this she was product manager for Gmail in at Google in Mountain View, CA. Stephanie holds a B.S. Computer Systems Engineering and M.S.E.E. from Stanford University and an M.B.A. from Harvard Business School. , speakerldap: StephanieHannonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/f3nz12010-05-10T18:39:11.857ZRow: 187speakertitle: Steve Bazyl, speakercompany: Google, speakerabstract: Steve is a Developer Advocate at Google focused on developers integrating with Google Apps. Prior to joining Google, Steve was an architect at IGT and RSA Security where he worked on building scalable and secure web based systems. , speakerldap: Steve Bazylhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fgb2q2010-05-10T18:39:11.857ZRow: 188speakertitle: Susannah Raub, speakercompany: Google, speakerabstract: Susannah is the tech lead for the Maps API v3 team in Sydney, focused on latency and mobile. Prior to moving to Australia, she worked on Local Search in New York and Google Desktop in Mountain View. She earned her bachelors in Mathematics-Computer Science at Brown University., speakerldap: SusannahRaubhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fhpn72010-05-10T18:39:11.857ZRow: 189speakertitle: Thatcher Ulrich, speakercompany: Google, speakerabstract: Thatcher is a founding member of the Google Earth API and Browser Plugin team. Prior to joining Google he worked on games and graphics at Oddworld Inhabitants and a variety of startups., speakerldap: ThatcherUlrichhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fj47k2010-05-10T18:39:11.857ZRow: 190speakertitle: Thor Mitchell, speakercompany: Google, speakerabstract: Thor is a Product Manager for the Google Maps Developer Platform, based in Sydney, Australia. Although originally from the U.K., he does not have a strong preference between Marmite and Vegemite, despising both equally., speakerldap: ThorMitchellhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fkis12010-05-10T18:39:11.857ZRow: 191speakertitle: Timothy Jordan, speakercompany: Google, speakerabstract: Timothy is a Developer Advocate at Google working on the social web. He also teaches New Media at the Academy of Art University and announces Roller Derby. As an engineer he enjoys solving problems for the benefit of fellow humans. As an artist he enjoys the search for truth and beauty., speakerldap: TimothyJordanhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/faot22010-05-10T18:39:11.857ZRow: 192speakertitle: Toby Reyelts, speakercompany: Google, speakerabstract: Toby is a progenitor of App Engine for Java and a member of the Google Web Toolkit team. Toby's current areas of interest are performance (45.32us is too slow) and improving dynalangs on the JVM and App Engine for Java., speakerldap: TobyReyeltshttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fc3dj2010-05-10T18:39:11.857ZRow: 193speakertitle: Todd Jackson, speakercompany: Google, speakerabstract: Todd is a product manager on Gmail and the product lead for Google Buzz. He has worked at Google since 2004. Todd holds an MS and BS in Computer Science from Stanford University., speakerldap: ToddJacksonhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fdhxw2010-05-10T18:39:11.857ZRow: 194speakertitle: Tom Manshreck, speakercompany: Google, speakerabstract: Tom is a senior technical writer and has worked on the Maps API since 2007, where he writes and maintains the Maps API documentation set. Prior to Google, Tom was a managing editor for computer science at Prentice Hall/Pearson. He enjoys driving around the States with his cocker spaniel, Faulkner., speakerldap: TomManshreckhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fewid2010-05-10T18:39:11.857ZRow: 195speakertitle: Vic Gundotra, speakercompany: Google, speakerabstract: Vic joined Google in 2007 as VP of Engineering, responsible for mobile applications and developer evangelism. Vic spent 15 years at Microsoft, working on products and operating systems including Windows 3.0, NT, Windows XP, and Vista. He was recognized by MIT as a "Young Innovator under 35" for his work in sparking Microsoft's change from Win32 to the .NET programming model. Vic holds two patents in the area of distributed computing and identity-based access to cloud resources., speakerldap: VicGundotrahttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/frjm22010-05-10T18:39:11.857ZRow: 196speakertitle: Vijay Bangaru, speakercompany: Google, speakerabstract: Vijay is a product manager on the Google Docs team. Previously at Google, he ran core infrastructure teams for storage, object transport, indexing and crawling. Prior to Google, he worked at Microsoft where he drove product strategy and led development teams for SQL Server, WinFS and the .NET Framework. Vijay holds bachelor's degrees in computer engineering and electrical engineering from Washington University of St. Louis, where he graduated summa cum laude., speakerldap: VijayBangaruhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fsy6j2010-05-10T18:39:11.857ZRow: 197speakertitle: Virgil Dobjanschi, speakercompany: Google, speakerabstract: Virgil is an Android application developer at Google. He focuses on Social applications development. Prior to Android, Virgil was the President of Multiple Facets and Chief Architect at AudioTalk Networks and Essentel., speakerldap: VirgilDobjanschihttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od4/public/basic/fucqw2010-05-10T18:39:11.857ZRow: 198speakertitle: Zach Maier, speakercompany: Google, speakerabstract: Zach is the product manager for the Google Data Protocol, and his team is responsible for building the infrastructure behind most of Google's APIs. Rumor has it that Zach invented the hashtag #myjobisawesome after he started working on developer products at Google. Before APIs, Zach attended Kansas State University where he graduated summa cum laude with a degree in computer engineering and a specialty in machine intelligence., speakerldap: ZachMaier
\ No newline at end of file
diff --git a/assets/cache-vendors.xml b/assets/cache-vendors.xml
new file mode 100644
index 0000000..e824e87
--- /dev/null
+++ b/assets/cache-vendors.xml
@@ -0,0 +1,20 @@
+http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic2010-05-10T18:39:11.857Zsandboxchristine.leechristine.lee@gmail.com1831http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cpzh42010-05-10T18:39:11.857ZRow: 2companyname: 12wiki, companylocation: Breda, Noord-Brabant, The Netherlands, companydesc: 12wiki is a webdevelopment company specialised in open-source software., companyurl: www.12wiki.eu, productdesc: Currently we are working on MediawikiWave, which will allow you to edit Mediawiki articles (i.e. Wikipedia) in a Wave Enviroment, solving a lot of the issues with the current editor and making contributing content a pain-free experience., companylogo: 12wiki.png, companypod: Wave, companytags: Wave, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cre1l2010-05-10T18:39:11.857ZRow: 3companyname: 280 North, Inc., companylocation: San Francisco, California, companydesc: Creators of 280 Slides, a web based presentation editor; the Cappuccino Web Framework, for building desktop-class applications that run in any browser; and Atlas, a powerful visual editor for building applications with Cappuccino., companyurl: www.280north.com, productdesc: 280 Slides relies on the Google AJAX APIs to provide a rich media experience to users. Without leaving the site, users can integrate relevant pictures and videos from the web in just a few clicks., companylogo: 280north.png, companypod: Google APIs, companytags: Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/b8lvi2010-05-10T18:39:11.857ZRow: 4companyname: 6rounds, companylocation: Tel Aviv, Israel, companydesc: 6rounds is a social entertainment video chat platform. The platform combines interactive video chats, co-browsing activities and real-time games to create a fun, playful and thrilling place for people of all ages., companyurl: www.6rounds.com, productdesc: 6rounds created a Google Wave extension which enables Wave users to enjoy instant one-on-one video chats and collaborate together in real-time using webcams, various rich activities and different interactive games., companylogo: 6rounds.png, companypod: Wave, companytags: Wave, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/chk2m2010-05-10T18:39:11.857ZRow: 5companyname: Adobe Systems Incorporated, companylocation: San Jose, California, companydesc: Adobe revolutionizes how the world engages with ideas and information - anytime, anywhere and through any medium., companyurl: www.adobe.com, companylogo: adobe.png, companypod: Chrome, companytags: Chrome, adddate: 5/7/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ciyn32010-05-10T18:39:11.857ZRow: 6companyname: Aloqa, companylocation: Palo Alto, California, companydesc: Aloqa is a mobile service that proactively notifies you of interesting places, events, music, movies and other activities near you. Instead of having to search, you can just look at your phone and see your favorite hotspots, activities, events of interest, and recommended bargains close by., companyurl: www.aloqa.com, productdesc: We use several Google products: the Android SDK for building the Android client, the Google Static Maps API for maps on WinMo and J2ME phones, the Google Visualization API for our internal analytics, Google Local Search for our Pull-based Search Channel, Google AdSense for Mobile, and Google Web Toolkit for some of our websites., companylogo: aloqa.png, companypod: Android, companytags: Android, Geo, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ckd7g2010-05-10T18:39:11.857ZRow: 7companyname: Amazon, companylocation: Seattle, Washington, companydesc: Amazon.com, Inc., seeks to be Earth's most customer-centric company, where customers can find and discover anything they might want to buy online, and endeavors to offer its customers the lowest possible prices., companyurl: www.amazon.com, companylogo: amazon.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/clrrx2010-05-10T18:39:11.857ZRow: 8companyname: Appirio, companylocation: San Mateo, California, companydesc: Appirio is a cloud solution provider, delivering both products and professional services that help enterprises accelerate their adoption of cloud computing., companyurl: www.appirio.com, productdesc: Appirio uses Google App Engine, GWT, GData, OpenID/oAuth and OpenSocial Gadgets to build custom productivity extensions to Google Apps which help a wide range of customers increase productivity and enrich their experience with existing SaaS investments. For example, our popular "connector" products have helped almost 5,000 customers worldwide synchronize their contacts and calendar events between Salesforce CRM and Google Apps. , companylogo: appirio.png, companypod: Enterprise, companytags: Enterprise, App Engine, GWT, Google APIs, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cztg32010-05-10T18:39:11.857ZRow: 9companyname: Atlassian, companylocation: San Francisco, California, companydesc: Atlassian is an Australian software company specialising in collaboration and development tools to help technical teams build better software. Atlassian's flagship products include JIRA, the popular issue tracker, Confluence, the enterprise wiki, and JIRA Studio, a hosted development suite., companyurl: www.atlassian.com, productdesc: Atlassian makes use of many different Google products and technologies in its products. JIRA and Confluence are both OpenSocial containers and publishers, and Atlassian uses OpenSocial gadgets across its product portfolio to integrate and share information between products. JIRA Studio, Atlassian's hosted development suite, integrates with Google Apps, and several Atlassian products use Google Web Toolkit., companylogo: atlassian.png, companypod: Social Web, companytags: Social Web, GWT, Enterprise, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cssly2010-05-10T18:39:11.857ZRow: 10companyname: Aviary, companylocation: New York, New York, companydesc: Aviary has created a suite of powerful, free web apps for designers and multimedia creators. Whether you are trying to edit a screen capture of your website, remix a music track or create a logo for your new business, Aviary has the right tool for you., companyurl: www.aviary.com, productdesc: Aviary has developed for many of Google's APIs including: the Android SDK for our Android app, integration with OpenID and OAuth to let people login to Aviary using their Google account, Picasa's API to import people's photos, Google's Web Toolkit to build our website and a plugin for Google Chrome. We also use Google Apps for email, documents and collaboration and Google Analytics for traffic monitoring., companylogo: aviary.png, companypod: Enterprise, companytags: Enterprise, Android, Google APIs, Social Web, GWT, Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cu76f2010-05-10T18:39:11.857ZRow: 11companyname: BeeBole, companylocation: Wallonia, Belgium, companydesc: BeeBole builds web apps for small and medium businesses. The current core features are: contact manager, timesheet and reporting, but the main objective is to let anyone add modules, either from the community or their own. BeeBole is also the author of the popular open source JavaScript templates engine PURE., companyurl: www.beebole.com, productdesc: Google APIs fit very well with our integration design. BeeBole is using: Google Chart Tools, Google Maps, Google Calendar, Google Docs, Google Login Authentication., companylogo: beebole.png, companypod: Google APIs, companytags: Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ba0fz2010-05-10T18:39:11.857ZRow: 12companyname: Big Ladder, companylocation: Denver, Colorado, companydesc: Big Ladder is a software and consulting company offering services at the intersection of programming, engineering, and energy simulation. We specialize in developing open source tools for modeling energy usage in the built environment., companyurl: www.bigladdersoftware.com, productdesc: Big Ladder uses the Google SketchUp API to couple building energy simulation with SketchUp's intuitive 3-D drawing tools., companylogo: bigladder.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cvlqs2010-05-10T18:39:11.857ZRow: 13companyname: bit.ly, companylocation: New York, New York, companydesc: bit.ly shortens, shares, and tracks the social propagation of links on the Internet., companyurl: www.bit.ly, productdesc: We make use of Google code, perftools, Closure, Google Apps, the Google Safebrowsing, and other Google APIs in various roles for development and management of our product., companylogo: bitly.png, companypod: Chrome, companytags: Chrome, Enterprise, Google APIs, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cx0b92010-05-10T18:39:11.857ZRow: 14companyname: Black Tonic, companylocation: Portland, Oregon, companydesc: Present. Demo. Share. Whatever you do, be on the same page at the same time with Black Tonic - the only web based tool providing real-time presentation delivery and control without using plugins or flash. Changes can be made on the fly, delivery is non-linear, and content can be added while the conversation is taking place., companyurl: www.blacktonic.com, productdesc: Black Tonic uses Google Web Toolkit (GWT) to serve the client side interface of our web-page html synchronization solution, FLOW. , companylogo: blacktonic.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/d9ney2010-05-10T18:39:11.857ZRow: 15companyname: Box.net, companylocation: Palo Alto, California, companydesc: Box.net is a Cloud Content Management provider that makes it simple for businesses to access, manage, and share all of their content online. , companyurl: www.box.net, productdesc: Box.net’s Cloud Content Management system adds workflow and content-based administrative oversight to Google Apps. This integration provides access to Gmail, Google Calendar, Google Sites, and Google Docs within Box.net, and likewise makes Box.net's service accessible through Google’s universal navigation links and Google Gadgets., companylogo: boxnet.png, companypod: Google APIs, companytags: Google APIs, Enterprise, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/db1zf2010-05-10T18:39:11.857ZRow: 16companyname: BTBuckets, companylocation: São Paulo, SP, Brazil, companydesc: BTBuckets is a free profiling and behavioral targeting tool that aims to provide a complete solution for websites to deliver more relevant content, products and advertising to its users in an easy and low cost way., companyurl: www.btbuckets.com, productdesc: We use App Engine as our segmentation and targeting engine., companylogo: btbuckets.png, companypod: App Engine, companytags: App Engine, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dcgjs2010-05-10T18:39:11.857ZRow: 17companyname: Bump, companylocation: Mountain View, California, companydesc: Bump Technologies is building a mobile technology platform and apps to allow phones to connect and interact by simply bumping them together. Share photos, contacts, and even apps with just a bump of your phones., companyurl: www.bu.mp
+, productdesc: Android features used: Location services, Accelerometer, and Custom Intent to share via Bump., companylogo: bump.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ddv492010-05-10T18:39:11.857ZRow: 18companyname: BusyCal, companylocation: Seattle, Washington, companydesc: BusyCal is a desktop calendar for Mac OS X that provides seamless calendar sharing and enhanced productivity tools for workgroups and individuals. BusyCal syncs with Google Calendar offering online access to your calendar on any platform and the ability to share calendars with other Google Calendar users., companyurl: www.busycal.com, productdesc: BusyCal uses the Google Calendar Data API for performing two-way synchronization with Google Calendar., companylogo: busycal.png, companypod: Google APIs, companytags: Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/d415a2010-05-10T18:39:11.857ZRow: 19companyname: Caseish, companylocation: Copenhagen, Denmark, companydesc: Caseish helps organize your Waves to be more organized and helps you control the flow of every case that you’re managing. Caseish also collects metrics on your processes and visualizes your pain points., companyurl: www.caseish.com, productdesc: We are using Google Wave Robots and API to integrate with Wave, AppEngine to control the robot and Google Visualization API to display graphs., companylogo: caseish.png, companypod: Wave, companytags: Wave, Google APIs, App Engine, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/d5fpr2010-05-10T18:39:11.857ZRow: 20companyname: CELLULAR GmbH, companylocation: Hamburg, Germany, companydesc: CELLULAR is a leading international, full-service agencies for mobile solutions and services. We support customers with conceptualization and programming of mobile portals and applications for cell phones, smart phones and other web compatible devices. We develop solutions for market-leading mobile operators, brands, agencies and mass media., companyurl: www.cellular.de, productdesc: Android features used: Animations, lists, customized ListViews, customized toggle-buttons, customized tabs, customized video player, customized menus, exception reporting, LayoutInflater, SaxParser, ContentProvider, SQLite, and JSON., companylogo: cellular.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/d6ua42010-05-10T18:39:11.857ZRow: 21companyname: Chromed Bird, companylocation: Fortaleza, CE, Brazil, companydesc: I came from a web and mobile applications background and started to dive into Chrome extensions since early dev channel releases. Right now I'm working on maintaining and expanding Chromed Bird extension and trying to contribute further with Chromium community., companyurl: www.chromedbird.com, productdesc: Chromed Bird uses HTML5 and Chromium extensions API to build a powerful yet simple Twitter client., companylogo: chromedbird.png, companypod: Chrome, companytags: Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/d88ul2010-05-10T18:39:11.857ZRow: 22companyname: Cisco, companylocation: San Jose, California, companydesc: Cisco Systems, Inc. designs, manufactures, and sells Internet Protocol (IP)-based networking solutions, including Routers, Switches, IP Communications Equipment, Unified I/O Compute Systems and network management software and other products to the communications and IT industry worldwide. The company also offers optical networking products, cable access, and service provider VoIP services., companyurl: www.cisco.com, productdesc: Cisco built its north bound API in OpenSocial complaint way. It uses Google Map API and service for people location, Google Guice for backend dependency injection., and Android SDK for building the Android client (in process)., companylogo: cisco.png, companypod: Social Web, companytags: Social Web, Geo, Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dkvya2010-05-10T18:39:11.857ZRow: 23companyname: Civio, companylocation: Palo Alto, California, companydesc: Civio provides the platform to orchestrate interactions that target and engage people in meaningful ways. Customers use Civio to engage people in actions online and offline and receive the benefit of increased participation, affinity and contribution levels. , companyurl: www.civio.com, productdesc: We've built our platform on top of Google App Engine for our hosting and web-based infrastructure. We’re also using Google Maps and the Geocoding Service to provide geographical overlays to our users., companylogo: civio.png, companypod: App Engine, companytags: App Engine, Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dmair2010-05-10T18:39:11.857ZRow: 24companyname: Clicker, companylocation: Los Angeles, California, companydesc: Clicker is the complete programming guide for the new era of Internet television. Clicker catalogues all TV shows, movies and Web originals available on the Web in one place, delivering a seamless, organized experience so you can easily discover what’s available, where you can watch it, and get suggestions for other content you might find worth watching. , companyurl: www.clicker.com, productdesc: We use Chrome for HTML5 support and Javascript/UI debugging; the YouTube API to programmatically control YouTube embeds; Page speed to analyze our UI speed; and the Notifications API to serve up notifications when new shows are online., companylogo: clicker.png, companypod: Chrome, companytags: Chrome, Google APIs, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dnp342010-05-10T18:39:11.857ZRow: 25companyname: Cloud Sherpas, companylocation: Atlanta, Georgia, companydesc: Cloud Sherpas develops software and provides professional services to help enterprises leverage Google Apps and adopt cloud computing. As one of the early Google Apps partners, Cloud Sherpas has migrated tens of thousands of users to Google Apps and developed SherpaTools, an valuable toolset that enhances the functionality and ease-of-use of Google Apps for both IT administrators and end users., companyurl: www.cloudsherpas.com, productdesc: SherpaTools for Google Apps is built entirely on Google App Engine and makes heavy use of Google Apps APIs and Google Web Toolkit. SherpaTools also relies on reusable open standards such as OpenID and OAuth and leverages Google Apps as its OpenID identity provider., companylogo: cloudsherpas.png, companypod: Enterprise, companytags: Enterprise, App Engine, Google APIs, GWT, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dp3nl2010-05-10T18:39:11.857ZRow: 26companyname: Columbus Internet GmbH, companylocation: Berlin, Germany, companydesc: Columbus Internet provides two acclaimed gadgets for Google Wave: Travel WithMe and Hostel WithMe. Travel WithMe allows groups of people to plan trips together in real time, presenting content from Lonely Planet and hotel information from Expedia in a map and calendar-based GUI, while Hostel WithMe lets groups of users choose and book the perfect budget accomodation using a simple but powerful GUI., companyurl: www.rucksack.com, productdesc: The WithMe gadget family harnesses the collaborative power of Google Wave to allow users to work and play together and make informed collective decisions. Travel WithMe makes innovative use of Google Maps, Street View and Google Local and allows the planned trip to be exported to Google Calendar., companylogo: columbusinternet.png, companypod: Wave, companytags: Wave, Geo, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/df9om2010-05-10T18:39:11.857ZRow: 27companyname: Com2Us, companylocation: Seoul, South Korea, companydesc: Com2uS is a fun and creative mobile game company with various award-winning games in Korea and abroad. Founded in 1998, Com2us continues to bring innovative gameplay beyond mobile to multi-platform as a worldwide leader in mobile gaming with overseas offices in US, Japan and China., companyurl: www.com2us.com, productdesc: Android features used: multi-touch, gyro sensor, vibration, network connectivity using 3G or WiFi, and Developed by NDK(with C and C++ game code)., companylogo: com2us.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dgo932010-05-10T18:39:11.857ZRow: 28companyname: ConnectWise, companylocation: Tampa, Florida, companydesc: ConnectWise is a leading web-based professional service automation software designed exclusively for IT solutions providers by an IT solution provider. More than 37,000 IT professionals rely on us to more efficiently integrate key business operations, lower costs, increase revenue and improve customer satisfaction., companyurl: www.connectwise.com, productdesc: We’re using Google Web Toolkit to help build our community platform and working to embed Google Apps into our management software., companylogo: connectwise.png, companypod: Enterprise, companytags: Enterprise, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/di2tg2010-05-10T18:39:11.857ZRow: 29companyname: Cordys, companylocation: Palo Alto, California, companydesc: Cordys is a global provider of software for business process innovation and offers Cordys Process Factory, a simple, effective mechanism for anyone in the world to create and deploy Mashup Applications (MashApps®). These web-based, process-centric applications are ideal for SME’s and departmental deployments to create applications and business processes on the fly, at low cost and without IT involvement., companyurl: www.cordys.com, productdesc: Cordys Process Factory has out-of-the box integration with Google Apps allowing users of Google Apps to easily enrich their offering., companylogo: cordys.jpg, companypod: Enterprise, companytags: Enterprise, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/djhdx2010-05-10T18:39:11.857ZRow: 30companyname: Coupons.com, companylocation: Mountain View, California, companydesc: Coupons.com Inc. is a leader in digital coupons, including mobile, online printable, and save-to-loyalty card promotions., companyurl: www.coupons.com, productdesc: Grocery iQ for Android makes full use of the Android platform including native UI widgets, Voice Recognition to add new items to your list, dynamically resizing and re-scaling the UI and images based on hardware model, etc. In addition, Grocery iQ supports a unique intent that allows other applications to easily add items to a user's grocery list., companylogo: coupons.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dw4je2010-05-10T18:39:11.857ZRow: 31companyname: CS Odessa, companylocation: Odessa, Ukraine, companydesc: Founded in 1993, CS Odessa supplies productivity tools and graphics technologies to professional and corporate users. With headquarters in Odessa, Ukraine and an office in California, USA, CS Odessa sells products in over 120 countries., companyurl: www.csodessa.com, productdesc: We are building a mind mapping gadget for Google Wave using Google Wave's Extensions API and OpenSocial Gadgets API together to help people view real time the structure of their collaboration., companylogo: csodessa.png, companypod: Wave, companytags: Wave, Social Web, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dxj3v2010-05-10T18:39:11.857ZRow: 32companyname: DataViz, companylocation: Milford, Connecticut, companydesc: DataViz is an industry leader in developing and marketing Office compatibility solutions across a variety of platforms including Android, BlackBerry, Java®, Linux, Palm OS®, Symbian OS™, Windows Mobile®, Windows® and Macintosh®., companyurl: www.dataviz.com, productdesc: Key Android-related features include: Open files and attachments via intents; run in the background; multi-touch and pinch-to-zoom; rotate page; and live folders., companylogo: dataviz.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/e0c8p2010-05-10T18:39:11.857ZRow: 33companyname: Digg, companylocation: San Francisco, California, companydesc: Digg is focused on building the best internet platform for news. Digg is building a platform to enable social curation of the world's content and the conversation around it. It's Digg's belief that people across the world have a constant thirst for finding relevant news in a timely manner., companyurl: www.digg.com, productdesc: Digg develops for the Android SDK for our Android client and the Chrome extension platform., companylogo: digg.png, companypod: Chrome, companytags: Chrome, Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/drwu72010-05-10T18:39:11.857ZRow: 34companyname: Dito, companylocation: Manassas, Virginia, companydesc: Dito provides Google Apps deployment, development, training, and supporting services to businesses and organizations of all sizes. Our mission is to provide a complete solution for our clients that are seeking to maximize the potential of Google technologies., companyurl: www.ditoweb.com, productdesc: Dito Directory for Google Apps provides external contact management for organizations and is built on Google App Engine with Google Web Toolkit. Dito Directory integrates seamlessly with Google Apps using OpenID for authentication and multiple Google Apps APIs., companylogo: dito.png, companypod: Enterprise, companytags: Enterprise, App Engine, GWT, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dtbek2010-05-10T18:39:11.857ZRow: 35companyname: DotSpots, companylocation: Los Angeles, California, companydesc: DotSpots enables anyone to create dots or 'distributed objects of thought.' Dots are bigger than tweets, smaller than blogs, allow wiki-like collaboration and can be easily distributed as global annotations on text in webpages or via social media. By allowing dots on their pages, publishers enable the wisdom of crowds to interactively enrich their content., companyurl: www.dotspots.com, productdesc: DotSpots uses Google Web Toolkit to build their website, and Firefox and Chrome extensions. They're also using the YouTube and Picasa APIs to integrate video and photos into user-generated content., companylogo: dotspots.png, companypod: GWT, companytags: GWT, Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/dupz12010-05-10T18:39:11.857ZRow: 36companyname: Drawloop Technologies, companylocation: Irvine, California, companydesc: Drawloop Technologies delivers powerful on-demand document creation services to your organization. We provide a simple business process for users to merge and automate dynamic document creation using any combination of Google Docs as well as other documents like Microsoft Word, PowerPoint and Excel., companyurl: www.drawloop.com, productdesc: Drawloop uses many Google technologies. For automating document creation, we utilize the Google Docs API as well as Google's two-legged OAuth. In addition, we use Google Gadgets within Gmail and Google Sites to deploy Dynamic Document Packages (DDPs)., companylogo: drawloop.png, companypod: Enterprise, companytags: Enterprise, Google APIs, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/e7d2q2010-05-10T18:39:11.857ZRow: 37companyname: EA, companylocation: Redwood City, California, companydesc: EA Mobile is a division of Electronic Arts Inc. (EA), a leading publisher and developer of mobile games. Award-winning hits from EA Mobile include Tetris, Bejeweled, The Sims titles, and Spore Origins. With its strong ties to EA's overall franchises, EA Mobile continues to develop and produce innovative products across all major mobile platforms, including Android.
+, companyurl: www.ea.com, productdesc: Rock Band is now on Android. Rock out on guitar, bass, drums or vocals to MP3-quality songs. Fully optimized for Android 2.0 devices.
+, companylogo: ea.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/e8rn72010-05-10T18:39:11.857ZRow: 38companyname: eBay, companylocation: San Jose, California, companydesc: Founded in 1995, eBay Inc. connects hundreds of millions of people around the world every day by providing the Internet platforms of choice for global commerce, payments and communications. Since its inception, eBay Inc. has expanded to include some of the strongest brands in the world, including eBay, PayPal, Skype, StubHub, Shopping.com, and others., companyurl: www.ebay.com, productdesc: eBay uses OpenSocial and Gadget standard to power its Selling Manager application platform marketplace. We also developed a prototype Gadget using Google Friend Connect that distributes eBay inventory across the GFC network of publishers., companylogo: ebay.png, companypod: Social Web, companytags: Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ea67k2010-05-10T18:39:11.857ZRow: 39companyname: EchoSign, companylocation: Palo Alto, California, companydesc: EchoSign is a leading electronic signature service with over 10,000 customers and over 1 million users and is used by medium-sized businesses, enterprises, and Fortune 500 companies alike. EchoSign natively integrates with your Google Apps account, including Google docs, spreadsheets, etc. for instant on-line review, approval, and eSignatures., companyurl: www.echosign.com, productdesc: EchoSign integrates Google as an OpenID provider for easy login across both standard Google accounts as well as for Google Apps for businesses and accesses Google Docs via OAuth as a content source allowing customers to easily send any Google Doc for e-Signature., companylogo: echosign.png, companypod: Enterprise, companytags: Enterprise, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ebks12010-05-10T18:39:11.857ZRow: 40companyname: Eclipse, companylocation: Ottawa, Ontario Canada, companydesc: Eclipse is an open source community, whose projects are focused on building an open development platform comprised of extensible frameworks, tools and runtimes for building, deploying and managing software across the lifecycle. A large, vibrant ecosystem of major technology vendors, innovative start-ups, universities and research institutions and individuals extend, complement and support the Eclipse Platform., companyurl: www.eclipse.org, productdesc: The Eclipse community is introducing the next generation of the Eclipse platform, code name e4, that includes the ability to integrate OpenSocial gadgets into Eclipse based applications., companylogo: eclipse.png, companypod: Social Web, companytags: Social Web, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/e1qt22010-05-10T18:39:11.857ZRow: 41companyname: Entrinsik, companylocation: Raleigh, North Carolina, companydesc: Entrinsik's Informer Web Reporting software delivers true real-time access to operational data simply and easily using a built-in Web-based query engine. Informer leverages a metadata model that creates consistency among data descriptions and structures to provide a single point of access to disparate data sources for ad hoc report customization and analysis., companyurl: www.entrinsik.com, productdesc: Google Web Toolkit is the engine behind Informer’s drag-and-drop analytics, one click charting, and WYSIWYG criteria builder., companylogo: entrinsik.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/e35dj2010-05-10T18:39:11.857ZRow: 42companyname: Epocrates, companylocation: San Mateo, California, companydesc: Epocrates, Inc. develops clinical information and decision support tools that enable healthcare professionals to find answers more quickly and confidently at the point of care. More than 950,000 healthcare professionals worldwide and 40% of U.S. physicians use Epocrates’ innovative mobile and web-based products to help them reduce medical errors, improve patient care and increase productivity.
+, companyurl: www.epocrates.com, productdesc: You can search for a drug without launching the Epocrates application using the Quick Search box on Android devices., companylogo: epocrates.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/e4jxw2010-05-10T18:39:11.857ZRow: 43companyname: ESPN EMEA, companylocation: London, United Kingdom, companydesc: ESPN EMEA is the European digital arm of ESPN international, building and running Cricinfo.com, Soccernet.com, Scrum.com, ESPNF1.com and espn.co.uk, as well as the UK’s ESPN TV channel and other digital properties., companyurl: international.espn.com, productdesc: We are currently using google's Chrome Extension support to engage with cricket fans from all over the world. We use Background Pages and Notification Badges to update our users the moment cricketing news breaks on our website. We’re doing this with Scrum.com and plan on rolling it out for our other properties too. A project is underway to localize our scorecards to Hindi. Google's Transliteration APIs are playing a key role in getting our comprehensive database of over 50,000 players localized., companylogo: espn.png, companypod: Chrome, companytags: Chrome, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/e5yid2010-05-10T18:39:11.857ZRow: 44companyname: Exceptional Software, companylocation: Linthicum, Maryland, companydesc: Exceptional Software Strategies, Inc. is a leading provider of information technology solutions and services to the U.S. Government, educational institutions, and private industry. Exceptional Software can meet all your information technology needs – from customized software products and solutions such as Geographic Information Systems, enterprise-level portals, and information management, to services such as network administration, security design, project management, and interactive training., companyurl: www.exceptionalsoftware.com, productdesc: EarthSpector was developed using the Google Web Toolkit (GWT), and brings GIS analysis capabilities found in heavier desktop products to the flexible platform of the Google Earth Plug-in. EarthSpector is a product of Exceptional Software Strategies, Inc., companylogo: exceptionalsoftware.png, companypod: Geo, companytags: Geo, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/emtbd2010-05-10T18:39:11.857ZRow: 45companyname: eXo Platform, companylocation: San Francisco, California, companydesc: eXo provides a new way to extend, build and deploy applications, complete with rich social computing capabilities, by reusing existing services and components. An integrated framework and user experience platform in one, the eXo Platform extends and enables rapid development of Java applications with modern APIs and tooling such as OpenSocial, gadgets, REST, Groovy, and mashups., companyurl: www.exoplatform.com, productdesc: Google Web Toolkit is an important part of the eXo Platform, as a part of our IDE and CMIS implementation. We also use the Google Gadget API in GateIn, the open source portal framework that serves as the foundation of eXo Platform. Finally, we support OpenSocial in eXo Enterprise Social, an extended service for eXo Platform., companylogo: exoplatform.png, companypod: Social Web, companytags: Social Web, GWT, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/eczce2010-05-10T18:39:11.857ZRow: 46companyname: ExtensionFM, companylocation: New York, New York, companydesc: ExtensionFM turns the entire web into your personal music library. Discovering new music has never been easier: simply browse to your music site of choice and ExtensionFM will automatically add any music it finds into your library and keep you up to date with any new music the site publishes., companyurl: www.extension.fm, productdesc: ExtensionFM is a Google Chrome extension that makes heavy use of HTML5., companylogo: extensionfm.png, companypod: Chrome, companytags: Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/eedwv2010-05-10T18:39:11.857ZRow: 47companyname: Eye-Fi, companylocation: Mountain View, California, companydesc: Founded in 2005, Eye-Fi is dedicated to building products and services that help consumers navigate, nurture and share their visual memories. Eye-Fi’s patent-pending technology works with Wi-Fi networks to automatically send photos from a digital camera to online, in-home and retail destinations., companyurl: www.eye.fi, productdesc: Eye-Fi Cards upload images and videos to Picasa Web Albums and to YouTube. We use the API's to enable uploads from our servers, to Google's servers., companylogo: eyefi.png, companypod: Google APIs, companytags: Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/efsh82010-05-10T18:39:11.857ZRow: 48companyname: EyeCon, companylocation: Palo Alto, California, companydesc: Eyecon allows you to search through all your media simultaneously and play it to any device, without cluttering the screen, without getting up, and with zero hassle., companyurl: www.eyecontec.com, productdesc: The Eyecon application was developed using the Android SDK, companylogo: eyecon.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/eh71p2010-05-10T18:39:11.857ZRow: 49companyname: EZasset, companylocation: Columbus, Ohio, companydesc: EZasset is an asset management application for home and business. EZasset is a simple and safe way to record and manage data on all your assets. EZasset provides all of the tools needed to manage assets efficiently online., companyurl: www.ezasset.com, productdesc: EZasset utilizes several Google APIs such as Google Calendar for maintenance tracking for assets and Google Docs for easy importing and exporting of information for assets. The Google Maps API is integrated to give visual representation of a physical location where assets are stored. We are running both our home and business applications on App Engine., companylogo: ezasset.png, companypod: Enterprise, companytags: Enterprise, Google APIs, Geo, App Engine, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/etu5e2010-05-10T18:39:11.857ZRow: 50companyname: Facebook, companylocation: Palo Alto, California, companydesc: Facebook is all about connections—whether it’s to a friend, or a school, or a community of people. Connections help Facebook create a relevant and meaningful experience for users, and deliver an engaging interaction for those who want to learn more about each other, companyurl: www.facebook.com, productdesc: Facebook for Android lets other Android applications leverage the content of Facebook by allowing direct linking to relevant contact. Facebook also offers Connect to Android applications., companylogo: facebook.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ev8pv2010-05-10T18:39:11.857ZRow: 51companyname: Fandango, companylocation: Los Angeles, California, companydesc: Fandango, a leading moviegoer destination, sells tickets to more than 16,000 screens. Fandango entertains and informs consumers with exclusive film clips, trailers, celebrity interviews, fan reviews and news, while offering them the ability to quickly select a film and conveniently buy tickets in advance., companyurl: www.fandango.com, productdesc: Android technologies used: widgets, intents, voice search, Quick Search integration and more., companylogo: fandango.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ewna82010-05-10T18:39:11.857ZRow: 52companyname: Farmigo, companylocation: San Rafael, California, companydesc: Our mission is to make small farming more sustainable so that many more farms can join the growing trend of farms selling direct. Farmigo CSA System is a web-based service created for small farms to promote, collaborate, sell and explain their food items online. Each farm on the Farmigo network receives farm management software for tracking harvest, processing orders, managing logistics, and servicing customers., companyurl: www.farmigo.com, productdesc: Farmigo has built a multi-tenant enterprise solution on Google App Engine enabling small organic farms to network with one another while directly selling to their local communities., companylogo: farmigo.png, companypod: App Engine, companytags: App Engine, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ey1up2010-05-10T18:39:11.857ZRow: 53companyname: FortiusOne, companylocation: Arlington, Virginia, companydesc: FortiusOne makes it possible to visualize and analyze massive amounts of data, make decisions and solve problems without extensive training using traditional mapping tools by delivering easy-to-use, browser-based solutions. FortiusOne's GeoIQ is a complete data management, visualization, and analysis platform that enables intuitive exploration, sharing and collaboration around geospatial information., companyurl: www.fortiusone.com, productdesc: FortiusOne has integrated with both Google Earth Fusion and the Google Maps API in order to visualize geospatial analysis with GeoIQ Enterprise, on field laptop using GeoIQ Portable, or at the community GeoCommons.com., companylogo: fortiusone.png, companypod: Geo, companytags: Geo, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/eo7vq2010-05-10T18:39:11.857ZRow: 54companyname: Gameloft, companylocation: San Francisco, California, companydesc: Gameloft is an international publisher and developer of downloadable video games. Gameloft is present on all the continents with its own production studios, employing over 3,500 developers, and distributes its games in over 100 countries., companyurl: www.gameloft.com, productdesc: Asphalt is rocketing onto Android! Get behind the wheel of the best vehicles from prestigious manufacturers (Lamborghini, Kawasaki, Ford, etc) and make a name for yourself in amazing 3D surroundings.
+, companylogo: gameloft.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/epmg72010-05-10T18:39:11.857ZRow: 55companyname: GetGlue, companylocation: New York, New York, companydesc: GetGlue is an innovative social recommendation network for movies, books, and music. The GetGlue website provides a recommendation stream based on personal tastes, what friends like, and what's most popular right now. The GetGlue browser extension brings filtered friend reviews, personal recommendations, and contextual content to popular sites around the web, such as Wikipedia, Amazon, IMDB, and hundreds more., companyurl: www.getglue.com, productdesc: GetGlue makes use of the following Google APIs: Search to retrieve site and page specific information, Contacts to surface the existing social graph, and Books, YouTube, and Images to retrieve rich content. AdaptiveBlue also uses Google Web Toolkit to build their website, and Chrome and Firefox extensions. , companylogo: getglue.png, companypod: Chrome, companytags: Chrome, Google APIs, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/er10k2010-05-10T18:39:11.857ZRow: 56companyname: Glu, companylocation: San Mateo, California, companydesc: Glu is a global publisher of mobile games for feature phones and smartphones. Its portfolio of top-rated games includes original titles: Beat It!, Bonsai Blast, Brain Genius, Glyder, Stranded, and Super K.O. Boxing! Consumers can find high-quality, fresh entertainment created exclusively for their mobile phones wherever they see the 'g' character logo., companyurl: www.glu.com, productdesc: Super KO Boxing 2 takes full advantage of the Android Native Development Kit (NDK) to bring the highest quality visuals and experience to Android Market. This title features touch screen controls and uses the SD Card to store the 86MB resource file required to run., companylogo: glu.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/esfl12010-05-10T18:39:11.857ZRow: 57companyname: Google Checkout, companylocation: Mountain View, California, companydesc: Google Checkout is an alternative payment flow that allows consumers to use a single account to purchase from multiple sites across the web., companyurl: code.google.com/apis/checkout, companylogo: google.png, companypod: Google APIs, companytags: Google APIs, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/a6oij2010-05-10T18:39:11.857ZRow: 58companyname: Google Code Labs, companylocation: Mountain View, California, companydesc: Google Code Labs is home to developer products that are still in their formative stages., companyurl: code.google.com/labs, companylogo: google.png, companypod: App Engine, companytags: App Engine, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bbf0c2010-05-10T18:39:11.857ZRow: 59companyname: Google Corporate Engineering, companylocation: Mountain View, California, companydesc: Much like Google's mission, Corporate Engineering is about solving real problems that affect a large number of Googlers. Our goal is to provide the internal technology, infrastructure and support that allow Googlers to be the happiest and most productive workforce on the planet. , companylogo: google.png, companypod: App Engine, companytags: App Engine, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/a832w2010-05-10T18:39:11.857ZRow: 60companyname: Google Customer Solutions Engineering, companylocation: Mountain View, California, companydesc: The Customer Solutions Engineering team works with select Google customers to promote consumer product and API adoption. We'll be demoing real-world examples of websites enhanced by Google's APIs., companylogo: google.png, companypod: Google APIs, companytags: Google APIs, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/9znoe2010-05-10T18:39:11.857ZRow: 61companyname: Google Earth, companylocation: Mountain View, California, companydesc: Google Earth takes advantage of the Android NDK and the OpenGL ES1.1 module. The main application is written in Java, and communicates to the native engine using JNI. Several open source modules in the NDK are used, including libcurl, freeimage, libjpeg, and others. , companyurl: www.google.com/mobile/earth, companylogo: google.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/a128v2010-05-10T18:39:11.857ZRow: 62companyname: Google Goggles, companylocation: Mountain View, California, companydesc: Use pictures to search the web. Photograph objects or places to learn more about them. Photograph text to translate it to another language., companyurl: www.google.com/mobile/goggles, companylogo: google.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/a2gt82010-05-10T18:39:11.857ZRow: 63companyname: Google Native Client, companylocation: Mountain View, California, companydesc: Native Client is an open-source technology for running native code in web applications, with the goal of maintaining the browser neutrality, portability, and safety that people expect from web apps., companyurl: code.google.com/p/nativeclient, companylogo: google.png, companypod: Chrome, companytags: Chrome, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/a3vdp2010-05-10T18:39:11.857ZRow: 64companyname: Google SkyMap, companylocation: Mountain View, California, companydesc: Google Sky Map turns your Android-powered mobile phone into a window on the night sky. It will identify objects that appear in the sky and allow users to search for various objects. By using your Android phone's orientation sensors, we can show you a star map for your location. You can search for a variety of objects and Google Sky Map direct you to the object's location in the sky., companyurl: www.google.com/sky/skymap, companylogo: google.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/agihe2010-05-10T18:39:11.857ZRow: 65companyname: Google Wave, companylocation: Mountain View, California, companydesc: Google Wave is an online tool for real-time communication and collaboration, built with GWT. A wave can be both a conversation and a document where people can discuss and work together using richly formatted text, photos, videos, maps, and more, including developer-built extensions (robots and gadgets) with the Google Wave APIs., companyurl: code.google.com/apis/wave, companylogo: google.png, companypod: GWT, companytags: GWT, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ahx1v2010-05-10T18:39:11.857ZRow: 66companyname: Handmark, companylocation: Kansas City, Missouri, companydesc: Handmark is a leading developer and distributor of mobile applications, specializing in mobile solutions for publishers and media companies. Trusted by many of the world’s leading media brands, Handmark delivers a full-service mobile publishing solution including development, distribution, monetization and on-going innovation.
+, companyurl: www.handmark.com, productdesc: Goal.com 3.0, developed on the Handmark Mobile Publishing Platform, provides users with live scores, tables, fixtures, and editorial content for football matches around the world. The Mobile Publishing Platform uses the embedded Media Player to present high quality video content, leverages linkify to tightly integrate content into Android’s platform services, and brings timely content to the home screen through Android App Widgets., companylogo: handmark.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ajbm82010-05-10T18:39:11.857ZRow: 67companyname: Hasbro, companylocation: Pawtucket, Rhode Island, companydesc: Hasbro, Inc. is a worldwide leader in children's and family leisure time products and services with a rich portfolio of brands and entertainment properties that provides some of the highest quality and most recognizable play and recreational experiences in the world., companyurl: www.hasbro.com/monopoly/en_US/discover/news/Monopoly-City-Streets-Has-Ended.cfm, productdesc: MONOPOLY City Streets used Google Maps API, as a way to excite consumers and demonstrate the new game play features to launch the MONOPOLY City board game. To promote the “try before you buy” experience we also worked with Google Sketch Up and ran a competition to design new buildings of which the top 3 were featured in MONOPOLY City Streets., companylogo: hasbro.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/akq6p2010-05-10T18:39:11.857ZRow: 68companyname: HP, companylocation: Palo Alto, California, companydesc: HP creates new possibilities for technology to have a meaningful impact on people, businesses and society. HP brings together a portfolio that spans printing, personal computing, software, services and IT infrastructure to solve customer problems., companyurl: www.hp.com, productdesc: Used Google Maps API to create an HP Print App that allows users to print a map and point to point directions directly from HP printers., companylogo: hp.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/aaw7q2010-05-10T18:39:11.857ZRow: 69companyname: HTC, companylocation: Taoyuan, Taiwan, companydesc: HTC Corporation (HTC) is one of the fastest growing companies in the mobile phone industry and continues to pioneer industry-leading mobile experiences through design, usability and innovation that are sparked by how the mobile phone can improve how people live and communicate. The company is listed on the Taiwan Stock Exchange under ticker 2498., companyurl: developer.htc.com, productdesc: As a founding member of the OHA and the first to ship Android on a phone, HTC has continued to introduce new Android phones around the world including the recently unveiled HTC Evo 4G, Droid Incredible by HTC, HTC Desire and HTC Legend., companylogo: htc.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/acas72010-05-10T18:39:11.857ZRow: 70companyname: Hydro4GE Inc., companylocation: Scranton, Pennsylvania, companydesc: Hydro4GE (pronounced hy-dro-forge) is a Platform-as-a-Service (PaaS) that enables developers to rapidly develop complex and adaptable software systems using an online, fourth generation development environment. Hydro4GE shortens the traditional software development life cycle and accelerates time to client-ready solutions by eliminating the most time-consuming and expensive steps for a developer: low-level coding and unit testing., companyurl: www.hydro4ge.com, productdesc: The Hydro4GE user interface is built entirely using the Google Web Toolkit (GWT), relying on a single external dependency, RaphaelJS, for interactive SVG diagrams. GWT allowed us to create a highly-interactive, rich user interface (UI) with emerging features such as database schema visualization via interactive, scalable graphics., companylogo: hydro4ge.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/adpck2010-05-10T18:39:11.857ZRow: 71companyname: IBM, companylocation: Armonk, New York, companydesc: For nearly 100 years, IBM has served clients in more than 170 countries around the world. Today, IBM’s portfolio is built around networked, modularized and embedded technologies, such as service-oriented architecture (SOA), business intelligence and analytics. IBM is working with our clients to make many aspects of our world "smarter": roadways, health care, power grids, food production for a better way of living. , companyurl: www.ibm.com, productdesc: IBM is partnering with Google and others in the industry to develop emerging open standards (i.e. OpenSocial) and open source (i.e. Apache Shindig) for the advancement of social networking technologies., companylogo: ibm.png, companypod: Social Web, companytags: Social Web, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/af3x12010-05-10T18:39:11.857ZRow: 72companyname: Instantiations, Inc., companylocation: Portland, Oregon, companydesc: Instantiations develops innovative software products for the Eclipse Java open source platform with a wide range of tools for Eclipse, IBM Rational,® JBuilder® and MyEclipse. Instantiations’ AJAX offering, GWT Designer,™ is the leading GUI building tool for GWT and is based on Instantiations’ award-winning WindowBuilder product, named Best Commercial Eclipse-Based Developer Tool of 2009 by the Eclipse Foundation., companyurl: www.instantiations.com, productdesc: GWT Designer helps developers rapidly create GUIs in Eclipse and GWT/Java before deploying them as AJAX applications – using wizards, drag-and-drop visual design, bi-directional code generation, internationalization, a multitude of layout managers, and support for popular add-on widget packages such as Ext GWT (GXT) and GWT-Ext., companylogo: instantiations.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/arr0q2010-05-10T18:39:11.857ZRow: 73companyname: Instituto Federal Electoral, companylocation: Mexico Distrito Federal, Mexico, companydesc: Instituto Federal Electoral (IFE) is the government agency in Mexico in charge of Elections., companyurl: www.ife.org.mx/portal/site/ifev2, productdesc: We are using Google Maps APIs to provide our website with the precise locations of voting sites so all of our citizens can locate the precise place where they can vote, and access data of the available candidates, also our voters roll registering offices location., companylogo: ife.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/at5l72010-05-10T18:39:11.857ZRow: 74companyname: InterContinental Hotels Group , companylocation: Atlanta, Georgia, companydesc: IHG is an international hotel company that welcomes over 180 million guests each year in nearly 4,400 hotels (that's over 640,000 rooms!) in 100 countries. We operate seven hotel brands – InterContinental, Crowne Plaza, Hotel Indigo, Holiday Inn, Holiday Inn Express, Staybridge Suites, Candlewood Suites and our Priority Club Loyalty Program., companyurl: www.ichotelsgroup.com, productdesc: We utilize GWT for our public facing websites and many of our internal applications. We use Google Maps on our public facing website and Wallboard application located in our hotel lobbies. InterContinetal.com will soon be launching a new Google maps interface that allows our guests to find nearby concierge suggested attractions, restaurants and services., companylogo: ihg.png, companypod: Geo, companytags: Geo, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/auk5k2010-05-10T18:39:11.857ZRow: 75companyname: JetBrains, companylocation: Prague, Czech Republic, companydesc: JetBrains is a software development company that's crazy-passionate about developer productivity. Want more intelligent development tools? Consider it done. How about more efficient development processes? We've got you covered. What about shorter development cycles? Uh huh, we do that too. Simply put, our tools automate your routine tasks and help you effectively solve the challenging ones., companyurl: www.jetbrains.com, productdesc: We use Google Web Toolkit for several internal systems, and we use Google AJAX APIs on our web sites. But most importantly, we build developer tools for GWT, Google App Engine and Android SDK that allow other developers to build great apps using all these technologies., companylogo: jetbrains.png, companypod: GWT, companytags: GWT, Google APIs, App Engine, Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/avyq12010-05-10T18:39:11.857ZRow: 76companyname: Jive Software, companylocation: Portland, Oregon, companydesc: Jive SBS takes all the great things people love about social networking software, collaboration software, and community software and makes those work for business. Jive is a leader in Social Business Software, with an extensive solution, large implementations, and expertise in delivering value., companyurl: www.jivesoftware.com, productdesc: Jive uses OpenSocial in our Jive SBS product to host OpenSocial and Google gadgets., companylogo: jive.png, companypod: Social Web, companytags: Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/am4r22010-05-10T18:39:11.857ZRow: 77companyname: Juice Analytics, companylocation: Reston, Virginia, companydesc: Juice crafts easy-to-use web applications that engage users with your data. Unlike static dashboards and time-consuming reports, our solutions help your data tell a story., companyurl: www.juiceanalytics.com, productdesc: We use Google Analytics and Website Optimizer to improve our website. Google Analytics API and AppEngine power our analytics visualizations., companylogo: juiceanalytics.png, companypod: Google APIs, companytags: Google APIs, App Engine, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/anjbj2010-05-10T18:39:11.857ZRow: 78companyname: Kaazing, companylocation: Mountain View, California, companydesc: Kaazing is a web infrastructure software company that enables companies to quickly deliver massively scalable real-time web applications and significantly simplify their back-end server infrastructure. Its flagship product, Kaazing WebSockets Gateway, can connect hundreds of thousands of concurrent users directly to the real-time data flowing through an organization., companyurl: www.kaazing.com, productdesc: By using the HTML5 WebSockets standard and avoiding the overhead and latency inherent in HTTP, Kaazing WebSockets Gateway extends the use of any TCP-based messaging format to any browser, delivering ultra-high performance bi-directional communication over the Web without the use of browser plug-ins., companylogo: kaazing.png, companypod: Chrome, companytags: Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/aoxvw2010-05-10T18:39:11.857ZRow: 79companyname: Kashoo, companylocation: Vancouver, BC, Canada, companydesc: Kashoo is online accounting for small business. Designed with simplicity and ease-of-use in mind, Kashoo allows you to take control of your finances and record keeping without having to install and learn complicated software., companyurl: www.kashoo.com, productdesc: We're using Google Web Toolkit to build our online software., companylogo: kashoo.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/aqcgd2010-05-10T18:39:11.857ZRow: 80companyname: Korem, companylocation: Quebec City, QC, Canada, companydesc: Korem is a North American geospatial system integrator that has been at the forefront of geospatial technology, business Intelligence and location-oriented demographic analysis since 1993. We create and implement integrated geospatial solutions based on your business needs, and we enable you to better utilize your data and improve your business decision-making capabilities., companyurl: www.korem.com, productdesc: Korem develops and implements geospatial solutions using the Google Maps API and Google Earth Enterprise., companylogo: korem.png, companypod: Geo, companytags: Geo, Enterprise, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/b2zk22010-05-10T18:39:11.857ZRow: 81companyname: Kuali Foundation, Inc., companylocation: Bloomington, Indiana, companydesc: Kuali Student is a next-generation student system currently under development. We will offer a flexible, scalable, cost-effective system that can be configured to meet the business requirements of any higher education institution. Our student-centric design will help students, faculty, and institutions deal with the dynamic learning environment., companyurl: student.kuali.org, productdesc: Kuali Student is using GWT to building its rich user interface which will be used by faculty, students and staff., companylogo: kuali.png, companypod: GWT, companytags: GWT, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/b4e4j2010-05-10T18:39:11.857ZRow: 82companyname: Laminar Research, companylocation: Columbia, South Carolina, companydesc: Laminar Research is the brains behind X-Plane, the most realistic flight simulator available for person computers. Started in 1995 by Austin Meyer, Laminar Research is well known to be the company behind the most physically accurate games and simulations available in the consumer market., companyurl: www.x-plane.com, productdesc: Android technologies used: OpenGL ES for drawing; Accelerometers and multi-touch for input; and sound streaming interface for sound, companylogo: laminarresearch.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/b5sow2010-05-10T18:39:11.857ZRow: 83companyname: LastPass, companylocation: Vienna, Virginia, companydesc: LastPass provides a free password manager that works across all major platforms and browsers including Google Chrome, Firefox, Safari and Internet Explorer on Windows, Mac and Linux. Multi-factor authentication solutions and mobile solutions on Android, iPhone, Blackberry, Symbian, webOS and Windows Mobile are also available., companyurl: www.lastpass.com, productdesc: We are using Chrome Extension APIs to build a native chrome password manager extension, and the AndroidSDK to build the LastPass for Android mobile app., companylogo: lastpass.png, companypod: Chrome, companytags: Chrome, Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/b779d2010-05-10T18:39:11.857ZRow: 84companyname: LGE, companylocation: Seoul, Korea, companydesc: Founded in 1958, LG Electronics has led the way in bringing advanced digital products and applied technologies to our customers. With our commitment to innovation and assertive global business policies, we aim to become a worldwide leader in advanced digital technology., companyurl: www.lge.com, productdesc: We are using Google Mobile Services on our mobile devices to enhance user value., companylogo: lge.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/axdae2010-05-10T18:39:11.857ZRow: 85companyname: LinkedIn, companylocation: Mountain View, CA, companydesc: LinkedIn takes your professional network online, giving you access to people, jobs and opportunities like never before. Built upon trusted connections and relationships, LinkedIn has established the world’s largest and most powerful professional network. Currently, more than 55 million professionals are on LinkedIn., companyurl: www.linkedin.com, productdesc: LinkedIn uses many Google technologies. For Java development we use Google Collections and Guice to speed development. Our OpenSocial-based InApps platform uses Apache Shindig, which powers LinkedIn Polls, Events, Company Buzz and Tweets. Shindig OAuth technology is also used in our Open API Program., companylogo: linkedin.png, companypod: Social Web, companytags: Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ayruv2010-05-10T18:39:11.857ZRow: 86companyname: Lombardi, An IBM Company, companylocation: Austin, Texas, companydesc: Lombardi believes that a Process-Driven approach is the best way to win. We deliver a suite of Business Process Management (BPM) software and services that enable organizations to quickly become Process-Driven., companyurl: www.lombardi.com, productdesc: Lombardi has been using GWT since 2007. We use it to develop all the user interface for Lombardi Blueprint, our software as a service process discovery and documentation product, and the web based portions of the developer interface for Lombardi Teamworks 7, our process automation, simulation and management suite., companylogo: lombardi.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/b06f82010-05-10T18:39:11.857ZRow: 87companyname: LTech, companylocation: Bridgewater, New Jersey , companydesc: LTech is a provider of enterprise cloud computing services and broad mix of cloud enablement products for the Google Apps platform. As an early Google Enterprise Partner, LTech has successfully completed dozens of Google Apps deployments and helped develop best-practices for adopting and successfully scaling cloud computing programs., companyurl: www.ltech.com, productdesc: LTech relies heavily on the Google Apps APIs and OAuth for several products. Our LTech Power Panel for Google Apps product is built on top of Google App Engine, leverages Google Web Toolkit and uses many of the Google Apps Management and Application APIs., companylogo: ltech.png, companypod: Enterprise, companytags: Enterprise, Google APIs, Social Web, App Engine, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/b1kzp2010-05-10T18:39:11.857ZRow: 88companyname: Manymoon, companylocation: San Francisco, California, companydesc: Manymoon is the simplest way to get work done online with your contacts. Track projects, complete tasks and share information with no training or setup. Easy enough for anyone to use, Manymoon provides the privacy required for work and the simplicity of social applications. Manymoon is a top-rated application for Google Apps and is trusted by tens of thousands of businesses., companyurl: www.manymoon.com/auth/login?v=googleIOsandbox, productdesc: Manymoon uses the Google Documents Data List API, Calendar API and Google Charts to work with tasks, projects and work activity. Google Gadgets API and OpenSocial are used to access Manymoon within Google Apps. Customers can securely login using OpenID. The Provisioning API is used for administration., companylogo: manymoon.png, companypod: Enterprise, companytags: Enterprise, Google APIs, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/be8562010-05-10T18:39:11.857ZRow: 89companyname: MediaBeacon, companylocation: Minneapolis, Minnesota, companydesc: MediaBeacon is a Digital Asset Management and Enterprise Search company that provides rich media management and intelligent content discovery solutions to enterprises. , companyurl: www.mediabeacon.com, productdesc: We use Google Web Toolkit to power the entire interface layer of our enterprise digital asset management system., companylogo: mediabeacon.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bfmpn2010-05-10T18:39:11.857ZRow: 90companyname: Meebo, companylocation: Mountain View, California, companydesc: Meebo enables over 100 million users to instantly connect, share, and communicate with all of their friends. Because Meebo integrates all social network and communication channels into a single, simple-to-use interface, users can easily share content and communicate in real time with the people who matter to them., companyurl: www.meebo.com, productdesc: Meebo has been developing XAuth as an open standard for simplifying access to users' online services anywhere on the web. Together with the Google Buzz APIs, we are making our products smarter and more personalized for visitors as they navigate across the social web., companylogo: meebo.png, companypod: Social Web, companytags: Social Web, Google APIs, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bh1a02010-05-10T18:39:11.857ZRow: 91companyname: Memeo, companylocation: Aliso Viejo, California, companydesc: Memeo Inc. is a privately-held software company focused on helping consumers and businesses share, manage, and protect their digital content. The company was founded in 2003 and has shipped 45 million software licenses to over 150 countries. , companyurl: www.memeo.com, productdesc: Memeo Connect bridges Google Docs cloud storage with desktop software. With Memeo Connect, instant access to your important documents while online or offline is now very possible. Memeo Connect is available on both Windows and Mac platforms., companylogo: memeo.png, companypod: Enterprise, companytags: Enterprise, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bifuh2010-05-10T18:39:11.857ZRow: 92companyname: Mibbit, companylocation: London, United Kingdom, companydesc: Mibbit gives anyone the capability to host fast, flexible group chat on their website through the use of its customisable Javascript widget. Mibbit currently connects to IRC networks and Twitter, YouTube and other Google services giving people the ability to collaborate around content and data in real-time., companyurl: www.mibbit.com, productdesc: We use several Google APIs: the YouTube data API lets users preview and play content inside chat; the Maps API shows maps inside chat, helping people explore locations; the Google Translate API translates chats across different languages in real-time; and the AdSense API displays contextually targeted ads that reflect users' preferences., companylogo: mibbit.png, companypod: Google APIs, companytags: Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bctkt2010-05-10T18:39:11.857ZRow: 93companyname: MindMeister, companylocation: Munich, Germany, companydesc: MindMeister allows your team to be more innovative by providing a shared collaboration and brainstorming environment using the proven mind mapping technique. Plan projects, manage meetings and sketch out business plans online with partners and colleagues, all in real time!, companyurl: www.mindmeister.com, productdesc: MindMeister uses the Google Documents Data List API and Contacts API to export mind maps to Google Docs and allow sharing with Google contacts. Customers can securely login using OpenID. The Provisioning API is used for administration., companylogo: mindmeister.png, companypod: Enterprise, companytags: Enterprise, Google APIs, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bpgoi2010-05-10T18:39:11.857ZRow: 94companyname: Mint.com, companylocation: Mountain View, California, companydesc: Mint.com lets you see all of your accounts in one place, set budgets, see where you spend, and find personalized savings – all for free.
+, companyurl: www.mint.com, productdesc: Mint.com for Android helps you track all of your accounts, budget, and manage your money right from your phone, updated automatically. The app takes advantage of key Android features such as widgets, smart folders, and system searches of Mint transactions.
+, companylogo: mint.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bqv8z2010-05-10T18:39:11.857ZRow: 95companyname: MLB, companylocation: New York, New York, companydesc: MLB Advanced Media, the interactive media company of Major League Baseball, manages the official league site and each of the 30 individual Club sites to create the most comprehensive Major League Baseball resource on the Internet.
+, companyurl: www.mlb.com, productdesc: MLB.com At Bat 2010 delivers an exclusive and reliable mobile experience for fans wherever they go with up-to-the-minute box scores and statistics, live audio with home and away broadcast feeds, in-game video highlights, MLB.com Gameday pitch speed and location tracking, and more on Android., companylogo: mlb.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bs9tc2010-05-10T18:39:11.857ZRow: 96companyname: Mobiata, companylocation: Ann Arbor, Michigan, companydesc: Mobiata, founded in November 2008, is a leader in mobile travel applications through innovative and beautifully designed products., companyurl: www.mobiata.com, productdesc: Track all your flights on beautiful satellite maps or elegant flight cards that update in real-time. An Android exclusive is a home screen widget that provides the latest information on your flight regardless of which app is running.
+, companylogo: mobiata.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/btodt2010-05-10T18:39:11.857ZRow: 97companyname: MobiTV, companylocation: Emeryville, California, companydesc: MobiTV, Inc. is a provider of technology solutions that deliver media over wireless and broadband networks. MobiTV’s flexible platform powers an end-to-end managed service for content ingestion, encoding and optimized delivery of media for major mobile operators., companyurl: www.mobitv.com, productdesc: MobiTV has taken advantage of Android’s open platform to create a unique media player that supports download of high quality video content with digital rights management for offline playback., companylogo: mobitv.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bjueu2010-05-10T18:39:11.857ZRow: 98companyname: Motorola, companylocation: Schaumburg, Illinois, companydesc: Motorola is known around the world for innovation in communications and focused on advancing the way the world connects. The Motorola MOTODEV™ Developer Program provides developers access to a wealth of services, support, and information to help design, develop and market wireless applications., companyurl: developer.motorola.com, productdesc: Motorola has chosen Android as a simple, powerful means to bring a whole new generation of phenomenal mobile experiences to Motorola users and has already announced an extensive portfolio of Android based handsets around the world. MOTODEV, Motorola's developer network, offers the one, easy-to-access, internet-enabled, open environment developers need to succeed in the future., companylogo: motorola.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bl8zb2010-05-10T18:39:11.857ZRow: 99companyname: mSpot, companylocation: Redwood City, California, companydesc: mSpot is free and the simplest way to get your entire music collection to your Android phone and listen from other internet-connected devices. mSpot is a mobile and PC entertainment company that delivers music, movies, and radio to more than six million mobile customers across 10 wireless carriers., companyurl: www.mspot.com, productdesc: Android technologies used: Android SDK and Eclipse Plug-in for app development; and Google Closure Tools to verify our JavaScript code used on website
+, companylogo: mspot.png, companypod: Android, companytags: Android, GWT, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bmnjo2010-05-10T18:39:11.857ZRow: 100companyname: myERP, companylocation: San Francisco, California, companydesc: myERP.com is a free online business suite that allows small businesses to manage their business in the cloud. myERP.com streamlines small businesses: sales, purchases, CRM, accounting, inventory, and logistics., companyurl: www.myerp.com, productdesc: myERP.com is developed using Google Web Toolkit on the client-side. GWT converts the Java code into a user interface of the AJAX type. While the user interfaces display in a few milliseconds, the application is just as user-friendly and intuitive as a locally installed application. myERP.com is also integrated with Google Apps, Gmail, Google Calendar and Contacts., companylogo: myerp.png, companypod: Enterprise, companytags: Enterprise, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bo2452010-05-10T18:39:11.857ZRow: 101companyname: MySpace, companylocation: Beverly Hills, California, companydesc: MySpace is a technology company connecting people through personal expression, content, and culture. MySpace empowers its global community to experience the Internet through a social lens by integrating personal profiles, photos, videos, mobile, messaging, games, and the world's largest music community., companyurl: www.myspace.com, productdesc: MySpace supports the development of applications based on OpenSocial, allowing developers to have a standardized way to develop great social apps across platforms. As a founding partner of OpenSocial with Google, MySpace is dedicated to open standards and specifications., companylogo: myspace.png, companypod: Social Web, companytags: Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/c0p7u2010-05-10T18:39:11.857ZRow: 102companyname: NaturalMotion , companylocation: San Francisco, California, companydesc: In addition to developing games, NaturalMotion provides advanced animation technologies for titles such as GTA IV, Red Dead Redemption and Star Wars.
+, companyurl: www.naturalmotion.com, productdesc: Backbreaker is an Android game with Android-specific features including uprated graphics and effects optimised for Android devices., companylogo: naturalmotion.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/c23sb2010-05-10T18:39:11.857ZRow: 103companyname: nextstop, companylocation: San Francisco, California, companydesc: nextstop is a community effort to build a catalog of all the best things to do, places to go, and experiences to try anywhere in the world. Our long term goal is to help you find something great to get out and do — whatever you are interested in, whatever language you speak, and wherever you are., companyurl: www.nextstop.com, productdesc: nextstop's web app runs on smartphones like Android, iPhone and modern browsers by using the latest HTML5 features such as localStorage, AppCache and GeoLocation. We also extensively use the Google AJAX APIs and Google Maps APIs., companylogo: nextstop.png, companypod: Chrome, companytags: Chrome, Android, Geo, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/c3ico2010-05-10T18:39:11.857ZRow: 104companyname: Ning, companylocation: Palo Alto, California, companydesc: Ning is a social platform for the world’s interests and passions online, offering an easy-to-use service that allows people to join and create Ning Networks. With more than 1.9 million Ning Networks created and 40 million registered users, millions of people every day are coming together across Ning to explore and express their interests, discover new passions, and meet new people around shared pursuits., companyurl: www.ning.com, productdesc: Ning Apps empower Ning Network Creators with a greater set of choices over the functionality of their Ning Network and ways they can increase engagement by their members. We support a subset of OpenSocial 0.8 APIs., companylogo: ning.png, companypod: Social Web, companytags: Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/c4wx52010-05-10T18:39:11.857ZRow: 105companyname: NordicTrack, companylocation: Logan, Utah, companydesc: NordicTrack is the premium brand more people buy for treadmills, ellipticals and other fitness products. Our consumers want to stay motivated and engaged for fitness, so we launched the interactive website iFit.com. The flagship feature is iFit Live powered by Google Maps, where you can draw a custom running route anywhere in the world there’s a map available., companyurl: www.ifit.com, productdesc: Using Wi-Fi, iFit.com sends a workout to your equipment that simulates the distance and the terrain of the route you’ve drawn. You can watch your real-time progress on Google Maps with satellite, 3D, and Street views. We’re bringing our NordicTrack X7i Incline Trainer – an extreme treadmill that inclines to 40% - so come by and experience the combination of NordicTrack, iFit, the Google Static Maps, JavaScript Maps and Earth API!, companylogo: nordictrack.png, companypod: Geo, companytags: Geo, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bv2y62010-05-10T18:39:11.857ZRow: 106companyname: Novell, companylocation: Waltham, Massachusetts, companydesc: Through our infrastructure software and ecosystem of partnerships, Novell harmoniously integrates mixed IT environments, allowing people and technology to work as one. Novell® Making IT Work As One™., companyurl: www.novell.com, productdesc: Novell Pulse is a new real-time collaboration environment for enterprises that's being built with Google Wave Federation Protocol so people can co-edit messages together across Novell Pulse and Google Wave in real-time. Novell Pulse also supports the Wave Gadgets API and Wave Robots API so extensions built by 3rd parties work seamlessly with a mix of users on Novell Pulse and Google Wave., companylogo: novell.png, companypod: Wave, companytags: Wave, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bwhin2010-05-10T18:39:11.857ZRow: 107companyname: NPR, companylocation: Washington, D.C., companydesc: NPR is an award-winning, multimedia news organization and an influential force in American life. In collaboration with more than 800 independent public radio stations nationwide, NPR strives to create a more informed public, one challenged and invigorated by a deeper understanding and appreciation of events, ideas, and cultures. NPR content is also available on other platforms such as satellite radio, an expansive web site, and mobile devices., companyurl: www.npr.org, productdesc: NPR news and content is available as an Android app (developed using the Android SDK), a Chrome Extension, and iGoogle Gadget. Additionally, search for NPR.org and the NPR API are powered by Google Search Appliances., companylogo: npr.png, companypod: Chrome, companytags: Chrome, Android, Enterprise, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bxw302010-05-10T18:39:11.857ZRow: 108companyname: NYSTROM Herff Jones Education Division, companylocation: Indianapolis, Indiana, companydesc: NYSTROM Herff Jones Education Division is the leading publisher of maps, globes, atlases, hands-on programs, and technology for the K-12 market. Introduced by NYSTROM in September 2009, StrataLogicaTM is a revolutionary, web-based product that delivers layers of age-appropriate, curriculum-based Social Studies content for K-12., companyurl: www.herffjones.com, productdesc: NYSTROM Herff Jones Education Division is using the Google Earth API to power the 3-D educational content available through StrataLogica. By using the Google Earth API, Nystrom and Roundarch have partnered to reinvent the classroom map and globe and present educational content in a groundbreaking, interactive format. , companylogo: herffjones.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/bzanh2010-05-10T18:39:11.857ZRow: 109companyname: OffiSync, companylocation: Seattle, Washington, companydesc: OffiSync Supercharges Microsoft Office, enabling users to significantly improve the way they create, collaborate and share their documents by integrating Microsoft Office with Google Docs, and Google Apps., companyurl: www.offisync.com, productdesc: OffiSync uses the Google Doclist API and Google Search API to extend the core functionality of Microsoft OFfice and introduce new innovative ways of collaboration in the cloud., companylogo: offisync.png, companypod: Enterprise, companytags: Enterprise, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cbxr62010-05-10T18:39:11.857ZRow: 110companyname: OnStar, companylocation: Detroit, Michigan, companydesc: OnStar, a leading provider of in-vehicle safety, security and communication services, has nearly 5.5 million subscribers, and is available on more than 30 MY 2010 GM models., companyurl: www.onstar.com, productdesc: The Chevrolet Volt Mobile App is powered by OnStar technology and is available on Android phones. Features include the ability to lock/unlock, remotely start your vehicle, view state of charge and remotely control when your Volt draws power from the grid., companylogo: onstar.png, companypod: Android, companytags: Android, adddate: 5/4/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cdcbn2010-05-10T18:39:11.857ZRow: 111companyname: OpenTable, companylocation: San Francisco, California, companydesc: OpenTable is a leading online provider of free, real-time online restaurant reservations for diners and reservation and guest management solutions for restaurants. OpenTable has more than 11,000 restaurant customers, and since its inception in 1998, has seated more than 120 million diners around the world., companyurl: www.opentable.com, productdesc: We use Google Maps and two versions of the Geolocation API to display real-time restaurant availability around a user’s current location on opentable.com and the Android smartphone platform., companylogo: opentable.png, companypod: Geo, companytags: Geo, Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ceqw02010-05-10T18:39:11.857ZRow: 112companyname: Oracle, companylocation: Redwood Shores, California, companydesc: Oracle provides a complete, open, and integrated business software and hardware systems to more than 370,000 customers—including 100 of the Fortune 100—that represent a variety of sizes and industries in more than 145 countries around the globe., companyurl: www.oracle.com, productdesc: Demonstrating Oracle CRM OnDemand and OnPremise Collaboration with Google Wave, Google Docs, and OpenSocial gadgets. Integration developed using Google Web Toolkit, Google App Engine and CRM OnDemand and OnPremise Web Services., companylogo: oracle.png, companypod: Wave, companytags: Wave, Social Web, App Engine, GWT, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/cg5gh2010-05-10T18:39:11.857ZRow: 113companyname: Orange Honey, companylocation: Seattle, Washington, companydesc: Orange Honey has developed online photo editing and vector sketching applications that bring desktop application quality to the browser and
+mobile experience., companyurl: www.orangehoney.com, productdesc: Orange Honey products utilize Google Chrome and the HTML5 Canvas standard to support cross-platform image sharing and accessibility, providing a rich image creation and sharing experience for all users., companylogo: orangehoney.png, companypod: Chrome, companytags: Chrome, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/c6bhi2010-05-10T18:39:11.857ZRow: 114companyname: OrangeScape, companylocation: Chennai, Tamilnadu, India, companydesc: OrangeScape is a Plaform-as-a-Service for building business applications that can run across cloud platforms - Google App Engine, IBM SmartCloud, Amazon EC2 or your own data center., companyurl: www.orangescape.com, productdesc: The development environment - OrangeScape Studio - runs on App Engine and the application developed on OrangeScape Studio can be deployed on App Engine., companylogo: orangescape.png, companypod: App Engine, companytags: App Engine, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/c7q1z2010-05-10T18:39:11.857ZRow: 115companyname: Oxylabs Networks, companylocation: Gurgaon, Haryana, India, companydesc: Oxylabs develops cross-platform, cross-device social games., companyurl: www.oxylabs.com, productdesc: We use several Google technologies to develop our platform: App Engine for a scalable back end; OpenSocial for distribution; Google Charts for our API; Google Translate for our feedback management system; and Google Apps for email, collaboration and documents., companylogo: oxylabs.png, companypod: App Engine, companytags: App Engine, Social Web, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/c94mc2010-05-10T18:39:11.857ZRow: 116companyname: Palm, companylocation: Sunnyvale, California, companydesc: Palm creates intuitive and powerful mobile experiences that enable consumers and businesses to connect to their information in more useful and usable ways., companyurl: www.palm.com, productdesc: Palm has a long history of working closely with Google, building services like Google Search and Google Maps directly into Palm devices. Palm and Google share the same belief in the Web and the opportunities it provides developers. The native platform for Palm's groundbreaking webOS is the Web, allowing you to write real applications for mobile as easily as you can write them for the Web. , companylogo: palm.png, companypod: Chrome, companytags: Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/caj6t2010-05-10T18:39:11.857ZRow: 117companyname: Pandora, companylocation: Oakland, California, companydesc: Pandora is the world’s largest internet radio service available through personal computers, mobile phones, and a wide array of consumer electronic devices. More than 45 million listeners enjoy personalized radio stations based on Pandora’s Music Genome Project™, which enables Pandora to quickly and easily create relevant playlists, full of discovery., companyurl: www.pandora.com, productdesc: We use a number of Google products: the Android SDK for creating the Pandora app, Google Analytics, Google Webmaster Tools, AdSense and DoubleClick., companylogo: pandora.png, companypod: Android, companytags: Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hmyxm2010-05-10T18:39:11.857ZRow: 118companyname: Parrot, companylocation: Paris, France, companydesc: Parrot, a global leader in wireless devices for mobile phones, stands on the cutting edge of innovation. In January 2010, Parrot introduced the Parrot AR.Drone, a flying Wi-Fi quadricopter, which allows users to play video games in Augmented Reality., companyurl: www.parrot.com, productdesc: The Parrot AR.Drone utilizes wifi connectivity, OpenGL ES for video streaming, and the accelerometer and trackball for navigation controls., companylogo: parrot.png, companypod: Android, companytags: Android, adddate: 5/4/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hodi32010-05-10T18:39:11.857ZRow: 119companyname: PayPal, companylocation: San Jose, California, companydesc: PayPal is the faster, safer way to pay and get paid online. The service allows members to send money without sharing financial information, with the flexibility to pay using their account balances, bank accounts, credit cards or promotional financing., companyurl: www.paypal.com, productdesc: With the PayPal Mobile Payments Library, Android developers will be able to start accepting in-app payments for physical goods, services, donations and personal payments in a matter of minutes. Collecting financial information or requiring the consumer to fill long forms to complete a purchase is no longer needed, the PayPal library takes care of the payment flow and developers can keep focusing on building great apps.
+, companylogo: paypal.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hps2g2010-05-10T18:39:11.857ZRow: 120companyname: PlanetInAction, companylocation: Auckland, New Zealand, companydesc: Dinther Product Design Ltd showcases their browser-based games on PlanetInAction.com, an example of what can be done with the Google Earth API. We also handle commercial projects where we develop innovative software solutions using Google Earth and other Google products., companyurl: www.planetinaction.com, productdesc: We mainly use the Google Maps and Earth API. Our solutions tend to be low tech, because we focus on results and short development times., companylogo: planetinaction.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hr6mx2010-05-10T18:39:11.857ZRow: 121companyname: Plaxo, companylocation: Mountain View, California, companydesc: Plaxo is working to deliver on the vision of a truly smart, socially-aware address book that lives securely in the cloud, while interacting with all of the applications, services, and devices in your life where contact information matters., companyurl: www.plaxo.com, productdesc: We are using Google Contacts Data APIs to build two-way contact sync between Plaxo and Google., companylogo: plaxo.png, companypod: Social Web, companytags: Social Web, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hhcny2010-05-10T18:39:11.857ZRow: 122companyname: Playfish, companylocation: San Francisco, California, companydesc: Playfish is a leader in the social gaming industry in innovation and creativity with award-winning, category-defining games designed for friends to play together. The company has changed the way people play games by creating more social and connected experiences., companyurl: www.playfish.com, productdesc: Playfish has brought some of its most iconic social games to Open Social communities around the world, from Who Has The Biggest Brain? on iGoogle, to Pet Society on Cyworld, and Word Challenge on Netlog., companylogo: playfish.png, companypod: Social Web, companytags: Social Web, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hir8f2010-05-10T18:39:11.857ZRow: 123companyname: PolarBit, companylocation: Stockholm, Sweden, companydesc: Polarbit is a leading developer of high-end 3D games and middleware for the mobile and handheld markets worldwide. Our dedication to our work and uncompromising attitude towards quality ensure customers and users that Polarbit technology is always synonymous with world class technology.
+, companyurl: www.polarbit.com, productdesc: Polarbit games are built using the Android NDK, companylogo: polarbit.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hk5ss2010-05-10T18:39:11.857ZRow: 124companyname: Prezi, companylocation: San Francisco, CA, companydesc: Prezi is a web-based presentation application and storytelling tool that uses a single canvas instead of traditional slides. The canvas allows users to create non-linear presentations, where they can zoom in and out of a visual map., companyurl: www.prezi.com, productdesc: Prezi uses the Google Wave Gadgets API to create new features and functionality for our presentation editor., companylogo: prezi.png, companypod: Wave, companytags: Wave, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hlkd92010-05-10T18:39:11.857ZRow: 125companyname: ProcessOne, companylocation: Paris, France, companydesc: ProcessOne provides real-time interpersonal web communications for telecommunications organisations, ISPs, social networking sites and large enterprises. Well-known businesses benefit from our expertise in real-time instant messaging., companyurl: www.process-one.net, productdesc: As an extension to its XMPP server, ProcessOne uses the Google Wave Federation Protocol in its own "WaveOne" server, federated with Google's WaveSandbox and FedOne., companylogo: processone.png, companypod: Wave, companytags: Wave, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hy7gy2010-05-10T18:39:11.857ZRow: 126companyname: processWave.org, companylocation: Potsdam, Germany, companydesc: processWave.org is the project of seven students from Germany's #1 ranked software engineering school, the Hasso Plattner Institute in Potsdam, Germany. We are working on a collaborative diagram editor that supports software and business process modeling with UML, BPMN and EPC. It allows multiple people to edit the same diagram at the same time., companyurl: www.processwave.org, productdesc: Our diagram editor is realized as a gadget for Google Wave, which provides the collaborative basis for our product., companylogo: processwave.png, companypod: Wave, companytags: Wave, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hzm1f2010-05-10T18:39:11.857ZRow: 127companyname: Qik, companylocation: Redwood City, California, companydesc: Qik lets people capture and share live and recorded video with their friends, family, and the world, right from their phone., companyurl: www.qik.com, productdesc: Qik uses the Android SDK to produce video recording and broadcasting software for all Android phones, and Google Maps for website video location information, companylogo: qik.png, companypod: Android, companytags: Android, Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/i10ls2010-05-10T18:39:11.857ZRow: 128companyname: Red Hat, companylocation: Raleigh, North Carolina, companydesc: JBoss Enterprise Middleware is a broad portfolio of enterprise-class open source middleware, delivered by Red Hat, a leader in open source solutions. The portfolio includes application, portal, data integration, rules management and ESB-based SOA platforms. JBoss Enterprise Middleware is available via subscriptions that include patches, updates, support, multi-year maintenance policies, and software assurance., companyurl: www.redhat.com, productdesc: We use GWT for several of our products, including the JBoss Web Framwork Kit, the JBoss Business Rules Management System, and development frameworks like Seam, which provides GWT integration layer. GateIn, our new portal project, includes the ability for end users to easily create GWT widgets and portlet-based applications and dashboards within a secure, personalized site experience., companylogo: redhat.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/i2f692010-05-10T18:39:11.857ZRow: 129companyname: Remember The Milk, companylocation: Sydney, NSW, Australia, companydesc: Remember The Milk is an online task management service with more than 2 million users. With products available in 31 languages, Remember The Milk helps people all over the world be more organized and productive., companyurl: www.rememberthemilk.com, productdesc: Remember The Milk makes extensive use of Google technologies. Products include an Android app, offline web app access with Gears, a Google Maps mashup to visualize task locations, Google Talk reminders, Google Calendar sidebar and event gadgets, an iGoogle gadget, a Gmail sidebar gadget, and Firefox and Google Chrome extensions that integrate with Gmail., companylogo: rememberthemilk.png, companypod: Google APIs, companytags: Google APIs, Android, Geo, Chrome, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hsl7a2010-05-10T18:39:11.857ZRow: 130companyname: Rent.com, companylocation: Santa Monica, California, companydesc: With Rent.com, property owners and managers can create rich listings that include photos, floorplans, amenities, move-in specials, and detailed descriptions, while renters can search millions of units, filtering by number of beds/bath, city, neighborhood, price and amenities in order to find their perfect home., companyurl: www.rent.com, productdesc: We are using the Google maps API V3.0 on the Android and iPhone OS for mapping the location of, and providing driving directions to, rental properties that match a user's specific search criteria. We are also providing a search nearby feature that makes use of the Google Maps API V3.0., companylogo: rent.png, companypod: Geo, companytags: Geo, Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/htzrr2010-05-10T18:39:11.857ZRow: 131companyname: Roambi, companylocation: San Diego, California, companydesc: Roambi, from MeLLmo, is the next generation mobile business application. Roambi puts the pulse of your business in the palm of your hand by transforming existing business reports and data into rich, interactive visualizations and mobile dashboards, delivered to any iPhone. Roambi lets busy professionals track the status of their project, department or company – anytime and anywhere – and make faster, smarter decisions on-the-go., companyurl: www.roambi.com, productdesc: Roambi utilizes the Google Docs and Spreadsheet APIs as well as OAuth to give users the ability to convert data from Google spreadsheets into our interactive views., companylogo: roambi.png, companypod: Enterprise, companytags: Enterprise, Social Web, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/hwswl2010-05-10T18:39:11.857ZRow: 132companyname: RockYou, companylocation: Redwood City, California, companydesc: RockYou is a leading innovator, creator, and distributor of content on the social web, with over 90M monthly US users, 250M users worldwide, and 15 billion global impressions., companyurl: www.rockyou.com, productdesc: We utilize Open Social to implement our applications for many of the large social networks out there including MySpace in the US, Mixi in Japan, and Cyworld in Korea. We've been big proponents for the Open Social platform and were key in pushing adoption by all of the Asian platforms., companylogo: rockyou.png, companypod: Social Web, companytags: Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/i9g0a2010-05-10T18:39:11.857ZRow: 133companyname: Rosetta, companylocation: Hamilton, New Jersey, companydesc: Rosetta is the largest independent interactive agency in the country whose Personality-based segmentations uncover the drivers of brand choice and product usage. Rosetta's industry-focused marketers, world-class technology teams and creative teams translate those insights into relevant marketing solutions that attract, retain and deepen a brand's best customer relationships., companyurl: www.rosetta.com, productdesc: We use Google Web Toolkit to create the user interface for a number of our different ecommerce clients. The toolkit flexibility and ease of use enables us to create a cutting edge, brilliant, web 2.0 feel for our websites., companylogo: rosetta.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/iaukr2010-05-10T18:39:11.857ZRow: 134companyname: RunMyProcess, companylocation: Paris, France, companydesc: RunMyProcess is the platform as a service of choice for workflows and integration in the Cloud. It offers a complete integration with Google Apps, as well as a set of 1,000+ connectors to link it in a few clicks to most traditionnal or SaaS softwares., companyurl: www.runmyprocess.com, productdesc: We provide an abstraction layer on almost all of Google's web service API (Calendar, Sites, Spreadsheet, Docs, Analytics, etc) allowing users to use any API easily within workflows or business process designed on our platform. We use Google Web Toolkit for dynamically generating web applications and gadgets, as well as GViz for reporting dashboard on workflows embedded within gadgets. W also use Google's enablement of OAuth, OpenID and SAMLv2 for unified authentication capabilities., companylogo: runmyprocess.png, companypod: Enterprise, companytags: Enterprise, Google APIs, GWT, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ic9542010-05-10T18:39:11.857ZRow: 135companyname: SADA Systems, companylocation: Los Angeles, California, companydesc: One of the first 10 Google Apps for Enterprise Partners worldwide and initial contributor to the Google Apps Open Source provisioning tool kit, SADA Systems, Inc. has developed a proven track record in successful implementations of Google Apps solutions. SADA works closely with its clients to create custom, leading edge solutions that strategically meet their business and institutional objectives., companyurl: www.sadasystems.com, productdesc: At SADA, we not only resell Google Apps, but use Google Apps internally for collaboration along with the GData API to develop migration tools. We utilize App Engine to host custom applications, all of which allow us to more efficiently develop custom products for our clients such as our Single Sign On (SSO) tool, User Provisioning, On-premise and hosted Sync Tools., companylogo: sada.png, companypod: Enterprise, companytags: Enterprise, Google APIs, App Engine, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/idnpl2010-05-10T18:39:11.857ZRow: 136companyname: Safari Books Online, companylocation: Sebastopol, California, companydesc: Safari Books Online is an on-demand digital library that provides searchable, online access to the full content of thousands of books, training videos and pre-published manuscripts from the world's leading authors and publishers. For one low monthly price, Safari Books Online delivers access to the most current and critical technology, digital media and business titles, as well as time-saving tools that make it easy to organize information and personalize your learning experience., companyurl: www.safaribooksonline.com, productdesc: To deliver an improved mobile reading experience, we are implementing HTML5 features such as local storage for storing offline books and CSS transitions., companylogo: safaribooksonline.png, companypod: Chrome, companytags: Chrome, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/i3tqm2010-05-10T18:39:11.857ZRow: 137companyname: Salesforce.com, companylocation: San Francisco, California, companydesc: Salesforce.com is an enterprise cloud computing company. Based on Salesforce.com's real-time, multitenant architecture, the company's platform and CRM applications have revolutionized the way companies collaborate and communicate with their customers., companyurl: www.salesforce.com, productdesc: The Force.com Toolkit for Google Data APIs provides a set of tools and services that developers can use to take advantage of Google Data APIs from within Force.com. The Force.com for Google App Engine library enables you to access the Force.com platform from Google App Engine Apps - Cloud to Cloud. Salesforce.com has done recent prototyping with Google Wave APIs, and the company also offers Google Apps and Google Adwords integration., companylogo: salesforce.png, companypod: Wave, companytags: Wave, Google APIs, App Engine, Enterprise, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/i58b32010-05-10T18:39:11.857ZRow: 138companyname: Samsung, companylocation: San Jose, California, companydesc: Samsung Electronics Co., Ltd. is a global leader in semiconductor, telecommunication, digital media and digital convergence technologies with 2009 consolidated sales of US$116.7 billion. Recognized as one of the fastest growing global brands, Samsung Electronics is a leading producer of digital TVs, memory chips, mobile phones and TFT- LCDs., companyurl: www.samsung.com, productdesc: As a member of Open Handset Alliance, Samsung develops mobile phones based on Android for markets across the world. Including the recently announced Samsung Galaxy S., companylogo: samsung.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/g2s5e2010-05-10T18:39:11.857ZRow: 139companyname: SAP, companylocation: Palo Alto, California, companydesc: As a leading provider of business software, SAP delivers products and services that help accelerate business innovation for our customers. Today, customers in more than 120 countries run SAP applications -- from distinct solutions addressing the needs of small businesses and midsize companies to suite offerings for global organizations., companyurl: www.sap.com, companylogo: sap.png, companypod: Wave, companytags: Wavehttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/i81fx2010-05-10T18:39:11.857ZRow: 140companyname: Seesmic, companylocation: San Francisco, California, companydesc: Seesmic has become one of the most popular Twitter and Facebook clients that allow users to easily access their social networks in one application and successfully manage their online presence. From web to mobile or email to chat, Seesmic looks to be the gateway to all your social services on any online platform., companyurl: www.seesmic.com, productdesc: Seesmic is available on Android, taking advantage of the unique features of the mobile platform, thus making a powerful, yet nevertheless simple and easy to use application. Seesmic also relies on Google Web Toolkit as the main foundation technology for its rich and appreciated web application: Seesmic Web., companylogo: seesmic.png, companypod: Android, companytags: Android, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ikojm2010-05-10T18:39:11.857ZRow: 141companyname: SGN, companylocation: Palo Alto, California, companydesc: SGN is a publisher of mobile social games that emphasize console-quality graphics and social engagement., companyurl: www.sgn.com, productdesc: Android Platform technologies used: NDK r3 based game engine; fully support android multi-tasking, resuming right in the middle of a multiplayer game; OpenGL ES 1.1 graphics; high detailed terrain and models, water effects; AudioPool sound system bridge from NDK to JAVA Classes; and Linux polling and sockets for multiplayer., companylogo: sgn.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/im3432010-05-10T18:39:11.857ZRow: 142companyname: SheepDog Inc., companylocation: Halifax, Nova Scotia, Canada, companydesc: SheepDog, like its namesake, is reliable, loyal, protective and highly trained to manage the heart of business today: information and connectivity. SheepDog facilitates migration projects to the Google Apps environment as well as showing clients how to fully leverage the most valuable and powerful aspects of the Google cloud., companyurl: www.sheepdoginc.ca, productdesc: Gtrax is an application that is built on App Engine with Google Web Toolkit. Its key advantage is that it has been designed to tightly integrate with Google Apps APIs so that it simplifies use. It's fully integrated with OpenID, MVP design pattern, command design pattern, and DI design pattern. , companylogo: sheepdog.png, companypod: Enterprise, companytags: Enterprise, App Engine, GWT, Google APIs, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/inhog2010-05-10T18:39:11.857ZRow: 143companyname: shopkick Inc., companylocation: Palo Alto, California, companydesc: shopkick turns the physical retail world into an interactive world for consumers, through the use of their cell phones. Its first app is CauseWorld - the first app that lets you do good deeds simply for walking into a store. Sponsors like Citibank and Kraft Foods provide donation money; consumers decide where it goes., companyurl: www.shopkick.com, productdesc: The CauseWorld mobile application runs on the Google Android platform., companylogo: shopkick.png, companypod: Android, companytags: Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/iow8x2010-05-10T18:39:11.857ZRow: 144companyname: ShufflePoint, companylocation: Newark, Delaware, companydesc: ShufflePoint transforms Web Analytics data into PowerPoint, Excel or Google Gadgets. We provide powerful solutions for Web Business Analysts that eliminate tedious cut-and-paste work so they can instead spend their time creating business value., companyurl: www.shufflepoint.com, productdesc: We use the Google Analytics API to retrieve data used for report generation. We also use the Google Visualization API and gadgets to create web dashboards., companylogo: shufflepoint.png, companypod: Google APIs, companytags: Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/if29y2010-05-10T18:39:11.857ZRow: 145companyname: Skytap, companylocation: Seattle, Washington, companydesc: Skytap a leading provider of self-service cloud automation solutions for dynamic workloads. Skytap enables users to run enterprise apps unchanged in the cloud, collaborate securely with global teams, and gain unparalleled business productivity. IT organizations can use Skytap for application development, testing, virtual training, ERP migration and sales demonstration projects and reduce costs by up to 70%., companyurl: www.skytap.com, productdesc: Skytap uses Google Analytics APIs and Google Apps as its OpenID identity provider., companylogo: skytap.png, companypod: Enterprise, companytags: Enterprise, Social Web, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/igguf2010-05-10T18:39:11.857ZRow: 146companyname: SlideRocket, companylocation: San Francisco, California, companydesc: SlideRocket is reinventing presentations with its feature rich, innovative platform that lets you create, manage, share and measure your presentations in one complete and integrated application., companyurl: www.sliderocket.com, productdesc: SlideRocket uses Google Apps API's to provide seamless integration between Google Docs and SlideRocket. We use: Google's user provisioning API for integrating accounts; the Apps licensing API and federated login (OpenID + OAuth) for enabling single sign-on; the Google Docs read/write API for importing dynamic tables, charts and presentation from Google Docs to SlideRocket; and the Contacts read/write API for integrating Google Contacts into SlideRocket., companylogo: sliderocket.png, companypod: Enterprise, companytags: Enterprise, Google APIs, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ihves2010-05-10T18:39:11.857ZRow: 147companyname: Smartsheet, companylocation: Bellevue, Washington, companydesc: Smartsheet provides an online collaboration tool used by thousands of small businesses and teams in larger corporations for project and task management, sales pipeline management, crowdsourcing and other types of work management., companyurl: www.smartsheet.com, productdesc: Smartsheet enables single sign in for Googe Apps and Google Accounts using Google Account Authentication. Smartsheet uses the Google Documents List Data API to enable customers to attach Google Docs to any task and the Google Spreadsheets Data API for import/export between Smartsheet and Google spreadsheets. We also use the Google Gadgets API for our Smartsheet iGoogle gadgets., companylogo: smartsheet.png, companypod: Enterprise, companytags: Enterprise, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ij9z92010-05-10T18:39:11.857ZRow: 148companyname: SnapABug, companylocation: Huntsville, Alabama, companydesc: The SnapABug Help widget reduces companies QA and support cycle. SnapABug can easily be embedded on any website and provides all the information necessary to debug the situation quickly, including a snapshot of the remote visitor webpage. SnapABug live also lets you chat with your websites visitors while they browse, and does all this with no software install., companyurl: www.snapabug.com, productdesc: SnapABug is a 100% Google App Engine Java application. It interfaces with several Google services such as Google Talk and Google Maps API. SnapABug also relies on Google Web Toolkit as the technology behind its Widget Configurator user interface., companylogo: snapabug.png, companypod: App Engine, companytags: App Engine, Geo, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ivx4q2010-05-10T18:39:11.857ZRow: 149companyname: Socialwok, companylocation: Singapore, companydesc: Voiceroute Pte Ltd is the company behind Socialwok, a feed-based, business, social networking service that help thousands of small and medium businesses to collaborate on Google Apps. Socialwok is the winner of the Techcrunch 2009 demopit award., companyurl: www.socialwok.com, productdesc: Socialwok is a Google App Engine Java application that is tightly integrated with Google Apps using Google Data APIs. Our user interface uses Google Web Toolkit., companylogo: socialwok.png, companypod: App Engine, companytags: App Engine, Enterprise, Google APIs, GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ixbp72010-05-10T18:39:11.857ZRow: 150companyname: Sony Ericsson, companylocation: Lund, Sweden, companydesc: Sony Ericsson is a 50:50 joint venture by Sony and Ericsson established in October 2001, with global corporate functions located in London and operations in all major markets. Sony Ericsson vision is to become the industry leader in Communication Entertainment; where new styles of communicating through the internet and social media, become entertainment. Sony Ericsson offers exciting consumer experiences through phones, accessories, content and applications., companyurl: www.sonyericsson.com, productdesc: As one of the leading OEMs of Android phones, Sony Ericsson makes use of Android code management and review tools built around git, including Google-created gerrit and repo tools. Sony Ericsson uses the Android SDK and related tools, as well as creating new additions to Android which are in many cases contributed to the Android platform., companylogo: sonyericsson.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/iyq9k2010-05-10T18:39:11.857ZRow: 151companyname: Spanning, companylocation: Austin, Texas, companydesc: Spanning extends the Google Apps collaboration and messaging suite. Spanning Sync 3 syncs both Google Calendar with Apple iCal and Google Contacts with the OS X Address Book. Spanning Backup for Google Apps is a new cloud-based application that continually backs up Google Calendar, Contacts, and Docs., companyurl: www.spanning.com, productdesc: Our products use many of the Google Data APIs, including those for Calendar, Contacts, Docs, Spreadsheets, and Provisioning. Plus, we use Google Apps, Groups, Analytics, Feedburner, YouTube, and Picasa Web Albums to run our business., companylogo: spanningsync.png, companypod: Enterprise, companytags: Enterprise, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/j04u12010-05-10T18:39:11.857ZRow: 152companyname: Spiral Universe, companylocation: White Plains, New York, companydesc: Spiral is a powerful information management system and learning platform used by schools and colleges in 60 countries around the world. Spiral uses the power of modern technologies to improve the learning experience and provide valuable tools to school administrators, teachers, students, and parents. Our mission is to bring the best information technology to all schools regardless of their means., companyurl: www.spiraluniverse.com, productdesc: Spiral is one of the largest GWT-based applications ever built, offering hundreds of tools for school administrators, teachers, students, and parents through a cloud-based webtop user interface. We recommend Chrome to our users because of its speed and support for HTML5 features used in our system., companylogo: spiraluniverse.png, companypod: GWT, companytags: GWT, Chrome, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/iqav22010-05-10T18:39:11.857ZRow: 153companyname: Springpad, companylocation: Charlestown, Massachusetts, companydesc: Springpad is a free web app that gives you many ways to save and access anything you want to remember. It automatically organizes and enhances the data you've stored with useful links and offers to save you time and money., companyurl: www.springpadit.com, productdesc: Springpad was built from the ground up using Google Web Toolkit. Springpad also uses Google's AJAX api's (search, maps, local, gcal), and OpenID/OAuth. An Android application is in the works., companylogo: springpad.png, companypod: GWT, companytags: GWT, Google APIs, Social Web, Android, Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/irpfj2010-05-10T18:39:11.857ZRow: 154companyname: Square, companylocation: San Francisco, California, companydesc: Square is focused on bringing immediacy, transparency, and approachability to the world of payments: an inherently social interaction each of us participates in daily., companyurl: www.squareup.com, productdesc: Android features used: Intent API for 3rd party developers; background processing; and notifications., companylogo: square.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/iuikd2010-05-10T18:39:11.857ZRow: 155companyname: StudyBlue, companylocation: Madison, Wisconsin, companydesc: StudyBlue is a student-driven study network. Students using our services cut their study time dramatically while performing better, consistently, with less stress regardless of proficiency. We offer class and subject-specific content coupled with sophisticated tools that provide real-time adaptive feedback into the most effective methods of learning and retention., companyurl: www.studyblue.com, productdesc: Our website is built entirely with Google Web Toolkit (GWT). GWT allows for complete AJAX integration without sacrificing usability or integration capabilities. Our system architecture enables rapid evolution and adaptation to changing user expectations/needs, and delivery/consumption alternatives., companylogo: studyblue.png, companypod: GWT, companytags: GWT, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/j75o22010-05-10T18:39:11.857ZRow: 156companyname: The Huffington Post, companylocation: New York, New York, companydesc: The Huffington Post is a leading social news and opinion site, which in four and a half years has become an influential media brand -- "The Internet Newspaper." The site offers coverage of politics, media, business, entertainment, living, style, sustainable “green” living, world news, technology, and comedy, and is a top destination for news, blogs and original content., companyurl: www.huffingtonpost.com, productdesc: We use a number of Google products, including Google Apps for messaging and collaboration, Google Analytics for web trends, and the Search and Trends APIs in our CMS. Our website lets users sign in via Google Friend Connect, Google Site Search powers searches on our website, and we've built a HuffPost Google gadget and Android app. We're using the YouTube Video Uploader to help cover citizen journalism and allow our community to participate. HuffPost content is also featured on Google’s Fast Flip that is integrated within Google News pages., companylogo: huffingtonpost.png, companypod: Google APIs, companytags: Google APIs, Enterprise, Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/j8k8j2010-05-10T18:39:11.857ZRow: 157companyname: The New York Times, companylocation: New York, New York, companydesc: The New York Times Company includes The New York Times, the International Herald Tribune, The Boston Globe, 15 other daily newspapers and more than 50 web sites, including NYTimes.com, Boston.com and About.com. The Company’s core purpose is to enhance society by creating, collecting and distributing high-quality news, information and entertainment., companyurl: www.nytimes.com, productdesc: The New York Times is using the latest in HTML5 to advance our online products, including video, Application Cache, DOM storage, and advanced CSS3 features. The result is exciting new Desktop-class apps that will run great on mobile phones, Chrome, and the forthcoming Chrome OS., companylogo: nytimes.png, companypod: Chrome, companytags: Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jbddd2010-05-10T18:39:11.857ZRow: 158companyname: TheDeadline, companylocation: Hamburg, Germany, companydesc: TheDeadline is an intelligent Todo-Manager. Adding and tagging todos is as easy as writing a Twitter post. TheDeadline provides a novel way to share todos fast and easy with co-workers to collaborate in small and large projects. An integrated artificial intelligence gives status updates, asks for decisions and automatically re-submits overdue todos in a smart way without annoying the user., companyurl: the-deadline.appspot.com, productdesc: We are using Google App Engine and Google Closure. Integration with Google Apps will be available by the time of Google I/O 2010., companylogo: thedeadline.png, companypod: App Engine, companytags: App Engine, Enterprise, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/j1jee2010-05-10T18:39:11.857ZRow: 159companyname: Thermopylae Sciences and Technology, companylocation: Arlington, Virginia, companydesc: Thermopylae Sciences + Technology is non-traditional services and software company that harnesses new and emerging concepts and technologies to rapidly deliver solutions to customers in Federal/DoD/commercial sectors. These specific solutions include products like iSpatial and iHarvest, as well as custom solution development built specifically around a customer or problem set., companyurl: www.t-sciences.com, productdesc: Thermopylae uses the Google Maps API, Earth API, Google Earth Enterprise, as well as the Android SDK in both our iSpatial & UBIQUITY products and within our custom solution development efforts for customers like the Secretary of State, US State Department and US Army, SOUTHCOM for Haiti Relief effort., companylogo: tsciences.png, companypod: Geo, companytags: Geo, Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/j2xyv2010-05-10T18:39:11.857ZRow: 160companyname: Thumbplay, companylocation: New York, New York, companydesc: Thumbplay, a leading provider of mobile entertainment in the U.S., has built an award-winning service centered on delivering millions of pieces of mobile content to 95 percent of U.S. devices among every major carrier. Thumbplay’s latest offering is Thumbplay Music, a next generation, cloud-based music service that delivers millions of songs on-demand to smartphones and PCs/Macs., companyurl: www.thumbplay.com, productdesc: Thumbplay Music is delivering cloud-based access to millions of songs via Android and soon via Chrome OS using HTML5., companylogo: thumbplay.png, companypod: Chrome, companytags: Chrome, Android, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/j4cj82010-05-10T18:39:11.857ZRow: 161companyname: TimeBridge Inc., companylocation: Berkeley, California, companydesc: TimeBridge is an easy-to-use web app that helps busy professionals schedule and run great meetings across companies and calendaring systems. TimeBridge helps you quickly find a time to meet, add a phone or web conference, and collaborate on the agenda, action items, meeting documents and notes., companyurl: www.timebridge.com, productdesc: TimeBridge created extensions to Calendar and Wave, using GData and OpenID/oAuth; and supports Google Apps Marketplace with Universal Navigation integration that allows for the enablement of whole domains by administrators. Our web app fully supports Calendar and Contact integration for seamless experience and Single Sign-on., companylogo: timebridge.png, companypod: Wave, companytags: Wave, Google APIs, Social Web, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jie7e2010-05-10T18:39:11.857ZRow: 162companyname: TribalDDB London, companylocation: London, United Kingdom, companydesc: We are a full service digital agency based in London. Our approach to digital communications is invention, we invent based on human insight, we build and produce never-seen-before digital experiences which enthral, excite, provoke action and emotion, thereby providing new ways for people to engage with our clients' brands., companyurl: blogs.tribalddb.co.uk, productdesc: We use the Google Maps API to produce compelling geo-aware Web applications., companylogo: tribalddb.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jjsrv2010-05-10T18:39:11.857ZRow: 163companyname: TripIt, companylocation: San Francisco, California, companydesc: TripIt helps people organize and share their travel plans no matter where they book. Travelers simply forward their travel confirmation emails from over 1,000 sites to plans@tripit.com and TripIt automatically creates a master itinerary that combines all their travel plans in one place plus weather, maps, restaurants and more, and can be accessed anytime online or through a mobile device. , companyurl: www.tripit.com, productdesc: We use Open Social for our LinkedIn app, we use the Android Software Development Kit for our Android app, and we use Google Geo APIs for maps and directions in our trip itineraries., companylogo: tripit.png, companypod: Android, companytags: Android, Social Web, Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jl7c82010-05-10T18:39:11.857ZRow: 164companyname: Trulia, companylocation: San Francisco, California, companydesc: Trulia is a real estate site focused on empowering you with smarter tools to help you find the right home. Whether you are an active buyer, seller or real estate enthusiast, Trulia gives you all the information you care about, from rich, property data to a personalized search experience. Trulia brings together local information, community insights, market data and national listings all in one place., companyurl: www.trulia.com, productdesc: We use the Google Maps API to enrich Trulia’s search result pages and residential real estate property pages., companylogo: trulia.png, companypod: Geo, companytags: Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jcrxq2010-05-10T18:39:11.857ZRow: 165companyname: TweakerSoft, companylocation: Torino, Italy, companydesc: TweakerSoft originated from more than a decade of software development in CAD/CAM, graphics and mobile applications. They specialize in the development of innovative Mac and iPhone software., companyurl: www.tweakersoft.com, productdesc: Our mobile application, AroundMe, uses the Google Maps API and Local Search API. AroundMe is also available for Android devices., companylogo: tweakersoft.png, companypod: Google APIs, companytags: Google APIs, Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/je6i72010-05-10T18:39:11.857ZRow: 166companyname: TweetDeck, companylocation: London, United Kingdom, companydesc: TweetDeck is a leading browser for the real-time and social web, allowing users to connect with Twitter, Facebook, LinkedIn and MySpace. Our advanced user interface gives you more power in managing your social stream across desktop and mobile platforms. , companyurl: www.tweetdeck.com, productdesc: Our upcoming product is being developed on Android and we're also using AppEngine to power various parts of our backend infrastructure. In addition, we consume a variety of APIs: Google Analytics for traffic monitoring, the Visualization API for internal statistics tracking systems, and the Social Graph API to help model the social web., companylogo: tweetdeck.png, companypod: App Engine, companytags: App Engine, Android, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jfl2k2010-05-10T18:39:11.857ZRow: 167companyname: Twilio, companylocation: San Francisco, California, companydesc: Twilio provides a web-service API for businesses to build scalable, reliable communication apps that make and receive phone calls and and send and receive SMS messages., companyurl: www.twilio.com, productdesc: Twilio uses the Google Wave API to build a robot extension called twiliobot that enables users to make and receive phone calls from inside Google Wave. Calls are recorded, transcribed, and posted back to the wave., companylogo: twilio.png, companypod: Wave, companytags: Wave, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jgzn12010-05-10T18:39:11.857ZRow: 168companyname: Ubisoft, companylocation: San Francisco, California, companydesc: Ubisoft is a leading producer, publisher and distributor of interactive entertainment products worldwide and has grown considerably through a strong and diversified line-up of products and partnerships. Ubisoft is present in 28 countries and has sales in more than 55 countries around the globe., companyurl: www.ubisoft.com, productdesc: Ubisoft is using Google App Engine to host TickTock and other future social games the company is developing., companylogo: ubisoft.png, companypod: App Engine, companytags: App Engine, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jtmqq2010-05-10T18:39:11.857ZRow: 169companyname: UnaWave, companylocation: Los Angeles, California, companydesc: UnaWave is a simple and elegant work management application that works on top of Google Wave and fully leverages its rich real time collaboration and communications framework. UnaWave enables managers, team leaders, and team members to easily assign, track and report on work status, reducing the complexity, cost and effort required to manage short term and long term work assignments., companyurl: www.unawave.com, productdesc: We're using Google Web Toolkit, OpenSocial Gadgets, and the Google Wave real-time communications and collaboration platform to enable customers to create, assign, share and report on tasks for team work-assignments of all types of durations., companylogo: unawave.png, companypod: Wave, companytags: GWT, Wave, Social Web, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jv1b72010-05-10T18:39:11.857ZRow: 170companyname: Unity, companylocation: San Francisco, California, companydesc: Unity Technologies is an emerging market leader in mobile and online 3D technology. It's flagship product Unity is revolutionizing the game industry and beyond, delivering great power, workflows and platform reach.
+, companyurl: www.unity3d.com, productdesc: Unity Android enables developers to create multiplatform 3D games and experiences, while also allowing them to harness the specific strengths of the individual hardware and OS platform., companylogo: unity.png, companypod: Android, companytags: Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jxug12010-05-10T18:39:11.857ZRow: 171companyname: Ushahidi, companylocation: Orlando, Florida, companydesc: The Ushahidi Platform allows anyone to gather distributed data via SMS, email or web and visualize it on a map or timeline. Our goal is to create the simplest way of aggregating information from the public for use in crisis response., companyurl: www.ushahidi.com, productdesc: We've built an Android-based mobile app that syncs up with our web service; we use Google Maps to display geo-tagged locations and media in our open source platform; and we use Google Geocoding web service to geo-tag text messages in our open source platform., companylogo: ushahidi.png, companypod: Geo, companytags: Geo, Android, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jo0h22010-05-10T18:39:11.857ZRow: 172companyname: VendAsta, companylocation: Saskatoon, Saskatchewan, Canada, companydesc: VendAsta is sowing trust by making social context portable. With our applications StepRep (for small businesses) and MashedIn (for bloggers and website owners), the networks you've built up on Facebook, Twitter, etc. can extend to you and your social connections all over the web., companyurl: www.vendasta.com, productdesc: We use App Engine to host our core products. We leverage Open Social, Maps, and Social Graph API in our products. In addition, VendAsta uses Google Apps and Analytics extensively for running its core business. VendAsta is the developer of the open source library asynctools, which allows developers to perform App Engine API calls in parallel, most notably datastore queries., companylogo: vendasta.png, companypod: App Engine, companytags: App Engine, Social Web, Google APIs, Geo, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jpf1j2010-05-10T18:39:11.857ZRow: 173companyname: Voxeo, companylocation: Orlando, Florida, companydesc: Voxeo unlocks the value of communications with open standards, disruptive innovation and a passion for problem solving. More than 100,000 developers, 45,000 companies and half of the Fortune 100 use Voxeo to deliver speech-enabled inbound and outbound IVR, call control, conferencing, and SIP-based VoIP applications, including multi-channel applications that span voice, SMS, IM, the mobile web and social networking., companyurl: www.voxeo.com/GoogleIO, productdesc: Voxeo's collaboration and communication platforms can be tightly integrated with Google technologies. Build and deploy rich multi-modal communications applications on Google App Engine. Organize a conference call within Google Wave, then call in with your phone or participate directly from your browser. Embed collaboration and web meeting spaces into any Google Friend Connect site. Or use Google Talk to interact with your users through any application., companylogo: voxeo.png, companypod: Social Web, companytags: Social Web, App Engine, Wave, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/jqtlw2010-05-10T18:39:11.857ZRow: 174companyname: Walk Score, companylocation: Seattle, Washington, companydesc: Walk Score measures the walkability of any address and through integrations with real estate sites helps millions of homebuyers find a walkable place to live. We believe walkable neighborhoods are one of the simplest and most effective solutions to halt climate change, improve our health, and strengthen our communities., companyurl: www.walkscore.com, productdesc: The Walk Score API on Google App Engine serves 3 million Walk Scores a day to thousands of real estate partner sites. We also use Google Maps, Google AJAX APIs for geocoding and local search, Django on App Engine to host our site City-Go-Round.com, and we are developing a Transit API and Transit Score on App Engine using public GTFS data from transit agencies around the world., companylogo: walkscore.png, companypod: App Engine, companytags: App Engine, Geo, Google APIs, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/js86d2010-05-10T18:39:11.857ZRow: 175companyname: wave.to, companylocation: Bangor, North Wales, United Kingdom, companydesc: wave.to integrates existing products and services into the Google Wave platform. We're a small dedicated development team who have been involved in extending the platform since its announcement and have integrated Google Wave into social networking, instant messaging, CRM platforms, mobile devices and e-mail. Our e-mail platform Mr-Ray won the Judges Choice award in Mashable's Google Wave API Challenge., companyurl: www.wave.to, productdesc: We use a mixture of Google App Engine and the V8 javascript engine (node.js) to host our services that communicate with the Google Wave servers using the robots and gadget APIs. We are developing clients for our services using the Android SDK to extend them beyond desktop browsers., companylogo: waveto.png, companypod: Wave, companytags: Wave, App Engine, Android, adddate: 4/25/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/f52je2010-05-10T18:39:11.857ZRow: 176companyname: WebFilings, companylocation: Los Altos, California, companydesc: WebFilings delivers a revolution in collaboration software for regulatory compliance — providing the only end-to-end solution to meet SEC reporting requirements. Our intuitive user interface integrates word processing and spreadsheet editing capabilities into a single, rich internet application - streamlining the entire reporting process with features and document controls fine-tuned to the needs of SEC reporting professionals. WebFilings helps companies reduce the time, risk and costs associated with creating and filing SEC reports., companyurl: www.webfilings.com, productdesc: WebFilings uses web services built on the App Engine python SDK. Our use of App Engine and its datastore provides our customers a secure, reliable and scalable platform for their mission-critical SEC report management process., companylogo: webfilings.png, companypod: App Engine, companytags: App Engine, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/f7vo82010-05-10T18:39:11.857ZRow: 177companyname: WHERE, companylocation: Boston, Massachusetts, companydesc: WHERE is a leading local search and recommendation service. WHERE puts the best local information at users’ finger tips with real-time reviews and special offers from local merchants. WHERE is available on over one hundred mobile devices and a leading application on all major smartphone platforms and carriers., companyurl: www.where.com, productdesc: We're using the Android SDK to build the WHERE mobile application for Android. , companylogo: where.png, companypod: Android, companytags: Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/f9a8p2010-05-10T18:39:11.857ZRow: 178companyname: WithWaves, companylocation: Seattle, Washington, companydesc: WithWaves is the exploration of Google Wave utilizing technology developed under the Streamd.in umbrella. WithWaves is a small team of developers focusing on providing simple collaborative applications and tools. We have developed http://streamd.in, amazon.withwaves.com, ebay.withwaves.com and run the withwaves.com blog, all of them with support to run standalone and as gadgets in the Google Wave ecosystem., companyurl: www.withwaves.com, productdesc: We use Google Wave to deploy our robot and gadgets and App Engine to host most of them. We also use the Google Maps API V.3 in our latest project http://streamd.in., companylogo: withwaves.png, companypod: Wave, companytags: Wave, App Engine, Geo, adddate: 4/22/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/ezg9q2010-05-10T18:39:11.857ZRow: 179companyname: WorkSmart Labs, companylocation: New York, New York, companydesc: The WorkSmart Labs mission is to use technology to make fitness more motivating and effective. We make CardioTrainer mobile wellness software on the Android, as well as provide many gym entertainment solutions., companyurl: www.worksmartlabs.com, productdesc: We are using the Android SDK and a large set of other Google technologies (Protocol Buffers, Build Tools, KML, etc) to create our mobile applications and website., companylogo: worksmartlabs.png, companypod: Android, companytags: Android, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/f0uu72010-05-10T18:39:11.857ZRow: 180companyname: WOT, companylocation: Helsinki, Finland, companydesc: Web of Trust is a community-based safe surfing tool that uses an intuitive traffic-light style rating system to help you avoid risky sites when you search, browse and shop online., companyurl: www.mywot.com, productdesc: We develop extensions for Google Chrome., companylogo: wot.png, companypod: Chrome, companytags: Chrome, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/f29ek2010-05-10T18:39:11.857ZRow: 181companyname: Yahoo! Inc., companylocation: Sunnyvale, California, companydesc: Yahoo! Inc. offers a robust set of developer tools, APIs, and resources through the Yahoo! Developer Network., companyurl: www.yahoo.com, productdesc: The Yahoo! Application Platform (YAP) provides an OpenSocial container on the Yahoo! homepage and is the largest user of the Google Caja project. Yahoo! uses iGoogle as a canvas to publish Yahoo! user updates through its Updates API and integrates numerous open Google APIs and feeds within the Yahoo! Query Language (YQL). Yahoo! is a charter member of the OpenSocial Foundation, on the Board of OpenID, and a key contributor to the OAuth spec., companylogo: yahoo.png, companypod: Social Web, companytags: Social Web, adddate: xhttp://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/f3nz12010-05-10T18:39:11.857ZRow: 182companyname: Yelp, companylocation: San Francisco, California, companydesc: Yelp is a website that connects people with great local businesses in the US, Canada, UK and Ireland. Yelpers have written more than 10 million local reviews, making Yelp a local guide for real word-of-mouth on everything from boutiques and mechanics to restaurants and dentists.
+, companyurl: www.yelp.com, productdesc: Yelp for Android helps you find great businesses around you. Using location services, Google Maps, shared Intents and a host of other features in the Android SDK, the app delivers a simple-yet-powerful Yelp experience on Android devices.
+, companylogo: yelp.png, companypod: Android, companytags: Android, Geo, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/fgb2q2010-05-10T18:39:11.857ZRow: 183companyname: Zillow, companylocation: Seattle, Washington, companydesc: Zillow.com is an online real estate marketplace where homeowners, buyers, sellers, renters, real estate agents and mortgage professionals find and share vital information about homes and mortgages.
+, companyurl: www.zillow.com, productdesc: Zillow.com uses Google Maps in our mobile applications, including our Android application. Zillow.com uses Google Street View on our main website as well as in our Android application, companylogo: zillow.png, companypod: Android, companytags: Android, Geo, adddate: 5/3/2010http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od5/public/basic/fhpn72010-05-10T18:39:11.857ZRow: 184companyname: Zoho, companylocation: Pleasanton, California, companydesc: Zoho offers a broad suite of Online Business, Productivity & Collaboration Applications for Small and Medium Organizations. , companyurl: www.zoho.com, productdesc: We use a number of Google products, including Google Web Toolkit. We also build extensions for Chrome, solutions for Google Apps, OpenSocial gadgets, and Google OpenID/OAuth., companylogo: zoho.png, companypod: Enterprise, companytags: Enterprise, GWT, Social Web, Chrome, adddate: x
\ No newline at end of file
diff --git a/build.properties b/build.properties
new file mode 100644
index 0000000..873bd38
--- /dev/null
+++ b/build.properties
@@ -0,0 +1,21 @@
+# This file is used to override default values used by the Ant build system.
+#
+# This file must be checked in Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# The name of your application package as defined in the manifest.
+# Used by the 'uninstall' rule.
+application-package=com.google.android.apps.iosched
+
+# You can use this to override default values such as
+# 'source.dir' for the location of your java source folder and
+# 'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+# 'key.store' for the location of your keystore and
+# 'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..ee2bd56
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/default.properties b/default.properties
new file mode 100644
index 0000000..d84434c
--- /dev/null
+++ b/default.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Indicates whether an apk should be generated for each density.
+split.density=false
+
+# Project target.
+target=android-5
diff --git a/local.properties b/local.properties
new file mode 100644
index 0000000..8c4030b
--- /dev/null
+++ b/local.properties
@@ -0,0 +1,2 @@
+sdk.dir=/home/jsharkey/android-sdk-linux
+
diff --git a/res/.svn/all-wcprops b/res/.svn/all-wcprops
new file mode 100644
index 0000000..ee8ef07
--- /dev/null
+++ b/res/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 25
+/svn/!svn/ver/2/trunk/res
+END
diff --git a/res/.svn/entries b/res/.svn/entries
new file mode 100644
index 0000000..8af31c0
--- /dev/null
+++ b/res/.svn/entries
@@ -0,0 +1,52 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/res
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+values
+dir
+
+xml
+dir
+
+drawable
+dir
+
+drawable-hdpi
+dir
+
+anim
+dir
+
+drawable-mdpi
+dir
+
+layout-land
+dir
+
+layout
+dir
+
diff --git a/res/anim/.svn/all-wcprops b/res/anim/.svn/all-wcprops
new file mode 100644
index 0000000..768dc76
--- /dev/null
+++ b/res/anim/.svn/all-wcprops
@@ -0,0 +1,29 @@
+K 25
+svn:wc:ra_dav:version-url
+V 30
+/svn/!svn/ver/2/trunk/res/anim
+END
+slide_left_in.xml
+K 25
+svn:wc:ra_dav:version-url
+V 48
+/svn/!svn/ver/2/trunk/res/anim/slide_left_in.xml
+END
+slide_left_out.xml
+K 25
+svn:wc:ra_dav:version-url
+V 49
+/svn/!svn/ver/2/trunk/res/anim/slide_left_out.xml
+END
+slide_right_in.xml
+K 25
+svn:wc:ra_dav:version-url
+V 49
+/svn/!svn/ver/2/trunk/res/anim/slide_right_in.xml
+END
+slide_right_out.xml
+K 25
+svn:wc:ra_dav:version-url
+V 50
+/svn/!svn/ver/2/trunk/res/anim/slide_right_out.xml
+END
diff --git a/res/anim/.svn/entries b/res/anim/.svn/entries
new file mode 100644
index 0000000..4db0c93
--- /dev/null
+++ b/res/anim/.svn/entries
@@ -0,0 +1,164 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/res/anim
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+slide_left_in.xml
+file
+
+
+
+
+2010-06-03T15:23:16.865630Z
+a2d34248d00f19b755b414e9ad2acbdb
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+907
+
+slide_left_out.xml
+file
+
+
+
+
+2010-06-03T15:23:16.865630Z
+db3cef4e676de8e729f37c5d44f85d59
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+906
+
+slide_right_in.xml
+file
+
+
+
+
+2010-06-03T15:23:16.865630Z
+9149bb47db1db524a6b5978680c2a75b
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+906
+
+slide_right_out.xml
+file
+
+
+
+
+2010-06-03T15:23:16.865630Z
+0b9920902c7a4f5621736c4bd6ea253c
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+907
+
diff --git a/res/anim/.svn/text-base/slide_left_in.xml.svn-base b/res/anim/.svn/text-base/slide_left_in.xml.svn-base
new file mode 100644
index 0000000..74c3166
--- /dev/null
+++ b/res/anim/.svn/text-base/slide_left_in.xml.svn-base
@@ -0,0 +1,21 @@
+
+
+
diff --git a/res/anim/.svn/text-base/slide_left_out.xml.svn-base b/res/anim/.svn/text-base/slide_left_out.xml.svn-base
new file mode 100644
index 0000000..0e67939
--- /dev/null
+++ b/res/anim/.svn/text-base/slide_left_out.xml.svn-base
@@ -0,0 +1,21 @@
+
+
+
diff --git a/res/anim/.svn/text-base/slide_right_in.xml.svn-base b/res/anim/.svn/text-base/slide_right_in.xml.svn-base
new file mode 100644
index 0000000..7020b29
--- /dev/null
+++ b/res/anim/.svn/text-base/slide_right_in.xml.svn-base
@@ -0,0 +1,21 @@
+
+
+
diff --git a/res/anim/.svn/text-base/slide_right_out.xml.svn-base b/res/anim/.svn/text-base/slide_right_out.xml.svn-base
new file mode 100644
index 0000000..cfbf956
--- /dev/null
+++ b/res/anim/.svn/text-base/slide_right_out.xml.svn-base
@@ -0,0 +1,21 @@
+
+
+
diff --git a/res/anim/slide_left_in.xml b/res/anim/slide_left_in.xml
new file mode 100644
index 0000000..74c3166
--- /dev/null
+++ b/res/anim/slide_left_in.xml
@@ -0,0 +1,21 @@
+
+
+
diff --git a/res/anim/slide_left_out.xml b/res/anim/slide_left_out.xml
new file mode 100644
index 0000000..0e67939
--- /dev/null
+++ b/res/anim/slide_left_out.xml
@@ -0,0 +1,21 @@
+
+
+
diff --git a/res/anim/slide_right_in.xml b/res/anim/slide_right_in.xml
new file mode 100644
index 0000000..7020b29
--- /dev/null
+++ b/res/anim/slide_right_in.xml
@@ -0,0 +1,21 @@
+
+
+
diff --git a/res/anim/slide_right_out.xml b/res/anim/slide_right_out.xml
new file mode 100644
index 0000000..cfbf956
--- /dev/null
+++ b/res/anim/slide_right_out.xml
@@ -0,0 +1,21 @@
+
+
+
diff --git a/res/drawable-hdpi/.svn/all-wcprops b/res/drawable-hdpi/.svn/all-wcprops
new file mode 100644
index 0000000..0dc7f98
--- /dev/null
+++ b/res/drawable-hdpi/.svn/all-wcprops
@@ -0,0 +1,347 @@
+K 25
+svn:wc:ra_dav:version-url
+V 39
+/svn/!svn/ver/2/trunk/res/drawable-hdpi
+END
+home_btn_schedule_pressed.png
+K 25
+svn:wc:ra_dav:version-url
+V 69
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_schedule_pressed.png
+END
+home_btn_map_default.png
+K 25
+svn:wc:ra_dav:version-url
+V 64
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_map_default.png
+END
+minitab_default.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 61
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/minitab_default.9.png
+END
+home_btn_office_hours_selected.png
+K 25
+svn:wc:ra_dav:version-url
+V 74
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_office_hours_selected.png
+END
+colorstrip.png
+K 25
+svn:wc:ra_dav:version-url
+V 54
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/colorstrip.png
+END
+home_btn_map_selected.png
+K 25
+svn:wc:ra_dav:version-url
+V 65
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_map_selected.png
+END
+ic_title_export_default.png
+K 25
+svn:wc:ra_dav:version-url
+V 67
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_title_export_default.png
+END
+home_btn_starred_pressed.png
+K 25
+svn:wc:ra_dav:version-url
+V 68
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_starred_pressed.png
+END
+btn_now_playing_default.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 69
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/btn_now_playing_default.9.png
+END
+title_logo_default.png
+K 25
+svn:wc:ra_dav:version-url
+V 62
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/title_logo_default.png
+END
+ic_title_home_default.png
+K 25
+svn:wc:ra_dav:version-url
+V 65
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_title_home_default.png
+END
+btn_bg_selected.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 61
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/btn_bg_selected.9.png
+END
+ic_title_refresh_alt.png
+K 25
+svn:wc:ra_dav:version-url
+V 64
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_title_refresh_alt.png
+END
+home_btn_office_hours_pressed.png
+K 25
+svn:wc:ra_dav:version-url
+V 73
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_office_hours_pressed.png
+END
+home_btn_sandbox_default.png
+K 25
+svn:wc:ra_dav:version-url
+V 68
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_sandbox_default.png
+END
+home_btn_map_pressed.png
+K 25
+svn:wc:ra_dav:version-url
+V 64
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_map_pressed.png
+END
+minitab_pressed.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 61
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/minitab_pressed.9.png
+END
+home_btn_schedule_selected.png
+K 25
+svn:wc:ra_dav:version-url
+V 70
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_schedule_selected.png
+END
+ic_now_playing_logo_alt.png
+K 25
+svn:wc:ra_dav:version-url
+V 67
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_now_playing_logo_alt.png
+END
+ic_title_share_default.png
+K 25
+svn:wc:ra_dav:version-url
+V 66
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_title_share_default.png
+END
+ic_now_playing_more_alt.png
+K 25
+svn:wc:ra_dav:version-url
+V 67
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_now_playing_more_alt.png
+END
+home_btn_sessions_default.png
+K 25
+svn:wc:ra_dav:version-url
+V 69
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_sessions_default.png
+END
+ic_title_search_default.png
+K 25
+svn:wc:ra_dav:version-url
+V 67
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_title_search_default.png
+END
+btn_now_playing_pressed.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 69
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/btn_now_playing_pressed.9.png
+END
+title_logo_pressed.png
+K 25
+svn:wc:ra_dav:version-url
+V 62
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/title_logo_pressed.png
+END
+home_btn_starred_selected.png
+K 25
+svn:wc:ra_dav:version-url
+V 69
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_starred_selected.png
+END
+ic_track_color_target.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 67
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_track_color_target.9.png
+END
+ic_title_map_default.png
+K 25
+svn:wc:ra_dav:version-url
+V 64
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_title_map_default.png
+END
+btn_block_star.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 60
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/btn_block_star.9.png
+END
+ic_title_export_alt.png
+K 25
+svn:wc:ra_dav:version-url
+V 63
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_title_export_alt.png
+END
+now_bar.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 53
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/now_bar.9.png
+END
+home_btn_sandbox_pressed.png
+K 25
+svn:wc:ra_dav:version-url
+V 68
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_sandbox_pressed.png
+END
+now_playing_icon.png
+K 25
+svn:wc:ra_dav:version-url
+V 60
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/now_playing_icon.png
+END
+minitab_selected.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 62
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/minitab_selected.9.png
+END
+ic_title_home_alt.png
+K 25
+svn:wc:ra_dav:version-url
+V 61
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_title_home_alt.png
+END
+minitab_unselected.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 64
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/minitab_unselected.9.png
+END
+btn_block_border_default.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 70
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/btn_block_border_default.9.png
+END
+home_btn_sessions_pressed.png
+K 25
+svn:wc:ra_dav:version-url
+V 69
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_sessions_pressed.png
+END
+btn_block_color_target.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 68
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/btn_block_color_target.9.png
+END
+btn_now_playing_selected.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 70
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/btn_now_playing_selected.9.png
+END
+title_logo_selected.png
+K 25
+svn:wc:ra_dav:version-url
+V 63
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/title_logo_selected.png
+END
+btn_block_border_selected.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 71
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/btn_block_border_selected.9.png
+END
+home_btn_schedule_default.png
+K 25
+svn:wc:ra_dav:version-url
+V 69
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_schedule_default.png
+END
+ic_launcher.png
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_launcher.png
+END
+ic_title_share_alt.png
+K 25
+svn:wc:ra_dav:version-url
+V 62
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_title_share_alt.png
+END
+ic_title_refresh_default.png
+K 25
+svn:wc:ra_dav:version-url
+V 68
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_title_refresh_default.png
+END
+home_btn_sandbox_selected.png
+K 25
+svn:wc:ra_dav:version-url
+V 69
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_sandbox_selected.png
+END
+ic_title_search_alt.png
+K 25
+svn:wc:ra_dav:version-url
+V 63
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_title_search_alt.png
+END
+home_btn_starred_default.png
+K 25
+svn:wc:ra_dav:version-url
+V 68
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_starred_default.png
+END
+ic_track_border.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 61
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_track_border.9.png
+END
+ic_now_playing_logo_default.png
+K 25
+svn:wc:ra_dav:version-url
+V 71
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_now_playing_logo_default.png
+END
+btn_block_border_pressed.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 70
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/btn_block_border_pressed.9.png
+END
+ic_now_playing_more_default.png
+K 25
+svn:wc:ra_dav:version-url
+V 71
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_now_playing_more_default.png
+END
+btn_bg_pressed.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 60
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/btn_bg_pressed.9.png
+END
+ic_title_map_alt.png
+K 25
+svn:wc:ra_dav:version-url
+V 60
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/ic_title_map_alt.png
+END
+home_btn_sessions_selected.png
+K 25
+svn:wc:ra_dav:version-url
+V 70
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_sessions_selected.png
+END
+home_btn_office_hours_default.png
+K 25
+svn:wc:ra_dav:version-url
+V 73
+/svn/!svn/ver/2/trunk/res/drawable-hdpi/home_btn_office_hours_default.png
+END
diff --git a/res/drawable-hdpi/.svn/entries b/res/drawable-hdpi/.svn/entries
new file mode 100644
index 0000000..dd9faf9
--- /dev/null
+++ b/res/drawable-hdpi/.svn/entries
@@ -0,0 +1,1966 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/res/drawable-hdpi
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+home_btn_schedule_pressed.png
+file
+
+
+
+
+2010-06-03T15:23:16.789639Z
+c0bedccf99592366b5927d843f7f0721
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+14235
+
+home_btn_map_default.png
+file
+
+
+
+
+2010-06-03T15:23:16.793622Z
+00ece8738a710909335d891bd222ac70
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8271
+
+minitab_default.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.793622Z
+78cf05402d0a0d034ee6157021224b21
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+205
+
+home_btn_office_hours_selected.png
+file
+
+
+
+
+2010-06-03T15:23:16.793622Z
+572536c5736d807f85fd4f7a3b35c630
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+12787
+
+colorstrip.png
+file
+
+
+
+
+2010-06-03T15:23:16.793622Z
+021b8ba5502b8974dd6494475bff1dba
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+235
+
+home_btn_map_selected.png
+file
+
+
+
+
+2010-06-03T15:23:16.793622Z
+d73d6c8c499badacb887158728d73fcc
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+11354
+
+ic_title_export_default.png
+file
+
+
+
+
+2010-06-03T15:23:16.793622Z
+c770f04f8e06f705a4e87da627ff5de6
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+550
+
+home_btn_starred_pressed.png
+file
+
+
+
+
+2010-06-03T15:23:16.793622Z
+845ef6955b270ede2ef37272bc994b2c
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10416
+
+btn_now_playing_default.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.793622Z
+ed6479736a2787697e17b2d2d05f43a1
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+224
+
+title_logo_default.png
+file
+
+
+
+
+2010-06-03T15:23:16.793622Z
+a83d9cb90e0d754e81cb1e65021e8dea
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6446
+
+ic_title_home_default.png
+file
+
+
+
+
+2010-06-03T15:23:16.793622Z
+def864df44cfede093e7695f5c92d49d
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+425
+
+btn_bg_selected.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.797622Z
+7c971f251463d2450570054e8741443b
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+338
+
+ic_title_refresh_alt.png
+file
+
+
+
+
+2010-06-03T15:23:16.797622Z
+424cb3c450b5eee57168b880ead1dca9
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+682
+
+home_btn_office_hours_pressed.png
+file
+
+
+
+
+2010-06-03T15:23:16.797622Z
+45b7c1048288804b02a3b95d6b2cf7bb
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+13017
+
+home_btn_map_pressed.png
+file
+
+
+
+
+2010-06-03T15:23:16.797622Z
+bca596f9c386bdb49a740186165a4773
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+11429
+
+home_btn_sandbox_default.png
+file
+
+
+
+
+2010-06-03T15:23:16.797622Z
+724a69f9a7f9a696f5d17365cb99b377
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+11150
+
+minitab_pressed.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.797622Z
+65d7b7a55e99ca952714258579494484
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+203
+
+home_btn_schedule_selected.png
+file
+
+
+
+
+2010-06-03T15:23:16.797622Z
+bbae79edfc42a65e164f913e5e6bd648
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+13945
+
+ic_now_playing_logo_alt.png
+file
+
+
+
+
+2010-06-03T15:23:16.797622Z
+09e8132e85203baf8443fad6e7bb18e8
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3959
+
+ic_title_share_default.png
+file
+
+
+
+
+2010-06-03T15:23:16.797622Z
+b7e9833f58caad2a33ed061285bee258
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+681
+
+ic_now_playing_more_alt.png
+file
+
+
+
+
+2010-06-03T15:23:16.797622Z
+64efce4bafb6a8c8f24f594509fbc31c
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+188
+
+home_btn_sessions_default.png
+file
+
+
+
+
+2010-06-03T15:23:16.801621Z
+2abffeb51fbfbde913ee04d0462c29e1
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8905
+
+ic_title_search_default.png
+file
+
+
+
+
+2010-06-03T15:23:16.801621Z
+46a8c6270209736b4831814adf46dff7
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+641
+
+btn_now_playing_pressed.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.797622Z
+a54f131c694f7e5ad0f9612d892d2ca9
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+238
+
+title_logo_pressed.png
+file
+
+
+
+
+2010-06-03T15:23:16.801621Z
+70736ae2114e6b8b6398f8b55b82fcfa
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+9937
+
+home_btn_starred_selected.png
+file
+
+
+
+
+2010-06-03T15:23:16.801621Z
+cba9c9539122a43b62f50dabefbdc1ae
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10747
+
+ic_track_color_target.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.801621Z
+86cb1c462bbb61d7020332985b7c6c3f
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+420
+
+ic_title_map_default.png
+file
+
+
+
+
+2010-06-03T15:23:16.801621Z
+64cbfd5505420101c62185447c376e75
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+591
+
+btn_block_star.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.801621Z
+4185c361a0c33981cf95b67d4dcbeceb
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1576
+
+ic_title_export_alt.png
+file
+
+
+
+
+2010-06-03T15:23:16.801621Z
+433a7c7b6dace91101a1ef75aa968dd4
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+518
+
+home_btn_sandbox_pressed.png
+file
+
+
+
+
+2010-06-03T15:23:16.801621Z
+177cc3806d49d811d9d6130b9bc58950
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+13952
+
+now_bar.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.805628Z
+3ec756272d1d512bfc3872a3b34fc1df
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+160
+
+now_playing_icon.png
+file
+
+
+
+
+2010-06-03T15:23:16.805628Z
+b0d81c589d8fff63871f21508187c141
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+482
+
+minitab_selected.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.805628Z
+d96b756abba93f121fd5b74e0e2be4da
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+203
+
+ic_title_home_alt.png
+file
+
+
+
+
+2010-06-03T15:23:16.805628Z
+8cc62251b74c0f86c9a4a7839213f7c6
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+362
+
+minitab_unselected.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.805628Z
+5964f780d2a3969bfd84efeb162ff5e1
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2844
+
+btn_block_border_default.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.805628Z
+d484ecb1acab4371dcc9d491376882f5
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+369
+
+home_btn_sessions_pressed.png
+file
+
+
+
+
+2010-06-03T15:23:16.805628Z
+b4d396478d843bc6dfb75cf5cfb6f98e
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+11592
+
+btn_block_color_target.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.809626Z
+ac0381e6fef3d05db35ad9bea7f55483
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+300
+
+btn_now_playing_selected.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.809626Z
+7967ffbba984645f537e334061e9a5fb
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+238
+
+title_logo_selected.png
+file
+
+
+
+
+2010-06-03T15:23:16.809626Z
+e3d1071176d19b89d62d40a0207c80a6
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+9106
+
+btn_block_border_selected.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.809626Z
+505e6628687ac795ecde957701746710
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+402
+
+home_btn_schedule_default.png
+file
+
+
+
+
+2010-06-03T15:23:16.809626Z
+93cd44548e4a51acd9ca7dadd2a0092f
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+11554
+
+ic_launcher.png
+file
+
+
+
+
+2010-06-03T15:23:16.809626Z
+cd6861be59f28596e7cbd82448d3d4bb
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5534
+
+ic_title_share_alt.png
+file
+
+
+
+
+2010-06-03T15:23:16.809626Z
+1583bbe365097b0961eb7b6017011bdc
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+660
+
+ic_title_refresh_default.png
+file
+
+
+
+
+2010-06-03T15:23:16.809626Z
+c53afefd8d9bf86033488dc47e1c660d
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+683
+
+home_btn_sandbox_selected.png
+file
+
+
+
+
+2010-06-03T15:23:16.809626Z
+70ac603bc087ab525c6a90ccc0e4d1d2
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+13479
+
+ic_title_search_alt.png
+file
+
+
+
+
+2010-06-03T15:23:16.809626Z
+70347c074fd20ca51e165b7350bf0543
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+630
+
+home_btn_starred_default.png
+file
+
+
+
+
+2010-06-03T15:23:16.809626Z
+ce53e68f6dc5744eba4c3344c5c5a8c3
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7539
+
+ic_track_border.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.813648Z
+868095b52edb3b0a7b478d18f9e6304b
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+811
+
+ic_now_playing_logo_default.png
+file
+
+
+
+
+2010-06-03T15:23:16.813648Z
+db054dfa00ba0e84ed9a4c023aa0258e
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3960
+
+btn_block_border_pressed.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.813648Z
+e5d300422b91543c9ea22bc1f4d5f351
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+406
+
+ic_now_playing_more_default.png
+file
+
+
+
+
+2010-06-03T15:23:16.813648Z
+f52637f19cdafd519d7fd7ce0e8d3e57
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+196
+
+btn_bg_pressed.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.813648Z
+1a0e2ed29e9f1271c2fa9842cbb44853
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3037
+
+ic_title_map_alt.png
+file
+
+
+
+
+2010-06-03T15:23:16.813648Z
+7e68f83f30abcf7015e1039b648c1601
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+577
+
+home_btn_sessions_selected.png
+file
+
+
+
+
+2010-06-03T15:23:16.813648Z
+d1ecf1046a98c547cc0862b587d68dd9
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+11520
+
+home_btn_office_hours_default.png
+file
+
+
+
+
+2010-06-03T15:23:16.813648Z
+b657a1596b449cd91d36ec99fd08260f
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10122
+
diff --git a/res/drawable-hdpi/.svn/prop-base/btn_bg_pressed.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/btn_bg_pressed.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/btn_bg_pressed.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/btn_bg_selected.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/btn_bg_selected.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/btn_bg_selected.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/btn_block_border_default.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/btn_block_border_default.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/btn_block_border_default.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/btn_block_border_pressed.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/btn_block_border_pressed.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/btn_block_border_pressed.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/btn_block_border_selected.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/btn_block_border_selected.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/btn_block_border_selected.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/btn_block_color_target.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/btn_block_color_target.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/btn_block_color_target.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/btn_block_star.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/btn_block_star.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/btn_block_star.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/btn_now_playing_default.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/btn_now_playing_default.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/btn_now_playing_default.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/btn_now_playing_pressed.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/btn_now_playing_pressed.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/btn_now_playing_pressed.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/btn_now_playing_selected.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/btn_now_playing_selected.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/btn_now_playing_selected.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/colorstrip.png.svn-base b/res/drawable-hdpi/.svn/prop-base/colorstrip.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/colorstrip.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_map_default.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_map_default.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_map_default.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_map_pressed.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_map_pressed.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_map_pressed.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_map_selected.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_map_selected.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_map_selected.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_office_hours_default.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_office_hours_default.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_office_hours_default.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_office_hours_pressed.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_office_hours_pressed.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_office_hours_pressed.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_office_hours_selected.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_office_hours_selected.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_office_hours_selected.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_sandbox_default.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_sandbox_default.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_sandbox_default.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_sandbox_pressed.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_sandbox_pressed.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_sandbox_pressed.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_sandbox_selected.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_sandbox_selected.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_sandbox_selected.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_schedule_default.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_schedule_default.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_schedule_default.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_schedule_pressed.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_schedule_pressed.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_schedule_pressed.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_schedule_selected.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_schedule_selected.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_schedule_selected.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_sessions_default.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_sessions_default.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_sessions_default.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_sessions_pressed.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_sessions_pressed.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_sessions_pressed.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_sessions_selected.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_sessions_selected.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_sessions_selected.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_starred_default.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_starred_default.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_starred_default.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_starred_pressed.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_starred_pressed.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_starred_pressed.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/home_btn_starred_selected.png.svn-base b/res/drawable-hdpi/.svn/prop-base/home_btn_starred_selected.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/home_btn_starred_selected.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_launcher.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_launcher.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_launcher.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_now_playing_logo_alt.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_now_playing_logo_alt.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_now_playing_logo_alt.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_now_playing_logo_default.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_now_playing_logo_default.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_now_playing_logo_default.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_now_playing_more_alt.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_now_playing_more_alt.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_now_playing_more_alt.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_now_playing_more_default.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_now_playing_more_default.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_now_playing_more_default.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_title_export_alt.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_title_export_alt.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_title_export_alt.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_title_export_default.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_title_export_default.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_title_export_default.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_title_home_alt.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_title_home_alt.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_title_home_alt.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_title_home_default.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_title_home_default.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_title_home_default.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_title_map_alt.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_title_map_alt.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_title_map_alt.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_title_map_default.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_title_map_default.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_title_map_default.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_title_refresh_alt.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_title_refresh_alt.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_title_refresh_alt.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_title_refresh_default.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_title_refresh_default.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_title_refresh_default.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_title_search_alt.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_title_search_alt.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_title_search_alt.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_title_search_default.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_title_search_default.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_title_search_default.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_title_share_alt.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_title_share_alt.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_title_share_alt.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_title_share_default.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_title_share_default.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_title_share_default.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_track_border.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_track_border.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_track_border.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/ic_track_color_target.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/ic_track_color_target.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/ic_track_color_target.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/minitab_default.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/minitab_default.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/minitab_default.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/minitab_pressed.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/minitab_pressed.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/minitab_pressed.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/minitab_selected.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/minitab_selected.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/minitab_selected.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/minitab_unselected.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/minitab_unselected.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/minitab_unselected.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/now_bar.9.png.svn-base b/res/drawable-hdpi/.svn/prop-base/now_bar.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/now_bar.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/now_playing_icon.png.svn-base b/res/drawable-hdpi/.svn/prop-base/now_playing_icon.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/now_playing_icon.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/title_logo_default.png.svn-base b/res/drawable-hdpi/.svn/prop-base/title_logo_default.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/title_logo_default.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/title_logo_pressed.png.svn-base b/res/drawable-hdpi/.svn/prop-base/title_logo_pressed.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/title_logo_pressed.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/prop-base/title_logo_selected.png.svn-base b/res/drawable-hdpi/.svn/prop-base/title_logo_selected.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-hdpi/.svn/prop-base/title_logo_selected.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-hdpi/.svn/text-base/btn_bg_pressed.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/btn_bg_pressed.9.png.svn-base
new file mode 100644
index 0000000..b1afd4b
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/btn_bg_pressed.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/btn_bg_selected.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/btn_bg_selected.9.png.svn-base
new file mode 100644
index 0000000..331f96f
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/btn_bg_selected.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/btn_block_border_default.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/btn_block_border_default.9.png.svn-base
new file mode 100644
index 0000000..997baff
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/btn_block_border_default.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/btn_block_border_pressed.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/btn_block_border_pressed.9.png.svn-base
new file mode 100644
index 0000000..7a48abc
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/btn_block_border_pressed.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/btn_block_border_selected.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/btn_block_border_selected.9.png.svn-base
new file mode 100644
index 0000000..4f54149
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/btn_block_border_selected.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/btn_block_color_target.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/btn_block_color_target.9.png.svn-base
new file mode 100644
index 0000000..05a9ef2
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/btn_block_color_target.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/btn_block_star.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/btn_block_star.9.png.svn-base
new file mode 100644
index 0000000..7a09b11
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/btn_block_star.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/btn_now_playing_default.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/btn_now_playing_default.9.png.svn-base
new file mode 100644
index 0000000..f6541cd
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/btn_now_playing_default.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/btn_now_playing_pressed.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/btn_now_playing_pressed.9.png.svn-base
new file mode 100644
index 0000000..fc64d9e
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/btn_now_playing_pressed.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/btn_now_playing_selected.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/btn_now_playing_selected.9.png.svn-base
new file mode 100644
index 0000000..24cb45b
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/btn_now_playing_selected.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/colorstrip.png.svn-base b/res/drawable-hdpi/.svn/text-base/colorstrip.png.svn-base
new file mode 100644
index 0000000..f899bd2
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/colorstrip.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_map_default.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_map_default.png.svn-base
new file mode 100644
index 0000000..f897c12
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_map_default.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_map_pressed.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_map_pressed.png.svn-base
new file mode 100644
index 0000000..a6ea899
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_map_pressed.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_map_selected.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_map_selected.png.svn-base
new file mode 100644
index 0000000..f774655
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_map_selected.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_office_hours_default.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_office_hours_default.png.svn-base
new file mode 100644
index 0000000..40151ff
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_office_hours_default.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_office_hours_pressed.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_office_hours_pressed.png.svn-base
new file mode 100644
index 0000000..1dc9169
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_office_hours_pressed.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_office_hours_selected.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_office_hours_selected.png.svn-base
new file mode 100644
index 0000000..6dcdae4
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_office_hours_selected.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_sandbox_default.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_sandbox_default.png.svn-base
new file mode 100644
index 0000000..3d9916f
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_sandbox_default.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_sandbox_pressed.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_sandbox_pressed.png.svn-base
new file mode 100644
index 0000000..c32a390
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_sandbox_pressed.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_sandbox_selected.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_sandbox_selected.png.svn-base
new file mode 100644
index 0000000..2368b8e
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_sandbox_selected.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_schedule_default.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_schedule_default.png.svn-base
new file mode 100644
index 0000000..95041a1
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_schedule_default.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_schedule_pressed.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_schedule_pressed.png.svn-base
new file mode 100644
index 0000000..17b55f4
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_schedule_pressed.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_schedule_selected.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_schedule_selected.png.svn-base
new file mode 100644
index 0000000..a3b558d
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_schedule_selected.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_sessions_default.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_sessions_default.png.svn-base
new file mode 100644
index 0000000..4a0f509
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_sessions_default.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_sessions_pressed.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_sessions_pressed.png.svn-base
new file mode 100644
index 0000000..674dbad
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_sessions_pressed.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_sessions_selected.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_sessions_selected.png.svn-base
new file mode 100644
index 0000000..d362107
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_sessions_selected.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_starred_default.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_starred_default.png.svn-base
new file mode 100644
index 0000000..51e4413
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_starred_default.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_starred_pressed.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_starred_pressed.png.svn-base
new file mode 100644
index 0000000..9bc9e75
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_starred_pressed.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/home_btn_starred_selected.png.svn-base b/res/drawable-hdpi/.svn/text-base/home_btn_starred_selected.png.svn-base
new file mode 100644
index 0000000..b3f70ed
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/home_btn_starred_selected.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_launcher.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_launcher.png.svn-base
new file mode 100644
index 0000000..ab621fc
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_launcher.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_now_playing_logo_alt.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_now_playing_logo_alt.png.svn-base
new file mode 100644
index 0000000..72fbc47
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_now_playing_logo_alt.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_now_playing_logo_default.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_now_playing_logo_default.png.svn-base
new file mode 100644
index 0000000..c8d2d8d
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_now_playing_logo_default.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_now_playing_more_alt.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_now_playing_more_alt.png.svn-base
new file mode 100644
index 0000000..ba27b5c
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_now_playing_more_alt.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_now_playing_more_default.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_now_playing_more_default.png.svn-base
new file mode 100644
index 0000000..b098015
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_now_playing_more_default.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_title_export_alt.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_title_export_alt.png.svn-base
new file mode 100644
index 0000000..0de5f13
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_title_export_alt.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_title_export_default.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_title_export_default.png.svn-base
new file mode 100644
index 0000000..b86014d
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_title_export_default.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_title_home_alt.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_title_home_alt.png.svn-base
new file mode 100644
index 0000000..7f2e9aa
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_title_home_alt.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_title_home_default.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_title_home_default.png.svn-base
new file mode 100644
index 0000000..af72258
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_title_home_default.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_title_map_alt.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_title_map_alt.png.svn-base
new file mode 100644
index 0000000..567d9a5
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_title_map_alt.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_title_map_default.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_title_map_default.png.svn-base
new file mode 100644
index 0000000..21b8885
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_title_map_default.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_title_refresh_alt.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_title_refresh_alt.png.svn-base
new file mode 100644
index 0000000..deb7075
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_title_refresh_alt.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_title_refresh_default.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_title_refresh_default.png.svn-base
new file mode 100644
index 0000000..08c32e0
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_title_refresh_default.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_title_search_alt.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_title_search_alt.png.svn-base
new file mode 100644
index 0000000..1c66fd6
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_title_search_alt.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_title_search_default.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_title_search_default.png.svn-base
new file mode 100644
index 0000000..59de344
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_title_search_default.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_title_share_alt.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_title_share_alt.png.svn-base
new file mode 100644
index 0000000..95739a8
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_title_share_alt.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_title_share_default.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_title_share_default.png.svn-base
new file mode 100644
index 0000000..e748c85
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_title_share_default.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_track_border.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_track_border.9.png.svn-base
new file mode 100644
index 0000000..7da0290
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_track_border.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/ic_track_color_target.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/ic_track_color_target.9.png.svn-base
new file mode 100644
index 0000000..7ad2ceb
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/ic_track_color_target.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/minitab_default.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/minitab_default.9.png.svn-base
new file mode 100644
index 0000000..ed6014c
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/minitab_default.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/minitab_pressed.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/minitab_pressed.9.png.svn-base
new file mode 100644
index 0000000..c41c0a7
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/minitab_pressed.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/minitab_selected.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/minitab_selected.9.png.svn-base
new file mode 100644
index 0000000..f3f0156
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/minitab_selected.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/minitab_unselected.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/minitab_unselected.9.png.svn-base
new file mode 100644
index 0000000..9188fdf
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/minitab_unselected.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/now_bar.9.png.svn-base b/res/drawable-hdpi/.svn/text-base/now_bar.9.png.svn-base
new file mode 100644
index 0000000..faf5e4b
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/now_bar.9.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/now_playing_icon.png.svn-base b/res/drawable-hdpi/.svn/text-base/now_playing_icon.png.svn-base
new file mode 100644
index 0000000..bbc22b3
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/now_playing_icon.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/title_logo_default.png.svn-base b/res/drawable-hdpi/.svn/text-base/title_logo_default.png.svn-base
new file mode 100644
index 0000000..758e979
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/title_logo_default.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/title_logo_pressed.png.svn-base b/res/drawable-hdpi/.svn/text-base/title_logo_pressed.png.svn-base
new file mode 100644
index 0000000..1992d58
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/title_logo_pressed.png.svn-base differ
diff --git a/res/drawable-hdpi/.svn/text-base/title_logo_selected.png.svn-base b/res/drawable-hdpi/.svn/text-base/title_logo_selected.png.svn-base
new file mode 100644
index 0000000..fc60847
Binary files /dev/null and b/res/drawable-hdpi/.svn/text-base/title_logo_selected.png.svn-base differ
diff --git a/res/drawable-hdpi/btn_bg_pressed.9.png b/res/drawable-hdpi/btn_bg_pressed.9.png
new file mode 100644
index 0000000..b1afd4b
Binary files /dev/null and b/res/drawable-hdpi/btn_bg_pressed.9.png differ
diff --git a/res/drawable-hdpi/btn_bg_selected.9.png b/res/drawable-hdpi/btn_bg_selected.9.png
new file mode 100644
index 0000000..331f96f
Binary files /dev/null and b/res/drawable-hdpi/btn_bg_selected.9.png differ
diff --git a/res/drawable-hdpi/btn_block_border_default.9.png b/res/drawable-hdpi/btn_block_border_default.9.png
new file mode 100644
index 0000000..997baff
Binary files /dev/null and b/res/drawable-hdpi/btn_block_border_default.9.png differ
diff --git a/res/drawable-hdpi/btn_block_border_pressed.9.png b/res/drawable-hdpi/btn_block_border_pressed.9.png
new file mode 100644
index 0000000..7a48abc
Binary files /dev/null and b/res/drawable-hdpi/btn_block_border_pressed.9.png differ
diff --git a/res/drawable-hdpi/btn_block_border_selected.9.png b/res/drawable-hdpi/btn_block_border_selected.9.png
new file mode 100644
index 0000000..4f54149
Binary files /dev/null and b/res/drawable-hdpi/btn_block_border_selected.9.png differ
diff --git a/res/drawable-hdpi/btn_block_color_target.9.png b/res/drawable-hdpi/btn_block_color_target.9.png
new file mode 100644
index 0000000..05a9ef2
Binary files /dev/null and b/res/drawable-hdpi/btn_block_color_target.9.png differ
diff --git a/res/drawable-hdpi/btn_block_star.9.png b/res/drawable-hdpi/btn_block_star.9.png
new file mode 100644
index 0000000..7a09b11
Binary files /dev/null and b/res/drawable-hdpi/btn_block_star.9.png differ
diff --git a/res/drawable-hdpi/btn_now_playing_default.9.png b/res/drawable-hdpi/btn_now_playing_default.9.png
new file mode 100644
index 0000000..f6541cd
Binary files /dev/null and b/res/drawable-hdpi/btn_now_playing_default.9.png differ
diff --git a/res/drawable-hdpi/btn_now_playing_pressed.9.png b/res/drawable-hdpi/btn_now_playing_pressed.9.png
new file mode 100644
index 0000000..fc64d9e
Binary files /dev/null and b/res/drawable-hdpi/btn_now_playing_pressed.9.png differ
diff --git a/res/drawable-hdpi/btn_now_playing_selected.9.png b/res/drawable-hdpi/btn_now_playing_selected.9.png
new file mode 100644
index 0000000..24cb45b
Binary files /dev/null and b/res/drawable-hdpi/btn_now_playing_selected.9.png differ
diff --git a/res/drawable-hdpi/colorstrip.png b/res/drawable-hdpi/colorstrip.png
new file mode 100644
index 0000000..f899bd2
Binary files /dev/null and b/res/drawable-hdpi/colorstrip.png differ
diff --git a/res/drawable-hdpi/home_btn_map_default.png b/res/drawable-hdpi/home_btn_map_default.png
new file mode 100644
index 0000000..f897c12
Binary files /dev/null and b/res/drawable-hdpi/home_btn_map_default.png differ
diff --git a/res/drawable-hdpi/home_btn_map_pressed.png b/res/drawable-hdpi/home_btn_map_pressed.png
new file mode 100644
index 0000000..a6ea899
Binary files /dev/null and b/res/drawable-hdpi/home_btn_map_pressed.png differ
diff --git a/res/drawable-hdpi/home_btn_map_selected.png b/res/drawable-hdpi/home_btn_map_selected.png
new file mode 100644
index 0000000..f774655
Binary files /dev/null and b/res/drawable-hdpi/home_btn_map_selected.png differ
diff --git a/res/drawable-hdpi/home_btn_office_hours_default.png b/res/drawable-hdpi/home_btn_office_hours_default.png
new file mode 100644
index 0000000..40151ff
Binary files /dev/null and b/res/drawable-hdpi/home_btn_office_hours_default.png differ
diff --git a/res/drawable-hdpi/home_btn_office_hours_pressed.png b/res/drawable-hdpi/home_btn_office_hours_pressed.png
new file mode 100644
index 0000000..1dc9169
Binary files /dev/null and b/res/drawable-hdpi/home_btn_office_hours_pressed.png differ
diff --git a/res/drawable-hdpi/home_btn_office_hours_selected.png b/res/drawable-hdpi/home_btn_office_hours_selected.png
new file mode 100644
index 0000000..6dcdae4
Binary files /dev/null and b/res/drawable-hdpi/home_btn_office_hours_selected.png differ
diff --git a/res/drawable-hdpi/home_btn_sandbox_default.png b/res/drawable-hdpi/home_btn_sandbox_default.png
new file mode 100644
index 0000000..3d9916f
Binary files /dev/null and b/res/drawable-hdpi/home_btn_sandbox_default.png differ
diff --git a/res/drawable-hdpi/home_btn_sandbox_pressed.png b/res/drawable-hdpi/home_btn_sandbox_pressed.png
new file mode 100644
index 0000000..c32a390
Binary files /dev/null and b/res/drawable-hdpi/home_btn_sandbox_pressed.png differ
diff --git a/res/drawable-hdpi/home_btn_sandbox_selected.png b/res/drawable-hdpi/home_btn_sandbox_selected.png
new file mode 100644
index 0000000..2368b8e
Binary files /dev/null and b/res/drawable-hdpi/home_btn_sandbox_selected.png differ
diff --git a/res/drawable-hdpi/home_btn_schedule_default.png b/res/drawable-hdpi/home_btn_schedule_default.png
new file mode 100644
index 0000000..95041a1
Binary files /dev/null and b/res/drawable-hdpi/home_btn_schedule_default.png differ
diff --git a/res/drawable-hdpi/home_btn_schedule_pressed.png b/res/drawable-hdpi/home_btn_schedule_pressed.png
new file mode 100644
index 0000000..17b55f4
Binary files /dev/null and b/res/drawable-hdpi/home_btn_schedule_pressed.png differ
diff --git a/res/drawable-hdpi/home_btn_schedule_selected.png b/res/drawable-hdpi/home_btn_schedule_selected.png
new file mode 100644
index 0000000..a3b558d
Binary files /dev/null and b/res/drawable-hdpi/home_btn_schedule_selected.png differ
diff --git a/res/drawable-hdpi/home_btn_sessions_default.png b/res/drawable-hdpi/home_btn_sessions_default.png
new file mode 100644
index 0000000..4a0f509
Binary files /dev/null and b/res/drawable-hdpi/home_btn_sessions_default.png differ
diff --git a/res/drawable-hdpi/home_btn_sessions_pressed.png b/res/drawable-hdpi/home_btn_sessions_pressed.png
new file mode 100644
index 0000000..674dbad
Binary files /dev/null and b/res/drawable-hdpi/home_btn_sessions_pressed.png differ
diff --git a/res/drawable-hdpi/home_btn_sessions_selected.png b/res/drawable-hdpi/home_btn_sessions_selected.png
new file mode 100644
index 0000000..d362107
Binary files /dev/null and b/res/drawable-hdpi/home_btn_sessions_selected.png differ
diff --git a/res/drawable-hdpi/home_btn_starred_default.png b/res/drawable-hdpi/home_btn_starred_default.png
new file mode 100644
index 0000000..51e4413
Binary files /dev/null and b/res/drawable-hdpi/home_btn_starred_default.png differ
diff --git a/res/drawable-hdpi/home_btn_starred_pressed.png b/res/drawable-hdpi/home_btn_starred_pressed.png
new file mode 100644
index 0000000..9bc9e75
Binary files /dev/null and b/res/drawable-hdpi/home_btn_starred_pressed.png differ
diff --git a/res/drawable-hdpi/home_btn_starred_selected.png b/res/drawable-hdpi/home_btn_starred_selected.png
new file mode 100644
index 0000000..b3f70ed
Binary files /dev/null and b/res/drawable-hdpi/home_btn_starred_selected.png differ
diff --git a/res/drawable-hdpi/ic_launcher.png b/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..ab621fc
Binary files /dev/null and b/res/drawable-hdpi/ic_launcher.png differ
diff --git a/res/drawable-hdpi/ic_now_playing_logo_alt.png b/res/drawable-hdpi/ic_now_playing_logo_alt.png
new file mode 100644
index 0000000..72fbc47
Binary files /dev/null and b/res/drawable-hdpi/ic_now_playing_logo_alt.png differ
diff --git a/res/drawable-hdpi/ic_now_playing_logo_default.png b/res/drawable-hdpi/ic_now_playing_logo_default.png
new file mode 100644
index 0000000..c8d2d8d
Binary files /dev/null and b/res/drawable-hdpi/ic_now_playing_logo_default.png differ
diff --git a/res/drawable-hdpi/ic_now_playing_more_alt.png b/res/drawable-hdpi/ic_now_playing_more_alt.png
new file mode 100644
index 0000000..ba27b5c
Binary files /dev/null and b/res/drawable-hdpi/ic_now_playing_more_alt.png differ
diff --git a/res/drawable-hdpi/ic_now_playing_more_default.png b/res/drawable-hdpi/ic_now_playing_more_default.png
new file mode 100644
index 0000000..b098015
Binary files /dev/null and b/res/drawable-hdpi/ic_now_playing_more_default.png differ
diff --git a/res/drawable-hdpi/ic_title_export_alt.png b/res/drawable-hdpi/ic_title_export_alt.png
new file mode 100644
index 0000000..0de5f13
Binary files /dev/null and b/res/drawable-hdpi/ic_title_export_alt.png differ
diff --git a/res/drawable-hdpi/ic_title_export_default.png b/res/drawable-hdpi/ic_title_export_default.png
new file mode 100644
index 0000000..b86014d
Binary files /dev/null and b/res/drawable-hdpi/ic_title_export_default.png differ
diff --git a/res/drawable-hdpi/ic_title_home_alt.png b/res/drawable-hdpi/ic_title_home_alt.png
new file mode 100644
index 0000000..7f2e9aa
Binary files /dev/null and b/res/drawable-hdpi/ic_title_home_alt.png differ
diff --git a/res/drawable-hdpi/ic_title_home_default.png b/res/drawable-hdpi/ic_title_home_default.png
new file mode 100644
index 0000000..af72258
Binary files /dev/null and b/res/drawable-hdpi/ic_title_home_default.png differ
diff --git a/res/drawable-hdpi/ic_title_map_alt.png b/res/drawable-hdpi/ic_title_map_alt.png
new file mode 100644
index 0000000..567d9a5
Binary files /dev/null and b/res/drawable-hdpi/ic_title_map_alt.png differ
diff --git a/res/drawable-hdpi/ic_title_map_default.png b/res/drawable-hdpi/ic_title_map_default.png
new file mode 100644
index 0000000..21b8885
Binary files /dev/null and b/res/drawable-hdpi/ic_title_map_default.png differ
diff --git a/res/drawable-hdpi/ic_title_refresh_alt.png b/res/drawable-hdpi/ic_title_refresh_alt.png
new file mode 100644
index 0000000..deb7075
Binary files /dev/null and b/res/drawable-hdpi/ic_title_refresh_alt.png differ
diff --git a/res/drawable-hdpi/ic_title_refresh_default.png b/res/drawable-hdpi/ic_title_refresh_default.png
new file mode 100644
index 0000000..08c32e0
Binary files /dev/null and b/res/drawable-hdpi/ic_title_refresh_default.png differ
diff --git a/res/drawable-hdpi/ic_title_search_alt.png b/res/drawable-hdpi/ic_title_search_alt.png
new file mode 100644
index 0000000..1c66fd6
Binary files /dev/null and b/res/drawable-hdpi/ic_title_search_alt.png differ
diff --git a/res/drawable-hdpi/ic_title_search_default.png b/res/drawable-hdpi/ic_title_search_default.png
new file mode 100644
index 0000000..59de344
Binary files /dev/null and b/res/drawable-hdpi/ic_title_search_default.png differ
diff --git a/res/drawable-hdpi/ic_title_share_alt.png b/res/drawable-hdpi/ic_title_share_alt.png
new file mode 100644
index 0000000..95739a8
Binary files /dev/null and b/res/drawable-hdpi/ic_title_share_alt.png differ
diff --git a/res/drawable-hdpi/ic_title_share_default.png b/res/drawable-hdpi/ic_title_share_default.png
new file mode 100644
index 0000000..e748c85
Binary files /dev/null and b/res/drawable-hdpi/ic_title_share_default.png differ
diff --git a/res/drawable-hdpi/ic_track_border.9.png b/res/drawable-hdpi/ic_track_border.9.png
new file mode 100644
index 0000000..7da0290
Binary files /dev/null and b/res/drawable-hdpi/ic_track_border.9.png differ
diff --git a/res/drawable-hdpi/ic_track_color_target.9.png b/res/drawable-hdpi/ic_track_color_target.9.png
new file mode 100644
index 0000000..7ad2ceb
Binary files /dev/null and b/res/drawable-hdpi/ic_track_color_target.9.png differ
diff --git a/res/drawable-hdpi/minitab_default.9.png b/res/drawable-hdpi/minitab_default.9.png
new file mode 100644
index 0000000..ed6014c
Binary files /dev/null and b/res/drawable-hdpi/minitab_default.9.png differ
diff --git a/res/drawable-hdpi/minitab_pressed.9.png b/res/drawable-hdpi/minitab_pressed.9.png
new file mode 100644
index 0000000..c41c0a7
Binary files /dev/null and b/res/drawable-hdpi/minitab_pressed.9.png differ
diff --git a/res/drawable-hdpi/minitab_selected.9.png b/res/drawable-hdpi/minitab_selected.9.png
new file mode 100644
index 0000000..f3f0156
Binary files /dev/null and b/res/drawable-hdpi/minitab_selected.9.png differ
diff --git a/res/drawable-hdpi/minitab_unselected.9.png b/res/drawable-hdpi/minitab_unselected.9.png
new file mode 100644
index 0000000..9188fdf
Binary files /dev/null and b/res/drawable-hdpi/minitab_unselected.9.png differ
diff --git a/res/drawable-hdpi/now_bar.9.png b/res/drawable-hdpi/now_bar.9.png
new file mode 100644
index 0000000..faf5e4b
Binary files /dev/null and b/res/drawable-hdpi/now_bar.9.png differ
diff --git a/res/drawable-hdpi/now_playing_icon.png b/res/drawable-hdpi/now_playing_icon.png
new file mode 100644
index 0000000..bbc22b3
Binary files /dev/null and b/res/drawable-hdpi/now_playing_icon.png differ
diff --git a/res/drawable-hdpi/title_logo_default.png b/res/drawable-hdpi/title_logo_default.png
new file mode 100644
index 0000000..758e979
Binary files /dev/null and b/res/drawable-hdpi/title_logo_default.png differ
diff --git a/res/drawable-hdpi/title_logo_pressed.png b/res/drawable-hdpi/title_logo_pressed.png
new file mode 100644
index 0000000..1992d58
Binary files /dev/null and b/res/drawable-hdpi/title_logo_pressed.png differ
diff --git a/res/drawable-hdpi/title_logo_selected.png b/res/drawable-hdpi/title_logo_selected.png
new file mode 100644
index 0000000..fc60847
Binary files /dev/null and b/res/drawable-hdpi/title_logo_selected.png differ
diff --git a/res/drawable-mdpi/.svn/all-wcprops b/res/drawable-mdpi/.svn/all-wcprops
new file mode 100644
index 0000000..fcec6d4
--- /dev/null
+++ b/res/drawable-mdpi/.svn/all-wcprops
@@ -0,0 +1,29 @@
+K 25
+svn:wc:ra_dav:version-url
+V 39
+/svn/!svn/ver/2/trunk/res/drawable-mdpi
+END
+minitab_default.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 61
+/svn/!svn/ver/2/trunk/res/drawable-mdpi/minitab_default.9.png
+END
+minitab_pressed.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 61
+/svn/!svn/ver/2/trunk/res/drawable-mdpi/minitab_pressed.9.png
+END
+minitab_selected.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 62
+/svn/!svn/ver/2/trunk/res/drawable-mdpi/minitab_selected.9.png
+END
+minitab_unselected.9.png
+K 25
+svn:wc:ra_dav:version-url
+V 64
+/svn/!svn/ver/2/trunk/res/drawable-mdpi/minitab_unselected.9.png
+END
diff --git a/res/drawable-mdpi/.svn/entries b/res/drawable-mdpi/.svn/entries
new file mode 100644
index 0000000..325137f
--- /dev/null
+++ b/res/drawable-mdpi/.svn/entries
@@ -0,0 +1,164 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/res/drawable-mdpi
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+minitab_default.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.885645Z
+8ce2b4b5523161e006d4afafa02f36be
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+206
+
+minitab_pressed.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.889648Z
+d4601f18f49dde35e9d703680b554977
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+216
+
+minitab_selected.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.889648Z
+3c5ec57a313ef1c573e24085bee7f1ed
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+224
+
+minitab_unselected.9.png
+file
+
+
+
+
+2010-06-03T15:23:16.889648Z
+9af541354217f42da1d4c564f2f6e975
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2839
+
diff --git a/res/drawable-mdpi/.svn/prop-base/minitab_default.9.png.svn-base b/res/drawable-mdpi/.svn/prop-base/minitab_default.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-mdpi/.svn/prop-base/minitab_default.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-mdpi/.svn/prop-base/minitab_pressed.9.png.svn-base b/res/drawable-mdpi/.svn/prop-base/minitab_pressed.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-mdpi/.svn/prop-base/minitab_pressed.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-mdpi/.svn/prop-base/minitab_selected.9.png.svn-base b/res/drawable-mdpi/.svn/prop-base/minitab_selected.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-mdpi/.svn/prop-base/minitab_selected.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-mdpi/.svn/prop-base/minitab_unselected.9.png.svn-base b/res/drawable-mdpi/.svn/prop-base/minitab_unselected.9.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/res/drawable-mdpi/.svn/prop-base/minitab_unselected.9.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/res/drawable-mdpi/.svn/text-base/minitab_default.9.png.svn-base b/res/drawable-mdpi/.svn/text-base/minitab_default.9.png.svn-base
new file mode 100644
index 0000000..6358bb8
Binary files /dev/null and b/res/drawable-mdpi/.svn/text-base/minitab_default.9.png.svn-base differ
diff --git a/res/drawable-mdpi/.svn/text-base/minitab_pressed.9.png.svn-base b/res/drawable-mdpi/.svn/text-base/minitab_pressed.9.png.svn-base
new file mode 100644
index 0000000..f0030f6
Binary files /dev/null and b/res/drawable-mdpi/.svn/text-base/minitab_pressed.9.png.svn-base differ
diff --git a/res/drawable-mdpi/.svn/text-base/minitab_selected.9.png.svn-base b/res/drawable-mdpi/.svn/text-base/minitab_selected.9.png.svn-base
new file mode 100644
index 0000000..a02efb1
Binary files /dev/null and b/res/drawable-mdpi/.svn/text-base/minitab_selected.9.png.svn-base differ
diff --git a/res/drawable-mdpi/.svn/text-base/minitab_unselected.9.png.svn-base b/res/drawable-mdpi/.svn/text-base/minitab_unselected.9.png.svn-base
new file mode 100644
index 0000000..f47f042
Binary files /dev/null and b/res/drawable-mdpi/.svn/text-base/minitab_unselected.9.png.svn-base differ
diff --git a/res/drawable-mdpi/minitab_default.9.png b/res/drawable-mdpi/minitab_default.9.png
new file mode 100644
index 0000000..6358bb8
Binary files /dev/null and b/res/drawable-mdpi/minitab_default.9.png differ
diff --git a/res/drawable-mdpi/minitab_pressed.9.png b/res/drawable-mdpi/minitab_pressed.9.png
new file mode 100644
index 0000000..f0030f6
Binary files /dev/null and b/res/drawable-mdpi/minitab_pressed.9.png differ
diff --git a/res/drawable-mdpi/minitab_selected.9.png b/res/drawable-mdpi/minitab_selected.9.png
new file mode 100644
index 0000000..a02efb1
Binary files /dev/null and b/res/drawable-mdpi/minitab_selected.9.png differ
diff --git a/res/drawable-mdpi/minitab_unselected.9.png b/res/drawable-mdpi/minitab_unselected.9.png
new file mode 100644
index 0000000..f47f042
Binary files /dev/null and b/res/drawable-mdpi/minitab_unselected.9.png differ
diff --git a/res/drawable/.svn/all-wcprops b/res/drawable/.svn/all-wcprops
new file mode 100644
index 0000000..8e14f1a
--- /dev/null
+++ b/res/drawable/.svn/all-wcprops
@@ -0,0 +1,173 @@
+K 25
+svn:wc:ra_dav:version-url
+V 34
+/svn/!svn/ver/2/trunk/res/drawable
+END
+ic_title_map_light.xml
+K 25
+svn:wc:ra_dav:version-url
+V 57
+/svn/!svn/ver/2/trunk/res/drawable/ic_title_map_light.xml
+END
+home_btn_sessions.xml
+K 25
+svn:wc:ra_dav:version-url
+V 56
+/svn/!svn/ver/2/trunk/res/drawable/home_btn_sessions.xml
+END
+ic_title_search.xml
+K 25
+svn:wc:ra_dav:version-url
+V 54
+/svn/!svn/ver/2/trunk/res/drawable/ic_title_search.xml
+END
+ic_now_playing_logo.xml
+K 25
+svn:wc:ra_dav:version-url
+V 58
+/svn/!svn/ver/2/trunk/res/drawable/ic_now_playing_logo.xml
+END
+ic_now_playing_more.xml
+K 25
+svn:wc:ra_dav:version-url
+V 58
+/svn/!svn/ver/2/trunk/res/drawable/ic_now_playing_more.xml
+END
+minitab.xml
+K 25
+svn:wc:ra_dav:version-url
+V 46
+/svn/!svn/ver/2/trunk/res/drawable/minitab.xml
+END
+ic_title_export_light.xml
+K 25
+svn:wc:ra_dav:version-url
+V 60
+/svn/!svn/ver/2/trunk/res/drawable/ic_title_export_light.xml
+END
+home_btn_office_hours.xml
+K 25
+svn:wc:ra_dav:version-url
+V 60
+/svn/!svn/ver/2/trunk/res/drawable/home_btn_office_hours.xml
+END
+home_btn_map.xml
+K 25
+svn:wc:ra_dav:version-url
+V 51
+/svn/!svn/ver/2/trunk/res/drawable/home_btn_map.xml
+END
+btn_now_playing.xml
+K 25
+svn:wc:ra_dav:version-url
+V 54
+/svn/!svn/ver/2/trunk/res/drawable/btn_now_playing.xml
+END
+ic_title_map.xml
+K 25
+svn:wc:ra_dav:version-url
+V 51
+/svn/!svn/ver/2/trunk/res/drawable/ic_title_map.xml
+END
+btn_block_border.xml
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/2/trunk/res/drawable/btn_block_border.xml
+END
+ic_title_home_light.xml
+K 25
+svn:wc:ra_dav:version-url
+V 58
+/svn/!svn/ver/2/trunk/res/drawable/ic_title_home_light.xml
+END
+ic_title_export.xml
+K 25
+svn:wc:ra_dav:version-url
+V 54
+/svn/!svn/ver/2/trunk/res/drawable/ic_title_export.xml
+END
+btn_block.xml
+K 25
+svn:wc:ra_dav:version-url
+V 48
+/svn/!svn/ver/2/trunk/res/drawable/btn_block.xml
+END
+ic_title_refresh_light.xml
+K 25
+svn:wc:ra_dav:version-url
+V 61
+/svn/!svn/ver/2/trunk/res/drawable/ic_title_refresh_light.xml
+END
+title_logo.xml
+K 25
+svn:wc:ra_dav:version-url
+V 49
+/svn/!svn/ver/2/trunk/res/drawable/title_logo.xml
+END
+ic_title_home.xml
+K 25
+svn:wc:ra_dav:version-url
+V 52
+/svn/!svn/ver/2/trunk/res/drawable/ic_title_home.xml
+END
+ic_title_share_light.xml
+K 25
+svn:wc:ra_dav:version-url
+V 59
+/svn/!svn/ver/2/trunk/res/drawable/ic_title_share_light.xml
+END
+home_btn_schedule.xml
+K 25
+svn:wc:ra_dav:version-url
+V 56
+/svn/!svn/ver/2/trunk/res/drawable/home_btn_schedule.xml
+END
+ic_title_search_light.xml
+K 25
+svn:wc:ra_dav:version-url
+V 60
+/svn/!svn/ver/2/trunk/res/drawable/ic_title_search_light.xml
+END
+home_btn_sandbox.xml
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/2/trunk/res/drawable/home_btn_sandbox.xml
+END
+ic_title_refresh.xml
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/2/trunk/res/drawable/ic_title_refresh.xml
+END
+btn_now_playing_more.xml
+K 25
+svn:wc:ra_dav:version-url
+V 59
+/svn/!svn/ver/2/trunk/res/drawable/btn_now_playing_more.xml
+END
+title_button.xml
+K 25
+svn:wc:ra_dav:version-url
+V 51
+/svn/!svn/ver/2/trunk/res/drawable/title_button.xml
+END
+ic_title_share.xml
+K 25
+svn:wc:ra_dav:version-url
+V 53
+/svn/!svn/ver/2/trunk/res/drawable/ic_title_share.xml
+END
+ic_track.xml
+K 25
+svn:wc:ra_dav:version-url
+V 47
+/svn/!svn/ver/2/trunk/res/drawable/ic_track.xml
+END
+home_btn_starred.xml
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/2/trunk/res/drawable/home_btn_starred.xml
+END
diff --git a/res/drawable/.svn/entries b/res/drawable/.svn/entries
new file mode 100644
index 0000000..b6669eb
--- /dev/null
+++ b/res/drawable/.svn/entries
@@ -0,0 +1,980 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/res/drawable
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+ic_title_map_light.xml
+file
+
+
+
+
+2010-06-03T15:23:15.889646Z
+7555fc62344a7617b3dffa6ac6b14c58
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1165
+
+home_btn_sessions.xml
+file
+
+
+
+
+2010-06-03T15:23:15.889646Z
+99a23de2978e7410ff5b61b5dbccf796
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1198
+
+ic_title_search.xml
+file
+
+
+
+
+2010-06-03T15:23:15.889646Z
+8435ea5b58c5e459e333b7dd05cad10b
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+893
+
+ic_now_playing_logo.xml
+file
+
+
+
+
+2010-06-03T15:23:15.889646Z
+9d731bc22ceee6a9cfbdc4ebc03e7c7f
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1193
+
+ic_now_playing_more.xml
+file
+
+
+
+
+2010-06-03T15:23:15.889646Z
+3e42e2ba208eea9cfaab76f5e71ab4c9
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1280
+
+minitab.xml
+file
+
+
+
+
+2010-06-03T15:23:15.889646Z
+8e63170ddfd56d1e91ce43d681411133
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1402
+
+ic_title_export_light.xml
+file
+
+
+
+
+2010-06-03T15:23:15.889646Z
+8e4e651c83713a5b38e0c38af60c926d
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1177
+
+home_btn_office_hours.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+1212dd8ba51a77253b46c46086ff5cce
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1214
+
+home_btn_map.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+e7f0be983c5219295838df6a801b0a95
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1178
+
+btn_now_playing.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+b9801be9979e4dc2520ad7e316d3e00c
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1277
+
+ic_title_map.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+bed42b6f62540bf84f692df578830e9f
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+887
+
+btn_block_border.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+5ee793e0646de9e62d40c3e51282cadf
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1281
+
+ic_title_home_light.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+1fc42360c2e6f96ce455871fd621a51f
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1169
+
+ic_title_export.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+f9fb1808276adf0345f90600f978c878
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+893
+
+btn_block.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+e165a343f96478c0bce2e7dea72fc3dd
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+905
+
+ic_title_refresh_light.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+5279c3ab3209a80759f3dfd5502e4dae
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1181
+
+title_logo.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+19089aba73c3e23b5a7fac16f98bc296
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1170
+
+ic_title_home.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+7a276b082acfcf1c230b5acebdae2a72
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+889
+
+ic_title_share_light.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+38561c6afee0e629948e9cbe92daf242
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1173
+
+home_btn_schedule.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+4680afaac1a68d28b59de89e3d6f60ad
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1198
+
+ic_title_search_light.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+3f3178b19e9ba423472efc08c95ba78f
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1177
+
+home_btn_sandbox.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+bebc5a94c9b1b86fa15eb0a3ffaf2dec
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1194
+
+ic_title_refresh.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+c636c4ab9c7a481a95c2b583149f8792
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+895
+
+btn_now_playing_more.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+7031a934807760f8c977aa8d813e3706
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1235
+
+title_button.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+8a46964e69541770653ed0cd20004145
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1156
+
+ic_title_share.xml
+file
+
+
+
+
+2010-06-03T15:23:15.893629Z
+dd8449e393d472feeba39764a063b842
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+891
+
+ic_track.xml
+file
+
+
+
+
+2010-06-03T15:23:15.897645Z
+14a8e66b01ed797c6dd43354597ba52b
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+847
+
+home_btn_starred.xml
+file
+
+
+
+
+2010-06-03T15:23:15.897645Z
+3cca3688c9e0c004ed821d9e2a09c7ed
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1194
+
diff --git a/res/drawable/.svn/text-base/btn_block.xml.svn-base b/res/drawable/.svn/text-base/btn_block.xml.svn-base
new file mode 100644
index 0000000..9c40c70
--- /dev/null
+++ b/res/drawable/.svn/text-base/btn_block.xml.svn-base
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/btn_block_border.xml.svn-base b/res/drawable/.svn/text-base/btn_block_border.xml.svn-base
new file mode 100644
index 0000000..3d72f79
--- /dev/null
+++ b/res/drawable/.svn/text-base/btn_block_border.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/btn_now_playing.xml.svn-base b/res/drawable/.svn/text-base/btn_now_playing.xml.svn-base
new file mode 100644
index 0000000..e2193d9
--- /dev/null
+++ b/res/drawable/.svn/text-base/btn_now_playing.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/btn_now_playing_more.xml.svn-base b/res/drawable/.svn/text-base/btn_now_playing_more.xml.svn-base
new file mode 100644
index 0000000..182480d
--- /dev/null
+++ b/res/drawable/.svn/text-base/btn_now_playing_more.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/home_btn_map.xml.svn-base b/res/drawable/.svn/text-base/home_btn_map.xml.svn-base
new file mode 100644
index 0000000..7aba3c7
--- /dev/null
+++ b/res/drawable/.svn/text-base/home_btn_map.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/home_btn_office_hours.xml.svn-base b/res/drawable/.svn/text-base/home_btn_office_hours.xml.svn-base
new file mode 100644
index 0000000..088ad2e
--- /dev/null
+++ b/res/drawable/.svn/text-base/home_btn_office_hours.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/home_btn_sandbox.xml.svn-base b/res/drawable/.svn/text-base/home_btn_sandbox.xml.svn-base
new file mode 100644
index 0000000..5dfad0a
--- /dev/null
+++ b/res/drawable/.svn/text-base/home_btn_sandbox.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/home_btn_schedule.xml.svn-base b/res/drawable/.svn/text-base/home_btn_schedule.xml.svn-base
new file mode 100644
index 0000000..b86b5f1
--- /dev/null
+++ b/res/drawable/.svn/text-base/home_btn_schedule.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/home_btn_sessions.xml.svn-base b/res/drawable/.svn/text-base/home_btn_sessions.xml.svn-base
new file mode 100644
index 0000000..199c59f
--- /dev/null
+++ b/res/drawable/.svn/text-base/home_btn_sessions.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/home_btn_starred.xml.svn-base b/res/drawable/.svn/text-base/home_btn_starred.xml.svn-base
new file mode 100644
index 0000000..f07e20d
--- /dev/null
+++ b/res/drawable/.svn/text-base/home_btn_starred.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/ic_now_playing_logo.xml.svn-base b/res/drawable/.svn/text-base/ic_now_playing_logo.xml.svn-base
new file mode 100644
index 0000000..9a3c4b2
--- /dev/null
+++ b/res/drawable/.svn/text-base/ic_now_playing_logo.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/ic_now_playing_more.xml.svn-base b/res/drawable/.svn/text-base/ic_now_playing_more.xml.svn-base
new file mode 100644
index 0000000..bcbd69f
--- /dev/null
+++ b/res/drawable/.svn/text-base/ic_now_playing_more.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/ic_title_export.xml.svn-base b/res/drawable/.svn/text-base/ic_title_export.xml.svn-base
new file mode 100644
index 0000000..136acf8
--- /dev/null
+++ b/res/drawable/.svn/text-base/ic_title_export.xml.svn-base
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/ic_title_export_light.xml.svn-base b/res/drawable/.svn/text-base/ic_title_export_light.xml.svn-base
new file mode 100644
index 0000000..49a7cfb
--- /dev/null
+++ b/res/drawable/.svn/text-base/ic_title_export_light.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/ic_title_home.xml.svn-base b/res/drawable/.svn/text-base/ic_title_home.xml.svn-base
new file mode 100644
index 0000000..344dd6e
--- /dev/null
+++ b/res/drawable/.svn/text-base/ic_title_home.xml.svn-base
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/ic_title_home_light.xml.svn-base b/res/drawable/.svn/text-base/ic_title_home_light.xml.svn-base
new file mode 100644
index 0000000..9a40bd5
--- /dev/null
+++ b/res/drawable/.svn/text-base/ic_title_home_light.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/ic_title_map.xml.svn-base b/res/drawable/.svn/text-base/ic_title_map.xml.svn-base
new file mode 100644
index 0000000..a02ded8
--- /dev/null
+++ b/res/drawable/.svn/text-base/ic_title_map.xml.svn-base
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/ic_title_map_light.xml.svn-base b/res/drawable/.svn/text-base/ic_title_map_light.xml.svn-base
new file mode 100644
index 0000000..75e5edf
--- /dev/null
+++ b/res/drawable/.svn/text-base/ic_title_map_light.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/ic_title_refresh.xml.svn-base b/res/drawable/.svn/text-base/ic_title_refresh.xml.svn-base
new file mode 100644
index 0000000..3d0d093
--- /dev/null
+++ b/res/drawable/.svn/text-base/ic_title_refresh.xml.svn-base
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/ic_title_refresh_light.xml.svn-base b/res/drawable/.svn/text-base/ic_title_refresh_light.xml.svn-base
new file mode 100644
index 0000000..89107fa
--- /dev/null
+++ b/res/drawable/.svn/text-base/ic_title_refresh_light.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/ic_title_search.xml.svn-base b/res/drawable/.svn/text-base/ic_title_search.xml.svn-base
new file mode 100644
index 0000000..feabb28
--- /dev/null
+++ b/res/drawable/.svn/text-base/ic_title_search.xml.svn-base
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/ic_title_search_light.xml.svn-base b/res/drawable/.svn/text-base/ic_title_search_light.xml.svn-base
new file mode 100644
index 0000000..02872b4
--- /dev/null
+++ b/res/drawable/.svn/text-base/ic_title_search_light.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/ic_title_share.xml.svn-base b/res/drawable/.svn/text-base/ic_title_share.xml.svn-base
new file mode 100644
index 0000000..51213ea
--- /dev/null
+++ b/res/drawable/.svn/text-base/ic_title_share.xml.svn-base
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/ic_title_share_light.xml.svn-base b/res/drawable/.svn/text-base/ic_title_share_light.xml.svn-base
new file mode 100644
index 0000000..920a833
--- /dev/null
+++ b/res/drawable/.svn/text-base/ic_title_share_light.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/ic_track.xml.svn-base b/res/drawable/.svn/text-base/ic_track.xml.svn-base
new file mode 100644
index 0000000..aca5487
--- /dev/null
+++ b/res/drawable/.svn/text-base/ic_track.xml.svn-base
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/minitab.xml.svn-base b/res/drawable/.svn/text-base/minitab.xml.svn-base
new file mode 100644
index 0000000..74347e8
--- /dev/null
+++ b/res/drawable/.svn/text-base/minitab.xml.svn-base
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/title_button.xml.svn-base b/res/drawable/.svn/text-base/title_button.xml.svn-base
new file mode 100644
index 0000000..d0d992d
--- /dev/null
+++ b/res/drawable/.svn/text-base/title_button.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/.svn/text-base/title_logo.xml.svn-base b/res/drawable/.svn/text-base/title_logo.xml.svn-base
new file mode 100644
index 0000000..a5740ab
--- /dev/null
+++ b/res/drawable/.svn/text-base/title_logo.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/btn_block.xml b/res/drawable/btn_block.xml
new file mode 100644
index 0000000..9c40c70
--- /dev/null
+++ b/res/drawable/btn_block.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/res/drawable/btn_block_border.xml b/res/drawable/btn_block_border.xml
new file mode 100644
index 0000000..3d72f79
--- /dev/null
+++ b/res/drawable/btn_block_border.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/btn_now_playing.xml b/res/drawable/btn_now_playing.xml
new file mode 100644
index 0000000..e2193d9
--- /dev/null
+++ b/res/drawable/btn_now_playing.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/btn_now_playing_more.xml b/res/drawable/btn_now_playing_more.xml
new file mode 100644
index 0000000..182480d
--- /dev/null
+++ b/res/drawable/btn_now_playing_more.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/home_btn_map.xml b/res/drawable/home_btn_map.xml
new file mode 100644
index 0000000..7aba3c7
--- /dev/null
+++ b/res/drawable/home_btn_map.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/home_btn_office_hours.xml b/res/drawable/home_btn_office_hours.xml
new file mode 100644
index 0000000..088ad2e
--- /dev/null
+++ b/res/drawable/home_btn_office_hours.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/home_btn_sandbox.xml b/res/drawable/home_btn_sandbox.xml
new file mode 100644
index 0000000..5dfad0a
--- /dev/null
+++ b/res/drawable/home_btn_sandbox.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/home_btn_schedule.xml b/res/drawable/home_btn_schedule.xml
new file mode 100644
index 0000000..b86b5f1
--- /dev/null
+++ b/res/drawable/home_btn_schedule.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/home_btn_sessions.xml b/res/drawable/home_btn_sessions.xml
new file mode 100644
index 0000000..199c59f
--- /dev/null
+++ b/res/drawable/home_btn_sessions.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/home_btn_starred.xml b/res/drawable/home_btn_starred.xml
new file mode 100644
index 0000000..f07e20d
--- /dev/null
+++ b/res/drawable/home_btn_starred.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/ic_now_playing_logo.xml b/res/drawable/ic_now_playing_logo.xml
new file mode 100644
index 0000000..9a3c4b2
--- /dev/null
+++ b/res/drawable/ic_now_playing_logo.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/ic_now_playing_more.xml b/res/drawable/ic_now_playing_more.xml
new file mode 100644
index 0000000..bcbd69f
--- /dev/null
+++ b/res/drawable/ic_now_playing_more.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/ic_title_export.xml b/res/drawable/ic_title_export.xml
new file mode 100644
index 0000000..136acf8
--- /dev/null
+++ b/res/drawable/ic_title_export.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/res/drawable/ic_title_export_light.xml b/res/drawable/ic_title_export_light.xml
new file mode 100644
index 0000000..49a7cfb
--- /dev/null
+++ b/res/drawable/ic_title_export_light.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/ic_title_home.xml b/res/drawable/ic_title_home.xml
new file mode 100644
index 0000000..344dd6e
--- /dev/null
+++ b/res/drawable/ic_title_home.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/res/drawable/ic_title_home_light.xml b/res/drawable/ic_title_home_light.xml
new file mode 100644
index 0000000..9a40bd5
--- /dev/null
+++ b/res/drawable/ic_title_home_light.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/ic_title_map.xml b/res/drawable/ic_title_map.xml
new file mode 100644
index 0000000..a02ded8
--- /dev/null
+++ b/res/drawable/ic_title_map.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/res/drawable/ic_title_map_light.xml b/res/drawable/ic_title_map_light.xml
new file mode 100644
index 0000000..75e5edf
--- /dev/null
+++ b/res/drawable/ic_title_map_light.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/ic_title_refresh.xml b/res/drawable/ic_title_refresh.xml
new file mode 100644
index 0000000..3d0d093
--- /dev/null
+++ b/res/drawable/ic_title_refresh.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/res/drawable/ic_title_refresh_light.xml b/res/drawable/ic_title_refresh_light.xml
new file mode 100644
index 0000000..89107fa
--- /dev/null
+++ b/res/drawable/ic_title_refresh_light.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/ic_title_search.xml b/res/drawable/ic_title_search.xml
new file mode 100644
index 0000000..feabb28
--- /dev/null
+++ b/res/drawable/ic_title_search.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/res/drawable/ic_title_search_light.xml b/res/drawable/ic_title_search_light.xml
new file mode 100644
index 0000000..02872b4
--- /dev/null
+++ b/res/drawable/ic_title_search_light.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/ic_title_share.xml b/res/drawable/ic_title_share.xml
new file mode 100644
index 0000000..51213ea
--- /dev/null
+++ b/res/drawable/ic_title_share.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/res/drawable/ic_title_share_light.xml b/res/drawable/ic_title_share_light.xml
new file mode 100644
index 0000000..920a833
--- /dev/null
+++ b/res/drawable/ic_title_share_light.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/ic_track.xml b/res/drawable/ic_track.xml
new file mode 100644
index 0000000..aca5487
--- /dev/null
+++ b/res/drawable/ic_track.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/res/drawable/minitab.xml b/res/drawable/minitab.xml
new file mode 100644
index 0000000..74347e8
--- /dev/null
+++ b/res/drawable/minitab.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/title_button.xml b/res/drawable/title_button.xml
new file mode 100644
index 0000000..d0d992d
--- /dev/null
+++ b/res/drawable/title_button.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/drawable/title_logo.xml b/res/drawable/title_logo.xml
new file mode 100644
index 0000000..a5740ab
--- /dev/null
+++ b/res/drawable/title_logo.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/res/layout-land/.svn/all-wcprops b/res/layout-land/.svn/all-wcprops
new file mode 100644
index 0000000..bf6e5e3
--- /dev/null
+++ b/res/layout-land/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 37
+/svn/!svn/ver/2/trunk/res/layout-land
+END
+activity_home.xml
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/2/trunk/res/layout-land/activity_home.xml
+END
diff --git a/res/layout-land/.svn/entries b/res/layout-land/.svn/entries
new file mode 100644
index 0000000..821496e
--- /dev/null
+++ b/res/layout-land/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/res/layout-land
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+activity_home.xml
+file
+
+
+
+
+2010-06-03T15:23:16.909629Z
+fe7a4f16da222de70c951c1d2fa15f60
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4158
+
diff --git a/res/layout-land/.svn/text-base/activity_home.xml.svn-base b/res/layout-land/.svn/text-base/activity_home.xml.svn-base
new file mode 100644
index 0000000..6e90355
--- /dev/null
+++ b/res/layout-land/.svn/text-base/activity_home.xml.svn-base
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout-land/activity_home.xml b/res/layout-land/activity_home.xml
new file mode 100644
index 0000000..6e90355
--- /dev/null
+++ b/res/layout-land/activity_home.xml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/all-wcprops b/res/layout/.svn/all-wcprops
new file mode 100644
index 0000000..6a699da
--- /dev/null
+++ b/res/layout/.svn/all-wcprops
@@ -0,0 +1,209 @@
+K 25
+svn:wc:ra_dav:version-url
+V 32
+/svn/!svn/ver/2/trunk/res/layout
+END
+activity_starred.xml
+K 25
+svn:wc:ra_dav:version-url
+V 53
+/svn/!svn/ver/2/trunk/res/layout/activity_starred.xml
+END
+activity_session_detail.xml
+K 25
+svn:wc:ra_dav:version-url
+V 60
+/svn/!svn/ver/2/trunk/res/layout/activity_session_detail.xml
+END
+activity_search.xml
+K 25
+svn:wc:ra_dav:version-url
+V 52
+/svn/!svn/ver/2/trunk/res/layout/activity_search.xml
+END
+activity_sessions.xml
+K 25
+svn:wc:ra_dav:version-url
+V 54
+/svn/!svn/ver/2/trunk/res/layout/activity_sessions.xml
+END
+activity_blocks_content.xml
+K 25
+svn:wc:ra_dav:version-url
+V 60
+/svn/!svn/ver/2/trunk/res/layout/activity_blocks_content.xml
+END
+activity_track_details.xml
+K 25
+svn:wc:ra_dav:version-url
+V 59
+/svn/!svn/ver/2/trunk/res/layout/activity_track_details.xml
+END
+activity_map.xml
+K 25
+svn:wc:ra_dav:version-url
+V 49
+/svn/!svn/ver/2/trunk/res/layout/activity_map.xml
+END
+activity_vendor_detail.xml
+K 25
+svn:wc:ra_dav:version-url
+V 59
+/svn/!svn/ver/2/trunk/res/layout/activity_vendor_detail.xml
+END
+activity_vendors.xml
+K 25
+svn:wc:ra_dav:version-url
+V 53
+/svn/!svn/ver/2/trunk/res/layout/activity_vendors.xml
+END
+list_item_note_create.xml
+K 25
+svn:wc:ra_dav:version-url
+V 58
+/svn/!svn/ver/2/trunk/res/layout/list_item_note_create.xml
+END
+tab_indicator.xml
+K 25
+svn:wc:ra_dav:version-url
+V 50
+/svn/!svn/ver/2/trunk/res/layout/tab_indicator.xml
+END
+activity_notes.xml
+K 25
+svn:wc:ra_dav:version-url
+V 51
+/svn/!svn/ver/2/trunk/res/layout/activity_notes.xml
+END
+list_item_vendor.xml
+K 25
+svn:wc:ra_dav:version-url
+V 53
+/svn/!svn/ver/2/trunk/res/layout/list_item_vendor.xml
+END
+localize_on_demand.xml
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/2/trunk/res/layout/localize_on_demand.xml
+END
+now_playing_after.xml
+K 25
+svn:wc:ra_dav:version-url
+V 54
+/svn/!svn/ver/2/trunk/res/layout/now_playing_after.xml
+END
+now_playing_before.xml
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/2/trunk/res/layout/now_playing_before.xml
+END
+activity_schedule.xml
+K 25
+svn:wc:ra_dav:version-url
+V 54
+/svn/!svn/ver/2/trunk/res/layout/activity_schedule.xml
+END
+activity_sessions_content.xml
+K 25
+svn:wc:ra_dav:version-url
+V 62
+/svn/!svn/ver/2/trunk/res/layout/activity_sessions_content.xml
+END
+speaker_detail.xml
+K 25
+svn:wc:ra_dav:version-url
+V 51
+/svn/!svn/ver/2/trunk/res/layout/speaker_detail.xml
+END
+list_item_note.xml
+K 25
+svn:wc:ra_dav:version-url
+V 51
+/svn/!svn/ver/2/trunk/res/layout/list_item_note.xml
+END
+tab_session_moderator.xml
+K 25
+svn:wc:ra_dav:version-url
+V 58
+/svn/!svn/ver/2/trunk/res/layout/tab_session_moderator.xml
+END
+activity_vendors_content.xml
+K 25
+svn:wc:ra_dav:version-url
+V 61
+/svn/!svn/ver/2/trunk/res/layout/activity_vendors_content.xml
+END
+activity_note_edit.xml
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/2/trunk/res/layout/activity_note_edit.xml
+END
+tab_session_summary.xml
+K 25
+svn:wc:ra_dav:version-url
+V 56
+/svn/!svn/ver/2/trunk/res/layout/tab_session_summary.xml
+END
+list_item_session.xml
+K 25
+svn:wc:ra_dav:version-url
+V 54
+/svn/!svn/ver/2/trunk/res/layout/list_item_session.xml
+END
+now_playing_during.xml
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/2/trunk/res/layout/now_playing_during.xml
+END
+activity_notes_content.xml
+K 25
+svn:wc:ra_dav:version-url
+V 59
+/svn/!svn/ver/2/trunk/res/layout/activity_notes_content.xml
+END
+list_section.xml
+K 25
+svn:wc:ra_dav:version-url
+V 49
+/svn/!svn/ver/2/trunk/res/layout/list_section.xml
+END
+colorstrip.xml
+K 25
+svn:wc:ra_dav:version-url
+V 47
+/svn/!svn/ver/2/trunk/res/layout/colorstrip.xml
+END
+activity_tracks.xml
+K 25
+svn:wc:ra_dav:version-url
+V 52
+/svn/!svn/ver/2/trunk/res/layout/activity_tracks.xml
+END
+activity_home.xml
+K 25
+svn:wc:ra_dav:version-url
+V 50
+/svn/!svn/ver/2/trunk/res/layout/activity_home.xml
+END
+titlebar.xml
+K 25
+svn:wc:ra_dav:version-url
+V 45
+/svn/!svn/ver/2/trunk/res/layout/titlebar.xml
+END
+tab_track_summary.xml
+K 25
+svn:wc:ra_dav:version-url
+V 54
+/svn/!svn/ver/2/trunk/res/layout/tab_track_summary.xml
+END
+list_item_track.xml
+K 25
+svn:wc:ra_dav:version-url
+V 52
+/svn/!svn/ver/2/trunk/res/layout/list_item_track.xml
+END
diff --git a/res/layout/.svn/entries b/res/layout/.svn/entries
new file mode 100644
index 0000000..b02c744
--- /dev/null
+++ b/res/layout/.svn/entries
@@ -0,0 +1,1184 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/res/layout
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+activity_starred.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+f7a53b80ad6e3c87fcc94f8d4c378f17
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2488
+
+activity_session_detail.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+b306bc8bca08ef798acad300b53d4e4a
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3373
+
+activity_search.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+f7a53b80ad6e3c87fcc94f8d4c378f17
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2488
+
+activity_sessions.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+7fa8a7739d2ce986c3ba9f4d2bfc759a
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1604
+
+activity_blocks_content.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+36ab8c107551cd033f95e4bc8713ef81
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1971
+
+activity_track_details.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+fe61596720c332b503e6bb99f67a6388
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2627
+
+activity_map.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+2e0a0725550da07813be4c239bf14a88
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2318
+
+activity_vendor_detail.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+43b932b3999ddff2a01c46d4abed1623
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3602
+
+activity_vendors.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+8424bb4bc2dabcf95ab1f7fa685303cb
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1604
+
+list_item_note_create.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+bc5ee8242598cc27c2b541ff655150af
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1124
+
+tab_indicator.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+d8ace219fbe6a5081c2b689032abd880
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1005
+
+activity_notes.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+2a03ef9bcf20bd4a43b0efc29190bcbd
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1875
+
+list_item_vendor.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+f561f6e08f3af4b5fa2d9bd93e522be9
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2087
+
+localize_on_demand.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+4d5775c61c1b97c618369ae732264aa6
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1476
+
+now_playing_after.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+9aae73d4ed588efd43a0ff43aba82b59
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2786
+
+now_playing_before.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+afc64010962a476e62d5d300ac524aa4
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2816
+
+activity_schedule.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+1fb2f3e470e80fd20a692ad06ba7fbfb
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2567
+
+activity_sessions_content.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+ca906983de9e068fdcec48a21ca33d84
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1240
+
+speaker_detail.xml
+file
+
+
+
+
+2010-06-03T15:23:16.942680Z
+0d0ea43684bf26efc0b6d910c19cda0b
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1278
+
+list_item_note.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+57059dcd85751c11d7051fecfbdb7ebb
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1413
+
+tab_session_moderator.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+ebf25a00eab853400d911734382d1823
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3448
+
+activity_vendors_content.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+fa48c2b81d91b5e5034ba3853fe432b4
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1280
+
+activity_note_edit.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+d7426ea85c2b4fab13f2ce831379d917
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2579
+
+tab_session_summary.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+6281c5329422e28cec04889383549435
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3046
+
+list_item_session.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+d74a1cd2713419f349889821537f7aae
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2093
+
+now_playing_during.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+9c4f9414c75e2f0391dbfc38efee99d7
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2758
+
+activity_notes_content.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+b0ab781f2b72971a90262c7636e6cfbb
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1237
+
+list_section.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+54819a2a8ea306844d1dd06d4c2114a9
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1274
+
+colorstrip.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+6cffa8191b33698585fc55fff6c6c845
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+846
+
+activity_tracks.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+f24172c49bd9f819b11ec347e1b3c7cc
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1687
+
+activity_home.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+e686f0df87ad4723a65c8c6ed8290257
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5362
+
+titlebar.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+762495c30c0ac81fa26db61e67df4dca
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4818
+
+tab_track_summary.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+892f002f403ec96c0baf4b62ce6a4e73
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1200
+
+list_item_track.xml
+file
+
+
+
+
+2010-06-03T15:23:16.946653Z
+eff0b69ce9f9a1728964e5da186cfffd
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1566
+
diff --git a/res/layout/.svn/prop-base/localize_on_demand.xml.svn-base b/res/layout/.svn/prop-base/localize_on_demand.xml.svn-base
new file mode 100644
index 0000000..869ac71
--- /dev/null
+++ b/res/layout/.svn/prop-base/localize_on_demand.xml.svn-base
@@ -0,0 +1,5 @@
+K 14
+svn:executable
+V 1
+*
+END
diff --git a/res/layout/.svn/text-base/activity_blocks_content.xml.svn-base b/res/layout/.svn/text-base/activity_blocks_content.xml.svn-base
new file mode 100644
index 0000000..f93be93
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_blocks_content.xml.svn-base
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_home.xml.svn-base b/res/layout/.svn/text-base/activity_home.xml.svn-base
new file mode 100644
index 0000000..4dbeff0
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_home.xml.svn-base
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_map.xml.svn-base b/res/layout/.svn/text-base/activity_map.xml.svn-base
new file mode 100644
index 0000000..d2b7fec
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_map.xml.svn-base
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_note_edit.xml.svn-base b/res/layout/.svn/text-base/activity_note_edit.xml.svn-base
new file mode 100644
index 0000000..a0d2214
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_note_edit.xml.svn-base
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_notes.xml.svn-base b/res/layout/.svn/text-base/activity_notes.xml.svn-base
new file mode 100644
index 0000000..c0e9927
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_notes.xml.svn-base
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_notes_content.xml.svn-base b/res/layout/.svn/text-base/activity_notes_content.xml.svn-base
new file mode 100644
index 0000000..248a3e1
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_notes_content.xml.svn-base
@@ -0,0 +1,31 @@
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_schedule.xml.svn-base b/res/layout/.svn/text-base/activity_schedule.xml.svn-base
new file mode 100644
index 0000000..c649a04
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_schedule.xml.svn-base
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_search.xml.svn-base b/res/layout/.svn/text-base/activity_search.xml.svn-base
new file mode 100644
index 0000000..12c9b16
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_search.xml.svn-base
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_session_detail.xml.svn-base b/res/layout/.svn/text-base/activity_session_detail.xml.svn-base
new file mode 100644
index 0000000..7e0374e
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_session_detail.xml.svn-base
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_sessions.xml.svn-base b/res/layout/.svn/text-base/activity_sessions.xml.svn-base
new file mode 100644
index 0000000..a0f024e
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_sessions.xml.svn-base
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_sessions_content.xml.svn-base b/res/layout/.svn/text-base/activity_sessions_content.xml.svn-base
new file mode 100644
index 0000000..814cdaa
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_sessions_content.xml.svn-base
@@ -0,0 +1,31 @@
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_starred.xml.svn-base b/res/layout/.svn/text-base/activity_starred.xml.svn-base
new file mode 100644
index 0000000..12c9b16
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_starred.xml.svn-base
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_track_details.xml.svn-base b/res/layout/.svn/text-base/activity_track_details.xml.svn-base
new file mode 100644
index 0000000..0e3ff85
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_track_details.xml.svn-base
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_tracks.xml.svn-base b/res/layout/.svn/text-base/activity_tracks.xml.svn-base
new file mode 100644
index 0000000..d2630d6
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_tracks.xml.svn-base
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_vendor_detail.xml.svn-base b/res/layout/.svn/text-base/activity_vendor_detail.xml.svn-base
new file mode 100644
index 0000000..dd58449
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_vendor_detail.xml.svn-base
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_vendors.xml.svn-base b/res/layout/.svn/text-base/activity_vendors.xml.svn-base
new file mode 100644
index 0000000..92a88bd
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_vendors.xml.svn-base
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/activity_vendors_content.xml.svn-base b/res/layout/.svn/text-base/activity_vendors_content.xml.svn-base
new file mode 100644
index 0000000..8c3f939
--- /dev/null
+++ b/res/layout/.svn/text-base/activity_vendors_content.xml.svn-base
@@ -0,0 +1,32 @@
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/colorstrip.xml.svn-base b/res/layout/.svn/text-base/colorstrip.xml.svn-base
new file mode 100644
index 0000000..b7cdf99
--- /dev/null
+++ b/res/layout/.svn/text-base/colorstrip.xml.svn-base
@@ -0,0 +1,20 @@
+
+
+
diff --git a/res/layout/.svn/text-base/list_item_note.xml.svn-base b/res/layout/.svn/text-base/list_item_note.xml.svn-base
new file mode 100644
index 0000000..a4dc8e1
--- /dev/null
+++ b/res/layout/.svn/text-base/list_item_note.xml.svn-base
@@ -0,0 +1,38 @@
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/list_item_note_create.xml.svn-base b/res/layout/.svn/text-base/list_item_note_create.xml.svn-base
new file mode 100644
index 0000000..13fa055
--- /dev/null
+++ b/res/layout/.svn/text-base/list_item_note_create.xml.svn-base
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/list_item_session.xml.svn-base b/res/layout/.svn/text-base/list_item_session.xml.svn-base
new file mode 100644
index 0000000..390fe13
--- /dev/null
+++ b/res/layout/.svn/text-base/list_item_session.xml.svn-base
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/list_item_track.xml.svn-base b/res/layout/.svn/text-base/list_item_track.xml.svn-base
new file mode 100644
index 0000000..0553d8c
--- /dev/null
+++ b/res/layout/.svn/text-base/list_item_track.xml.svn-base
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/list_item_vendor.xml.svn-base b/res/layout/.svn/text-base/list_item_vendor.xml.svn-base
new file mode 100644
index 0000000..338439a
--- /dev/null
+++ b/res/layout/.svn/text-base/list_item_vendor.xml.svn-base
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/list_section.xml.svn-base b/res/layout/.svn/text-base/list_section.xml.svn-base
new file mode 100644
index 0000000..16358da
--- /dev/null
+++ b/res/layout/.svn/text-base/list_section.xml.svn-base
@@ -0,0 +1,32 @@
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/localize_on_demand.xml.svn-base b/res/layout/.svn/text-base/localize_on_demand.xml.svn-base
new file mode 100644
index 0000000..ea40dd8
--- /dev/null
+++ b/res/layout/.svn/text-base/localize_on_demand.xml.svn-base
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/now_playing_after.xml.svn-base b/res/layout/.svn/text-base/now_playing_after.xml.svn-base
new file mode 100644
index 0000000..dfa3650
--- /dev/null
+++ b/res/layout/.svn/text-base/now_playing_after.xml.svn-base
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/now_playing_before.xml.svn-base b/res/layout/.svn/text-base/now_playing_before.xml.svn-base
new file mode 100644
index 0000000..f4bfb67
--- /dev/null
+++ b/res/layout/.svn/text-base/now_playing_before.xml.svn-base
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/now_playing_during.xml.svn-base b/res/layout/.svn/text-base/now_playing_during.xml.svn-base
new file mode 100644
index 0000000..2aa4b48
--- /dev/null
+++ b/res/layout/.svn/text-base/now_playing_during.xml.svn-base
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/speaker_detail.xml.svn-base b/res/layout/.svn/text-base/speaker_detail.xml.svn-base
new file mode 100644
index 0000000..729291e
--- /dev/null
+++ b/res/layout/.svn/text-base/speaker_detail.xml.svn-base
@@ -0,0 +1,33 @@
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/tab_indicator.xml.svn-base b/res/layout/.svn/text-base/tab_indicator.xml.svn-base
new file mode 100644
index 0000000..eb9654c
--- /dev/null
+++ b/res/layout/.svn/text-base/tab_indicator.xml.svn-base
@@ -0,0 +1,26 @@
+
+
+
diff --git a/res/layout/.svn/text-base/tab_session_moderator.xml.svn-base b/res/layout/.svn/text-base/tab_session_moderator.xml.svn-base
new file mode 100644
index 0000000..f89bb64
--- /dev/null
+++ b/res/layout/.svn/text-base/tab_session_moderator.xml.svn-base
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/tab_session_summary.xml.svn-base b/res/layout/.svn/text-base/tab_session_summary.xml.svn-base
new file mode 100644
index 0000000..67b7033
--- /dev/null
+++ b/res/layout/.svn/text-base/tab_session_summary.xml.svn-base
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/tab_track_summary.xml.svn-base b/res/layout/.svn/text-base/tab_track_summary.xml.svn-base
new file mode 100644
index 0000000..3af7226
--- /dev/null
+++ b/res/layout/.svn/text-base/tab_track_summary.xml.svn-base
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/res/layout/.svn/text-base/titlebar.xml.svn-base b/res/layout/.svn/text-base/titlebar.xml.svn-base
new file mode 100644
index 0000000..8fdd9fe
--- /dev/null
+++ b/res/layout/.svn/text-base/titlebar.xml.svn-base
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_blocks_content.xml b/res/layout/activity_blocks_content.xml
new file mode 100644
index 0000000..f93be93
--- /dev/null
+++ b/res/layout/activity_blocks_content.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_home.xml b/res/layout/activity_home.xml
new file mode 100644
index 0000000..4dbeff0
--- /dev/null
+++ b/res/layout/activity_home.xml
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_map.xml b/res/layout/activity_map.xml
new file mode 100644
index 0000000..d2b7fec
--- /dev/null
+++ b/res/layout/activity_map.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_note_edit.xml b/res/layout/activity_note_edit.xml
new file mode 100644
index 0000000..a0d2214
--- /dev/null
+++ b/res/layout/activity_note_edit.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_notes.xml b/res/layout/activity_notes.xml
new file mode 100644
index 0000000..c0e9927
--- /dev/null
+++ b/res/layout/activity_notes.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_notes_content.xml b/res/layout/activity_notes_content.xml
new file mode 100644
index 0000000..248a3e1
--- /dev/null
+++ b/res/layout/activity_notes_content.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
diff --git a/res/layout/activity_schedule.xml b/res/layout/activity_schedule.xml
new file mode 100644
index 0000000..c649a04
--- /dev/null
+++ b/res/layout/activity_schedule.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_search.xml b/res/layout/activity_search.xml
new file mode 100644
index 0000000..12c9b16
--- /dev/null
+++ b/res/layout/activity_search.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_session_detail.xml b/res/layout/activity_session_detail.xml
new file mode 100644
index 0000000..7e0374e
--- /dev/null
+++ b/res/layout/activity_session_detail.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_sessions.xml b/res/layout/activity_sessions.xml
new file mode 100644
index 0000000..a0f024e
--- /dev/null
+++ b/res/layout/activity_sessions.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_sessions_content.xml b/res/layout/activity_sessions_content.xml
new file mode 100644
index 0000000..814cdaa
--- /dev/null
+++ b/res/layout/activity_sessions_content.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
diff --git a/res/layout/activity_starred.xml b/res/layout/activity_starred.xml
new file mode 100644
index 0000000..12c9b16
--- /dev/null
+++ b/res/layout/activity_starred.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_track_details.xml b/res/layout/activity_track_details.xml
new file mode 100644
index 0000000..0e3ff85
--- /dev/null
+++ b/res/layout/activity_track_details.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_tracks.xml b/res/layout/activity_tracks.xml
new file mode 100644
index 0000000..d2630d6
--- /dev/null
+++ b/res/layout/activity_tracks.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_vendor_detail.xml b/res/layout/activity_vendor_detail.xml
new file mode 100644
index 0000000..dd58449
--- /dev/null
+++ b/res/layout/activity_vendor_detail.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_vendors.xml b/res/layout/activity_vendors.xml
new file mode 100644
index 0000000..92a88bd
--- /dev/null
+++ b/res/layout/activity_vendors.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_vendors_content.xml b/res/layout/activity_vendors_content.xml
new file mode 100644
index 0000000..8c3f939
--- /dev/null
+++ b/res/layout/activity_vendors_content.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
diff --git a/res/layout/colorstrip.xml b/res/layout/colorstrip.xml
new file mode 100644
index 0000000..b7cdf99
--- /dev/null
+++ b/res/layout/colorstrip.xml
@@ -0,0 +1,20 @@
+
+
+
diff --git a/res/layout/list_item_note.xml b/res/layout/list_item_note.xml
new file mode 100644
index 0000000..a4dc8e1
--- /dev/null
+++ b/res/layout/list_item_note.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
diff --git a/res/layout/list_item_note_create.xml b/res/layout/list_item_note_create.xml
new file mode 100644
index 0000000..13fa055
--- /dev/null
+++ b/res/layout/list_item_note_create.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/res/layout/list_item_session.xml b/res/layout/list_item_session.xml
new file mode 100644
index 0000000..390fe13
--- /dev/null
+++ b/res/layout/list_item_session.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
diff --git a/res/layout/list_item_track.xml b/res/layout/list_item_track.xml
new file mode 100644
index 0000000..0553d8c
--- /dev/null
+++ b/res/layout/list_item_track.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/list_item_vendor.xml b/res/layout/list_item_vendor.xml
new file mode 100644
index 0000000..338439a
--- /dev/null
+++ b/res/layout/list_item_vendor.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
diff --git a/res/layout/list_section.xml b/res/layout/list_section.xml
new file mode 100644
index 0000000..16358da
--- /dev/null
+++ b/res/layout/list_section.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
diff --git a/res/layout/localize_on_demand.xml b/res/layout/localize_on_demand.xml
new file mode 100755
index 0000000..ea40dd8
--- /dev/null
+++ b/res/layout/localize_on_demand.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
diff --git a/res/layout/now_playing_after.xml b/res/layout/now_playing_after.xml
new file mode 100644
index 0000000..dfa3650
--- /dev/null
+++ b/res/layout/now_playing_after.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/now_playing_before.xml b/res/layout/now_playing_before.xml
new file mode 100644
index 0000000..f4bfb67
--- /dev/null
+++ b/res/layout/now_playing_before.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/now_playing_during.xml b/res/layout/now_playing_during.xml
new file mode 100644
index 0000000..2aa4b48
--- /dev/null
+++ b/res/layout/now_playing_during.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/speaker_detail.xml b/res/layout/speaker_detail.xml
new file mode 100644
index 0000000..729291e
--- /dev/null
+++ b/res/layout/speaker_detail.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
diff --git a/res/layout/tab_indicator.xml b/res/layout/tab_indicator.xml
new file mode 100644
index 0000000..eb9654c
--- /dev/null
+++ b/res/layout/tab_indicator.xml
@@ -0,0 +1,26 @@
+
+
+
diff --git a/res/layout/tab_session_moderator.xml b/res/layout/tab_session_moderator.xml
new file mode 100644
index 0000000..f89bb64
--- /dev/null
+++ b/res/layout/tab_session_moderator.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/tab_session_summary.xml b/res/layout/tab_session_summary.xml
new file mode 100644
index 0000000..67b7033
--- /dev/null
+++ b/res/layout/tab_session_summary.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/tab_track_summary.xml b/res/layout/tab_track_summary.xml
new file mode 100644
index 0000000..3af7226
--- /dev/null
+++ b/res/layout/tab_track_summary.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/res/layout/titlebar.xml b/res/layout/titlebar.xml
new file mode 100644
index 0000000..8fdd9fe
--- /dev/null
+++ b/res/layout/titlebar.xml
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/.svn/all-wcprops b/res/values/.svn/all-wcprops
new file mode 100644
index 0000000..1a8637d
--- /dev/null
+++ b/res/values/.svn/all-wcprops
@@ -0,0 +1,47 @@
+K 25
+svn:wc:ra_dav:version-url
+V 32
+/svn/!svn/ver/2/trunk/res/values
+END
+colors.xml
+K 25
+svn:wc:ra_dav:version-url
+V 43
+/svn/!svn/ver/2/trunk/res/values/colors.xml
+END
+styles.xml
+K 25
+svn:wc:ra_dav:version-url
+V 43
+/svn/!svn/ver/2/trunk/res/values/styles.xml
+END
+config.xml
+K 25
+svn:wc:ra_dav:version-url
+V 43
+/svn/!svn/ver/2/trunk/res/values/config.xml
+END
+strings.xml
+K 25
+svn:wc:ra_dav:version-url
+V 44
+/svn/!svn/ver/2/trunk/res/values/strings.xml
+END
+attrs.xml
+K 25
+svn:wc:ra_dav:version-url
+V 42
+/svn/!svn/ver/2/trunk/res/values/attrs.xml
+END
+dimens.xml
+K 25
+svn:wc:ra_dav:version-url
+V 43
+/svn/!svn/ver/2/trunk/res/values/dimens.xml
+END
+ids.xml
+K 25
+svn:wc:ra_dav:version-url
+V 40
+/svn/!svn/ver/2/trunk/res/values/ids.xml
+END
diff --git a/res/values/.svn/entries b/res/values/.svn/entries
new file mode 100644
index 0000000..7b25971
--- /dev/null
+++ b/res/values/.svn/entries
@@ -0,0 +1,266 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/res/values
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+colors.xml
+file
+
+
+
+
+2010-06-03T15:23:15.565621Z
+3c3a9ba7cd1b676ffb6c47846038cd72
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1232
+
+styles.xml
+file
+
+
+
+
+2010-06-03T15:23:15.565621Z
+4b5519d5965f0aae7d695e020706228c
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6275
+
+config.xml
+file
+
+
+
+
+2010-06-03T15:23:15.565621Z
+8e3b233d0f9188c3baf30b52f8e58bf3
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+725
+
+strings.xml
+file
+
+
+
+
+2010-06-03T15:23:15.565621Z
+c7b55a5f64184f5b2aa731beffa4308e
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6683
+
+attrs.xml
+file
+
+
+
+
+2010-06-03T15:23:15.565621Z
+3eeb45e693de8c774bc7d6f45f2c04fd
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1344
+
+dimens.xml
+file
+
+
+
+
+2010-06-03T15:23:15.569622Z
+4222d4263eb00732ab41dba48e60dbf9
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+953
+
+ids.xml
+file
+
+
+
+
+2010-06-03T15:23:15.569622Z
+71a81a12560b6e9c4fe4f2188e6459a6
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1024
+
diff --git a/res/values/.svn/text-base/attrs.xml.svn-base b/res/values/.svn/text-base/attrs.xml.svn-base
new file mode 100644
index 0000000..64f4eda
--- /dev/null
+++ b/res/values/.svn/text-base/attrs.xml.svn-base
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/.svn/text-base/colors.xml.svn-base b/res/values/.svn/text-base/colors.xml.svn-base
new file mode 100644
index 0000000..82feef2
--- /dev/null
+++ b/res/values/.svn/text-base/colors.xml.svn-base
@@ -0,0 +1,31 @@
+
+
+
+ #ff355689
+ #ffffffff
+ #ff000000
+ #ff355689
+ #ff7081a3
+ #ffffffff
+ #ffd5ddeb
+ #ffe3e8f1
+ #40ffffff
+
+ #ff999999
+
+ #ff202020
+
diff --git a/res/values/.svn/text-base/config.xml.svn-base b/res/values/.svn/text-base/config.xml.svn-base
new file mode 100644
index 0000000..e0c61d5
--- /dev/null
+++ b/res/values/.svn/text-base/config.xml.svn-base
@@ -0,0 +1,19 @@
+
+
+
+ 350
+
diff --git a/res/values/.svn/text-base/dimens.xml.svn-base b/res/values/.svn/text-base/dimens.xml.svn-base
new file mode 100644
index 0000000..56e0a57
--- /dev/null
+++ b/res/values/.svn/text-base/dimens.xml.svn-base
@@ -0,0 +1,24 @@
+
+
+
+ 6dip
+ 45dip
+ 90dip
+ 14sp
+ 18sp
+ 22sp
+
diff --git a/res/values/.svn/text-base/ids.xml.svn-base b/res/values/.svn/text-base/ids.xml.svn-base
new file mode 100644
index 0000000..36214cb
--- /dev/null
+++ b/res/values/.svn/text-base/ids.xml.svn-base
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/.svn/text-base/strings.xml.svn-base b/res/values/.svn/text-base/strings.xml.svn-base
new file mode 100644
index 0000000..c635cb4
--- /dev/null
+++ b/res/values/.svn/text-base/strings.xml.svn-base
@@ -0,0 +1,133 @@
+
+
+
+ Google I/O 2010
+
+ Google I/O 2010
+ Home
+ Search
+ Map
+ Refresh
+ Share
+ Export
+ Star
+
+ Now Playing
+ Schedule
+ Sessions
+ Starred
+ Tracks
+ Sandbox
+ Map
+ Notes
+ Edit note
+
+ Session tracks
+ Sandbox tracks
+
+ Share using
+
+ Search
+ Search for \'%1$s\'
+
+ Schedule
+ Map
+ Sessions
+ Starred
+ Sandbox
+ My Notes
+
+ Sync error: %1$s
+
+ %1$s in %2$s
+ %1$s questions submitted and %2$s votes cast.
+
+ No sessions found
+ No session details found
+ No sandbox results found
+ No notes found
+ No search results found
+
+ Summary
+ Notes
+ Moderator
+ Interact
+ Requirements
+ Speakers
+
+ Summary
+ Sessions
+ Sandbox
+
+ Sessions
+ Sandbox
+
+ Sessions
+ Sandbox
+
+ Google I/O
+ Session keywords
+
+ Checking out \'%1$s\' at Google #io2010 %2$s
+ Exported notes from Google I/O 2010
+
+ Moderator
+ Use Moderator to ask questions during this session, and vote up/down other questions.
+ Wave
+ Use Wave to collaborate in real-time with others during this session.
+
+ Google Moderator
+ The official Google Moderator app is available in Market, would you like to install it or continue using a normal browser?
+
+ Use Browser
+ Visit Market
+
+ Google Wave
+ The Google Wave mobile interface is still in beta, and using a desktop or laptop is recommended for the best experience.\n\nContinue to Google Wave anyway?
+
+ %1$s, %2$s
+
+ Save
+ Discard
+
+ Create new note
+ Enter note
+
+ Discard note?
+ Are you sure you want to discard this note, deleting any contents?
+
+ %1$s before session
+ %1$s into session
+ %1$s after session
+
+ Looking up nearby sessions\u2026
+ No sessions nearby.
+ Tap here to enable Wi-Fi for indoor location.
+
+
+ %2$s
+ %1$s day, %2$s
+ %1$s days, %2$s
+
+
+ Countdown to Google I/O!
+ See you next year!
+ Thanks for tuning in.
+
+ Modify Google I/O schedule data
+
+
diff --git a/res/values/.svn/text-base/styles.xml.svn-base b/res/values/.svn/text-base/styles.xml.svn-base
new file mode 100644
index 0000000..e860a4b
--- /dev/null
+++ b/res/values/.svn/text-base/styles.xml.svn-base
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
new file mode 100644
index 0000000..64f4eda
--- /dev/null
+++ b/res/values/attrs.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/colors.xml b/res/values/colors.xml
new file mode 100644
index 0000000..82feef2
--- /dev/null
+++ b/res/values/colors.xml
@@ -0,0 +1,31 @@
+
+
+
+ #ff355689
+ #ffffffff
+ #ff000000
+ #ff355689
+ #ff7081a3
+ #ffffffff
+ #ffd5ddeb
+ #ffe3e8f1
+ #40ffffff
+
+ #ff999999
+
+ #ff202020
+
diff --git a/res/values/config.xml b/res/values/config.xml
new file mode 100644
index 0000000..e0c61d5
--- /dev/null
+++ b/res/values/config.xml
@@ -0,0 +1,19 @@
+
+
+
+ 350
+
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
new file mode 100644
index 0000000..56e0a57
--- /dev/null
+++ b/res/values/dimens.xml
@@ -0,0 +1,24 @@
+
+
+
+ 6dip
+ 45dip
+ 90dip
+ 14sp
+ 18sp
+ 22sp
+
diff --git a/res/values/ids.xml b/res/values/ids.xml
new file mode 100644
index 0000000..36214cb
--- /dev/null
+++ b/res/values/ids.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 0000000..c635cb4
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,133 @@
+
+
+
+ Google I/O 2010
+
+ Google I/O 2010
+ Home
+ Search
+ Map
+ Refresh
+ Share
+ Export
+ Star
+
+ Now Playing
+ Schedule
+ Sessions
+ Starred
+ Tracks
+ Sandbox
+ Map
+ Notes
+ Edit note
+
+ Session tracks
+ Sandbox tracks
+
+ Share using
+
+ Search
+ Search for \'%1$s\'
+
+ Schedule
+ Map
+ Sessions
+ Starred
+ Sandbox
+ My Notes
+
+ Sync error: %1$s
+
+ %1$s in %2$s
+ %1$s questions submitted and %2$s votes cast.
+
+ No sessions found
+ No session details found
+ No sandbox results found
+ No notes found
+ No search results found
+
+ Summary
+ Notes
+ Moderator
+ Interact
+ Requirements
+ Speakers
+
+ Summary
+ Sessions
+ Sandbox
+
+ Sessions
+ Sandbox
+
+ Sessions
+ Sandbox
+
+ Google I/O
+ Session keywords
+
+ Checking out \'%1$s\' at Google #io2010 %2$s
+ Exported notes from Google I/O 2010
+
+ Moderator
+ Use Moderator to ask questions during this session, and vote up/down other questions.
+ Wave
+ Use Wave to collaborate in real-time with others during this session.
+
+ Google Moderator
+ The official Google Moderator app is available in Market, would you like to install it or continue using a normal browser?
+
+ Use Browser
+ Visit Market
+
+ Google Wave
+ The Google Wave mobile interface is still in beta, and using a desktop or laptop is recommended for the best experience.\n\nContinue to Google Wave anyway?
+
+ %1$s, %2$s
+
+ Save
+ Discard
+
+ Create new note
+ Enter note
+
+ Discard note?
+ Are you sure you want to discard this note, deleting any contents?
+
+ %1$s before session
+ %1$s into session
+ %1$s after session
+
+ Looking up nearby sessions\u2026
+ No sessions nearby.
+ Tap here to enable Wi-Fi for indoor location.
+
+
+ %2$s
+ %1$s day, %2$s
+ %1$s days, %2$s
+
+
+ Countdown to Google I/O!
+ See you next year!
+ Thanks for tuning in.
+
+ Modify Google I/O schedule data
+
+
diff --git a/res/values/styles.xml b/res/values/styles.xml
new file mode 100644
index 0000000..e860a4b
--- /dev/null
+++ b/res/values/styles.xml
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/.svn/all-wcprops b/res/xml/.svn/all-wcprops
new file mode 100644
index 0000000..bf09d64
--- /dev/null
+++ b/res/xml/.svn/all-wcprops
@@ -0,0 +1,41 @@
+K 25
+svn:wc:ra_dav:version-url
+V 29
+/svn/!svn/ver/2/trunk/res/xml
+END
+search_suggest.xml
+K 25
+svn:wc:ra_dav:version-url
+V 48
+/svn/!svn/ver/2/trunk/res/xml/search_suggest.xml
+END
+sessions.xml
+K 25
+svn:wc:ra_dav:version-url
+V 42
+/svn/!svn/ver/2/trunk/res/xml/sessions.xml
+END
+tracks.xml
+K 25
+svn:wc:ra_dav:version-url
+V 40
+/svn/!svn/ver/2/trunk/res/xml/tracks.xml
+END
+searchable.xml
+K 25
+svn:wc:ra_dav:version-url
+V 44
+/svn/!svn/ver/2/trunk/res/xml/searchable.xml
+END
+blocks.xml
+K 25
+svn:wc:ra_dav:version-url
+V 40
+/svn/!svn/ver/2/trunk/res/xml/blocks.xml
+END
+rooms.xml
+K 25
+svn:wc:ra_dav:version-url
+V 39
+/svn/!svn/ver/2/trunk/res/xml/rooms.xml
+END
diff --git a/res/xml/.svn/entries b/res/xml/.svn/entries
new file mode 100644
index 0000000..cca8ffe
--- /dev/null
+++ b/res/xml/.svn/entries
@@ -0,0 +1,232 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/res/xml
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+search_suggest.xml
+file
+
+
+
+
+2010-06-03T15:23:15.597622Z
+7c1c4ad84f88767603719e0a5ca28c3a
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2497
+
+sessions.xml
+file
+
+
+
+
+2010-06-03T15:23:15.597622Z
+80e8e9473a1ed0d17d1ea58b459cf2f9
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7842
+
+tracks.xml
+file
+
+
+
+
+2010-06-03T15:23:15.597622Z
+7ccb4a7510df35fd4f3147720f22673f
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6042
+
+searchable.xml
+file
+
+
+
+
+2010-06-03T15:23:15.597622Z
+732112e3e5f620e372a9db0ddd61f939
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+923
+
+blocks.xml
+file
+
+
+
+
+2010-06-03T15:23:15.597622Z
+5552ab7cf5377b13522dad7fc89c8d7c
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3927
+
+rooms.xml
+file
+
+
+
+
+2010-06-03T15:23:15.597622Z
+863081f5fd7b46cfbfff494b536968d3
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1986
+
diff --git a/res/xml/.svn/text-base/blocks.xml.svn-base b/res/xml/.svn/text-base/blocks.xml.svn-base
new file mode 100644
index 0000000..016a75b
--- /dev/null
+++ b/res/xml/.svn/text-base/blocks.xml.svn-base
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+ 2010-05-18T12:00:00.000-07:00
+ 2010-05-18T19:00:00.000-07:00
+ On-site registration
+
+
+ 2010-05-18T14:00:00.000-07:00
+ 2010-05-18T20:00:00.000-07:00
+ BootCamp
+
+
+
+
+ 2010-05-19T07:00:00.000-07:00
+ 2010-05-19T22:00:00.000-07:00
+ On-site registration
+
+
+ 2010-05-19T07:30:00.000-07:00
+ 2010-05-19T10:00:00.000-07:00
+ Breakfast
+ food
+
+
+ 2010-05-19T09:00:00.000-07:00
+ 2010-05-19T10:30:00.000-07:00
+ Keynote
+ session
+
+
+ 2010-05-19T10:30:00.000-07:00
+ 2010-05-19T18:30:00.000-07:00
+ Developer Sandbox
+
+
+ 2010-05-19T11:45:00.000-07:00
+ 2010-05-19T13:30:00.000-07:00
+ Lunch served
+ food
+
+
+ 2010-05-19T12:00:00.000-07:00
+ 2010-05-19T14:30:00.000-07:00
+ Office hours
+ officehours
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office hours
+ officehours
+
+
+ 2010-05-19T18:30:00.000-07:00
+ 2010-05-19T22:30:00.000-07:00
+ After Hours evening party
+ food
+
+
+
+
+ 2010-05-20T07:00:00.000-07:00
+ 2010-05-20T16:00:00.000-07:00
+ On-site registration
+
+
+ 2010-05-20T08:00:00.000-07:00
+ 2010-05-20T09:30:00.000-07:00
+ Breakfast
+ food
+
+
+ 2010-05-20T08:30:00.000-07:00
+ 2010-05-20T10:00:00.000-07:00
+ Keynote
+ session
+
+
+ 2010-05-20T10:00:00.000-07:00
+ 2010-05-20T18:00:00.000-07:00
+ Developer Sandbox
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T14:00:00.000-07:00
+ Lunch served
+ food
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office hours
+ officehours
+
+
+ 2010-05-20T15:00:00.000-07:00
+ 2010-05-20T17:30:00.000-07:00
+ Office hours
+ officehours
+
+
+
diff --git a/res/xml/.svn/text-base/rooms.xml.svn-base b/res/xml/.svn/text-base/rooms.xml.svn-base
new file mode 100644
index 0000000..6bf2f6d
--- /dev/null
+++ b/res/xml/.svn/text-base/rooms.xml.svn-base
@@ -0,0 +1,39 @@
+
+
+
+
+ 1Room 12
+ 2Room 22
+ 3Room 32
+ 4Room 42
+ 5Room 52
+ 6Room 62
+ 7Room 72
+ 8Room 82
+ 9Room 92
+
+ firesidechatroomFireside Chat Room2
+
+ officehoursspaceaOffice Hours Space A2
+ officehoursspacebOffice Hours Space B2
+ officehoursspacecOffice Hours Space C2
+ officehoursspacedOffice Hours Space D2
+ officehoursspaceeOffice Hours Space E2
+ officehoursspacefOffice Hours Space F2
+ officehoursspacegOffice Hours Space G2
+
+
diff --git a/res/xml/.svn/text-base/search_suggest.xml.svn-base b/res/xml/.svn/text-base/search_suggest.xml.svn-base
new file mode 100644
index 0000000..65d4c35
--- /dev/null
+++ b/res/xml/.svn/text-base/search_suggest.xml.svn-base
@@ -0,0 +1,106 @@
+
+
+
+activitystrea.ms
+adsense
+ajax
+analytics
+android
+api
+app engine
+applications
+apps
+atom
+background
+build
+business
+buzz
+calendar
+charts
+chat
+chrome
+closure
+cloud
+compiler
+dalvik
+data
+datastore
+design
+desktop
+development
+docs
+earth
+embedded
+enterprise
+extension
+extensions
+fireside
+framework
+friends
+gadgets
+gaming
+geo
+geospatial
+gmail
+go
+google
+gwt
+handset
+html
+igoogle
+iphone
+java
+javascript
+kml
+library
+maps
+marketplace
+mobile
+monetizing
+native
+oauth
+open
+openid
+opensocial
+performance
+platform
+power
+powermeter
+pubsubhubbub
+python
+restful
+robots
+rss
+saas
+scalable
+scripting
+search
+seo
+server
+share
+sites
+sketchup
+social
+source
+speed
+team
+testing
+toolkit
+wave
+webfinger
+youtube
+
diff --git a/res/xml/.svn/text-base/searchable.xml.svn-base b/res/xml/.svn/text-base/searchable.xml.svn-base
new file mode 100644
index 0000000..b189a65
--- /dev/null
+++ b/res/xml/.svn/text-base/searchable.xml.svn-base
@@ -0,0 +1,21 @@
+
+
+
diff --git a/res/xml/.svn/text-base/sessions.xml.svn-base b/res/xml/.svn/text-base/sessions.xml.svn-base
new file mode 100644
index 0000000..c1410f1
--- /dev/null
+++ b/res/xml/.svn/text-base/sessions.xml.svn-base
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+ 2010-05-19T12:00:00.000-07:00
+ 2010-05-19T14:30:00.000-07:00
+ Office Hours Space A
+
+ Enterprise Office Hours
+
+
+
+ 2010-05-19T12:00:00.000-07:00
+ 2010-05-19T14:30:00.000-07:00
+ Office Hours Space B
+ Go Office Hours
+
+
+
+ 2010-05-19T12:00:00.000-07:00
+ 2010-05-19T14:30:00.000-07:00
+ Office Hours Space C
+ Google Project Hosting Office Hours
+
+
+
+ 2010-05-19T12:00:00.000-07:00
+ 2010-05-19T14:30:00.000-07:00
+ Office Hours Space D
+
+ Social Web Office Hours
+
+
+
+ 2010-05-19T12:00:00.000-07:00
+ 2010-05-19T14:30:00.000-07:00
+
+ Office Hours Space E
+
+ Google APIs Office Hours
+
+
+
+ 2010-05-19T12:00:00.000-07:00
+ 2010-05-19T14:30:00.000-07:00
+ Office Hours Space G
+
+ App Engine Office Hours
+
+
+
+
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office Hours Space A
+
+ Chrome Office Hours
+
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office Hours Space B
+ Closure Compiler Office Hours
+
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office Hours Space C
+
+ Geo Office Hours
+
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office Hours Space D
+
+ Google Web Toolkit Office Hours
+
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office Hours Space E
+
+ Wave Office Hours
+
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office Hours Space F
+ Developer Docs Office Hours
+
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office Hours Space G
+
+ App Engine Office Hours 2
+
+
+
+
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office Hours Space A
+
+ Chrome Office Hours 2
+
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office Hours Space B
+
+ Android Office Hours
+
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office Hours Space C
+
+ Geo Office Hours 2
+
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office Hours Space D
+
+ Google Web Toolkit Office Hours 2
+
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office Hours Space E
+
+ Wave Office Hours 2
+
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office Hours Space F
+ Developer Docs Office Hours 2
+
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office Hours Space G
+
+ App Engine Office Hours 3
+
+
+
+
+
+
+ 2010-05-20T15:00:00.000-07:00
+ 2010-05-20T17:30:00.000-07:00
+ Office Hours Space A
+
+ Enterprise Office Hours 2
+
+
+
+ 2010-05-20T15:00:00.000-07:00
+ 2010-05-20T17:30:00.000-07:00
+ Office Hours Space B
+
+ Android Office Hours 2
+
+
+
+ 2010-05-20T15:00:00.000-07:00
+ 2010-05-20T17:30:00.000-07:00
+ Office Hours Space C
+ Google Project Hosting Office Hours 2
+
+
+
+ 2010-05-20T15:00:00.000-07:00
+ 2010-05-20T17:30:00.000-07:00
+ Office Hours Space D
+
+ Social Web Office Hours 2
+
+
+
+ 2010-05-20T15:00:00.000-07:00
+ 2010-05-20T17:30:00.000-07:00
+
+ Office Hours Space E
+
+ Google APIs Office Hours 2
+
+
+
+ 2010-05-20T15:00:00.000-07:00
+ 2010-05-20T17:30:00.000-07:00
+ Office Hours Space G
+
+ App Engine Office Hours 4
+
+
+
diff --git a/res/xml/.svn/text-base/tracks.xml.svn-base b/res/xml/.svn/text-base/tracks.xml.svn-base
new file mode 100644
index 0000000..959a898
--- /dev/null
+++ b/res/xml/.svn/text-base/tracks.xml.svn-base
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/blocks.xml b/res/xml/blocks.xml
new file mode 100644
index 0000000..016a75b
--- /dev/null
+++ b/res/xml/blocks.xml
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+ 2010-05-18T12:00:00.000-07:00
+ 2010-05-18T19:00:00.000-07:00
+ On-site registration
+
+
+ 2010-05-18T14:00:00.000-07:00
+ 2010-05-18T20:00:00.000-07:00
+ BootCamp
+
+
+
+
+ 2010-05-19T07:00:00.000-07:00
+ 2010-05-19T22:00:00.000-07:00
+ On-site registration
+
+
+ 2010-05-19T07:30:00.000-07:00
+ 2010-05-19T10:00:00.000-07:00
+ Breakfast
+ food
+
+
+ 2010-05-19T09:00:00.000-07:00
+ 2010-05-19T10:30:00.000-07:00
+ Keynote
+ session
+
+
+ 2010-05-19T10:30:00.000-07:00
+ 2010-05-19T18:30:00.000-07:00
+ Developer Sandbox
+
+
+ 2010-05-19T11:45:00.000-07:00
+ 2010-05-19T13:30:00.000-07:00
+ Lunch served
+ food
+
+
+ 2010-05-19T12:00:00.000-07:00
+ 2010-05-19T14:30:00.000-07:00
+ Office hours
+ officehours
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office hours
+ officehours
+
+
+ 2010-05-19T18:30:00.000-07:00
+ 2010-05-19T22:30:00.000-07:00
+ After Hours evening party
+ food
+
+
+
+
+ 2010-05-20T07:00:00.000-07:00
+ 2010-05-20T16:00:00.000-07:00
+ On-site registration
+
+
+ 2010-05-20T08:00:00.000-07:00
+ 2010-05-20T09:30:00.000-07:00
+ Breakfast
+ food
+
+
+ 2010-05-20T08:30:00.000-07:00
+ 2010-05-20T10:00:00.000-07:00
+ Keynote
+ session
+
+
+ 2010-05-20T10:00:00.000-07:00
+ 2010-05-20T18:00:00.000-07:00
+ Developer Sandbox
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T14:00:00.000-07:00
+ Lunch served
+ food
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office hours
+ officehours
+
+
+ 2010-05-20T15:00:00.000-07:00
+ 2010-05-20T17:30:00.000-07:00
+ Office hours
+ officehours
+
+
+
diff --git a/res/xml/rooms.xml b/res/xml/rooms.xml
new file mode 100644
index 0000000..6bf2f6d
--- /dev/null
+++ b/res/xml/rooms.xml
@@ -0,0 +1,39 @@
+
+
+
+
+ 1Room 12
+ 2Room 22
+ 3Room 32
+ 4Room 42
+ 5Room 52
+ 6Room 62
+ 7Room 72
+ 8Room 82
+ 9Room 92
+
+ firesidechatroomFireside Chat Room2
+
+ officehoursspaceaOffice Hours Space A2
+ officehoursspacebOffice Hours Space B2
+ officehoursspacecOffice Hours Space C2
+ officehoursspacedOffice Hours Space D2
+ officehoursspaceeOffice Hours Space E2
+ officehoursspacefOffice Hours Space F2
+ officehoursspacegOffice Hours Space G2
+
+
diff --git a/res/xml/search_suggest.xml b/res/xml/search_suggest.xml
new file mode 100644
index 0000000..65d4c35
--- /dev/null
+++ b/res/xml/search_suggest.xml
@@ -0,0 +1,106 @@
+
+
+
+activitystrea.ms
+adsense
+ajax
+analytics
+android
+api
+app engine
+applications
+apps
+atom
+background
+build
+business
+buzz
+calendar
+charts
+chat
+chrome
+closure
+cloud
+compiler
+dalvik
+data
+datastore
+design
+desktop
+development
+docs
+earth
+embedded
+enterprise
+extension
+extensions
+fireside
+framework
+friends
+gadgets
+gaming
+geo
+geospatial
+gmail
+go
+google
+gwt
+handset
+html
+igoogle
+iphone
+java
+javascript
+kml
+library
+maps
+marketplace
+mobile
+monetizing
+native
+oauth
+open
+openid
+opensocial
+performance
+platform
+power
+powermeter
+pubsubhubbub
+python
+restful
+robots
+rss
+saas
+scalable
+scripting
+search
+seo
+server
+share
+sites
+sketchup
+social
+source
+speed
+team
+testing
+toolkit
+wave
+webfinger
+youtube
+
diff --git a/res/xml/searchable.xml b/res/xml/searchable.xml
new file mode 100644
index 0000000..b189a65
--- /dev/null
+++ b/res/xml/searchable.xml
@@ -0,0 +1,21 @@
+
+
+
diff --git a/res/xml/sessions.xml b/res/xml/sessions.xml
new file mode 100644
index 0000000..c1410f1
--- /dev/null
+++ b/res/xml/sessions.xml
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+ 2010-05-19T12:00:00.000-07:00
+ 2010-05-19T14:30:00.000-07:00
+ Office Hours Space A
+
+ Enterprise Office Hours
+
+
+
+ 2010-05-19T12:00:00.000-07:00
+ 2010-05-19T14:30:00.000-07:00
+ Office Hours Space B
+ Go Office Hours
+
+
+
+ 2010-05-19T12:00:00.000-07:00
+ 2010-05-19T14:30:00.000-07:00
+ Office Hours Space C
+ Google Project Hosting Office Hours
+
+
+
+ 2010-05-19T12:00:00.000-07:00
+ 2010-05-19T14:30:00.000-07:00
+ Office Hours Space D
+
+ Social Web Office Hours
+
+
+
+ 2010-05-19T12:00:00.000-07:00
+ 2010-05-19T14:30:00.000-07:00
+
+ Office Hours Space E
+
+ Google APIs Office Hours
+
+
+
+ 2010-05-19T12:00:00.000-07:00
+ 2010-05-19T14:30:00.000-07:00
+ Office Hours Space G
+
+ App Engine Office Hours
+
+
+
+
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office Hours Space A
+
+ Chrome Office Hours
+
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office Hours Space B
+ Closure Compiler Office Hours
+
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office Hours Space C
+
+ Geo Office Hours
+
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office Hours Space D
+
+ Google Web Toolkit Office Hours
+
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office Hours Space E
+
+ Wave Office Hours
+
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office Hours Space F
+ Developer Docs Office Hours
+
+
+
+ 2010-05-19T14:30:00.000-07:00
+ 2010-05-19T17:00:00.000-07:00
+ Office Hours Space G
+
+ App Engine Office Hours 2
+
+
+
+
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office Hours Space A
+
+ Chrome Office Hours 2
+
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office Hours Space B
+
+ Android Office Hours
+
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office Hours Space C
+
+ Geo Office Hours 2
+
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office Hours Space D
+
+ Google Web Toolkit Office Hours 2
+
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office Hours Space E
+
+ Wave Office Hours 2
+
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office Hours Space F
+ Developer Docs Office Hours 2
+
+
+
+ 2010-05-20T12:30:00.000-07:00
+ 2010-05-20T15:00:00.000-07:00
+ Office Hours Space G
+
+ App Engine Office Hours 3
+
+
+
+
+
+
+ 2010-05-20T15:00:00.000-07:00
+ 2010-05-20T17:30:00.000-07:00
+ Office Hours Space A
+
+ Enterprise Office Hours 2
+
+
+
+ 2010-05-20T15:00:00.000-07:00
+ 2010-05-20T17:30:00.000-07:00
+ Office Hours Space B
+
+ Android Office Hours 2
+
+
+
+ 2010-05-20T15:00:00.000-07:00
+ 2010-05-20T17:30:00.000-07:00
+ Office Hours Space C
+ Google Project Hosting Office Hours 2
+
+
+
+ 2010-05-20T15:00:00.000-07:00
+ 2010-05-20T17:30:00.000-07:00
+ Office Hours Space D
+
+ Social Web Office Hours 2
+
+
+
+ 2010-05-20T15:00:00.000-07:00
+ 2010-05-20T17:30:00.000-07:00
+
+ Office Hours Space E
+
+ Google APIs Office Hours 2
+
+
+
+ 2010-05-20T15:00:00.000-07:00
+ 2010-05-20T17:30:00.000-07:00
+ Office Hours Space G
+
+ App Engine Office Hours 4
+
+
+
diff --git a/res/xml/tracks.xml b/res/xml/tracks.xml
new file mode 100644
index 0000000..959a898
--- /dev/null
+++ b/res/xml/tracks.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.svn/all-wcprops b/src/.svn/all-wcprops
new file mode 100644
index 0000000..c5a1ee7
--- /dev/null
+++ b/src/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 25
+/svn/!svn/ver/2/trunk/src
+END
diff --git a/src/.svn/entries b/src/.svn/entries
new file mode 100644
index 0000000..58e31cf
--- /dev/null
+++ b/src/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/src
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+com
+dir
+
diff --git a/src/com/.svn/all-wcprops b/src/com/.svn/all-wcprops
new file mode 100644
index 0000000..a0e3843
--- /dev/null
+++ b/src/com/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 29
+/svn/!svn/ver/2/trunk/src/com
+END
diff --git a/src/com/.svn/entries b/src/com/.svn/entries
new file mode 100644
index 0000000..63181ae
--- /dev/null
+++ b/src/com/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/src/com
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+google
+dir
+
diff --git a/src/com/google/.svn/all-wcprops b/src/com/google/.svn/all-wcprops
new file mode 100644
index 0000000..033fb2c
--- /dev/null
+++ b/src/com/google/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 36
+/svn/!svn/ver/2/trunk/src/com/google
+END
diff --git a/src/com/google/.svn/entries b/src/com/google/.svn/entries
new file mode 100644
index 0000000..077ae3b
--- /dev/null
+++ b/src/com/google/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/src/com/google
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+android
+dir
+
diff --git a/src/com/google/android/.svn/all-wcprops b/src/com/google/android/.svn/all-wcprops
new file mode 100644
index 0000000..c296755
--- /dev/null
+++ b/src/com/google/android/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 44
+/svn/!svn/ver/2/trunk/src/com/google/android
+END
diff --git a/src/com/google/android/.svn/entries b/src/com/google/android/.svn/entries
new file mode 100644
index 0000000..14dee80
--- /dev/null
+++ b/src/com/google/android/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/src/com/google/android
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+apps
+dir
+
diff --git a/src/com/google/android/apps/.svn/all-wcprops b/src/com/google/android/apps/.svn/all-wcprops
new file mode 100644
index 0000000..8bf03a2
--- /dev/null
+++ b/src/com/google/android/apps/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 49
+/svn/!svn/ver/2/trunk/src/com/google/android/apps
+END
diff --git a/src/com/google/android/apps/.svn/entries b/src/com/google/android/apps/.svn/entries
new file mode 100644
index 0000000..7bf9413
--- /dev/null
+++ b/src/com/google/android/apps/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/src/com/google/android/apps
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+iosched
+dir
+
diff --git a/src/com/google/android/apps/iosched/.svn/all-wcprops b/src/com/google/android/apps/iosched/.svn/all-wcprops
new file mode 100644
index 0000000..526559d
--- /dev/null
+++ b/src/com/google/android/apps/iosched/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 57
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched
+END
diff --git a/src/com/google/android/apps/iosched/.svn/entries b/src/com/google/android/apps/iosched/.svn/entries
new file mode 100644
index 0000000..2fb271f
--- /dev/null
+++ b/src/com/google/android/apps/iosched/.svn/entries
@@ -0,0 +1,43 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/src/com/google/android/apps/iosched
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+service
+dir
+
+io
+dir
+
+provider
+dir
+
+util
+dir
+
+ui
+dir
+
diff --git a/src/com/google/android/apps/iosched/io/.svn/all-wcprops b/src/com/google/android/apps/iosched/io/.svn/all-wcprops
new file mode 100644
index 0000000..997f697
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/.svn/all-wcprops
@@ -0,0 +1,77 @@
+K 25
+svn:wc:ra_dav:version-url
+V 60
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/io
+END
+LocalSessionsHandler.java
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/io/LocalSessionsHandler.java
+END
+RemoteSessionsHandler.java
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/io/RemoteSessionsHandler.java
+END
+LocalTracksHandler.java
+K 25
+svn:wc:ra_dav:version-url
+V 84
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/io/LocalTracksHandler.java
+END
+LocalSearchSuggestHandler.java
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/io/LocalSearchSuggestHandler.java
+END
+LocalBlocksHandler.java
+K 25
+svn:wc:ra_dav:version-url
+V 84
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/io/LocalBlocksHandler.java
+END
+RemoteSpeakersHandler.java
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/io/RemoteSpeakersHandler.java
+END
+LocalRoomsHandler.java
+K 25
+svn:wc:ra_dav:version-url
+V 83
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/io/LocalRoomsHandler.java
+END
+RemoteWorksheetsHandler.java
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/io/RemoteWorksheetsHandler.java
+END
+LocalExecutor.java
+K 25
+svn:wc:ra_dav:version-url
+V 79
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/io/LocalExecutor.java
+END
+RemoteVendorsHandler.java
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/io/RemoteVendorsHandler.java
+END
+RemoteExecutor.java
+K 25
+svn:wc:ra_dav:version-url
+V 80
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/io/RemoteExecutor.java
+END
+XmlHandler.java
+K 25
+svn:wc:ra_dav:version-url
+V 76
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/io/XmlHandler.java
+END
diff --git a/src/com/google/android/apps/iosched/io/.svn/entries b/src/com/google/android/apps/iosched/io/.svn/entries
new file mode 100644
index 0000000..49b3e1b
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/.svn/entries
@@ -0,0 +1,436 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/src/com/google/android/apps/iosched/io
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+LocalSessionsHandler.java
+file
+
+
+
+
+2010-06-03T15:23:15.357628Z
+2fd9b88dbb35ef954a9b7c5ae58690ac
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6186
+
+RemoteSessionsHandler.java
+file
+
+
+
+
+2010-06-03T15:23:15.357628Z
+df66924507aca5216c512f21884db995
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+11847
+
+LocalTracksHandler.java
+file
+
+
+
+
+2010-06-03T15:23:15.357628Z
+0992ae23f1c9bf0c402ac15962d9de8b
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3618
+
+LocalSearchSuggestHandler.java
+file
+
+
+
+
+2010-06-03T15:23:15.357628Z
+d7e920ab2368e0c6a74549e1d730d1af
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2635
+
+LocalBlocksHandler.java
+file
+
+
+
+
+2010-06-03T15:23:15.361622Z
+d9f2ec585f8bc06a41f7d7478443be7d
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4289
+
+RemoteSpeakersHandler.java
+file
+
+
+
+
+2010-06-03T15:23:15.361622Z
+1686220027f466806ce77a9bd6288b56
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4720
+
+LocalRoomsHandler.java
+file
+
+
+
+
+2010-06-03T15:23:15.361622Z
+303a86736dffec38b7d6bab655489d22
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3659
+
+RemoteWorksheetsHandler.java
+file
+
+
+
+
+2010-06-03T15:23:15.361622Z
+145f726e59bb4eeec28111a899bdca8a
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4770
+
+LocalExecutor.java
+file
+
+
+
+
+2010-06-03T15:23:15.361622Z
+cc75c7419719ecd9f932aadc8ad8a243
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2372
+
+RemoteVendorsHandler.java
+file
+
+
+
+
+2010-06-03T15:23:15.361622Z
+960ba8091b0345284d6fa697eb6c6c3d
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7110
+
+RemoteExecutor.java
+file
+
+
+
+
+2010-06-03T15:23:15.361622Z
+c86daebc9bbe9cd8b90cdff1793402e6
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3249
+
+XmlHandler.java
+file
+
+
+
+
+2010-06-03T15:23:15.361622Z
+ba6c3c3fbc44f57b6324948f4bd844e2
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3951
+
diff --git a/src/com/google/android/apps/iosched/io/.svn/text-base/LocalBlocksHandler.java.svn-base b/src/com/google/android/apps/iosched/io/.svn/text-base/LocalBlocksHandler.java.svn-base
new file mode 100644
index 0000000..417d376
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/.svn/text-base/LocalBlocksHandler.java.svn-base
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.util.Lists;
+import com.google.android.apps.iosched.util.ParserUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class LocalBlocksHandler extends XmlHandler {
+
+ public LocalBlocksHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+
+ // Clear any existing static blocks, as they may have been updated.
+ final String selection = Blocks.BLOCK_TYPE + "=? OR " + Blocks.BLOCK_TYPE +"=?";
+ final String[] selectionArgs = {
+ ParserUtils.BLOCK_TYPE_FOOD,
+ ParserUtils.BLOCK_TYPE_OFFICE_HOURS
+ };
+ batch.add(ContentProviderOperation.newDelete(Blocks.CONTENT_URI)
+ .withSelection(selection, selectionArgs).build());
+
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && Tags.BLOCK.equals(parser.getName())) {
+ batch.add(parseBlock(parser));
+ }
+ }
+
+ return batch;
+ }
+
+ private static ContentProviderOperation parseBlock(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ final int depth = parser.getDepth();
+ final ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Blocks.CONTENT_URI);
+
+ String title = null;
+ long startTime = -1;
+ long endTime = -1;
+ String blockType = null;
+
+ String tag = null;
+ int type;
+ while (((type = parser.next()) != END_TAG ||
+ parser.getDepth() > depth) && type != END_DOCUMENT) {
+ if (type == START_TAG) {
+ tag = parser.getName();
+ } else if (type == END_TAG) {
+ tag = null;
+ } else if (type == TEXT) {
+ final String text = parser.getText();
+ if (Tags.TITLE.equals(tag)) {
+ title = text;
+ } else if (Tags.START.equals(tag)) {
+ startTime = ParserUtils.parseTime(text);
+ } else if (Tags.END.equals(tag)) {
+ endTime = ParserUtils.parseTime(text);
+ } else if (Tags.TYPE.equals(tag)) {
+ blockType = text;
+ }
+ }
+ }
+
+ final String blockId = Blocks.generateBlockId(startTime, endTime);
+
+ builder.withValue(Blocks.BLOCK_ID, blockId);
+ builder.withValue(Blocks.BLOCK_TITLE, title);
+ builder.withValue(Blocks.BLOCK_START, startTime);
+ builder.withValue(Blocks.BLOCK_END, endTime);
+ builder.withValue(Blocks.BLOCK_TYPE, blockType);
+
+ return builder.build();
+ }
+
+ interface Tags {
+ String BLOCK = "block";
+ String TITLE = "title";
+ String START = "start";
+ String END = "end";
+ String TYPE = "type";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/.svn/text-base/LocalExecutor.java.svn-base b/src/com/google/android/apps/iosched/io/.svn/text-base/LocalExecutor.java.svn-base
new file mode 100644
index 0000000..ca33237
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/.svn/text-base/LocalExecutor.java.svn-base
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import com.google.android.apps.iosched.io.XmlHandler.HandlerException;
+import com.google.android.apps.iosched.util.ParserUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Opens a local {@link Resources#getXml(int)} and passes the resulting
+ * {@link XmlPullParser} to the given {@link XmlHandler}.
+ */
+public class LocalExecutor {
+ private Resources mRes;
+ private ContentResolver mResolver;
+
+ public LocalExecutor(Resources res, ContentResolver resolver) {
+ mRes = res;
+ mResolver = resolver;
+ }
+
+ public void execute(Context context, String assetName, XmlHandler handler)
+ throws HandlerException {
+ try {
+ final InputStream input = context.getAssets().open(assetName);
+ final XmlPullParser parser = ParserUtils.newPullParser(input);
+ handler.parseAndApply(parser, mResolver);
+ } catch (HandlerException e) {
+ throw e;
+ } catch (XmlPullParserException e) {
+ throw new HandlerException("Problem parsing local asset: " + assetName, e);
+ } catch (IOException e) {
+ throw new HandlerException("Problem parsing local asset: " + assetName, e);
+ }
+ }
+
+ public void execute(int resId, XmlHandler handler) throws HandlerException {
+ final XmlResourceParser parser = mRes.getXml(resId);
+ try {
+ handler.parseAndApply(parser, mResolver);
+ } finally {
+ parser.close();
+ }
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/.svn/text-base/LocalRoomsHandler.java.svn-base b/src/com/google/android/apps/iosched/io/.svn/text-base/LocalRoomsHandler.java.svn-base
new file mode 100644
index 0000000..57df67d
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/.svn/text-base/LocalRoomsHandler.java.svn-base
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.util.Lists;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Handle a local {@link XmlPullParser} that defines a set of {@link Rooms}
+ * entries. Usually loaded from {@link R.xml} resources.
+ */
+public class LocalRoomsHandler extends XmlHandler {
+
+ public LocalRoomsHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && Tags.ROOM.equals(parser.getName())) {
+ parseRoom(parser, batch, resolver);
+ }
+ }
+
+ return batch;
+ }
+
+ /**
+ * Parse a given {@link Rooms} entry, building
+ * {@link ContentProviderOperation} to define it locally.
+ */
+ private static void parseRoom(XmlPullParser parser,
+ ArrayList batch, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final int depth = parser.getDepth();
+ ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Rooms.CONTENT_URI);
+
+ String tag = null;
+ int type;
+ while (((type = parser.next()) != END_TAG ||
+ parser.getDepth() > depth) && type != END_DOCUMENT) {
+ if (type == START_TAG) {
+ tag = parser.getName();
+ } else if (type == END_TAG) {
+ tag = null;
+ } else if (type == TEXT) {
+ final String text = parser.getText();
+ if (Tags.ID.equals(tag)) {
+ builder.withValue(Rooms.ROOM_ID, text);
+ } else if (Tags.NAME.equals(tag)) {
+ builder.withValue(Rooms.ROOM_NAME, text);
+ } else if (Tags.FLOOR.equals(tag)) {
+ builder.withValue(Rooms.ROOM_FLOOR, text);
+ }
+ }
+ }
+
+ batch.add(builder.build());
+ }
+
+ /** XML tags expected from local source. */
+ private interface Tags {
+ String ROOM = "room";
+ String ID = "id";
+ String NAME = "name";
+ String FLOOR = "floor";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/.svn/text-base/LocalSearchSuggestHandler.java.svn-base b/src/com/google/android/apps/iosched/io/.svn/text-base/LocalSearchSuggestHandler.java.svn-base
new file mode 100644
index 0000000..4b88e3a
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/.svn/text-base/LocalSearchSuggestHandler.java.svn-base
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.SearchSuggest;
+import com.google.android.apps.iosched.util.Lists;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.app.SearchManager;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class LocalSearchSuggestHandler extends XmlHandler {
+
+ public LocalSearchSuggestHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+
+ // Clear any existing suggestion words
+ batch.add(ContentProviderOperation.newDelete(SearchSuggest.CONTENT_URI).build());
+
+ String tag = null;
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG) {
+ tag = parser.getName();
+ } else if (type == END_TAG) {
+ tag = null;
+ } else if (type == TEXT) {
+ final String text = parser.getText();
+ if (Tags.WORD.equals(tag)) {
+ // Insert word as search suggestion
+ batch.add(ContentProviderOperation.newInsert(SearchSuggest.CONTENT_URI)
+ .withValue(SearchManager.SUGGEST_COLUMN_TEXT_1, text).build());
+ }
+ }
+ }
+
+ return batch;
+ }
+
+ private interface Tags {
+ String WORD = "word";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/.svn/text-base/LocalSessionsHandler.java.svn-base b/src/com/google/android/apps/iosched/io/.svn/text-base/LocalSessionsHandler.java.svn-base
new file mode 100644
index 0000000..3c719f7
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/.svn/text-base/LocalSessionsHandler.java.svn-base
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.SessionsTracks;
+import com.google.android.apps.iosched.util.Lists;
+import com.google.android.apps.iosched.util.ParserUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class LocalSessionsHandler extends XmlHandler {
+
+ public LocalSessionsHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && Tags.SESSION.equals(parser.getName())) {
+ parseSession(parser, batch, resolver);
+ }
+ }
+
+ return batch;
+ }
+
+ private static void parseSession(XmlPullParser parser,
+ ArrayList batch, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final int depth = parser.getDepth();
+ ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Sessions.CONTENT_URI);
+ builder.withValue(Sessions.UPDATED, 0);
+
+ long startTime = -1;
+ long endTime = -1;
+ String title = null;
+ String sessionId = null;
+ String trackId = null;
+
+ String tag = null;
+ int type;
+ while (((type = parser.next()) != END_TAG ||
+ parser.getDepth() > depth) && type != END_DOCUMENT) {
+ if (type == START_TAG) {
+ tag = parser.getName();
+ } else if (type == END_TAG) {
+ tag = null;
+ } else if (type == TEXT) {
+ final String text = parser.getText();
+ if (Tags.START.equals(tag)) {
+ startTime = ParserUtils.parseTime(text);
+ } else if (Tags.END.equals(tag)) {
+ endTime = ParserUtils.parseTime(text);
+ } else if (Tags.ROOM.equals(tag)) {
+ final String roomId = Rooms.generateRoomId(text);
+ builder.withValue(Sessions.ROOM_ID, roomId);
+ } else if (Tags.TRACK.equals(tag)) {
+ trackId = Tracks.generateTrackId(text);
+ } else if (Tags.ID.equals(tag)) {
+ sessionId = text;
+ } else if (Tags.TITLE.equals(tag)) {
+ title = text;
+ } else if (Tags.ABSTRACT.equals(tag)) {
+ builder.withValue(Sessions.ABSTRACT, text);
+ }
+ }
+ }
+
+ if (sessionId == null) {
+ sessionId = Sessions.generateSessionId(title);
+ }
+
+ builder.withValue(Sessions.SESSION_ID, sessionId);
+ builder.withValue(Sessions.TITLE, title);
+
+ // Use empty strings to make sure SQLite search trigger has valid data
+ // for updating search index.
+ builder.withValue(Sessions.ABSTRACT, "");
+ builder.withValue(Sessions.REQUIREMENTS, "");
+ builder.withValue(Sessions.KEYWORDS, "");
+
+ final String blockId = ParserUtils.findBlock(title, startTime, endTime);
+ builder.withValue(Sessions.BLOCK_ID, blockId);
+
+ // Propagate any existing starred value
+ final Uri sessionUri = Sessions.buildSessionUri(sessionId);
+ final int starred = querySessionStarred(sessionUri, resolver);
+ if (starred != -1) {
+ builder.withValue(Sessions.STARRED, starred);
+ }
+
+ batch.add(builder.build());
+
+ if (trackId != null) {
+ // TODO: support parsing multiple tracks per session
+ final Uri sessionTracks = Sessions.buildTracksDirUri(sessionId);
+ batch.add(ContentProviderOperation.newInsert(sessionTracks)
+ .withValue(SessionsTracks.SESSION_ID, sessionId)
+ .withValue(SessionsTracks.TRACK_ID, trackId).build());
+ }
+ }
+
+ public static int querySessionStarred(Uri uri, ContentResolver resolver) {
+ final String[] projection = { Sessions.STARRED };
+ final Cursor cursor = resolver.query(uri, projection, null, null, null);
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getInt(0);
+ } else {
+ return -1;
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ interface Tags {
+ String SESSION = "session";
+ String ID = "id";
+ String START = "start";
+ String END = "end";
+ String ROOM = "room";
+ String TRACK = "track";
+ String TITLE = "title";
+ String ABSTRACT = "abstract";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/.svn/text-base/LocalTracksHandler.java.svn-base b/src/com/google/android/apps/iosched/io/.svn/text-base/LocalTracksHandler.java.svn-base
new file mode 100644
index 0000000..91aa12e
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/.svn/text-base/LocalTracksHandler.java.svn-base
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static com.google.android.apps.iosched.util.ParserUtils.sanitizeId;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.util.Lists;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.graphics.Color;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class LocalTracksHandler extends XmlHandler {
+
+ public LocalTracksHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+ batch.add(ContentProviderOperation.newDelete(Tracks.CONTENT_URI).build());
+
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && Tags.TRACK.equals(parser.getName())) {
+ batch.add(parseTrack(parser));
+ }
+ }
+
+ return batch;
+ }
+
+ private static ContentProviderOperation parseTrack(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ final int depth = parser.getDepth();
+ final ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Tracks.CONTENT_URI);
+
+ String tag = null;
+ int type;
+ while (((type = parser.next()) != END_TAG ||
+ parser.getDepth() > depth) && type != END_DOCUMENT) {
+ if (type == START_TAG) {
+ tag = parser.getName();
+ } else if (type == END_TAG) {
+ tag = null;
+ } else if (type == TEXT) {
+ final String text = parser.getText();
+ if (Tags.NAME.equals(tag)) {
+ final String trackId = sanitizeId(text);
+ builder.withValue(Tracks.TRACK_ID, trackId);
+ builder.withValue(Tracks.TRACK_NAME, text);
+ } else if (Tags.COLOR.equals(tag)) {
+ final int color = Color.parseColor(text);
+ builder.withValue(Tracks.TRACK_COLOR, color);
+ } else if (Tags.ABSTRACT.equals(tag)) {
+ builder.withValue(Tracks.TRACK_ABSTRACT, text);
+ }
+ }
+ }
+
+ return builder.build();
+ }
+
+ interface Tags {
+ String TRACK = "track";
+ String NAME = "name";
+ String COLOR = "color";
+ String ABSTRACT = "abstract";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/.svn/text-base/RemoteExecutor.java.svn-base b/src/com/google/android/apps/iosched/io/.svn/text-base/RemoteExecutor.java.svn-base
new file mode 100644
index 0000000..5ee071a
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/.svn/text-base/RemoteExecutor.java.svn-base
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import com.google.android.apps.iosched.io.XmlHandler.HandlerException;
+import com.google.android.apps.iosched.util.ParserUtils;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentResolver;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Executes an {@link HttpUriRequest} and passes the result as an
+ * {@link XmlPullParser} to the given {@link XmlHandler}.
+ */
+public class RemoteExecutor {
+ private final HttpClient mHttpClient;
+ private final ContentResolver mResolver;
+
+ public RemoteExecutor(HttpClient httpClient, ContentResolver resolver) {
+ mHttpClient = httpClient;
+ mResolver = resolver;
+ }
+
+ /**
+ * Execute a {@link HttpGet} request, passing a valid response through
+ * {@link XmlHandler#parseAndApply(XmlPullParser, ContentResolver)}.
+ */
+ public void executeGet(String url, XmlHandler handler) throws HandlerException {
+ final HttpUriRequest request = new HttpGet(url);
+ execute(request, handler);
+ }
+
+ /**
+ * Execute this {@link HttpUriRequest}, passing a valid response through
+ * {@link XmlHandler#parseAndApply(XmlPullParser, ContentResolver)}.
+ */
+ public void execute(HttpUriRequest request, XmlHandler handler) throws HandlerException {
+ try {
+ final HttpResponse resp = mHttpClient.execute(request);
+ final int status = resp.getStatusLine().getStatusCode();
+ if (status != HttpStatus.SC_OK) {
+ throw new HandlerException("Unexpected server response " + resp.getStatusLine()
+ + " for " + request.getRequestLine());
+ }
+
+ final InputStream input = resp.getEntity().getContent();
+ try {
+ final XmlPullParser parser = ParserUtils.newPullParser(input);
+ handler.parseAndApply(parser, mResolver);
+ } catch (XmlPullParserException e) {
+ throw new HandlerException("Malformed response for " + request.getRequestLine(), e);
+ } finally {
+ if (input != null) input.close();
+ }
+ } catch (HandlerException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new HandlerException("Problem reading remote response for "
+ + request.getRequestLine(), e);
+ }
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/.svn/text-base/RemoteSessionsHandler.java.svn-base b/src/com/google/android/apps/iosched/io/.svn/text-base/RemoteSessionsHandler.java.svn-base
new file mode 100644
index 0000000..9a71908
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/.svn/text-base/RemoteSessionsHandler.java.svn-base
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static com.google.android.apps.iosched.util.ParserUtils.sanitizeId;
+import static com.google.android.apps.iosched.util.ParserUtils.splitComma;
+import static com.google.android.apps.iosched.util.ParserUtils.translateTrackIdAlias;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.ENTRY;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.SyncColumns;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.SessionsSpeakers;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.SessionsTracks;
+import com.google.android.apps.iosched.util.Lists;
+import com.google.android.apps.iosched.util.ParserUtils;
+import com.google.android.apps.iosched.util.SpreadsheetEntry;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.text.format.Time;
+import android.util.Log;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Locale;
+
+/**
+ * Handle a remote {@link XmlPullParser} that defines a set of {@link Sessions}
+ * entries. Assumes that the remote source is a Google Spreadsheet.
+ */
+public class RemoteSessionsHandler extends XmlHandler {
+ private static final String TAG = "SessionsHandler";
+
+ /**
+ * Custom format used internally that matches expected concatenation of
+ * {@link Columns#SESSION_DATE} and {@link Columns#SESSION_TIME}.
+ */
+ private static final SimpleDateFormat sTimeFormat = new SimpleDateFormat(
+ "EEEE MMM d yyyy h:mma Z", Locale.US);
+
+ public RemoteSessionsHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+
+ // Walk document, parsing any incoming entries
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && ENTRY.equals(parser.getName())) {
+ // Process single spreadsheet row at a time
+ final SpreadsheetEntry entry = SpreadsheetEntry.fromParser(parser);
+
+ final String sessionId = sanitizeId(entry.get(Columns.SESSION_LINK));
+ final Uri sessionUri = Sessions.buildSessionUri(sessionId);
+
+ // Check for existing details, only update when changed
+ final ContentValues values = querySessionDetails(sessionUri, resolver);
+ final long localUpdated = values.getAsLong(SyncColumns.UPDATED);
+ final long serverUpdated = entry.getUpdated();
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "found session " + entry.toString());
+ Log.v(TAG, "found localUpdated=" + localUpdated + ", server=" + serverUpdated);
+ }
+ if (localUpdated >= serverUpdated) continue;
+
+ final Uri sessionTracksUri = Sessions.buildTracksDirUri(sessionId);
+ final Uri sessionSpeakersUri = Sessions.buildSpeakersDirUri(sessionId);
+
+ // Clear any existing values for this session, treating the
+ // incoming details as authoritative.
+ batch.add(ContentProviderOperation.newDelete(sessionUri).build());
+ batch.add(ContentProviderOperation.newDelete(sessionTracksUri).build());
+ batch.add(ContentProviderOperation.newDelete(sessionSpeakersUri).build());
+
+ final ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Sessions.CONTENT_URI);
+
+ builder.withValue(SyncColumns.UPDATED, serverUpdated);
+ builder.withValue(Sessions.SESSION_ID, sessionId);
+ builder.withValue(Sessions.TYPE, entry.get(Columns.SESSION_TYPE));
+ builder.withValue(Sessions.TITLE, entry.get(Columns.SESSION_TITLE));
+ builder.withValue(Sessions.ABSTRACT, entry.get(Columns.SESSION_ABSTRACT));
+ builder.withValue(Sessions.REQUIREMENTS, entry.get(Columns.SESSION_REQUIREMENTS));
+ builder.withValue(Sessions.MODERATOR_URL, entry.get(Columns.MODERATORLINK));
+ builder.withValue(Sessions.WAVE_URL, entry.get(Columns.WAVELINK));
+ builder.withValue(Sessions.KEYWORDS, entry.get(Columns.TAGS));
+ builder.withValue(Sessions.HASHTAG, entry.get(Columns.SESSION_HASHTAG));
+
+ // Inherit starred value from previous row
+ if (values.containsKey(Sessions.STARRED)) {
+ builder.withValue(Sessions.STARRED, values.getAsInteger(Sessions.STARRED));
+ }
+
+ // Parse time string from two columns, which is pretty ugly code
+ // since it assumes the column format is "Wednesday May 19" and
+ // "10:45am-11:45am". Future spreadsheets should use RFC 3339.
+ final String date = entry.get(Columns.SESSION_DATE);
+ final String time = entry.get(Columns.SESSION_TIME);
+ final int timeSplit = time.indexOf("-");
+ if (timeSplit == -1) {
+ throw new HandlerException("Expecting " + Columns.SESSION_TIME
+ + " to express span");
+ }
+
+ final long startTime = parseTime(date, time.substring(0, timeSplit));
+ final long endTime = parseTime(date, time.substring(timeSplit + 1));
+
+ final String blockId = ParserUtils.findOrCreateBlock(
+ ParserUtils.BLOCK_TITLE_BREAKOUT_SESSIONS,
+ ParserUtils.BLOCK_TYPE_SESSION,
+ startTime, endTime, batch, resolver);
+ builder.withValue(Sessions.BLOCK_ID, blockId);
+
+ // Assign room
+ final String roomId = sanitizeId(entry.get(Columns.ROOM));
+ builder.withValue(Sessions.ROOM_ID, roomId);
+
+ // Normal session details ready, write to provider
+ batch.add(builder.build());
+
+ // Assign tracks
+ final String[] tracks = splitComma(entry.get(Columns.TRACK));
+ for (String track : tracks) {
+ final String trackId = translateTrackIdAlias(sanitizeId(track));
+ batch.add(ContentProviderOperation.newInsert(sessionTracksUri)
+ .withValue(SessionsTracks.SESSION_ID, sessionId)
+ .withValue(SessionsTracks.TRACK_ID, trackId).build());
+ }
+
+ // Assign speakers
+ final String[] speakers = splitComma(entry.get(Columns.SESSION_SPEAKERS));
+ for (String speaker : speakers) {
+ final String speakerId = sanitizeId(speaker, true);
+ batch.add(ContentProviderOperation.newInsert(sessionSpeakersUri)
+ .withValue(SessionsSpeakers.SESSION_ID, sessionId)
+ .withValue(SessionsSpeakers.SPEAKER_ID, speakerId).build());
+ }
+ }
+ }
+
+ return batch;
+ }
+
+ /**
+ * Parse the given date and time coming from spreadsheet. This is tightly
+ * tied to a specific format. Ideally, if the source used use RFC 3339 we
+ * could parse quickly using {@link Time#parse3339}.
+ *
+ * Internally assumes PST time zone and year 2010.
+ *
+ * @param date String of format "Wednesday May 19", usually read from
+ * {@link Columns#SESSION_DATE}.
+ * @param time String of format "10:45am", usually after splitting
+ * {@link Columns#SESSION_TIME}.
+ */
+ private static long parseTime(String date, String time) throws HandlerException {
+ final String composed = String.format("%s 2010 %s -0700", date, time);
+ try {
+ return sTimeFormat.parse(composed).getTime();
+ } catch (java.text.ParseException e) {
+ throw new HandlerException("Problem parsing timestamp", e);
+ }
+ }
+
+ private static ContentValues querySessionDetails(Uri uri, ContentResolver resolver) {
+ final ContentValues values = new ContentValues();
+ final Cursor cursor = resolver.query(uri, SessionsQuery.PROJECTION, null, null, null);
+ try {
+ if (cursor.moveToFirst()) {
+ values.put(SyncColumns.UPDATED, cursor.getLong(SessionsQuery.UPDATED));
+ values.put(Sessions.STARRED, cursor.getInt(SessionsQuery.STARRED));
+ } else {
+ values.put(SyncColumns.UPDATED, ScheduleContract.UPDATED_NEVER);
+ }
+ } finally {
+ cursor.close();
+ }
+ return values;
+ }
+
+ private interface SessionsQuery {
+ String[] PROJECTION = {
+ SyncColumns.UPDATED,
+ Sessions.STARRED,
+ };
+
+ int UPDATED = 0;
+ int STARRED = 1;
+ }
+
+ /** Columns coming from remote spreadsheet. */
+ private interface Columns {
+ String SESSION_DATE = "sessiondate";
+ String SESSION_TIME = "sessiontime";
+ String ROOM = "room";
+ String PRODUCT = "product";
+ String TRACK = "track";
+ String SESSION_TYPE = "sessiontype";
+ String SESSION_TITLE = "sessiontitle";
+ String TAGS = "tags";
+ String SESSION_SPEAKERS = "sessionspeakers";
+ String SPEAKERS = "speakers";
+ String SESSION_ABSTRACT = "sessionabstract";
+ String SESSION_REQUIREMENTS = "sessionrequirements";
+ String SESSION_LINK = "sessionlink";
+ String SESSION_HASHTAG = "sessionhashtag";
+ String FULLLINK = "fulllink";
+ String YOUTUBELINK = "youtubelink";
+ String PDFLINK = "pdflink";
+
+ String MODERATORLINK = "moderatorlink";
+ String WAVELINK = "wavelink";
+ String WAVEID = "waveid";
+
+ // session_date: Wednesday May 19
+ // session_time: 10:45am-11:45am
+ // room: 6
+ // product: App Engine
+ // track: Enterprise, App Engine
+ // session_type: 201
+ // session_title: Run corporate applications on Google App Engine? Yes we do.
+ // tags: Enterprise, SaaS, PaaS, Hosting, App Engine, Java
+ // session_speakers: Ben Fried
+ // speakers: bf
+ // session_abstract: And you can too! Come hear Google's CIO Ben Fried describe
+ // session_requirements: None
+ // session_link: run-corp-apps-on-app-engine
+ // session_hashtag: #android1
+ // wavelink
+ // fulllink
+ // youtubelink
+ // pdflink
+
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/.svn/text-base/RemoteSpeakersHandler.java.svn-base b/src/com/google/android/apps/iosched/io/.svn/text-base/RemoteSpeakersHandler.java.svn-base
new file mode 100644
index 0000000..dc0cc91
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/.svn/text-base/RemoteSpeakersHandler.java.svn-base
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static com.google.android.apps.iosched.util.ParserUtils.queryItemUpdated;
+import static com.google.android.apps.iosched.util.ParserUtils.sanitizeId;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.ENTRY;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Speakers;
+import com.google.android.apps.iosched.provider.ScheduleContract.SyncColumns;
+import com.google.android.apps.iosched.util.Lists;
+import com.google.android.apps.iosched.util.SpreadsheetEntry;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Handle a remote {@link XmlPullParser} that defines a set of {@link Speakers}
+ * entries. Assumes that the remote source is a Google Spreadsheet.
+ */
+public class RemoteSpeakersHandler extends XmlHandler {
+ private static final String TAG = "SpeakersHandler";
+
+ public RemoteSpeakersHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+
+ // Walk document, parsing any incoming entries
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && ENTRY.equals(parser.getName())) {
+ // Process single spreadsheet row at a time
+ final SpreadsheetEntry entry = SpreadsheetEntry.fromParser(parser);
+
+ final String speakerId = sanitizeId(entry.get(Columns.SPEAKER_TITLE), true);
+ final Uri speakerUri = Speakers.buildSpeakerUri(speakerId);
+
+ // Check for existing details, only update when changed
+ final long localUpdated = queryItemUpdated(speakerUri, resolver);
+ final long serverUpdated = entry.getUpdated();
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "found speaker " + entry.toString());
+ Log.v(TAG, "found localUpdated=" + localUpdated + ", server=" + serverUpdated);
+ }
+ if (localUpdated >= serverUpdated) continue;
+
+ // Clear any existing values for this speaker, treating the
+ // incoming details as authoritative.
+ batch.add(ContentProviderOperation.newDelete(speakerUri).build());
+
+ final ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Speakers.CONTENT_URI);
+
+ builder.withValue(SyncColumns.UPDATED, serverUpdated);
+ builder.withValue(Speakers.SPEAKER_ID, speakerId);
+ builder.withValue(Speakers.SPEAKER_NAME, entry.get(Columns.SPEAKER_TITLE));
+ builder.withValue(Speakers.SPEAKER_COMPANY, entry.get(Columns.SPEAKER_COMPANY));
+ builder.withValue(Speakers.SPEAKER_ABSTRACT, entry.get(Columns.SPEAKER_ABSTRACT));
+
+ // Normal speaker details ready, write to provider
+ batch.add(builder.build());
+ }
+ }
+
+ return batch;
+ }
+
+ /** Columns coming from remote spreadsheet. */
+ private interface Columns {
+ String SPEAKER_TITLE = "speakertitle";
+ String SPEAKER_COMPANY = "speakercompany";
+ String SPEAKER_ABSTRACT = "speakerabstract";
+ String SPEAKER_LDAP = "speakerldap";
+
+ // speaker_title: Aaron Koblin
+ // speaker_company: Google
+ // speaker_abstract: Aaron takes social and infrastructural data and uses
+ // speaker_ldap: AaronKoblin
+
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/.svn/text-base/RemoteVendorsHandler.java.svn-base b/src/com/google/android/apps/iosched/io/.svn/text-base/RemoteVendorsHandler.java.svn-base
new file mode 100644
index 0000000..a2b6664
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/.svn/text-base/RemoteVendorsHandler.java.svn-base
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static com.google.android.apps.iosched.util.ParserUtils.sanitizeId;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.ENTRY;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.SyncColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.util.Lists;
+import com.google.android.apps.iosched.util.ParserUtils;
+import com.google.android.apps.iosched.util.SpreadsheetEntry;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Handle a remote {@link XmlPullParser} that defines a set of {@link Vendors}
+ * entries. Assumes that the remote source is a Google Spreadsheet.
+ */
+public class RemoteVendorsHandler extends XmlHandler {
+ private static final String TAG = "VendorsHandler";
+
+ /** Base {@link Uri} where {@link Columns#COMPANY_LOGO} images live. */
+ private static final Uri LOGO_BASE = Uri
+ .parse("http://code.google.com/events/io/2010/images/");
+
+ public RemoteVendorsHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+
+ // Walk document, parsing any incoming entries
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && ENTRY.equals(parser.getName())) {
+ // Process single spreadsheet row at a time
+ final SpreadsheetEntry entry = SpreadsheetEntry.fromParser(parser);
+
+ final String vendorId = sanitizeId(entry.get(Columns.COMPANY_NAME));
+ final Uri vendorUri = Vendors.buildVendorUri(vendorId);
+
+ // Check for existing details, only update when changed
+ final ContentValues values = queryVendorDetails(vendorUri, resolver);
+ final long localUpdated = values.getAsLong(SyncColumns.UPDATED);
+ final long serverUpdated = entry.getUpdated();
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "found vendor " + entry.toString());
+ Log.v(TAG, "found localUpdated=" + localUpdated + ", server=" + serverUpdated);
+ }
+ if (localUpdated >= serverUpdated) continue;
+
+ // Clear any existing values for this vendor, treating the
+ // incoming details as authoritative.
+ batch.add(ContentProviderOperation.newDelete(vendorUri).build());
+
+ final ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Vendors.CONTENT_URI);
+
+ builder.withValue(SyncColumns.UPDATED, serverUpdated);
+ builder.withValue(Vendors.VENDOR_ID, vendorId);
+ builder.withValue(Vendors.NAME, entry.get(Columns.COMPANY_NAME));
+ builder.withValue(Vendors.LOCATION, entry.get(Columns.COMPANY_LOCATION));
+ builder.withValue(Vendors.DESC, entry.get(Columns.COMPANY_DESC));
+ builder.withValue(Vendors.URL, entry.get(Columns.COMPANY_URL));
+ builder.withValue(Vendors.PRODUCT_DESC, entry.get(Columns.PRODUCT_DESC));
+
+ // Inherit starred value from previous row
+ if (values.containsKey(Vendors.STARRED)) {
+ builder.withValue(Vendors.STARRED, values.getAsInteger(Vendors.STARRED));
+ }
+
+ // Assign track
+ final String trackId = ParserUtils.translateTrackIdAlias(sanitizeId(entry
+ .get(Columns.COMPANY_POD)));
+ builder.withValue(Vendors.TRACK_ID, trackId);
+
+ // Assign logo path
+ final String logoUrl = LOGO_BASE.buildUpon().appendPath(
+ entry.get(Columns.COMPANY_LOGO)).build().toString();
+ builder.withValue(Vendors.LOGO_URL, logoUrl);
+
+ // Normal vendor details ready, write to provider
+ batch.add(builder.build());
+ }
+ }
+
+ return batch;
+ }
+
+ private static ContentValues queryVendorDetails(Uri uri, ContentResolver resolver) {
+ final ContentValues values = new ContentValues();
+ final Cursor cursor = resolver.query(uri, VendorsQuery.PROJECTION, null, null, null);
+ try {
+ if (cursor.moveToFirst()) {
+ values.put(SyncColumns.UPDATED, cursor.getLong(VendorsQuery.UPDATED));
+ values.put(Vendors.STARRED, cursor.getInt(VendorsQuery.STARRED));
+ } else {
+ values.put(SyncColumns.UPDATED, ScheduleContract.UPDATED_NEVER);
+ }
+ } finally {
+ cursor.close();
+ }
+ return values;
+ }
+
+ private interface VendorsQuery {
+ String[] PROJECTION = {
+ SyncColumns.UPDATED,
+ Vendors.STARRED,
+ };
+
+ int UPDATED = 0;
+ int STARRED = 1;
+ }
+
+ /** Columns coming from remote spreadsheet. */
+ private interface Columns {
+ String COMPANY_NAME = "companyname";
+ String COMPANY_LOCATION = "companylocation";
+ String COMPANY_DESC = "companydesc";
+ String COMPANY_URL = "companyurl";
+ String PRODUCT_DESC = "productdesc";
+ String COMPANY_LOGO = "companylogo";
+ String COMPANY_POD = "companypod";
+ String COMPANY_TAGS = "companytags";
+
+ // company_name: 280 North, Inc.
+ // company_location: San Francisco, California
+ // company_desc: Creators of 280 Slides, a web based presentation
+ // company_url: www.280north.com
+ // product_desc: 280 Slides relies on the Google AJAX APIs to provide
+ // company_logo: 280north.png
+ // company_pod: Google APIs
+ // company_tags:
+
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/.svn/text-base/RemoteWorksheetsHandler.java.svn-base b/src/com/google/android/apps/iosched/io/.svn/text-base/RemoteWorksheetsHandler.java.svn-base
new file mode 100644
index 0000000..2b13718
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/.svn/text-base/RemoteWorksheetsHandler.java.svn-base
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.ENTRY;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Speakers;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.util.Lists;
+import com.google.android.apps.iosched.util.Maps;
+import com.google.android.apps.iosched.util.ParserUtils;
+import com.google.android.apps.iosched.util.WorksheetEntry;
+
+import org.apache.http.client.methods.HttpGet;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class RemoteWorksheetsHandler extends XmlHandler {
+ private static final String TAG = "WorksheetsHandler";
+
+ private RemoteExecutor mExecutor;
+
+ public RemoteWorksheetsHandler(RemoteExecutor executor) {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ mExecutor = executor;
+ }
+
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final HashMap sheets = Maps.newHashMap();
+
+ // walk response, collecting all known spreadsheets
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && ENTRY.equals(parser.getName())) {
+ final WorksheetEntry entry = WorksheetEntry.fromParser(parser);
+ Log.d(TAG, "found worksheet " + entry.toString());
+ sheets.put(entry.getTitle(), entry);
+ }
+ }
+
+ // consider updating each spreadsheet based on update timestamp
+ considerUpdate(sheets, Worksheets.SESSIONS, Sessions.CONTENT_URI, resolver);
+ considerUpdate(sheets, Worksheets.SPEAKERS, Speakers.CONTENT_URI, resolver);
+ considerUpdate(sheets, Worksheets.VENDORS, Vendors.CONTENT_URI, resolver);
+
+ return Lists.newArrayList();
+ }
+
+ private void considerUpdate(HashMap sheets, String sheetName,
+ Uri targetDir, ContentResolver resolver) throws HandlerException {
+ final WorksheetEntry entry = sheets.get(sheetName);
+ if (entry == null) {
+ // Silently ignore missing spreadsheets to allow sync to continue.
+ Log.w(TAG, "Missing '" + sheetName + "' worksheet data");
+ return;
+// throw new HandlerException("Missing '" + sheetName + "' worksheet data");
+ }
+
+ final long localUpdated = ParserUtils.queryDirUpdated(targetDir, resolver);
+ final long serverUpdated = entry.getUpdated();
+ Log.d(TAG, "considerUpdate() for " + entry.getTitle() + " found localUpdated="
+ + localUpdated + ", server=" + serverUpdated);
+ if (localUpdated >= serverUpdated) return;
+
+ final HttpGet request = new HttpGet(entry.getListFeed());
+ final XmlHandler handler = createRemoteHandler(entry);
+ mExecutor.execute(request, handler);
+ }
+
+ private XmlHandler createRemoteHandler(WorksheetEntry entry) {
+ final String title = entry.getTitle();
+ if (Worksheets.SESSIONS.equals(title)) {
+ return new RemoteSessionsHandler();
+ } else if (Worksheets.SPEAKERS.equals(title)) {
+ return new RemoteSpeakersHandler();
+ } else if (Worksheets.VENDORS.equals(title)) {
+ return new RemoteVendorsHandler();
+ } else {
+ throw new IllegalArgumentException("Unknown worksheet type");
+ }
+ }
+
+ interface Worksheets {
+ String SESSIONS = "sessions";
+ String SPEAKERS = "speakers";
+ String VENDORS = "sandbox";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/.svn/text-base/XmlHandler.java.svn-base b/src/com/google/android/apps/iosched/io/.svn/text-base/XmlHandler.java.svn-base
new file mode 100644
index 0000000..e6cfbe0
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/.svn/text-base/XmlHandler.java.svn-base
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProvider;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.OperationApplicationException;
+import android.os.RemoteException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Abstract class that handles reading and parsing an {@link XmlPullParser} into
+ * a set of {@link ContentProviderOperation}. It catches recoverable network
+ * exceptions and rethrows them as {@link HandlerException}. Any local
+ * {@link ContentProvider} exceptions are considered unrecoverable.
+ *
+ * This class is only designed to handle simple one-way synchronization.
+ */
+public abstract class XmlHandler {
+ private final String mAuthority;
+
+ public XmlHandler(String authority) {
+ mAuthority = authority;
+ }
+
+ /**
+ * Parse the given {@link XmlPullParser}, turning into a series of
+ * {@link ContentProviderOperation} that are immediately applied using the
+ * given {@link ContentResolver}.
+ */
+ public void parseAndApply(XmlPullParser parser, ContentResolver resolver)
+ throws HandlerException {
+ try {
+ final ArrayList batch = parse(parser, resolver);
+ resolver.applyBatch(mAuthority, batch);
+
+ } catch (HandlerException e) {
+ throw e;
+ } catch (XmlPullParserException e) {
+ throw new HandlerException("Problem parsing XML response", e);
+ } catch (IOException e) {
+ throw new HandlerException("Problem reading response", e);
+ } catch (RemoteException e) {
+ // Failed binder transactions aren't recoverable
+ throw new RuntimeException("Problem applying batch operation", e);
+ } catch (OperationApplicationException e) {
+ // Failures like constraint violation aren't recoverable
+ // TODO: write unit tests to exercise full provider
+ // TODO: consider catching version checking asserts here, and then
+ // wrapping around to retry parsing again.
+ throw new RuntimeException("Problem applying batch operation", e);
+ }
+ }
+
+ /**
+ * Parse the given {@link XmlPullParser}, returning a set of
+ * {@link ContentProviderOperation} that will bring the
+ * {@link ContentProvider} into sync with the parsed data.
+ */
+ public abstract ArrayList parse(XmlPullParser parser,
+ ContentResolver resolver) throws XmlPullParserException, IOException;
+
+ /**
+ * General {@link IOException} that indicates a problem occured while
+ * parsing or applying an {@link XmlPullParser}.
+ */
+ public static class HandlerException extends IOException {
+ public HandlerException(String message) {
+ super(message);
+ }
+
+ public HandlerException(String message, Throwable cause) {
+ super(message);
+ initCause(cause);
+ }
+
+ @Override
+ public String toString() {
+ if (getCause() != null) {
+ return getLocalizedMessage() + ": " + getCause();
+ } else {
+ return getLocalizedMessage();
+ }
+ }
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/LocalBlocksHandler.java b/src/com/google/android/apps/iosched/io/LocalBlocksHandler.java
new file mode 100644
index 0000000..417d376
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/LocalBlocksHandler.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.util.Lists;
+import com.google.android.apps.iosched.util.ParserUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class LocalBlocksHandler extends XmlHandler {
+
+ public LocalBlocksHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+
+ // Clear any existing static blocks, as they may have been updated.
+ final String selection = Blocks.BLOCK_TYPE + "=? OR " + Blocks.BLOCK_TYPE +"=?";
+ final String[] selectionArgs = {
+ ParserUtils.BLOCK_TYPE_FOOD,
+ ParserUtils.BLOCK_TYPE_OFFICE_HOURS
+ };
+ batch.add(ContentProviderOperation.newDelete(Blocks.CONTENT_URI)
+ .withSelection(selection, selectionArgs).build());
+
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && Tags.BLOCK.equals(parser.getName())) {
+ batch.add(parseBlock(parser));
+ }
+ }
+
+ return batch;
+ }
+
+ private static ContentProviderOperation parseBlock(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ final int depth = parser.getDepth();
+ final ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Blocks.CONTENT_URI);
+
+ String title = null;
+ long startTime = -1;
+ long endTime = -1;
+ String blockType = null;
+
+ String tag = null;
+ int type;
+ while (((type = parser.next()) != END_TAG ||
+ parser.getDepth() > depth) && type != END_DOCUMENT) {
+ if (type == START_TAG) {
+ tag = parser.getName();
+ } else if (type == END_TAG) {
+ tag = null;
+ } else if (type == TEXT) {
+ final String text = parser.getText();
+ if (Tags.TITLE.equals(tag)) {
+ title = text;
+ } else if (Tags.START.equals(tag)) {
+ startTime = ParserUtils.parseTime(text);
+ } else if (Tags.END.equals(tag)) {
+ endTime = ParserUtils.parseTime(text);
+ } else if (Tags.TYPE.equals(tag)) {
+ blockType = text;
+ }
+ }
+ }
+
+ final String blockId = Blocks.generateBlockId(startTime, endTime);
+
+ builder.withValue(Blocks.BLOCK_ID, blockId);
+ builder.withValue(Blocks.BLOCK_TITLE, title);
+ builder.withValue(Blocks.BLOCK_START, startTime);
+ builder.withValue(Blocks.BLOCK_END, endTime);
+ builder.withValue(Blocks.BLOCK_TYPE, blockType);
+
+ return builder.build();
+ }
+
+ interface Tags {
+ String BLOCK = "block";
+ String TITLE = "title";
+ String START = "start";
+ String END = "end";
+ String TYPE = "type";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/LocalExecutor.java b/src/com/google/android/apps/iosched/io/LocalExecutor.java
new file mode 100644
index 0000000..ca33237
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/LocalExecutor.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import com.google.android.apps.iosched.io.XmlHandler.HandlerException;
+import com.google.android.apps.iosched.util.ParserUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Opens a local {@link Resources#getXml(int)} and passes the resulting
+ * {@link XmlPullParser} to the given {@link XmlHandler}.
+ */
+public class LocalExecutor {
+ private Resources mRes;
+ private ContentResolver mResolver;
+
+ public LocalExecutor(Resources res, ContentResolver resolver) {
+ mRes = res;
+ mResolver = resolver;
+ }
+
+ public void execute(Context context, String assetName, XmlHandler handler)
+ throws HandlerException {
+ try {
+ final InputStream input = context.getAssets().open(assetName);
+ final XmlPullParser parser = ParserUtils.newPullParser(input);
+ handler.parseAndApply(parser, mResolver);
+ } catch (HandlerException e) {
+ throw e;
+ } catch (XmlPullParserException e) {
+ throw new HandlerException("Problem parsing local asset: " + assetName, e);
+ } catch (IOException e) {
+ throw new HandlerException("Problem parsing local asset: " + assetName, e);
+ }
+ }
+
+ public void execute(int resId, XmlHandler handler) throws HandlerException {
+ final XmlResourceParser parser = mRes.getXml(resId);
+ try {
+ handler.parseAndApply(parser, mResolver);
+ } finally {
+ parser.close();
+ }
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/LocalRoomsHandler.java b/src/com/google/android/apps/iosched/io/LocalRoomsHandler.java
new file mode 100644
index 0000000..57df67d
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/LocalRoomsHandler.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.util.Lists;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Handle a local {@link XmlPullParser} that defines a set of {@link Rooms}
+ * entries. Usually loaded from {@link R.xml} resources.
+ */
+public class LocalRoomsHandler extends XmlHandler {
+
+ public LocalRoomsHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && Tags.ROOM.equals(parser.getName())) {
+ parseRoom(parser, batch, resolver);
+ }
+ }
+
+ return batch;
+ }
+
+ /**
+ * Parse a given {@link Rooms} entry, building
+ * {@link ContentProviderOperation} to define it locally.
+ */
+ private static void parseRoom(XmlPullParser parser,
+ ArrayList batch, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final int depth = parser.getDepth();
+ ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Rooms.CONTENT_URI);
+
+ String tag = null;
+ int type;
+ while (((type = parser.next()) != END_TAG ||
+ parser.getDepth() > depth) && type != END_DOCUMENT) {
+ if (type == START_TAG) {
+ tag = parser.getName();
+ } else if (type == END_TAG) {
+ tag = null;
+ } else if (type == TEXT) {
+ final String text = parser.getText();
+ if (Tags.ID.equals(tag)) {
+ builder.withValue(Rooms.ROOM_ID, text);
+ } else if (Tags.NAME.equals(tag)) {
+ builder.withValue(Rooms.ROOM_NAME, text);
+ } else if (Tags.FLOOR.equals(tag)) {
+ builder.withValue(Rooms.ROOM_FLOOR, text);
+ }
+ }
+ }
+
+ batch.add(builder.build());
+ }
+
+ /** XML tags expected from local source. */
+ private interface Tags {
+ String ROOM = "room";
+ String ID = "id";
+ String NAME = "name";
+ String FLOOR = "floor";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/LocalSearchSuggestHandler.java b/src/com/google/android/apps/iosched/io/LocalSearchSuggestHandler.java
new file mode 100644
index 0000000..4b88e3a
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/LocalSearchSuggestHandler.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.SearchSuggest;
+import com.google.android.apps.iosched.util.Lists;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.app.SearchManager;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class LocalSearchSuggestHandler extends XmlHandler {
+
+ public LocalSearchSuggestHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+
+ // Clear any existing suggestion words
+ batch.add(ContentProviderOperation.newDelete(SearchSuggest.CONTENT_URI).build());
+
+ String tag = null;
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG) {
+ tag = parser.getName();
+ } else if (type == END_TAG) {
+ tag = null;
+ } else if (type == TEXT) {
+ final String text = parser.getText();
+ if (Tags.WORD.equals(tag)) {
+ // Insert word as search suggestion
+ batch.add(ContentProviderOperation.newInsert(SearchSuggest.CONTENT_URI)
+ .withValue(SearchManager.SUGGEST_COLUMN_TEXT_1, text).build());
+ }
+ }
+ }
+
+ return batch;
+ }
+
+ private interface Tags {
+ String WORD = "word";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/LocalSessionsHandler.java b/src/com/google/android/apps/iosched/io/LocalSessionsHandler.java
new file mode 100644
index 0000000..3c719f7
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/LocalSessionsHandler.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.SessionsTracks;
+import com.google.android.apps.iosched.util.Lists;
+import com.google.android.apps.iosched.util.ParserUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class LocalSessionsHandler extends XmlHandler {
+
+ public LocalSessionsHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && Tags.SESSION.equals(parser.getName())) {
+ parseSession(parser, batch, resolver);
+ }
+ }
+
+ return batch;
+ }
+
+ private static void parseSession(XmlPullParser parser,
+ ArrayList batch, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final int depth = parser.getDepth();
+ ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Sessions.CONTENT_URI);
+ builder.withValue(Sessions.UPDATED, 0);
+
+ long startTime = -1;
+ long endTime = -1;
+ String title = null;
+ String sessionId = null;
+ String trackId = null;
+
+ String tag = null;
+ int type;
+ while (((type = parser.next()) != END_TAG ||
+ parser.getDepth() > depth) && type != END_DOCUMENT) {
+ if (type == START_TAG) {
+ tag = parser.getName();
+ } else if (type == END_TAG) {
+ tag = null;
+ } else if (type == TEXT) {
+ final String text = parser.getText();
+ if (Tags.START.equals(tag)) {
+ startTime = ParserUtils.parseTime(text);
+ } else if (Tags.END.equals(tag)) {
+ endTime = ParserUtils.parseTime(text);
+ } else if (Tags.ROOM.equals(tag)) {
+ final String roomId = Rooms.generateRoomId(text);
+ builder.withValue(Sessions.ROOM_ID, roomId);
+ } else if (Tags.TRACK.equals(tag)) {
+ trackId = Tracks.generateTrackId(text);
+ } else if (Tags.ID.equals(tag)) {
+ sessionId = text;
+ } else if (Tags.TITLE.equals(tag)) {
+ title = text;
+ } else if (Tags.ABSTRACT.equals(tag)) {
+ builder.withValue(Sessions.ABSTRACT, text);
+ }
+ }
+ }
+
+ if (sessionId == null) {
+ sessionId = Sessions.generateSessionId(title);
+ }
+
+ builder.withValue(Sessions.SESSION_ID, sessionId);
+ builder.withValue(Sessions.TITLE, title);
+
+ // Use empty strings to make sure SQLite search trigger has valid data
+ // for updating search index.
+ builder.withValue(Sessions.ABSTRACT, "");
+ builder.withValue(Sessions.REQUIREMENTS, "");
+ builder.withValue(Sessions.KEYWORDS, "");
+
+ final String blockId = ParserUtils.findBlock(title, startTime, endTime);
+ builder.withValue(Sessions.BLOCK_ID, blockId);
+
+ // Propagate any existing starred value
+ final Uri sessionUri = Sessions.buildSessionUri(sessionId);
+ final int starred = querySessionStarred(sessionUri, resolver);
+ if (starred != -1) {
+ builder.withValue(Sessions.STARRED, starred);
+ }
+
+ batch.add(builder.build());
+
+ if (trackId != null) {
+ // TODO: support parsing multiple tracks per session
+ final Uri sessionTracks = Sessions.buildTracksDirUri(sessionId);
+ batch.add(ContentProviderOperation.newInsert(sessionTracks)
+ .withValue(SessionsTracks.SESSION_ID, sessionId)
+ .withValue(SessionsTracks.TRACK_ID, trackId).build());
+ }
+ }
+
+ public static int querySessionStarred(Uri uri, ContentResolver resolver) {
+ final String[] projection = { Sessions.STARRED };
+ final Cursor cursor = resolver.query(uri, projection, null, null, null);
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getInt(0);
+ } else {
+ return -1;
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ interface Tags {
+ String SESSION = "session";
+ String ID = "id";
+ String START = "start";
+ String END = "end";
+ String ROOM = "room";
+ String TRACK = "track";
+ String TITLE = "title";
+ String ABSTRACT = "abstract";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/LocalTracksHandler.java b/src/com/google/android/apps/iosched/io/LocalTracksHandler.java
new file mode 100644
index 0000000..91aa12e
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/LocalTracksHandler.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static com.google.android.apps.iosched.util.ParserUtils.sanitizeId;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.util.Lists;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.graphics.Color;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class LocalTracksHandler extends XmlHandler {
+
+ public LocalTracksHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+ batch.add(ContentProviderOperation.newDelete(Tracks.CONTENT_URI).build());
+
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && Tags.TRACK.equals(parser.getName())) {
+ batch.add(parseTrack(parser));
+ }
+ }
+
+ return batch;
+ }
+
+ private static ContentProviderOperation parseTrack(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ final int depth = parser.getDepth();
+ final ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Tracks.CONTENT_URI);
+
+ String tag = null;
+ int type;
+ while (((type = parser.next()) != END_TAG ||
+ parser.getDepth() > depth) && type != END_DOCUMENT) {
+ if (type == START_TAG) {
+ tag = parser.getName();
+ } else if (type == END_TAG) {
+ tag = null;
+ } else if (type == TEXT) {
+ final String text = parser.getText();
+ if (Tags.NAME.equals(tag)) {
+ final String trackId = sanitizeId(text);
+ builder.withValue(Tracks.TRACK_ID, trackId);
+ builder.withValue(Tracks.TRACK_NAME, text);
+ } else if (Tags.COLOR.equals(tag)) {
+ final int color = Color.parseColor(text);
+ builder.withValue(Tracks.TRACK_COLOR, color);
+ } else if (Tags.ABSTRACT.equals(tag)) {
+ builder.withValue(Tracks.TRACK_ABSTRACT, text);
+ }
+ }
+ }
+
+ return builder.build();
+ }
+
+ interface Tags {
+ String TRACK = "track";
+ String NAME = "name";
+ String COLOR = "color";
+ String ABSTRACT = "abstract";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/RemoteExecutor.java b/src/com/google/android/apps/iosched/io/RemoteExecutor.java
new file mode 100644
index 0000000..5ee071a
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/RemoteExecutor.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import com.google.android.apps.iosched.io.XmlHandler.HandlerException;
+import com.google.android.apps.iosched.util.ParserUtils;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentResolver;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Executes an {@link HttpUriRequest} and passes the result as an
+ * {@link XmlPullParser} to the given {@link XmlHandler}.
+ */
+public class RemoteExecutor {
+ private final HttpClient mHttpClient;
+ private final ContentResolver mResolver;
+
+ public RemoteExecutor(HttpClient httpClient, ContentResolver resolver) {
+ mHttpClient = httpClient;
+ mResolver = resolver;
+ }
+
+ /**
+ * Execute a {@link HttpGet} request, passing a valid response through
+ * {@link XmlHandler#parseAndApply(XmlPullParser, ContentResolver)}.
+ */
+ public void executeGet(String url, XmlHandler handler) throws HandlerException {
+ final HttpUriRequest request = new HttpGet(url);
+ execute(request, handler);
+ }
+
+ /**
+ * Execute this {@link HttpUriRequest}, passing a valid response through
+ * {@link XmlHandler#parseAndApply(XmlPullParser, ContentResolver)}.
+ */
+ public void execute(HttpUriRequest request, XmlHandler handler) throws HandlerException {
+ try {
+ final HttpResponse resp = mHttpClient.execute(request);
+ final int status = resp.getStatusLine().getStatusCode();
+ if (status != HttpStatus.SC_OK) {
+ throw new HandlerException("Unexpected server response " + resp.getStatusLine()
+ + " for " + request.getRequestLine());
+ }
+
+ final InputStream input = resp.getEntity().getContent();
+ try {
+ final XmlPullParser parser = ParserUtils.newPullParser(input);
+ handler.parseAndApply(parser, mResolver);
+ } catch (XmlPullParserException e) {
+ throw new HandlerException("Malformed response for " + request.getRequestLine(), e);
+ } finally {
+ if (input != null) input.close();
+ }
+ } catch (HandlerException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new HandlerException("Problem reading remote response for "
+ + request.getRequestLine(), e);
+ }
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/RemoteSessionsHandler.java b/src/com/google/android/apps/iosched/io/RemoteSessionsHandler.java
new file mode 100644
index 0000000..9a71908
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/RemoteSessionsHandler.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static com.google.android.apps.iosched.util.ParserUtils.sanitizeId;
+import static com.google.android.apps.iosched.util.ParserUtils.splitComma;
+import static com.google.android.apps.iosched.util.ParserUtils.translateTrackIdAlias;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.ENTRY;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.SyncColumns;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.SessionsSpeakers;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.SessionsTracks;
+import com.google.android.apps.iosched.util.Lists;
+import com.google.android.apps.iosched.util.ParserUtils;
+import com.google.android.apps.iosched.util.SpreadsheetEntry;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.text.format.Time;
+import android.util.Log;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Locale;
+
+/**
+ * Handle a remote {@link XmlPullParser} that defines a set of {@link Sessions}
+ * entries. Assumes that the remote source is a Google Spreadsheet.
+ */
+public class RemoteSessionsHandler extends XmlHandler {
+ private static final String TAG = "SessionsHandler";
+
+ /**
+ * Custom format used internally that matches expected concatenation of
+ * {@link Columns#SESSION_DATE} and {@link Columns#SESSION_TIME}.
+ */
+ private static final SimpleDateFormat sTimeFormat = new SimpleDateFormat(
+ "EEEE MMM d yyyy h:mma Z", Locale.US);
+
+ public RemoteSessionsHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+
+ // Walk document, parsing any incoming entries
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && ENTRY.equals(parser.getName())) {
+ // Process single spreadsheet row at a time
+ final SpreadsheetEntry entry = SpreadsheetEntry.fromParser(parser);
+
+ final String sessionId = sanitizeId(entry.get(Columns.SESSION_LINK));
+ final Uri sessionUri = Sessions.buildSessionUri(sessionId);
+
+ // Check for existing details, only update when changed
+ final ContentValues values = querySessionDetails(sessionUri, resolver);
+ final long localUpdated = values.getAsLong(SyncColumns.UPDATED);
+ final long serverUpdated = entry.getUpdated();
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "found session " + entry.toString());
+ Log.v(TAG, "found localUpdated=" + localUpdated + ", server=" + serverUpdated);
+ }
+ if (localUpdated >= serverUpdated) continue;
+
+ final Uri sessionTracksUri = Sessions.buildTracksDirUri(sessionId);
+ final Uri sessionSpeakersUri = Sessions.buildSpeakersDirUri(sessionId);
+
+ // Clear any existing values for this session, treating the
+ // incoming details as authoritative.
+ batch.add(ContentProviderOperation.newDelete(sessionUri).build());
+ batch.add(ContentProviderOperation.newDelete(sessionTracksUri).build());
+ batch.add(ContentProviderOperation.newDelete(sessionSpeakersUri).build());
+
+ final ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Sessions.CONTENT_URI);
+
+ builder.withValue(SyncColumns.UPDATED, serverUpdated);
+ builder.withValue(Sessions.SESSION_ID, sessionId);
+ builder.withValue(Sessions.TYPE, entry.get(Columns.SESSION_TYPE));
+ builder.withValue(Sessions.TITLE, entry.get(Columns.SESSION_TITLE));
+ builder.withValue(Sessions.ABSTRACT, entry.get(Columns.SESSION_ABSTRACT));
+ builder.withValue(Sessions.REQUIREMENTS, entry.get(Columns.SESSION_REQUIREMENTS));
+ builder.withValue(Sessions.MODERATOR_URL, entry.get(Columns.MODERATORLINK));
+ builder.withValue(Sessions.WAVE_URL, entry.get(Columns.WAVELINK));
+ builder.withValue(Sessions.KEYWORDS, entry.get(Columns.TAGS));
+ builder.withValue(Sessions.HASHTAG, entry.get(Columns.SESSION_HASHTAG));
+
+ // Inherit starred value from previous row
+ if (values.containsKey(Sessions.STARRED)) {
+ builder.withValue(Sessions.STARRED, values.getAsInteger(Sessions.STARRED));
+ }
+
+ // Parse time string from two columns, which is pretty ugly code
+ // since it assumes the column format is "Wednesday May 19" and
+ // "10:45am-11:45am". Future spreadsheets should use RFC 3339.
+ final String date = entry.get(Columns.SESSION_DATE);
+ final String time = entry.get(Columns.SESSION_TIME);
+ final int timeSplit = time.indexOf("-");
+ if (timeSplit == -1) {
+ throw new HandlerException("Expecting " + Columns.SESSION_TIME
+ + " to express span");
+ }
+
+ final long startTime = parseTime(date, time.substring(0, timeSplit));
+ final long endTime = parseTime(date, time.substring(timeSplit + 1));
+
+ final String blockId = ParserUtils.findOrCreateBlock(
+ ParserUtils.BLOCK_TITLE_BREAKOUT_SESSIONS,
+ ParserUtils.BLOCK_TYPE_SESSION,
+ startTime, endTime, batch, resolver);
+ builder.withValue(Sessions.BLOCK_ID, blockId);
+
+ // Assign room
+ final String roomId = sanitizeId(entry.get(Columns.ROOM));
+ builder.withValue(Sessions.ROOM_ID, roomId);
+
+ // Normal session details ready, write to provider
+ batch.add(builder.build());
+
+ // Assign tracks
+ final String[] tracks = splitComma(entry.get(Columns.TRACK));
+ for (String track : tracks) {
+ final String trackId = translateTrackIdAlias(sanitizeId(track));
+ batch.add(ContentProviderOperation.newInsert(sessionTracksUri)
+ .withValue(SessionsTracks.SESSION_ID, sessionId)
+ .withValue(SessionsTracks.TRACK_ID, trackId).build());
+ }
+
+ // Assign speakers
+ final String[] speakers = splitComma(entry.get(Columns.SESSION_SPEAKERS));
+ for (String speaker : speakers) {
+ final String speakerId = sanitizeId(speaker, true);
+ batch.add(ContentProviderOperation.newInsert(sessionSpeakersUri)
+ .withValue(SessionsSpeakers.SESSION_ID, sessionId)
+ .withValue(SessionsSpeakers.SPEAKER_ID, speakerId).build());
+ }
+ }
+ }
+
+ return batch;
+ }
+
+ /**
+ * Parse the given date and time coming from spreadsheet. This is tightly
+ * tied to a specific format. Ideally, if the source used use RFC 3339 we
+ * could parse quickly using {@link Time#parse3339}.
+ *
+ * Internally assumes PST time zone and year 2010.
+ *
+ * @param date String of format "Wednesday May 19", usually read from
+ * {@link Columns#SESSION_DATE}.
+ * @param time String of format "10:45am", usually after splitting
+ * {@link Columns#SESSION_TIME}.
+ */
+ private static long parseTime(String date, String time) throws HandlerException {
+ final String composed = String.format("%s 2010 %s -0700", date, time);
+ try {
+ return sTimeFormat.parse(composed).getTime();
+ } catch (java.text.ParseException e) {
+ throw new HandlerException("Problem parsing timestamp", e);
+ }
+ }
+
+ private static ContentValues querySessionDetails(Uri uri, ContentResolver resolver) {
+ final ContentValues values = new ContentValues();
+ final Cursor cursor = resolver.query(uri, SessionsQuery.PROJECTION, null, null, null);
+ try {
+ if (cursor.moveToFirst()) {
+ values.put(SyncColumns.UPDATED, cursor.getLong(SessionsQuery.UPDATED));
+ values.put(Sessions.STARRED, cursor.getInt(SessionsQuery.STARRED));
+ } else {
+ values.put(SyncColumns.UPDATED, ScheduleContract.UPDATED_NEVER);
+ }
+ } finally {
+ cursor.close();
+ }
+ return values;
+ }
+
+ private interface SessionsQuery {
+ String[] PROJECTION = {
+ SyncColumns.UPDATED,
+ Sessions.STARRED,
+ };
+
+ int UPDATED = 0;
+ int STARRED = 1;
+ }
+
+ /** Columns coming from remote spreadsheet. */
+ private interface Columns {
+ String SESSION_DATE = "sessiondate";
+ String SESSION_TIME = "sessiontime";
+ String ROOM = "room";
+ String PRODUCT = "product";
+ String TRACK = "track";
+ String SESSION_TYPE = "sessiontype";
+ String SESSION_TITLE = "sessiontitle";
+ String TAGS = "tags";
+ String SESSION_SPEAKERS = "sessionspeakers";
+ String SPEAKERS = "speakers";
+ String SESSION_ABSTRACT = "sessionabstract";
+ String SESSION_REQUIREMENTS = "sessionrequirements";
+ String SESSION_LINK = "sessionlink";
+ String SESSION_HASHTAG = "sessionhashtag";
+ String FULLLINK = "fulllink";
+ String YOUTUBELINK = "youtubelink";
+ String PDFLINK = "pdflink";
+
+ String MODERATORLINK = "moderatorlink";
+ String WAVELINK = "wavelink";
+ String WAVEID = "waveid";
+
+ // session_date: Wednesday May 19
+ // session_time: 10:45am-11:45am
+ // room: 6
+ // product: App Engine
+ // track: Enterprise, App Engine
+ // session_type: 201
+ // session_title: Run corporate applications on Google App Engine? Yes we do.
+ // tags: Enterprise, SaaS, PaaS, Hosting, App Engine, Java
+ // session_speakers: Ben Fried
+ // speakers: bf
+ // session_abstract: And you can too! Come hear Google's CIO Ben Fried describe
+ // session_requirements: None
+ // session_link: run-corp-apps-on-app-engine
+ // session_hashtag: #android1
+ // wavelink
+ // fulllink
+ // youtubelink
+ // pdflink
+
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/RemoteSpeakersHandler.java b/src/com/google/android/apps/iosched/io/RemoteSpeakersHandler.java
new file mode 100644
index 0000000..dc0cc91
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/RemoteSpeakersHandler.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static com.google.android.apps.iosched.util.ParserUtils.queryItemUpdated;
+import static com.google.android.apps.iosched.util.ParserUtils.sanitizeId;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.ENTRY;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Speakers;
+import com.google.android.apps.iosched.provider.ScheduleContract.SyncColumns;
+import com.google.android.apps.iosched.util.Lists;
+import com.google.android.apps.iosched.util.SpreadsheetEntry;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Handle a remote {@link XmlPullParser} that defines a set of {@link Speakers}
+ * entries. Assumes that the remote source is a Google Spreadsheet.
+ */
+public class RemoteSpeakersHandler extends XmlHandler {
+ private static final String TAG = "SpeakersHandler";
+
+ public RemoteSpeakersHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+
+ // Walk document, parsing any incoming entries
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && ENTRY.equals(parser.getName())) {
+ // Process single spreadsheet row at a time
+ final SpreadsheetEntry entry = SpreadsheetEntry.fromParser(parser);
+
+ final String speakerId = sanitizeId(entry.get(Columns.SPEAKER_TITLE), true);
+ final Uri speakerUri = Speakers.buildSpeakerUri(speakerId);
+
+ // Check for existing details, only update when changed
+ final long localUpdated = queryItemUpdated(speakerUri, resolver);
+ final long serverUpdated = entry.getUpdated();
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "found speaker " + entry.toString());
+ Log.v(TAG, "found localUpdated=" + localUpdated + ", server=" + serverUpdated);
+ }
+ if (localUpdated >= serverUpdated) continue;
+
+ // Clear any existing values for this speaker, treating the
+ // incoming details as authoritative.
+ batch.add(ContentProviderOperation.newDelete(speakerUri).build());
+
+ final ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Speakers.CONTENT_URI);
+
+ builder.withValue(SyncColumns.UPDATED, serverUpdated);
+ builder.withValue(Speakers.SPEAKER_ID, speakerId);
+ builder.withValue(Speakers.SPEAKER_NAME, entry.get(Columns.SPEAKER_TITLE));
+ builder.withValue(Speakers.SPEAKER_COMPANY, entry.get(Columns.SPEAKER_COMPANY));
+ builder.withValue(Speakers.SPEAKER_ABSTRACT, entry.get(Columns.SPEAKER_ABSTRACT));
+
+ // Normal speaker details ready, write to provider
+ batch.add(builder.build());
+ }
+ }
+
+ return batch;
+ }
+
+ /** Columns coming from remote spreadsheet. */
+ private interface Columns {
+ String SPEAKER_TITLE = "speakertitle";
+ String SPEAKER_COMPANY = "speakercompany";
+ String SPEAKER_ABSTRACT = "speakerabstract";
+ String SPEAKER_LDAP = "speakerldap";
+
+ // speaker_title: Aaron Koblin
+ // speaker_company: Google
+ // speaker_abstract: Aaron takes social and infrastructural data and uses
+ // speaker_ldap: AaronKoblin
+
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/RemoteVendorsHandler.java b/src/com/google/android/apps/iosched/io/RemoteVendorsHandler.java
new file mode 100644
index 0000000..a2b6664
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/RemoteVendorsHandler.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static com.google.android.apps.iosched.util.ParserUtils.sanitizeId;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.ENTRY;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.SyncColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.util.Lists;
+import com.google.android.apps.iosched.util.ParserUtils;
+import com.google.android.apps.iosched.util.SpreadsheetEntry;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Handle a remote {@link XmlPullParser} that defines a set of {@link Vendors}
+ * entries. Assumes that the remote source is a Google Spreadsheet.
+ */
+public class RemoteVendorsHandler extends XmlHandler {
+ private static final String TAG = "VendorsHandler";
+
+ /** Base {@link Uri} where {@link Columns#COMPANY_LOGO} images live. */
+ private static final Uri LOGO_BASE = Uri
+ .parse("http://code.google.com/events/io/2010/images/");
+
+ public RemoteVendorsHandler() {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final ArrayList batch = Lists.newArrayList();
+
+ // Walk document, parsing any incoming entries
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && ENTRY.equals(parser.getName())) {
+ // Process single spreadsheet row at a time
+ final SpreadsheetEntry entry = SpreadsheetEntry.fromParser(parser);
+
+ final String vendorId = sanitizeId(entry.get(Columns.COMPANY_NAME));
+ final Uri vendorUri = Vendors.buildVendorUri(vendorId);
+
+ // Check for existing details, only update when changed
+ final ContentValues values = queryVendorDetails(vendorUri, resolver);
+ final long localUpdated = values.getAsLong(SyncColumns.UPDATED);
+ final long serverUpdated = entry.getUpdated();
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "found vendor " + entry.toString());
+ Log.v(TAG, "found localUpdated=" + localUpdated + ", server=" + serverUpdated);
+ }
+ if (localUpdated >= serverUpdated) continue;
+
+ // Clear any existing values for this vendor, treating the
+ // incoming details as authoritative.
+ batch.add(ContentProviderOperation.newDelete(vendorUri).build());
+
+ final ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Vendors.CONTENT_URI);
+
+ builder.withValue(SyncColumns.UPDATED, serverUpdated);
+ builder.withValue(Vendors.VENDOR_ID, vendorId);
+ builder.withValue(Vendors.NAME, entry.get(Columns.COMPANY_NAME));
+ builder.withValue(Vendors.LOCATION, entry.get(Columns.COMPANY_LOCATION));
+ builder.withValue(Vendors.DESC, entry.get(Columns.COMPANY_DESC));
+ builder.withValue(Vendors.URL, entry.get(Columns.COMPANY_URL));
+ builder.withValue(Vendors.PRODUCT_DESC, entry.get(Columns.PRODUCT_DESC));
+
+ // Inherit starred value from previous row
+ if (values.containsKey(Vendors.STARRED)) {
+ builder.withValue(Vendors.STARRED, values.getAsInteger(Vendors.STARRED));
+ }
+
+ // Assign track
+ final String trackId = ParserUtils.translateTrackIdAlias(sanitizeId(entry
+ .get(Columns.COMPANY_POD)));
+ builder.withValue(Vendors.TRACK_ID, trackId);
+
+ // Assign logo path
+ final String logoUrl = LOGO_BASE.buildUpon().appendPath(
+ entry.get(Columns.COMPANY_LOGO)).build().toString();
+ builder.withValue(Vendors.LOGO_URL, logoUrl);
+
+ // Normal vendor details ready, write to provider
+ batch.add(builder.build());
+ }
+ }
+
+ return batch;
+ }
+
+ private static ContentValues queryVendorDetails(Uri uri, ContentResolver resolver) {
+ final ContentValues values = new ContentValues();
+ final Cursor cursor = resolver.query(uri, VendorsQuery.PROJECTION, null, null, null);
+ try {
+ if (cursor.moveToFirst()) {
+ values.put(SyncColumns.UPDATED, cursor.getLong(VendorsQuery.UPDATED));
+ values.put(Vendors.STARRED, cursor.getInt(VendorsQuery.STARRED));
+ } else {
+ values.put(SyncColumns.UPDATED, ScheduleContract.UPDATED_NEVER);
+ }
+ } finally {
+ cursor.close();
+ }
+ return values;
+ }
+
+ private interface VendorsQuery {
+ String[] PROJECTION = {
+ SyncColumns.UPDATED,
+ Vendors.STARRED,
+ };
+
+ int UPDATED = 0;
+ int STARRED = 1;
+ }
+
+ /** Columns coming from remote spreadsheet. */
+ private interface Columns {
+ String COMPANY_NAME = "companyname";
+ String COMPANY_LOCATION = "companylocation";
+ String COMPANY_DESC = "companydesc";
+ String COMPANY_URL = "companyurl";
+ String PRODUCT_DESC = "productdesc";
+ String COMPANY_LOGO = "companylogo";
+ String COMPANY_POD = "companypod";
+ String COMPANY_TAGS = "companytags";
+
+ // company_name: 280 North, Inc.
+ // company_location: San Francisco, California
+ // company_desc: Creators of 280 Slides, a web based presentation
+ // company_url: www.280north.com
+ // product_desc: 280 Slides relies on the Google AJAX APIs to provide
+ // company_logo: 280north.png
+ // company_pod: Google APIs
+ // company_tags:
+
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/RemoteWorksheetsHandler.java b/src/com/google/android/apps/iosched/io/RemoteWorksheetsHandler.java
new file mode 100644
index 0000000..2b13718
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/RemoteWorksheetsHandler.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.ENTRY;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Speakers;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.util.Lists;
+import com.google.android.apps.iosched.util.Maps;
+import com.google.android.apps.iosched.util.ParserUtils;
+import com.google.android.apps.iosched.util.WorksheetEntry;
+
+import org.apache.http.client.methods.HttpGet;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class RemoteWorksheetsHandler extends XmlHandler {
+ private static final String TAG = "WorksheetsHandler";
+
+ private RemoteExecutor mExecutor;
+
+ public RemoteWorksheetsHandler(RemoteExecutor executor) {
+ super(ScheduleContract.CONTENT_AUTHORITY);
+ mExecutor = executor;
+ }
+
+ @Override
+ public ArrayList parse(XmlPullParser parser, ContentResolver resolver)
+ throws XmlPullParserException, IOException {
+ final HashMap sheets = Maps.newHashMap();
+
+ // walk response, collecting all known spreadsheets
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ if (type == START_TAG && ENTRY.equals(parser.getName())) {
+ final WorksheetEntry entry = WorksheetEntry.fromParser(parser);
+ Log.d(TAG, "found worksheet " + entry.toString());
+ sheets.put(entry.getTitle(), entry);
+ }
+ }
+
+ // consider updating each spreadsheet based on update timestamp
+ considerUpdate(sheets, Worksheets.SESSIONS, Sessions.CONTENT_URI, resolver);
+ considerUpdate(sheets, Worksheets.SPEAKERS, Speakers.CONTENT_URI, resolver);
+ considerUpdate(sheets, Worksheets.VENDORS, Vendors.CONTENT_URI, resolver);
+
+ return Lists.newArrayList();
+ }
+
+ private void considerUpdate(HashMap sheets, String sheetName,
+ Uri targetDir, ContentResolver resolver) throws HandlerException {
+ final WorksheetEntry entry = sheets.get(sheetName);
+ if (entry == null) {
+ // Silently ignore missing spreadsheets to allow sync to continue.
+ Log.w(TAG, "Missing '" + sheetName + "' worksheet data");
+ return;
+// throw new HandlerException("Missing '" + sheetName + "' worksheet data");
+ }
+
+ final long localUpdated = ParserUtils.queryDirUpdated(targetDir, resolver);
+ final long serverUpdated = entry.getUpdated();
+ Log.d(TAG, "considerUpdate() for " + entry.getTitle() + " found localUpdated="
+ + localUpdated + ", server=" + serverUpdated);
+ if (localUpdated >= serverUpdated) return;
+
+ final HttpGet request = new HttpGet(entry.getListFeed());
+ final XmlHandler handler = createRemoteHandler(entry);
+ mExecutor.execute(request, handler);
+ }
+
+ private XmlHandler createRemoteHandler(WorksheetEntry entry) {
+ final String title = entry.getTitle();
+ if (Worksheets.SESSIONS.equals(title)) {
+ return new RemoteSessionsHandler();
+ } else if (Worksheets.SPEAKERS.equals(title)) {
+ return new RemoteSpeakersHandler();
+ } else if (Worksheets.VENDORS.equals(title)) {
+ return new RemoteVendorsHandler();
+ } else {
+ throw new IllegalArgumentException("Unknown worksheet type");
+ }
+ }
+
+ interface Worksheets {
+ String SESSIONS = "sessions";
+ String SPEAKERS = "speakers";
+ String VENDORS = "sandbox";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/io/XmlHandler.java b/src/com/google/android/apps/iosched/io/XmlHandler.java
new file mode 100644
index 0000000..e6cfbe0
--- /dev/null
+++ b/src/com/google/android/apps/iosched/io/XmlHandler.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentProvider;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.OperationApplicationException;
+import android.os.RemoteException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Abstract class that handles reading and parsing an {@link XmlPullParser} into
+ * a set of {@link ContentProviderOperation}. It catches recoverable network
+ * exceptions and rethrows them as {@link HandlerException}. Any local
+ * {@link ContentProvider} exceptions are considered unrecoverable.
+ *
+ * This class is only designed to handle simple one-way synchronization.
+ */
+public abstract class XmlHandler {
+ private final String mAuthority;
+
+ public XmlHandler(String authority) {
+ mAuthority = authority;
+ }
+
+ /**
+ * Parse the given {@link XmlPullParser}, turning into a series of
+ * {@link ContentProviderOperation} that are immediately applied using the
+ * given {@link ContentResolver}.
+ */
+ public void parseAndApply(XmlPullParser parser, ContentResolver resolver)
+ throws HandlerException {
+ try {
+ final ArrayList batch = parse(parser, resolver);
+ resolver.applyBatch(mAuthority, batch);
+
+ } catch (HandlerException e) {
+ throw e;
+ } catch (XmlPullParserException e) {
+ throw new HandlerException("Problem parsing XML response", e);
+ } catch (IOException e) {
+ throw new HandlerException("Problem reading response", e);
+ } catch (RemoteException e) {
+ // Failed binder transactions aren't recoverable
+ throw new RuntimeException("Problem applying batch operation", e);
+ } catch (OperationApplicationException e) {
+ // Failures like constraint violation aren't recoverable
+ // TODO: write unit tests to exercise full provider
+ // TODO: consider catching version checking asserts here, and then
+ // wrapping around to retry parsing again.
+ throw new RuntimeException("Problem applying batch operation", e);
+ }
+ }
+
+ /**
+ * Parse the given {@link XmlPullParser}, returning a set of
+ * {@link ContentProviderOperation} that will bring the
+ * {@link ContentProvider} into sync with the parsed data.
+ */
+ public abstract ArrayList parse(XmlPullParser parser,
+ ContentResolver resolver) throws XmlPullParserException, IOException;
+
+ /**
+ * General {@link IOException} that indicates a problem occured while
+ * parsing or applying an {@link XmlPullParser}.
+ */
+ public static class HandlerException extends IOException {
+ public HandlerException(String message) {
+ super(message);
+ }
+
+ public HandlerException(String message, Throwable cause) {
+ super(message);
+ initCause(cause);
+ }
+
+ @Override
+ public String toString() {
+ if (getCause() != null) {
+ return getLocalizedMessage() + ": " + getCause();
+ } else {
+ return getLocalizedMessage();
+ }
+ }
+ }
+}
diff --git a/src/com/google/android/apps/iosched/provider/.svn/all-wcprops b/src/com/google/android/apps/iosched/provider/.svn/all-wcprops
new file mode 100644
index 0000000..f8fab1e
--- /dev/null
+++ b/src/com/google/android/apps/iosched/provider/.svn/all-wcprops
@@ -0,0 +1,23 @@
+K 25
+svn:wc:ra_dav:version-url
+V 66
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/provider
+END
+ScheduleDatabase.java
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/provider/ScheduleDatabase.java
+END
+ScheduleProvider.java
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/provider/ScheduleProvider.java
+END
+ScheduleContract.java
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/provider/ScheduleContract.java
+END
diff --git a/src/com/google/android/apps/iosched/provider/.svn/entries b/src/com/google/android/apps/iosched/provider/.svn/entries
new file mode 100644
index 0000000..4a1398b
--- /dev/null
+++ b/src/com/google/android/apps/iosched/provider/.svn/entries
@@ -0,0 +1,130 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/src/com/google/android/apps/iosched/provider
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+ScheduleDatabase.java
+file
+
+
+
+
+2010-06-03T15:23:15.377627Z
+fa2baf1ee5c58899cafe1363479726b9
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+18633
+
+ScheduleProvider.java
+file
+
+
+
+
+2010-06-03T15:23:15.377627Z
+fe3e5800be7c40249323a52628c5ee12
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+33632
+
+ScheduleContract.java
+file
+
+
+
+
+2010-06-03T15:23:15.377627Z
+989586eef8a25851251300520770a668
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+21916
+
diff --git a/src/com/google/android/apps/iosched/provider/.svn/text-base/ScheduleContract.java.svn-base b/src/com/google/android/apps/iosched/provider/.svn/text-base/ScheduleContract.java.svn-base
new file mode 100644
index 0000000..c3c3973
--- /dev/null
+++ b/src/com/google/android/apps/iosched/provider/.svn/text-base/ScheduleContract.java.svn-base
@@ -0,0 +1,556 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.provider;
+
+import com.google.android.apps.iosched.util.ParserUtils;
+
+import android.app.SearchManager;
+import android.content.ContentUris;
+import android.graphics.Color;
+import android.net.Uri;
+import android.provider.BaseColumns;
+import android.text.format.DateUtils;
+
+/**
+ * Contract class for interacting with {@link ScheduleProvider}. Unless
+ * otherwise noted, all time-based fields are milliseconds since epoch and can
+ * be compared against {@link System#currentTimeMillis()}.
+ *
+ * The backing {@link ContentProvider} assumes that {@link Uri} are generated
+ * using stronger {@link String} identifiers, instead of {@code int}
+ * {@link BaseColumns#_ID} values, which are prone to shuffle during sync.
+ */
+public class ScheduleContract {
+
+ /**
+ * Special value for {@link SyncColumns#UPDATED} indicating that an entry
+ * has never been updated, or doesn't exist yet.
+ */
+ public static final long UPDATED_NEVER = -2;
+
+ /**
+ * Special value for {@link SyncColumns#UPDATED} indicating that the last
+ * update time is unknown, usually when inserted from a local file source.
+ */
+ public static final long UPDATED_UNKNOWN = -1;
+
+ public interface SyncColumns {
+ /** Last time this entry was updated or synchronized. */
+ String UPDATED = "updated";
+ }
+
+ interface BlocksColumns {
+ /** Unique string identifying this block of time. */
+ String BLOCK_ID = "block_id";
+ /** Title describing this block of time. */
+ String BLOCK_TITLE = "block_title";
+ /** Time when this block starts. */
+ String BLOCK_START = "block_start";
+ /** Time when this block ends. */
+ String BLOCK_END = "block_end";
+ /** Type describing this block. */
+ String BLOCK_TYPE = "block_type";
+ }
+
+ interface TracksColumns {
+ /** Unique string identifying this track. */
+ String TRACK_ID = "track_id";
+ /** Name describing this track. */
+ String TRACK_NAME = "track_name";
+ /** Color used to identify this track, in {@link Color#argb} format. */
+ String TRACK_COLOR = "track_color";
+ /** Body of text explaining this track in detail. */
+ String TRACK_ABSTRACT = "track_abstract";
+ }
+
+ interface RoomsColumns {
+ /** Unique string identifying this room. */
+ String ROOM_ID = "room_id";
+ /** Name describing this room. */
+ String ROOM_NAME = "room_name";
+ /** Building floor this room exists on. */
+ String ROOM_FLOOR = "room_floor";
+ }
+
+ interface SessionsColumns {
+ /** Unique string identifying this session. */
+ String SESSION_ID = "session_id";
+ /** Type of session, such as difficulty level. */
+ String TYPE = "type";
+ /** Title describing this track. */
+ String TITLE = "title";
+ /** Body of text explaining this session in detail. */
+ String ABSTRACT = "abstract";
+ /** Requirements that attendees should meet. */
+ String REQUIREMENTS = "requirements";
+ /** User-specific flag indicating starred status. */
+ String STARRED = "starred";
+ /** Link to Moderator for this session. */
+ String MODERATOR_URL = "moderator_url";
+ /** Link to Wave for this session. */
+ String WAVE_URL = "wave_url";
+ /** Additional keywords that describe this session. */
+ String KEYWORDS = "keywords";
+ /** Hashtag used to identify this session in social media. */
+ String HASHTAG = "hashtag";
+
+ // TODO: moderator, wave, other online links
+ }
+
+ interface SpeakersColumns {
+ /** Unique string identifying this speaker. */
+ String SPEAKER_ID = "speaker_id";
+ /** Name of this speaker. */
+ String SPEAKER_NAME = "speaker_name";
+ /** Company this speaker works for. */
+ String SPEAKER_COMPANY = "speaker_company";
+ /** Body of text describing this speaker in detail. */
+ String SPEAKER_ABSTRACT = "speaker_abstract";
+ }
+
+ interface VendorsColumns {
+ /** Unique string identifying this vendor. */
+ String VENDOR_ID = "vendor_id";
+ /** Name of this vendor. */
+ String NAME = "name";
+ /** Location or city this vendor is based in. */
+ String LOCATION = "location";
+ /** Body of text describing this vendor. */
+ String DESC = "desc";
+ /** Link to vendor online. */
+ String URL = "url";
+ /** Body of text describing the product of this vendor. */
+ String PRODUCT_DESC = "product_desc";
+ /** Link to vendor logo. */
+ String LOGO_URL = "logo_url";
+ /** {@link #LOGO_URL} stored as local blob, when available. */
+ String LOGO = "logo";
+ /** User-specific flag indicating starred status. */
+ String STARRED = "starred";
+ }
+
+ interface NotesColumns {
+ /** Time this note was created. */
+ String NOTE_TIME = "note_time";
+ /** User-generated content of note. */
+ String NOTE_CONTENT = "note_content";
+ }
+
+ public static final String CONTENT_AUTHORITY = "com.google.android.apps.iosched";
+
+ private static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
+
+ private static final String PATH_BLOCKS = "blocks";
+ private static final String PATH_AT = "at";
+ private static final String PATH_BETWEEN = "between";
+ private static final String PATH_TRACKS = "tracks";
+ private static final String PATH_ROOMS = "rooms";
+ private static final String PATH_SESSIONS = "sessions";
+ private static final String PATH_STARRED = "starred";
+ private static final String PATH_SPEAKERS = "speakers";
+ private static final String PATH_VENDORS = "vendors";
+ private static final String PATH_NOTES = "notes";
+ private static final String PATH_EXPORT = "export";
+ private static final String PATH_SEARCH = "search";
+ private static final String PATH_SEARCH_SUGGEST = "search_suggest_query";
+
+ /**
+ * Blocks are generic timeslots that {@link Sessions} and other related
+ * events fall into.
+ */
+ public static class Blocks implements BlocksColumns, BaseColumns {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_BLOCKS).build();
+
+ public static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/vnd.iosched.block";
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.iosched.block";
+
+ /** Count of {@link Sessions} inside given block. */
+ public static final String SESSIONS_COUNT = "sessions_count";
+
+ /**
+ * Flag indicating that at least one {@link Sessions#SESSION_ID} inside
+ * this block has {@link Sessions#STARRED} set.
+ */
+ public static final String CONTAINS_STARRED = "contains_starred";
+
+ /** Default "ORDER BY" clause. */
+ public static final String DEFAULT_SORT = BlocksColumns.BLOCK_START + " ASC, "
+ + BlocksColumns.BLOCK_END + " ASC";
+
+ /** Build {@link Uri} for requested {@link #BLOCK_ID}. */
+ public static Uri buildBlockUri(String blockId) {
+ return CONTENT_URI.buildUpon().appendPath(blockId).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Sessions} associated
+ * with the requested {@link #BLOCK_ID}.
+ */
+ public static Uri buildSessionsUri(String blockId) {
+ return CONTENT_URI.buildUpon().appendPath(blockId).appendPath(PATH_SESSIONS).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Blocks} that occur
+ * between the requested time boundaries.
+ */
+ public static Uri buildBlocksBetweenDirUri(long startTime, long endTime) {
+ return CONTENT_URI.buildUpon().appendPath(PATH_BETWEEN).appendPath(
+ String.valueOf(startTime)).appendPath(String.valueOf(endTime)).build();
+ }
+
+ /** Read {@link #BLOCK_ID} from {@link Blocks} {@link Uri}. */
+ public static String getBlockId(Uri uri) {
+ return uri.getPathSegments().get(1);
+ }
+
+ /**
+ * Generate a {@link #BLOCK_ID} that will always match the requested
+ * {@link Blocks} details.
+ */
+ public static String generateBlockId(long startTime, long endTime) {
+ startTime /= DateUtils.SECOND_IN_MILLIS;
+ endTime /= DateUtils.SECOND_IN_MILLIS;
+ return ParserUtils.sanitizeId(startTime + "-" + endTime);
+ }
+ }
+
+ /**
+ * Tracks are overall categories for {@link Sessions} and {@link Vendors},
+ * such as "Android" or "Enterprise."
+ */
+ public static class Tracks implements TracksColumns, BaseColumns {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_TRACKS).build();
+
+ public static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/vnd.iosched.track";
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.iosched.track";
+
+ /** Count of {@link Sessions} inside given track. */
+ public static final String SESSIONS_COUNT = "sessions_count";
+ /** Count of {@link Vendors} inside given track. */
+ public static final String VENDORS_COUNT = "vendors_count";
+
+ /** Default "ORDER BY" clause. */
+ public static final String DEFAULT_SORT = TracksColumns.TRACK_NAME + " ASC";
+
+ /** Build {@link Uri} for requested {@link #TRACK_ID}. */
+ public static Uri buildTrackUri(String trackId) {
+ return CONTENT_URI.buildUpon().appendPath(trackId).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Sessions} associated
+ * with the requested {@link #TRACK_ID}.
+ */
+ public static Uri buildSessionsUri(String trackId) {
+ return CONTENT_URI.buildUpon().appendPath(trackId).appendPath(PATH_SESSIONS).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Vendors} associated with
+ * the requested {@link #TRACK_ID}.
+ */
+ public static Uri buildVendorsUri(String trackId) {
+ return CONTENT_URI.buildUpon().appendPath(trackId).appendPath(PATH_VENDORS).build();
+ }
+
+ /** Read {@link #TRACK_ID} from {@link Tracks} {@link Uri}. */
+ public static String getTrackId(Uri uri) {
+ return uri.getPathSegments().get(1);
+ }
+
+ /**
+ * Generate a {@link #TRACK_ID} that will always match the requested
+ * {@link Tracks} details.
+ */
+ public static String generateTrackId(String title) {
+ return ParserUtils.sanitizeId(title);
+ }
+ }
+
+ /**
+ * Rooms are physical locations at the conference venue.
+ */
+ public static class Rooms implements RoomsColumns, BaseColumns {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_ROOMS).build();
+
+ public static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/vnd.iosched.room";
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.iosched.room";
+
+ /** Default "ORDER BY" clause. */
+ public static final String DEFAULT_SORT = RoomsColumns.ROOM_FLOOR + " ASC, "
+ + RoomsColumns.ROOM_NAME + " COLLATE NOCASE ASC";
+
+ /** Build {@link Uri} for requested {@link #ROOM_ID}. */
+ public static Uri buildRoomUri(String roomId) {
+ return CONTENT_URI.buildUpon().appendPath(roomId).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Sessions} associated
+ * with the requested {@link #ROOM_ID}.
+ */
+ public static Uri buildSessionsDirUri(String roomId) {
+ return CONTENT_URI.buildUpon().appendPath(roomId).appendPath(PATH_SESSIONS).build();
+ }
+
+ /** Read {@link #ROOM_ID} from {@link Rooms} {@link Uri}. */
+ public static String getRoomId(Uri uri) {
+ return uri.getPathSegments().get(1);
+ }
+
+ /**
+ * Generate a {@link #ROOM_ID} that will always match the requested
+ * {@link Rooms} details.
+ */
+ public static String generateRoomId(String room) {
+ return ParserUtils.sanitizeId(room);
+ }
+ }
+
+ /**
+ * Each session is a block of time that has a {@link Tracks}, a
+ * {@link Rooms}, and zero or more {@link Speakers}.
+ */
+ public static class Sessions implements SessionsColumns, BlocksColumns, RoomsColumns,
+ SyncColumns, BaseColumns {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_SESSIONS).build();
+ public static final Uri CONTENT_STARRED_URI =
+ CONTENT_URI.buildUpon().appendPath(PATH_STARRED).build();
+
+ public static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/vnd.iosched.session";
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.iosched.session";
+
+ public static final String BLOCK_ID = "block_id";
+ public static final String ROOM_ID = "room_id";
+
+ public static final String SEARCH_SNIPPET = "search_snippet";
+
+ // TODO: shortcut primary track to offer sub-sorting here
+ /** Default "ORDER BY" clause. */
+ public static final String DEFAULT_SORT = BlocksColumns.BLOCK_START + " ASC,"
+ + SessionsColumns.TITLE + " COLLATE NOCASE ASC";
+
+ /** Build {@link Uri} for requested {@link #SESSION_ID}. */
+ public static Uri buildSessionUri(String sessionId) {
+ return CONTENT_URI.buildUpon().appendPath(sessionId).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Speakers} associated
+ * with the requested {@link #SESSION_ID}.
+ */
+ public static Uri buildSpeakersDirUri(String sessionId) {
+ return CONTENT_URI.buildUpon().appendPath(sessionId).appendPath(PATH_SPEAKERS).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Tracks} associated with
+ * the requested {@link #SESSION_ID}.
+ */
+ public static Uri buildTracksDirUri(String sessionId) {
+ return CONTENT_URI.buildUpon().appendPath(sessionId).appendPath(PATH_TRACKS).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Notes} associated with
+ * the requested {@link #SESSION_ID}.
+ */
+ public static Uri buildNotesDirUri(String sessionId) {
+ return CONTENT_URI.buildUpon().appendPath(sessionId).appendPath(PATH_NOTES).build();
+ }
+
+ public static Uri buildSessionsAtDirUri(long time) {
+ return CONTENT_URI.buildUpon().appendPath(PATH_AT).appendPath(String.valueOf(time))
+ .build();
+ }
+
+ public static Uri buildSearchUri(String query) {
+ return CONTENT_URI.buildUpon().appendPath(PATH_SEARCH).appendPath(query).build();
+ }
+
+ public static boolean isSearchUri(Uri uri) {
+ return PATH_SEARCH.equals(uri.getPathSegments().get(1));
+ }
+
+ /** Read {@link #SESSION_ID} from {@link Sessions} {@link Uri}. */
+ public static String getSessionId(Uri uri) {
+ return uri.getPathSegments().get(1);
+ }
+
+ public static String getSearchQuery(Uri uri) {
+ return uri.getPathSegments().get(2);
+ }
+
+ /**
+ * Generate a {@link #SESSION_ID} that will always match the requested
+ * {@link Sessions} details.
+ */
+ public static String generateSessionId(String title) {
+ return ParserUtils.sanitizeId(title);
+ }
+ }
+
+ /**
+ * Speakers are individual people that lead {@link Sessions}.
+ */
+ public static class Speakers implements SpeakersColumns, SyncColumns, BaseColumns {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_SPEAKERS).build();
+
+ public static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/vnd.iosched.speaker";
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.iosched.speaker";
+
+ /** Default "ORDER BY" clause. */
+ public static final String DEFAULT_SORT = SpeakersColumns.SPEAKER_NAME
+ + " COLLATE NOCASE ASC";
+
+ /** Build {@link Uri} for requested {@link #SPEAKER_ID}. */
+ public static Uri buildSpeakerUri(String speakerId) {
+ return CONTENT_URI.buildUpon().appendPath(speakerId).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Sessions} associated
+ * with the requested {@link #SPEAKER_ID}.
+ */
+ public static Uri buildSessionsDirUri(String speakerId) {
+ return CONTENT_URI.buildUpon().appendPath(speakerId).appendPath(PATH_SESSIONS).build();
+ }
+
+ /** Read {@link #SPEAKER_ID} from {@link Speakers} {@link Uri}. */
+ public static String getSpeakerId(Uri uri) {
+ return uri.getPathSegments().get(1);
+ }
+
+ /**
+ * Generate a {@link #SPEAKER_ID} that will always match the requested
+ * {@link Speakers} details.
+ */
+ public static String generateSpeakerId(String speakerLdap) {
+ return ParserUtils.sanitizeId(speakerLdap);
+ }
+ }
+
+ /**
+ * Each vendor is a company appearing at the conference that may be
+ * associated with a specific {@link Tracks}.
+ */
+ public static class Vendors implements VendorsColumns, SyncColumns, BaseColumns {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_VENDORS).build();
+ public static final Uri CONTENT_STARRED_URI =
+ CONTENT_URI.buildUpon().appendPath(PATH_STARRED).build();
+
+ public static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/vnd.iosched.vendor";
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.iosched.vendor";
+
+ /** {@link Tracks#TRACK_ID} that this vendor belongs to. */
+ public static final String TRACK_ID = "track_id";
+
+ public static final String SEARCH_SNIPPET = "search_snippet";
+
+ /** Default "ORDER BY" clause. */
+ public static final String DEFAULT_SORT = VendorsColumns.NAME + " COLLATE NOCASE ASC";
+
+ /** Build {@link Uri} for requested {@link #VENDOR_ID}. */
+ public static Uri buildVendorUri(String vendorId) {
+ return CONTENT_URI.buildUpon().appendPath(vendorId).build();
+ }
+
+ public static Uri buildSearchUri(String query) {
+ return CONTENT_URI.buildUpon().appendPath(PATH_SEARCH).appendPath(query).build();
+ }
+
+ public static boolean isSearchUri(Uri uri) {
+ return PATH_SEARCH.equals(uri.getPathSegments().get(1));
+ }
+
+ /** Read {@link #VENDOR_ID} from {@link Vendors} {@link Uri}. */
+ public static String getVendorId(Uri uri) {
+ return uri.getPathSegments().get(1);
+ }
+
+ public static String getSearchQuery(Uri uri) {
+ return uri.getPathSegments().get(2);
+ }
+
+ /**
+ * Generate a {@link #VENDOR_ID} that will always match the requested
+ * {@link Vendors} details.
+ */
+ public static String generateVendorId(String companyLogo) {
+ return ParserUtils.sanitizeId(companyLogo);
+ }
+ }
+
+ /**
+ * Notes are user-generated data related to specific {@link Sessions}.
+ */
+ public static class Notes implements NotesColumns, BaseColumns {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_NOTES).build();
+ public static final Uri CONTENT_EXPORT_URI =
+ CONTENT_URI.buildUpon().appendPath(PATH_EXPORT).build();
+
+ /** {@link Sessions#SESSION_ID} that this note references. */
+ public static final String SESSION_ID = "session_id";
+
+ /** Default "ORDER BY" clause. */
+ public static final String DEFAULT_SORT = NotesColumns.NOTE_TIME + " DESC";
+
+ public static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/vnd.iosched.note";
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.iosched.note";
+
+ public static Uri buildNoteUri(long noteId) {
+ return ContentUris.withAppendedId(CONTENT_URI, noteId);
+ }
+
+ public static long getNoteId(Uri uri) {
+ return ContentUris.parseId(uri);
+ }
+ }
+
+ public static class SearchSuggest {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_SEARCH_SUGGEST).build();
+
+ public static final String DEFAULT_SORT = SearchManager.SUGGEST_COLUMN_TEXT_1
+ + " COLLATE NOCASE ASC";
+ }
+
+ private ScheduleContract() {
+ }
+}
diff --git a/src/com/google/android/apps/iosched/provider/.svn/text-base/ScheduleDatabase.java.svn-base b/src/com/google/android/apps/iosched/provider/.svn/text-base/ScheduleDatabase.java.svn-base
new file mode 100644
index 0000000..dde3c99
--- /dev/null
+++ b/src/com/google/android/apps/iosched/provider/.svn/text-base/ScheduleDatabase.java.svn-base
@@ -0,0 +1,383 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.provider;
+
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.provider.ScheduleContract.BlocksColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Notes;
+import com.google.android.apps.iosched.provider.ScheduleContract.NotesColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.provider.ScheduleContract.RoomsColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.SessionsColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Speakers;
+import com.google.android.apps.iosched.provider.ScheduleContract.SpeakersColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.SyncColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.provider.ScheduleContract.TracksColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.provider.ScheduleContract.VendorsColumns;
+
+import android.app.SearchManager;
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.provider.BaseColumns;
+import android.util.Log;
+
+/**
+ * Helper for managing {@link SQLiteDatabase} that stores data for
+ * {@link ScheduleProvider}.
+ */
+public class ScheduleDatabase extends SQLiteOpenHelper {
+ private static final String TAG = "ScheduleDatabase";
+
+ private static final String DATABASE_NAME = "schedule.db";
+
+ // NOTE: carefully update onUpgrade() when bumping database versions to make
+ // sure user data is saved.
+
+ private static final int VER_LAUNCH = 18;
+ private static final int VER_SESSION_HASHTAG = 19;
+
+ private static final int DATABASE_VERSION = VER_SESSION_HASHTAG;
+
+ interface Tables {
+ String BLOCKS = "blocks";
+ String TRACKS = "tracks";
+ String ROOMS = "rooms";
+ String SESSIONS = "sessions";
+ String SPEAKERS = "speakers";
+ String SESSIONS_SPEAKERS = "sessions_speakers";
+ String SESSIONS_TRACKS = "sessions_tracks";
+ String VENDORS = "vendors";
+ String NOTES = "notes";
+
+ String SESSIONS_SEARCH = "sessions_search";
+ String VENDORS_SEARCH = "vendors_search";
+
+ String SEARCH_SUGGEST = "search_suggest";
+
+ String SESSIONS_JOIN_BLOCKS_ROOMS = "sessions "
+ + "LEFT OUTER JOIN blocks ON sessions.block_id=blocks.block_id "
+ + "LEFT OUTER JOIN rooms ON sessions.room_id=rooms.room_id";
+
+ String VENDORS_JOIN_TRACKS = "vendors "
+ + "LEFT OUTER JOIN tracks ON vendors.track_id=tracks.track_id";
+
+ String SESSIONS_SPEAKERS_JOIN_SPEAKERS = "sessions_speakers "
+ + "LEFT OUTER JOIN speakers ON sessions_speakers.speaker_id=speakers.speaker_id";
+
+ String SESSIONS_SPEAKERS_JOIN_SESSIONS_BLOCKS_ROOMS = "sessions_speakers "
+ + "LEFT OUTER JOIN sessions ON sessions_speakers.session_id=sessions.session_id "
+ + "LEFT OUTER JOIN blocks ON sessions.block_id=blocks.block_id "
+ + "LEFT OUTER JOIN rooms ON sessions.room_id=rooms.room_id";
+
+ String SESSIONS_TRACKS_JOIN_TRACKS = "sessions_tracks "
+ + "LEFT OUTER JOIN tracks ON sessions_tracks.track_id=tracks.track_id";
+
+ String SESSIONS_TRACKS_JOIN_SESSIONS_BLOCKS_ROOMS = "sessions_tracks "
+ + "LEFT OUTER JOIN sessions ON sessions_tracks.session_id=sessions.session_id "
+ + "LEFT OUTER JOIN blocks ON sessions.block_id=blocks.block_id "
+ + "LEFT OUTER JOIN rooms ON sessions.room_id=rooms.room_id";
+
+ String SESSIONS_SEARCH_JOIN_SESSIONS_BLOCKS_ROOMS = "sessions_search "
+ + "LEFT OUTER JOIN sessions ON sessions_search.session_id=sessions.session_id "
+ + "LEFT OUTER JOIN blocks ON sessions.block_id=blocks.block_id "
+ + "LEFT OUTER JOIN rooms ON sessions.room_id=rooms.room_id";
+
+ String VENDORS_SEARCH_JOIN_VENDORS_TRACKS = "vendors_search "
+ + "LEFT OUTER JOIN vendors ON vendors_search.vendor_id=vendors.vendor_id "
+ + "LEFT OUTER JOIN tracks ON vendors.track_id=tracks.track_id";
+
+ }
+
+ private interface Triggers {
+ String SESSIONS_SEARCH_INSERT = "sessions_search_insert";
+ String SESSIONS_SEARCH_DELETE = "sessions_search_delete";
+
+ String VENDORS_SEARCH_INSERT = "vendors_search_insert";
+ String VENDORS_SEARCH_DELETE = "vendors_search_delete";
+ }
+
+ public interface SessionsSpeakers {
+ String SESSION_ID = "session_id";
+ String SPEAKER_ID = "speaker_id";
+ }
+
+ public interface SessionsTracks {
+ String SESSION_ID = "session_id";
+ String TRACK_ID = "track_id";
+ }
+
+ interface SessionsSearchColumns {
+ String SESSION_ID = "session_id";
+ String BODY = "body";
+ }
+
+ interface VendorsSearchColumns {
+ String VENDOR_ID = "vendor_id";
+ String BODY = "body";
+ }
+
+ /** Fully-qualified field names. */
+ private interface Qualified {
+ String SESSIONS_SEARCH_SESSION_ID = Tables.SESSIONS_SEARCH + "."
+ + SessionsSearchColumns.SESSION_ID;
+ String VENDORS_SEARCH_VENDOR_ID = Tables.VENDORS_SEARCH + "."
+ + VendorsSearchColumns.VENDOR_ID;
+
+ String SESSIONS_SEARCH = Tables.SESSIONS_SEARCH + "(" + SessionsSearchColumns.SESSION_ID
+ + "," + SessionsSearchColumns.BODY + ")";
+ String VENDORS_SEARCH = Tables.VENDORS_SEARCH + "(" + VendorsSearchColumns.VENDOR_ID + ","
+ + VendorsSearchColumns.BODY + ")";
+ }
+
+ /** {@code REFERENCES} clauses. */
+ private interface References {
+ String BLOCK_ID = "REFERENCES " + Tables.BLOCKS + "(" + Blocks.BLOCK_ID + ")";
+ String TRACK_ID = "REFERENCES " + Tables.TRACKS + "(" + Tracks.TRACK_ID + ")";
+ String ROOM_ID = "REFERENCES " + Tables.ROOMS + "(" + Rooms.ROOM_ID + ")";
+ String SESSION_ID = "REFERENCES " + Tables.SESSIONS + "(" + Sessions.SESSION_ID + ")";
+ String SPEAKER_ID = "REFERENCES " + Tables.SPEAKERS + "(" + Speakers.SPEAKER_ID + ")";
+ String VENDOR_ID = "REFERENCES " + Tables.VENDORS + "(" + Vendors.VENDOR_ID + ")";
+ }
+
+ private interface Subquery {
+ /**
+ * Subquery used to build the {@link SessionsSearchColumns#BODY} string
+ * used for indexing {@link Sessions} content.
+ */
+ String SESSIONS_BODY = "(new." + Sessions.TITLE + "||'; '||new." + Sessions.ABSTRACT
+ + "||'; '||new." + Sessions.REQUIREMENTS + "||'; '||" + "new." + Sessions.KEYWORDS
+ + ")";
+
+ /**
+ * Subquery used to build the {@link VendorsSearchColumns#BODY} string
+ * used for indexing {@link Vendors} content.
+ */
+ String VENDORS_BODY = "(new." + Vendors.NAME + "||'; '||new." + Vendors.DESC
+ + "||'; '||new." + Vendors.PRODUCT_DESC + ")";
+ }
+
+ public ScheduleDatabase(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + Tables.BLOCKS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + BlocksColumns.BLOCK_ID + " TEXT NOT NULL,"
+ + BlocksColumns.BLOCK_TITLE + " TEXT NOT NULL,"
+ + BlocksColumns.BLOCK_START + " INTEGER NOT NULL,"
+ + BlocksColumns.BLOCK_END + " INTEGER NOT NULL,"
+ + BlocksColumns.BLOCK_TYPE + " TEXT,"
+ + "UNIQUE (" + BlocksColumns.BLOCK_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.TRACKS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + TracksColumns.TRACK_ID + " TEXT NOT NULL,"
+ + TracksColumns.TRACK_NAME + " TEXT,"
+ + TracksColumns.TRACK_COLOR + " INTEGER,"
+ + TracksColumns.TRACK_ABSTRACT + " TEXT,"
+ + "UNIQUE (" + TracksColumns.TRACK_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.ROOMS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + RoomsColumns.ROOM_ID + " TEXT NOT NULL,"
+ + RoomsColumns.ROOM_NAME + " TEXT,"
+ + RoomsColumns.ROOM_FLOOR + " TEXT,"
+ + "UNIQUE (" + RoomsColumns.ROOM_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.SESSIONS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SyncColumns.UPDATED + " INTEGER NOT NULL,"
+ + SessionsColumns.SESSION_ID + " TEXT NOT NULL,"
+ + Sessions.BLOCK_ID + " TEXT " + References.BLOCK_ID + ","
+ + Sessions.ROOM_ID + " TEXT " + References.ROOM_ID + ","
+ + SessionsColumns.TYPE + " TEXT,"
+ + SessionsColumns.TITLE + " TEXT,"
+ + SessionsColumns.ABSTRACT + " TEXT,"
+ + SessionsColumns.REQUIREMENTS + " TEXT,"
+ + SessionsColumns.MODERATOR_URL + " TEXT,"
+ + SessionsColumns.WAVE_URL + " TEXT,"
+ + SessionsColumns.KEYWORDS + " TEXT,"
+ + SessionsColumns.HASHTAG + " TEXT,"
+ + SessionsColumns.STARRED + " INTEGER NOT NULL DEFAULT 0,"
+ + "UNIQUE (" + SessionsColumns.SESSION_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.SPEAKERS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SyncColumns.UPDATED + " INTEGER NOT NULL,"
+ + SpeakersColumns.SPEAKER_ID + " TEXT NOT NULL,"
+ + SpeakersColumns.SPEAKER_NAME + " TEXT,"
+ + SpeakersColumns.SPEAKER_COMPANY + " TEXT,"
+ + SpeakersColumns.SPEAKER_ABSTRACT + " TEXT,"
+ + "UNIQUE (" + SpeakersColumns.SPEAKER_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.SESSIONS_SPEAKERS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SessionsSpeakers.SESSION_ID + " TEXT NOT NULL " + References.SESSION_ID + ","
+ + SessionsSpeakers.SPEAKER_ID + " TEXT NOT NULL " + References.SPEAKER_ID + ","
+ + "UNIQUE (" + SessionsSpeakers.SESSION_ID + ","
+ + SessionsSpeakers.SPEAKER_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.SESSIONS_TRACKS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SessionsTracks.SESSION_ID + " TEXT NOT NULL " + References.SESSION_ID + ","
+ + SessionsTracks.TRACK_ID + " TEXT NOT NULL " + References.TRACK_ID + ","
+ + "UNIQUE (" + SessionsTracks.SESSION_ID + ","
+ + SessionsTracks.TRACK_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.VENDORS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SyncColumns.UPDATED + " INTEGER NOT NULL,"
+ + VendorsColumns.VENDOR_ID + " TEXT NOT NULL,"
+ + Vendors.TRACK_ID + " TEXT " + References.TRACK_ID + ","
+ + VendorsColumns.NAME + " TEXT,"
+ + VendorsColumns.LOCATION + " TEXT,"
+ + VendorsColumns.DESC + " TEXT,"
+ + VendorsColumns.URL + " TEXT,"
+ + VendorsColumns.PRODUCT_DESC + " TEXT,"
+ + VendorsColumns.LOGO_URL + " TEXT,"
+ + VendorsColumns.LOGO + " BLOB DEFAULT NULL,"
+ + VendorsColumns.STARRED + " INTEGER,"
+ + "UNIQUE (" + VendorsColumns.VENDOR_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.NOTES + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + Notes.SESSION_ID + " TEXT NOT NULL " + References.SESSION_ID + ","
+ + NotesColumns.NOTE_TIME + " INTEGER NOT NULL,"
+ + NotesColumns.NOTE_CONTENT + " TEXT)");
+
+ createSessionsSearch(db);
+ createVendorsSearch(db);
+
+ db.execSQL("CREATE TABLE " + Tables.SEARCH_SUGGEST + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SearchManager.SUGGEST_COLUMN_TEXT_1 + " TEXT NOT NULL)");
+
+ }
+
+ /**
+ * Create triggers that automatically build {@link Tables#SESSIONS_SEARCH}
+ * as values are changed in {@link Tables#SESSIONS}.
+ */
+ private static void createSessionsSearch(SQLiteDatabase db) {
+ // Using the "porter" tokenizer for simple stemming, so that
+ // "frustration" matches "frustrated."
+
+ db.execSQL("CREATE VIRTUAL TABLE " + Tables.SESSIONS_SEARCH + " USING fts3("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SessionsSearchColumns.BODY + " TEXT NOT NULL,"
+ + SessionsSearchColumns.SESSION_ID
+ + " TEXT NOT NULL " + References.SESSION_ID + ","
+ + "UNIQUE (" + SessionsSearchColumns.SESSION_ID + ") ON CONFLICT REPLACE,"
+ + "tokenize=porter)");
+
+ // TODO: handle null fields in body, which cause trigger to fail
+ // TODO: implement update trigger, not currently exercised
+
+ db.execSQL("CREATE TRIGGER " + Triggers.SESSIONS_SEARCH_INSERT + " AFTER INSERT ON "
+ + Tables.SESSIONS + " BEGIN INSERT INTO " + Qualified.SESSIONS_SEARCH + " "
+ + " VALUES(new." + Sessions.SESSION_ID + ", " + Subquery.SESSIONS_BODY + ");"
+ + " END;");
+
+ db.execSQL("CREATE TRIGGER " + Triggers.SESSIONS_SEARCH_DELETE + " AFTER DELETE ON "
+ + Tables.SESSIONS + " BEGIN DELETE FROM " + Tables.SESSIONS_SEARCH + " "
+ + " WHERE " + Qualified.SESSIONS_SEARCH_SESSION_ID + "=old." + Sessions.SESSION_ID
+ + ";" + " END;");
+
+ }
+
+ /**
+ * Create triggers that automatically build {@link Tables#VENDORS_SEARCH} as
+ * values are changed in {@link Tables#VENDORS}.
+ */
+ private static void createVendorsSearch(SQLiteDatabase db) {
+ // Using the "porter" tokenizer for simple stemming, so that
+ // "frustration" matches "frustrated."
+
+ db.execSQL("CREATE VIRTUAL TABLE " + Tables.VENDORS_SEARCH + " USING fts3("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + VendorsSearchColumns.BODY + " TEXT NOT NULL,"
+ + VendorsSearchColumns.VENDOR_ID
+ + " TEXT NOT NULL " + References.VENDOR_ID + ","
+ + "UNIQUE (" + VendorsSearchColumns.VENDOR_ID + ") ON CONFLICT REPLACE,"
+ + "tokenize=porter)");
+
+ // TODO: handle null fields in body, which cause trigger to fail
+ // TODO: implement update trigger, not currently exercised
+
+ db.execSQL("CREATE TRIGGER " + Triggers.VENDORS_SEARCH_INSERT + " AFTER INSERT ON "
+ + Tables.VENDORS + " BEGIN INSERT INTO " + Qualified.VENDORS_SEARCH + " "
+ + " VALUES(new." + Vendors.VENDOR_ID + ", " + Subquery.VENDORS_BODY + ");"
+ + " END;");
+
+ db.execSQL("CREATE TRIGGER " + Triggers.VENDORS_SEARCH_DELETE + " AFTER DELETE ON "
+ + Tables.VENDORS + " BEGIN DELETE FROM " + Tables.VENDORS_SEARCH + " "
+ + " WHERE " + Qualified.VENDORS_SEARCH_VENDOR_ID + "=old." + Vendors.VENDOR_ID
+ + ";" + " END;");
+
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.d(TAG, "onUpgrade() from " + oldVersion + " to " + newVersion);
+
+ // NOTE: This switch statement is designed to handle cascading database
+ // updates, starting at the current version and falling through to all
+ // future upgrade cases. Only use "break;" when you want to drop and
+ // recreate the entire database.
+ int version = oldVersion;
+ switch (version) {
+ case VER_LAUNCH:
+ // Version 19 added column for session hashtags.
+ db.execSQL("ALTER TABLE " + Tables.SESSIONS + " ADD COLUMN "
+ + SessionsColumns.HASHTAG + " TEXT");
+ version = VER_SESSION_HASHTAG;
+ }
+
+ Log.d(TAG, "after upgrade logic, at version " + version);
+ if (version != DATABASE_VERSION) {
+ Log.w(TAG, "Destroying old data during upgrade");
+
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.BLOCKS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.TRACKS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.ROOMS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.SESSIONS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.SPEAKERS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.SESSIONS_SPEAKERS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.SESSIONS_TRACKS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.VENDORS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.NOTES);
+
+ db.execSQL("DROP TRIGGER IF EXISTS " + Triggers.SESSIONS_SEARCH_INSERT);
+ db.execSQL("DROP TRIGGER IF EXISTS " + Triggers.SESSIONS_SEARCH_DELETE);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.SESSIONS_SEARCH);
+
+ db.execSQL("DROP TRIGGER IF EXISTS " + Triggers.VENDORS_SEARCH_INSERT);
+ db.execSQL("DROP TRIGGER IF EXISTS " + Triggers.VENDORS_SEARCH_DELETE);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.VENDORS_SEARCH);
+
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.SEARCH_SUGGEST);
+
+ onCreate(db);
+ }
+ }
+}
diff --git a/src/com/google/android/apps/iosched/provider/.svn/text-base/ScheduleProvider.java.svn-base b/src/com/google/android/apps/iosched/provider/.svn/text-base/ScheduleProvider.java.svn-base
new file mode 100644
index 0000000..d0d9585
--- /dev/null
+++ b/src/com/google/android/apps/iosched/provider/.svn/text-base/ScheduleProvider.java.svn-base
@@ -0,0 +1,729 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.provider;
+
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Notes;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.provider.ScheduleContract.SearchSuggest;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Speakers;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.SessionsSearchColumns;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.SessionsSpeakers;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.SessionsTracks;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.Tables;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.VendorsSearchColumns;
+import com.google.android.apps.iosched.service.SyncService;
+import com.google.android.apps.iosched.util.NotesExporter;
+import com.google.android.apps.iosched.util.SelectionBuilder;
+
+import android.app.Activity;
+import android.app.SearchManager;
+import android.content.ContentProvider;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.provider.BaseColumns;
+import android.provider.OpenableColumns;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Provider that stores {@link ScheduleContract} data. Data is usually inserted
+ * by {@link SyncService}, and queried by various {@link Activity} instances.
+ */
+public class ScheduleProvider extends ContentProvider {
+ private static final String TAG = "ScheduleProvider";
+ private static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
+
+ private ScheduleDatabase mOpenHelper;
+
+ private static final UriMatcher sUriMatcher = buildUriMatcher();
+
+ private static final int BLOCKS = 100;
+ private static final int BLOCKS_BETWEEN = 101;
+ private static final int BLOCKS_ID = 102;
+ private static final int BLOCKS_ID_SESSIONS = 103;
+
+ private static final int TRACKS = 200;
+ private static final int TRACKS_ID = 201;
+ private static final int TRACKS_ID_SESSIONS = 202;
+ private static final int TRACKS_ID_VENDORS = 203;
+
+ private static final int ROOMS = 300;
+ private static final int ROOMS_ID = 301;
+ private static final int ROOMS_ID_SESSIONS = 302;
+
+ private static final int SESSIONS = 400;
+ private static final int SESSIONS_STARRED = 401;
+ private static final int SESSIONS_SEARCH = 402;
+ private static final int SESSIONS_AT = 403;
+ private static final int SESSIONS_ID = 404;
+ private static final int SESSIONS_ID_SPEAKERS = 405;
+ private static final int SESSIONS_ID_TRACKS = 406;
+ private static final int SESSIONS_ID_NOTES = 407;
+
+ private static final int SPEAKERS = 500;
+ private static final int SPEAKERS_ID = 501;
+ private static final int SPEAKERS_ID_SESSIONS = 502;
+
+ private static final int VENDORS = 600;
+ private static final int VENDORS_STARRED = 601;
+ private static final int VENDORS_SEARCH = 603;
+ private static final int VENDORS_ID = 604;
+
+ private static final int NOTES = 700;
+ private static final int NOTES_EXPORT = 701;
+ private static final int NOTES_ID = 702;
+
+ private static final int SEARCH_SUGGEST = 800;
+
+ private static final String MIME_XML = "text/xml";
+
+ /**
+ * Build and return a {@link UriMatcher} that catches all {@link Uri}
+ * variations supported by this {@link ContentProvider}.
+ */
+ private static UriMatcher buildUriMatcher() {
+ final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
+ final String authority = ScheduleContract.CONTENT_AUTHORITY;
+
+ matcher.addURI(authority, "blocks", BLOCKS);
+ matcher.addURI(authority, "blocks/between/*/*", BLOCKS_BETWEEN);
+ matcher.addURI(authority, "blocks/*", BLOCKS_ID);
+ matcher.addURI(authority, "blocks/*/sessions", BLOCKS_ID_SESSIONS);
+
+ matcher.addURI(authority, "tracks", TRACKS);
+ matcher.addURI(authority, "tracks/*", TRACKS_ID);
+ matcher.addURI(authority, "tracks/*/sessions", TRACKS_ID_SESSIONS);
+ matcher.addURI(authority, "tracks/*/vendors", TRACKS_ID_VENDORS);
+
+ matcher.addURI(authority, "rooms", ROOMS);
+ matcher.addURI(authority, "rooms/*", ROOMS_ID);
+ matcher.addURI(authority, "rooms/*/sessions", ROOMS_ID_SESSIONS);
+
+ matcher.addURI(authority, "sessions", SESSIONS);
+ matcher.addURI(authority, "sessions/starred", SESSIONS_STARRED);
+ matcher.addURI(authority, "sessions/search/*", SESSIONS_SEARCH);
+ matcher.addURI(authority, "sessions/at/*", SESSIONS_AT);
+ matcher.addURI(authority, "sessions/*", SESSIONS_ID);
+ matcher.addURI(authority, "sessions/*/speakers", SESSIONS_ID_SPEAKERS);
+ matcher.addURI(authority, "sessions/*/tracks", SESSIONS_ID_TRACKS);
+ matcher.addURI(authority, "sessions/*/notes", SESSIONS_ID_NOTES);
+
+ matcher.addURI(authority, "speakers", SPEAKERS);
+ matcher.addURI(authority, "speakers/*", SPEAKERS_ID);
+ matcher.addURI(authority, "speakers/*/sessions", SPEAKERS_ID_SESSIONS);
+
+ matcher.addURI(authority, "vendors", VENDORS);
+ matcher.addURI(authority, "vendors/starred", VENDORS_STARRED);
+ matcher.addURI(authority, "vendors/search/*", VENDORS_SEARCH);
+ matcher.addURI(authority, "vendors/*", VENDORS_ID);
+
+ matcher.addURI(authority, "notes", NOTES);
+ matcher.addURI(authority, "notes/export", NOTES_EXPORT);
+ matcher.addURI(authority, "notes/*", NOTES_ID);
+
+ matcher.addURI(authority, "search_suggest_query", SEARCH_SUGGEST);
+
+ return matcher;
+ }
+
+ @Override
+ public boolean onCreate() {
+ final Context context = getContext();
+ mOpenHelper = new ScheduleDatabase(context);
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getType(Uri uri) {
+ final int match = sUriMatcher.match(uri);
+ switch (match) {
+ case BLOCKS:
+ return Blocks.CONTENT_TYPE;
+ case BLOCKS_BETWEEN:
+ return Blocks.CONTENT_TYPE;
+ case BLOCKS_ID:
+ return Blocks.CONTENT_ITEM_TYPE;
+ case BLOCKS_ID_SESSIONS:
+ return Sessions.CONTENT_TYPE;
+ case TRACKS:
+ return Tracks.CONTENT_TYPE;
+ case TRACKS_ID:
+ return Tracks.CONTENT_ITEM_TYPE;
+ case TRACKS_ID_SESSIONS:
+ return Sessions.CONTENT_TYPE;
+ case TRACKS_ID_VENDORS:
+ return Vendors.CONTENT_TYPE;
+ case ROOMS:
+ return Rooms.CONTENT_TYPE;
+ case ROOMS_ID:
+ return Rooms.CONTENT_ITEM_TYPE;
+ case ROOMS_ID_SESSIONS:
+ return Sessions.CONTENT_TYPE;
+ case SESSIONS:
+ return Sessions.CONTENT_TYPE;
+ case SESSIONS_STARRED:
+ return Sessions.CONTENT_TYPE;
+ case SESSIONS_SEARCH:
+ return Sessions.CONTENT_TYPE;
+ case SESSIONS_AT:
+ return Sessions.CONTENT_TYPE;
+ case SESSIONS_ID:
+ return Sessions.CONTENT_ITEM_TYPE;
+ case SESSIONS_ID_SPEAKERS:
+ return Speakers.CONTENT_TYPE;
+ case SESSIONS_ID_TRACKS:
+ return Tracks.CONTENT_TYPE;
+ case SESSIONS_ID_NOTES:
+ return Notes.CONTENT_TYPE;
+ case SPEAKERS:
+ return Speakers.CONTENT_TYPE;
+ case SPEAKERS_ID:
+ return Speakers.CONTENT_ITEM_TYPE;
+ case SPEAKERS_ID_SESSIONS:
+ return Sessions.CONTENT_TYPE;
+ case VENDORS:
+ return Vendors.CONTENT_TYPE;
+ case VENDORS_STARRED:
+ return Vendors.CONTENT_TYPE;
+ case VENDORS_SEARCH:
+ return Vendors.CONTENT_TYPE;
+ case VENDORS_ID:
+ return Vendors.CONTENT_ITEM_TYPE;
+ case NOTES:
+ return Notes.CONTENT_TYPE;
+ case NOTES_EXPORT:
+ return MIME_XML;
+ case NOTES_ID:
+ return Notes.CONTENT_ITEM_TYPE;
+ default:
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ if (LOGV) Log.v(TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")");
+ final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+
+ final int match = sUriMatcher.match(uri);
+ switch (match) {
+ default: {
+ // Most cases are handled with simple SelectionBuilder
+ final SelectionBuilder builder = buildExpandedSelection(uri, match);
+ return builder.where(selection, selectionArgs).query(db, projection, sortOrder);
+ }
+ case NOTES_EXPORT: {
+ // Provide query values for file attachments
+ final String[] columns = { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE };
+ final MatrixCursor cursor = new MatrixCursor(columns, 1);
+ cursor.addRow(new String[] { "notes.xml", null });
+ return cursor;
+ }
+ case SEARCH_SUGGEST: {
+ final SelectionBuilder builder = new SelectionBuilder();
+
+ // Adjust incoming query to become SQL text match
+ selectionArgs[0] = selectionArgs[0] + "%";
+ builder.table(Tables.SEARCH_SUGGEST);
+ builder.where(selection, selectionArgs);
+ builder.map(SearchManager.SUGGEST_COLUMN_QUERY,
+ SearchManager.SUGGEST_COLUMN_TEXT_1);
+
+ projection = new String[] { BaseColumns._ID, SearchManager.SUGGEST_COLUMN_TEXT_1,
+ SearchManager.SUGGEST_COLUMN_QUERY };
+
+ final String limit = uri.getQueryParameter(SearchManager.SUGGEST_PARAMETER_LIMIT);
+ return builder.query(db, projection, null, null, SearchSuggest.DEFAULT_SORT, limit);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ if (LOGV) Log.v(TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")");
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ final int match = sUriMatcher.match(uri);
+ switch (match) {
+ case BLOCKS: {
+ db.insertOrThrow(Tables.BLOCKS, null, values);
+ return Blocks.buildBlockUri(values.getAsString(Blocks.BLOCK_ID));
+ }
+ case TRACKS: {
+ db.insertOrThrow(Tables.TRACKS, null, values);
+ return Tracks.buildTrackUri(values.getAsString(Tracks.TRACK_ID));
+ }
+ case ROOMS: {
+ db.insertOrThrow(Tables.ROOMS, null, values);
+ return Rooms.buildRoomUri(values.getAsString(Rooms.ROOM_ID));
+ }
+ case SESSIONS: {
+ db.insertOrThrow(Tables.SESSIONS, null, values);
+ return Sessions.buildSessionUri(values.getAsString(Sessions.SESSION_ID));
+ }
+ case SESSIONS_ID_SPEAKERS: {
+ db.insertOrThrow(Tables.SESSIONS_SPEAKERS, null, values);
+ return Speakers.buildSpeakerUri(values.getAsString(SessionsSpeakers.SPEAKER_ID));
+ }
+ case SESSIONS_ID_TRACKS: {
+ db.insertOrThrow(Tables.SESSIONS_TRACKS, null, values);
+ return Tracks.buildTrackUri(values.getAsString(SessionsTracks.TRACK_ID));
+ }
+ case SESSIONS_ID_NOTES: {
+ final String sessionId = Sessions.getSessionId(uri);
+ values.put(Notes.SESSION_ID, sessionId);
+ final long noteId = db.insertOrThrow(Tables.NOTES, null, values);
+ return ContentUris.withAppendedId(Notes.CONTENT_URI, noteId);
+ }
+ case SPEAKERS: {
+ db.insertOrThrow(Tables.SPEAKERS, null, values);
+ return Speakers.buildSpeakerUri(values.getAsString(Speakers.SPEAKER_ID));
+ }
+ case VENDORS: {
+ db.insertOrThrow(Tables.VENDORS, null, values);
+ return Vendors.buildVendorUri(values.getAsString(Vendors.VENDOR_ID));
+ }
+ case NOTES: {
+ final long noteId = db.insertOrThrow(Tables.NOTES, null, values);
+ return ContentUris.withAppendedId(Notes.CONTENT_URI, noteId);
+ }
+ case SEARCH_SUGGEST: {
+ db.insertOrThrow(Tables.SEARCH_SUGGEST, null, values);
+ return SearchSuggest.CONTENT_URI;
+ }
+ default: {
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ if (LOGV) Log.v(TAG, "update(uri=" + uri + ", values=" + values.toString() + ")");
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ final SelectionBuilder builder = buildSimpleSelection(uri);
+ return builder.where(selection, selectionArgs).update(db, values);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ if (LOGV) Log.v(TAG, "delete(uri=" + uri + ")");
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ final SelectionBuilder builder = buildSimpleSelection(uri);
+ return builder.where(selection, selectionArgs).delete(db);
+ }
+
+ /**
+ * Apply the given set of {@link ContentProviderOperation}, executing inside
+ * a {@link SQLiteDatabase} transaction. All changes will be rolled back if
+ * any single one fails.
+ */
+ @Override
+ public ContentProviderResult[] applyBatch(ArrayList operations)
+ throws OperationApplicationException {
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ final int numOperations = operations.size();
+ final ContentProviderResult[] results = new ContentProviderResult[numOperations];
+ for (int i = 0; i < numOperations; i++) {
+ results[i] = operations.get(i).apply(this, results, i);
+ }
+ db.setTransactionSuccessful();
+ return results;
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ /**
+ * Build a simple {@link SelectionBuilder} to match the requested
+ * {@link Uri}. This is usually enough to support {@link #insert},
+ * {@link #update}, and {@link #delete} operations.
+ */
+ private SelectionBuilder buildSimpleSelection(Uri uri) {
+ final SelectionBuilder builder = new SelectionBuilder();
+ final int match = sUriMatcher.match(uri);
+ switch (match) {
+ case BLOCKS: {
+ return builder.table(Tables.BLOCKS);
+ }
+ case BLOCKS_ID: {
+ final String blockId = Blocks.getBlockId(uri);
+ return builder.table(Tables.BLOCKS)
+ .where(Blocks.BLOCK_ID + "=?", blockId);
+ }
+ case TRACKS: {
+ return builder.table(Tables.TRACKS);
+ }
+ case TRACKS_ID: {
+ final String trackId = Tracks.getTrackId(uri);
+ return builder.table(Tables.TRACKS)
+ .where(Tracks.TRACK_ID + "=?", trackId);
+ }
+ case ROOMS: {
+ return builder.table(Tables.ROOMS);
+ }
+ case ROOMS_ID: {
+ final String roomId = Rooms.getRoomId(uri);
+ return builder.table(Tables.ROOMS)
+ .where(Rooms.ROOM_ID + "=?", roomId);
+ }
+ case SESSIONS: {
+ return builder.table(Tables.SESSIONS);
+ }
+ case SESSIONS_ID: {
+ final String sessionId = Sessions.getSessionId(uri);
+ return builder.table(Tables.SESSIONS)
+ .where(Sessions.SESSION_ID + "=?", sessionId);
+ }
+ case SESSIONS_ID_SPEAKERS: {
+ final String sessionId = Sessions.getSessionId(uri);
+ return builder.table(Tables.SESSIONS_SPEAKERS)
+ .where(Sessions.SESSION_ID + "=?", sessionId);
+ }
+ case SESSIONS_ID_TRACKS: {
+ final String sessionId = Sessions.getSessionId(uri);
+ return builder.table(Tables.SESSIONS_TRACKS)
+ .where(Sessions.SESSION_ID + "=?", sessionId);
+ }
+ case SPEAKERS: {
+ return builder.table(Tables.SPEAKERS);
+ }
+ case SPEAKERS_ID: {
+ final String speakerId = Speakers.getSpeakerId(uri);
+ return builder.table(Tables.SPEAKERS)
+ .where(Speakers.SPEAKER_ID + "=?", speakerId);
+ }
+ case VENDORS: {
+ return builder.table(Tables.VENDORS);
+ }
+ case VENDORS_ID: {
+ final String vendorId = Vendors.getVendorId(uri);
+ return builder.table(Tables.VENDORS)
+ .where(Vendors.VENDOR_ID + "=?", vendorId);
+ }
+ case NOTES: {
+ return builder.table(Tables.NOTES);
+ }
+ case NOTES_ID: {
+ final String noteId = uri.getPathSegments().get(1);
+ return builder.table(Tables.NOTES)
+ .where(Notes._ID + "=?", noteId);
+ }
+ case SEARCH_SUGGEST: {
+ return builder.table(Tables.SEARCH_SUGGEST);
+ }
+ default: {
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+ }
+ }
+
+ /**
+ * Build an advanced {@link SelectionBuilder} to match the requested
+ * {@link Uri}. This is usually only used by {@link #query}, since it
+ * performs table joins useful for {@link Cursor} data.
+ */
+ private SelectionBuilder buildExpandedSelection(Uri uri, int match) {
+ final SelectionBuilder builder = new SelectionBuilder();
+ switch (match) {
+ case BLOCKS: {
+ return builder.table(Tables.BLOCKS);
+ }
+ case BLOCKS_BETWEEN: {
+ final List segments = uri.getPathSegments();
+ final String startTime = segments.get(2);
+ final String endTime = segments.get(3);
+ return builder.table(Tables.BLOCKS)
+ .map(Blocks.SESSIONS_COUNT, Subquery.BLOCK_SESSIONS_COUNT)
+ .map(Blocks.CONTAINS_STARRED, Subquery.BLOCK_CONTAINS_STARRED)
+ .where(Blocks.BLOCK_START + ">=?", startTime)
+ .where(Blocks.BLOCK_START + "<=?", endTime);
+ }
+ case BLOCKS_ID: {
+ final String blockId = Blocks.getBlockId(uri);
+ return builder.table(Tables.BLOCKS)
+ .map(Blocks.SESSIONS_COUNT, Subquery.BLOCK_SESSIONS_COUNT)
+ .map(Blocks.CONTAINS_STARRED, Subquery.BLOCK_CONTAINS_STARRED)
+ .where(Blocks.BLOCK_ID + "=?", blockId);
+ }
+ case BLOCKS_ID_SESSIONS: {
+ final String blockId = Blocks.getBlockId(uri);
+ return builder.table(Tables.SESSIONS_JOIN_BLOCKS_ROOMS)
+ .map(Blocks.SESSIONS_COUNT, Subquery.BLOCK_SESSIONS_COUNT)
+ .map(Blocks.CONTAINS_STARRED, Subquery.BLOCK_CONTAINS_STARRED)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.SESSION_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(Qualified.SESSIONS_BLOCK_ID + "=?", blockId);
+ }
+ case TRACKS: {
+ return builder.table(Tables.TRACKS)
+ .map(Tracks.SESSIONS_COUNT, Subquery.TRACK_SESSIONS_COUNT)
+ .map(Tracks.VENDORS_COUNT, Subquery.TRACK_VENDORS_COUNT);
+ }
+ case TRACKS_ID: {
+ final String trackId = Tracks.getTrackId(uri);
+ return builder.table(Tables.TRACKS)
+ .where(Tracks.TRACK_ID + "=?", trackId);
+ }
+ case TRACKS_ID_SESSIONS: {
+ final String trackId = Tracks.getTrackId(uri);
+ return builder.table(Tables.SESSIONS_TRACKS_JOIN_SESSIONS_BLOCKS_ROOMS)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.SESSION_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(Qualified.SESSIONS_TRACKS_TRACK_ID + "=?", trackId);
+ }
+ case TRACKS_ID_VENDORS: {
+ final String trackId = Tracks.getTrackId(uri);
+ return builder.table(Tables.VENDORS_JOIN_TRACKS)
+ .mapToTable(Vendors._ID, Tables.VENDORS)
+ .mapToTable(Vendors.TRACK_ID, Tables.VENDORS)
+ .where(Qualified.VENDORS_TRACK_ID + "=?", trackId);
+ }
+ case ROOMS: {
+ return builder.table(Tables.ROOMS);
+ }
+ case ROOMS_ID: {
+ final String roomId = Rooms.getRoomId(uri);
+ return builder.table(Tables.ROOMS)
+ .where(Rooms.ROOM_ID + "=?", roomId);
+ }
+ case ROOMS_ID_SESSIONS: {
+ final String roomId = Rooms.getRoomId(uri);
+ return builder.table(Tables.SESSIONS_JOIN_BLOCKS_ROOMS)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(Qualified.SESSIONS_ROOM_ID + "=?", roomId);
+ }
+ case SESSIONS: {
+ return builder.table(Tables.SESSIONS_JOIN_BLOCKS_ROOMS)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS);
+ }
+ case SESSIONS_STARRED: {
+ return builder.table(Tables.SESSIONS_JOIN_BLOCKS_ROOMS)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(Sessions.STARRED + "=1");
+ }
+ case SESSIONS_SEARCH: {
+ final String query = Sessions.getSearchQuery(uri);
+ return builder.table(Tables.SESSIONS_SEARCH_JOIN_SESSIONS_BLOCKS_ROOMS)
+ .map(Sessions.SEARCH_SNIPPET, Subquery.SESSIONS_SNIPPET)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.SESSION_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(SessionsSearchColumns.BODY + " MATCH ?", query);
+ }
+ case SESSIONS_AT: {
+ final List segments = uri.getPathSegments();
+ final String time = segments.get(2);
+ return builder.table(Tables.SESSIONS_JOIN_BLOCKS_ROOMS)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(Sessions.BLOCK_START + "<=?", time)
+ .where(Sessions.BLOCK_END + ">=?", time);
+ }
+ case SESSIONS_ID: {
+ final String sessionId = Sessions.getSessionId(uri);
+ return builder.table(Tables.SESSIONS_JOIN_BLOCKS_ROOMS)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(Qualified.SESSIONS_SESSION_ID + "=?", sessionId);
+ }
+ case SESSIONS_ID_SPEAKERS: {
+ final String sessionId = Sessions.getSessionId(uri);
+ return builder.table(Tables.SESSIONS_SPEAKERS_JOIN_SPEAKERS)
+ .mapToTable(Speakers._ID, Tables.SPEAKERS)
+ .mapToTable(Speakers.SPEAKER_ID, Tables.SPEAKERS)
+ .where(Qualified.SESSIONS_SPEAKERS_SESSION_ID + "=?", sessionId);
+ }
+ case SESSIONS_ID_TRACKS: {
+ final String sessionId = Sessions.getSessionId(uri);
+ return builder.table(Tables.SESSIONS_TRACKS_JOIN_TRACKS)
+ .mapToTable(Tracks._ID, Tables.TRACKS)
+ .mapToTable(Tracks.TRACK_ID, Tables.TRACKS)
+ .where(Qualified.SESSIONS_TRACKS_SESSION_ID + "=?", sessionId);
+ }
+ case SESSIONS_ID_NOTES: {
+ final String sessionId = Sessions.getSessionId(uri);
+ return builder.table(Tables.NOTES)
+ .where(Notes.SESSION_ID + "=?", sessionId);
+ }
+ case SPEAKERS: {
+ return builder.table(Tables.SPEAKERS);
+ }
+ case SPEAKERS_ID: {
+ final String speakerId = Speakers.getSpeakerId(uri);
+ return builder.table(Tables.SPEAKERS)
+ .where(Speakers.SPEAKER_ID + "=?", speakerId);
+ }
+ case SPEAKERS_ID_SESSIONS: {
+ final String speakerId = Speakers.getSpeakerId(uri);
+ return builder.table(Tables.SESSIONS_SPEAKERS_JOIN_SESSIONS_BLOCKS_ROOMS)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.SESSION_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(Qualified.SESSIONS_SPEAKERS_SPEAKER_ID + "=?", speakerId);
+ }
+ case VENDORS: {
+ return builder.table(Tables.VENDORS_JOIN_TRACKS)
+ .mapToTable(Vendors._ID, Tables.VENDORS)
+ .mapToTable(Vendors.TRACK_ID, Tables.VENDORS);
+ }
+ case VENDORS_STARRED: {
+ return builder.table(Tables.VENDORS_JOIN_TRACKS)
+ .mapToTable(Vendors._ID, Tables.VENDORS)
+ .mapToTable(Vendors.TRACK_ID, Tables.VENDORS)
+ .where(Vendors.STARRED + "=1");
+ }
+ case VENDORS_SEARCH: {
+ final String query = Vendors.getSearchQuery(uri);
+ return builder.table(Tables.VENDORS_SEARCH_JOIN_VENDORS_TRACKS)
+ .map(Vendors.SEARCH_SNIPPET, Subquery.VENDORS_SNIPPET)
+ .mapToTable(Vendors._ID, Tables.VENDORS)
+ .mapToTable(Vendors.VENDOR_ID, Tables.VENDORS)
+ .mapToTable(Vendors.TRACK_ID, Tables.VENDORS)
+ .where(VendorsSearchColumns.BODY + " MATCH ?", query);
+ }
+ case VENDORS_ID: {
+ final String vendorId = Vendors.getVendorId(uri);
+ return builder.table(Tables.VENDORS_JOIN_TRACKS)
+ .mapToTable(Vendors._ID, Tables.VENDORS)
+ .mapToTable(Vendors.TRACK_ID, Tables.VENDORS)
+ .where(Vendors.VENDOR_ID + "=?", vendorId);
+ }
+ case NOTES: {
+ return builder.table(Tables.NOTES);
+ }
+ case NOTES_ID: {
+ final long noteId = Notes.getNoteId(uri);
+ return builder.table(Tables.NOTES)
+ .where(Notes._ID + "=?", Long.toString(noteId));
+ }
+ default: {
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+ }
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ final int match = sUriMatcher.match(uri);
+ switch (match) {
+ case NOTES_EXPORT: {
+ try {
+ final File notesFile = NotesExporter.writeExportedNotes(getContext());
+ return ParcelFileDescriptor
+ .open(notesFile, ParcelFileDescriptor.MODE_READ_ONLY);
+ } catch (IOException e) {
+ throw new FileNotFoundException("Unable to export notes: " + e.toString());
+ }
+ }
+ default: {
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+ }
+ }
+
+ private interface Subquery {
+ String BLOCK_SESSIONS_COUNT = "(SELECT COUNT(" + Qualified.SESSIONS_SESSION_ID + ") FROM "
+ + Tables.SESSIONS + " WHERE " + Qualified.SESSIONS_BLOCK_ID + "="
+ + Qualified.BLOCKS_BLOCK_ID + ")";
+
+ String BLOCK_CONTAINS_STARRED = "(SELECT MAX(" + Qualified.SESSIONS_STARRED + ") FROM "
+ + Tables.SESSIONS + " WHERE " + Qualified.SESSIONS_BLOCK_ID + "="
+ + Qualified.BLOCKS_BLOCK_ID + ")";
+
+ String TRACK_SESSIONS_COUNT = "(SELECT COUNT(" + Qualified.SESSIONS_TRACKS_SESSION_ID
+ + ") FROM " + Tables.SESSIONS_TRACKS + " WHERE "
+ + Qualified.SESSIONS_TRACKS_TRACK_ID + "=" + Qualified.TRACKS_TRACK_ID + ")";
+
+ String TRACK_VENDORS_COUNT = "(SELECT COUNT(" + Qualified.VENDORS_VENDOR_ID + ") FROM "
+ + Tables.VENDORS + " WHERE " + Qualified.VENDORS_TRACK_ID + "="
+ + Qualified.TRACKS_TRACK_ID + ")";
+
+ String SESSIONS_SNIPPET = "snippet(" + Tables.SESSIONS_SEARCH + ",'{','}','\u2026')";
+ String VENDORS_SNIPPET = "snippet(" + Tables.VENDORS_SEARCH + ",'{','}','\u2026')";
+ }
+
+ /**
+ * {@link ScheduleContract} fields that are fully qualified with a specific
+ * parent {@link Tables}. Used when needed to work around SQL ambiguity.
+ */
+ private interface Qualified {
+ String SESSIONS_SESSION_ID = Tables.SESSIONS + "." + Sessions.SESSION_ID;
+ String SESSIONS_BLOCK_ID = Tables.SESSIONS + "." + Sessions.BLOCK_ID;
+ String SESSIONS_ROOM_ID = Tables.SESSIONS + "." + Sessions.ROOM_ID;
+
+ String SESSIONS_TRACKS_SESSION_ID = Tables.SESSIONS_TRACKS + "."
+ + SessionsTracks.SESSION_ID;
+ String SESSIONS_TRACKS_TRACK_ID = Tables.SESSIONS_TRACKS + "."
+ + SessionsTracks.TRACK_ID;
+
+ String SESSIONS_SPEAKERS_SESSION_ID = Tables.SESSIONS_SPEAKERS + "."
+ + SessionsSpeakers.SESSION_ID;
+ String SESSIONS_SPEAKERS_SPEAKER_ID = Tables.SESSIONS_SPEAKERS + "."
+ + SessionsSpeakers.SPEAKER_ID;
+
+ String VENDORS_VENDOR_ID = Tables.VENDORS + "." + Vendors.VENDOR_ID;
+ String VENDORS_TRACK_ID = Tables.VENDORS + "." + Vendors.TRACK_ID;
+
+ @SuppressWarnings("hiding")
+ String SESSIONS_STARRED = Tables.SESSIONS + "." + Sessions.STARRED;
+
+ String TRACKS_TRACK_ID = Tables.TRACKS + "." + Tracks.TRACK_ID;
+ String BLOCKS_BLOCK_ID = Tables.BLOCKS + "." + Blocks.BLOCK_ID;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/provider/ScheduleContract.java b/src/com/google/android/apps/iosched/provider/ScheduleContract.java
new file mode 100644
index 0000000..c3c3973
--- /dev/null
+++ b/src/com/google/android/apps/iosched/provider/ScheduleContract.java
@@ -0,0 +1,556 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.provider;
+
+import com.google.android.apps.iosched.util.ParserUtils;
+
+import android.app.SearchManager;
+import android.content.ContentUris;
+import android.graphics.Color;
+import android.net.Uri;
+import android.provider.BaseColumns;
+import android.text.format.DateUtils;
+
+/**
+ * Contract class for interacting with {@link ScheduleProvider}. Unless
+ * otherwise noted, all time-based fields are milliseconds since epoch and can
+ * be compared against {@link System#currentTimeMillis()}.
+ *
+ * The backing {@link ContentProvider} assumes that {@link Uri} are generated
+ * using stronger {@link String} identifiers, instead of {@code int}
+ * {@link BaseColumns#_ID} values, which are prone to shuffle during sync.
+ */
+public class ScheduleContract {
+
+ /**
+ * Special value for {@link SyncColumns#UPDATED} indicating that an entry
+ * has never been updated, or doesn't exist yet.
+ */
+ public static final long UPDATED_NEVER = -2;
+
+ /**
+ * Special value for {@link SyncColumns#UPDATED} indicating that the last
+ * update time is unknown, usually when inserted from a local file source.
+ */
+ public static final long UPDATED_UNKNOWN = -1;
+
+ public interface SyncColumns {
+ /** Last time this entry was updated or synchronized. */
+ String UPDATED = "updated";
+ }
+
+ interface BlocksColumns {
+ /** Unique string identifying this block of time. */
+ String BLOCK_ID = "block_id";
+ /** Title describing this block of time. */
+ String BLOCK_TITLE = "block_title";
+ /** Time when this block starts. */
+ String BLOCK_START = "block_start";
+ /** Time when this block ends. */
+ String BLOCK_END = "block_end";
+ /** Type describing this block. */
+ String BLOCK_TYPE = "block_type";
+ }
+
+ interface TracksColumns {
+ /** Unique string identifying this track. */
+ String TRACK_ID = "track_id";
+ /** Name describing this track. */
+ String TRACK_NAME = "track_name";
+ /** Color used to identify this track, in {@link Color#argb} format. */
+ String TRACK_COLOR = "track_color";
+ /** Body of text explaining this track in detail. */
+ String TRACK_ABSTRACT = "track_abstract";
+ }
+
+ interface RoomsColumns {
+ /** Unique string identifying this room. */
+ String ROOM_ID = "room_id";
+ /** Name describing this room. */
+ String ROOM_NAME = "room_name";
+ /** Building floor this room exists on. */
+ String ROOM_FLOOR = "room_floor";
+ }
+
+ interface SessionsColumns {
+ /** Unique string identifying this session. */
+ String SESSION_ID = "session_id";
+ /** Type of session, such as difficulty level. */
+ String TYPE = "type";
+ /** Title describing this track. */
+ String TITLE = "title";
+ /** Body of text explaining this session in detail. */
+ String ABSTRACT = "abstract";
+ /** Requirements that attendees should meet. */
+ String REQUIREMENTS = "requirements";
+ /** User-specific flag indicating starred status. */
+ String STARRED = "starred";
+ /** Link to Moderator for this session. */
+ String MODERATOR_URL = "moderator_url";
+ /** Link to Wave for this session. */
+ String WAVE_URL = "wave_url";
+ /** Additional keywords that describe this session. */
+ String KEYWORDS = "keywords";
+ /** Hashtag used to identify this session in social media. */
+ String HASHTAG = "hashtag";
+
+ // TODO: moderator, wave, other online links
+ }
+
+ interface SpeakersColumns {
+ /** Unique string identifying this speaker. */
+ String SPEAKER_ID = "speaker_id";
+ /** Name of this speaker. */
+ String SPEAKER_NAME = "speaker_name";
+ /** Company this speaker works for. */
+ String SPEAKER_COMPANY = "speaker_company";
+ /** Body of text describing this speaker in detail. */
+ String SPEAKER_ABSTRACT = "speaker_abstract";
+ }
+
+ interface VendorsColumns {
+ /** Unique string identifying this vendor. */
+ String VENDOR_ID = "vendor_id";
+ /** Name of this vendor. */
+ String NAME = "name";
+ /** Location or city this vendor is based in. */
+ String LOCATION = "location";
+ /** Body of text describing this vendor. */
+ String DESC = "desc";
+ /** Link to vendor online. */
+ String URL = "url";
+ /** Body of text describing the product of this vendor. */
+ String PRODUCT_DESC = "product_desc";
+ /** Link to vendor logo. */
+ String LOGO_URL = "logo_url";
+ /** {@link #LOGO_URL} stored as local blob, when available. */
+ String LOGO = "logo";
+ /** User-specific flag indicating starred status. */
+ String STARRED = "starred";
+ }
+
+ interface NotesColumns {
+ /** Time this note was created. */
+ String NOTE_TIME = "note_time";
+ /** User-generated content of note. */
+ String NOTE_CONTENT = "note_content";
+ }
+
+ public static final String CONTENT_AUTHORITY = "com.google.android.apps.iosched";
+
+ private static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
+
+ private static final String PATH_BLOCKS = "blocks";
+ private static final String PATH_AT = "at";
+ private static final String PATH_BETWEEN = "between";
+ private static final String PATH_TRACKS = "tracks";
+ private static final String PATH_ROOMS = "rooms";
+ private static final String PATH_SESSIONS = "sessions";
+ private static final String PATH_STARRED = "starred";
+ private static final String PATH_SPEAKERS = "speakers";
+ private static final String PATH_VENDORS = "vendors";
+ private static final String PATH_NOTES = "notes";
+ private static final String PATH_EXPORT = "export";
+ private static final String PATH_SEARCH = "search";
+ private static final String PATH_SEARCH_SUGGEST = "search_suggest_query";
+
+ /**
+ * Blocks are generic timeslots that {@link Sessions} and other related
+ * events fall into.
+ */
+ public static class Blocks implements BlocksColumns, BaseColumns {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_BLOCKS).build();
+
+ public static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/vnd.iosched.block";
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.iosched.block";
+
+ /** Count of {@link Sessions} inside given block. */
+ public static final String SESSIONS_COUNT = "sessions_count";
+
+ /**
+ * Flag indicating that at least one {@link Sessions#SESSION_ID} inside
+ * this block has {@link Sessions#STARRED} set.
+ */
+ public static final String CONTAINS_STARRED = "contains_starred";
+
+ /** Default "ORDER BY" clause. */
+ public static final String DEFAULT_SORT = BlocksColumns.BLOCK_START + " ASC, "
+ + BlocksColumns.BLOCK_END + " ASC";
+
+ /** Build {@link Uri} for requested {@link #BLOCK_ID}. */
+ public static Uri buildBlockUri(String blockId) {
+ return CONTENT_URI.buildUpon().appendPath(blockId).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Sessions} associated
+ * with the requested {@link #BLOCK_ID}.
+ */
+ public static Uri buildSessionsUri(String blockId) {
+ return CONTENT_URI.buildUpon().appendPath(blockId).appendPath(PATH_SESSIONS).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Blocks} that occur
+ * between the requested time boundaries.
+ */
+ public static Uri buildBlocksBetweenDirUri(long startTime, long endTime) {
+ return CONTENT_URI.buildUpon().appendPath(PATH_BETWEEN).appendPath(
+ String.valueOf(startTime)).appendPath(String.valueOf(endTime)).build();
+ }
+
+ /** Read {@link #BLOCK_ID} from {@link Blocks} {@link Uri}. */
+ public static String getBlockId(Uri uri) {
+ return uri.getPathSegments().get(1);
+ }
+
+ /**
+ * Generate a {@link #BLOCK_ID} that will always match the requested
+ * {@link Blocks} details.
+ */
+ public static String generateBlockId(long startTime, long endTime) {
+ startTime /= DateUtils.SECOND_IN_MILLIS;
+ endTime /= DateUtils.SECOND_IN_MILLIS;
+ return ParserUtils.sanitizeId(startTime + "-" + endTime);
+ }
+ }
+
+ /**
+ * Tracks are overall categories for {@link Sessions} and {@link Vendors},
+ * such as "Android" or "Enterprise."
+ */
+ public static class Tracks implements TracksColumns, BaseColumns {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_TRACKS).build();
+
+ public static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/vnd.iosched.track";
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.iosched.track";
+
+ /** Count of {@link Sessions} inside given track. */
+ public static final String SESSIONS_COUNT = "sessions_count";
+ /** Count of {@link Vendors} inside given track. */
+ public static final String VENDORS_COUNT = "vendors_count";
+
+ /** Default "ORDER BY" clause. */
+ public static final String DEFAULT_SORT = TracksColumns.TRACK_NAME + " ASC";
+
+ /** Build {@link Uri} for requested {@link #TRACK_ID}. */
+ public static Uri buildTrackUri(String trackId) {
+ return CONTENT_URI.buildUpon().appendPath(trackId).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Sessions} associated
+ * with the requested {@link #TRACK_ID}.
+ */
+ public static Uri buildSessionsUri(String trackId) {
+ return CONTENT_URI.buildUpon().appendPath(trackId).appendPath(PATH_SESSIONS).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Vendors} associated with
+ * the requested {@link #TRACK_ID}.
+ */
+ public static Uri buildVendorsUri(String trackId) {
+ return CONTENT_URI.buildUpon().appendPath(trackId).appendPath(PATH_VENDORS).build();
+ }
+
+ /** Read {@link #TRACK_ID} from {@link Tracks} {@link Uri}. */
+ public static String getTrackId(Uri uri) {
+ return uri.getPathSegments().get(1);
+ }
+
+ /**
+ * Generate a {@link #TRACK_ID} that will always match the requested
+ * {@link Tracks} details.
+ */
+ public static String generateTrackId(String title) {
+ return ParserUtils.sanitizeId(title);
+ }
+ }
+
+ /**
+ * Rooms are physical locations at the conference venue.
+ */
+ public static class Rooms implements RoomsColumns, BaseColumns {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_ROOMS).build();
+
+ public static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/vnd.iosched.room";
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.iosched.room";
+
+ /** Default "ORDER BY" clause. */
+ public static final String DEFAULT_SORT = RoomsColumns.ROOM_FLOOR + " ASC, "
+ + RoomsColumns.ROOM_NAME + " COLLATE NOCASE ASC";
+
+ /** Build {@link Uri} for requested {@link #ROOM_ID}. */
+ public static Uri buildRoomUri(String roomId) {
+ return CONTENT_URI.buildUpon().appendPath(roomId).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Sessions} associated
+ * with the requested {@link #ROOM_ID}.
+ */
+ public static Uri buildSessionsDirUri(String roomId) {
+ return CONTENT_URI.buildUpon().appendPath(roomId).appendPath(PATH_SESSIONS).build();
+ }
+
+ /** Read {@link #ROOM_ID} from {@link Rooms} {@link Uri}. */
+ public static String getRoomId(Uri uri) {
+ return uri.getPathSegments().get(1);
+ }
+
+ /**
+ * Generate a {@link #ROOM_ID} that will always match the requested
+ * {@link Rooms} details.
+ */
+ public static String generateRoomId(String room) {
+ return ParserUtils.sanitizeId(room);
+ }
+ }
+
+ /**
+ * Each session is a block of time that has a {@link Tracks}, a
+ * {@link Rooms}, and zero or more {@link Speakers}.
+ */
+ public static class Sessions implements SessionsColumns, BlocksColumns, RoomsColumns,
+ SyncColumns, BaseColumns {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_SESSIONS).build();
+ public static final Uri CONTENT_STARRED_URI =
+ CONTENT_URI.buildUpon().appendPath(PATH_STARRED).build();
+
+ public static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/vnd.iosched.session";
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.iosched.session";
+
+ public static final String BLOCK_ID = "block_id";
+ public static final String ROOM_ID = "room_id";
+
+ public static final String SEARCH_SNIPPET = "search_snippet";
+
+ // TODO: shortcut primary track to offer sub-sorting here
+ /** Default "ORDER BY" clause. */
+ public static final String DEFAULT_SORT = BlocksColumns.BLOCK_START + " ASC,"
+ + SessionsColumns.TITLE + " COLLATE NOCASE ASC";
+
+ /** Build {@link Uri} for requested {@link #SESSION_ID}. */
+ public static Uri buildSessionUri(String sessionId) {
+ return CONTENT_URI.buildUpon().appendPath(sessionId).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Speakers} associated
+ * with the requested {@link #SESSION_ID}.
+ */
+ public static Uri buildSpeakersDirUri(String sessionId) {
+ return CONTENT_URI.buildUpon().appendPath(sessionId).appendPath(PATH_SPEAKERS).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Tracks} associated with
+ * the requested {@link #SESSION_ID}.
+ */
+ public static Uri buildTracksDirUri(String sessionId) {
+ return CONTENT_URI.buildUpon().appendPath(sessionId).appendPath(PATH_TRACKS).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Notes} associated with
+ * the requested {@link #SESSION_ID}.
+ */
+ public static Uri buildNotesDirUri(String sessionId) {
+ return CONTENT_URI.buildUpon().appendPath(sessionId).appendPath(PATH_NOTES).build();
+ }
+
+ public static Uri buildSessionsAtDirUri(long time) {
+ return CONTENT_URI.buildUpon().appendPath(PATH_AT).appendPath(String.valueOf(time))
+ .build();
+ }
+
+ public static Uri buildSearchUri(String query) {
+ return CONTENT_URI.buildUpon().appendPath(PATH_SEARCH).appendPath(query).build();
+ }
+
+ public static boolean isSearchUri(Uri uri) {
+ return PATH_SEARCH.equals(uri.getPathSegments().get(1));
+ }
+
+ /** Read {@link #SESSION_ID} from {@link Sessions} {@link Uri}. */
+ public static String getSessionId(Uri uri) {
+ return uri.getPathSegments().get(1);
+ }
+
+ public static String getSearchQuery(Uri uri) {
+ return uri.getPathSegments().get(2);
+ }
+
+ /**
+ * Generate a {@link #SESSION_ID} that will always match the requested
+ * {@link Sessions} details.
+ */
+ public static String generateSessionId(String title) {
+ return ParserUtils.sanitizeId(title);
+ }
+ }
+
+ /**
+ * Speakers are individual people that lead {@link Sessions}.
+ */
+ public static class Speakers implements SpeakersColumns, SyncColumns, BaseColumns {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_SPEAKERS).build();
+
+ public static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/vnd.iosched.speaker";
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.iosched.speaker";
+
+ /** Default "ORDER BY" clause. */
+ public static final String DEFAULT_SORT = SpeakersColumns.SPEAKER_NAME
+ + " COLLATE NOCASE ASC";
+
+ /** Build {@link Uri} for requested {@link #SPEAKER_ID}. */
+ public static Uri buildSpeakerUri(String speakerId) {
+ return CONTENT_URI.buildUpon().appendPath(speakerId).build();
+ }
+
+ /**
+ * Build {@link Uri} that references any {@link Sessions} associated
+ * with the requested {@link #SPEAKER_ID}.
+ */
+ public static Uri buildSessionsDirUri(String speakerId) {
+ return CONTENT_URI.buildUpon().appendPath(speakerId).appendPath(PATH_SESSIONS).build();
+ }
+
+ /** Read {@link #SPEAKER_ID} from {@link Speakers} {@link Uri}. */
+ public static String getSpeakerId(Uri uri) {
+ return uri.getPathSegments().get(1);
+ }
+
+ /**
+ * Generate a {@link #SPEAKER_ID} that will always match the requested
+ * {@link Speakers} details.
+ */
+ public static String generateSpeakerId(String speakerLdap) {
+ return ParserUtils.sanitizeId(speakerLdap);
+ }
+ }
+
+ /**
+ * Each vendor is a company appearing at the conference that may be
+ * associated with a specific {@link Tracks}.
+ */
+ public static class Vendors implements VendorsColumns, SyncColumns, BaseColumns {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_VENDORS).build();
+ public static final Uri CONTENT_STARRED_URI =
+ CONTENT_URI.buildUpon().appendPath(PATH_STARRED).build();
+
+ public static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/vnd.iosched.vendor";
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.iosched.vendor";
+
+ /** {@link Tracks#TRACK_ID} that this vendor belongs to. */
+ public static final String TRACK_ID = "track_id";
+
+ public static final String SEARCH_SNIPPET = "search_snippet";
+
+ /** Default "ORDER BY" clause. */
+ public static final String DEFAULT_SORT = VendorsColumns.NAME + " COLLATE NOCASE ASC";
+
+ /** Build {@link Uri} for requested {@link #VENDOR_ID}. */
+ public static Uri buildVendorUri(String vendorId) {
+ return CONTENT_URI.buildUpon().appendPath(vendorId).build();
+ }
+
+ public static Uri buildSearchUri(String query) {
+ return CONTENT_URI.buildUpon().appendPath(PATH_SEARCH).appendPath(query).build();
+ }
+
+ public static boolean isSearchUri(Uri uri) {
+ return PATH_SEARCH.equals(uri.getPathSegments().get(1));
+ }
+
+ /** Read {@link #VENDOR_ID} from {@link Vendors} {@link Uri}. */
+ public static String getVendorId(Uri uri) {
+ return uri.getPathSegments().get(1);
+ }
+
+ public static String getSearchQuery(Uri uri) {
+ return uri.getPathSegments().get(2);
+ }
+
+ /**
+ * Generate a {@link #VENDOR_ID} that will always match the requested
+ * {@link Vendors} details.
+ */
+ public static String generateVendorId(String companyLogo) {
+ return ParserUtils.sanitizeId(companyLogo);
+ }
+ }
+
+ /**
+ * Notes are user-generated data related to specific {@link Sessions}.
+ */
+ public static class Notes implements NotesColumns, BaseColumns {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_NOTES).build();
+ public static final Uri CONTENT_EXPORT_URI =
+ CONTENT_URI.buildUpon().appendPath(PATH_EXPORT).build();
+
+ /** {@link Sessions#SESSION_ID} that this note references. */
+ public static final String SESSION_ID = "session_id";
+
+ /** Default "ORDER BY" clause. */
+ public static final String DEFAULT_SORT = NotesColumns.NOTE_TIME + " DESC";
+
+ public static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/vnd.iosched.note";
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.iosched.note";
+
+ public static Uri buildNoteUri(long noteId) {
+ return ContentUris.withAppendedId(CONTENT_URI, noteId);
+ }
+
+ public static long getNoteId(Uri uri) {
+ return ContentUris.parseId(uri);
+ }
+ }
+
+ public static class SearchSuggest {
+ public static final Uri CONTENT_URI =
+ BASE_CONTENT_URI.buildUpon().appendPath(PATH_SEARCH_SUGGEST).build();
+
+ public static final String DEFAULT_SORT = SearchManager.SUGGEST_COLUMN_TEXT_1
+ + " COLLATE NOCASE ASC";
+ }
+
+ private ScheduleContract() {
+ }
+}
diff --git a/src/com/google/android/apps/iosched/provider/ScheduleDatabase.java b/src/com/google/android/apps/iosched/provider/ScheduleDatabase.java
new file mode 100644
index 0000000..dde3c99
--- /dev/null
+++ b/src/com/google/android/apps/iosched/provider/ScheduleDatabase.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.provider;
+
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.provider.ScheduleContract.BlocksColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Notes;
+import com.google.android.apps.iosched.provider.ScheduleContract.NotesColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.provider.ScheduleContract.RoomsColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.SessionsColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Speakers;
+import com.google.android.apps.iosched.provider.ScheduleContract.SpeakersColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.SyncColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.provider.ScheduleContract.TracksColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.provider.ScheduleContract.VendorsColumns;
+
+import android.app.SearchManager;
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.provider.BaseColumns;
+import android.util.Log;
+
+/**
+ * Helper for managing {@link SQLiteDatabase} that stores data for
+ * {@link ScheduleProvider}.
+ */
+public class ScheduleDatabase extends SQLiteOpenHelper {
+ private static final String TAG = "ScheduleDatabase";
+
+ private static final String DATABASE_NAME = "schedule.db";
+
+ // NOTE: carefully update onUpgrade() when bumping database versions to make
+ // sure user data is saved.
+
+ private static final int VER_LAUNCH = 18;
+ private static final int VER_SESSION_HASHTAG = 19;
+
+ private static final int DATABASE_VERSION = VER_SESSION_HASHTAG;
+
+ interface Tables {
+ String BLOCKS = "blocks";
+ String TRACKS = "tracks";
+ String ROOMS = "rooms";
+ String SESSIONS = "sessions";
+ String SPEAKERS = "speakers";
+ String SESSIONS_SPEAKERS = "sessions_speakers";
+ String SESSIONS_TRACKS = "sessions_tracks";
+ String VENDORS = "vendors";
+ String NOTES = "notes";
+
+ String SESSIONS_SEARCH = "sessions_search";
+ String VENDORS_SEARCH = "vendors_search";
+
+ String SEARCH_SUGGEST = "search_suggest";
+
+ String SESSIONS_JOIN_BLOCKS_ROOMS = "sessions "
+ + "LEFT OUTER JOIN blocks ON sessions.block_id=blocks.block_id "
+ + "LEFT OUTER JOIN rooms ON sessions.room_id=rooms.room_id";
+
+ String VENDORS_JOIN_TRACKS = "vendors "
+ + "LEFT OUTER JOIN tracks ON vendors.track_id=tracks.track_id";
+
+ String SESSIONS_SPEAKERS_JOIN_SPEAKERS = "sessions_speakers "
+ + "LEFT OUTER JOIN speakers ON sessions_speakers.speaker_id=speakers.speaker_id";
+
+ String SESSIONS_SPEAKERS_JOIN_SESSIONS_BLOCKS_ROOMS = "sessions_speakers "
+ + "LEFT OUTER JOIN sessions ON sessions_speakers.session_id=sessions.session_id "
+ + "LEFT OUTER JOIN blocks ON sessions.block_id=blocks.block_id "
+ + "LEFT OUTER JOIN rooms ON sessions.room_id=rooms.room_id";
+
+ String SESSIONS_TRACKS_JOIN_TRACKS = "sessions_tracks "
+ + "LEFT OUTER JOIN tracks ON sessions_tracks.track_id=tracks.track_id";
+
+ String SESSIONS_TRACKS_JOIN_SESSIONS_BLOCKS_ROOMS = "sessions_tracks "
+ + "LEFT OUTER JOIN sessions ON sessions_tracks.session_id=sessions.session_id "
+ + "LEFT OUTER JOIN blocks ON sessions.block_id=blocks.block_id "
+ + "LEFT OUTER JOIN rooms ON sessions.room_id=rooms.room_id";
+
+ String SESSIONS_SEARCH_JOIN_SESSIONS_BLOCKS_ROOMS = "sessions_search "
+ + "LEFT OUTER JOIN sessions ON sessions_search.session_id=sessions.session_id "
+ + "LEFT OUTER JOIN blocks ON sessions.block_id=blocks.block_id "
+ + "LEFT OUTER JOIN rooms ON sessions.room_id=rooms.room_id";
+
+ String VENDORS_SEARCH_JOIN_VENDORS_TRACKS = "vendors_search "
+ + "LEFT OUTER JOIN vendors ON vendors_search.vendor_id=vendors.vendor_id "
+ + "LEFT OUTER JOIN tracks ON vendors.track_id=tracks.track_id";
+
+ }
+
+ private interface Triggers {
+ String SESSIONS_SEARCH_INSERT = "sessions_search_insert";
+ String SESSIONS_SEARCH_DELETE = "sessions_search_delete";
+
+ String VENDORS_SEARCH_INSERT = "vendors_search_insert";
+ String VENDORS_SEARCH_DELETE = "vendors_search_delete";
+ }
+
+ public interface SessionsSpeakers {
+ String SESSION_ID = "session_id";
+ String SPEAKER_ID = "speaker_id";
+ }
+
+ public interface SessionsTracks {
+ String SESSION_ID = "session_id";
+ String TRACK_ID = "track_id";
+ }
+
+ interface SessionsSearchColumns {
+ String SESSION_ID = "session_id";
+ String BODY = "body";
+ }
+
+ interface VendorsSearchColumns {
+ String VENDOR_ID = "vendor_id";
+ String BODY = "body";
+ }
+
+ /** Fully-qualified field names. */
+ private interface Qualified {
+ String SESSIONS_SEARCH_SESSION_ID = Tables.SESSIONS_SEARCH + "."
+ + SessionsSearchColumns.SESSION_ID;
+ String VENDORS_SEARCH_VENDOR_ID = Tables.VENDORS_SEARCH + "."
+ + VendorsSearchColumns.VENDOR_ID;
+
+ String SESSIONS_SEARCH = Tables.SESSIONS_SEARCH + "(" + SessionsSearchColumns.SESSION_ID
+ + "," + SessionsSearchColumns.BODY + ")";
+ String VENDORS_SEARCH = Tables.VENDORS_SEARCH + "(" + VendorsSearchColumns.VENDOR_ID + ","
+ + VendorsSearchColumns.BODY + ")";
+ }
+
+ /** {@code REFERENCES} clauses. */
+ private interface References {
+ String BLOCK_ID = "REFERENCES " + Tables.BLOCKS + "(" + Blocks.BLOCK_ID + ")";
+ String TRACK_ID = "REFERENCES " + Tables.TRACKS + "(" + Tracks.TRACK_ID + ")";
+ String ROOM_ID = "REFERENCES " + Tables.ROOMS + "(" + Rooms.ROOM_ID + ")";
+ String SESSION_ID = "REFERENCES " + Tables.SESSIONS + "(" + Sessions.SESSION_ID + ")";
+ String SPEAKER_ID = "REFERENCES " + Tables.SPEAKERS + "(" + Speakers.SPEAKER_ID + ")";
+ String VENDOR_ID = "REFERENCES " + Tables.VENDORS + "(" + Vendors.VENDOR_ID + ")";
+ }
+
+ private interface Subquery {
+ /**
+ * Subquery used to build the {@link SessionsSearchColumns#BODY} string
+ * used for indexing {@link Sessions} content.
+ */
+ String SESSIONS_BODY = "(new." + Sessions.TITLE + "||'; '||new." + Sessions.ABSTRACT
+ + "||'; '||new." + Sessions.REQUIREMENTS + "||'; '||" + "new." + Sessions.KEYWORDS
+ + ")";
+
+ /**
+ * Subquery used to build the {@link VendorsSearchColumns#BODY} string
+ * used for indexing {@link Vendors} content.
+ */
+ String VENDORS_BODY = "(new." + Vendors.NAME + "||'; '||new." + Vendors.DESC
+ + "||'; '||new." + Vendors.PRODUCT_DESC + ")";
+ }
+
+ public ScheduleDatabase(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + Tables.BLOCKS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + BlocksColumns.BLOCK_ID + " TEXT NOT NULL,"
+ + BlocksColumns.BLOCK_TITLE + " TEXT NOT NULL,"
+ + BlocksColumns.BLOCK_START + " INTEGER NOT NULL,"
+ + BlocksColumns.BLOCK_END + " INTEGER NOT NULL,"
+ + BlocksColumns.BLOCK_TYPE + " TEXT,"
+ + "UNIQUE (" + BlocksColumns.BLOCK_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.TRACKS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + TracksColumns.TRACK_ID + " TEXT NOT NULL,"
+ + TracksColumns.TRACK_NAME + " TEXT,"
+ + TracksColumns.TRACK_COLOR + " INTEGER,"
+ + TracksColumns.TRACK_ABSTRACT + " TEXT,"
+ + "UNIQUE (" + TracksColumns.TRACK_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.ROOMS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + RoomsColumns.ROOM_ID + " TEXT NOT NULL,"
+ + RoomsColumns.ROOM_NAME + " TEXT,"
+ + RoomsColumns.ROOM_FLOOR + " TEXT,"
+ + "UNIQUE (" + RoomsColumns.ROOM_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.SESSIONS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SyncColumns.UPDATED + " INTEGER NOT NULL,"
+ + SessionsColumns.SESSION_ID + " TEXT NOT NULL,"
+ + Sessions.BLOCK_ID + " TEXT " + References.BLOCK_ID + ","
+ + Sessions.ROOM_ID + " TEXT " + References.ROOM_ID + ","
+ + SessionsColumns.TYPE + " TEXT,"
+ + SessionsColumns.TITLE + " TEXT,"
+ + SessionsColumns.ABSTRACT + " TEXT,"
+ + SessionsColumns.REQUIREMENTS + " TEXT,"
+ + SessionsColumns.MODERATOR_URL + " TEXT,"
+ + SessionsColumns.WAVE_URL + " TEXT,"
+ + SessionsColumns.KEYWORDS + " TEXT,"
+ + SessionsColumns.HASHTAG + " TEXT,"
+ + SessionsColumns.STARRED + " INTEGER NOT NULL DEFAULT 0,"
+ + "UNIQUE (" + SessionsColumns.SESSION_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.SPEAKERS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SyncColumns.UPDATED + " INTEGER NOT NULL,"
+ + SpeakersColumns.SPEAKER_ID + " TEXT NOT NULL,"
+ + SpeakersColumns.SPEAKER_NAME + " TEXT,"
+ + SpeakersColumns.SPEAKER_COMPANY + " TEXT,"
+ + SpeakersColumns.SPEAKER_ABSTRACT + " TEXT,"
+ + "UNIQUE (" + SpeakersColumns.SPEAKER_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.SESSIONS_SPEAKERS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SessionsSpeakers.SESSION_ID + " TEXT NOT NULL " + References.SESSION_ID + ","
+ + SessionsSpeakers.SPEAKER_ID + " TEXT NOT NULL " + References.SPEAKER_ID + ","
+ + "UNIQUE (" + SessionsSpeakers.SESSION_ID + ","
+ + SessionsSpeakers.SPEAKER_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.SESSIONS_TRACKS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SessionsTracks.SESSION_ID + " TEXT NOT NULL " + References.SESSION_ID + ","
+ + SessionsTracks.TRACK_ID + " TEXT NOT NULL " + References.TRACK_ID + ","
+ + "UNIQUE (" + SessionsTracks.SESSION_ID + ","
+ + SessionsTracks.TRACK_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.VENDORS + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SyncColumns.UPDATED + " INTEGER NOT NULL,"
+ + VendorsColumns.VENDOR_ID + " TEXT NOT NULL,"
+ + Vendors.TRACK_ID + " TEXT " + References.TRACK_ID + ","
+ + VendorsColumns.NAME + " TEXT,"
+ + VendorsColumns.LOCATION + " TEXT,"
+ + VendorsColumns.DESC + " TEXT,"
+ + VendorsColumns.URL + " TEXT,"
+ + VendorsColumns.PRODUCT_DESC + " TEXT,"
+ + VendorsColumns.LOGO_URL + " TEXT,"
+ + VendorsColumns.LOGO + " BLOB DEFAULT NULL,"
+ + VendorsColumns.STARRED + " INTEGER,"
+ + "UNIQUE (" + VendorsColumns.VENDOR_ID + ") ON CONFLICT REPLACE)");
+
+ db.execSQL("CREATE TABLE " + Tables.NOTES + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + Notes.SESSION_ID + " TEXT NOT NULL " + References.SESSION_ID + ","
+ + NotesColumns.NOTE_TIME + " INTEGER NOT NULL,"
+ + NotesColumns.NOTE_CONTENT + " TEXT)");
+
+ createSessionsSearch(db);
+ createVendorsSearch(db);
+
+ db.execSQL("CREATE TABLE " + Tables.SEARCH_SUGGEST + " ("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SearchManager.SUGGEST_COLUMN_TEXT_1 + " TEXT NOT NULL)");
+
+ }
+
+ /**
+ * Create triggers that automatically build {@link Tables#SESSIONS_SEARCH}
+ * as values are changed in {@link Tables#SESSIONS}.
+ */
+ private static void createSessionsSearch(SQLiteDatabase db) {
+ // Using the "porter" tokenizer for simple stemming, so that
+ // "frustration" matches "frustrated."
+
+ db.execSQL("CREATE VIRTUAL TABLE " + Tables.SESSIONS_SEARCH + " USING fts3("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + SessionsSearchColumns.BODY + " TEXT NOT NULL,"
+ + SessionsSearchColumns.SESSION_ID
+ + " TEXT NOT NULL " + References.SESSION_ID + ","
+ + "UNIQUE (" + SessionsSearchColumns.SESSION_ID + ") ON CONFLICT REPLACE,"
+ + "tokenize=porter)");
+
+ // TODO: handle null fields in body, which cause trigger to fail
+ // TODO: implement update trigger, not currently exercised
+
+ db.execSQL("CREATE TRIGGER " + Triggers.SESSIONS_SEARCH_INSERT + " AFTER INSERT ON "
+ + Tables.SESSIONS + " BEGIN INSERT INTO " + Qualified.SESSIONS_SEARCH + " "
+ + " VALUES(new." + Sessions.SESSION_ID + ", " + Subquery.SESSIONS_BODY + ");"
+ + " END;");
+
+ db.execSQL("CREATE TRIGGER " + Triggers.SESSIONS_SEARCH_DELETE + " AFTER DELETE ON "
+ + Tables.SESSIONS + " BEGIN DELETE FROM " + Tables.SESSIONS_SEARCH + " "
+ + " WHERE " + Qualified.SESSIONS_SEARCH_SESSION_ID + "=old." + Sessions.SESSION_ID
+ + ";" + " END;");
+
+ }
+
+ /**
+ * Create triggers that automatically build {@link Tables#VENDORS_SEARCH} as
+ * values are changed in {@link Tables#VENDORS}.
+ */
+ private static void createVendorsSearch(SQLiteDatabase db) {
+ // Using the "porter" tokenizer for simple stemming, so that
+ // "frustration" matches "frustrated."
+
+ db.execSQL("CREATE VIRTUAL TABLE " + Tables.VENDORS_SEARCH + " USING fts3("
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + VendorsSearchColumns.BODY + " TEXT NOT NULL,"
+ + VendorsSearchColumns.VENDOR_ID
+ + " TEXT NOT NULL " + References.VENDOR_ID + ","
+ + "UNIQUE (" + VendorsSearchColumns.VENDOR_ID + ") ON CONFLICT REPLACE,"
+ + "tokenize=porter)");
+
+ // TODO: handle null fields in body, which cause trigger to fail
+ // TODO: implement update trigger, not currently exercised
+
+ db.execSQL("CREATE TRIGGER " + Triggers.VENDORS_SEARCH_INSERT + " AFTER INSERT ON "
+ + Tables.VENDORS + " BEGIN INSERT INTO " + Qualified.VENDORS_SEARCH + " "
+ + " VALUES(new." + Vendors.VENDOR_ID + ", " + Subquery.VENDORS_BODY + ");"
+ + " END;");
+
+ db.execSQL("CREATE TRIGGER " + Triggers.VENDORS_SEARCH_DELETE + " AFTER DELETE ON "
+ + Tables.VENDORS + " BEGIN DELETE FROM " + Tables.VENDORS_SEARCH + " "
+ + " WHERE " + Qualified.VENDORS_SEARCH_VENDOR_ID + "=old." + Vendors.VENDOR_ID
+ + ";" + " END;");
+
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.d(TAG, "onUpgrade() from " + oldVersion + " to " + newVersion);
+
+ // NOTE: This switch statement is designed to handle cascading database
+ // updates, starting at the current version and falling through to all
+ // future upgrade cases. Only use "break;" when you want to drop and
+ // recreate the entire database.
+ int version = oldVersion;
+ switch (version) {
+ case VER_LAUNCH:
+ // Version 19 added column for session hashtags.
+ db.execSQL("ALTER TABLE " + Tables.SESSIONS + " ADD COLUMN "
+ + SessionsColumns.HASHTAG + " TEXT");
+ version = VER_SESSION_HASHTAG;
+ }
+
+ Log.d(TAG, "after upgrade logic, at version " + version);
+ if (version != DATABASE_VERSION) {
+ Log.w(TAG, "Destroying old data during upgrade");
+
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.BLOCKS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.TRACKS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.ROOMS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.SESSIONS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.SPEAKERS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.SESSIONS_SPEAKERS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.SESSIONS_TRACKS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.VENDORS);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.NOTES);
+
+ db.execSQL("DROP TRIGGER IF EXISTS " + Triggers.SESSIONS_SEARCH_INSERT);
+ db.execSQL("DROP TRIGGER IF EXISTS " + Triggers.SESSIONS_SEARCH_DELETE);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.SESSIONS_SEARCH);
+
+ db.execSQL("DROP TRIGGER IF EXISTS " + Triggers.VENDORS_SEARCH_INSERT);
+ db.execSQL("DROP TRIGGER IF EXISTS " + Triggers.VENDORS_SEARCH_DELETE);
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.VENDORS_SEARCH);
+
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.SEARCH_SUGGEST);
+
+ onCreate(db);
+ }
+ }
+}
diff --git a/src/com/google/android/apps/iosched/provider/ScheduleProvider.java b/src/com/google/android/apps/iosched/provider/ScheduleProvider.java
new file mode 100644
index 0000000..d0d9585
--- /dev/null
+++ b/src/com/google/android/apps/iosched/provider/ScheduleProvider.java
@@ -0,0 +1,729 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.provider;
+
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Notes;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.provider.ScheduleContract.SearchSuggest;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Speakers;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.SessionsSearchColumns;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.SessionsSpeakers;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.SessionsTracks;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.Tables;
+import com.google.android.apps.iosched.provider.ScheduleDatabase.VendorsSearchColumns;
+import com.google.android.apps.iosched.service.SyncService;
+import com.google.android.apps.iosched.util.NotesExporter;
+import com.google.android.apps.iosched.util.SelectionBuilder;
+
+import android.app.Activity;
+import android.app.SearchManager;
+import android.content.ContentProvider;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.provider.BaseColumns;
+import android.provider.OpenableColumns;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Provider that stores {@link ScheduleContract} data. Data is usually inserted
+ * by {@link SyncService}, and queried by various {@link Activity} instances.
+ */
+public class ScheduleProvider extends ContentProvider {
+ private static final String TAG = "ScheduleProvider";
+ private static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
+
+ private ScheduleDatabase mOpenHelper;
+
+ private static final UriMatcher sUriMatcher = buildUriMatcher();
+
+ private static final int BLOCKS = 100;
+ private static final int BLOCKS_BETWEEN = 101;
+ private static final int BLOCKS_ID = 102;
+ private static final int BLOCKS_ID_SESSIONS = 103;
+
+ private static final int TRACKS = 200;
+ private static final int TRACKS_ID = 201;
+ private static final int TRACKS_ID_SESSIONS = 202;
+ private static final int TRACKS_ID_VENDORS = 203;
+
+ private static final int ROOMS = 300;
+ private static final int ROOMS_ID = 301;
+ private static final int ROOMS_ID_SESSIONS = 302;
+
+ private static final int SESSIONS = 400;
+ private static final int SESSIONS_STARRED = 401;
+ private static final int SESSIONS_SEARCH = 402;
+ private static final int SESSIONS_AT = 403;
+ private static final int SESSIONS_ID = 404;
+ private static final int SESSIONS_ID_SPEAKERS = 405;
+ private static final int SESSIONS_ID_TRACKS = 406;
+ private static final int SESSIONS_ID_NOTES = 407;
+
+ private static final int SPEAKERS = 500;
+ private static final int SPEAKERS_ID = 501;
+ private static final int SPEAKERS_ID_SESSIONS = 502;
+
+ private static final int VENDORS = 600;
+ private static final int VENDORS_STARRED = 601;
+ private static final int VENDORS_SEARCH = 603;
+ private static final int VENDORS_ID = 604;
+
+ private static final int NOTES = 700;
+ private static final int NOTES_EXPORT = 701;
+ private static final int NOTES_ID = 702;
+
+ private static final int SEARCH_SUGGEST = 800;
+
+ private static final String MIME_XML = "text/xml";
+
+ /**
+ * Build and return a {@link UriMatcher} that catches all {@link Uri}
+ * variations supported by this {@link ContentProvider}.
+ */
+ private static UriMatcher buildUriMatcher() {
+ final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
+ final String authority = ScheduleContract.CONTENT_AUTHORITY;
+
+ matcher.addURI(authority, "blocks", BLOCKS);
+ matcher.addURI(authority, "blocks/between/*/*", BLOCKS_BETWEEN);
+ matcher.addURI(authority, "blocks/*", BLOCKS_ID);
+ matcher.addURI(authority, "blocks/*/sessions", BLOCKS_ID_SESSIONS);
+
+ matcher.addURI(authority, "tracks", TRACKS);
+ matcher.addURI(authority, "tracks/*", TRACKS_ID);
+ matcher.addURI(authority, "tracks/*/sessions", TRACKS_ID_SESSIONS);
+ matcher.addURI(authority, "tracks/*/vendors", TRACKS_ID_VENDORS);
+
+ matcher.addURI(authority, "rooms", ROOMS);
+ matcher.addURI(authority, "rooms/*", ROOMS_ID);
+ matcher.addURI(authority, "rooms/*/sessions", ROOMS_ID_SESSIONS);
+
+ matcher.addURI(authority, "sessions", SESSIONS);
+ matcher.addURI(authority, "sessions/starred", SESSIONS_STARRED);
+ matcher.addURI(authority, "sessions/search/*", SESSIONS_SEARCH);
+ matcher.addURI(authority, "sessions/at/*", SESSIONS_AT);
+ matcher.addURI(authority, "sessions/*", SESSIONS_ID);
+ matcher.addURI(authority, "sessions/*/speakers", SESSIONS_ID_SPEAKERS);
+ matcher.addURI(authority, "sessions/*/tracks", SESSIONS_ID_TRACKS);
+ matcher.addURI(authority, "sessions/*/notes", SESSIONS_ID_NOTES);
+
+ matcher.addURI(authority, "speakers", SPEAKERS);
+ matcher.addURI(authority, "speakers/*", SPEAKERS_ID);
+ matcher.addURI(authority, "speakers/*/sessions", SPEAKERS_ID_SESSIONS);
+
+ matcher.addURI(authority, "vendors", VENDORS);
+ matcher.addURI(authority, "vendors/starred", VENDORS_STARRED);
+ matcher.addURI(authority, "vendors/search/*", VENDORS_SEARCH);
+ matcher.addURI(authority, "vendors/*", VENDORS_ID);
+
+ matcher.addURI(authority, "notes", NOTES);
+ matcher.addURI(authority, "notes/export", NOTES_EXPORT);
+ matcher.addURI(authority, "notes/*", NOTES_ID);
+
+ matcher.addURI(authority, "search_suggest_query", SEARCH_SUGGEST);
+
+ return matcher;
+ }
+
+ @Override
+ public boolean onCreate() {
+ final Context context = getContext();
+ mOpenHelper = new ScheduleDatabase(context);
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getType(Uri uri) {
+ final int match = sUriMatcher.match(uri);
+ switch (match) {
+ case BLOCKS:
+ return Blocks.CONTENT_TYPE;
+ case BLOCKS_BETWEEN:
+ return Blocks.CONTENT_TYPE;
+ case BLOCKS_ID:
+ return Blocks.CONTENT_ITEM_TYPE;
+ case BLOCKS_ID_SESSIONS:
+ return Sessions.CONTENT_TYPE;
+ case TRACKS:
+ return Tracks.CONTENT_TYPE;
+ case TRACKS_ID:
+ return Tracks.CONTENT_ITEM_TYPE;
+ case TRACKS_ID_SESSIONS:
+ return Sessions.CONTENT_TYPE;
+ case TRACKS_ID_VENDORS:
+ return Vendors.CONTENT_TYPE;
+ case ROOMS:
+ return Rooms.CONTENT_TYPE;
+ case ROOMS_ID:
+ return Rooms.CONTENT_ITEM_TYPE;
+ case ROOMS_ID_SESSIONS:
+ return Sessions.CONTENT_TYPE;
+ case SESSIONS:
+ return Sessions.CONTENT_TYPE;
+ case SESSIONS_STARRED:
+ return Sessions.CONTENT_TYPE;
+ case SESSIONS_SEARCH:
+ return Sessions.CONTENT_TYPE;
+ case SESSIONS_AT:
+ return Sessions.CONTENT_TYPE;
+ case SESSIONS_ID:
+ return Sessions.CONTENT_ITEM_TYPE;
+ case SESSIONS_ID_SPEAKERS:
+ return Speakers.CONTENT_TYPE;
+ case SESSIONS_ID_TRACKS:
+ return Tracks.CONTENT_TYPE;
+ case SESSIONS_ID_NOTES:
+ return Notes.CONTENT_TYPE;
+ case SPEAKERS:
+ return Speakers.CONTENT_TYPE;
+ case SPEAKERS_ID:
+ return Speakers.CONTENT_ITEM_TYPE;
+ case SPEAKERS_ID_SESSIONS:
+ return Sessions.CONTENT_TYPE;
+ case VENDORS:
+ return Vendors.CONTENT_TYPE;
+ case VENDORS_STARRED:
+ return Vendors.CONTENT_TYPE;
+ case VENDORS_SEARCH:
+ return Vendors.CONTENT_TYPE;
+ case VENDORS_ID:
+ return Vendors.CONTENT_ITEM_TYPE;
+ case NOTES:
+ return Notes.CONTENT_TYPE;
+ case NOTES_EXPORT:
+ return MIME_XML;
+ case NOTES_ID:
+ return Notes.CONTENT_ITEM_TYPE;
+ default:
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ if (LOGV) Log.v(TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")");
+ final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+
+ final int match = sUriMatcher.match(uri);
+ switch (match) {
+ default: {
+ // Most cases are handled with simple SelectionBuilder
+ final SelectionBuilder builder = buildExpandedSelection(uri, match);
+ return builder.where(selection, selectionArgs).query(db, projection, sortOrder);
+ }
+ case NOTES_EXPORT: {
+ // Provide query values for file attachments
+ final String[] columns = { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE };
+ final MatrixCursor cursor = new MatrixCursor(columns, 1);
+ cursor.addRow(new String[] { "notes.xml", null });
+ return cursor;
+ }
+ case SEARCH_SUGGEST: {
+ final SelectionBuilder builder = new SelectionBuilder();
+
+ // Adjust incoming query to become SQL text match
+ selectionArgs[0] = selectionArgs[0] + "%";
+ builder.table(Tables.SEARCH_SUGGEST);
+ builder.where(selection, selectionArgs);
+ builder.map(SearchManager.SUGGEST_COLUMN_QUERY,
+ SearchManager.SUGGEST_COLUMN_TEXT_1);
+
+ projection = new String[] { BaseColumns._ID, SearchManager.SUGGEST_COLUMN_TEXT_1,
+ SearchManager.SUGGEST_COLUMN_QUERY };
+
+ final String limit = uri.getQueryParameter(SearchManager.SUGGEST_PARAMETER_LIMIT);
+ return builder.query(db, projection, null, null, SearchSuggest.DEFAULT_SORT, limit);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ if (LOGV) Log.v(TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")");
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ final int match = sUriMatcher.match(uri);
+ switch (match) {
+ case BLOCKS: {
+ db.insertOrThrow(Tables.BLOCKS, null, values);
+ return Blocks.buildBlockUri(values.getAsString(Blocks.BLOCK_ID));
+ }
+ case TRACKS: {
+ db.insertOrThrow(Tables.TRACKS, null, values);
+ return Tracks.buildTrackUri(values.getAsString(Tracks.TRACK_ID));
+ }
+ case ROOMS: {
+ db.insertOrThrow(Tables.ROOMS, null, values);
+ return Rooms.buildRoomUri(values.getAsString(Rooms.ROOM_ID));
+ }
+ case SESSIONS: {
+ db.insertOrThrow(Tables.SESSIONS, null, values);
+ return Sessions.buildSessionUri(values.getAsString(Sessions.SESSION_ID));
+ }
+ case SESSIONS_ID_SPEAKERS: {
+ db.insertOrThrow(Tables.SESSIONS_SPEAKERS, null, values);
+ return Speakers.buildSpeakerUri(values.getAsString(SessionsSpeakers.SPEAKER_ID));
+ }
+ case SESSIONS_ID_TRACKS: {
+ db.insertOrThrow(Tables.SESSIONS_TRACKS, null, values);
+ return Tracks.buildTrackUri(values.getAsString(SessionsTracks.TRACK_ID));
+ }
+ case SESSIONS_ID_NOTES: {
+ final String sessionId = Sessions.getSessionId(uri);
+ values.put(Notes.SESSION_ID, sessionId);
+ final long noteId = db.insertOrThrow(Tables.NOTES, null, values);
+ return ContentUris.withAppendedId(Notes.CONTENT_URI, noteId);
+ }
+ case SPEAKERS: {
+ db.insertOrThrow(Tables.SPEAKERS, null, values);
+ return Speakers.buildSpeakerUri(values.getAsString(Speakers.SPEAKER_ID));
+ }
+ case VENDORS: {
+ db.insertOrThrow(Tables.VENDORS, null, values);
+ return Vendors.buildVendorUri(values.getAsString(Vendors.VENDOR_ID));
+ }
+ case NOTES: {
+ final long noteId = db.insertOrThrow(Tables.NOTES, null, values);
+ return ContentUris.withAppendedId(Notes.CONTENT_URI, noteId);
+ }
+ case SEARCH_SUGGEST: {
+ db.insertOrThrow(Tables.SEARCH_SUGGEST, null, values);
+ return SearchSuggest.CONTENT_URI;
+ }
+ default: {
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ if (LOGV) Log.v(TAG, "update(uri=" + uri + ", values=" + values.toString() + ")");
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ final SelectionBuilder builder = buildSimpleSelection(uri);
+ return builder.where(selection, selectionArgs).update(db, values);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ if (LOGV) Log.v(TAG, "delete(uri=" + uri + ")");
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ final SelectionBuilder builder = buildSimpleSelection(uri);
+ return builder.where(selection, selectionArgs).delete(db);
+ }
+
+ /**
+ * Apply the given set of {@link ContentProviderOperation}, executing inside
+ * a {@link SQLiteDatabase} transaction. All changes will be rolled back if
+ * any single one fails.
+ */
+ @Override
+ public ContentProviderResult[] applyBatch(ArrayList operations)
+ throws OperationApplicationException {
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ final int numOperations = operations.size();
+ final ContentProviderResult[] results = new ContentProviderResult[numOperations];
+ for (int i = 0; i < numOperations; i++) {
+ results[i] = operations.get(i).apply(this, results, i);
+ }
+ db.setTransactionSuccessful();
+ return results;
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ /**
+ * Build a simple {@link SelectionBuilder} to match the requested
+ * {@link Uri}. This is usually enough to support {@link #insert},
+ * {@link #update}, and {@link #delete} operations.
+ */
+ private SelectionBuilder buildSimpleSelection(Uri uri) {
+ final SelectionBuilder builder = new SelectionBuilder();
+ final int match = sUriMatcher.match(uri);
+ switch (match) {
+ case BLOCKS: {
+ return builder.table(Tables.BLOCKS);
+ }
+ case BLOCKS_ID: {
+ final String blockId = Blocks.getBlockId(uri);
+ return builder.table(Tables.BLOCKS)
+ .where(Blocks.BLOCK_ID + "=?", blockId);
+ }
+ case TRACKS: {
+ return builder.table(Tables.TRACKS);
+ }
+ case TRACKS_ID: {
+ final String trackId = Tracks.getTrackId(uri);
+ return builder.table(Tables.TRACKS)
+ .where(Tracks.TRACK_ID + "=?", trackId);
+ }
+ case ROOMS: {
+ return builder.table(Tables.ROOMS);
+ }
+ case ROOMS_ID: {
+ final String roomId = Rooms.getRoomId(uri);
+ return builder.table(Tables.ROOMS)
+ .where(Rooms.ROOM_ID + "=?", roomId);
+ }
+ case SESSIONS: {
+ return builder.table(Tables.SESSIONS);
+ }
+ case SESSIONS_ID: {
+ final String sessionId = Sessions.getSessionId(uri);
+ return builder.table(Tables.SESSIONS)
+ .where(Sessions.SESSION_ID + "=?", sessionId);
+ }
+ case SESSIONS_ID_SPEAKERS: {
+ final String sessionId = Sessions.getSessionId(uri);
+ return builder.table(Tables.SESSIONS_SPEAKERS)
+ .where(Sessions.SESSION_ID + "=?", sessionId);
+ }
+ case SESSIONS_ID_TRACKS: {
+ final String sessionId = Sessions.getSessionId(uri);
+ return builder.table(Tables.SESSIONS_TRACKS)
+ .where(Sessions.SESSION_ID + "=?", sessionId);
+ }
+ case SPEAKERS: {
+ return builder.table(Tables.SPEAKERS);
+ }
+ case SPEAKERS_ID: {
+ final String speakerId = Speakers.getSpeakerId(uri);
+ return builder.table(Tables.SPEAKERS)
+ .where(Speakers.SPEAKER_ID + "=?", speakerId);
+ }
+ case VENDORS: {
+ return builder.table(Tables.VENDORS);
+ }
+ case VENDORS_ID: {
+ final String vendorId = Vendors.getVendorId(uri);
+ return builder.table(Tables.VENDORS)
+ .where(Vendors.VENDOR_ID + "=?", vendorId);
+ }
+ case NOTES: {
+ return builder.table(Tables.NOTES);
+ }
+ case NOTES_ID: {
+ final String noteId = uri.getPathSegments().get(1);
+ return builder.table(Tables.NOTES)
+ .where(Notes._ID + "=?", noteId);
+ }
+ case SEARCH_SUGGEST: {
+ return builder.table(Tables.SEARCH_SUGGEST);
+ }
+ default: {
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+ }
+ }
+
+ /**
+ * Build an advanced {@link SelectionBuilder} to match the requested
+ * {@link Uri}. This is usually only used by {@link #query}, since it
+ * performs table joins useful for {@link Cursor} data.
+ */
+ private SelectionBuilder buildExpandedSelection(Uri uri, int match) {
+ final SelectionBuilder builder = new SelectionBuilder();
+ switch (match) {
+ case BLOCKS: {
+ return builder.table(Tables.BLOCKS);
+ }
+ case BLOCKS_BETWEEN: {
+ final List segments = uri.getPathSegments();
+ final String startTime = segments.get(2);
+ final String endTime = segments.get(3);
+ return builder.table(Tables.BLOCKS)
+ .map(Blocks.SESSIONS_COUNT, Subquery.BLOCK_SESSIONS_COUNT)
+ .map(Blocks.CONTAINS_STARRED, Subquery.BLOCK_CONTAINS_STARRED)
+ .where(Blocks.BLOCK_START + ">=?", startTime)
+ .where(Blocks.BLOCK_START + "<=?", endTime);
+ }
+ case BLOCKS_ID: {
+ final String blockId = Blocks.getBlockId(uri);
+ return builder.table(Tables.BLOCKS)
+ .map(Blocks.SESSIONS_COUNT, Subquery.BLOCK_SESSIONS_COUNT)
+ .map(Blocks.CONTAINS_STARRED, Subquery.BLOCK_CONTAINS_STARRED)
+ .where(Blocks.BLOCK_ID + "=?", blockId);
+ }
+ case BLOCKS_ID_SESSIONS: {
+ final String blockId = Blocks.getBlockId(uri);
+ return builder.table(Tables.SESSIONS_JOIN_BLOCKS_ROOMS)
+ .map(Blocks.SESSIONS_COUNT, Subquery.BLOCK_SESSIONS_COUNT)
+ .map(Blocks.CONTAINS_STARRED, Subquery.BLOCK_CONTAINS_STARRED)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.SESSION_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(Qualified.SESSIONS_BLOCK_ID + "=?", blockId);
+ }
+ case TRACKS: {
+ return builder.table(Tables.TRACKS)
+ .map(Tracks.SESSIONS_COUNT, Subquery.TRACK_SESSIONS_COUNT)
+ .map(Tracks.VENDORS_COUNT, Subquery.TRACK_VENDORS_COUNT);
+ }
+ case TRACKS_ID: {
+ final String trackId = Tracks.getTrackId(uri);
+ return builder.table(Tables.TRACKS)
+ .where(Tracks.TRACK_ID + "=?", trackId);
+ }
+ case TRACKS_ID_SESSIONS: {
+ final String trackId = Tracks.getTrackId(uri);
+ return builder.table(Tables.SESSIONS_TRACKS_JOIN_SESSIONS_BLOCKS_ROOMS)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.SESSION_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(Qualified.SESSIONS_TRACKS_TRACK_ID + "=?", trackId);
+ }
+ case TRACKS_ID_VENDORS: {
+ final String trackId = Tracks.getTrackId(uri);
+ return builder.table(Tables.VENDORS_JOIN_TRACKS)
+ .mapToTable(Vendors._ID, Tables.VENDORS)
+ .mapToTable(Vendors.TRACK_ID, Tables.VENDORS)
+ .where(Qualified.VENDORS_TRACK_ID + "=?", trackId);
+ }
+ case ROOMS: {
+ return builder.table(Tables.ROOMS);
+ }
+ case ROOMS_ID: {
+ final String roomId = Rooms.getRoomId(uri);
+ return builder.table(Tables.ROOMS)
+ .where(Rooms.ROOM_ID + "=?", roomId);
+ }
+ case ROOMS_ID_SESSIONS: {
+ final String roomId = Rooms.getRoomId(uri);
+ return builder.table(Tables.SESSIONS_JOIN_BLOCKS_ROOMS)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(Qualified.SESSIONS_ROOM_ID + "=?", roomId);
+ }
+ case SESSIONS: {
+ return builder.table(Tables.SESSIONS_JOIN_BLOCKS_ROOMS)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS);
+ }
+ case SESSIONS_STARRED: {
+ return builder.table(Tables.SESSIONS_JOIN_BLOCKS_ROOMS)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(Sessions.STARRED + "=1");
+ }
+ case SESSIONS_SEARCH: {
+ final String query = Sessions.getSearchQuery(uri);
+ return builder.table(Tables.SESSIONS_SEARCH_JOIN_SESSIONS_BLOCKS_ROOMS)
+ .map(Sessions.SEARCH_SNIPPET, Subquery.SESSIONS_SNIPPET)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.SESSION_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(SessionsSearchColumns.BODY + " MATCH ?", query);
+ }
+ case SESSIONS_AT: {
+ final List segments = uri.getPathSegments();
+ final String time = segments.get(2);
+ return builder.table(Tables.SESSIONS_JOIN_BLOCKS_ROOMS)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(Sessions.BLOCK_START + "<=?", time)
+ .where(Sessions.BLOCK_END + ">=?", time);
+ }
+ case SESSIONS_ID: {
+ final String sessionId = Sessions.getSessionId(uri);
+ return builder.table(Tables.SESSIONS_JOIN_BLOCKS_ROOMS)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(Qualified.SESSIONS_SESSION_ID + "=?", sessionId);
+ }
+ case SESSIONS_ID_SPEAKERS: {
+ final String sessionId = Sessions.getSessionId(uri);
+ return builder.table(Tables.SESSIONS_SPEAKERS_JOIN_SPEAKERS)
+ .mapToTable(Speakers._ID, Tables.SPEAKERS)
+ .mapToTable(Speakers.SPEAKER_ID, Tables.SPEAKERS)
+ .where(Qualified.SESSIONS_SPEAKERS_SESSION_ID + "=?", sessionId);
+ }
+ case SESSIONS_ID_TRACKS: {
+ final String sessionId = Sessions.getSessionId(uri);
+ return builder.table(Tables.SESSIONS_TRACKS_JOIN_TRACKS)
+ .mapToTable(Tracks._ID, Tables.TRACKS)
+ .mapToTable(Tracks.TRACK_ID, Tables.TRACKS)
+ .where(Qualified.SESSIONS_TRACKS_SESSION_ID + "=?", sessionId);
+ }
+ case SESSIONS_ID_NOTES: {
+ final String sessionId = Sessions.getSessionId(uri);
+ return builder.table(Tables.NOTES)
+ .where(Notes.SESSION_ID + "=?", sessionId);
+ }
+ case SPEAKERS: {
+ return builder.table(Tables.SPEAKERS);
+ }
+ case SPEAKERS_ID: {
+ final String speakerId = Speakers.getSpeakerId(uri);
+ return builder.table(Tables.SPEAKERS)
+ .where(Speakers.SPEAKER_ID + "=?", speakerId);
+ }
+ case SPEAKERS_ID_SESSIONS: {
+ final String speakerId = Speakers.getSpeakerId(uri);
+ return builder.table(Tables.SESSIONS_SPEAKERS_JOIN_SESSIONS_BLOCKS_ROOMS)
+ .mapToTable(Sessions._ID, Tables.SESSIONS)
+ .mapToTable(Sessions.SESSION_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS)
+ .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS)
+ .where(Qualified.SESSIONS_SPEAKERS_SPEAKER_ID + "=?", speakerId);
+ }
+ case VENDORS: {
+ return builder.table(Tables.VENDORS_JOIN_TRACKS)
+ .mapToTable(Vendors._ID, Tables.VENDORS)
+ .mapToTable(Vendors.TRACK_ID, Tables.VENDORS);
+ }
+ case VENDORS_STARRED: {
+ return builder.table(Tables.VENDORS_JOIN_TRACKS)
+ .mapToTable(Vendors._ID, Tables.VENDORS)
+ .mapToTable(Vendors.TRACK_ID, Tables.VENDORS)
+ .where(Vendors.STARRED + "=1");
+ }
+ case VENDORS_SEARCH: {
+ final String query = Vendors.getSearchQuery(uri);
+ return builder.table(Tables.VENDORS_SEARCH_JOIN_VENDORS_TRACKS)
+ .map(Vendors.SEARCH_SNIPPET, Subquery.VENDORS_SNIPPET)
+ .mapToTable(Vendors._ID, Tables.VENDORS)
+ .mapToTable(Vendors.VENDOR_ID, Tables.VENDORS)
+ .mapToTable(Vendors.TRACK_ID, Tables.VENDORS)
+ .where(VendorsSearchColumns.BODY + " MATCH ?", query);
+ }
+ case VENDORS_ID: {
+ final String vendorId = Vendors.getVendorId(uri);
+ return builder.table(Tables.VENDORS_JOIN_TRACKS)
+ .mapToTable(Vendors._ID, Tables.VENDORS)
+ .mapToTable(Vendors.TRACK_ID, Tables.VENDORS)
+ .where(Vendors.VENDOR_ID + "=?", vendorId);
+ }
+ case NOTES: {
+ return builder.table(Tables.NOTES);
+ }
+ case NOTES_ID: {
+ final long noteId = Notes.getNoteId(uri);
+ return builder.table(Tables.NOTES)
+ .where(Notes._ID + "=?", Long.toString(noteId));
+ }
+ default: {
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+ }
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ final int match = sUriMatcher.match(uri);
+ switch (match) {
+ case NOTES_EXPORT: {
+ try {
+ final File notesFile = NotesExporter.writeExportedNotes(getContext());
+ return ParcelFileDescriptor
+ .open(notesFile, ParcelFileDescriptor.MODE_READ_ONLY);
+ } catch (IOException e) {
+ throw new FileNotFoundException("Unable to export notes: " + e.toString());
+ }
+ }
+ default: {
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+ }
+ }
+
+ private interface Subquery {
+ String BLOCK_SESSIONS_COUNT = "(SELECT COUNT(" + Qualified.SESSIONS_SESSION_ID + ") FROM "
+ + Tables.SESSIONS + " WHERE " + Qualified.SESSIONS_BLOCK_ID + "="
+ + Qualified.BLOCKS_BLOCK_ID + ")";
+
+ String BLOCK_CONTAINS_STARRED = "(SELECT MAX(" + Qualified.SESSIONS_STARRED + ") FROM "
+ + Tables.SESSIONS + " WHERE " + Qualified.SESSIONS_BLOCK_ID + "="
+ + Qualified.BLOCKS_BLOCK_ID + ")";
+
+ String TRACK_SESSIONS_COUNT = "(SELECT COUNT(" + Qualified.SESSIONS_TRACKS_SESSION_ID
+ + ") FROM " + Tables.SESSIONS_TRACKS + " WHERE "
+ + Qualified.SESSIONS_TRACKS_TRACK_ID + "=" + Qualified.TRACKS_TRACK_ID + ")";
+
+ String TRACK_VENDORS_COUNT = "(SELECT COUNT(" + Qualified.VENDORS_VENDOR_ID + ") FROM "
+ + Tables.VENDORS + " WHERE " + Qualified.VENDORS_TRACK_ID + "="
+ + Qualified.TRACKS_TRACK_ID + ")";
+
+ String SESSIONS_SNIPPET = "snippet(" + Tables.SESSIONS_SEARCH + ",'{','}','\u2026')";
+ String VENDORS_SNIPPET = "snippet(" + Tables.VENDORS_SEARCH + ",'{','}','\u2026')";
+ }
+
+ /**
+ * {@link ScheduleContract} fields that are fully qualified with a specific
+ * parent {@link Tables}. Used when needed to work around SQL ambiguity.
+ */
+ private interface Qualified {
+ String SESSIONS_SESSION_ID = Tables.SESSIONS + "." + Sessions.SESSION_ID;
+ String SESSIONS_BLOCK_ID = Tables.SESSIONS + "." + Sessions.BLOCK_ID;
+ String SESSIONS_ROOM_ID = Tables.SESSIONS + "." + Sessions.ROOM_ID;
+
+ String SESSIONS_TRACKS_SESSION_ID = Tables.SESSIONS_TRACKS + "."
+ + SessionsTracks.SESSION_ID;
+ String SESSIONS_TRACKS_TRACK_ID = Tables.SESSIONS_TRACKS + "."
+ + SessionsTracks.TRACK_ID;
+
+ String SESSIONS_SPEAKERS_SESSION_ID = Tables.SESSIONS_SPEAKERS + "."
+ + SessionsSpeakers.SESSION_ID;
+ String SESSIONS_SPEAKERS_SPEAKER_ID = Tables.SESSIONS_SPEAKERS + "."
+ + SessionsSpeakers.SPEAKER_ID;
+
+ String VENDORS_VENDOR_ID = Tables.VENDORS + "." + Vendors.VENDOR_ID;
+ String VENDORS_TRACK_ID = Tables.VENDORS + "." + Vendors.TRACK_ID;
+
+ @SuppressWarnings("hiding")
+ String SESSIONS_STARRED = Tables.SESSIONS + "." + Sessions.STARRED;
+
+ String TRACKS_TRACK_ID = Tables.TRACKS + "." + Tracks.TRACK_ID;
+ String BLOCKS_BLOCK_ID = Tables.BLOCKS + "." + Blocks.BLOCK_ID;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/service/.svn/all-wcprops b/src/com/google/android/apps/iosched/service/.svn/all-wcprops
new file mode 100644
index 0000000..aa7cbb7
--- /dev/null
+++ b/src/com/google/android/apps/iosched/service/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 65
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/service
+END
+SyncService.java
+K 25
+svn:wc:ra_dav:version-url
+V 82
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/service/SyncService.java
+END
diff --git a/src/com/google/android/apps/iosched/service/.svn/entries b/src/com/google/android/apps/iosched/service/.svn/entries
new file mode 100644
index 0000000..72a5a4c
--- /dev/null
+++ b/src/com/google/android/apps/iosched/service/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/src/com/google/android/apps/iosched/service
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+SyncService.java
+file
+
+
+
+
+2010-06-03T15:23:15.313622Z
+7e68c494e60024f98bca61d91969df35
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10536
+
diff --git a/src/com/google/android/apps/iosched/service/.svn/text-base/SyncService.java.svn-base b/src/com/google/android/apps/iosched/service/.svn/text-base/SyncService.java.svn-base
new file mode 100644
index 0000000..76685a5
--- /dev/null
+++ b/src/com/google/android/apps/iosched/service/.svn/text-base/SyncService.java.svn-base
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.service;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.io.LocalBlocksHandler;
+import com.google.android.apps.iosched.io.LocalExecutor;
+import com.google.android.apps.iosched.io.LocalRoomsHandler;
+import com.google.android.apps.iosched.io.LocalSearchSuggestHandler;
+import com.google.android.apps.iosched.io.LocalSessionsHandler;
+import com.google.android.apps.iosched.io.LocalTracksHandler;
+import com.google.android.apps.iosched.io.RemoteExecutor;
+import com.google.android.apps.iosched.io.RemoteSessionsHandler;
+import com.google.android.apps.iosched.io.RemoteSpeakersHandler;
+import com.google.android.apps.iosched.io.RemoteVendorsHandler;
+import com.google.android.apps.iosched.io.RemoteWorksheetsHandler;
+import com.google.android.apps.iosched.provider.ScheduleProvider;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.client.HttpClient;
+import org.apache.http.entity.HttpEntityWrapper;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.HttpContext;
+
+import android.app.IntentService;
+import android.app.Service;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * Background {@link Service} that synchronizes data living in
+ * {@link ScheduleProvider}. Reads data from both local {@link Resources} and
+ * from remote sources, such as a spreadsheet.
+ */
+public class SyncService extends IntentService {
+ private static final String TAG = "SyncService";
+
+ public static final String EXTRA_STATUS_RECEIVER =
+ "com.google.android.iosched.extra.STATUS_RECEIVER";
+
+ public static final int STATUS_RUNNING = 0x1;
+ public static final int STATUS_ERROR = 0x2;
+ public static final int STATUS_FINISHED = 0x3;
+
+ private static final int SECOND_IN_MILLIS = (int) DateUtils.SECOND_IN_MILLIS;
+
+ /** Root worksheet feed for online data source */
+ private static final String WORKSHEETS_URL = "http://spreadsheets.google.com"
+ + "/feeds/worksheets/t0bDxnEqbFO4XuYpkA070Nw/public/basic";
+
+ private static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
+ private static final String ENCODING_GZIP = "gzip";
+
+ private static final int VERSION_NONE = 0;
+ private static final int VERSION_CURRENT = 5;
+
+ private LocalExecutor mLocalExecutor;
+ private RemoteExecutor mRemoteExecutor;
+
+ public SyncService() {
+ super(TAG);
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ final HttpClient httpClient = getHttpClient(this);
+ final ContentResolver resolver = getContentResolver();
+
+ mLocalExecutor = new LocalExecutor(getResources(), resolver);
+ mRemoteExecutor = new RemoteExecutor(httpClient, resolver);
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ Log.d(TAG, "onHandleIntent(intent=" + intent.toString() + ")");
+
+ final ResultReceiver receiver = intent.getParcelableExtra(EXTRA_STATUS_RECEIVER);
+ if (receiver != null) receiver.send(STATUS_RUNNING, Bundle.EMPTY);
+
+ final Context context = this;
+ final SharedPreferences prefs = getSharedPreferences(Prefs.IOSCHED_SYNC,
+ Context.MODE_PRIVATE);
+ final int localVersion = prefs.getInt(Prefs.LOCAL_VERSION, VERSION_NONE);
+
+ try {
+ // Bulk of sync work, performed by executing several fetches from
+ // local and online sources.
+
+ final long startLocal = System.currentTimeMillis();
+ final boolean localParse = localVersion < VERSION_CURRENT;
+ Log.d(TAG, "found localVersion=" + localVersion + " and VERSION_CURRENT="
+ + VERSION_CURRENT);
+ if (localParse) {
+ // Load static local data
+ mLocalExecutor.execute(R.xml.blocks, new LocalBlocksHandler());
+ mLocalExecutor.execute(R.xml.rooms, new LocalRoomsHandler());
+ mLocalExecutor.execute(R.xml.tracks, new LocalTracksHandler());
+ mLocalExecutor.execute(R.xml.search_suggest, new LocalSearchSuggestHandler());
+ mLocalExecutor.execute(R.xml.sessions, new LocalSessionsHandler());
+
+ // Parse values from local cache first, since spreadsheet copy
+ // or network might be down.
+ mLocalExecutor.execute(context, "cache-sessions.xml", new RemoteSessionsHandler());
+ mLocalExecutor.execute(context, "cache-speakers.xml", new RemoteSpeakersHandler());
+ mLocalExecutor.execute(context, "cache-vendors.xml", new RemoteVendorsHandler());
+
+ // Save local parsed version
+ prefs.edit().putInt(Prefs.LOCAL_VERSION, VERSION_CURRENT).commit();
+ }
+ Log.d(TAG, "local sync took " + (System.currentTimeMillis() - startLocal) + "ms");
+
+ // Always hit remote spreadsheet for any updates
+ final long startRemote = System.currentTimeMillis();
+ mRemoteExecutor
+ .executeGet(WORKSHEETS_URL, new RemoteWorksheetsHandler(mRemoteExecutor));
+ Log.d(TAG, "remote sync took " + (System.currentTimeMillis() - startRemote) + "ms");
+
+ } catch (Exception e) {
+ Log.e(TAG, "Problem while syncing", e);
+
+ if (receiver != null) {
+ // Pass back error to surface listener
+ final Bundle bundle = new Bundle();
+ bundle.putString(Intent.EXTRA_TEXT, e.toString());
+ receiver.send(STATUS_ERROR, bundle);
+ }
+ }
+
+ // Announce success to any surface listener
+ Log.d(TAG, "sync finished");
+ if (receiver != null) receiver.send(STATUS_FINISHED, Bundle.EMPTY);
+ }
+
+ /**
+ * Generate and return a {@link HttpClient} configured for general use,
+ * including setting an application-specific user-agent string.
+ */
+ public static HttpClient getHttpClient(Context context) {
+ final HttpParams params = new BasicHttpParams();
+
+ // Use generous timeouts for slow mobile networks
+ HttpConnectionParams.setConnectionTimeout(params, 20 * SECOND_IN_MILLIS);
+ HttpConnectionParams.setSoTimeout(params, 20 * SECOND_IN_MILLIS);
+
+ HttpConnectionParams.setSocketBufferSize(params, 8192);
+ HttpProtocolParams.setUserAgent(params, buildUserAgent(context));
+
+ final DefaultHttpClient client = new DefaultHttpClient(params);
+
+ client.addRequestInterceptor(new HttpRequestInterceptor() {
+ public void process(HttpRequest request, HttpContext context) {
+ // Add header to accept gzip content
+ if (!request.containsHeader(HEADER_ACCEPT_ENCODING)) {
+ request.addHeader(HEADER_ACCEPT_ENCODING, ENCODING_GZIP);
+ }
+ }
+ });
+
+ client.addResponseInterceptor(new HttpResponseInterceptor() {
+ public void process(HttpResponse response, HttpContext context) {
+ // Inflate any responses compressed with gzip
+ final HttpEntity entity = response.getEntity();
+ final Header encoding = entity.getContentEncoding();
+ if (encoding != null) {
+ for (HeaderElement element : encoding.getElements()) {
+ if (element.getName().equalsIgnoreCase(ENCODING_GZIP)) {
+ response.setEntity(new InflatingEntity(response.getEntity()));
+ break;
+ }
+ }
+ }
+ }
+ });
+
+ return client;
+ }
+
+ /**
+ * Build and return a user-agent string that can identify this application
+ * to remote servers. Contains the package name and version code.
+ */
+ private static String buildUserAgent(Context context) {
+ try {
+ final PackageManager manager = context.getPackageManager();
+ final PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
+
+ // Some APIs require "(gzip)" in the user-agent string.
+ return info.packageName + "/" + info.versionName
+ + " (" + info.versionCode + ") (gzip)";
+ } catch (NameNotFoundException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Simple {@link HttpEntityWrapper} that inflates the wrapped
+ * {@link HttpEntity} by passing it through {@link GZIPInputStream}.
+ */
+ private static class InflatingEntity extends HttpEntityWrapper {
+ public InflatingEntity(HttpEntity wrapped) {
+ super(wrapped);
+ }
+
+ @Override
+ public InputStream getContent() throws IOException {
+ return new GZIPInputStream(wrappedEntity.getContent());
+ }
+
+ @Override
+ public long getContentLength() {
+ return -1;
+ }
+ }
+
+ private interface Prefs {
+ String IOSCHED_SYNC = "iosched_sync";
+ String LOCAL_VERSION = "local_version";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/service/SyncService.java b/src/com/google/android/apps/iosched/service/SyncService.java
new file mode 100644
index 0000000..76685a5
--- /dev/null
+++ b/src/com/google/android/apps/iosched/service/SyncService.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.service;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.io.LocalBlocksHandler;
+import com.google.android.apps.iosched.io.LocalExecutor;
+import com.google.android.apps.iosched.io.LocalRoomsHandler;
+import com.google.android.apps.iosched.io.LocalSearchSuggestHandler;
+import com.google.android.apps.iosched.io.LocalSessionsHandler;
+import com.google.android.apps.iosched.io.LocalTracksHandler;
+import com.google.android.apps.iosched.io.RemoteExecutor;
+import com.google.android.apps.iosched.io.RemoteSessionsHandler;
+import com.google.android.apps.iosched.io.RemoteSpeakersHandler;
+import com.google.android.apps.iosched.io.RemoteVendorsHandler;
+import com.google.android.apps.iosched.io.RemoteWorksheetsHandler;
+import com.google.android.apps.iosched.provider.ScheduleProvider;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.client.HttpClient;
+import org.apache.http.entity.HttpEntityWrapper;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.HttpContext;
+
+import android.app.IntentService;
+import android.app.Service;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * Background {@link Service} that synchronizes data living in
+ * {@link ScheduleProvider}. Reads data from both local {@link Resources} and
+ * from remote sources, such as a spreadsheet.
+ */
+public class SyncService extends IntentService {
+ private static final String TAG = "SyncService";
+
+ public static final String EXTRA_STATUS_RECEIVER =
+ "com.google.android.iosched.extra.STATUS_RECEIVER";
+
+ public static final int STATUS_RUNNING = 0x1;
+ public static final int STATUS_ERROR = 0x2;
+ public static final int STATUS_FINISHED = 0x3;
+
+ private static final int SECOND_IN_MILLIS = (int) DateUtils.SECOND_IN_MILLIS;
+
+ /** Root worksheet feed for online data source */
+ private static final String WORKSHEETS_URL = "http://spreadsheets.google.com"
+ + "/feeds/worksheets/t0bDxnEqbFO4XuYpkA070Nw/public/basic";
+
+ private static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
+ private static final String ENCODING_GZIP = "gzip";
+
+ private static final int VERSION_NONE = 0;
+ private static final int VERSION_CURRENT = 5;
+
+ private LocalExecutor mLocalExecutor;
+ private RemoteExecutor mRemoteExecutor;
+
+ public SyncService() {
+ super(TAG);
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ final HttpClient httpClient = getHttpClient(this);
+ final ContentResolver resolver = getContentResolver();
+
+ mLocalExecutor = new LocalExecutor(getResources(), resolver);
+ mRemoteExecutor = new RemoteExecutor(httpClient, resolver);
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ Log.d(TAG, "onHandleIntent(intent=" + intent.toString() + ")");
+
+ final ResultReceiver receiver = intent.getParcelableExtra(EXTRA_STATUS_RECEIVER);
+ if (receiver != null) receiver.send(STATUS_RUNNING, Bundle.EMPTY);
+
+ final Context context = this;
+ final SharedPreferences prefs = getSharedPreferences(Prefs.IOSCHED_SYNC,
+ Context.MODE_PRIVATE);
+ final int localVersion = prefs.getInt(Prefs.LOCAL_VERSION, VERSION_NONE);
+
+ try {
+ // Bulk of sync work, performed by executing several fetches from
+ // local and online sources.
+
+ final long startLocal = System.currentTimeMillis();
+ final boolean localParse = localVersion < VERSION_CURRENT;
+ Log.d(TAG, "found localVersion=" + localVersion + " and VERSION_CURRENT="
+ + VERSION_CURRENT);
+ if (localParse) {
+ // Load static local data
+ mLocalExecutor.execute(R.xml.blocks, new LocalBlocksHandler());
+ mLocalExecutor.execute(R.xml.rooms, new LocalRoomsHandler());
+ mLocalExecutor.execute(R.xml.tracks, new LocalTracksHandler());
+ mLocalExecutor.execute(R.xml.search_suggest, new LocalSearchSuggestHandler());
+ mLocalExecutor.execute(R.xml.sessions, new LocalSessionsHandler());
+
+ // Parse values from local cache first, since spreadsheet copy
+ // or network might be down.
+ mLocalExecutor.execute(context, "cache-sessions.xml", new RemoteSessionsHandler());
+ mLocalExecutor.execute(context, "cache-speakers.xml", new RemoteSpeakersHandler());
+ mLocalExecutor.execute(context, "cache-vendors.xml", new RemoteVendorsHandler());
+
+ // Save local parsed version
+ prefs.edit().putInt(Prefs.LOCAL_VERSION, VERSION_CURRENT).commit();
+ }
+ Log.d(TAG, "local sync took " + (System.currentTimeMillis() - startLocal) + "ms");
+
+ // Always hit remote spreadsheet for any updates
+ final long startRemote = System.currentTimeMillis();
+ mRemoteExecutor
+ .executeGet(WORKSHEETS_URL, new RemoteWorksheetsHandler(mRemoteExecutor));
+ Log.d(TAG, "remote sync took " + (System.currentTimeMillis() - startRemote) + "ms");
+
+ } catch (Exception e) {
+ Log.e(TAG, "Problem while syncing", e);
+
+ if (receiver != null) {
+ // Pass back error to surface listener
+ final Bundle bundle = new Bundle();
+ bundle.putString(Intent.EXTRA_TEXT, e.toString());
+ receiver.send(STATUS_ERROR, bundle);
+ }
+ }
+
+ // Announce success to any surface listener
+ Log.d(TAG, "sync finished");
+ if (receiver != null) receiver.send(STATUS_FINISHED, Bundle.EMPTY);
+ }
+
+ /**
+ * Generate and return a {@link HttpClient} configured for general use,
+ * including setting an application-specific user-agent string.
+ */
+ public static HttpClient getHttpClient(Context context) {
+ final HttpParams params = new BasicHttpParams();
+
+ // Use generous timeouts for slow mobile networks
+ HttpConnectionParams.setConnectionTimeout(params, 20 * SECOND_IN_MILLIS);
+ HttpConnectionParams.setSoTimeout(params, 20 * SECOND_IN_MILLIS);
+
+ HttpConnectionParams.setSocketBufferSize(params, 8192);
+ HttpProtocolParams.setUserAgent(params, buildUserAgent(context));
+
+ final DefaultHttpClient client = new DefaultHttpClient(params);
+
+ client.addRequestInterceptor(new HttpRequestInterceptor() {
+ public void process(HttpRequest request, HttpContext context) {
+ // Add header to accept gzip content
+ if (!request.containsHeader(HEADER_ACCEPT_ENCODING)) {
+ request.addHeader(HEADER_ACCEPT_ENCODING, ENCODING_GZIP);
+ }
+ }
+ });
+
+ client.addResponseInterceptor(new HttpResponseInterceptor() {
+ public void process(HttpResponse response, HttpContext context) {
+ // Inflate any responses compressed with gzip
+ final HttpEntity entity = response.getEntity();
+ final Header encoding = entity.getContentEncoding();
+ if (encoding != null) {
+ for (HeaderElement element : encoding.getElements()) {
+ if (element.getName().equalsIgnoreCase(ENCODING_GZIP)) {
+ response.setEntity(new InflatingEntity(response.getEntity()));
+ break;
+ }
+ }
+ }
+ }
+ });
+
+ return client;
+ }
+
+ /**
+ * Build and return a user-agent string that can identify this application
+ * to remote servers. Contains the package name and version code.
+ */
+ private static String buildUserAgent(Context context) {
+ try {
+ final PackageManager manager = context.getPackageManager();
+ final PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
+
+ // Some APIs require "(gzip)" in the user-agent string.
+ return info.packageName + "/" + info.versionName
+ + " (" + info.versionCode + ") (gzip)";
+ } catch (NameNotFoundException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Simple {@link HttpEntityWrapper} that inflates the wrapped
+ * {@link HttpEntity} by passing it through {@link GZIPInputStream}.
+ */
+ private static class InflatingEntity extends HttpEntityWrapper {
+ public InflatingEntity(HttpEntity wrapped) {
+ super(wrapped);
+ }
+
+ @Override
+ public InputStream getContent() throws IOException {
+ return new GZIPInputStream(wrappedEntity.getContent());
+ }
+
+ @Override
+ public long getContentLength() {
+ return -1;
+ }
+ }
+
+ private interface Prefs {
+ String IOSCHED_SYNC = "iosched_sync";
+ String LOCAL_VERSION = "local_version";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/.svn/all-wcprops b/src/com/google/android/apps/iosched/ui/.svn/all-wcprops
new file mode 100644
index 0000000..fb41524
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/all-wcprops
@@ -0,0 +1,89 @@
+K 25
+svn:wc:ra_dav:version-url
+V 60
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui
+END
+StarredActivity.java
+K 25
+svn:wc:ra_dav:version-url
+V 81
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/StarredActivity.java
+END
+SearchActivity.java
+K 25
+svn:wc:ra_dav:version-url
+V 80
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/SearchActivity.java
+END
+SessionDetailActivity.java
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/SessionDetailActivity.java
+END
+SessionsActivity.java
+K 25
+svn:wc:ra_dav:version-url
+V 82
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/SessionsActivity.java
+END
+TrackDetailActivity.java
+K 25
+svn:wc:ra_dav:version-url
+V 85
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/TrackDetailActivity.java
+END
+TracksActivity.java
+K 25
+svn:wc:ra_dav:version-url
+V 80
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/TracksActivity.java
+END
+NotesActivity.java
+K 25
+svn:wc:ra_dav:version-url
+V 79
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/NotesActivity.java
+END
+HomeActivity.java
+K 25
+svn:wc:ra_dav:version-url
+V 78
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/HomeActivity.java
+END
+NoteEditActivity.java
+K 25
+svn:wc:ra_dav:version-url
+V 82
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/NoteEditActivity.java
+END
+ScheduleActivity.java
+K 25
+svn:wc:ra_dav:version-url
+V 82
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/ScheduleActivity.java
+END
+MapActivity.java
+K 25
+svn:wc:ra_dav:version-url
+V 77
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/MapActivity.java
+END
+BlocksActivity.java
+K 25
+svn:wc:ra_dav:version-url
+V 80
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/BlocksActivity.java
+END
+VendorsActivity.java
+K 25
+svn:wc:ra_dav:version-url
+V 81
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/VendorsActivity.java
+END
+VendorDetailActivity.java
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/VendorDetailActivity.java
+END
diff --git a/src/com/google/android/apps/iosched/ui/.svn/entries b/src/com/google/android/apps/iosched/ui/.svn/entries
new file mode 100644
index 0000000..61077a6
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/entries
@@ -0,0 +1,507 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/src/com/google/android/apps/iosched/ui
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+widget
+dir
+
+TracksActivity.java
+file
+
+
+
+
+2010-06-03T15:23:15.477623Z
+faa1afc9ebe0aada4171e90d83c3c8ee
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6359
+
+TrackDetailActivity.java
+file
+
+
+
+
+2010-06-03T15:23:15.477623Z
+d4c41dcbfe73de9847094ee8e7f2d3bc
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6665
+
+HomeActivity.java
+file
+
+
+
+
+2010-06-03T15:23:15.477623Z
+c8a1528f9dcf0d4ab258b9ff5974d10f
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+15240
+
+NotesActivity.java
+file
+
+
+
+
+2010-06-03T15:23:15.477623Z
+6c83a3ae8838ca905fff2c76b6ed5342
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5868
+
+ScheduleActivity.java
+file
+
+
+
+
+2010-06-03T15:23:15.477623Z
+4c7dbc747f088d034508ec5d650eaad5
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6802
+
+StarredActivity.java
+file
+
+
+
+
+2010-06-03T15:23:15.477623Z
+9902fda1d1f4c6d4f518f59a55613462
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3048
+
+SearchActivity.java
+file
+
+
+
+
+2010-06-03T15:23:15.477623Z
+72a442dc2b1d839fc7911a844fa587ae
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3593
+
+SessionsActivity.java
+file
+
+
+
+
+2010-06-03T15:23:15.477623Z
+3a1bcd891f6b4f912b8a535046fbeb13
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+9522
+
+SessionDetailActivity.java
+file
+
+
+
+
+2010-06-03T15:23:15.477623Z
+f84d056f850cbbc450380f06aebf266c
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+24336
+
+NoteEditActivity.java
+file
+
+
+
+
+2010-06-03T15:23:15.477623Z
+df3089b241b31fdb7123ecb7768433a6
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6234
+
+BlocksActivity.java
+file
+
+
+
+
+2010-06-03T15:23:15.481625Z
+1ff57702da028c14ee8864b0a4faf7bd
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8631
+
+MapActivity.java
+file
+
+
+
+
+2010-06-03T15:23:15.477623Z
+56df6ad2b82759d3b3e12256b49f31fa
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7861
+
+VendorDetailActivity.java
+file
+
+
+
+
+2010-06-03T15:23:15.481625Z
+1ed8b09674b9729587789d136edcea4d
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8654
+
+VendorsActivity.java
+file
+
+
+
+
+2010-06-03T15:23:15.481625Z
+309b00f35a2911056854a18430ad215c
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6920
+
diff --git a/src/com/google/android/apps/iosched/ui/.svn/text-base/BlocksActivity.java.svn-base b/src/com/google/android/apps/iosched/ui/.svn/text-base/BlocksActivity.java.svn-base
new file mode 100644
index 0000000..0bc121e
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/text-base/BlocksActivity.java.svn-base
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.ui.widget.BlockView;
+import com.google.android.apps.iosched.ui.widget.BlocksLayout;
+import com.google.android.apps.iosched.util.Maps;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.ParserUtils;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.graphics.Rect;
+import android.graphics.drawable.LayerDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.BaseColumns;
+import android.util.Log;
+import android.view.View;
+import android.widget.ScrollView;
+
+import java.util.HashMap;
+
+/**
+ * {@link Activity} that displays a high-level view of a single day of
+ * {@link Blocks} across the conference. Shows them lined up against a vertical
+ * ruler of times across the day.
+ */
+public class BlocksActivity extends Activity implements AsyncQueryListener, View.OnClickListener {
+ private static final String TAG = "BlocksActivity";
+
+ // TODO: these layouts and views are structured pretty weird, ask someone to
+ // review them and come up with better organization.
+
+ // TODO: show blocks that don't fall into columns at the bottom
+
+ public static final String EXTRA_TIME_START = "com.google.android.iosched.extra.TIME_START";
+ public static final String EXTRA_TIME_END = "com.google.android.iosched.extra.TIME_END";
+
+ private ScrollView mScrollView;
+ private BlocksLayout mBlocks;
+ private View mNowView;
+
+ private long mTimeStart = -1;
+ private long mTimeEnd = -1;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ private static final int DISABLED_BLOCK_ALPHA = 160;
+
+ private static final HashMap sTypeColumnMap = buildTypeColumnMap();
+
+ private static HashMap buildTypeColumnMap() {
+ final HashMap map = Maps.newHashMap();
+ map.put(ParserUtils.BLOCK_TYPE_FOOD, 0);
+ map.put(ParserUtils.BLOCK_TYPE_SESSION, 1);
+ map.put(ParserUtils.BLOCK_TYPE_OFFICE_HOURS, 2);
+ return map;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_blocks_content);
+
+ mTimeStart = getIntent().getLongExtra(EXTRA_TIME_START, mTimeStart);
+ mTimeEnd = getIntent().getLongExtra(EXTRA_TIME_END, mTimeEnd);
+
+ mScrollView = (ScrollView) findViewById(R.id.blocks_scroll);
+ mBlocks = (BlocksLayout) findViewById(R.id.blocks);
+ mNowView = findViewById(R.id.blocks_now);
+
+ mBlocks.setDrawingCacheEnabled(true);
+ mBlocks.setAlwaysDrawnWithCacheEnabled(true);
+
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // Since we build our views manually instead of using an adapter, we
+ // need to manually requery every time launched.
+ final Uri blocksUri = getIntent().getData();
+ mHandler.startQuery(blocksUri, BlocksQuery.PROJECTION, Blocks.DEFAULT_SORT);
+
+ // Start listening for time updates to adjust "now" bar. TIME_TICK is
+ // triggered once per minute, which is how we move the bar over time.
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_TIME_TICK);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ registerReceiver(mReceiver, filter, null, new Handler());
+
+ mNowView.post(new Runnable() {
+ public void run() {
+ updateNowView(true);
+ }
+ });
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ unregisterReceiver(mReceiver);
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ // Clear out any existing sessions before inserting again
+ mBlocks.removeAllBlocks();
+
+ try {
+ while (cursor.moveToNext()) {
+ final String type = cursor.getString(BlocksQuery.BLOCK_TYPE);
+ final Integer column = sTypeColumnMap.get(type);
+ // TODO: place random blocks at bottom of entire layout
+ if (column == null) continue;
+
+ final String blockId = cursor.getString(BlocksQuery.BLOCK_ID);
+ final String title = cursor.getString(BlocksQuery.BLOCK_TITLE);
+ final long start = cursor.getLong(BlocksQuery.BLOCK_START);
+ final long end = cursor.getLong(BlocksQuery.BLOCK_END);
+ final boolean containsStarred = cursor.getInt(BlocksQuery.CONTAINS_STARRED) != 0;
+
+ final BlockView blockView = new BlockView(this, blockId, title, start, end,
+ containsStarred, column);
+
+ final int sessionsCount = cursor.getInt(BlocksQuery.SESSIONS_COUNT);
+ if (sessionsCount > 0) {
+ blockView.setOnClickListener(this);
+ } else {
+ blockView.setFocusable(false);
+ blockView.setEnabled(false);
+ LayerDrawable buttonDrawable = (LayerDrawable) blockView.getBackground();
+ buttonDrawable.getDrawable(0).setAlpha(DISABLED_BLOCK_ALPHA);
+ buttonDrawable.getDrawable(2).setAlpha(DISABLED_BLOCK_ALPHA);
+ }
+
+ mBlocks.addBlock(blockView);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ public void onRefreshClick(View v) {
+ }
+
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** {@inheritDoc} */
+ public void onClick(View view) {
+ if (view instanceof BlockView) {
+ final String blockId = ((BlockView) view).getBlockId();
+ final Uri sessionsUri = Blocks.buildSessionsUri(blockId);
+ startActivity(new Intent(Intent.ACTION_VIEW, sessionsUri));
+ }
+ }
+
+ /**
+ * Update position and visibility of "now" view.
+ */
+ private void updateNowView(boolean forceScroll) {
+ final long now = System.currentTimeMillis();
+
+ final boolean visible = now >= mTimeStart && now <= mTimeEnd;
+ mNowView.setVisibility(visible ? View.VISIBLE : View.GONE);
+
+ if (visible && forceScroll) {
+ // Scroll to show "now" in center
+ final int offset = mScrollView.getHeight() / 2;
+ mNowView.requestRectangleOnScreen(new Rect(0, offset, 0, offset), true);
+ }
+
+ mBlocks.requestLayout();
+ }
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "onReceive time update");
+ updateNowView(false);
+ }
+ };
+
+ private interface BlocksQuery {
+ String[] PROJECTION = {
+ BaseColumns._ID,
+ Blocks.BLOCK_ID,
+ Blocks.BLOCK_TITLE,
+ Blocks.BLOCK_START,
+ Blocks.BLOCK_END,
+ Blocks.BLOCK_TYPE,
+ Blocks.SESSIONS_COUNT,
+ Blocks.CONTAINS_STARRED,
+ };
+
+ int _ID = 0;
+ int BLOCK_ID = 1;
+ int BLOCK_TITLE = 2;
+ int BLOCK_START = 3;
+ int BLOCK_END = 4;
+ int BLOCK_TYPE = 5;
+ int SESSIONS_COUNT = 6;
+ int CONTAINS_STARRED = 7;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/.svn/text-base/HomeActivity.java.svn-base b/src/com/google/android/apps/iosched/ui/.svn/text-base/HomeActivity.java.svn-base
new file mode 100644
index 0000000..cb9c5cf
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/text-base/HomeActivity.java.svn-base
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import static com.google.android.apps.iosched.util.UIUtils.formatSessionSubtitle;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Notes;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.service.SyncService;
+import com.google.android.apps.iosched.util.DetachableResultReceiver;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.Settings;
+import android.text.format.DateUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Front-door {@link Activity} that displays high-level features the schedule
+ * application offers to users.
+ */
+public class HomeActivity extends Activity implements AsyncQueryListener,
+ DetachableResultReceiver.Receiver {
+ private static final String TAG = "HomeActivity";
+
+ /** State held between configuration changes. */
+ private State mState;
+
+ private Handler mMessageHandler = new Handler();
+ private NotifyingAsyncQueryHandler mQueryHandler;
+
+ private TextView mCountdownTextView;
+ private View mNowPlayingLoadingView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_home);
+
+ mNowPlayingLoadingView = findViewById(R.id.now_playing_loading);
+
+ mState = (State) getLastNonConfigurationInstance();
+ final boolean previousState = mState != null;
+
+ if (previousState) {
+ // Start listening for SyncService updates again
+ mState.mReceiver.setReceiver(this);
+ updateRefreshStatus();
+ reloadNowPlaying(true);
+
+ } else {
+ mState = new State();
+ mState.mReceiver.setReceiver(this);
+ onRefreshClick(null);
+ }
+
+ // Set up handler for now playing session query.
+ mQueryHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ }
+
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ // Clear any strong references to this Activity, we'll reattach to
+ // handle events on the other side.
+ mState.mReceiver.clearReceiver();
+ return mState;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // If 'tap here to enable Wifi' was shown, check if the user has enabled Wifi
+ if (mState.mNowPlayingUri != null || mState.mNowPlayingGotoWifi) {
+ reloadNowPlaying(false);
+ } else if (mState.mNoResults) {
+ showNowPlayingNoResults();
+ }
+ }
+
+ /** Handle "refresh" title-bar action. */
+ public void onRefreshClick(View v) {
+ // trigger off background sync
+ final Intent intent = new Intent(Intent.ACTION_SYNC, null, this, SyncService.class);
+ intent.putExtra(SyncService.EXTRA_STATUS_RECEIVER, mState.mReceiver);
+ startService(intent);
+
+ reloadNowPlaying(true);
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** Handle "schedule" action. */
+ public void onScheduleClick(View v) {
+ // Launch overall conference schedule
+ startActivity(new Intent(this, ScheduleActivity.class));
+ }
+
+ /** Handle "map" action. */
+ public void onMapClick(View v) {
+ // Launch map of conference venue
+ startActivity(new Intent(this, MapActivity.class));
+ }
+
+ /** Handle "sessions" action. */
+ public void onSessionsClick(View v) {
+ // Launch sessions clustered by track
+ final Intent intent = new Intent(Intent.ACTION_VIEW, Tracks.CONTENT_URI);
+ intent.putExtra(Intent.EXTRA_TITLE, getString(R.string.title_session_tracks));
+ intent.putExtra(TrackDetailActivity.EXTRA_FOCUS_TAG, TrackDetailActivity.TAG_SESSIONS);
+ startActivity(intent);
+ }
+
+ /** Handle "starred" action. */
+ public void onStarredClick(View v) {
+ // Launch list of sessions user has starred
+ startActivity(new Intent(this, StarredActivity.class));
+ }
+
+ /** Handle "sandbox" or "vendors" action. */
+ public void onVendorsClick(View v) {
+ // Launch list of vendors at conference
+ final Intent intent = new Intent(Intent.ACTION_VIEW, Tracks.CONTENT_URI);
+ intent.putExtra(Intent.EXTRA_TITLE, getString(R.string.title_vendor_tracks));
+ intent.putExtra(TrackDetailActivity.EXTRA_FOCUS_TAG, TrackDetailActivity.TAG_VENDORS);
+ startActivity(intent);
+ }
+
+ /** Handle "my notes" action. */
+ public void onNotesClick(View v) {
+ // Launch list of notes user has taken
+ startActivity(new Intent(Intent.ACTION_VIEW, Notes.CONTENT_URI));
+ }
+
+ /** Handle "now playing" action. */
+ public void onNowPlayingClick(View v) {
+ // Shortcut launch directly to session the user is currently attending
+ // (or to enable wifi if wifi is off)
+ if (mState.mNowPlayingGotoWifi) {
+ Intent wifiSettingsIntent = new Intent();
+ wifiSettingsIntent.setAction(Settings.ACTION_WIFI_SETTINGS);
+ startActivity(wifiSettingsIntent);
+ } else if (mState.mNowPlayingUri != null) {
+ startActivity(new Intent(Intent.ACTION_VIEW, mState.mNowPlayingUri));
+ }
+ }
+
+ /** Handle "now playing > more" action. */
+ public void onNowPlayingMoreClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Sessions.buildSessionsAtDirUri(System.currentTimeMillis()));
+ intent.putExtra(Intent.EXTRA_TITLE, getString(R.string.title_now_playing));
+ startActivity(intent);
+ }
+
+ /** Handle "now playing" logo (Google I/O logo in before/after state) action. */
+ public void onNowPlayingLogoClick(View v) {
+ startActivity(new Intent(Intent.ACTION_VIEW, UIUtils.CONFERENCE_URL));
+ }
+
+ private void reloadNowPlaying(boolean forceRelocate) {
+ mMessageHandler.removeCallbacks(mCountdownRunnable);
+
+ final long currentTimeMillis = System.currentTimeMillis();
+
+ if (mNowPlayingLoadingView == null) // Landscape orientation
+ return;
+
+ ViewGroup homeRoot = (ViewGroup) findViewById(R.id.home_root);
+ View nowPlaying = findViewById(R.id.now_playing);
+ if (nowPlaying != null) {
+ homeRoot.removeView(nowPlaying);
+ nowPlaying = null;
+ }
+
+ // Show Loading... and load the view corresponding to the current state
+ mNowPlayingLoadingView.setVisibility(View.VISIBLE);
+ mState.mNoResults = false;
+ mState.mNowPlayingGotoWifi = false;
+ if (currentTimeMillis < UIUtils.CONFERENCE_START_MILLIS) {
+ nowPlaying = createNowPlayingBeforeView();
+ } else if (currentTimeMillis > UIUtils.CONFERENCE_END_MILLIS) {
+ nowPlaying = createNowPlayingAfterView();
+ } else {
+ nowPlaying = createNowPlayingDuringView(forceRelocate);
+ }
+
+ homeRoot.addView(nowPlaying, new LayoutParams(
+ LayoutParams.FILL_PARENT,
+ (int) getResources().getDimension(R.dimen.now_playing_height)));
+ }
+
+ private View createNowPlayingBeforeView() {
+ // Before conference, show countdown.
+ final View nowPlaying = getLayoutInflater().inflate(R.layout.now_playing_before, null);
+ final TextView nowPlayingTitle = (TextView) nowPlaying.findViewById(
+ R.id.now_playing_title);
+
+ mCountdownTextView = nowPlayingTitle;
+ mMessageHandler.post(mCountdownRunnable);
+ mNowPlayingLoadingView.setVisibility(View.GONE);
+ nowPlaying.setVisibility(View.VISIBLE);
+ return nowPlaying;
+ }
+
+ private View createNowPlayingAfterView() {
+ // After conference, show canned text.
+ final View nowPlaying = getLayoutInflater().inflate(R.layout.now_playing_after, null);
+ mNowPlayingLoadingView.setVisibility(View.GONE);
+ nowPlaying.setVisibility(View.VISIBLE);
+ return nowPlaying;
+ }
+
+ private View createNowPlayingDuringView(boolean forceRelocate) {
+ // Conference in progress, show now playing.
+ final View nowPlaying = getLayoutInflater().inflate(R.layout.now_playing_during, null);
+ nowPlaying.setVisibility(View.GONE);
+ if (!forceRelocate && mState.mNowPlayingUri != null) {
+ mQueryHandler.startQuery(mState.mNowPlayingUri, SessionsQuery.PROJECTION);
+ }
+ return nowPlaying;
+ }
+
+ /**
+ * Event that updates countdown timer. Posts itself again to
+ * {@link #mMessageHandler} to continue updating time.
+ */
+ private Runnable mCountdownRunnable = new Runnable() {
+ public void run() {
+ int remainingSec = (int) Math.max(0,
+ (UIUtils.CONFERENCE_START_MILLIS - System.currentTimeMillis()) / 1000);
+ final boolean conferenceStarted = remainingSec == 0;
+
+ if (conferenceStarted) {
+ // Conference started while in countdown mode, switch modes and
+ // bail on future countdown updates.
+ mMessageHandler.postDelayed(new Runnable() {
+ public void run() {
+ reloadNowPlaying(true);
+ }
+ }, 100);
+ return;
+ }
+
+ final int secs = remainingSec % 86400;
+ final int days = remainingSec / 86400;
+ final String str = getResources().getQuantityString(
+ R.plurals.now_playing_countdown, days, days,
+ DateUtils.formatElapsedTime(secs));
+ mCountdownTextView.setText(str);
+
+ // Repost ourselves to keep updating countdown
+ mMessageHandler.postDelayed(mCountdownRunnable, 1000);
+ }
+ };
+
+ private void showNowPlayingNoResults() {
+ mState.mNoResults = true;
+ runOnUiThread(new Runnable() {
+ public void run() {
+ final View loadingView = findViewById(R.id.now_playing_loading);
+ if (loadingView == null) return;
+
+ loadingView.setVisibility(View.GONE);
+ findViewById(R.id.now_playing).setVisibility(View.VISIBLE);
+ ((TextView) findViewById(R.id.now_playing_title)).setText(
+ R.string.now_playing_no_results);
+ }
+ });
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ try {
+ if (!cursor.moveToFirst()) {
+ showNowPlayingNoResults();
+ return;
+ }
+
+ mState.mNowPlayingUri = Sessions.buildSessionUri(cursor
+ .getString(SessionsQuery.SESSION_ID));
+
+ // Format time block this session occupies
+ final long blockStart = cursor.getLong(SessionsQuery.BLOCK_START);
+ final long blockEnd = cursor.getLong(SessionsQuery.BLOCK_END);
+
+ final String roomName = cursor.getString(SessionsQuery.ROOM_NAME);
+ final String subtitle = formatSessionSubtitle(blockStart, blockEnd, roomName, this);
+
+ findViewById(R.id.now_playing_loading).setVisibility(View.GONE);
+ findViewById(R.id.now_playing).setVisibility(View.VISIBLE);
+ ((TextView) findViewById(R.id.now_playing_title)).setText(cursor
+ .getString(SessionsQuery.TITLE));
+ ((TextView) findViewById(R.id.now_playing_subtitle)).setText(subtitle);
+
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private void updateRefreshStatus() {
+ findViewById(R.id.btn_title_refresh).setVisibility(
+ mState.mSyncing ? View.GONE : View.VISIBLE);
+ findViewById(R.id.title_refresh_progress).setVisibility(
+ mState.mSyncing ? View.VISIBLE : View.GONE);
+ }
+
+ /** {@inheritDoc} */
+ public void onReceiveResult(int resultCode, Bundle resultData) {
+ switch (resultCode) {
+ case SyncService.STATUS_RUNNING: {
+ mState.mSyncing = true;
+ updateRefreshStatus();
+ break;
+ }
+ case SyncService.STATUS_FINISHED: {
+ mState.mSyncing = false;
+ updateRefreshStatus();
+ reloadNowPlaying(true);
+ break;
+ }
+ case SyncService.STATUS_ERROR: {
+ // Error happened down in SyncService, show as toast.
+ mState.mSyncing = false;
+ updateRefreshStatus();
+ final String errorText = getString(R.string.toast_sync_error, resultData
+ .getString(Intent.EXTRA_TEXT));
+ Toast.makeText(HomeActivity.this, errorText, Toast.LENGTH_LONG).show();
+ break;
+ }
+ }
+ }
+
+ /**
+ * State specific to {@link HomeActivity} that is held between configuration
+ * changes. Any strong {@link Activity} references must be
+ * cleared before {@link #onRetainNonConfigurationInstance()}, and this
+ * class should remain {@code static class}.
+ */
+ private static class State {
+ public DetachableResultReceiver mReceiver;
+ public Uri mNowPlayingUri = null;
+ public boolean mNowPlayingGotoWifi = false;
+ public boolean mSyncing = false;
+ public boolean mNoResults = false;
+
+ private State() {
+ mReceiver = new DetachableResultReceiver(new Handler());
+ }
+ }
+
+ private interface SessionsQuery {
+ String[] PROJECTION = {
+ Blocks.BLOCK_START,
+ Blocks.BLOCK_END,
+ Sessions.SESSION_ID,
+ Sessions.TITLE,
+ Rooms.ROOM_NAME,
+ };
+
+ int BLOCK_START = 0;
+ int BLOCK_END = 1;
+ int SESSION_ID = 2;
+ int TITLE = 3;
+ int ROOM_NAME = 4;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/.svn/text-base/MapActivity.java.svn-base b/src/com/google/android/apps/iosched/ui/.svn/text-base/MapActivity.java.svn-base
new file mode 100644
index 0000000..370a0fa
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/text-base/MapActivity.java.svn-base
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.util.ParserUtils;
+import com.google.android.apps.iosched.util.UIUtils;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.webkit.WebChromeClient;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Shows a {@link WebView} with a map of the conference venue.
+ */
+public class MapActivity extends Activity {
+ private static final String TAG = "MapActivity";
+
+ /**
+ * When specified, will automatically point the map to the requested room.
+ */
+ public static final String EXTRA_ROOM = "com.google.android.iosched.extra.ROOM";
+
+ public static final String OFFICE_HOURS_ROOM_ID = "officehours";
+
+ private static final String MAP_JSI_NAME = "MAP_CONTAINER";
+ private static final String MAP_URL = "http://code.google.com/events/io/2010/map/embed.html";
+ private static boolean CLEAR_CACHE_ON_LOAD = false;
+
+ private WebView mWebView;
+ private boolean mLoadingVisible = false;
+ private boolean mMapInitialized = false;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_map);
+
+ ((TextView) findViewById(R.id.title_text)).setText(getTitle());
+
+ showLoading(true);
+ mWebView = (WebView) findViewById(R.id.webview);
+ mWebView.post(new Runnable() {
+ public void run() {
+ // Initialize web view
+ if (CLEAR_CACHE_ON_LOAD) {
+ mWebView.clearCache(true);
+ }
+
+ mWebView.getSettings().setJavaScriptEnabled(true);
+ mWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(false);
+ mWebView.setWebChromeClient(new MapWebChromeClient());
+ mWebView.setWebViewClient(new MapWebViewClient());
+ mWebView.loadUrl(MAP_URL);
+ mWebView.addJavascriptInterface(new MapJsiImpl(), MAP_JSI_NAME);
+ }
+ });
+ }
+
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ public void onRefreshClick(View v) {
+ mWebView.reload();
+ }
+
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) {
+ mWebView.goBack();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ private void showLoading(boolean loading) {
+ if (mLoadingVisible == loading)
+ return;
+
+ View refreshButton = findViewById(R.id.btn_title_refresh);
+ View refreshProgress = findViewById(R.id.title_refresh_progress);
+ refreshButton.setVisibility(loading ? View.GONE : View.VISIBLE);
+ refreshProgress.setVisibility(loading ? View.VISIBLE : View.GONE);
+ mLoadingVisible = loading;
+ }
+
+ private void runJs(String js) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Loading javascript:" + js);
+ }
+ mWebView.loadUrl("javascript:" + js);
+ }
+
+ private class MapWebChromeClient extends WebChromeClient {
+ @Override
+ public void onProgressChanged(WebView view, int newProgress) {
+ showLoading(newProgress < 100);
+ super.onProgressChanged(view, newProgress);
+ }
+
+ public void onConsoleMessage(String message, int lineNumber, String sourceID) {
+ Log.i(TAG, "JS Console message: (" + sourceID + ": " + lineNumber + ") " + message);
+ }
+ }
+
+ private class MapWebViewClient extends WebViewClient {
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ super.onPageFinished(view, url);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Page finished loading: " + url);
+ }
+ }
+
+ @Override
+ public void onReceivedError(WebView view, int errorCode, String description,
+ String failingUrl) {
+ Log.e(TAG, "Error " + errorCode + ": " + description);
+ Toast.makeText(view.getContext(), "Error " + errorCode + ": " + description,
+ Toast.LENGTH_LONG).show();
+ super.onReceivedError(view, errorCode, description, failingUrl);
+ }
+ }
+
+ /**
+ * Helper method to escape JavaScript strings. Useful when passing strings to a WebView
+ * via "javascript:" calls.
+ */
+ private static String escapeJsString(String s) {
+ if (s == null)
+ return "";
+
+ return s.replace("'", "\\'").replace("\"", "\\\"");
+ }
+
+ /**
+ * I/O Conference Map JavaScript interface.
+ */
+ private interface MapJsi {
+ void openContentInfo(String test);
+ void onMapReady();
+ }
+
+ private class MapJsiImpl implements MapJsi {
+ public void openContentInfo(String roomId) {
+ final String possibleTrackId = ParserUtils.translateTrackIdAlias(roomId);
+ if (OFFICE_HOURS_ROOM_ID.equals(roomId)) {
+ // The office hours room was requested.
+ Uri officeHoursUri = Sessions.buildSearchUri("office hours");
+ Intent officeHoursIntent = new Intent(Intent.ACTION_VIEW, officeHoursUri);
+ startActivity(officeHoursIntent);
+ } else if (ParserUtils.LOCAL_TRACK_IDS.contains(possibleTrackId)) {
+ // This is a track; open up the sandbox for the track, since room IDs that are
+ // track IDs are sandbox areas in the map.
+ Uri trackVendorsUri = Tracks.buildTrackUri(possibleTrackId);
+ Intent trackVendorsIntent = new Intent(Intent.ACTION_VIEW, trackVendorsUri);
+ trackVendorsIntent.putExtra(TrackDetailActivity.EXTRA_FOCUS_TAG,
+ TrackDetailActivity.TAG_VENDORS);
+ startActivity(trackVendorsIntent);
+ } else {
+ Uri roomUri = Rooms.buildSessionsDirUri(roomId);
+ Intent roomIntent = new Intent(Intent.ACTION_VIEW, roomUri);
+ startActivity(roomIntent);
+ }
+ }
+
+ public void onMapReady() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onMapReady");
+ }
+
+ String showRoomId = null;
+ if (!mMapInitialized && getIntent().hasExtra(EXTRA_ROOM)) {
+ showRoomId = getIntent().getStringExtra(EXTRA_ROOM);
+ }
+
+ if (showRoomId != null) {
+ runJs("googleIo.showLocationById('" +
+ escapeJsString(showRoomId) + "');");
+ }
+
+ mMapInitialized = true;
+ }
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/.svn/text-base/NoteEditActivity.java.svn-base b/src/com/google/android/apps/iosched/ui/.svn/text-base/NoteEditActivity.java.svn-base
new file mode 100644
index 0000000..62f2d9f
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/text-base/NoteEditActivity.java.svn-base
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Notes;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ContentValues;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+
+/**
+ * Editor for {@link Notes}, handles both {@link Intent#ACTION_INSERT} and
+ * {@link Intent#ACTION_EDIT}.
+ */
+public class NoteEditActivity extends Activity implements AsyncQueryListener {
+
+ private EditText mText;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_note_edit);
+
+ ((TextView) findViewById(R.id.title_text)).setText(getTitle());
+
+ mText = (EditText) findViewById(android.R.id.text1);
+
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+
+ final String action = getIntent().getAction();
+ if (Intent.ACTION_EDIT.equals(action) && savedInstanceState == null) {
+ // Start background query to load current state
+ final Uri noteUri = getIntent().getData();
+ mHandler.startQuery(noteUri, NotesQuery.PROJECTION);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ try {
+ if (!cursor.moveToFirst()) return;
+
+ // Load current note content for editing
+ mText.setText(cursor.getString(NotesQuery.NOTE_CONTENT));
+
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public void onSaveClick(View v) {
+ saveContent();
+ }
+
+ public void onDiscardClick(View v) {
+ final String noteContent = mText.getText().toString();
+ if (TextUtils.isEmpty(noteContent)) {
+ // When empty content, shortcut to discard without confirm step
+ discardContent();
+ } else {
+ showDialog(R.id.dialog_discard_confirm);
+ }
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "refresh" title-bar action. */
+ public void onRefreshClick(View v) {
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case R.id.dialog_discard_confirm: {
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.note_discard_title)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(R.string.note_discard_confirm)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, new DiscardConfirmClickListener())
+ .setCancelable(false)
+ .create();
+ }
+ }
+ return null;
+ }
+
+ private class DiscardConfirmClickListener implements DialogInterface.OnClickListener {
+ public void onClick(DialogInterface dialog, int which) {
+ discardContent();
+ }
+ }
+
+ /**
+ * Persist the contents of into {@link Notes} backend. The actual save is
+ * performed asynchronously, so this method doesn't block.
+ */
+ private void saveContent() {
+ final String noteContent = mText.getText().toString();
+
+ // TODO: consider passing off to startService() to prevent our
+ // process from being killed before note is actually persisted.
+
+ // When empty content, treat as discard
+ if (TextUtils.isEmpty(noteContent)) {
+ discardContent();
+ return;
+ }
+
+ // Always store updated note content
+ final ContentValues values = new ContentValues();
+ values.put(Notes.NOTE_CONTENT, noteContent);
+
+ final String action = getIntent().getAction();
+ if (Intent.ACTION_INSERT.equals(action)) {
+ // Insert also includes current timestamp
+ values.put(Notes.NOTE_TIME, System.currentTimeMillis());
+
+ final Uri notesDirUri = getIntent().getData();
+ mHandler.startInsert(notesDirUri, values);
+ } else if (Intent.ACTION_EDIT.equals(action)) {
+ final Uri noteUri = getIntent().getData();
+ mHandler.startUpdate(noteUri, values);
+ }
+
+ finish();
+ }
+
+ private void discardContent() {
+ final String action = getIntent().getAction();
+ if (Intent.ACTION_EDIT.equals(action)) {
+ final Uri noteUri = getIntent().getData();
+ mHandler.startDelete(noteUri);
+
+ } else if (Intent.ACTION_INSERT.equals(action)) {
+ // Silently discard new note
+
+ }
+
+ finish();
+ }
+
+ /** {@link Notes} query parameters. */
+ private interface NotesQuery {
+ String[] PROJECTION = {
+ Notes.NOTE_TIME,
+ Notes.NOTE_CONTENT,
+ };
+
+ int NOTE_TIME = 0;
+ int NOTE_CONTENT = 1;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/.svn/text-base/NotesActivity.java.svn-base b/src/com/google/android/apps/iosched/ui/.svn/text-base/NotesActivity.java.svn-base
new file mode 100644
index 0000000..385d82b
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/text-base/NotesActivity.java.svn-base
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Notes;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.BaseColumns;
+import android.text.format.DateUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CursorAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * {@link ListActivity} that displays a set of {@link Notes}, as requested
+ * through {@link Intent#getData()}.
+ */
+public class NotesActivity extends ListActivity implements AsyncQueryListener {
+
+ public static final String EXTRA_SHOW_INSERT = "com.google.android.iosched.extra.SHOW_INSERT";
+
+ private NotesAdapter mAdapter;
+
+ private boolean mShowInsert = false;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (!getIntent().hasCategory(Intent.CATEGORY_TAB)) {
+ setContentView(R.layout.activity_notes);
+ ((TextView) findViewById(R.id.title_text)).setText(getTitle());
+
+ } else {
+ setContentView(R.layout.activity_notes_content);
+ }
+
+ mShowInsert = getIntent().getBooleanExtra(EXTRA_SHOW_INSERT, false);
+ if (mShowInsert) {
+ final ListView listView = getListView();
+ final View view = getLayoutInflater().inflate(R.layout.list_item_note_create,
+ listView, false);
+ listView.addHeaderView(view, null, true);
+ }
+
+ mAdapter = new NotesAdapter(this);
+ setListAdapter(mAdapter);
+
+ final Uri notesUri = getIntent().getData();
+
+ // Start background query to load notes
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ mHandler.startQuery(notesUri, NotesQuery.PROJECTION, Notes.DEFAULT_SORT);
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ startManagingCursor(cursor);
+ mAdapter.changeCursor(cursor);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ if (id >= 0) {
+ // Edit an existing note
+ final Uri noteUri = Notes.buildNoteUri(id);
+ startActivity(new Intent(Intent.ACTION_EDIT, noteUri));
+
+ } else {
+ // Insert new note
+ final Uri notesDirUri = getIntent().getData();
+ startActivity(new Intent(Intent.ACTION_INSERT, notesDirUri));
+ }
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "share" title-bar action. */
+ public void onShareClick(View v) {
+ final String shareText = getString(R.string.share_notes);
+
+ final Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType("plain/html");
+ intent.putExtra(Intent.EXTRA_SUBJECT, shareText);
+ intent.putExtra(Intent.EXTRA_TEXT, shareText);
+ intent.putExtra(Intent.EXTRA_STREAM, Notes.CONTENT_EXPORT_URI);
+
+ startActivity(Intent.createChooser(intent, getText(R.string.title_share)));
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /**
+ * {@link CursorAdapter} that renders a {@link NotesQuery}.
+ */
+ private class NotesAdapter extends CursorAdapter {
+ public NotesAdapter(Context context) {
+ super(context, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return getLayoutInflater().inflate(R.layout.list_item_note, parent, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ // TODO: format notes with better layout
+ ((TextView)view.findViewById(R.id.note_content)).setText(cursor
+ .getString(NotesQuery.NOTE_CONTENT));
+
+ // TODO: format using note_before/into/after
+ final long time = cursor.getLong(NotesQuery.NOTE_TIME);
+ final CharSequence relativeTime = DateUtils.getRelativeTimeSpanString(time);
+ ((TextView) view.findViewById(R.id.note_time)).setText(relativeTime);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return mShowInsert ? false : super.isEmpty();
+ }
+ }
+
+ /** {@link Notes} query parameters. */
+ private interface NotesQuery {
+ String[] PROJECTION = {
+ BaseColumns._ID,
+ Notes.NOTE_TIME,
+ Notes.NOTE_CONTENT,
+ };
+
+ int _ID = 0;
+ int NOTE_TIME = 1;
+ int NOTE_CONTENT = 2;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/.svn/text-base/ScheduleActivity.java.svn-base b/src/com/google/android/apps/iosched/ui/.svn/text-base/ScheduleActivity.java.svn-base
new file mode 100644
index 0000000..1180bfa
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/text-base/ScheduleActivity.java.svn-base
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.util.MathUtils;
+import com.google.android.apps.iosched.util.ParserUtils;
+import com.google.android.apps.iosched.util.UIUtils;
+
+import android.app.TabActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.format.DateUtils;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.TabHost;
+import android.widget.TextView;
+
+import java.util.TimeZone;
+
+public class ScheduleActivity extends TabActivity {
+
+ /** Flags used with {@link DateUtils#formatDateRange}. */
+ private static final int TIME_FLAGS = DateUtils.FORMAT_SHOW_DATE
+ | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_WEEKDAY;
+
+ private static final String TAG_TUE = "tuesday";
+ private static final String TAG_WED = "wednesday";
+ private static final String TAG_THU = "thursday";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_schedule);
+
+ ((TextView) findViewById(R.id.title_text)).setText(getTitle());
+
+ final long wedStart = ParserUtils.parseTime("2010-05-19T00:00:00.000-07:00");
+ final long thuStart = ParserUtils.parseTime("2010-05-20T00:00:00.000-07:00");
+
+ setupBlocksTab(TAG_WED, wedStart);
+ setupBlocksTab(TAG_THU, thuStart);
+
+ final long now = System.currentTimeMillis();
+ if (now >= thuStart) {
+ getTabHost().setCurrentTabByTag(TAG_THU);
+ } else {
+ // Otherwise start with first day
+ getTabHost().setCurrentTabByTag(TAG_WED);
+ }
+ }
+
+ public static class FlingableTabHost extends TabHost {
+ GestureDetector mGestureDetector;
+
+ Animation mRightInAnimation;
+ Animation mRightOutAnimation;
+ Animation mLeftInAnimation;
+ Animation mLeftOutAnimation;
+
+ public FlingableTabHost(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mRightInAnimation = AnimationUtils.loadAnimation(context, R.anim.slide_right_in);
+ mRightOutAnimation = AnimationUtils.loadAnimation(context, R.anim.slide_right_out);
+ mLeftInAnimation = AnimationUtils.loadAnimation(context, R.anim.slide_left_in);
+ mLeftOutAnimation = AnimationUtils.loadAnimation(context, R.anim.slide_left_out);
+
+ final int minScaledFlingVelocity = ViewConfiguration.get(context)
+ .getScaledMinimumFlingVelocity() * 10; // 10 = fudge by experimentation
+
+ mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ int tabCount = getTabWidget().getTabCount();
+ int currentTab = getCurrentTab();
+ if (Math.abs(velocityX) > minScaledFlingVelocity &&
+ Math.abs(velocityY) < minScaledFlingVelocity) {
+
+ final boolean right = velocityX < 0;
+ final int newTab = MathUtils.constrain(currentTab + (right ? 1 : -1),
+ 0, tabCount - 1);
+ if (newTab != currentTab) {
+ // Somewhat hacky, depends on current implementation of TabHost:
+ // http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;
+ // f=core/java/android/widget/TabHost.java
+ View currentView = getCurrentView();
+ setCurrentTab(newTab);
+ View newView = getCurrentView();
+
+ newView.startAnimation(right ? mRightInAnimation : mLeftInAnimation);
+ currentView.startAnimation(
+ right ? mRightOutAnimation : mLeftOutAnimation);
+ }
+ }
+ return super.onFling(e1, e2, velocityX, velocityY);
+ }
+ });
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mGestureDetector.onTouchEvent(ev)) {
+ return true;
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+ }
+
+ private void setupBlocksTab(String tag, long startMillis) {
+ final TabHost host = getTabHost();
+
+ final long endMillis = startMillis + DateUtils.DAY_IN_MILLIS;
+ final Uri blocksBetweenDirUri = Blocks.buildBlocksBetweenDirUri(startMillis, endMillis);
+
+ final Intent intent = new Intent(Intent.ACTION_VIEW, blocksBetweenDirUri);
+ intent.addCategory(Intent.CATEGORY_TAB);
+
+ intent.putExtra(BlocksActivity.EXTRA_TIME_START, startMillis);
+ intent.putExtra(BlocksActivity.EXTRA_TIME_END, endMillis);
+
+ TimeZone.setDefault(UIUtils.CONFERENCE_TIME_ZONE);
+ final String label = DateUtils.formatDateTime(this, startMillis, TIME_FLAGS);
+ host.addTab(host.newTabSpec(tag)
+ .setIndicator(buildIndicator(label))
+ .setContent(intent));
+ }
+
+ /**
+ * Build a {@link View} to be used as a tab indicator, setting the requested
+ * string resource as its label.
+ */
+ private View buildIndicator(String text) {
+ final TextView indicator = (TextView) getLayoutInflater().inflate(R.layout.tab_indicator,
+ getTabWidget(), false);
+ indicator.setText(text);
+ return indicator;
+ }
+
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/.svn/text-base/SearchActivity.java.svn-base b/src/com/google/android/apps/iosched/ui/.svn/text-base/SearchActivity.java.svn-base
new file mode 100644
index 0000000..6f9fc0f
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/text-base/SearchActivity.java.svn-base
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.util.UIUtils;
+
+import android.app.SearchManager;
+import android.app.TabActivity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TabHost;
+import android.widget.TextView;
+
+public class SearchActivity extends TabActivity {
+
+ public static final String TAG_SESSIONS = "sessions";
+ public static final String TAG_VENDORS = "vendors";
+
+ private String mQuery;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_search);
+
+ onNewIntent(getIntent());
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ mQuery = intent.getStringExtra(SearchManager.QUERY);
+ final CharSequence title = getString(R.string.title_search_query, mQuery);
+
+ setTitle(title);
+ ((TextView) findViewById(R.id.title_text)).setText(title);
+
+ final TabHost host = getTabHost();
+ host.setCurrentTab(0);
+ host.clearAllTabs();
+
+ setupSessionsTab();
+ setupVendorsTab();
+ }
+
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** Build and add "sessions" tab. */
+ private void setupSessionsTab() {
+ final TabHost host = getTabHost();
+
+ final Uri sessionsUri = Sessions.buildSearchUri(mQuery);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, sessionsUri);
+ intent.addCategory(Intent.CATEGORY_TAB);
+
+ // Sessions content comes from reused activity
+ host.addTab(host.newTabSpec(TAG_SESSIONS)
+ .setIndicator(buildIndicator(R.string.search_sessions))
+ .setContent(intent));
+ }
+
+ /** Build and add "vendors" tab. */
+ private void setupVendorsTab() {
+ final TabHost host = getTabHost();
+
+ final Uri vendorsUri = Vendors.buildSearchUri(mQuery);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, vendorsUri);
+ intent.addCategory(Intent.CATEGORY_TAB);
+
+ // Vendors content comes from reused activity
+ host.addTab(host.newTabSpec(TAG_VENDORS)
+ .setIndicator(buildIndicator(R.string.search_vendors))
+ .setContent(intent));
+ }
+
+ /**
+ * Build a {@link View} to be used as a tab indicator, setting the requested
+ * string resource as its label.
+ */
+ private View buildIndicator(int textRes) {
+ final TextView indicator = (TextView) getLayoutInflater().inflate(R.layout.tab_indicator,
+ getTabWidget(), false);
+ indicator.setText(textRes);
+ return indicator;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/.svn/text-base/SessionDetailActivity.java.svn-base b/src/com/google/android/apps/iosched/ui/.svn/text-base/SessionDetailActivity.java.svn-base
new file mode 100644
index 0000000..1ca8a98
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/text-base/SessionDetailActivity.java.svn-base
@@ -0,0 +1,635 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Speakers;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.service.SyncService;
+import com.google.android.apps.iosched.util.FractionalTouchDelegate;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONObject;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.TabActivity;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.Cursor;
+import android.graphics.RectF;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CompoundButton;
+import android.widget.TabHost;
+import android.widget.TextView;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+
+/**
+ * {@link Activity} that displays details about a specific
+ * {@link Sessions#SESSION_ID}, as requested through {@link Intent#getData()}.
+ */
+public class SessionDetailActivity extends TabActivity implements AsyncQueryListener,
+ OnCheckedChangeListener {
+ private static final String TAG = "SessionDetailActivity";
+
+ /**
+ * Since {@link Sessions} can belong to multiple {@link Tracks}, the parent
+ * {@link Activity} can send this extra specifying a {@link Tracks}
+ * {@link Uri} that should be used for coloring the title-bar.
+ */
+ public static final String EXTRA_TRACK = "com.google.android.iosched.extra.TRACK";
+
+ private static final String MODERATOR_PACKAGE = "com.google.android.apps.moderator";
+
+ private static final String TAG_SUMMARY = "summary";
+ private static final String TAG_NOTES = "notes";
+ private static final String TAG_MODERATOR = "moderator";
+
+ private String mSessionId;
+ private Uri mSessionUri;
+
+ private String mTitleString;
+ private String mHashtag;
+ private String mRoomId;
+
+ private TextView mTitle;
+ private TextView mSubtitle;
+ private CompoundButton mStarred;
+
+ private TextView mAbstract;
+ private TextView mRequirements;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ private boolean mSessionCursor = false;
+ private boolean mSpeakersCursor = false;
+ private boolean mHasSummaryContent = false;
+
+ private Uri mModeratorUri;
+ private Uri mWaveUri;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_session_detail);
+
+ mTitle = (TextView) findViewById(R.id.session_title);
+ mSubtitle = (TextView) findViewById(R.id.session_subtitle);
+ mStarred = (CompoundButton) findViewById(R.id.star_button);
+
+ mStarred.setFocusable(true);
+ mStarred.setClickable(true);
+
+ // Larger target triggers star toggle
+ final View starParent = findViewById(R.id.list_item_session);
+ FractionalTouchDelegate.setupDelegate(starParent, mStarred, new RectF(0.6f, 0f, 1f, 0.8f));
+
+ mAbstract = (TextView) findViewById(R.id.session_abstract);
+ mRequirements = (TextView) findViewById(R.id.session_requirements);
+
+ final Intent intent = getIntent();
+ mSessionUri = intent.getData();
+ mSessionId = Sessions.getSessionId(mSessionUri);
+
+ setupSummaryTab();
+ setupNotesTab();
+
+ // Start background queries to load session and track details
+ final Uri trackUri = resolveTrackUri(intent);
+ final Uri speakersUri = Sessions.buildSpeakersDirUri(mSessionId);
+
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ mHandler.startQuery(SessionsQuery._TOKEN, mSessionUri, SessionsQuery.PROJECTION);
+ mHandler.startQuery(TracksQuery._TOKEN, trackUri, TracksQuery.PROJECTION);
+ mHandler.startQuery(SpeakersQuery._TOKEN, speakersUri, SpeakersQuery.PROJECTION);
+ }
+
+ /** Build and add "summary" tab. */
+ private void setupSummaryTab() {
+ final TabHost host = getTabHost();
+
+ // Summary content comes from existing layout
+ host.addTab(host.newTabSpec(TAG_SUMMARY)
+ .setIndicator(buildIndicator(R.string.session_summary))
+ .setContent(R.id.tab_session_summary));
+ }
+
+ /** Build and add "notes" tab. */
+ private void setupNotesTab() {
+ final TabHost host = getTabHost();
+
+ final Uri notesUri = Sessions.buildNotesDirUri(mSessionId);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, notesUri);
+ intent.addCategory(Intent.CATEGORY_TAB);
+ intent.putExtra(NotesActivity.EXTRA_SHOW_INSERT, true);
+
+ // Notes content comes from reused activity
+ host.addTab(host.newTabSpec(TAG_NOTES)
+ .setIndicator(buildIndicator(R.string.session_notes))
+ .setContent(intent));
+ }
+
+ /** Build and add "moderator" tab. */
+ private void setupModeratorTab(Cursor sessionsCursor) {
+ final TabHost host = getTabHost();
+
+ // Insert Moderator when available
+ final View moderatorBlock = findViewById(R.id.moderator_block);
+ final String moderatorLink = sessionsCursor.getString(SessionsQuery.MODERATOR_LINK);
+ final boolean validModerator = !TextUtils.isEmpty(moderatorLink);
+ if (validModerator) {
+ mModeratorUri = Uri.parse(moderatorLink);
+
+ // Set link, but handle clicks manually
+ final TextView textView = (TextView) findViewById(R.id.moderator_link);
+ textView.setText(mModeratorUri.toString());
+ textView.setMovementMethod(null);
+ textView.setClickable(true);
+ textView.setFocusable(true);
+
+ // Start background fetch of moderator status
+ startModeratorStatusFetch(moderatorLink);
+
+ moderatorBlock.setVisibility(View.VISIBLE);
+ } else {
+ moderatorBlock.setVisibility(View.GONE);
+ }
+
+ // Insert Wave when available
+ final View waveBlock = findViewById(R.id.wave_block);
+ final String waveLink = sessionsCursor.getString(SessionsQuery.WAVE_LINK);
+ final boolean validWave = !TextUtils.isEmpty(waveLink);
+ if (validWave) {
+ // Rewrite incoming Wave URL to punch through user-agent check
+ mWaveUri = Uri.parse(waveLink).buildUpon()
+ .appendQueryParameter("nouacheck", "1").build();
+
+ // Set link, but handle clicks manually
+ final TextView textView = (TextView) findViewById(R.id.wave_link);
+ textView.setText(mWaveUri.toString());
+ textView.setMovementMethod(null);
+ textView.setClickable(true);
+ textView.setFocusable(true);
+
+ waveBlock.setVisibility(View.VISIBLE);
+ } else {
+ waveBlock.setVisibility(View.GONE);
+ }
+
+ if (validModerator || validWave) {
+ // Moderator content comes from existing layout
+ host.addTab(host.newTabSpec(TAG_MODERATOR)
+ .setIndicator(buildIndicator(R.string.session_interact))
+ .setContent(R.id.tab_session_moderator));
+ }
+ }
+
+ /** Handle Moderator link. */
+ public void onModeratorClick(View v) {
+ boolean appPresent = false;
+ try {
+ final PackageManager pm = getPackageManager();
+ final ApplicationInfo ai = pm.getApplicationInfo(MODERATOR_PACKAGE, 0);
+ if (ai != null) appPresent = true;
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Problem searching for moderator: " + e.toString());
+ }
+
+ if (appPresent) {
+ // Directly launch intent when already installed
+ startModerator();
+ } else {
+ // Otherwise suggest installing app from Market
+ showDialog(R.id.dialog_moderator);
+ }
+ }
+
+ /** Handle Wave link. */
+ public void onWaveClick(View v) {
+ showDialog(R.id.dialog_wave);
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case R.id.dialog_moderator: {
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.dialog_moderator_title)
+ .setIcon(android.R.drawable.ic_dialog_info)
+ .setMessage(R.string.dialog_moderator_message)
+ .setNegativeButton(R.string.dialog_moderator_market,
+ new ModeratorMarketClickListener())
+ .setPositiveButton(R.string.dialog_moderator_web,
+ new ModeratorStartClickListener())
+ .setCancelable(true)
+ .create();
+ }
+ case R.id.dialog_wave: {
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.dialog_wave_title)
+ .setIcon(android.R.drawable.ic_dialog_info)
+ .setMessage(R.string.dialog_wave_message)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, new WaveConfirmClickListener())
+ .setCancelable(true)
+ .create();
+ }
+ }
+ return null;
+ }
+
+ private class ModeratorStartClickListener implements DialogInterface.OnClickListener {
+ public void onClick(DialogInterface dialog, int which) {
+ startModerator();
+ }
+ }
+
+ private class ModeratorMarketClickListener implements DialogInterface.OnClickListener {
+ public void onClick(DialogInterface dialog, int which) {
+ final Uri marketUri = Uri.parse("http://market.android.com/search?q=pname:"
+ + MODERATOR_PACKAGE);
+ startActivity(new Intent(Intent.ACTION_VIEW, marketUri));
+ }
+ }
+
+ private class WaveConfirmClickListener implements DialogInterface.OnClickListener {
+ public void onClick(DialogInterface dialog, int which) {
+ startWave();
+ }
+ }
+
+ private void startModerator() {
+ startActivity(new Intent(Intent.ACTION_VIEW, mModeratorUri));
+ }
+
+ private void startWave() {
+ startActivity(new Intent(Intent.ACTION_VIEW, mWaveUri));
+ }
+
+ /**
+ * Build a {@link View} to be used as a tab indicator, setting the requested
+ * string resource as its label.
+ */
+ private View buildIndicator(int textRes) {
+ final TextView indicator = (TextView) getLayoutInflater().inflate(R.layout.tab_indicator,
+ getTabWidget(), false);
+ indicator.setText(textRes);
+ return indicator;
+ }
+
+ /**
+ * Derive {@link Tracks#CONTENT_ITEM_TYPE} {@link Uri} based on incoming
+ * {@link Intent}, using {@link #EXTRA_TRACK} when set.
+ */
+ private Uri resolveTrackUri(Intent intent) {
+ final Uri trackUri = intent.getParcelableExtra(EXTRA_TRACK);
+ if (trackUri != null) {
+ return trackUri;
+ } else {
+ return Sessions.buildTracksDirUri(mSessionId);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ if (token == SessionsQuery._TOKEN) {
+ onSessionQueryComplete(cursor);
+ } else if (token == TracksQuery._TOKEN) {
+ onTrackQueryComplete(cursor);
+ } else if (token == SpeakersQuery._TOKEN) {
+ onSpeakersQueryComplete(cursor);
+ } else {
+ cursor.close();
+ }
+ }
+
+ /** Handle {@link SessionsQuery} {@link Cursor}. */
+ private void onSessionQueryComplete(Cursor cursor) {
+ try {
+ mSessionCursor = true;
+ if (!cursor.moveToFirst()) return;
+
+ // Format time block this session occupies
+ final long blockStart = cursor.getLong(SessionsQuery.BLOCK_START);
+ final long blockEnd = cursor.getLong(SessionsQuery.BLOCK_END);
+ final String roomName = cursor.getString(SessionsQuery.ROOM_NAME);
+ final String subtitle = UIUtils.formatSessionSubtitle(blockStart,
+ blockEnd, roomName, this);
+
+ mTitleString = cursor.getString(SessionsQuery.TITLE);
+ mTitle.setText(mTitleString);
+ mSubtitle.setText(subtitle);
+
+ mHashtag = cursor.getString(SessionsQuery.HASHTAG);
+ if (TextUtils.isEmpty(mHashtag)) mHashtag = "";
+
+ mRoomId = cursor.getString(SessionsQuery.ROOM_ID);
+
+ // Unregister around setting checked state to avoid triggering
+ // listener since change isn't user generated.
+ mStarred.setOnCheckedChangeListener(null);
+ mStarred.setChecked(cursor.getInt(SessionsQuery.STARRED) != 0);
+ mStarred.setOnCheckedChangeListener(this);
+
+ final String sessionAbstract = cursor.getString(SessionsQuery.ABSTRACT);
+ if (!TextUtils.isEmpty(sessionAbstract)) {
+ UIUtils.setTextMaybeHtml(mAbstract, sessionAbstract);
+ mAbstract.setVisibility(View.VISIBLE);
+ mHasSummaryContent = true;
+ } else {
+ mAbstract.setVisibility(View.GONE);
+ }
+
+ final View requirementsBlock = findViewById(R.id.session_requirements_block);
+ final String sessionRequirements = cursor.getString(SessionsQuery.REQUIREMENTS);
+ if (!TextUtils.isEmpty(sessionRequirements)) {
+ UIUtils.setTextMaybeHtml(mRequirements, sessionRequirements);
+ requirementsBlock.setVisibility(View.VISIBLE);
+ mHasSummaryContent = true;
+ } else {
+ requirementsBlock.setVisibility(View.GONE);
+ }
+
+ setupModeratorTab(cursor);
+
+ // Show empty message when all data is loaded, and nothing to show
+ if (mSpeakersCursor && !mHasSummaryContent) {
+ findViewById(android.R.id.empty).setVisibility(View.VISIBLE);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /** Handle {@link TracksQuery} {@link Cursor}. */
+ private void onTrackQueryComplete(Cursor cursor) {
+ try {
+ if (!cursor.moveToFirst()) return;
+
+ // Use found track to build title-bar
+ ((TextView) findViewById(R.id.title_text)).setText(cursor
+ .getString(TracksQuery.TRACK_NAME));
+ UIUtils.setTitleBarColor(findViewById(R.id.title_container),
+ cursor.getInt(TracksQuery.TRACK_COLOR));
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private void onSpeakersQueryComplete(Cursor cursor) {
+ try {
+ mSpeakersCursor = true;
+
+ // TODO: remove any existing speakers from layout, since this cursor
+ // might be from a data change notification.
+ final ViewGroup speakersGroup = (ViewGroup) findViewById(R.id.session_speakers_block);
+ final LayoutInflater inflater = getLayoutInflater();
+
+ boolean hasSpeakers = false;
+
+ while (cursor.moveToNext()) {
+ final String speakerName = cursor.getString(SpeakersQuery.SPEAKER_NAME);
+ final String speakerCompany = cursor.getString(SpeakersQuery.SPEAKER_COMPANY);
+ if (TextUtils.isEmpty(speakerName)) continue;
+
+ final View speakerView = inflater.inflate(R.layout.speaker_detail,
+ speakersGroup, false);
+
+ final String speaker = getString(R.string.speaker_template, speakerName,
+ speakerCompany);
+ ((TextView) speakerView.findViewById(R.id.speaker_header)).setText(speaker);
+
+ final String speakerAbstract = cursor.getString(SpeakersQuery.SPEAKER_ABSTRACT);
+ final TextView abstractView = (TextView) speakerView
+ .findViewById(R.id.speaker_abstract);
+ UIUtils.setTextMaybeHtml(abstractView, speakerAbstract);
+
+ speakersGroup.addView(speakerView);
+ hasSpeakers = true;
+ mHasSummaryContent = true;
+ }
+
+ speakersGroup.setVisibility(hasSpeakers ? View.VISIBLE : View.GONE);
+
+ // Show empty message when all data is loaded, and nothing to show
+ if (mSessionCursor && !mHasSummaryContent) {
+ findViewById(android.R.id.empty).setVisibility(View.VISIBLE);
+ }
+
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "share" title-bar action. */
+ public void onShareClick(View v) {
+ // TODO: consider bringing in shortlink to session
+ final String shareString = getString(R.string.share_template, mTitleString, mHashtag);
+
+ final Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType("text/plain");
+ intent.putExtra(Intent.EXTRA_TEXT, shareString);
+
+ startActivity(Intent.createChooser(intent, getText(R.string.title_share)));
+ }
+
+ /** Handle "map" title-bar action. */
+ public void onMapClick(View v) {
+ final Intent intent = new Intent(this, MapActivity.class);
+ if (mRoomId != null && mRoomId.startsWith("officehours")) {
+ intent.putExtra(MapActivity.EXTRA_ROOM, MapActivity.OFFICE_HOURS_ROOM_ID);
+ } else {
+ intent.putExtra(MapActivity.EXTRA_ROOM, mRoomId);
+ }
+ startActivity(intent);
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** Handle toggling of starred checkbox. */
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ final ContentValues values = new ContentValues();
+ values.put(Vendors.STARRED, isChecked ? 1 : 0);
+ mHandler.startUpdate(mSessionUri, values);
+ }
+
+ private static HttpClient sHttpClient;
+
+ private static synchronized HttpClient getHttpClient(Context context) {
+ if (sHttpClient == null) {
+ sHttpClient = SyncService.getHttpClient(context);
+ }
+ return sHttpClient;
+ }
+
+ private void startModeratorStatusFetch(String moderatorLink) {
+ try {
+ // Extract series and topic from moderator link
+ // NOTE: this makes some ugly assumptions that the "t=" parameter
+ // occurs at the end of the URL.
+ final String clause = moderatorLink.substring(moderatorLink.indexOf("t=") + 2);
+
+ final int dotIndex = clause.indexOf('.');
+ final int seriesId = Integer.parseInt(clause.substring(0, dotIndex), 16);
+ final int topicId = Integer.parseInt(clause.substring(dotIndex + 1), 16);
+
+ // Kick off background request to find current status
+ final String remoteUrl = "http://www.googleapis.com/moderator/v1/series/" + seriesId
+ + "/topics/" + topicId;
+ new ModeratorStatusTask().execute(remoteUrl);
+
+ } catch (Exception e) {
+ Log.w(TAG, "Problem while parsing Moderator URL", e);
+ }
+ }
+
+ private class ModeratorStatusTask extends AsyncTask {
+ @Override
+ protected String doInBackground(String... params) {
+ final String param = params[0];
+
+ try {
+ final Context context = SessionDetailActivity.this;
+ final HttpClient httpClient = getHttpClient(context);
+ final HttpResponse resp = httpClient.execute(new HttpGet(param));
+ final HttpEntity entity = resp.getEntity();
+
+ final int statusCode = resp.getStatusLine().getStatusCode();
+ if (statusCode != HttpStatus.SC_OK || entity == null) return null;
+
+ final String respString = EntityUtils.toString(entity);
+ final JSONObject respJson = new JSONObject(respString);
+
+ final JSONObject data = respJson.getJSONObject("data");
+ final JSONObject counters = respJson.getJSONObject("counters");
+
+ final int questions = counters.getInt("submissions");
+ final int votes = counters.getInt("noteVotes") + counters.getInt("plusVotes")
+ + counters.getInt("minusVotes");
+
+ return getString(R.string.session_moderator_status, questions, votes);
+ } catch(Exception e) {
+ Log.w(TAG, "Problem while loading Moderator status: " + e.toString());
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(String result) {
+ final TextView status = (TextView) findViewById(R.id.moderator_status);
+ if (result == null) {
+ status.setVisibility(View.GONE);
+ } else {
+ status.setVisibility(View.VISIBLE);
+ status.setText(result);
+ }
+ }
+ }
+
+ /** {@link Sessions} query parameters. */
+ private interface SessionsQuery {
+ int _TOKEN = 0x1;
+
+ String[] PROJECTION = {
+ Blocks.BLOCK_START,
+ Blocks.BLOCK_END,
+ Sessions.TYPE,
+ Sessions.TITLE,
+ Sessions.ABSTRACT,
+ Sessions.REQUIREMENTS,
+ Sessions.STARRED,
+ Sessions.MODERATOR_URL,
+ Sessions.WAVE_URL,
+ Sessions.ROOM_ID,
+ Sessions.HASHTAG,
+ Rooms.ROOM_NAME,
+ };
+
+ int BLOCK_START = 0;
+ int BLOCK_END = 1;
+ int TYPE = 2;
+ int TITLE = 3;
+ int ABSTRACT = 4;
+ int REQUIREMENTS = 5;
+ int STARRED = 6;
+ int MODERATOR_LINK = 7;
+ int WAVE_LINK = 8;
+ int ROOM_ID = 9;
+ int HASHTAG = 10;
+ int ROOM_NAME = 11;
+ }
+
+ /** {@link Tracks} query parameters. */
+ private interface TracksQuery {
+ int _TOKEN = 0x2;
+
+ String[] PROJECTION = {
+ Tracks.TRACK_NAME,
+ Tracks.TRACK_COLOR,
+ };
+
+ int TRACK_NAME = 0;
+ int TRACK_COLOR = 1;
+ }
+
+ private interface SpeakersQuery {
+ int _TOKEN = 0x3;
+
+ String[] PROJECTION = {
+ Speakers.SPEAKER_NAME,
+ Speakers.SPEAKER_COMPANY,
+ Speakers.SPEAKER_ABSTRACT,
+ };
+
+ int SPEAKER_NAME = 0;
+ int SPEAKER_COMPANY = 1;
+ int SPEAKER_ABSTRACT = 2;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/.svn/text-base/SessionsActivity.java.svn-base b/src/com/google/android/apps/iosched/ui/.svn/text-base/SessionsActivity.java.svn-base
new file mode 100644
index 0000000..2004114
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/text-base/SessionsActivity.java.svn-base
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import static com.google.android.apps.iosched.util.UIUtils.buildStyledSnippet;
+import static com.google.android.apps.iosched.util.UIUtils.formatSessionSubtitle;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.provider.BaseColumns;
+import android.text.Spannable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CursorAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * {@link ListActivity} that displays a set of {@link Sessions}, as requested
+ * through {@link Intent#getData()}.
+ */
+public class SessionsActivity extends ListActivity implements AsyncQueryListener {
+
+ private Uri mTrackUri;
+ private CursorAdapter mAdapter;
+
+ private NotifyingAsyncQueryHandler mHandler;
+ private Handler mMessageQueueHandler = new Handler();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (!getIntent().hasCategory(Intent.CATEGORY_TAB)) {
+ setContentView(R.layout.activity_sessions);
+
+ final String customTitle = getIntent().getStringExtra(Intent.EXTRA_TITLE);
+ ((TextView) findViewById(R.id.title_text)).setText(
+ customTitle != null ? customTitle : getTitle());
+
+ } else {
+ setContentView(R.layout.activity_sessions_content);
+ }
+
+ final Intent intent = getIntent();
+ final Uri sessionsUri = intent.getData();
+
+ String[] projection;
+ if (!Sessions.isSearchUri(sessionsUri)) {
+ mAdapter = new SessionsAdapter(this);
+ projection = SessionsQuery.PROJECTION;
+
+ } else {
+ mAdapter = new SearchAdapter(this);
+ projection = SearchQuery.PROJECTION;
+ }
+
+ setListAdapter(mAdapter);
+
+ // If caller launched us with specific track hint, pass it along when
+ // launching session details.
+ mTrackUri = intent.getParcelableExtra(SessionDetailActivity.EXTRA_TRACK);
+
+ // Start background query to load sessions
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ mHandler.startQuery(sessionsUri, projection, Sessions.DEFAULT_SORT);
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ startManagingCursor(cursor);
+ mAdapter.changeCursor(cursor);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mMessageQueueHandler.post(mRefreshSessionsRunnable);
+ }
+
+ @Override
+ protected void onPause() {
+ mMessageQueueHandler.removeCallbacks(mRefreshSessionsRunnable);
+ super.onPause();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ // Launch viewer for specific session, passing along any track knowledge
+ // that should influence the title-bar.
+ final Cursor cursor = (Cursor)mAdapter.getItem(position);
+ final String sessionId = cursor.getString(cursor.getColumnIndex(Sessions.SESSION_ID));
+ final Uri sessionUri = Sessions.buildSessionUri(sessionId);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, sessionUri);
+ intent.putExtra(SessionDetailActivity.EXTRA_TRACK, mTrackUri);
+ startActivity(intent);
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /**
+ * {@link CursorAdapter} that renders a {@link SessionsQuery}.
+ */
+ private class SessionsAdapter extends CursorAdapter {
+ public SessionsAdapter(Context context) {
+ super(context, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return getLayoutInflater().inflate(R.layout.list_item_session, parent, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ final TextView titleView = (TextView) view.findViewById(R.id.session_title);
+ final TextView subtitleView = (TextView) view.findViewById(R.id.session_subtitle);
+ final CheckBox starButton = (CheckBox) view.findViewById(R.id.star_button);
+
+ titleView.setText(cursor.getString(SessionsQuery.TITLE));
+
+ // Format time block this session occupies
+ final long blockStart = cursor.getLong(SessionsQuery.BLOCK_START);
+ final long blockEnd = cursor.getLong(SessionsQuery.BLOCK_END);
+ final String roomName = cursor.getString(SessionsQuery.ROOM_NAME);
+ final String subtitle = formatSessionSubtitle(blockStart, blockEnd, roomName, context);
+
+ subtitleView.setText(subtitle);
+
+ final boolean starred = cursor.getInt(SessionsQuery.STARRED) != 0;
+ starButton.setVisibility(starred ? View.VISIBLE : View.INVISIBLE);
+ starButton.setChecked(starred);
+
+ // Possibly indicate that the session has occurred in the past.
+ UIUtils.setSessionTitleColor(blockStart, blockEnd, titleView, subtitleView);
+ }
+ }
+
+ /**
+ * {@link CursorAdapter} that renders a {@link SearchQuery}.
+ */
+ private class SearchAdapter extends CursorAdapter {
+ public SearchAdapter(Context context) {
+ super(context, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return getLayoutInflater().inflate(R.layout.list_item_session, parent, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ ((TextView) view.findViewById(R.id.session_title)).setText(cursor
+ .getString(SearchQuery.TITLE));
+
+ final String snippet = cursor.getString(SearchQuery.SEARCH_SNIPPET);
+ final Spannable styledSnippet = buildStyledSnippet(snippet);
+ ((TextView) view.findViewById(R.id.session_subtitle)).setText(styledSnippet);
+
+ final boolean starred = cursor.getInt(SearchQuery.STARRED) != 0;
+ final CheckBox starButton = (CheckBox) view.findViewById(R.id.star_button);
+ starButton.setVisibility(starred ? View.VISIBLE : View.INVISIBLE);
+ starButton.setChecked(starred);
+ }
+ }
+
+ private Runnable mRefreshSessionsRunnable = new Runnable() {
+ public void run() {
+ if (mAdapter != null) {
+ // This is used to refresh session title colors.
+ mAdapter.notifyDataSetChanged();
+ }
+
+ // Check again on the next quarter hour, with some padding to account for network
+ // time differences.
+ long nextQuarterHour = (SystemClock.uptimeMillis() / 900000 + 1) * 900000 + 5000;
+ mMessageQueueHandler.postAtTime(mRefreshSessionsRunnable, nextQuarterHour);
+ }
+ };
+
+ /** {@link Sessions} query parameters. */
+ private interface SessionsQuery {
+ String[] PROJECTION = {
+ BaseColumns._ID,
+ Sessions.SESSION_ID,
+ Sessions.TITLE,
+ Sessions.STARRED,
+ Blocks.BLOCK_START,
+ Blocks.BLOCK_END,
+ Rooms.ROOM_NAME,
+ };
+
+ int _ID = 0;
+ int SESSION_ID = 1;
+ int TITLE = 2;
+ int STARRED = 3;
+ int BLOCK_START = 4;
+ int BLOCK_END = 5;
+ int ROOM_NAME = 6;
+ }
+
+ /** {@link Sessions} search query parameters. */
+ private interface SearchQuery {
+ String[] PROJECTION = {
+ BaseColumns._ID,
+ Sessions.SESSION_ID,
+ Sessions.TITLE,
+ Sessions.SEARCH_SNIPPET,
+ Sessions.STARRED,
+ };
+
+ int _ID = 0;
+ int SESSION_ID = 1;
+ int TITLE = 2;
+ int SEARCH_SNIPPET = 3;
+ int STARRED = 4;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/.svn/text-base/StarredActivity.java.svn-base b/src/com/google/android/apps/iosched/ui/.svn/text-base/StarredActivity.java.svn-base
new file mode 100644
index 0000000..4c33fa2
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/text-base/StarredActivity.java.svn-base
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.util.UIUtils;
+
+import android.app.TabActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TabHost;
+import android.widget.TextView;
+
+public class StarredActivity extends TabActivity {
+
+ public static final String TAG_SESSIONS = "sessions";
+ public static final String TAG_VENDORS = "vendors";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_starred);
+
+ ((TextView) findViewById(R.id.title_text)).setText(getTitle());
+
+ setupSessionsTab();
+ setupVendorsTab();
+ }
+
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** Build and add "sessions" tab. */
+ private void setupSessionsTab() {
+ final TabHost host = getTabHost();
+
+ final Intent intent = new Intent(Intent.ACTION_VIEW, Sessions.CONTENT_STARRED_URI);
+ intent.addCategory(Intent.CATEGORY_TAB);
+
+ // Sessions content comes from reused activity
+ host.addTab(host.newTabSpec(TAG_SESSIONS)
+ .setIndicator(buildIndicator(R.string.starred_sessions))
+ .setContent(intent));
+ }
+
+ /** Build and add "vendors" tab. */
+ private void setupVendorsTab() {
+ final TabHost host = getTabHost();
+
+ final Intent intent = new Intent(Intent.ACTION_VIEW, Vendors.CONTENT_STARRED_URI);
+ intent.addCategory(Intent.CATEGORY_TAB);
+
+ // Vendors content comes from reused activity
+ host.addTab(host.newTabSpec(TAG_VENDORS)
+ .setIndicator(buildIndicator(R.string.starred_vendors))
+ .setContent(intent));
+ }
+
+ /**
+ * Build a {@link View} to be used as a tab indicator, setting the requested
+ * string resource as its label.
+ */
+ private View buildIndicator(int textRes) {
+ final TextView indicator = (TextView) getLayoutInflater().inflate(R.layout.tab_indicator,
+ getTabWidget(), false);
+ indicator.setText(textRes);
+ return indicator;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/.svn/text-base/TrackDetailActivity.java.svn-base b/src/com/google/android/apps/iosched/ui/.svn/text-base/TrackDetailActivity.java.svn-base
new file mode 100644
index 0000000..80d61bc
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/text-base/TrackDetailActivity.java.svn-base
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.Sets;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.Activity;
+import android.app.TabActivity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TabHost;
+import android.widget.TextView;
+
+import java.util.HashSet;
+
+/**
+ * {@link Activity} that displays details about a specific
+ * {@link Tracks#TRACK_ID}, as requested through {@link Intent#getData()}.
+ */
+public class TrackDetailActivity extends TabActivity implements AsyncQueryListener {
+
+ public static final String EXTRA_FOCUS_TAG = "com.google.android.iosched.extra.FOCUS_TAG";
+
+ public static final String TAG_SUMMARY = "summary";
+ public static final String TAG_SESSIONS = "sessions";
+ public static final String TAG_VENDORS = "vendors";
+
+ /**
+ * List of specific {@link Tracks#TRACK_ID} that never have {@link Vendors},
+ * which is used to hide the {@link #TAG_VENDORS} tab instead of a query.
+ */
+ // TODO: instead of using this static list, query for Tracks.VENDORS_COUNT
+ private static HashSet sTracksWithoutVendors = Sets.newHashSet("firesidechats",
+ "techtalks");
+
+ private Uri mTrackUri;
+ private String mTrackId;
+
+ private TextView mTitleText;
+ private TextView mTrackAbstract;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_track_details);
+
+ mTrackUri = getIntent().getData();
+ mTrackId = Tracks.getTrackId(mTrackUri);
+
+ mTitleText = (TextView) findViewById(R.id.title_text);
+ mTrackAbstract = (TextView) findViewById(R.id.track_abstract);
+
+ setupSummaryTab();
+ setupSessionsTab();
+
+ // Only add vendors tab when applicable
+ final boolean hasVendors = !sTracksWithoutVendors.contains(mTrackId);
+ if (hasVendors) setupVendorsTab();
+
+ // Show specific focus tag when requested, otherwise default
+ String focusTag = getIntent().getStringExtra(EXTRA_FOCUS_TAG);
+ if (focusTag == null) focusTag = TAG_SESSIONS;
+
+ getTabHost().setCurrentTabByTag(focusTag);
+
+ // Start background query to load track details
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ mHandler.startQuery(mTrackUri, TracksQuery.PROJECTION);
+ }
+
+ /** Build and add "summary" tab. */
+ private void setupSummaryTab() {
+ final TabHost host = getTabHost();
+
+ // Summary content comes from existing layout
+ host.addTab(host.newTabSpec(TAG_SUMMARY)
+ .setIndicator(buildIndicator(R.string.track_summary))
+ .setContent(R.id.tab_track_summary));
+ }
+
+ /** Build and add "sessions" tab. */
+ private void setupSessionsTab() {
+ final TabHost host = getTabHost();
+
+ final Uri trackUri = getIntent().getData();
+ final String trackId = Tracks.getTrackId(trackUri);
+ final Uri sessionsUri = Tracks.buildSessionsUri(trackId);
+
+ final Intent intent = new Intent(Intent.ACTION_VIEW, sessionsUri);
+ intent.putExtra(SessionDetailActivity.EXTRA_TRACK, trackUri);
+ intent.addCategory(Intent.CATEGORY_TAB);
+
+ // Sessions content comes from reused activity
+ host.addTab(host.newTabSpec(TAG_SESSIONS)
+ .setIndicator(buildIndicator(R.string.track_sessions))
+ .setContent(intent));
+ }
+
+ /** Build and add "vendors" tab. */
+ private void setupVendorsTab() {
+ final TabHost host = getTabHost();
+ final Uri vendorsUri = Tracks.buildVendorsUri(mTrackId);
+
+ final Intent intent = new Intent(Intent.ACTION_VIEW, vendorsUri);
+ intent.putExtra(SessionDetailActivity.EXTRA_TRACK, mTrackUri);
+ intent.addCategory(Intent.CATEGORY_TAB);
+
+ // Vendors content comes from reused activity
+ host.addTab(host.newTabSpec(TAG_VENDORS)
+ .setIndicator(buildIndicator(R.string.track_vendors))
+ .setContent(intent));
+ }
+
+ /**
+ * Build a {@link View} to be used as a tab indicator, setting the requested
+ * string resource as its label.
+ */
+ private View buildIndicator(int textRes) {
+ final TextView indicator = (TextView) getLayoutInflater().inflate(R.layout.tab_indicator,
+ getTabWidget(), false);
+ indicator.setText(textRes);
+ return indicator;
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ try {
+ if (!cursor.moveToFirst()) return;
+ mTitleText.setText(cursor.getString(TracksQuery.TRACK_NAME));
+ mTrackAbstract.setText(cursor.getString(TracksQuery.TRACK_ABSTRACT));
+ UIUtils.setTitleBarColor(findViewById(R.id.title_container),
+ cursor.getInt(TracksQuery.TRACK_COLOR));
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** {@link Tracks} query parameters. */
+ private interface TracksQuery {
+ String[] PROJECTION = {
+ Tracks.TRACK_NAME,
+ Tracks.TRACK_COLOR,
+ Tracks.TRACK_ABSTRACT,
+ };
+
+ int TRACK_NAME = 0;
+ int TRACK_COLOR = 1;
+ int TRACK_ABSTRACT = 2;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/.svn/text-base/TracksActivity.java.svn-base b/src/com/google/android/apps/iosched/ui/.svn/text-base/TracksActivity.java.svn-base
new file mode 100644
index 0000000..333e051
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/text-base/TracksActivity.java.svn-base
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.LayerDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.BaseColumns;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CursorAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * {@link ListActivity} that displays a set of {@link Tracks}, as requested
+ * through {@link Intent#getData()}.
+ */
+public class TracksActivity extends ListActivity implements AsyncQueryListener {
+
+ private String mFocusTag;
+ private TracksAdapter mAdapter;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_tracks);
+
+ final String customTitle = getIntent().getStringExtra(Intent.EXTRA_TITLE);
+ ((TextView) findViewById(R.id.title_text)).setText(
+ customTitle != null ? customTitle : getTitle());
+
+ mAdapter = new TracksAdapter(this);
+ setListAdapter(mAdapter);
+
+ final Intent intent = getIntent();
+ final Uri tracksUri = intent.getData();
+
+ mFocusTag = intent.getStringExtra(TrackDetailActivity.EXTRA_FOCUS_TAG);
+
+ // Filter our tracks query to only include those with valid results
+ String[] projection = TracksQuery.PROJECTION;
+ String selection = null;
+ if (TrackDetailActivity.TAG_SESSIONS.equals(mFocusTag)) {
+ // Only show tracks with at least one session
+ projection = TracksQuery.PROJECTION_WITH_SESSIONS_COUNT;
+ selection = Tracks.SESSIONS_COUNT + ">0";
+
+ } else if (TrackDetailActivity.TAG_VENDORS.equals(mFocusTag)) {
+ // Only show tracks with at least one vendor
+ projection = TracksQuery.PROJECTION_WITH_VENDORS_COUNT;
+ selection = Tracks.VENDORS_COUNT + ">0";
+ }
+
+ // Start background query to load tracks
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ mHandler.startQuery(tracksUri, projection, selection, null, Tracks.DEFAULT_SORT);
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ startManagingCursor(cursor);
+ mAdapter.changeCursor(cursor);
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "refresh" title-bar action. */
+ public void onRefreshClick(View v) {
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ // Launch viewer for specific track
+ final Cursor cursor = (Cursor)mAdapter.getItem(position);
+ final String trackId = cursor.getString(TracksQuery.TRACK_ID);
+ final Uri trackUri = Tracks.buildTrackUri(trackId);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, trackUri);
+
+ // Pass along any request for focus on a specific tag
+ intent.putExtra(TrackDetailActivity.EXTRA_FOCUS_TAG, mFocusTag);
+
+ startActivity(intent);
+ }
+
+ /**
+ * {@link CursorAdapter} that renders a {@link TracksQuery}.
+ */
+ private class TracksAdapter extends CursorAdapter {
+ public TracksAdapter(Context context) {
+ super(context, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return getLayoutInflater().inflate(R.layout.list_item_track, parent, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ final TextView textView = (TextView) view.findViewById(android.R.id.text1);
+ textView.setText(cursor.getString(TracksQuery.TRACK_NAME));
+
+ // Assign track color to visible block
+ final View iconView = view.findViewById(android.R.id.icon1);
+ LayerDrawable iconDrawable = (LayerDrawable) iconView.getBackground();
+ iconDrawable.getDrawable(0).setColorFilter(
+ cursor.getInt(TracksQuery.TRACK_COLOR), PorterDuff.Mode.SRC_ATOP);
+ }
+ }
+
+ /** {@link Tracks} query parameters. */
+ private interface TracksQuery {
+ String[] PROJECTION = {
+ BaseColumns._ID,
+ Tracks.TRACK_ID,
+ Tracks.TRACK_NAME,
+ Tracks.TRACK_COLOR,
+ };
+
+ String[] PROJECTION_WITH_SESSIONS_COUNT = {
+ BaseColumns._ID,
+ Tracks.TRACK_ID,
+ Tracks.TRACK_NAME,
+ Tracks.TRACK_COLOR,
+ Tracks.SESSIONS_COUNT,
+ };
+
+ String[] PROJECTION_WITH_VENDORS_COUNT = {
+ BaseColumns._ID,
+ Tracks.TRACK_ID,
+ Tracks.TRACK_NAME,
+ Tracks.TRACK_COLOR,
+ Tracks.VENDORS_COUNT,
+ };
+
+ int _ID = 0;
+ int TRACK_ID = 1;
+ int TRACK_NAME = 2;
+ int TRACK_COLOR = 3;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/.svn/text-base/VendorDetailActivity.java.svn-base b/src/com/google/android/apps/iosched/ui/.svn/text-base/VendorDetailActivity.java.svn-base
new file mode 100644
index 0000000..62512f9
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/text-base/VendorDetailActivity.java.svn-base
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.service.SyncService;
+import com.google.android.apps.iosched.util.FractionalTouchDelegate;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.ParserUtils;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.util.EntityUtils;
+
+import android.app.Activity;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.RectF;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+
+/**
+ * {@link Activity} that displays details about a specific
+ * {@link Vendors#VENDOR_ID}, as requested through {@link Intent#getData()}.
+ */
+public class VendorDetailActivity extends Activity implements AsyncQueryListener,
+ OnCheckedChangeListener {
+ private static final String TAG = "VendorDetailActivity";
+
+ private Uri mVendorUri;
+
+ private String mTrackId;
+
+ private TextView mName;
+ private TextView mLocation;
+ private CompoundButton mStarred;
+
+ private ImageView mLogo;
+ private TextView mUrl;
+ private TextView mDesc;
+ private TextView mProductDesc;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ public static final String EXTRA_TRACK = "com.google.android.iosched.extra.TRACK";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_vendor_detail);
+
+ mName = (TextView) findViewById(R.id.vendor_name);
+ mLocation = (TextView) findViewById(R.id.vendor_location);
+ mStarred = (CompoundButton) findViewById(R.id.star_button);
+
+ mStarred.setFocusable(true);
+ mStarred.setClickable(true);
+
+ // Larger target triggers star toggle
+ final View starParent = findViewById(R.id.list_item_vendor);
+ FractionalTouchDelegate.setupDelegate(starParent, mStarred, new RectF(0.6f, 0f, 1f, 0.8f));
+
+ mLogo = (ImageView) findViewById(R.id.vendor_logo);
+ mUrl = (TextView) findViewById(R.id.vendor_url);
+ mDesc = (TextView) findViewById(R.id.vendor_desc);
+ mProductDesc = (TextView) findViewById(R.id.vendor_product_desc);
+
+ final Intent intent = getIntent();
+ mVendorUri = intent.getData();
+
+ // Start background query to load vendor details
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ mHandler.startQuery(mVendorUri, VendorsQuery.PROJECTION);
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ try {
+ if (!cursor.moveToFirst()) return;
+
+ mName.setText(cursor.getString(VendorsQuery.NAME));
+ mLocation.setText(cursor.getString(VendorsQuery.LOCATION));
+
+ // Unregister around setting checked state to avoid triggering
+ // listener since change isn't user generated.
+ mStarred.setOnCheckedChangeListener(null);
+ mStarred.setChecked(cursor.getInt(VendorsQuery.STARRED) != 0);
+ mStarred.setOnCheckedChangeListener(this);
+
+ // Start background fetch to load vendor logo
+ final String logoUrl = cursor.getString(VendorsQuery.LOGO_URL);
+ new VendorLogoTask().execute(logoUrl);
+
+ mUrl.setText(cursor.getString(VendorsQuery.URL));
+ mDesc.setText(cursor.getString(VendorsQuery.DESC));
+ mProductDesc.setText(cursor.getString(VendorsQuery.PRODUCT_DESC));
+
+ mTrackId = cursor.getString(VendorsQuery.TRACK_ID);
+
+ // Assign track details when found
+ // TODO: handle vendors not attached to track
+ ((TextView) findViewById(R.id.title_text)).setText(cursor
+ .getString(VendorsQuery.TRACK_NAME));
+ UIUtils.setTitleBarColor(findViewById(R.id.title_container),
+ cursor.getInt(VendorsQuery.TRACK_COLOR));
+
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** Handle "map" title-bar action. */
+ public void onMapClick(View v) {
+ // The room ID for the sandbox, in the map, is just the track ID
+ final Intent intent = new Intent(this, MapActivity.class);
+ intent.putExtra(MapActivity.EXTRA_ROOM, ParserUtils.translateTrackIdAliasInverse(mTrackId));
+ startActivity(intent);
+ }
+
+ /** Handle toggling of starred checkbox. */
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ final ContentValues values = new ContentValues();
+ values.put(Vendors.STARRED, isChecked ? 1 : 0);
+ mHandler.startUpdate(mVendorUri, values);
+ }
+
+ private static HttpClient sHttpClient;
+
+ private static synchronized HttpClient getHttpClient(Context context) {
+ if (sHttpClient == null) {
+ sHttpClient = SyncService.getHttpClient(context);
+ }
+ return sHttpClient;
+ }
+
+ private class VendorLogoTask extends AsyncTask {
+ @Override
+ protected Bitmap doInBackground(String... params) {
+ final String param = params[0];
+
+ try {
+ final Context context = VendorDetailActivity.this;
+ final HttpClient httpClient = getHttpClient(context);
+ final HttpResponse resp = httpClient.execute(new HttpGet(param));
+ final HttpEntity entity = resp.getEntity();
+
+ final int statusCode = resp.getStatusLine().getStatusCode();
+ if (statusCode != HttpStatus.SC_OK || entity == null) return null;
+
+ final byte[] respBytes = EntityUtils.toByteArray(entity);
+ return BitmapFactory.decodeByteArray(respBytes, 0, respBytes.length);
+
+ } catch(Exception e) {
+ Log.w(TAG, "Problem while loading vendor logo: " + e.toString());
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap result) {
+ if (result == null) {
+ mLogo.setVisibility(View.GONE);
+ } else {
+ mLogo.setVisibility(View.VISIBLE);
+ mLogo.setImageBitmap(result);
+ }
+ }
+ }
+
+ /** {@link Vendors} query parameters. */
+ private interface VendorsQuery {
+ String[] PROJECTION = {
+ Vendors.NAME,
+ Vendors.LOCATION,
+ Vendors.DESC,
+ Vendors.URL,
+ Vendors.PRODUCT_DESC,
+ Vendors.LOGO_URL,
+ Vendors.STARRED,
+ Vendors.TRACK_ID,
+ Tracks.TRACK_NAME,
+ Tracks.TRACK_COLOR,
+ };
+
+ int NAME = 0;
+ int LOCATION = 1;
+ int DESC = 2;
+ int URL = 3;
+ int PRODUCT_DESC = 4;
+ int LOGO_URL = 5;
+ int STARRED = 6;
+ int TRACK_ID = 7;
+ int TRACK_NAME = 8;
+ int TRACK_COLOR = 9;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/.svn/text-base/VendorsActivity.java.svn-base b/src/com/google/android/apps/iosched/ui/.svn/text-base/VendorsActivity.java.svn-base
new file mode 100644
index 0000000..8c94d51
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/.svn/text-base/VendorsActivity.java.svn-base
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import static com.google.android.apps.iosched.util.UIUtils.buildStyledSnippet;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.BaseColumns;
+import android.text.Spannable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CursorAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * {@link ListActivity} that displays a set of {@link Vendors}, as requested
+ * through {@link Intent#getData()}.
+ */
+public class VendorsActivity extends ListActivity implements AsyncQueryListener {
+
+ private CursorAdapter mAdapter;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (!getIntent().hasCategory(Intent.CATEGORY_TAB)) {
+ setContentView(R.layout.activity_vendors);
+ ((TextView) findViewById(R.id.title_text)).setText(getTitle());
+
+ } else {
+ setContentView(R.layout.activity_vendors_content);
+ }
+
+ final Uri vendorsUri = getIntent().getData();
+
+ String[] projection;
+ if (!Vendors.isSearchUri(vendorsUri)) {
+ mAdapter = new VendorsAdapter(this);
+ projection = VendorsQuery.PROJECTION;
+
+ } else {
+ mAdapter = new SearchAdapter(this);
+ projection = SearchQuery.PROJECTION;
+ }
+
+ setListAdapter(mAdapter);
+
+ // Start background query to load vendors
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ mHandler.startQuery(vendorsUri, projection, Vendors.DEFAULT_SORT);
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ startManagingCursor(cursor);
+ mAdapter.changeCursor(cursor);
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ // Launch viewer for specific vendor
+ final Cursor cursor = (Cursor)mAdapter.getItem(position);
+ final String vendorId = cursor.getString(VendorsQuery.VENDOR_ID);
+ final Uri vendorUri = Vendors.buildVendorUri(vendorId);
+ startActivity(new Intent(Intent.ACTION_VIEW, vendorUri));
+ }
+
+ /**
+ * {@link CursorAdapter} that renders a {@link VendorsQuery}.
+ */
+ private class VendorsAdapter extends CursorAdapter {
+ public VendorsAdapter(Context context) {
+ super(context, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return getLayoutInflater().inflate(R.layout.list_item_vendor, parent, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ ((TextView) view.findViewById(R.id.vendor_name)).setText(cursor
+ .getString(VendorsQuery.NAME));
+ ((TextView) view.findViewById(R.id.vendor_location)).setText(cursor
+ .getString(VendorsQuery.LOCATION));
+
+ final boolean starred = cursor.getInt(VendorsQuery.STARRED) != 0;
+ final CheckBox starButton = (CheckBox) view.findViewById(R.id.star_button);
+ starButton.setVisibility(starred ? View.VISIBLE : View.INVISIBLE);
+ starButton.setChecked(starred);
+ }
+ }
+
+ /**
+ * {@link CursorAdapter} that renders a {@link SearchQuery}.
+ */
+ private class SearchAdapter extends CursorAdapter {
+ public SearchAdapter(Context context) {
+ super(context, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return getLayoutInflater().inflate(R.layout.list_item_vendor, parent, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ ((TextView) view.findViewById(R.id.vendor_name)).setText(cursor
+ .getString(SearchQuery.NAME));
+
+ final String snippet = cursor.getString(SearchQuery.SEARCH_SNIPPET);
+ final Spannable styledSnippet = buildStyledSnippet(snippet);
+ ((TextView) view.findViewById(R.id.vendor_location)).setText(styledSnippet);
+
+ final boolean starred = cursor.getInt(VendorsQuery.STARRED) != 0;
+ final CheckBox starButton = (CheckBox) view.findViewById(R.id.star_button);
+ starButton.setVisibility(starred ? View.VISIBLE : View.INVISIBLE);
+ starButton.setChecked(starred);
+ }
+ }
+
+ /** {@link Vendors} query parameters. */
+ private interface VendorsQuery {
+ String[] PROJECTION = {
+ BaseColumns._ID,
+ Vendors.VENDOR_ID,
+ Vendors.NAME,
+ Vendors.LOCATION,
+ Vendors.STARRED,
+ };
+
+ int _ID = 0;
+ int VENDOR_ID = 1;
+ int NAME = 2;
+ int LOCATION = 3;
+ int STARRED = 4;
+ }
+
+ /** {@link Vendors} search query parameters. */
+ private interface SearchQuery {
+ String[] PROJECTION = {
+ BaseColumns._ID,
+ Vendors.VENDOR_ID,
+ Vendors.NAME,
+ Vendors.SEARCH_SNIPPET,
+ Vendors.STARRED,
+ };
+
+ int _ID = 0;
+ int VENDOR_ID = 1;
+ int NAME = 2;
+ int SEARCH_SNIPPET = 3;
+ int STARRED = 4;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/BlocksActivity.java b/src/com/google/android/apps/iosched/ui/BlocksActivity.java
new file mode 100644
index 0000000..0bc121e
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/BlocksActivity.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.ui.widget.BlockView;
+import com.google.android.apps.iosched.ui.widget.BlocksLayout;
+import com.google.android.apps.iosched.util.Maps;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.ParserUtils;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.graphics.Rect;
+import android.graphics.drawable.LayerDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.BaseColumns;
+import android.util.Log;
+import android.view.View;
+import android.widget.ScrollView;
+
+import java.util.HashMap;
+
+/**
+ * {@link Activity} that displays a high-level view of a single day of
+ * {@link Blocks} across the conference. Shows them lined up against a vertical
+ * ruler of times across the day.
+ */
+public class BlocksActivity extends Activity implements AsyncQueryListener, View.OnClickListener {
+ private static final String TAG = "BlocksActivity";
+
+ // TODO: these layouts and views are structured pretty weird, ask someone to
+ // review them and come up with better organization.
+
+ // TODO: show blocks that don't fall into columns at the bottom
+
+ public static final String EXTRA_TIME_START = "com.google.android.iosched.extra.TIME_START";
+ public static final String EXTRA_TIME_END = "com.google.android.iosched.extra.TIME_END";
+
+ private ScrollView mScrollView;
+ private BlocksLayout mBlocks;
+ private View mNowView;
+
+ private long mTimeStart = -1;
+ private long mTimeEnd = -1;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ private static final int DISABLED_BLOCK_ALPHA = 160;
+
+ private static final HashMap sTypeColumnMap = buildTypeColumnMap();
+
+ private static HashMap buildTypeColumnMap() {
+ final HashMap map = Maps.newHashMap();
+ map.put(ParserUtils.BLOCK_TYPE_FOOD, 0);
+ map.put(ParserUtils.BLOCK_TYPE_SESSION, 1);
+ map.put(ParserUtils.BLOCK_TYPE_OFFICE_HOURS, 2);
+ return map;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_blocks_content);
+
+ mTimeStart = getIntent().getLongExtra(EXTRA_TIME_START, mTimeStart);
+ mTimeEnd = getIntent().getLongExtra(EXTRA_TIME_END, mTimeEnd);
+
+ mScrollView = (ScrollView) findViewById(R.id.blocks_scroll);
+ mBlocks = (BlocksLayout) findViewById(R.id.blocks);
+ mNowView = findViewById(R.id.blocks_now);
+
+ mBlocks.setDrawingCacheEnabled(true);
+ mBlocks.setAlwaysDrawnWithCacheEnabled(true);
+
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // Since we build our views manually instead of using an adapter, we
+ // need to manually requery every time launched.
+ final Uri blocksUri = getIntent().getData();
+ mHandler.startQuery(blocksUri, BlocksQuery.PROJECTION, Blocks.DEFAULT_SORT);
+
+ // Start listening for time updates to adjust "now" bar. TIME_TICK is
+ // triggered once per minute, which is how we move the bar over time.
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_TIME_TICK);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ registerReceiver(mReceiver, filter, null, new Handler());
+
+ mNowView.post(new Runnable() {
+ public void run() {
+ updateNowView(true);
+ }
+ });
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ unregisterReceiver(mReceiver);
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ // Clear out any existing sessions before inserting again
+ mBlocks.removeAllBlocks();
+
+ try {
+ while (cursor.moveToNext()) {
+ final String type = cursor.getString(BlocksQuery.BLOCK_TYPE);
+ final Integer column = sTypeColumnMap.get(type);
+ // TODO: place random blocks at bottom of entire layout
+ if (column == null) continue;
+
+ final String blockId = cursor.getString(BlocksQuery.BLOCK_ID);
+ final String title = cursor.getString(BlocksQuery.BLOCK_TITLE);
+ final long start = cursor.getLong(BlocksQuery.BLOCK_START);
+ final long end = cursor.getLong(BlocksQuery.BLOCK_END);
+ final boolean containsStarred = cursor.getInt(BlocksQuery.CONTAINS_STARRED) != 0;
+
+ final BlockView blockView = new BlockView(this, blockId, title, start, end,
+ containsStarred, column);
+
+ final int sessionsCount = cursor.getInt(BlocksQuery.SESSIONS_COUNT);
+ if (sessionsCount > 0) {
+ blockView.setOnClickListener(this);
+ } else {
+ blockView.setFocusable(false);
+ blockView.setEnabled(false);
+ LayerDrawable buttonDrawable = (LayerDrawable) blockView.getBackground();
+ buttonDrawable.getDrawable(0).setAlpha(DISABLED_BLOCK_ALPHA);
+ buttonDrawable.getDrawable(2).setAlpha(DISABLED_BLOCK_ALPHA);
+ }
+
+ mBlocks.addBlock(blockView);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ public void onRefreshClick(View v) {
+ }
+
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** {@inheritDoc} */
+ public void onClick(View view) {
+ if (view instanceof BlockView) {
+ final String blockId = ((BlockView) view).getBlockId();
+ final Uri sessionsUri = Blocks.buildSessionsUri(blockId);
+ startActivity(new Intent(Intent.ACTION_VIEW, sessionsUri));
+ }
+ }
+
+ /**
+ * Update position and visibility of "now" view.
+ */
+ private void updateNowView(boolean forceScroll) {
+ final long now = System.currentTimeMillis();
+
+ final boolean visible = now >= mTimeStart && now <= mTimeEnd;
+ mNowView.setVisibility(visible ? View.VISIBLE : View.GONE);
+
+ if (visible && forceScroll) {
+ // Scroll to show "now" in center
+ final int offset = mScrollView.getHeight() / 2;
+ mNowView.requestRectangleOnScreen(new Rect(0, offset, 0, offset), true);
+ }
+
+ mBlocks.requestLayout();
+ }
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "onReceive time update");
+ updateNowView(false);
+ }
+ };
+
+ private interface BlocksQuery {
+ String[] PROJECTION = {
+ BaseColumns._ID,
+ Blocks.BLOCK_ID,
+ Blocks.BLOCK_TITLE,
+ Blocks.BLOCK_START,
+ Blocks.BLOCK_END,
+ Blocks.BLOCK_TYPE,
+ Blocks.SESSIONS_COUNT,
+ Blocks.CONTAINS_STARRED,
+ };
+
+ int _ID = 0;
+ int BLOCK_ID = 1;
+ int BLOCK_TITLE = 2;
+ int BLOCK_START = 3;
+ int BLOCK_END = 4;
+ int BLOCK_TYPE = 5;
+ int SESSIONS_COUNT = 6;
+ int CONTAINS_STARRED = 7;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/HomeActivity.java b/src/com/google/android/apps/iosched/ui/HomeActivity.java
new file mode 100644
index 0000000..cb9c5cf
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/HomeActivity.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import static com.google.android.apps.iosched.util.UIUtils.formatSessionSubtitle;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Notes;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.service.SyncService;
+import com.google.android.apps.iosched.util.DetachableResultReceiver;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.Settings;
+import android.text.format.DateUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Front-door {@link Activity} that displays high-level features the schedule
+ * application offers to users.
+ */
+public class HomeActivity extends Activity implements AsyncQueryListener,
+ DetachableResultReceiver.Receiver {
+ private static final String TAG = "HomeActivity";
+
+ /** State held between configuration changes. */
+ private State mState;
+
+ private Handler mMessageHandler = new Handler();
+ private NotifyingAsyncQueryHandler mQueryHandler;
+
+ private TextView mCountdownTextView;
+ private View mNowPlayingLoadingView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_home);
+
+ mNowPlayingLoadingView = findViewById(R.id.now_playing_loading);
+
+ mState = (State) getLastNonConfigurationInstance();
+ final boolean previousState = mState != null;
+
+ if (previousState) {
+ // Start listening for SyncService updates again
+ mState.mReceiver.setReceiver(this);
+ updateRefreshStatus();
+ reloadNowPlaying(true);
+
+ } else {
+ mState = new State();
+ mState.mReceiver.setReceiver(this);
+ onRefreshClick(null);
+ }
+
+ // Set up handler for now playing session query.
+ mQueryHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ }
+
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ // Clear any strong references to this Activity, we'll reattach to
+ // handle events on the other side.
+ mState.mReceiver.clearReceiver();
+ return mState;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // If 'tap here to enable Wifi' was shown, check if the user has enabled Wifi
+ if (mState.mNowPlayingUri != null || mState.mNowPlayingGotoWifi) {
+ reloadNowPlaying(false);
+ } else if (mState.mNoResults) {
+ showNowPlayingNoResults();
+ }
+ }
+
+ /** Handle "refresh" title-bar action. */
+ public void onRefreshClick(View v) {
+ // trigger off background sync
+ final Intent intent = new Intent(Intent.ACTION_SYNC, null, this, SyncService.class);
+ intent.putExtra(SyncService.EXTRA_STATUS_RECEIVER, mState.mReceiver);
+ startService(intent);
+
+ reloadNowPlaying(true);
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** Handle "schedule" action. */
+ public void onScheduleClick(View v) {
+ // Launch overall conference schedule
+ startActivity(new Intent(this, ScheduleActivity.class));
+ }
+
+ /** Handle "map" action. */
+ public void onMapClick(View v) {
+ // Launch map of conference venue
+ startActivity(new Intent(this, MapActivity.class));
+ }
+
+ /** Handle "sessions" action. */
+ public void onSessionsClick(View v) {
+ // Launch sessions clustered by track
+ final Intent intent = new Intent(Intent.ACTION_VIEW, Tracks.CONTENT_URI);
+ intent.putExtra(Intent.EXTRA_TITLE, getString(R.string.title_session_tracks));
+ intent.putExtra(TrackDetailActivity.EXTRA_FOCUS_TAG, TrackDetailActivity.TAG_SESSIONS);
+ startActivity(intent);
+ }
+
+ /** Handle "starred" action. */
+ public void onStarredClick(View v) {
+ // Launch list of sessions user has starred
+ startActivity(new Intent(this, StarredActivity.class));
+ }
+
+ /** Handle "sandbox" or "vendors" action. */
+ public void onVendorsClick(View v) {
+ // Launch list of vendors at conference
+ final Intent intent = new Intent(Intent.ACTION_VIEW, Tracks.CONTENT_URI);
+ intent.putExtra(Intent.EXTRA_TITLE, getString(R.string.title_vendor_tracks));
+ intent.putExtra(TrackDetailActivity.EXTRA_FOCUS_TAG, TrackDetailActivity.TAG_VENDORS);
+ startActivity(intent);
+ }
+
+ /** Handle "my notes" action. */
+ public void onNotesClick(View v) {
+ // Launch list of notes user has taken
+ startActivity(new Intent(Intent.ACTION_VIEW, Notes.CONTENT_URI));
+ }
+
+ /** Handle "now playing" action. */
+ public void onNowPlayingClick(View v) {
+ // Shortcut launch directly to session the user is currently attending
+ // (or to enable wifi if wifi is off)
+ if (mState.mNowPlayingGotoWifi) {
+ Intent wifiSettingsIntent = new Intent();
+ wifiSettingsIntent.setAction(Settings.ACTION_WIFI_SETTINGS);
+ startActivity(wifiSettingsIntent);
+ } else if (mState.mNowPlayingUri != null) {
+ startActivity(new Intent(Intent.ACTION_VIEW, mState.mNowPlayingUri));
+ }
+ }
+
+ /** Handle "now playing > more" action. */
+ public void onNowPlayingMoreClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Sessions.buildSessionsAtDirUri(System.currentTimeMillis()));
+ intent.putExtra(Intent.EXTRA_TITLE, getString(R.string.title_now_playing));
+ startActivity(intent);
+ }
+
+ /** Handle "now playing" logo (Google I/O logo in before/after state) action. */
+ public void onNowPlayingLogoClick(View v) {
+ startActivity(new Intent(Intent.ACTION_VIEW, UIUtils.CONFERENCE_URL));
+ }
+
+ private void reloadNowPlaying(boolean forceRelocate) {
+ mMessageHandler.removeCallbacks(mCountdownRunnable);
+
+ final long currentTimeMillis = System.currentTimeMillis();
+
+ if (mNowPlayingLoadingView == null) // Landscape orientation
+ return;
+
+ ViewGroup homeRoot = (ViewGroup) findViewById(R.id.home_root);
+ View nowPlaying = findViewById(R.id.now_playing);
+ if (nowPlaying != null) {
+ homeRoot.removeView(nowPlaying);
+ nowPlaying = null;
+ }
+
+ // Show Loading... and load the view corresponding to the current state
+ mNowPlayingLoadingView.setVisibility(View.VISIBLE);
+ mState.mNoResults = false;
+ mState.mNowPlayingGotoWifi = false;
+ if (currentTimeMillis < UIUtils.CONFERENCE_START_MILLIS) {
+ nowPlaying = createNowPlayingBeforeView();
+ } else if (currentTimeMillis > UIUtils.CONFERENCE_END_MILLIS) {
+ nowPlaying = createNowPlayingAfterView();
+ } else {
+ nowPlaying = createNowPlayingDuringView(forceRelocate);
+ }
+
+ homeRoot.addView(nowPlaying, new LayoutParams(
+ LayoutParams.FILL_PARENT,
+ (int) getResources().getDimension(R.dimen.now_playing_height)));
+ }
+
+ private View createNowPlayingBeforeView() {
+ // Before conference, show countdown.
+ final View nowPlaying = getLayoutInflater().inflate(R.layout.now_playing_before, null);
+ final TextView nowPlayingTitle = (TextView) nowPlaying.findViewById(
+ R.id.now_playing_title);
+
+ mCountdownTextView = nowPlayingTitle;
+ mMessageHandler.post(mCountdownRunnable);
+ mNowPlayingLoadingView.setVisibility(View.GONE);
+ nowPlaying.setVisibility(View.VISIBLE);
+ return nowPlaying;
+ }
+
+ private View createNowPlayingAfterView() {
+ // After conference, show canned text.
+ final View nowPlaying = getLayoutInflater().inflate(R.layout.now_playing_after, null);
+ mNowPlayingLoadingView.setVisibility(View.GONE);
+ nowPlaying.setVisibility(View.VISIBLE);
+ return nowPlaying;
+ }
+
+ private View createNowPlayingDuringView(boolean forceRelocate) {
+ // Conference in progress, show now playing.
+ final View nowPlaying = getLayoutInflater().inflate(R.layout.now_playing_during, null);
+ nowPlaying.setVisibility(View.GONE);
+ if (!forceRelocate && mState.mNowPlayingUri != null) {
+ mQueryHandler.startQuery(mState.mNowPlayingUri, SessionsQuery.PROJECTION);
+ }
+ return nowPlaying;
+ }
+
+ /**
+ * Event that updates countdown timer. Posts itself again to
+ * {@link #mMessageHandler} to continue updating time.
+ */
+ private Runnable mCountdownRunnable = new Runnable() {
+ public void run() {
+ int remainingSec = (int) Math.max(0,
+ (UIUtils.CONFERENCE_START_MILLIS - System.currentTimeMillis()) / 1000);
+ final boolean conferenceStarted = remainingSec == 0;
+
+ if (conferenceStarted) {
+ // Conference started while in countdown mode, switch modes and
+ // bail on future countdown updates.
+ mMessageHandler.postDelayed(new Runnable() {
+ public void run() {
+ reloadNowPlaying(true);
+ }
+ }, 100);
+ return;
+ }
+
+ final int secs = remainingSec % 86400;
+ final int days = remainingSec / 86400;
+ final String str = getResources().getQuantityString(
+ R.plurals.now_playing_countdown, days, days,
+ DateUtils.formatElapsedTime(secs));
+ mCountdownTextView.setText(str);
+
+ // Repost ourselves to keep updating countdown
+ mMessageHandler.postDelayed(mCountdownRunnable, 1000);
+ }
+ };
+
+ private void showNowPlayingNoResults() {
+ mState.mNoResults = true;
+ runOnUiThread(new Runnable() {
+ public void run() {
+ final View loadingView = findViewById(R.id.now_playing_loading);
+ if (loadingView == null) return;
+
+ loadingView.setVisibility(View.GONE);
+ findViewById(R.id.now_playing).setVisibility(View.VISIBLE);
+ ((TextView) findViewById(R.id.now_playing_title)).setText(
+ R.string.now_playing_no_results);
+ }
+ });
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ try {
+ if (!cursor.moveToFirst()) {
+ showNowPlayingNoResults();
+ return;
+ }
+
+ mState.mNowPlayingUri = Sessions.buildSessionUri(cursor
+ .getString(SessionsQuery.SESSION_ID));
+
+ // Format time block this session occupies
+ final long blockStart = cursor.getLong(SessionsQuery.BLOCK_START);
+ final long blockEnd = cursor.getLong(SessionsQuery.BLOCK_END);
+
+ final String roomName = cursor.getString(SessionsQuery.ROOM_NAME);
+ final String subtitle = formatSessionSubtitle(blockStart, blockEnd, roomName, this);
+
+ findViewById(R.id.now_playing_loading).setVisibility(View.GONE);
+ findViewById(R.id.now_playing).setVisibility(View.VISIBLE);
+ ((TextView) findViewById(R.id.now_playing_title)).setText(cursor
+ .getString(SessionsQuery.TITLE));
+ ((TextView) findViewById(R.id.now_playing_subtitle)).setText(subtitle);
+
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private void updateRefreshStatus() {
+ findViewById(R.id.btn_title_refresh).setVisibility(
+ mState.mSyncing ? View.GONE : View.VISIBLE);
+ findViewById(R.id.title_refresh_progress).setVisibility(
+ mState.mSyncing ? View.VISIBLE : View.GONE);
+ }
+
+ /** {@inheritDoc} */
+ public void onReceiveResult(int resultCode, Bundle resultData) {
+ switch (resultCode) {
+ case SyncService.STATUS_RUNNING: {
+ mState.mSyncing = true;
+ updateRefreshStatus();
+ break;
+ }
+ case SyncService.STATUS_FINISHED: {
+ mState.mSyncing = false;
+ updateRefreshStatus();
+ reloadNowPlaying(true);
+ break;
+ }
+ case SyncService.STATUS_ERROR: {
+ // Error happened down in SyncService, show as toast.
+ mState.mSyncing = false;
+ updateRefreshStatus();
+ final String errorText = getString(R.string.toast_sync_error, resultData
+ .getString(Intent.EXTRA_TEXT));
+ Toast.makeText(HomeActivity.this, errorText, Toast.LENGTH_LONG).show();
+ break;
+ }
+ }
+ }
+
+ /**
+ * State specific to {@link HomeActivity} that is held between configuration
+ * changes. Any strong {@link Activity} references must be
+ * cleared before {@link #onRetainNonConfigurationInstance()}, and this
+ * class should remain {@code static class}.
+ */
+ private static class State {
+ public DetachableResultReceiver mReceiver;
+ public Uri mNowPlayingUri = null;
+ public boolean mNowPlayingGotoWifi = false;
+ public boolean mSyncing = false;
+ public boolean mNoResults = false;
+
+ private State() {
+ mReceiver = new DetachableResultReceiver(new Handler());
+ }
+ }
+
+ private interface SessionsQuery {
+ String[] PROJECTION = {
+ Blocks.BLOCK_START,
+ Blocks.BLOCK_END,
+ Sessions.SESSION_ID,
+ Sessions.TITLE,
+ Rooms.ROOM_NAME,
+ };
+
+ int BLOCK_START = 0;
+ int BLOCK_END = 1;
+ int SESSION_ID = 2;
+ int TITLE = 3;
+ int ROOM_NAME = 4;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/MapActivity.java b/src/com/google/android/apps/iosched/ui/MapActivity.java
new file mode 100644
index 0000000..370a0fa
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/MapActivity.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.util.ParserUtils;
+import com.google.android.apps.iosched.util.UIUtils;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.webkit.WebChromeClient;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Shows a {@link WebView} with a map of the conference venue.
+ */
+public class MapActivity extends Activity {
+ private static final String TAG = "MapActivity";
+
+ /**
+ * When specified, will automatically point the map to the requested room.
+ */
+ public static final String EXTRA_ROOM = "com.google.android.iosched.extra.ROOM";
+
+ public static final String OFFICE_HOURS_ROOM_ID = "officehours";
+
+ private static final String MAP_JSI_NAME = "MAP_CONTAINER";
+ private static final String MAP_URL = "http://code.google.com/events/io/2010/map/embed.html";
+ private static boolean CLEAR_CACHE_ON_LOAD = false;
+
+ private WebView mWebView;
+ private boolean mLoadingVisible = false;
+ private boolean mMapInitialized = false;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_map);
+
+ ((TextView) findViewById(R.id.title_text)).setText(getTitle());
+
+ showLoading(true);
+ mWebView = (WebView) findViewById(R.id.webview);
+ mWebView.post(new Runnable() {
+ public void run() {
+ // Initialize web view
+ if (CLEAR_CACHE_ON_LOAD) {
+ mWebView.clearCache(true);
+ }
+
+ mWebView.getSettings().setJavaScriptEnabled(true);
+ mWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(false);
+ mWebView.setWebChromeClient(new MapWebChromeClient());
+ mWebView.setWebViewClient(new MapWebViewClient());
+ mWebView.loadUrl(MAP_URL);
+ mWebView.addJavascriptInterface(new MapJsiImpl(), MAP_JSI_NAME);
+ }
+ });
+ }
+
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ public void onRefreshClick(View v) {
+ mWebView.reload();
+ }
+
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) {
+ mWebView.goBack();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ private void showLoading(boolean loading) {
+ if (mLoadingVisible == loading)
+ return;
+
+ View refreshButton = findViewById(R.id.btn_title_refresh);
+ View refreshProgress = findViewById(R.id.title_refresh_progress);
+ refreshButton.setVisibility(loading ? View.GONE : View.VISIBLE);
+ refreshProgress.setVisibility(loading ? View.VISIBLE : View.GONE);
+ mLoadingVisible = loading;
+ }
+
+ private void runJs(String js) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Loading javascript:" + js);
+ }
+ mWebView.loadUrl("javascript:" + js);
+ }
+
+ private class MapWebChromeClient extends WebChromeClient {
+ @Override
+ public void onProgressChanged(WebView view, int newProgress) {
+ showLoading(newProgress < 100);
+ super.onProgressChanged(view, newProgress);
+ }
+
+ public void onConsoleMessage(String message, int lineNumber, String sourceID) {
+ Log.i(TAG, "JS Console message: (" + sourceID + ": " + lineNumber + ") " + message);
+ }
+ }
+
+ private class MapWebViewClient extends WebViewClient {
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ super.onPageFinished(view, url);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Page finished loading: " + url);
+ }
+ }
+
+ @Override
+ public void onReceivedError(WebView view, int errorCode, String description,
+ String failingUrl) {
+ Log.e(TAG, "Error " + errorCode + ": " + description);
+ Toast.makeText(view.getContext(), "Error " + errorCode + ": " + description,
+ Toast.LENGTH_LONG).show();
+ super.onReceivedError(view, errorCode, description, failingUrl);
+ }
+ }
+
+ /**
+ * Helper method to escape JavaScript strings. Useful when passing strings to a WebView
+ * via "javascript:" calls.
+ */
+ private static String escapeJsString(String s) {
+ if (s == null)
+ return "";
+
+ return s.replace("'", "\\'").replace("\"", "\\\"");
+ }
+
+ /**
+ * I/O Conference Map JavaScript interface.
+ */
+ private interface MapJsi {
+ void openContentInfo(String test);
+ void onMapReady();
+ }
+
+ private class MapJsiImpl implements MapJsi {
+ public void openContentInfo(String roomId) {
+ final String possibleTrackId = ParserUtils.translateTrackIdAlias(roomId);
+ if (OFFICE_HOURS_ROOM_ID.equals(roomId)) {
+ // The office hours room was requested.
+ Uri officeHoursUri = Sessions.buildSearchUri("office hours");
+ Intent officeHoursIntent = new Intent(Intent.ACTION_VIEW, officeHoursUri);
+ startActivity(officeHoursIntent);
+ } else if (ParserUtils.LOCAL_TRACK_IDS.contains(possibleTrackId)) {
+ // This is a track; open up the sandbox for the track, since room IDs that are
+ // track IDs are sandbox areas in the map.
+ Uri trackVendorsUri = Tracks.buildTrackUri(possibleTrackId);
+ Intent trackVendorsIntent = new Intent(Intent.ACTION_VIEW, trackVendorsUri);
+ trackVendorsIntent.putExtra(TrackDetailActivity.EXTRA_FOCUS_TAG,
+ TrackDetailActivity.TAG_VENDORS);
+ startActivity(trackVendorsIntent);
+ } else {
+ Uri roomUri = Rooms.buildSessionsDirUri(roomId);
+ Intent roomIntent = new Intent(Intent.ACTION_VIEW, roomUri);
+ startActivity(roomIntent);
+ }
+ }
+
+ public void onMapReady() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onMapReady");
+ }
+
+ String showRoomId = null;
+ if (!mMapInitialized && getIntent().hasExtra(EXTRA_ROOM)) {
+ showRoomId = getIntent().getStringExtra(EXTRA_ROOM);
+ }
+
+ if (showRoomId != null) {
+ runJs("googleIo.showLocationById('" +
+ escapeJsString(showRoomId) + "');");
+ }
+
+ mMapInitialized = true;
+ }
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/NoteEditActivity.java b/src/com/google/android/apps/iosched/ui/NoteEditActivity.java
new file mode 100644
index 0000000..62f2d9f
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/NoteEditActivity.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Notes;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ContentValues;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+
+/**
+ * Editor for {@link Notes}, handles both {@link Intent#ACTION_INSERT} and
+ * {@link Intent#ACTION_EDIT}.
+ */
+public class NoteEditActivity extends Activity implements AsyncQueryListener {
+
+ private EditText mText;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_note_edit);
+
+ ((TextView) findViewById(R.id.title_text)).setText(getTitle());
+
+ mText = (EditText) findViewById(android.R.id.text1);
+
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+
+ final String action = getIntent().getAction();
+ if (Intent.ACTION_EDIT.equals(action) && savedInstanceState == null) {
+ // Start background query to load current state
+ final Uri noteUri = getIntent().getData();
+ mHandler.startQuery(noteUri, NotesQuery.PROJECTION);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ try {
+ if (!cursor.moveToFirst()) return;
+
+ // Load current note content for editing
+ mText.setText(cursor.getString(NotesQuery.NOTE_CONTENT));
+
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public void onSaveClick(View v) {
+ saveContent();
+ }
+
+ public void onDiscardClick(View v) {
+ final String noteContent = mText.getText().toString();
+ if (TextUtils.isEmpty(noteContent)) {
+ // When empty content, shortcut to discard without confirm step
+ discardContent();
+ } else {
+ showDialog(R.id.dialog_discard_confirm);
+ }
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "refresh" title-bar action. */
+ public void onRefreshClick(View v) {
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case R.id.dialog_discard_confirm: {
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.note_discard_title)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(R.string.note_discard_confirm)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, new DiscardConfirmClickListener())
+ .setCancelable(false)
+ .create();
+ }
+ }
+ return null;
+ }
+
+ private class DiscardConfirmClickListener implements DialogInterface.OnClickListener {
+ public void onClick(DialogInterface dialog, int which) {
+ discardContent();
+ }
+ }
+
+ /**
+ * Persist the contents of into {@link Notes} backend. The actual save is
+ * performed asynchronously, so this method doesn't block.
+ */
+ private void saveContent() {
+ final String noteContent = mText.getText().toString();
+
+ // TODO: consider passing off to startService() to prevent our
+ // process from being killed before note is actually persisted.
+
+ // When empty content, treat as discard
+ if (TextUtils.isEmpty(noteContent)) {
+ discardContent();
+ return;
+ }
+
+ // Always store updated note content
+ final ContentValues values = new ContentValues();
+ values.put(Notes.NOTE_CONTENT, noteContent);
+
+ final String action = getIntent().getAction();
+ if (Intent.ACTION_INSERT.equals(action)) {
+ // Insert also includes current timestamp
+ values.put(Notes.NOTE_TIME, System.currentTimeMillis());
+
+ final Uri notesDirUri = getIntent().getData();
+ mHandler.startInsert(notesDirUri, values);
+ } else if (Intent.ACTION_EDIT.equals(action)) {
+ final Uri noteUri = getIntent().getData();
+ mHandler.startUpdate(noteUri, values);
+ }
+
+ finish();
+ }
+
+ private void discardContent() {
+ final String action = getIntent().getAction();
+ if (Intent.ACTION_EDIT.equals(action)) {
+ final Uri noteUri = getIntent().getData();
+ mHandler.startDelete(noteUri);
+
+ } else if (Intent.ACTION_INSERT.equals(action)) {
+ // Silently discard new note
+
+ }
+
+ finish();
+ }
+
+ /** {@link Notes} query parameters. */
+ private interface NotesQuery {
+ String[] PROJECTION = {
+ Notes.NOTE_TIME,
+ Notes.NOTE_CONTENT,
+ };
+
+ int NOTE_TIME = 0;
+ int NOTE_CONTENT = 1;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/NotesActivity.java b/src/com/google/android/apps/iosched/ui/NotesActivity.java
new file mode 100644
index 0000000..385d82b
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/NotesActivity.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Notes;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.BaseColumns;
+import android.text.format.DateUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CursorAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * {@link ListActivity} that displays a set of {@link Notes}, as requested
+ * through {@link Intent#getData()}.
+ */
+public class NotesActivity extends ListActivity implements AsyncQueryListener {
+
+ public static final String EXTRA_SHOW_INSERT = "com.google.android.iosched.extra.SHOW_INSERT";
+
+ private NotesAdapter mAdapter;
+
+ private boolean mShowInsert = false;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (!getIntent().hasCategory(Intent.CATEGORY_TAB)) {
+ setContentView(R.layout.activity_notes);
+ ((TextView) findViewById(R.id.title_text)).setText(getTitle());
+
+ } else {
+ setContentView(R.layout.activity_notes_content);
+ }
+
+ mShowInsert = getIntent().getBooleanExtra(EXTRA_SHOW_INSERT, false);
+ if (mShowInsert) {
+ final ListView listView = getListView();
+ final View view = getLayoutInflater().inflate(R.layout.list_item_note_create,
+ listView, false);
+ listView.addHeaderView(view, null, true);
+ }
+
+ mAdapter = new NotesAdapter(this);
+ setListAdapter(mAdapter);
+
+ final Uri notesUri = getIntent().getData();
+
+ // Start background query to load notes
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ mHandler.startQuery(notesUri, NotesQuery.PROJECTION, Notes.DEFAULT_SORT);
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ startManagingCursor(cursor);
+ mAdapter.changeCursor(cursor);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ if (id >= 0) {
+ // Edit an existing note
+ final Uri noteUri = Notes.buildNoteUri(id);
+ startActivity(new Intent(Intent.ACTION_EDIT, noteUri));
+
+ } else {
+ // Insert new note
+ final Uri notesDirUri = getIntent().getData();
+ startActivity(new Intent(Intent.ACTION_INSERT, notesDirUri));
+ }
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "share" title-bar action. */
+ public void onShareClick(View v) {
+ final String shareText = getString(R.string.share_notes);
+
+ final Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType("plain/html");
+ intent.putExtra(Intent.EXTRA_SUBJECT, shareText);
+ intent.putExtra(Intent.EXTRA_TEXT, shareText);
+ intent.putExtra(Intent.EXTRA_STREAM, Notes.CONTENT_EXPORT_URI);
+
+ startActivity(Intent.createChooser(intent, getText(R.string.title_share)));
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /**
+ * {@link CursorAdapter} that renders a {@link NotesQuery}.
+ */
+ private class NotesAdapter extends CursorAdapter {
+ public NotesAdapter(Context context) {
+ super(context, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return getLayoutInflater().inflate(R.layout.list_item_note, parent, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ // TODO: format notes with better layout
+ ((TextView)view.findViewById(R.id.note_content)).setText(cursor
+ .getString(NotesQuery.NOTE_CONTENT));
+
+ // TODO: format using note_before/into/after
+ final long time = cursor.getLong(NotesQuery.NOTE_TIME);
+ final CharSequence relativeTime = DateUtils.getRelativeTimeSpanString(time);
+ ((TextView) view.findViewById(R.id.note_time)).setText(relativeTime);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return mShowInsert ? false : super.isEmpty();
+ }
+ }
+
+ /** {@link Notes} query parameters. */
+ private interface NotesQuery {
+ String[] PROJECTION = {
+ BaseColumns._ID,
+ Notes.NOTE_TIME,
+ Notes.NOTE_CONTENT,
+ };
+
+ int _ID = 0;
+ int NOTE_TIME = 1;
+ int NOTE_CONTENT = 2;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/ScheduleActivity.java b/src/com/google/android/apps/iosched/ui/ScheduleActivity.java
new file mode 100644
index 0000000..1180bfa
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/ScheduleActivity.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.util.MathUtils;
+import com.google.android.apps.iosched.util.ParserUtils;
+import com.google.android.apps.iosched.util.UIUtils;
+
+import android.app.TabActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.format.DateUtils;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.TabHost;
+import android.widget.TextView;
+
+import java.util.TimeZone;
+
+public class ScheduleActivity extends TabActivity {
+
+ /** Flags used with {@link DateUtils#formatDateRange}. */
+ private static final int TIME_FLAGS = DateUtils.FORMAT_SHOW_DATE
+ | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_WEEKDAY;
+
+ private static final String TAG_TUE = "tuesday";
+ private static final String TAG_WED = "wednesday";
+ private static final String TAG_THU = "thursday";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_schedule);
+
+ ((TextView) findViewById(R.id.title_text)).setText(getTitle());
+
+ final long wedStart = ParserUtils.parseTime("2010-05-19T00:00:00.000-07:00");
+ final long thuStart = ParserUtils.parseTime("2010-05-20T00:00:00.000-07:00");
+
+ setupBlocksTab(TAG_WED, wedStart);
+ setupBlocksTab(TAG_THU, thuStart);
+
+ final long now = System.currentTimeMillis();
+ if (now >= thuStart) {
+ getTabHost().setCurrentTabByTag(TAG_THU);
+ } else {
+ // Otherwise start with first day
+ getTabHost().setCurrentTabByTag(TAG_WED);
+ }
+ }
+
+ public static class FlingableTabHost extends TabHost {
+ GestureDetector mGestureDetector;
+
+ Animation mRightInAnimation;
+ Animation mRightOutAnimation;
+ Animation mLeftInAnimation;
+ Animation mLeftOutAnimation;
+
+ public FlingableTabHost(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mRightInAnimation = AnimationUtils.loadAnimation(context, R.anim.slide_right_in);
+ mRightOutAnimation = AnimationUtils.loadAnimation(context, R.anim.slide_right_out);
+ mLeftInAnimation = AnimationUtils.loadAnimation(context, R.anim.slide_left_in);
+ mLeftOutAnimation = AnimationUtils.loadAnimation(context, R.anim.slide_left_out);
+
+ final int minScaledFlingVelocity = ViewConfiguration.get(context)
+ .getScaledMinimumFlingVelocity() * 10; // 10 = fudge by experimentation
+
+ mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ int tabCount = getTabWidget().getTabCount();
+ int currentTab = getCurrentTab();
+ if (Math.abs(velocityX) > minScaledFlingVelocity &&
+ Math.abs(velocityY) < minScaledFlingVelocity) {
+
+ final boolean right = velocityX < 0;
+ final int newTab = MathUtils.constrain(currentTab + (right ? 1 : -1),
+ 0, tabCount - 1);
+ if (newTab != currentTab) {
+ // Somewhat hacky, depends on current implementation of TabHost:
+ // http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;
+ // f=core/java/android/widget/TabHost.java
+ View currentView = getCurrentView();
+ setCurrentTab(newTab);
+ View newView = getCurrentView();
+
+ newView.startAnimation(right ? mRightInAnimation : mLeftInAnimation);
+ currentView.startAnimation(
+ right ? mRightOutAnimation : mLeftOutAnimation);
+ }
+ }
+ return super.onFling(e1, e2, velocityX, velocityY);
+ }
+ });
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mGestureDetector.onTouchEvent(ev)) {
+ return true;
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+ }
+
+ private void setupBlocksTab(String tag, long startMillis) {
+ final TabHost host = getTabHost();
+
+ final long endMillis = startMillis + DateUtils.DAY_IN_MILLIS;
+ final Uri blocksBetweenDirUri = Blocks.buildBlocksBetweenDirUri(startMillis, endMillis);
+
+ final Intent intent = new Intent(Intent.ACTION_VIEW, blocksBetweenDirUri);
+ intent.addCategory(Intent.CATEGORY_TAB);
+
+ intent.putExtra(BlocksActivity.EXTRA_TIME_START, startMillis);
+ intent.putExtra(BlocksActivity.EXTRA_TIME_END, endMillis);
+
+ TimeZone.setDefault(UIUtils.CONFERENCE_TIME_ZONE);
+ final String label = DateUtils.formatDateTime(this, startMillis, TIME_FLAGS);
+ host.addTab(host.newTabSpec(tag)
+ .setIndicator(buildIndicator(label))
+ .setContent(intent));
+ }
+
+ /**
+ * Build a {@link View} to be used as a tab indicator, setting the requested
+ * string resource as its label.
+ */
+ private View buildIndicator(String text) {
+ final TextView indicator = (TextView) getLayoutInflater().inflate(R.layout.tab_indicator,
+ getTabWidget(), false);
+ indicator.setText(text);
+ return indicator;
+ }
+
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/SearchActivity.java b/src/com/google/android/apps/iosched/ui/SearchActivity.java
new file mode 100644
index 0000000..6f9fc0f
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/SearchActivity.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.util.UIUtils;
+
+import android.app.SearchManager;
+import android.app.TabActivity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TabHost;
+import android.widget.TextView;
+
+public class SearchActivity extends TabActivity {
+
+ public static final String TAG_SESSIONS = "sessions";
+ public static final String TAG_VENDORS = "vendors";
+
+ private String mQuery;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_search);
+
+ onNewIntent(getIntent());
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ mQuery = intent.getStringExtra(SearchManager.QUERY);
+ final CharSequence title = getString(R.string.title_search_query, mQuery);
+
+ setTitle(title);
+ ((TextView) findViewById(R.id.title_text)).setText(title);
+
+ final TabHost host = getTabHost();
+ host.setCurrentTab(0);
+ host.clearAllTabs();
+
+ setupSessionsTab();
+ setupVendorsTab();
+ }
+
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** Build and add "sessions" tab. */
+ private void setupSessionsTab() {
+ final TabHost host = getTabHost();
+
+ final Uri sessionsUri = Sessions.buildSearchUri(mQuery);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, sessionsUri);
+ intent.addCategory(Intent.CATEGORY_TAB);
+
+ // Sessions content comes from reused activity
+ host.addTab(host.newTabSpec(TAG_SESSIONS)
+ .setIndicator(buildIndicator(R.string.search_sessions))
+ .setContent(intent));
+ }
+
+ /** Build and add "vendors" tab. */
+ private void setupVendorsTab() {
+ final TabHost host = getTabHost();
+
+ final Uri vendorsUri = Vendors.buildSearchUri(mQuery);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, vendorsUri);
+ intent.addCategory(Intent.CATEGORY_TAB);
+
+ // Vendors content comes from reused activity
+ host.addTab(host.newTabSpec(TAG_VENDORS)
+ .setIndicator(buildIndicator(R.string.search_vendors))
+ .setContent(intent));
+ }
+
+ /**
+ * Build a {@link View} to be used as a tab indicator, setting the requested
+ * string resource as its label.
+ */
+ private View buildIndicator(int textRes) {
+ final TextView indicator = (TextView) getLayoutInflater().inflate(R.layout.tab_indicator,
+ getTabWidget(), false);
+ indicator.setText(textRes);
+ return indicator;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/SessionDetailActivity.java b/src/com/google/android/apps/iosched/ui/SessionDetailActivity.java
new file mode 100644
index 0000000..1ca8a98
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/SessionDetailActivity.java
@@ -0,0 +1,635 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Speakers;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.service.SyncService;
+import com.google.android.apps.iosched.util.FractionalTouchDelegate;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONObject;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.TabActivity;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.Cursor;
+import android.graphics.RectF;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CompoundButton;
+import android.widget.TabHost;
+import android.widget.TextView;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+
+/**
+ * {@link Activity} that displays details about a specific
+ * {@link Sessions#SESSION_ID}, as requested through {@link Intent#getData()}.
+ */
+public class SessionDetailActivity extends TabActivity implements AsyncQueryListener,
+ OnCheckedChangeListener {
+ private static final String TAG = "SessionDetailActivity";
+
+ /**
+ * Since {@link Sessions} can belong to multiple {@link Tracks}, the parent
+ * {@link Activity} can send this extra specifying a {@link Tracks}
+ * {@link Uri} that should be used for coloring the title-bar.
+ */
+ public static final String EXTRA_TRACK = "com.google.android.iosched.extra.TRACK";
+
+ private static final String MODERATOR_PACKAGE = "com.google.android.apps.moderator";
+
+ private static final String TAG_SUMMARY = "summary";
+ private static final String TAG_NOTES = "notes";
+ private static final String TAG_MODERATOR = "moderator";
+
+ private String mSessionId;
+ private Uri mSessionUri;
+
+ private String mTitleString;
+ private String mHashtag;
+ private String mRoomId;
+
+ private TextView mTitle;
+ private TextView mSubtitle;
+ private CompoundButton mStarred;
+
+ private TextView mAbstract;
+ private TextView mRequirements;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ private boolean mSessionCursor = false;
+ private boolean mSpeakersCursor = false;
+ private boolean mHasSummaryContent = false;
+
+ private Uri mModeratorUri;
+ private Uri mWaveUri;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_session_detail);
+
+ mTitle = (TextView) findViewById(R.id.session_title);
+ mSubtitle = (TextView) findViewById(R.id.session_subtitle);
+ mStarred = (CompoundButton) findViewById(R.id.star_button);
+
+ mStarred.setFocusable(true);
+ mStarred.setClickable(true);
+
+ // Larger target triggers star toggle
+ final View starParent = findViewById(R.id.list_item_session);
+ FractionalTouchDelegate.setupDelegate(starParent, mStarred, new RectF(0.6f, 0f, 1f, 0.8f));
+
+ mAbstract = (TextView) findViewById(R.id.session_abstract);
+ mRequirements = (TextView) findViewById(R.id.session_requirements);
+
+ final Intent intent = getIntent();
+ mSessionUri = intent.getData();
+ mSessionId = Sessions.getSessionId(mSessionUri);
+
+ setupSummaryTab();
+ setupNotesTab();
+
+ // Start background queries to load session and track details
+ final Uri trackUri = resolveTrackUri(intent);
+ final Uri speakersUri = Sessions.buildSpeakersDirUri(mSessionId);
+
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ mHandler.startQuery(SessionsQuery._TOKEN, mSessionUri, SessionsQuery.PROJECTION);
+ mHandler.startQuery(TracksQuery._TOKEN, trackUri, TracksQuery.PROJECTION);
+ mHandler.startQuery(SpeakersQuery._TOKEN, speakersUri, SpeakersQuery.PROJECTION);
+ }
+
+ /** Build and add "summary" tab. */
+ private void setupSummaryTab() {
+ final TabHost host = getTabHost();
+
+ // Summary content comes from existing layout
+ host.addTab(host.newTabSpec(TAG_SUMMARY)
+ .setIndicator(buildIndicator(R.string.session_summary))
+ .setContent(R.id.tab_session_summary));
+ }
+
+ /** Build and add "notes" tab. */
+ private void setupNotesTab() {
+ final TabHost host = getTabHost();
+
+ final Uri notesUri = Sessions.buildNotesDirUri(mSessionId);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, notesUri);
+ intent.addCategory(Intent.CATEGORY_TAB);
+ intent.putExtra(NotesActivity.EXTRA_SHOW_INSERT, true);
+
+ // Notes content comes from reused activity
+ host.addTab(host.newTabSpec(TAG_NOTES)
+ .setIndicator(buildIndicator(R.string.session_notes))
+ .setContent(intent));
+ }
+
+ /** Build and add "moderator" tab. */
+ private void setupModeratorTab(Cursor sessionsCursor) {
+ final TabHost host = getTabHost();
+
+ // Insert Moderator when available
+ final View moderatorBlock = findViewById(R.id.moderator_block);
+ final String moderatorLink = sessionsCursor.getString(SessionsQuery.MODERATOR_LINK);
+ final boolean validModerator = !TextUtils.isEmpty(moderatorLink);
+ if (validModerator) {
+ mModeratorUri = Uri.parse(moderatorLink);
+
+ // Set link, but handle clicks manually
+ final TextView textView = (TextView) findViewById(R.id.moderator_link);
+ textView.setText(mModeratorUri.toString());
+ textView.setMovementMethod(null);
+ textView.setClickable(true);
+ textView.setFocusable(true);
+
+ // Start background fetch of moderator status
+ startModeratorStatusFetch(moderatorLink);
+
+ moderatorBlock.setVisibility(View.VISIBLE);
+ } else {
+ moderatorBlock.setVisibility(View.GONE);
+ }
+
+ // Insert Wave when available
+ final View waveBlock = findViewById(R.id.wave_block);
+ final String waveLink = sessionsCursor.getString(SessionsQuery.WAVE_LINK);
+ final boolean validWave = !TextUtils.isEmpty(waveLink);
+ if (validWave) {
+ // Rewrite incoming Wave URL to punch through user-agent check
+ mWaveUri = Uri.parse(waveLink).buildUpon()
+ .appendQueryParameter("nouacheck", "1").build();
+
+ // Set link, but handle clicks manually
+ final TextView textView = (TextView) findViewById(R.id.wave_link);
+ textView.setText(mWaveUri.toString());
+ textView.setMovementMethod(null);
+ textView.setClickable(true);
+ textView.setFocusable(true);
+
+ waveBlock.setVisibility(View.VISIBLE);
+ } else {
+ waveBlock.setVisibility(View.GONE);
+ }
+
+ if (validModerator || validWave) {
+ // Moderator content comes from existing layout
+ host.addTab(host.newTabSpec(TAG_MODERATOR)
+ .setIndicator(buildIndicator(R.string.session_interact))
+ .setContent(R.id.tab_session_moderator));
+ }
+ }
+
+ /** Handle Moderator link. */
+ public void onModeratorClick(View v) {
+ boolean appPresent = false;
+ try {
+ final PackageManager pm = getPackageManager();
+ final ApplicationInfo ai = pm.getApplicationInfo(MODERATOR_PACKAGE, 0);
+ if (ai != null) appPresent = true;
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Problem searching for moderator: " + e.toString());
+ }
+
+ if (appPresent) {
+ // Directly launch intent when already installed
+ startModerator();
+ } else {
+ // Otherwise suggest installing app from Market
+ showDialog(R.id.dialog_moderator);
+ }
+ }
+
+ /** Handle Wave link. */
+ public void onWaveClick(View v) {
+ showDialog(R.id.dialog_wave);
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case R.id.dialog_moderator: {
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.dialog_moderator_title)
+ .setIcon(android.R.drawable.ic_dialog_info)
+ .setMessage(R.string.dialog_moderator_message)
+ .setNegativeButton(R.string.dialog_moderator_market,
+ new ModeratorMarketClickListener())
+ .setPositiveButton(R.string.dialog_moderator_web,
+ new ModeratorStartClickListener())
+ .setCancelable(true)
+ .create();
+ }
+ case R.id.dialog_wave: {
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.dialog_wave_title)
+ .setIcon(android.R.drawable.ic_dialog_info)
+ .setMessage(R.string.dialog_wave_message)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, new WaveConfirmClickListener())
+ .setCancelable(true)
+ .create();
+ }
+ }
+ return null;
+ }
+
+ private class ModeratorStartClickListener implements DialogInterface.OnClickListener {
+ public void onClick(DialogInterface dialog, int which) {
+ startModerator();
+ }
+ }
+
+ private class ModeratorMarketClickListener implements DialogInterface.OnClickListener {
+ public void onClick(DialogInterface dialog, int which) {
+ final Uri marketUri = Uri.parse("http://market.android.com/search?q=pname:"
+ + MODERATOR_PACKAGE);
+ startActivity(new Intent(Intent.ACTION_VIEW, marketUri));
+ }
+ }
+
+ private class WaveConfirmClickListener implements DialogInterface.OnClickListener {
+ public void onClick(DialogInterface dialog, int which) {
+ startWave();
+ }
+ }
+
+ private void startModerator() {
+ startActivity(new Intent(Intent.ACTION_VIEW, mModeratorUri));
+ }
+
+ private void startWave() {
+ startActivity(new Intent(Intent.ACTION_VIEW, mWaveUri));
+ }
+
+ /**
+ * Build a {@link View} to be used as a tab indicator, setting the requested
+ * string resource as its label.
+ */
+ private View buildIndicator(int textRes) {
+ final TextView indicator = (TextView) getLayoutInflater().inflate(R.layout.tab_indicator,
+ getTabWidget(), false);
+ indicator.setText(textRes);
+ return indicator;
+ }
+
+ /**
+ * Derive {@link Tracks#CONTENT_ITEM_TYPE} {@link Uri} based on incoming
+ * {@link Intent}, using {@link #EXTRA_TRACK} when set.
+ */
+ private Uri resolveTrackUri(Intent intent) {
+ final Uri trackUri = intent.getParcelableExtra(EXTRA_TRACK);
+ if (trackUri != null) {
+ return trackUri;
+ } else {
+ return Sessions.buildTracksDirUri(mSessionId);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ if (token == SessionsQuery._TOKEN) {
+ onSessionQueryComplete(cursor);
+ } else if (token == TracksQuery._TOKEN) {
+ onTrackQueryComplete(cursor);
+ } else if (token == SpeakersQuery._TOKEN) {
+ onSpeakersQueryComplete(cursor);
+ } else {
+ cursor.close();
+ }
+ }
+
+ /** Handle {@link SessionsQuery} {@link Cursor}. */
+ private void onSessionQueryComplete(Cursor cursor) {
+ try {
+ mSessionCursor = true;
+ if (!cursor.moveToFirst()) return;
+
+ // Format time block this session occupies
+ final long blockStart = cursor.getLong(SessionsQuery.BLOCK_START);
+ final long blockEnd = cursor.getLong(SessionsQuery.BLOCK_END);
+ final String roomName = cursor.getString(SessionsQuery.ROOM_NAME);
+ final String subtitle = UIUtils.formatSessionSubtitle(blockStart,
+ blockEnd, roomName, this);
+
+ mTitleString = cursor.getString(SessionsQuery.TITLE);
+ mTitle.setText(mTitleString);
+ mSubtitle.setText(subtitle);
+
+ mHashtag = cursor.getString(SessionsQuery.HASHTAG);
+ if (TextUtils.isEmpty(mHashtag)) mHashtag = "";
+
+ mRoomId = cursor.getString(SessionsQuery.ROOM_ID);
+
+ // Unregister around setting checked state to avoid triggering
+ // listener since change isn't user generated.
+ mStarred.setOnCheckedChangeListener(null);
+ mStarred.setChecked(cursor.getInt(SessionsQuery.STARRED) != 0);
+ mStarred.setOnCheckedChangeListener(this);
+
+ final String sessionAbstract = cursor.getString(SessionsQuery.ABSTRACT);
+ if (!TextUtils.isEmpty(sessionAbstract)) {
+ UIUtils.setTextMaybeHtml(mAbstract, sessionAbstract);
+ mAbstract.setVisibility(View.VISIBLE);
+ mHasSummaryContent = true;
+ } else {
+ mAbstract.setVisibility(View.GONE);
+ }
+
+ final View requirementsBlock = findViewById(R.id.session_requirements_block);
+ final String sessionRequirements = cursor.getString(SessionsQuery.REQUIREMENTS);
+ if (!TextUtils.isEmpty(sessionRequirements)) {
+ UIUtils.setTextMaybeHtml(mRequirements, sessionRequirements);
+ requirementsBlock.setVisibility(View.VISIBLE);
+ mHasSummaryContent = true;
+ } else {
+ requirementsBlock.setVisibility(View.GONE);
+ }
+
+ setupModeratorTab(cursor);
+
+ // Show empty message when all data is loaded, and nothing to show
+ if (mSpeakersCursor && !mHasSummaryContent) {
+ findViewById(android.R.id.empty).setVisibility(View.VISIBLE);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /** Handle {@link TracksQuery} {@link Cursor}. */
+ private void onTrackQueryComplete(Cursor cursor) {
+ try {
+ if (!cursor.moveToFirst()) return;
+
+ // Use found track to build title-bar
+ ((TextView) findViewById(R.id.title_text)).setText(cursor
+ .getString(TracksQuery.TRACK_NAME));
+ UIUtils.setTitleBarColor(findViewById(R.id.title_container),
+ cursor.getInt(TracksQuery.TRACK_COLOR));
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private void onSpeakersQueryComplete(Cursor cursor) {
+ try {
+ mSpeakersCursor = true;
+
+ // TODO: remove any existing speakers from layout, since this cursor
+ // might be from a data change notification.
+ final ViewGroup speakersGroup = (ViewGroup) findViewById(R.id.session_speakers_block);
+ final LayoutInflater inflater = getLayoutInflater();
+
+ boolean hasSpeakers = false;
+
+ while (cursor.moveToNext()) {
+ final String speakerName = cursor.getString(SpeakersQuery.SPEAKER_NAME);
+ final String speakerCompany = cursor.getString(SpeakersQuery.SPEAKER_COMPANY);
+ if (TextUtils.isEmpty(speakerName)) continue;
+
+ final View speakerView = inflater.inflate(R.layout.speaker_detail,
+ speakersGroup, false);
+
+ final String speaker = getString(R.string.speaker_template, speakerName,
+ speakerCompany);
+ ((TextView) speakerView.findViewById(R.id.speaker_header)).setText(speaker);
+
+ final String speakerAbstract = cursor.getString(SpeakersQuery.SPEAKER_ABSTRACT);
+ final TextView abstractView = (TextView) speakerView
+ .findViewById(R.id.speaker_abstract);
+ UIUtils.setTextMaybeHtml(abstractView, speakerAbstract);
+
+ speakersGroup.addView(speakerView);
+ hasSpeakers = true;
+ mHasSummaryContent = true;
+ }
+
+ speakersGroup.setVisibility(hasSpeakers ? View.VISIBLE : View.GONE);
+
+ // Show empty message when all data is loaded, and nothing to show
+ if (mSessionCursor && !mHasSummaryContent) {
+ findViewById(android.R.id.empty).setVisibility(View.VISIBLE);
+ }
+
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "share" title-bar action. */
+ public void onShareClick(View v) {
+ // TODO: consider bringing in shortlink to session
+ final String shareString = getString(R.string.share_template, mTitleString, mHashtag);
+
+ final Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType("text/plain");
+ intent.putExtra(Intent.EXTRA_TEXT, shareString);
+
+ startActivity(Intent.createChooser(intent, getText(R.string.title_share)));
+ }
+
+ /** Handle "map" title-bar action. */
+ public void onMapClick(View v) {
+ final Intent intent = new Intent(this, MapActivity.class);
+ if (mRoomId != null && mRoomId.startsWith("officehours")) {
+ intent.putExtra(MapActivity.EXTRA_ROOM, MapActivity.OFFICE_HOURS_ROOM_ID);
+ } else {
+ intent.putExtra(MapActivity.EXTRA_ROOM, mRoomId);
+ }
+ startActivity(intent);
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** Handle toggling of starred checkbox. */
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ final ContentValues values = new ContentValues();
+ values.put(Vendors.STARRED, isChecked ? 1 : 0);
+ mHandler.startUpdate(mSessionUri, values);
+ }
+
+ private static HttpClient sHttpClient;
+
+ private static synchronized HttpClient getHttpClient(Context context) {
+ if (sHttpClient == null) {
+ sHttpClient = SyncService.getHttpClient(context);
+ }
+ return sHttpClient;
+ }
+
+ private void startModeratorStatusFetch(String moderatorLink) {
+ try {
+ // Extract series and topic from moderator link
+ // NOTE: this makes some ugly assumptions that the "t=" parameter
+ // occurs at the end of the URL.
+ final String clause = moderatorLink.substring(moderatorLink.indexOf("t=") + 2);
+
+ final int dotIndex = clause.indexOf('.');
+ final int seriesId = Integer.parseInt(clause.substring(0, dotIndex), 16);
+ final int topicId = Integer.parseInt(clause.substring(dotIndex + 1), 16);
+
+ // Kick off background request to find current status
+ final String remoteUrl = "http://www.googleapis.com/moderator/v1/series/" + seriesId
+ + "/topics/" + topicId;
+ new ModeratorStatusTask().execute(remoteUrl);
+
+ } catch (Exception e) {
+ Log.w(TAG, "Problem while parsing Moderator URL", e);
+ }
+ }
+
+ private class ModeratorStatusTask extends AsyncTask {
+ @Override
+ protected String doInBackground(String... params) {
+ final String param = params[0];
+
+ try {
+ final Context context = SessionDetailActivity.this;
+ final HttpClient httpClient = getHttpClient(context);
+ final HttpResponse resp = httpClient.execute(new HttpGet(param));
+ final HttpEntity entity = resp.getEntity();
+
+ final int statusCode = resp.getStatusLine().getStatusCode();
+ if (statusCode != HttpStatus.SC_OK || entity == null) return null;
+
+ final String respString = EntityUtils.toString(entity);
+ final JSONObject respJson = new JSONObject(respString);
+
+ final JSONObject data = respJson.getJSONObject("data");
+ final JSONObject counters = respJson.getJSONObject("counters");
+
+ final int questions = counters.getInt("submissions");
+ final int votes = counters.getInt("noteVotes") + counters.getInt("plusVotes")
+ + counters.getInt("minusVotes");
+
+ return getString(R.string.session_moderator_status, questions, votes);
+ } catch(Exception e) {
+ Log.w(TAG, "Problem while loading Moderator status: " + e.toString());
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(String result) {
+ final TextView status = (TextView) findViewById(R.id.moderator_status);
+ if (result == null) {
+ status.setVisibility(View.GONE);
+ } else {
+ status.setVisibility(View.VISIBLE);
+ status.setText(result);
+ }
+ }
+ }
+
+ /** {@link Sessions} query parameters. */
+ private interface SessionsQuery {
+ int _TOKEN = 0x1;
+
+ String[] PROJECTION = {
+ Blocks.BLOCK_START,
+ Blocks.BLOCK_END,
+ Sessions.TYPE,
+ Sessions.TITLE,
+ Sessions.ABSTRACT,
+ Sessions.REQUIREMENTS,
+ Sessions.STARRED,
+ Sessions.MODERATOR_URL,
+ Sessions.WAVE_URL,
+ Sessions.ROOM_ID,
+ Sessions.HASHTAG,
+ Rooms.ROOM_NAME,
+ };
+
+ int BLOCK_START = 0;
+ int BLOCK_END = 1;
+ int TYPE = 2;
+ int TITLE = 3;
+ int ABSTRACT = 4;
+ int REQUIREMENTS = 5;
+ int STARRED = 6;
+ int MODERATOR_LINK = 7;
+ int WAVE_LINK = 8;
+ int ROOM_ID = 9;
+ int HASHTAG = 10;
+ int ROOM_NAME = 11;
+ }
+
+ /** {@link Tracks} query parameters. */
+ private interface TracksQuery {
+ int _TOKEN = 0x2;
+
+ String[] PROJECTION = {
+ Tracks.TRACK_NAME,
+ Tracks.TRACK_COLOR,
+ };
+
+ int TRACK_NAME = 0;
+ int TRACK_COLOR = 1;
+ }
+
+ private interface SpeakersQuery {
+ int _TOKEN = 0x3;
+
+ String[] PROJECTION = {
+ Speakers.SPEAKER_NAME,
+ Speakers.SPEAKER_COMPANY,
+ Speakers.SPEAKER_ABSTRACT,
+ };
+
+ int SPEAKER_NAME = 0;
+ int SPEAKER_COMPANY = 1;
+ int SPEAKER_ABSTRACT = 2;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/SessionsActivity.java b/src/com/google/android/apps/iosched/ui/SessionsActivity.java
new file mode 100644
index 0000000..2004114
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/SessionsActivity.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import static com.google.android.apps.iosched.util.UIUtils.buildStyledSnippet;
+import static com.google.android.apps.iosched.util.UIUtils.formatSessionSubtitle;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.provider.BaseColumns;
+import android.text.Spannable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CursorAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * {@link ListActivity} that displays a set of {@link Sessions}, as requested
+ * through {@link Intent#getData()}.
+ */
+public class SessionsActivity extends ListActivity implements AsyncQueryListener {
+
+ private Uri mTrackUri;
+ private CursorAdapter mAdapter;
+
+ private NotifyingAsyncQueryHandler mHandler;
+ private Handler mMessageQueueHandler = new Handler();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (!getIntent().hasCategory(Intent.CATEGORY_TAB)) {
+ setContentView(R.layout.activity_sessions);
+
+ final String customTitle = getIntent().getStringExtra(Intent.EXTRA_TITLE);
+ ((TextView) findViewById(R.id.title_text)).setText(
+ customTitle != null ? customTitle : getTitle());
+
+ } else {
+ setContentView(R.layout.activity_sessions_content);
+ }
+
+ final Intent intent = getIntent();
+ final Uri sessionsUri = intent.getData();
+
+ String[] projection;
+ if (!Sessions.isSearchUri(sessionsUri)) {
+ mAdapter = new SessionsAdapter(this);
+ projection = SessionsQuery.PROJECTION;
+
+ } else {
+ mAdapter = new SearchAdapter(this);
+ projection = SearchQuery.PROJECTION;
+ }
+
+ setListAdapter(mAdapter);
+
+ // If caller launched us with specific track hint, pass it along when
+ // launching session details.
+ mTrackUri = intent.getParcelableExtra(SessionDetailActivity.EXTRA_TRACK);
+
+ // Start background query to load sessions
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ mHandler.startQuery(sessionsUri, projection, Sessions.DEFAULT_SORT);
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ startManagingCursor(cursor);
+ mAdapter.changeCursor(cursor);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mMessageQueueHandler.post(mRefreshSessionsRunnable);
+ }
+
+ @Override
+ protected void onPause() {
+ mMessageQueueHandler.removeCallbacks(mRefreshSessionsRunnable);
+ super.onPause();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ // Launch viewer for specific session, passing along any track knowledge
+ // that should influence the title-bar.
+ final Cursor cursor = (Cursor)mAdapter.getItem(position);
+ final String sessionId = cursor.getString(cursor.getColumnIndex(Sessions.SESSION_ID));
+ final Uri sessionUri = Sessions.buildSessionUri(sessionId);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, sessionUri);
+ intent.putExtra(SessionDetailActivity.EXTRA_TRACK, mTrackUri);
+ startActivity(intent);
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /**
+ * {@link CursorAdapter} that renders a {@link SessionsQuery}.
+ */
+ private class SessionsAdapter extends CursorAdapter {
+ public SessionsAdapter(Context context) {
+ super(context, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return getLayoutInflater().inflate(R.layout.list_item_session, parent, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ final TextView titleView = (TextView) view.findViewById(R.id.session_title);
+ final TextView subtitleView = (TextView) view.findViewById(R.id.session_subtitle);
+ final CheckBox starButton = (CheckBox) view.findViewById(R.id.star_button);
+
+ titleView.setText(cursor.getString(SessionsQuery.TITLE));
+
+ // Format time block this session occupies
+ final long blockStart = cursor.getLong(SessionsQuery.BLOCK_START);
+ final long blockEnd = cursor.getLong(SessionsQuery.BLOCK_END);
+ final String roomName = cursor.getString(SessionsQuery.ROOM_NAME);
+ final String subtitle = formatSessionSubtitle(blockStart, blockEnd, roomName, context);
+
+ subtitleView.setText(subtitle);
+
+ final boolean starred = cursor.getInt(SessionsQuery.STARRED) != 0;
+ starButton.setVisibility(starred ? View.VISIBLE : View.INVISIBLE);
+ starButton.setChecked(starred);
+
+ // Possibly indicate that the session has occurred in the past.
+ UIUtils.setSessionTitleColor(blockStart, blockEnd, titleView, subtitleView);
+ }
+ }
+
+ /**
+ * {@link CursorAdapter} that renders a {@link SearchQuery}.
+ */
+ private class SearchAdapter extends CursorAdapter {
+ public SearchAdapter(Context context) {
+ super(context, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return getLayoutInflater().inflate(R.layout.list_item_session, parent, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ ((TextView) view.findViewById(R.id.session_title)).setText(cursor
+ .getString(SearchQuery.TITLE));
+
+ final String snippet = cursor.getString(SearchQuery.SEARCH_SNIPPET);
+ final Spannable styledSnippet = buildStyledSnippet(snippet);
+ ((TextView) view.findViewById(R.id.session_subtitle)).setText(styledSnippet);
+
+ final boolean starred = cursor.getInt(SearchQuery.STARRED) != 0;
+ final CheckBox starButton = (CheckBox) view.findViewById(R.id.star_button);
+ starButton.setVisibility(starred ? View.VISIBLE : View.INVISIBLE);
+ starButton.setChecked(starred);
+ }
+ }
+
+ private Runnable mRefreshSessionsRunnable = new Runnable() {
+ public void run() {
+ if (mAdapter != null) {
+ // This is used to refresh session title colors.
+ mAdapter.notifyDataSetChanged();
+ }
+
+ // Check again on the next quarter hour, with some padding to account for network
+ // time differences.
+ long nextQuarterHour = (SystemClock.uptimeMillis() / 900000 + 1) * 900000 + 5000;
+ mMessageQueueHandler.postAtTime(mRefreshSessionsRunnable, nextQuarterHour);
+ }
+ };
+
+ /** {@link Sessions} query parameters. */
+ private interface SessionsQuery {
+ String[] PROJECTION = {
+ BaseColumns._ID,
+ Sessions.SESSION_ID,
+ Sessions.TITLE,
+ Sessions.STARRED,
+ Blocks.BLOCK_START,
+ Blocks.BLOCK_END,
+ Rooms.ROOM_NAME,
+ };
+
+ int _ID = 0;
+ int SESSION_ID = 1;
+ int TITLE = 2;
+ int STARRED = 3;
+ int BLOCK_START = 4;
+ int BLOCK_END = 5;
+ int ROOM_NAME = 6;
+ }
+
+ /** {@link Sessions} search query parameters. */
+ private interface SearchQuery {
+ String[] PROJECTION = {
+ BaseColumns._ID,
+ Sessions.SESSION_ID,
+ Sessions.TITLE,
+ Sessions.SEARCH_SNIPPET,
+ Sessions.STARRED,
+ };
+
+ int _ID = 0;
+ int SESSION_ID = 1;
+ int TITLE = 2;
+ int SEARCH_SNIPPET = 3;
+ int STARRED = 4;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/StarredActivity.java b/src/com/google/android/apps/iosched/ui/StarredActivity.java
new file mode 100644
index 0000000..4c33fa2
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/StarredActivity.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.util.UIUtils;
+
+import android.app.TabActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TabHost;
+import android.widget.TextView;
+
+public class StarredActivity extends TabActivity {
+
+ public static final String TAG_SESSIONS = "sessions";
+ public static final String TAG_VENDORS = "vendors";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_starred);
+
+ ((TextView) findViewById(R.id.title_text)).setText(getTitle());
+
+ setupSessionsTab();
+ setupVendorsTab();
+ }
+
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** Build and add "sessions" tab. */
+ private void setupSessionsTab() {
+ final TabHost host = getTabHost();
+
+ final Intent intent = new Intent(Intent.ACTION_VIEW, Sessions.CONTENT_STARRED_URI);
+ intent.addCategory(Intent.CATEGORY_TAB);
+
+ // Sessions content comes from reused activity
+ host.addTab(host.newTabSpec(TAG_SESSIONS)
+ .setIndicator(buildIndicator(R.string.starred_sessions))
+ .setContent(intent));
+ }
+
+ /** Build and add "vendors" tab. */
+ private void setupVendorsTab() {
+ final TabHost host = getTabHost();
+
+ final Intent intent = new Intent(Intent.ACTION_VIEW, Vendors.CONTENT_STARRED_URI);
+ intent.addCategory(Intent.CATEGORY_TAB);
+
+ // Vendors content comes from reused activity
+ host.addTab(host.newTabSpec(TAG_VENDORS)
+ .setIndicator(buildIndicator(R.string.starred_vendors))
+ .setContent(intent));
+ }
+
+ /**
+ * Build a {@link View} to be used as a tab indicator, setting the requested
+ * string resource as its label.
+ */
+ private View buildIndicator(int textRes) {
+ final TextView indicator = (TextView) getLayoutInflater().inflate(R.layout.tab_indicator,
+ getTabWidget(), false);
+ indicator.setText(textRes);
+ return indicator;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/TrackDetailActivity.java b/src/com/google/android/apps/iosched/ui/TrackDetailActivity.java
new file mode 100644
index 0000000..80d61bc
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/TrackDetailActivity.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.Sets;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.Activity;
+import android.app.TabActivity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TabHost;
+import android.widget.TextView;
+
+import java.util.HashSet;
+
+/**
+ * {@link Activity} that displays details about a specific
+ * {@link Tracks#TRACK_ID}, as requested through {@link Intent#getData()}.
+ */
+public class TrackDetailActivity extends TabActivity implements AsyncQueryListener {
+
+ public static final String EXTRA_FOCUS_TAG = "com.google.android.iosched.extra.FOCUS_TAG";
+
+ public static final String TAG_SUMMARY = "summary";
+ public static final String TAG_SESSIONS = "sessions";
+ public static final String TAG_VENDORS = "vendors";
+
+ /**
+ * List of specific {@link Tracks#TRACK_ID} that never have {@link Vendors},
+ * which is used to hide the {@link #TAG_VENDORS} tab instead of a query.
+ */
+ // TODO: instead of using this static list, query for Tracks.VENDORS_COUNT
+ private static HashSet sTracksWithoutVendors = Sets.newHashSet("firesidechats",
+ "techtalks");
+
+ private Uri mTrackUri;
+ private String mTrackId;
+
+ private TextView mTitleText;
+ private TextView mTrackAbstract;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_track_details);
+
+ mTrackUri = getIntent().getData();
+ mTrackId = Tracks.getTrackId(mTrackUri);
+
+ mTitleText = (TextView) findViewById(R.id.title_text);
+ mTrackAbstract = (TextView) findViewById(R.id.track_abstract);
+
+ setupSummaryTab();
+ setupSessionsTab();
+
+ // Only add vendors tab when applicable
+ final boolean hasVendors = !sTracksWithoutVendors.contains(mTrackId);
+ if (hasVendors) setupVendorsTab();
+
+ // Show specific focus tag when requested, otherwise default
+ String focusTag = getIntent().getStringExtra(EXTRA_FOCUS_TAG);
+ if (focusTag == null) focusTag = TAG_SESSIONS;
+
+ getTabHost().setCurrentTabByTag(focusTag);
+
+ // Start background query to load track details
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ mHandler.startQuery(mTrackUri, TracksQuery.PROJECTION);
+ }
+
+ /** Build and add "summary" tab. */
+ private void setupSummaryTab() {
+ final TabHost host = getTabHost();
+
+ // Summary content comes from existing layout
+ host.addTab(host.newTabSpec(TAG_SUMMARY)
+ .setIndicator(buildIndicator(R.string.track_summary))
+ .setContent(R.id.tab_track_summary));
+ }
+
+ /** Build and add "sessions" tab. */
+ private void setupSessionsTab() {
+ final TabHost host = getTabHost();
+
+ final Uri trackUri = getIntent().getData();
+ final String trackId = Tracks.getTrackId(trackUri);
+ final Uri sessionsUri = Tracks.buildSessionsUri(trackId);
+
+ final Intent intent = new Intent(Intent.ACTION_VIEW, sessionsUri);
+ intent.putExtra(SessionDetailActivity.EXTRA_TRACK, trackUri);
+ intent.addCategory(Intent.CATEGORY_TAB);
+
+ // Sessions content comes from reused activity
+ host.addTab(host.newTabSpec(TAG_SESSIONS)
+ .setIndicator(buildIndicator(R.string.track_sessions))
+ .setContent(intent));
+ }
+
+ /** Build and add "vendors" tab. */
+ private void setupVendorsTab() {
+ final TabHost host = getTabHost();
+ final Uri vendorsUri = Tracks.buildVendorsUri(mTrackId);
+
+ final Intent intent = new Intent(Intent.ACTION_VIEW, vendorsUri);
+ intent.putExtra(SessionDetailActivity.EXTRA_TRACK, mTrackUri);
+ intent.addCategory(Intent.CATEGORY_TAB);
+
+ // Vendors content comes from reused activity
+ host.addTab(host.newTabSpec(TAG_VENDORS)
+ .setIndicator(buildIndicator(R.string.track_vendors))
+ .setContent(intent));
+ }
+
+ /**
+ * Build a {@link View} to be used as a tab indicator, setting the requested
+ * string resource as its label.
+ */
+ private View buildIndicator(int textRes) {
+ final TextView indicator = (TextView) getLayoutInflater().inflate(R.layout.tab_indicator,
+ getTabWidget(), false);
+ indicator.setText(textRes);
+ return indicator;
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ try {
+ if (!cursor.moveToFirst()) return;
+ mTitleText.setText(cursor.getString(TracksQuery.TRACK_NAME));
+ mTrackAbstract.setText(cursor.getString(TracksQuery.TRACK_ABSTRACT));
+ UIUtils.setTitleBarColor(findViewById(R.id.title_container),
+ cursor.getInt(TracksQuery.TRACK_COLOR));
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** {@link Tracks} query parameters. */
+ private interface TracksQuery {
+ String[] PROJECTION = {
+ Tracks.TRACK_NAME,
+ Tracks.TRACK_COLOR,
+ Tracks.TRACK_ABSTRACT,
+ };
+
+ int TRACK_NAME = 0;
+ int TRACK_COLOR = 1;
+ int TRACK_ABSTRACT = 2;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/TracksActivity.java b/src/com/google/android/apps/iosched/ui/TracksActivity.java
new file mode 100644
index 0000000..333e051
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/TracksActivity.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.LayerDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.BaseColumns;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CursorAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * {@link ListActivity} that displays a set of {@link Tracks}, as requested
+ * through {@link Intent#getData()}.
+ */
+public class TracksActivity extends ListActivity implements AsyncQueryListener {
+
+ private String mFocusTag;
+ private TracksAdapter mAdapter;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_tracks);
+
+ final String customTitle = getIntent().getStringExtra(Intent.EXTRA_TITLE);
+ ((TextView) findViewById(R.id.title_text)).setText(
+ customTitle != null ? customTitle : getTitle());
+
+ mAdapter = new TracksAdapter(this);
+ setListAdapter(mAdapter);
+
+ final Intent intent = getIntent();
+ final Uri tracksUri = intent.getData();
+
+ mFocusTag = intent.getStringExtra(TrackDetailActivity.EXTRA_FOCUS_TAG);
+
+ // Filter our tracks query to only include those with valid results
+ String[] projection = TracksQuery.PROJECTION;
+ String selection = null;
+ if (TrackDetailActivity.TAG_SESSIONS.equals(mFocusTag)) {
+ // Only show tracks with at least one session
+ projection = TracksQuery.PROJECTION_WITH_SESSIONS_COUNT;
+ selection = Tracks.SESSIONS_COUNT + ">0";
+
+ } else if (TrackDetailActivity.TAG_VENDORS.equals(mFocusTag)) {
+ // Only show tracks with at least one vendor
+ projection = TracksQuery.PROJECTION_WITH_VENDORS_COUNT;
+ selection = Tracks.VENDORS_COUNT + ">0";
+ }
+
+ // Start background query to load tracks
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ mHandler.startQuery(tracksUri, projection, selection, null, Tracks.DEFAULT_SORT);
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ startManagingCursor(cursor);
+ mAdapter.changeCursor(cursor);
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "refresh" title-bar action. */
+ public void onRefreshClick(View v) {
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ // Launch viewer for specific track
+ final Cursor cursor = (Cursor)mAdapter.getItem(position);
+ final String trackId = cursor.getString(TracksQuery.TRACK_ID);
+ final Uri trackUri = Tracks.buildTrackUri(trackId);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, trackUri);
+
+ // Pass along any request for focus on a specific tag
+ intent.putExtra(TrackDetailActivity.EXTRA_FOCUS_TAG, mFocusTag);
+
+ startActivity(intent);
+ }
+
+ /**
+ * {@link CursorAdapter} that renders a {@link TracksQuery}.
+ */
+ private class TracksAdapter extends CursorAdapter {
+ public TracksAdapter(Context context) {
+ super(context, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return getLayoutInflater().inflate(R.layout.list_item_track, parent, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ final TextView textView = (TextView) view.findViewById(android.R.id.text1);
+ textView.setText(cursor.getString(TracksQuery.TRACK_NAME));
+
+ // Assign track color to visible block
+ final View iconView = view.findViewById(android.R.id.icon1);
+ LayerDrawable iconDrawable = (LayerDrawable) iconView.getBackground();
+ iconDrawable.getDrawable(0).setColorFilter(
+ cursor.getInt(TracksQuery.TRACK_COLOR), PorterDuff.Mode.SRC_ATOP);
+ }
+ }
+
+ /** {@link Tracks} query parameters. */
+ private interface TracksQuery {
+ String[] PROJECTION = {
+ BaseColumns._ID,
+ Tracks.TRACK_ID,
+ Tracks.TRACK_NAME,
+ Tracks.TRACK_COLOR,
+ };
+
+ String[] PROJECTION_WITH_SESSIONS_COUNT = {
+ BaseColumns._ID,
+ Tracks.TRACK_ID,
+ Tracks.TRACK_NAME,
+ Tracks.TRACK_COLOR,
+ Tracks.SESSIONS_COUNT,
+ };
+
+ String[] PROJECTION_WITH_VENDORS_COUNT = {
+ BaseColumns._ID,
+ Tracks.TRACK_ID,
+ Tracks.TRACK_NAME,
+ Tracks.TRACK_COLOR,
+ Tracks.VENDORS_COUNT,
+ };
+
+ int _ID = 0;
+ int TRACK_ID = 1;
+ int TRACK_NAME = 2;
+ int TRACK_COLOR = 3;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/VendorDetailActivity.java b/src/com/google/android/apps/iosched/ui/VendorDetailActivity.java
new file mode 100644
index 0000000..62512f9
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/VendorDetailActivity.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.service.SyncService;
+import com.google.android.apps.iosched.util.FractionalTouchDelegate;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.ParserUtils;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.util.EntityUtils;
+
+import android.app.Activity;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.RectF;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+
+/**
+ * {@link Activity} that displays details about a specific
+ * {@link Vendors#VENDOR_ID}, as requested through {@link Intent#getData()}.
+ */
+public class VendorDetailActivity extends Activity implements AsyncQueryListener,
+ OnCheckedChangeListener {
+ private static final String TAG = "VendorDetailActivity";
+
+ private Uri mVendorUri;
+
+ private String mTrackId;
+
+ private TextView mName;
+ private TextView mLocation;
+ private CompoundButton mStarred;
+
+ private ImageView mLogo;
+ private TextView mUrl;
+ private TextView mDesc;
+ private TextView mProductDesc;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ public static final String EXTRA_TRACK = "com.google.android.iosched.extra.TRACK";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_vendor_detail);
+
+ mName = (TextView) findViewById(R.id.vendor_name);
+ mLocation = (TextView) findViewById(R.id.vendor_location);
+ mStarred = (CompoundButton) findViewById(R.id.star_button);
+
+ mStarred.setFocusable(true);
+ mStarred.setClickable(true);
+
+ // Larger target triggers star toggle
+ final View starParent = findViewById(R.id.list_item_vendor);
+ FractionalTouchDelegate.setupDelegate(starParent, mStarred, new RectF(0.6f, 0f, 1f, 0.8f));
+
+ mLogo = (ImageView) findViewById(R.id.vendor_logo);
+ mUrl = (TextView) findViewById(R.id.vendor_url);
+ mDesc = (TextView) findViewById(R.id.vendor_desc);
+ mProductDesc = (TextView) findViewById(R.id.vendor_product_desc);
+
+ final Intent intent = getIntent();
+ mVendorUri = intent.getData();
+
+ // Start background query to load vendor details
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ mHandler.startQuery(mVendorUri, VendorsQuery.PROJECTION);
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ try {
+ if (!cursor.moveToFirst()) return;
+
+ mName.setText(cursor.getString(VendorsQuery.NAME));
+ mLocation.setText(cursor.getString(VendorsQuery.LOCATION));
+
+ // Unregister around setting checked state to avoid triggering
+ // listener since change isn't user generated.
+ mStarred.setOnCheckedChangeListener(null);
+ mStarred.setChecked(cursor.getInt(VendorsQuery.STARRED) != 0);
+ mStarred.setOnCheckedChangeListener(this);
+
+ // Start background fetch to load vendor logo
+ final String logoUrl = cursor.getString(VendorsQuery.LOGO_URL);
+ new VendorLogoTask().execute(logoUrl);
+
+ mUrl.setText(cursor.getString(VendorsQuery.URL));
+ mDesc.setText(cursor.getString(VendorsQuery.DESC));
+ mProductDesc.setText(cursor.getString(VendorsQuery.PRODUCT_DESC));
+
+ mTrackId = cursor.getString(VendorsQuery.TRACK_ID);
+
+ // Assign track details when found
+ // TODO: handle vendors not attached to track
+ ((TextView) findViewById(R.id.title_text)).setText(cursor
+ .getString(VendorsQuery.TRACK_NAME));
+ UIUtils.setTitleBarColor(findViewById(R.id.title_container),
+ cursor.getInt(VendorsQuery.TRACK_COLOR));
+
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** Handle "map" title-bar action. */
+ public void onMapClick(View v) {
+ // The room ID for the sandbox, in the map, is just the track ID
+ final Intent intent = new Intent(this, MapActivity.class);
+ intent.putExtra(MapActivity.EXTRA_ROOM, ParserUtils.translateTrackIdAliasInverse(mTrackId));
+ startActivity(intent);
+ }
+
+ /** Handle toggling of starred checkbox. */
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ final ContentValues values = new ContentValues();
+ values.put(Vendors.STARRED, isChecked ? 1 : 0);
+ mHandler.startUpdate(mVendorUri, values);
+ }
+
+ private static HttpClient sHttpClient;
+
+ private static synchronized HttpClient getHttpClient(Context context) {
+ if (sHttpClient == null) {
+ sHttpClient = SyncService.getHttpClient(context);
+ }
+ return sHttpClient;
+ }
+
+ private class VendorLogoTask extends AsyncTask {
+ @Override
+ protected Bitmap doInBackground(String... params) {
+ final String param = params[0];
+
+ try {
+ final Context context = VendorDetailActivity.this;
+ final HttpClient httpClient = getHttpClient(context);
+ final HttpResponse resp = httpClient.execute(new HttpGet(param));
+ final HttpEntity entity = resp.getEntity();
+
+ final int statusCode = resp.getStatusLine().getStatusCode();
+ if (statusCode != HttpStatus.SC_OK || entity == null) return null;
+
+ final byte[] respBytes = EntityUtils.toByteArray(entity);
+ return BitmapFactory.decodeByteArray(respBytes, 0, respBytes.length);
+
+ } catch(Exception e) {
+ Log.w(TAG, "Problem while loading vendor logo: " + e.toString());
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap result) {
+ if (result == null) {
+ mLogo.setVisibility(View.GONE);
+ } else {
+ mLogo.setVisibility(View.VISIBLE);
+ mLogo.setImageBitmap(result);
+ }
+ }
+ }
+
+ /** {@link Vendors} query parameters. */
+ private interface VendorsQuery {
+ String[] PROJECTION = {
+ Vendors.NAME,
+ Vendors.LOCATION,
+ Vendors.DESC,
+ Vendors.URL,
+ Vendors.PRODUCT_DESC,
+ Vendors.LOGO_URL,
+ Vendors.STARRED,
+ Vendors.TRACK_ID,
+ Tracks.TRACK_NAME,
+ Tracks.TRACK_COLOR,
+ };
+
+ int NAME = 0;
+ int LOCATION = 1;
+ int DESC = 2;
+ int URL = 3;
+ int PRODUCT_DESC = 4;
+ int LOGO_URL = 5;
+ int STARRED = 6;
+ int TRACK_ID = 7;
+ int TRACK_NAME = 8;
+ int TRACK_COLOR = 9;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/VendorsActivity.java b/src/com/google/android/apps/iosched/ui/VendorsActivity.java
new file mode 100644
index 0000000..8c94d51
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/VendorsActivity.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui;
+
+import static com.google.android.apps.iosched.util.UIUtils.buildStyledSnippet;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Vendors;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler;
+import com.google.android.apps.iosched.util.UIUtils;
+import com.google.android.apps.iosched.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.BaseColumns;
+import android.text.Spannable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CursorAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * {@link ListActivity} that displays a set of {@link Vendors}, as requested
+ * through {@link Intent#getData()}.
+ */
+public class VendorsActivity extends ListActivity implements AsyncQueryListener {
+
+ private CursorAdapter mAdapter;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (!getIntent().hasCategory(Intent.CATEGORY_TAB)) {
+ setContentView(R.layout.activity_vendors);
+ ((TextView) findViewById(R.id.title_text)).setText(getTitle());
+
+ } else {
+ setContentView(R.layout.activity_vendors_content);
+ }
+
+ final Uri vendorsUri = getIntent().getData();
+
+ String[] projection;
+ if (!Vendors.isSearchUri(vendorsUri)) {
+ mAdapter = new VendorsAdapter(this);
+ projection = VendorsQuery.PROJECTION;
+
+ } else {
+ mAdapter = new SearchAdapter(this);
+ projection = SearchQuery.PROJECTION;
+ }
+
+ setListAdapter(mAdapter);
+
+ // Start background query to load vendors
+ mHandler = new NotifyingAsyncQueryHandler(getContentResolver(), this);
+ mHandler.startQuery(vendorsUri, projection, Vendors.DEFAULT_SORT);
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ startManagingCursor(cursor);
+ mAdapter.changeCursor(cursor);
+ }
+
+ /** Handle "home" title-bar action. */
+ public void onHomeClick(View v) {
+ UIUtils.goHome(this);
+ }
+
+ /** Handle "search" title-bar action. */
+ public void onSearchClick(View v) {
+ UIUtils.goSearch(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ // Launch viewer for specific vendor
+ final Cursor cursor = (Cursor)mAdapter.getItem(position);
+ final String vendorId = cursor.getString(VendorsQuery.VENDOR_ID);
+ final Uri vendorUri = Vendors.buildVendorUri(vendorId);
+ startActivity(new Intent(Intent.ACTION_VIEW, vendorUri));
+ }
+
+ /**
+ * {@link CursorAdapter} that renders a {@link VendorsQuery}.
+ */
+ private class VendorsAdapter extends CursorAdapter {
+ public VendorsAdapter(Context context) {
+ super(context, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return getLayoutInflater().inflate(R.layout.list_item_vendor, parent, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ ((TextView) view.findViewById(R.id.vendor_name)).setText(cursor
+ .getString(VendorsQuery.NAME));
+ ((TextView) view.findViewById(R.id.vendor_location)).setText(cursor
+ .getString(VendorsQuery.LOCATION));
+
+ final boolean starred = cursor.getInt(VendorsQuery.STARRED) != 0;
+ final CheckBox starButton = (CheckBox) view.findViewById(R.id.star_button);
+ starButton.setVisibility(starred ? View.VISIBLE : View.INVISIBLE);
+ starButton.setChecked(starred);
+ }
+ }
+
+ /**
+ * {@link CursorAdapter} that renders a {@link SearchQuery}.
+ */
+ private class SearchAdapter extends CursorAdapter {
+ public SearchAdapter(Context context) {
+ super(context, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return getLayoutInflater().inflate(R.layout.list_item_vendor, parent, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ ((TextView) view.findViewById(R.id.vendor_name)).setText(cursor
+ .getString(SearchQuery.NAME));
+
+ final String snippet = cursor.getString(SearchQuery.SEARCH_SNIPPET);
+ final Spannable styledSnippet = buildStyledSnippet(snippet);
+ ((TextView) view.findViewById(R.id.vendor_location)).setText(styledSnippet);
+
+ final boolean starred = cursor.getInt(VendorsQuery.STARRED) != 0;
+ final CheckBox starButton = (CheckBox) view.findViewById(R.id.star_button);
+ starButton.setVisibility(starred ? View.VISIBLE : View.INVISIBLE);
+ starButton.setChecked(starred);
+ }
+ }
+
+ /** {@link Vendors} query parameters. */
+ private interface VendorsQuery {
+ String[] PROJECTION = {
+ BaseColumns._ID,
+ Vendors.VENDOR_ID,
+ Vendors.NAME,
+ Vendors.LOCATION,
+ Vendors.STARRED,
+ };
+
+ int _ID = 0;
+ int VENDOR_ID = 1;
+ int NAME = 2;
+ int LOCATION = 3;
+ int STARRED = 4;
+ }
+
+ /** {@link Vendors} search query parameters. */
+ private interface SearchQuery {
+ String[] PROJECTION = {
+ BaseColumns._ID,
+ Vendors.VENDOR_ID,
+ Vendors.NAME,
+ Vendors.SEARCH_SNIPPET,
+ Vendors.STARRED,
+ };
+
+ int _ID = 0;
+ int VENDOR_ID = 1;
+ int NAME = 2;
+ int SEARCH_SNIPPET = 3;
+ int STARRED = 4;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/widget/.svn/all-wcprops b/src/com/google/android/apps/iosched/ui/widget/.svn/all-wcprops
new file mode 100644
index 0000000..a7cf02a
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/widget/.svn/all-wcprops
@@ -0,0 +1,23 @@
+K 25
+svn:wc:ra_dav:version-url
+V 67
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/widget
+END
+TimeRulerView.java
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/widget/TimeRulerView.java
+END
+BlockView.java
+K 25
+svn:wc:ra_dav:version-url
+V 82
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/widget/BlockView.java
+END
+BlocksLayout.java
+K 25
+svn:wc:ra_dav:version-url
+V 85
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/ui/widget/BlocksLayout.java
+END
diff --git a/src/com/google/android/apps/iosched/ui/widget/.svn/entries b/src/com/google/android/apps/iosched/ui/widget/.svn/entries
new file mode 100644
index 0000000..6ae0eff
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/widget/.svn/entries
@@ -0,0 +1,130 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/src/com/google/android/apps/iosched/ui/widget
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+TimeRulerView.java
+file
+
+
+
+
+2010-06-03T15:23:15.441623Z
+77d1fdf732451f99f088bca1b33ab774
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5990
+
+BlockView.java
+file
+
+
+
+
+2010-06-03T15:23:15.441623Z
+817e2c43c22d99a5d5e589fb4d49bc86
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3117
+
+BlocksLayout.java
+file
+
+
+
+
+2010-06-03T15:23:15.441623Z
+1a8f166d6f8c6644d54a063e6e41c958
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4609
+
diff --git a/src/com/google/android/apps/iosched/ui/widget/.svn/text-base/BlockView.java.svn-base b/src/com/google/android/apps/iosched/ui/widget/.svn/text-base/BlockView.java.svn-base
new file mode 100644
index 0000000..5c78c18
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/widget/.svn/text-base/BlockView.java.svn-base
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui.widget;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.LayerDrawable;
+import android.widget.Button;
+
+/**
+ * Custom view that represents a {@link Blocks#BLOCK_ID} instance, including its
+ * title and time span that it occupies. Usually organized automatically by
+ * {@link BlocksLayout} to match up against a {@link TimeRulerView} instance.
+ */
+public class BlockView extends Button {
+ private final String mBlockId;
+ private final String mTitle;
+ private final long mStartTime;
+ private final long mEndTime;
+ private final boolean mContainsStarred;
+ private final int mColumn;
+
+ public BlockView(Context context, String blockId, String title, long startTime,
+ long endTime, boolean containsStarred, int column) {
+ super(context);
+
+ mBlockId = blockId;
+ mTitle = title;
+ mStartTime = startTime;
+ mEndTime = endTime;
+ mContainsStarred = containsStarred;
+ mColumn = column;
+
+ setText(mTitle);
+
+ // TODO: turn into color state list with layers?
+ int textColor = -1;
+ int accentColor = -1;
+ switch (mColumn) {
+ case 0:
+ // blue
+ textColor = Color.WHITE;
+ accentColor = Color.parseColor("#18b6e6");
+ break;
+ case 1:
+ // red
+ textColor = Color.WHITE;
+ accentColor = Color.parseColor("#df1831");
+ break;
+ case 2:
+ // green
+ textColor = Color.WHITE;
+ accentColor = Color.parseColor("#00a549");
+ break;
+ }
+
+ LayerDrawable buttonDrawable = (LayerDrawable)
+ context.getResources().getDrawable(R.drawable.btn_block);
+ buttonDrawable.getDrawable(0).setColorFilter(accentColor, PorterDuff.Mode.SRC_ATOP);
+ buttonDrawable.getDrawable(1).setAlpha(mContainsStarred ? 255 : 0);
+
+ setTextColor(textColor);
+ setBackgroundDrawable(buttonDrawable);
+ }
+
+ public String getBlockId() {
+ return mBlockId;
+ }
+
+ public long getStartTime() {
+ return mStartTime;
+ }
+
+ public long getEndTime() {
+ return mEndTime;
+ }
+
+ public int getColumn() {
+ return mColumn;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/widget/.svn/text-base/BlocksLayout.java.svn-base b/src/com/google/android/apps/iosched/ui/widget/.svn/text-base/BlocksLayout.java.svn-base
new file mode 100644
index 0000000..dee6913
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/widget/.svn/text-base/BlocksLayout.java.svn-base
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui.widget;
+
+import com.google.android.apps.iosched.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Custom layout that contains and organizes a {@link TimeRulerView} and several
+ * instances of {@link BlockView}. Also positions current "now" divider using
+ * {@link R.id#blocks_now} view when applicable.
+ */
+public class BlocksLayout extends ViewGroup {
+ private int mColumns = 3;
+
+ private TimeRulerView mRulerView;
+ private View mNowView;
+
+ public BlocksLayout(Context context) {
+ this(context, null);
+ }
+
+ public BlocksLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public BlocksLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.BlocksLayout, defStyle, 0);
+
+ mColumns = a.getInt(R.styleable.TimeRulerView_headerWidth, mColumns);
+
+ a.recycle();
+ }
+
+ private void ensureChildren() {
+ mRulerView = (TimeRulerView) findViewById(R.id.blocks_ruler);
+ mRulerView.setDrawingCacheEnabled(true);
+ if (mRulerView == null) {
+ throw new IllegalStateException("Must include a R.id.blocks_ruler view.");
+ }
+
+ mNowView = findViewById(R.id.blocks_now);
+ mNowView.setDrawingCacheEnabled(true);
+ if (mNowView == null) {
+ throw new IllegalStateException("Must include a R.id.blocks_now view.");
+ }
+ }
+
+ /**
+ * Remove any {@link BlockView} instances, leaving only
+ * {@link TimeRulerView} remaining.
+ */
+ public void removeAllBlocks() {
+ ensureChildren();
+ removeAllViews();
+ addView(mRulerView);
+ addView(mNowView);
+ }
+
+ public void addBlock(BlockView blockView) {
+ blockView.setDrawingCacheEnabled(true);
+ addView(blockView, 1);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ ensureChildren();
+
+ mRulerView.measure(widthMeasureSpec, heightMeasureSpec);
+ mNowView.measure(widthMeasureSpec, heightMeasureSpec);
+
+ final int width = mRulerView.getMeasuredWidth();
+ final int height = mRulerView.getMeasuredHeight();
+
+ setMeasuredDimension(resolveSize(width, widthMeasureSpec),
+ resolveSize(height, heightMeasureSpec));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ ensureChildren();
+
+ final TimeRulerView rulerView = mRulerView;
+ final int headerWidth = rulerView.getHeaderWidth();
+ final int columnWidth = (getWidth() - headerWidth) / mColumns;
+
+ rulerView.layout(0, 0, getWidth(), getHeight());
+
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) continue;
+
+ if (child instanceof BlockView) {
+ final BlockView blockView = (BlockView) child;
+ final int top = rulerView.getTimeVerticalOffset(blockView.getStartTime());
+ final int bottom = rulerView.getTimeVerticalOffset(blockView.getEndTime());
+ final int left = headerWidth + (blockView.getColumn() * columnWidth);
+ final int right = left + columnWidth;
+ child.layout(left, top, right, bottom);
+ }
+ }
+
+ // Align now view to match current time
+ final View nowView = mNowView;
+ final long now = System.currentTimeMillis();
+
+ final int top = rulerView.getTimeVerticalOffset(now);
+ final int bottom = top + nowView.getMeasuredHeight();
+ final int left = 0;
+ final int right = getWidth();
+
+ nowView.layout(left, top, right, bottom);
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/widget/.svn/text-base/TimeRulerView.java.svn-base b/src/com/google/android/apps/iosched/ui/widget/.svn/text-base/TimeRulerView.java.svn-base
new file mode 100644
index 0000000..912ca7e
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/widget/.svn/text-base/TimeRulerView.java.svn-base
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui.widget;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.util.UIUtils;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.graphics.Paint.FontMetricsInt;
+import android.graphics.Paint.Style;
+import android.text.format.Time;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Custom view that draws a vertical time "ruler" representing the chronological
+ * progression of a single day. Usually shown along with {@link BlockView}
+ * instances to give a spatial sense of time.
+ */
+public class TimeRulerView extends View {
+
+ private int mHeaderWidth = 70;
+ private int mHourHeight = 90;
+ private boolean mHorizontalDivider = true;
+ private int mLabelTextSize = 20;
+ private int mLabelPaddingLeft = 8;
+ private int mLabelColor = Color.BLACK;
+ private int mDividerColor = Color.LTGRAY;
+ private int mStartHour = 0;
+ private int mEndHour = 23;
+
+ public TimeRulerView(Context context) {
+ this(context, null);
+ }
+
+ public TimeRulerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TimeRulerView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TimeRulerView,
+ defStyle, 0);
+
+ mHeaderWidth = a.getDimensionPixelSize(R.styleable.TimeRulerView_headerWidth,
+ mHeaderWidth);
+ mHourHeight = a
+ .getDimensionPixelSize(R.styleable.TimeRulerView_hourHeight, mHourHeight);
+ mHorizontalDivider = a.getBoolean(R.styleable.TimeRulerView_horizontalDivider,
+ mHorizontalDivider);
+ mLabelTextSize = a.getDimensionPixelSize(R.styleable.TimeRulerView_labelTextSize,
+ mLabelTextSize);
+ mLabelPaddingLeft = a.getDimensionPixelSize(R.styleable.TimeRulerView_labelPaddingLeft,
+ mLabelPaddingLeft);
+ mLabelColor = a.getColor(R.styleable.TimeRulerView_labelColor, mLabelColor);
+ mDividerColor = a.getColor(R.styleable.TimeRulerView_dividerColor, mDividerColor);
+ mStartHour = a.getInt(R.styleable.TimeRulerView_startHour, mStartHour);
+ mEndHour = a.getInt(R.styleable.TimeRulerView_endHour, mEndHour);
+
+ a.recycle();
+ }
+
+ /**
+ * Return the vertical offset (in pixels) for a requested time (in
+ * milliseconds since epoch).
+ */
+ public int getTimeVerticalOffset(long timeMillis) {
+ Time time = new Time(UIUtils.CONFERENCE_TIME_ZONE.getID());
+ time.set(timeMillis);
+
+ final int minutes = ((time.hour - mStartHour) * 60) + time.minute;
+ return (minutes * mHourHeight) / 60;
+ }
+
+ @Override
+ protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int hours = mEndHour - mStartHour;
+
+ int width = mHeaderWidth;
+ int height = mHourHeight * hours;
+
+ setMeasuredDimension(resolveSize(width, widthMeasureSpec),
+ resolveSize(height, heightMeasureSpec));
+ }
+
+ private Paint mDividerPaint = new Paint();
+ private Paint mLabelPaint = new Paint();
+
+ @Override
+ protected synchronized void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ final int hourHeight = mHourHeight;
+
+ final Paint dividerPaint = mDividerPaint;
+ dividerPaint.setColor(mDividerColor);
+ dividerPaint.setStyle(Style.FILL);
+
+ final Paint labelPaint = mLabelPaint;
+ labelPaint.setColor(mLabelColor);
+ labelPaint.setTextSize(mLabelTextSize);
+ labelPaint.setTypeface(Typeface.DEFAULT_BOLD);
+ labelPaint.setAntiAlias(true);
+
+ final FontMetricsInt metrics = labelPaint.getFontMetricsInt();
+ final int labelHeight = Math.abs(metrics.ascent);
+ final int labelOffset = labelHeight + ((hourHeight - labelHeight) / 2);
+
+ final int right = getRight();
+
+ // Walk left side of canvas drawing timestamps
+ final int hours = mEndHour - mStartHour;
+ for (int i = 0; i < hours; i++) {
+ final int dividerY = hourHeight * i;
+ final int nextDividerY = hourHeight * (i + 1);
+ canvas.drawLine(0, dividerY, right, dividerY, dividerPaint);
+
+ // draw text title for timestamp
+ canvas.drawRect(0, dividerY, mHeaderWidth, nextDividerY, dividerPaint);
+
+ // TODO: localize these labels better, including handling
+ // 24-hour mode when set in framework.
+ final int hour = mStartHour + i;
+ String label;
+ if (hour == 0) {
+ label = "12am";
+ } else if (hour <= 11) {
+ label = hour + "am";
+ } else if (hour == 12) {
+ label = "12pm";
+ } else {
+ label = (hour - 12) + "pm";
+ }
+
+ final float labelWidth = labelPaint.measureText(label);
+
+ canvas.drawText(label, 0, label.length(), mHeaderWidth - labelWidth
+ - mLabelPaddingLeft, dividerY + labelOffset, labelPaint);
+ }
+ }
+
+ public int getHeaderWidth() {
+ return mHeaderWidth;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/widget/BlockView.java b/src/com/google/android/apps/iosched/ui/widget/BlockView.java
new file mode 100644
index 0000000..5c78c18
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/widget/BlockView.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui.widget;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.LayerDrawable;
+import android.widget.Button;
+
+/**
+ * Custom view that represents a {@link Blocks#BLOCK_ID} instance, including its
+ * title and time span that it occupies. Usually organized automatically by
+ * {@link BlocksLayout} to match up against a {@link TimeRulerView} instance.
+ */
+public class BlockView extends Button {
+ private final String mBlockId;
+ private final String mTitle;
+ private final long mStartTime;
+ private final long mEndTime;
+ private final boolean mContainsStarred;
+ private final int mColumn;
+
+ public BlockView(Context context, String blockId, String title, long startTime,
+ long endTime, boolean containsStarred, int column) {
+ super(context);
+
+ mBlockId = blockId;
+ mTitle = title;
+ mStartTime = startTime;
+ mEndTime = endTime;
+ mContainsStarred = containsStarred;
+ mColumn = column;
+
+ setText(mTitle);
+
+ // TODO: turn into color state list with layers?
+ int textColor = -1;
+ int accentColor = -1;
+ switch (mColumn) {
+ case 0:
+ // blue
+ textColor = Color.WHITE;
+ accentColor = Color.parseColor("#18b6e6");
+ break;
+ case 1:
+ // red
+ textColor = Color.WHITE;
+ accentColor = Color.parseColor("#df1831");
+ break;
+ case 2:
+ // green
+ textColor = Color.WHITE;
+ accentColor = Color.parseColor("#00a549");
+ break;
+ }
+
+ LayerDrawable buttonDrawable = (LayerDrawable)
+ context.getResources().getDrawable(R.drawable.btn_block);
+ buttonDrawable.getDrawable(0).setColorFilter(accentColor, PorterDuff.Mode.SRC_ATOP);
+ buttonDrawable.getDrawable(1).setAlpha(mContainsStarred ? 255 : 0);
+
+ setTextColor(textColor);
+ setBackgroundDrawable(buttonDrawable);
+ }
+
+ public String getBlockId() {
+ return mBlockId;
+ }
+
+ public long getStartTime() {
+ return mStartTime;
+ }
+
+ public long getEndTime() {
+ return mEndTime;
+ }
+
+ public int getColumn() {
+ return mColumn;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/widget/BlocksLayout.java b/src/com/google/android/apps/iosched/ui/widget/BlocksLayout.java
new file mode 100644
index 0000000..dee6913
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/widget/BlocksLayout.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui.widget;
+
+import com.google.android.apps.iosched.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Custom layout that contains and organizes a {@link TimeRulerView} and several
+ * instances of {@link BlockView}. Also positions current "now" divider using
+ * {@link R.id#blocks_now} view when applicable.
+ */
+public class BlocksLayout extends ViewGroup {
+ private int mColumns = 3;
+
+ private TimeRulerView mRulerView;
+ private View mNowView;
+
+ public BlocksLayout(Context context) {
+ this(context, null);
+ }
+
+ public BlocksLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public BlocksLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.BlocksLayout, defStyle, 0);
+
+ mColumns = a.getInt(R.styleable.TimeRulerView_headerWidth, mColumns);
+
+ a.recycle();
+ }
+
+ private void ensureChildren() {
+ mRulerView = (TimeRulerView) findViewById(R.id.blocks_ruler);
+ mRulerView.setDrawingCacheEnabled(true);
+ if (mRulerView == null) {
+ throw new IllegalStateException("Must include a R.id.blocks_ruler view.");
+ }
+
+ mNowView = findViewById(R.id.blocks_now);
+ mNowView.setDrawingCacheEnabled(true);
+ if (mNowView == null) {
+ throw new IllegalStateException("Must include a R.id.blocks_now view.");
+ }
+ }
+
+ /**
+ * Remove any {@link BlockView} instances, leaving only
+ * {@link TimeRulerView} remaining.
+ */
+ public void removeAllBlocks() {
+ ensureChildren();
+ removeAllViews();
+ addView(mRulerView);
+ addView(mNowView);
+ }
+
+ public void addBlock(BlockView blockView) {
+ blockView.setDrawingCacheEnabled(true);
+ addView(blockView, 1);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ ensureChildren();
+
+ mRulerView.measure(widthMeasureSpec, heightMeasureSpec);
+ mNowView.measure(widthMeasureSpec, heightMeasureSpec);
+
+ final int width = mRulerView.getMeasuredWidth();
+ final int height = mRulerView.getMeasuredHeight();
+
+ setMeasuredDimension(resolveSize(width, widthMeasureSpec),
+ resolveSize(height, heightMeasureSpec));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ ensureChildren();
+
+ final TimeRulerView rulerView = mRulerView;
+ final int headerWidth = rulerView.getHeaderWidth();
+ final int columnWidth = (getWidth() - headerWidth) / mColumns;
+
+ rulerView.layout(0, 0, getWidth(), getHeight());
+
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) continue;
+
+ if (child instanceof BlockView) {
+ final BlockView blockView = (BlockView) child;
+ final int top = rulerView.getTimeVerticalOffset(blockView.getStartTime());
+ final int bottom = rulerView.getTimeVerticalOffset(blockView.getEndTime());
+ final int left = headerWidth + (blockView.getColumn() * columnWidth);
+ final int right = left + columnWidth;
+ child.layout(left, top, right, bottom);
+ }
+ }
+
+ // Align now view to match current time
+ final View nowView = mNowView;
+ final long now = System.currentTimeMillis();
+
+ final int top = rulerView.getTimeVerticalOffset(now);
+ final int bottom = top + nowView.getMeasuredHeight();
+ final int left = 0;
+ final int right = getWidth();
+
+ nowView.layout(left, top, right, bottom);
+ }
+}
diff --git a/src/com/google/android/apps/iosched/ui/widget/TimeRulerView.java b/src/com/google/android/apps/iosched/ui/widget/TimeRulerView.java
new file mode 100644
index 0000000..912ca7e
--- /dev/null
+++ b/src/com/google/android/apps/iosched/ui/widget/TimeRulerView.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.ui.widget;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.util.UIUtils;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.graphics.Paint.FontMetricsInt;
+import android.graphics.Paint.Style;
+import android.text.format.Time;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Custom view that draws a vertical time "ruler" representing the chronological
+ * progression of a single day. Usually shown along with {@link BlockView}
+ * instances to give a spatial sense of time.
+ */
+public class TimeRulerView extends View {
+
+ private int mHeaderWidth = 70;
+ private int mHourHeight = 90;
+ private boolean mHorizontalDivider = true;
+ private int mLabelTextSize = 20;
+ private int mLabelPaddingLeft = 8;
+ private int mLabelColor = Color.BLACK;
+ private int mDividerColor = Color.LTGRAY;
+ private int mStartHour = 0;
+ private int mEndHour = 23;
+
+ public TimeRulerView(Context context) {
+ this(context, null);
+ }
+
+ public TimeRulerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TimeRulerView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TimeRulerView,
+ defStyle, 0);
+
+ mHeaderWidth = a.getDimensionPixelSize(R.styleable.TimeRulerView_headerWidth,
+ mHeaderWidth);
+ mHourHeight = a
+ .getDimensionPixelSize(R.styleable.TimeRulerView_hourHeight, mHourHeight);
+ mHorizontalDivider = a.getBoolean(R.styleable.TimeRulerView_horizontalDivider,
+ mHorizontalDivider);
+ mLabelTextSize = a.getDimensionPixelSize(R.styleable.TimeRulerView_labelTextSize,
+ mLabelTextSize);
+ mLabelPaddingLeft = a.getDimensionPixelSize(R.styleable.TimeRulerView_labelPaddingLeft,
+ mLabelPaddingLeft);
+ mLabelColor = a.getColor(R.styleable.TimeRulerView_labelColor, mLabelColor);
+ mDividerColor = a.getColor(R.styleable.TimeRulerView_dividerColor, mDividerColor);
+ mStartHour = a.getInt(R.styleable.TimeRulerView_startHour, mStartHour);
+ mEndHour = a.getInt(R.styleable.TimeRulerView_endHour, mEndHour);
+
+ a.recycle();
+ }
+
+ /**
+ * Return the vertical offset (in pixels) for a requested time (in
+ * milliseconds since epoch).
+ */
+ public int getTimeVerticalOffset(long timeMillis) {
+ Time time = new Time(UIUtils.CONFERENCE_TIME_ZONE.getID());
+ time.set(timeMillis);
+
+ final int minutes = ((time.hour - mStartHour) * 60) + time.minute;
+ return (minutes * mHourHeight) / 60;
+ }
+
+ @Override
+ protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int hours = mEndHour - mStartHour;
+
+ int width = mHeaderWidth;
+ int height = mHourHeight * hours;
+
+ setMeasuredDimension(resolveSize(width, widthMeasureSpec),
+ resolveSize(height, heightMeasureSpec));
+ }
+
+ private Paint mDividerPaint = new Paint();
+ private Paint mLabelPaint = new Paint();
+
+ @Override
+ protected synchronized void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ final int hourHeight = mHourHeight;
+
+ final Paint dividerPaint = mDividerPaint;
+ dividerPaint.setColor(mDividerColor);
+ dividerPaint.setStyle(Style.FILL);
+
+ final Paint labelPaint = mLabelPaint;
+ labelPaint.setColor(mLabelColor);
+ labelPaint.setTextSize(mLabelTextSize);
+ labelPaint.setTypeface(Typeface.DEFAULT_BOLD);
+ labelPaint.setAntiAlias(true);
+
+ final FontMetricsInt metrics = labelPaint.getFontMetricsInt();
+ final int labelHeight = Math.abs(metrics.ascent);
+ final int labelOffset = labelHeight + ((hourHeight - labelHeight) / 2);
+
+ final int right = getRight();
+
+ // Walk left side of canvas drawing timestamps
+ final int hours = mEndHour - mStartHour;
+ for (int i = 0; i < hours; i++) {
+ final int dividerY = hourHeight * i;
+ final int nextDividerY = hourHeight * (i + 1);
+ canvas.drawLine(0, dividerY, right, dividerY, dividerPaint);
+
+ // draw text title for timestamp
+ canvas.drawRect(0, dividerY, mHeaderWidth, nextDividerY, dividerPaint);
+
+ // TODO: localize these labels better, including handling
+ // 24-hour mode when set in framework.
+ final int hour = mStartHour + i;
+ String label;
+ if (hour == 0) {
+ label = "12am";
+ } else if (hour <= 11) {
+ label = hour + "am";
+ } else if (hour == 12) {
+ label = "12pm";
+ } else {
+ label = (hour - 12) + "pm";
+ }
+
+ final float labelWidth = labelPaint.measureText(label);
+
+ canvas.drawText(label, 0, label.length(), mHeaderWidth - labelWidth
+ - mLabelPaddingLeft, dividerY + labelOffset, labelPaint);
+ }
+ }
+
+ public int getHeaderWidth() {
+ return mHeaderWidth;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/.svn/all-wcprops b/src/com/google/android/apps/iosched/util/.svn/all-wcprops
new file mode 100644
index 0000000..8fb28f8
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/.svn/all-wcprops
@@ -0,0 +1,83 @@
+K 25
+svn:wc:ra_dav:version-url
+V 62
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/util
+END
+DetachableResultReceiver.java
+K 25
+svn:wc:ra_dav:version-url
+V 92
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/util/DetachableResultReceiver.java
+END
+Maps.java
+K 25
+svn:wc:ra_dav:version-url
+V 72
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/util/Maps.java
+END
+FractionalTouchDelegate.java
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/util/FractionalTouchDelegate.java
+END
+NotesExporter.java
+K 25
+svn:wc:ra_dav:version-url
+V 81
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/util/NotesExporter.java
+END
+NotifyingAsyncQueryHandler.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/util/NotifyingAsyncQueryHandler.java
+END
+SpreadsheetEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 84
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/util/SpreadsheetEntry.java
+END
+MathUtils.java
+K 25
+svn:wc:ra_dav:version-url
+V 77
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/util/MathUtils.java
+END
+SelectionBuilder.java
+K 25
+svn:wc:ra_dav:version-url
+V 84
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/util/SelectionBuilder.java
+END
+ParserUtils.java
+K 25
+svn:wc:ra_dav:version-url
+V 79
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/util/ParserUtils.java
+END
+WorksheetEntry.java
+K 25
+svn:wc:ra_dav:version-url
+V 82
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/util/WorksheetEntry.java
+END
+Lists.java
+K 25
+svn:wc:ra_dav:version-url
+V 73
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/util/Lists.java
+END
+UIUtils.java
+K 25
+svn:wc:ra_dav:version-url
+V 75
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/util/UIUtils.java
+END
+Sets.java
+K 25
+svn:wc:ra_dav:version-url
+V 72
+/svn/!svn/ver/2/trunk/src/com/google/android/apps/iosched/util/Sets.java
+END
diff --git a/src/com/google/android/apps/iosched/util/.svn/entries b/src/com/google/android/apps/iosched/util/.svn/entries
new file mode 100644
index 0000000..af152de
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/.svn/entries
@@ -0,0 +1,470 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/src/com/google/android/apps/iosched/util
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+DetachableResultReceiver.java
+file
+
+
+
+
+2010-06-03T15:23:15.413622Z
+b1dcec695e35b60775cba422ae4726e4
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1853
+
+Maps.java
+file
+
+
+
+
+2010-06-03T15:23:15.413622Z
+3e0d6c1b758149ea85b1e187b8fa050c
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1313
+
+FractionalTouchDelegate.java
+file
+
+
+
+
+2010-06-03T15:23:15.413622Z
+820a46e9910b9575a43fc0cff4092cca
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5324
+
+NotesExporter.java
+file
+
+
+
+
+2010-06-03T15:23:15.417645Z
+c3f6e90b9732a8ff556a0df3c05d9f24
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3651
+
+NotifyingAsyncQueryHandler.java
+file
+
+
+
+
+2010-06-03T15:23:15.417645Z
+0c7416b633bf7314596edd31ccfaf397
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4742
+
+SpreadsheetEntry.java
+file
+
+
+
+
+2010-06-03T15:23:15.421647Z
+f38ec6de9fb29d7b4d7d98036bd6a3e6
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3107
+
+MathUtils.java
+file
+
+
+
+
+2010-06-03T15:23:15.421647Z
+ca62c5bb307b82a206e4cb78d4f4c84f
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1180
+
+SelectionBuilder.java
+file
+
+
+
+
+2010-06-03T15:23:15.421647Z
+053c1d7e45284ebe55967d6e62e569fb
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5613
+
+ParserUtils.java
+file
+
+
+
+
+2010-06-03T15:23:15.421647Z
+60d439860a9e1c130ba518c8924571be
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7946
+
+WorksheetEntry.java
+file
+
+
+
+
+2010-06-03T15:23:15.421647Z
+ea67b41c09c8fa1b4ffe38c8a57fb18a
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3241
+
+Lists.java
+file
+
+
+
+
+2010-06-03T15:23:15.421647Z
+4c769d30cb9bd8f33e50c1fcf8fed75f
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2194
+
+UIUtils.java
+file
+
+
+
+
+2010-06-03T15:23:15.421647Z
+36f552a23ca4545792136c2c4f61d94d
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7986
+
+Sets.java
+file
+
+
+
+
+2010-06-03T15:23:15.421647Z
+ec7bee6a7a543c65840f37d52018077e
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2747
+
diff --git a/src/com/google/android/apps/iosched/util/.svn/text-base/DetachableResultReceiver.java.svn-base b/src/com/google/android/apps/iosched/util/.svn/text-base/DetachableResultReceiver.java.svn-base
new file mode 100644
index 0000000..a5933f3
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/.svn/text-base/DetachableResultReceiver.java.svn-base
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+/**
+ * Proxy {@link ResultReceiver} that offers a listener interface that can be
+ * detached. Useful for when sending callbacks to a {@link Service} where a
+ * listening {@link Activity} can be swapped out during configuration changes.
+ */
+public class DetachableResultReceiver extends ResultReceiver {
+ private static final String TAG = "DetachableResultReceiver";
+
+ private Receiver mReceiver;
+
+ public DetachableResultReceiver(Handler handler) {
+ super(handler);
+ }
+
+ public void clearReceiver() {
+ mReceiver = null;
+ }
+
+ public void setReceiver(Receiver receiver) {
+ mReceiver = receiver;
+ }
+
+ public interface Receiver {
+ public void onReceiveResult(int resultCode, Bundle resultData);
+ }
+
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (mReceiver != null) {
+ mReceiver.onReceiveResult(resultCode, resultData);
+ } else {
+ Log.w(TAG, "Dropping result on floor for code " + resultCode + ": "
+ + resultData.toString());
+ }
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/.svn/text-base/FractionalTouchDelegate.java.svn-base b/src/com/google/android/apps/iosched/util/.svn/text-base/FractionalTouchDelegate.java.svn-base
new file mode 100644
index 0000000..fe01f2a
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/.svn/text-base/FractionalTouchDelegate.java.svn-base
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.MotionEvent;
+import android.view.TouchDelegate;
+import android.view.View;
+
+/**
+ * {@link TouchDelegate} that gates {@link MotionEvent} instances by comparing
+ * then against fractional dimensions of the source view.
+ *
+ * This is particularly useful when you want to define a rectangle in terms of
+ * the source dimensions, but when those dimensions might change due to pending
+ * or future layout passes.
+ *
+ * One example is catching touches that occur in the top-right quadrant of
+ * {@code sourceParent}, and relaying them to {@code targetChild}. This could be
+ * done with:
+ * FractionalTouchDelegate.setupDelegate(sourceParent, targetChild, new RectF(0.5f, 0f, 1f, 0.5f));
+ *
+ */
+public class FractionalTouchDelegate extends TouchDelegate {
+
+ private View mSource;
+ private View mTarget;
+
+ private RectF mSourceFraction;
+
+ private Rect mScrap = new Rect();
+
+ /** Cached full dimensions of {@link #mSource}. */
+ private Rect mSourceFull = new Rect();
+ /** Cached projection of {@link #mSourceFraction} onto {@link #mSource}. */
+ private Rect mSourcePartial = new Rect();
+
+ private boolean mDelegateTargeted;
+
+ public FractionalTouchDelegate(View source, View target, RectF sourceFraction) {
+ super(new Rect(0, 0, 0, 0), target);
+ mSource = source;
+ mTarget = target;
+ mSourceFraction = sourceFraction;
+ }
+
+ /**
+ * Helper to create and setup a {@link FractionalTouchDelegate} between the
+ * given {@link View}.
+ *
+ * @param source Larger source {@link View}, usually a parent, that will be
+ * assigned {@link View#setTouchDelegate(TouchDelegate)}.
+ * @param target Smaller target {@link View} which will receive
+ * {@link MotionEvent} that land in requested fractional area.
+ * @param sourceFraction Fractional area projected onto source {@link View}
+ * which determines when {@link MotionEvent} will be passed to
+ * target {@link View}.
+ */
+ public static void setupDelegate(View source, View target, RectF sourceFraction) {
+ source.setTouchDelegate(new FractionalTouchDelegate(source, target, sourceFraction));
+ }
+
+ /**
+ * Consider updating {@link #mSourcePartial} when {@link #mSource}
+ * dimensions have changed.
+ */
+ private void updateSourcePartial() {
+ mSource.getHitRect(mScrap);
+ if (!mScrap.equals(mSourceFull)) {
+ // Copy over and calculate fractional rectangle
+ mSourceFull.set(mScrap);
+
+ final int width = mSourceFull.width();
+ final int height = mSourceFull.height();
+
+ mSourcePartial.left = (int) (mSourceFraction.left * width);
+ mSourcePartial.top = (int) (mSourceFraction.top * height);
+ mSourcePartial.right = (int) (mSourceFraction.right * width);
+ mSourcePartial.bottom = (int) (mSourceFraction.bottom * height);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ updateSourcePartial();
+
+ // The logic below is mostly copied from the parent class, since we
+ // can't update private mBounds variable.
+
+ // http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;
+ // f=core/java/android/view/TouchDelegate.java;hb=eclair#l98
+
+ final Rect sourcePartial = mSourcePartial;
+ final View target = mTarget;
+
+ int x = (int)event.getX();
+ int y = (int)event.getY();
+
+ boolean sendToDelegate = false;
+ boolean hit = true;
+ boolean handled = false;
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ if (sourcePartial.contains(x, y)) {
+ mDelegateTargeted = true;
+ sendToDelegate = true;
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_MOVE:
+ sendToDelegate = mDelegateTargeted;
+ if (sendToDelegate) {
+ if (!sourcePartial.contains(x, y)) {
+ hit = false;
+ }
+ }
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ sendToDelegate = mDelegateTargeted;
+ mDelegateTargeted = false;
+ break;
+ }
+
+ if (sendToDelegate) {
+ if (hit) {
+ event.setLocation(target.getWidth() / 2, target.getHeight() / 2);
+ } else {
+ event.setLocation(-1, -1);
+ }
+ handled = target.dispatchTouchEvent(event);
+ }
+ return handled;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/.svn/text-base/Lists.java.svn-base b/src/com/google/android/apps/iosched/util/.svn/text-base/Lists.java.svn-base
new file mode 100644
index 0000000..5b5cb5e
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/.svn/text-base/Lists.java.svn-base
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Provides static methods for creating {@code List} instances easily, and other
+ * utility methods for working with lists.
+ */
+public class Lists {
+
+ /**
+ * Creates an empty {@code ArrayList} instance.
+ *
+ *
Note: if you only need an immutable empty List, use
+ * {@link Collections#emptyList} instead.
+ *
+ * @return a newly-created, initially-empty {@code ArrayList}
+ */
+ public static ArrayList newArrayList() {
+ return new ArrayList();
+ }
+
+ /**
+ * Creates a resizable {@code ArrayList} instance containing the given
+ * elements.
+ *
+ *
Note: due to a bug in javac 1.5.0_06, we cannot support the
+ * following:
+ *
+ *
{@code List list = Lists.newArrayList(sub1, sub2);}
+ *
+ *
where {@code sub1} and {@code sub2} are references to subtypes of
+ * {@code Base}, not of {@code Base} itself. To get around this, you must
+ * use:
+ *
+ *
{@code List list = Lists.newArrayList(sub1, sub2);}
+ *
+ * @param elements the elements that the list should contain, in order
+ * @return a newly-created {@code ArrayList} containing those elements
+ */
+ public static ArrayList newArrayList(E... elements) {
+ int capacity = (elements.length * 110) / 100 + 5;
+ ArrayList list = new ArrayList(capacity);
+ Collections.addAll(list, elements);
+ return list;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/.svn/text-base/Maps.java.svn-base b/src/com/google/android/apps/iosched/util/.svn/text-base/Maps.java.svn-base
new file mode 100644
index 0000000..f51c3b8
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/.svn/text-base/Maps.java.svn-base
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+
+/**
+ * Provides static methods for creating mutable {@code Maps} instances easily.
+ */
+public class Maps {
+ /**
+ * Creates a {@code HashMap} instance.
+ *
+ * @return a newly-created, initially-empty {@code HashMap}
+ */
+ public static HashMap newHashMap() {
+ return new HashMap();
+ }
+
+ /**
+ * Creates a {@code LinkedHashMap} instance.
+ *
+ * @return a newly-created, initially-empty {@code HashMap}
+ */
+ public static LinkedHashMap newLinkedHashMap() {
+ return new LinkedHashMap();
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/.svn/text-base/MathUtils.java.svn-base b/src/com/google/android/apps/iosched/util/.svn/text-base/MathUtils.java.svn-base
new file mode 100644
index 0000000..632348a
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/.svn/text-base/MathUtils.java.svn-base
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+/**
+ * Math utility functions.
+ */
+public final class MathUtils {
+ private MathUtils() {}
+
+ /**
+ * Equivalent to Math.max(low, Math.min(high, amount));
+ */
+ public static int constrain(int amount, int low, int high) {
+ return amount < low ? low : (amount > high ? high : amount);
+ }
+
+ /**
+ * Equivalent to Math.max(low, Math.min(high, amount));
+ */
+ public static float constrain(float amount, float low, float high) {
+ return amount < low ? low : (amount > high ? high : amount);
+ }
+}
\ No newline at end of file
diff --git a/src/com/google/android/apps/iosched/util/.svn/text-base/NotesExporter.java.svn-base b/src/com/google/android/apps/iosched/util/.svn/text-base/NotesExporter.java.svn-base
new file mode 100644
index 0000000..e9feab3
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/.svn/text-base/NotesExporter.java.svn-base
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import com.google.android.apps.iosched.provider.ScheduleContract.Notes;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.text.format.Time;
+import android.util.Xml;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+/**
+ * Simple exporter that writes {@link Notes} contents into a {@link File} using
+ * {@link Xml#newSerializer()}.
+ */
+public class NotesExporter {
+
+ public static File writeExportedNotes(Context context) throws IOException {
+ // TODO: allow customization by accepting dir uri and output file
+ final ContentResolver resolver = context.getContentResolver();
+ final File notesFile = context.getFileStreamPath("notes.xml");
+ final BufferedWriter out = new BufferedWriter(new FileWriter(notesFile));
+ final XmlSerializer serializer = Xml.newSerializer();
+
+ serializer.setOutput(out);
+ serializer.startDocument("UTF-8", true);
+ serializer.startTag("", Tags.NOTES);
+
+ final Time time = new Time();
+
+ final Uri notesUri = Notes.CONTENT_URI;
+ final Cursor cursor = resolver.query(notesUri, NotesQuery.PROJECTION, null, null,
+ Notes.DEFAULT_SORT);
+ try {
+ while (cursor.moveToNext()) {
+ serializer.startTag("", Tags.NOTE);
+ {
+ serializer.startTag("", Tags.SESSION_ID);
+ serializer.text(cursor.getString(NotesQuery.SESSION_ID));
+ serializer.endTag("", Tags.SESSION_ID);
+ }
+ {
+ serializer.startTag("", Tags.TIME);
+ time.set(cursor.getLong(NotesQuery.NOTE_TIME));
+ final String timeString = time.format3339(false);
+ serializer.text(timeString);
+ serializer.endTag("", Tags.TIME);
+ }
+ {
+ serializer.startTag("", Tags.CONTENT);
+ serializer.text(cursor.getString(NotesQuery.NOTE_CONTENT));
+ serializer.endTag("", Tags.CONTENT);
+ }
+ serializer.endTag("", Tags.NOTE);
+ }
+ } finally {
+ cursor.close();
+ }
+
+ serializer.endTag("", Tags.NOTES);
+ serializer.endDocument();
+
+ out.flush();
+ out.close();
+
+ return notesFile;
+ }
+
+ private interface Tags {
+ String NOTES = "notes";
+ String NOTE = "note";
+ String SESSION_ID = "sessionId";
+ String CONTENT = "content";
+ String TIME = "time";
+ }
+
+ private interface NotesQuery {
+ String[] PROJECTION = {
+ Notes.SESSION_ID,
+ Notes.NOTE_CONTENT,
+ Notes.NOTE_TIME,
+ };
+
+ int SESSION_ID = 0;
+ int NOTE_CONTENT = 1;
+ int NOTE_TIME = 2;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/.svn/text-base/NotifyingAsyncQueryHandler.java.svn-base b/src/com/google/android/apps/iosched/util/.svn/text-base/NotifyingAsyncQueryHandler.java.svn-base
new file mode 100644
index 0000000..5d56be3
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/.svn/text-base/NotifyingAsyncQueryHandler.java.svn-base
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Slightly more abstract {@link AsyncQueryHandler} that helps keep a
+ * {@link WeakReference} back to a listener. Will properly close any
+ * {@link Cursor} if the listener ceases to exist.
+ *
+ * This pattern can be used to perform background queries without leaking
+ * {@link Context} objects.
+ *
+ * @hide pending API council review
+ */
+public class NotifyingAsyncQueryHandler extends AsyncQueryHandler {
+ private WeakReference mListener;
+
+ /**
+ * Interface to listen for completed query operations.
+ */
+ public interface AsyncQueryListener {
+ void onQueryComplete(int token, Object cookie, Cursor cursor);
+ }
+
+ public NotifyingAsyncQueryHandler(ContentResolver resolver, AsyncQueryListener listener) {
+ super(resolver);
+ setQueryListener(listener);
+ }
+
+ /**
+ * Assign the given {@link AsyncQueryListener} to receive query events from
+ * asynchronous calls. Will replace any existing listener.
+ */
+ public void setQueryListener(AsyncQueryListener listener) {
+ mListener = new WeakReference(listener);
+ }
+
+ /**
+ * Clear any {@link AsyncQueryListener} set through
+ * {@link #setQueryListener(AsyncQueryListener)}
+ */
+ public void clearQueryListener() {
+ mListener = null;
+ }
+
+ /**
+ * Begin an asynchronous query with the given arguments. When finished,
+ * {@link AsyncQueryListener#onQueryComplete(int, Object, Cursor)} is
+ * called if a valid {@link AsyncQueryListener} is present.
+ */
+ public void startQuery(Uri uri, String[] projection) {
+ startQuery(-1, null, uri, projection, null, null, null);
+ }
+
+ /**
+ * Begin an asynchronous query with the given arguments. When finished,
+ * {@link AsyncQueryListener#onQueryComplete(int, Object, Cursor)} is called
+ * if a valid {@link AsyncQueryListener} is present.
+ *
+ * @param token Unique identifier passed through to
+ * {@link AsyncQueryListener#onQueryComplete(int, Object, Cursor)}
+ */
+ public void startQuery(int token, Uri uri, String[] projection) {
+ startQuery(token, null, uri, projection, null, null, null);
+ }
+
+ /**
+ * Begin an asynchronous query with the given arguments. When finished,
+ * {@link AsyncQueryListener#onQueryComplete(int, Object, Cursor)} is called
+ * if a valid {@link AsyncQueryListener} is present.
+ */
+ public void startQuery(Uri uri, String[] projection, String sortOrder) {
+ startQuery(-1, null, uri, projection, null, null, sortOrder);
+ }
+
+ /**
+ * Begin an asynchronous query with the given arguments. When finished,
+ * {@link AsyncQueryListener#onQueryComplete(int, Object, Cursor)} is called
+ * if a valid {@link AsyncQueryListener} is present.
+ */
+ public void startQuery(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String orderBy) {
+ startQuery(-1, null, uri, projection, selection, selectionArgs, orderBy);
+ }
+
+ /**
+ * Begin an asynchronous update with the given arguments.
+ */
+ public void startUpdate(Uri uri, ContentValues values) {
+ startUpdate(-1, null, uri, values, null, null);
+ }
+
+ public void startInsert(Uri uri, ContentValues values) {
+ startInsert(-1, null, uri, values);
+ }
+
+ public void startDelete(Uri uri) {
+ startDelete(-1, null, uri, null, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ final AsyncQueryListener listener = mListener == null ? null : mListener.get();
+ if (listener != null) {
+ listener.onQueryComplete(token, cookie, cursor);
+ } else if (cursor != null) {
+ cursor.close();
+ }
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/.svn/text-base/ParserUtils.java.svn-base b/src/com/google/android/apps/iosched/util/.svn/text-base/ParserUtils.java.svn-base
new file mode 100644
index 0000000..7c90fb1
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/.svn/text-base/ParserUtils.java.svn-base
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import com.google.android.apps.iosched.io.XmlHandler;
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.provider.ScheduleContract.SyncColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import android.content.ContentProvider;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.text.format.Time;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * Various utility methods used by {@link XmlHandler} implementations.
+ */
+public class ParserUtils {
+ // TODO: consider refactor to HandlerUtils?
+
+ // TODO: localize this string at some point
+ public static final String BLOCK_TITLE_BREAKOUT_SESSIONS = "Breakout sessions";
+
+ public static final String BLOCK_TYPE_FOOD = "food";
+ public static final String BLOCK_TYPE_SESSION = "session";
+ public static final String BLOCK_TYPE_OFFICE_HOURS = "officehours";
+
+ public static final Set LOCAL_TRACK_IDS = Sets.newHashSet(
+ "android", "appengine", "chrome", "enterprise", "firesidechats", "geo",
+ "googleapis", "googlewebtoolkit", "socialweb", "techtalks", "wave");
+
+ /** Used to sanitize a string to be {@link Uri} safe. */
+ private static final Pattern sSanitizePattern = Pattern.compile("[^a-z0-9-_]");
+ private static final Pattern sParenPattern = Pattern.compile("\\(.*?\\)");
+
+ /** Used to split a comma-separated string. */
+ private static final Pattern sCommaPattern = Pattern.compile("\\s*,\\s*");
+
+ private static Time sTime = new Time();
+ private static XmlPullParserFactory sFactory;
+
+ /**
+ * Sanitize the given string to be {@link Uri} safe for building
+ * {@link ContentProvider} paths.
+ */
+ public static String sanitizeId(String input) {
+ return sanitizeId(input, false);
+ }
+
+ /**
+ * Sanitize the given string to be {@link Uri} safe for building
+ * {@link ContentProvider} paths.
+ */
+ public static String sanitizeId(String input, boolean stripParen) {
+ if (input == null) return null;
+ if (stripParen) {
+ // Strip out all parenthetical statements when requested.
+ input = sParenPattern.matcher(input).replaceAll("");
+ }
+ return sSanitizePattern.matcher(input.toLowerCase()).replaceAll("");
+ }
+
+ /**
+ * Split the given comma-separated string, returning all values.
+ */
+ public static String[] splitComma(CharSequence input) {
+ if (input == null) return new String[0];
+ return sCommaPattern.split(input);
+ }
+
+ /**
+ * Build and return a new {@link XmlPullParser} with the given
+ * {@link InputStream} assigned to it.
+ */
+ public static XmlPullParser newPullParser(InputStream input) throws XmlPullParserException {
+ if (sFactory == null) {
+ sFactory = XmlPullParserFactory.newInstance();
+ }
+ final XmlPullParser parser = sFactory.newPullParser();
+ parser.setInput(input, null);
+ return parser;
+ }
+
+ /**
+ * Parse the given string as a RFC 3339 timestamp, returning the value as
+ * milliseconds since the epoch.
+ */
+ public static long parseTime(String time) {
+ sTime.parse3339(time);
+ return sTime.toMillis(false);
+ }
+
+ /**
+ * Return a {@link Blocks#BLOCK_ID} matching the requested arguments.
+ */
+ public static String findBlock(String title, long startTime, long endTime) {
+ // TODO: in future we might check provider if block exists
+ return Blocks.generateBlockId(startTime, endTime);
+ }
+
+ /**
+ * Return a {@link Blocks#BLOCK_ID} matching the requested arguments,
+ * inserting a new {@link Blocks} entry as a
+ * {@link ContentProviderOperation} when none already exists.
+ */
+ public static String findOrCreateBlock(String title, String type, long startTime, long endTime,
+ ArrayList batch, ContentResolver resolver) {
+ // TODO: check for existence instead of always blindly creating. it's
+ // okay for now since the database replaces on conflict.
+ final ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Blocks.CONTENT_URI);
+ final String blockId = Blocks.generateBlockId(startTime, endTime);
+ builder.withValue(Blocks.BLOCK_ID, blockId);
+ builder.withValue(Blocks.BLOCK_TITLE, title);
+ builder.withValue(Blocks.BLOCK_START, startTime);
+ builder.withValue(Blocks.BLOCK_END, endTime);
+ builder.withValue(Blocks.BLOCK_TYPE, type);
+ batch.add(builder.build());
+ return blockId;
+ }
+
+ /**
+ * Query and return the {@link SyncColumns#UPDATED} time for the requested
+ * {@link Uri}. Expects the {@link Uri} to reference a single item.
+ */
+ public static long queryItemUpdated(Uri uri, ContentResolver resolver) {
+ final String[] projection = { SyncColumns.UPDATED };
+ final Cursor cursor = resolver.query(uri, projection, null, null, null);
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getLong(0);
+ } else {
+ return ScheduleContract.UPDATED_NEVER;
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Query and return the newest {@link SyncColumns#UPDATED} time for all
+ * entries under the requested {@link Uri}. Expects the {@link Uri} to
+ * reference a directory of several items.
+ */
+ public static long queryDirUpdated(Uri uri, ContentResolver resolver) {
+ final String[] projection = { "MAX(" + SyncColumns.UPDATED + ")" };
+ final Cursor cursor = resolver.query(uri, projection, null, null, null);
+ try {
+ cursor.moveToFirst();
+ return cursor.getLong(0);
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Translate an incoming {@link Tracks#TRACK_ID}, usually passing directly
+ * through, but returning a different value when a local alias is defined.
+ */
+ public static String translateTrackIdAlias(String trackId) {
+ if ("gwt".equals(trackId)) {
+ return "googlewebtoolkit";
+ } else {
+ return trackId;
+ }
+ }
+
+ /**
+ * Translate a possibly locally aliased {@link Tracks#TRACK_ID} to its real value;
+ * this usually is a pass-through.
+ */
+ public static String translateTrackIdAliasInverse(String trackId) {
+ if ("googlewebtoolkit".equals(trackId)) {
+ return "gwt";
+ } else {
+ return trackId;
+ }
+ }
+
+ /** XML tag constants used by the Atom standard. */
+ public interface AtomTags {
+ String ENTRY = "entry";
+ String UPDATED = "updated";
+ String TITLE = "title";
+ String LINK = "link";
+ String CONTENT = "content";
+
+ String REL = "rel";
+ String HREF = "href";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/.svn/text-base/SelectionBuilder.java.svn-base b/src/com/google/android/apps/iosched/util/.svn/text-base/SelectionBuilder.java.svn-base
new file mode 100644
index 0000000..0d3ba4e
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/.svn/text-base/SelectionBuilder.java.svn-base
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+/*
+ * Modifications:
+ * -Imported from AOSP frameworks/base/core/java/com/android/internal/content
+ * -Changed package name
+ */
+
+package com.google.android.apps.iosched.util;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * Helper for building selection clauses for {@link SQLiteDatabase}. Each
+ * appended clause is combined using {@code AND}. This class is not
+ * thread safe.
+ */
+public class SelectionBuilder {
+ private static final String TAG = "SelectionBuilder";
+ private static final boolean LOGV = false;
+
+ private String mTable = null;
+ private Map mProjectionMap = Maps.newHashMap();
+ private StringBuilder mSelection = new StringBuilder();
+ private ArrayList mSelectionArgs = Lists.newArrayList();
+
+ /**
+ * Reset any internal state, allowing this builder to be recycled.
+ */
+ public SelectionBuilder reset() {
+ mTable = null;
+ mSelection.setLength(0);
+ mSelectionArgs.clear();
+ return this;
+ }
+
+ /**
+ * Append the given selection clause to the internal state. Each clause is
+ * surrounded with parenthesis and combined using {@code AND}.
+ */
+ public SelectionBuilder where(String selection, String... selectionArgs) {
+ if (TextUtils.isEmpty(selection)) {
+ if (selectionArgs != null && selectionArgs.length > 0) {
+ throw new IllegalArgumentException(
+ "Valid selection required when including arguments=");
+ }
+
+ // Shortcut when clause is empty
+ return this;
+ }
+
+ if (mSelection.length() > 0) {
+ mSelection.append(" AND ");
+ }
+
+ mSelection.append("(").append(selection).append(")");
+ if (selectionArgs != null) {
+ for (String arg : selectionArgs) {
+ mSelectionArgs.add(arg);
+ }
+ }
+
+ return this;
+ }
+
+ public SelectionBuilder table(String table) {
+ mTable = table;
+ return this;
+ }
+
+ private void assertTable() {
+ if (mTable == null) {
+ throw new IllegalStateException("Table not specified");
+ }
+ }
+
+ public SelectionBuilder mapToTable(String column, String table) {
+ mProjectionMap.put(column, table + "." + column);
+ return this;
+ }
+
+ public SelectionBuilder map(String fromColumn, String toClause) {
+ mProjectionMap.put(fromColumn, toClause + " AS " + fromColumn);
+ return this;
+ }
+
+ /**
+ * Return selection string for current internal state.
+ *
+ * @see #getSelectionArgs()
+ */
+ public String getSelection() {
+ return mSelection.toString();
+ }
+
+ /**
+ * Return selection arguments for current internal state.
+ *
+ * @see #getSelection()
+ */
+ public String[] getSelectionArgs() {
+ return mSelectionArgs.toArray(new String[mSelectionArgs.size()]);
+ }
+
+ private void mapColumns(String[] columns) {
+ for (int i = 0; i < columns.length; i++) {
+ final String target = mProjectionMap.get(columns[i]);
+ if (target != null) {
+ columns[i] = target;
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SelectionBuilder[table=" + mTable + ", selection=" + getSelection()
+ + ", selectionArgs=" + Arrays.toString(getSelectionArgs()) + "]";
+ }
+
+ /**
+ * Execute query using the current internal state as {@code WHERE} clause.
+ */
+ public Cursor query(SQLiteDatabase db, String[] columns, String orderBy) {
+ return query(db, columns, null, null, orderBy, null);
+ }
+
+ /**
+ * Execute query using the current internal state as {@code WHERE} clause.
+ */
+ public Cursor query(SQLiteDatabase db, String[] columns, String groupBy,
+ String having, String orderBy, String limit) {
+ assertTable();
+ if (columns != null) mapColumns(columns);
+ if (LOGV) Log.v(TAG, "query(columns=" + Arrays.toString(columns) + ") " + this);
+ return db.query(mTable, columns, getSelection(), getSelectionArgs(), groupBy, having,
+ orderBy, limit);
+ }
+
+ /**
+ * Execute update using the current internal state as {@code WHERE} clause.
+ */
+ public int update(SQLiteDatabase db, ContentValues values) {
+ assertTable();
+ if (LOGV) Log.v(TAG, "update() " + this);
+ return db.update(mTable, values, getSelection(), getSelectionArgs());
+ }
+
+ /**
+ * Execute delete using the current internal state as {@code WHERE} clause.
+ */
+ public int delete(SQLiteDatabase db) {
+ assertTable();
+ if (LOGV) Log.v(TAG, "delete() " + this);
+ return db.delete(mTable, getSelection(), getSelectionArgs());
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/.svn/text-base/Sets.java.svn-base b/src/com/google/android/apps/iosched/util/.svn/text-base/Sets.java.svn-base
new file mode 100644
index 0000000..7f93c4e
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/.svn/text-base/Sets.java.svn-base
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Provides static methods for creating mutable {@code Set} instances easily and
+ * other static methods for working with Sets.
+ *
+ */
+public class Sets {
+
+ /**
+ * Creates an empty {@code HashSet} instance.
+ *
+ *
Note: if {@code E} is an {@link Enum} type, use {@link
+ * EnumSet#noneOf} instead.
+ *
+ *
Note: if you only need an immutable empty Set,
+ * use {@link Collections#emptySet} instead.
+ *
+ * @return a newly-created, initially-empty {@code HashSet}
+ */
+ public static HashSet newHashSet() {
+ return new HashSet();
+ }
+
+ /**
+ * Creates a {@code HashSet} instance containing the given elements.
+ *
+ *
Note: due to a bug in javac 1.5.0_06, we cannot support the
+ * following:
+ *
+ *
{@code Set set = Sets.newHashSet(sub1, sub2);}
+ *
+ *
where {@code sub1} and {@code sub2} are references to subtypes of {@code
+ * Base}, not of {@code Base} itself. To get around this, you must use:
+ *
+ *
{@code Set set = Sets.newHashSet(sub1, sub2);}
+ *
+ * @param elements the elements that the set should contain
+ * @return a newly-created {@code HashSet} containing those elements (minus
+ * duplicates)
+ */
+ public static HashSet newHashSet(E... elements) {
+ int capacity = elements.length * 4 / 3 + 1;
+ HashSet set = new HashSet(capacity);
+ Collections.addAll(set, elements);
+ return set;
+ }
+
+ /**
+ * Creates a {@code SortedSet} instance containing the given elements.
+ *
+ * @param elements the elements that the set should contain
+ * @return a newly-created {@code SortedSet} containing those elements (minus
+ * duplicates)
+ */
+ public static SortedSet newSortedSet(E... elements) {
+ SortedSet set = new TreeSet();
+ Collections.addAll(set, elements);
+ return set;
+ }
+
+}
diff --git a/src/com/google/android/apps/iosched/util/.svn/text-base/SpreadsheetEntry.java.svn-base b/src/com/google/android/apps/iosched/util/.svn/text-base/SpreadsheetEntry.java.svn-base
new file mode 100644
index 0000000..3336e5c
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/.svn/text-base/SpreadsheetEntry.java.svn-base
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.CONTENT;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.UPDATED;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class SpreadsheetEntry extends HashMap {
+
+ private static final Pattern sContentPattern = Pattern.compile(
+ "(?:^|, )([_a-zA-Z0-9]+): (.*?)(?=\\s*$|, [_a-zA-Z0-9]+: )", Pattern.DOTALL);
+
+ private static Matcher sContentMatcher;
+
+ private static Matcher getContentMatcher(CharSequence input) {
+ if (sContentMatcher == null) {
+ sContentMatcher = sContentPattern.matcher(input);
+ } else {
+ sContentMatcher.reset(input);
+ }
+ return sContentMatcher;
+ }
+
+ private long mUpdated;
+
+ public long getUpdated() {
+ return mUpdated;
+ }
+
+ public static SpreadsheetEntry fromParser(XmlPullParser parser) throws XmlPullParserException,
+ IOException {
+ final int depth = parser.getDepth();
+ final SpreadsheetEntry entry = new SpreadsheetEntry();
+
+ String tag = null;
+ int type;
+ while (((type = parser.next()) != END_TAG ||
+ parser.getDepth() > depth) && type != END_DOCUMENT) {
+ if (type == START_TAG) {
+ tag = parser.getName();
+ } else if (type == END_TAG) {
+ tag = null;
+ } else if (type == TEXT) {
+ if (UPDATED.equals(tag)) {
+ final String text = parser.getText();
+ entry.mUpdated = ParserUtils.parseTime(text);
+ } else if (CONTENT.equals(tag)) {
+ final String text = parser.getText();
+ final Matcher matcher = getContentMatcher(text);
+ while (matcher.find()) {
+ final String key = matcher.group(1);
+ final String value = matcher.group(2).trim();
+ entry.put(key, value);
+ }
+ }
+ }
+ }
+ return entry;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/.svn/text-base/UIUtils.java.svn-base b/src/com/google/android/apps/iosched/util/.svn/text-base/UIUtils.java.svn-base
new file mode 100644
index 0000000..c199ba9
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/.svn/text-base/UIUtils.java.svn-base
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.ui.HomeActivity;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.graphics.drawable.LevelListDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.format.DateUtils;
+import android.text.method.LinkMovementMethod;
+import android.text.style.StyleSpan;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.TextView;
+
+import java.util.Formatter;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class UIUtils {
+
+ /**
+ * Time zone to use when formatting all session times. To always use the
+ * phone local time, use {@link TimeZone#getDefault()}.
+ */
+ public static TimeZone CONFERENCE_TIME_ZONE = TimeZone.getTimeZone("America/Los_Angeles");
+
+ public static final long CONFERENCE_START_MILLIS = ParserUtils.parseTime(
+ "2010-05-19T09:00:00.000-07:00");
+ public static final long CONFERENCE_END_MILLIS = ParserUtils.parseTime(
+ "2010-05-20T17:30:00.000-07:00");
+
+ public static final Uri CONFERENCE_URL = Uri.parse("http://code.google.com/events/io/2010/");
+
+ /** Flags used with {@link DateUtils#formatDateRange}. */
+ private static final int TIME_FLAGS = DateUtils.FORMAT_SHOW_TIME
+ | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_WEEKDAY;
+
+ private static final int BRIGHTNESS_THRESHOLD = 150;
+
+ /** {@link StringBuilder} used for formatting time block. */
+ private static StringBuilder sBuilder = new StringBuilder(50);
+ /** {@link Formatter} used for formatting time block. */
+ private static Formatter sFormatter = new Formatter(sBuilder, Locale.getDefault());
+
+ private static StyleSpan sBoldSpan = new StyleSpan(Typeface.BOLD);
+
+ public static void setTitleBarColor(View titleBarView, int color) {
+ final ViewGroup titleBar = (ViewGroup) titleBarView;
+ titleBar.setBackgroundColor(color);
+
+ /*
+ * Calculate the brightness of the titlebar color, based on the commonly known
+ * brightness formula:
+ *
+ * http://en.wikipedia.org/wiki/HSV_color_space%23Lightness
+ */
+ int brColor = (30 * Color.red(color) +
+ 59 * Color.green(color) +
+ 11 * Color.blue(color)) / 100;
+ if (brColor > BRIGHTNESS_THRESHOLD) {
+ ((TextView) titleBar.findViewById(R.id.title_text)).setTextColor(
+ titleBar.getContext().getResources().getColor(R.color.title_text_alt));
+
+ // Iterate through all children of the titlebar and if they're a LevelListDrawable,
+ // set their level to 1 (alternate).
+ // TODO: find a less hacky way of doing this.
+ titleBar.post(new Runnable() {
+ public void run() {
+ final int childCount = titleBar.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = titleBar.getChildAt(i);
+ if (child instanceof ImageButton) {
+ final ImageButton childButton = (ImageButton) child;
+ if (childButton.getDrawable() != null &&
+ childButton.getDrawable() instanceof LevelListDrawable) {
+ ((LevelListDrawable) childButton.getDrawable()).setLevel(1);
+ }
+ }
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Invoke "home" action, returning to {@link HomeActivity}.
+ */
+ public static void goHome(Context context) {
+ final Intent intent = new Intent(context, HomeActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ context.startActivity(intent);
+ }
+
+ /**
+ * Invoke "search" action, triggering a default search.
+ */
+ public static void goSearch(Activity activity) {
+ activity.startSearch(null, false, Bundle.EMPTY, false);
+ }
+
+ /**
+ * Format and return the given {@link Blocks} and {@link Rooms} values using
+ * {@link #CONFERENCE_TIME_ZONE}.
+ */
+ public static String formatSessionSubtitle(long blockStart, long blockEnd,
+ String roomName, Context context) {
+ TimeZone.setDefault(CONFERENCE_TIME_ZONE);
+
+ // NOTE: There is an efficient version of formatDateRange in Eclair and
+ // beyond that allows you to recycle a StringBuilder.
+ final CharSequence timeString = DateUtils.formatDateRange(context,
+ blockStart, blockEnd, TIME_FLAGS);
+
+ return context.getString(R.string.session_subtitle, timeString, roomName);
+ }
+
+ /**
+ * Populate the given {@link TextView} with the requested text, formatting
+ * through {@link Html#fromHtml(String)} when applicable. Also sets
+ * {@link TextView#setMovementMethod} so inline links are handled.
+ */
+ public static void setTextMaybeHtml(TextView view, String text) {
+ if (text.contains("<") && text.contains(">")) {
+ view.setText(Html.fromHtml(text));
+ view.setMovementMethod(LinkMovementMethod.getInstance());
+ } else {
+ view.setText(text);
+ }
+ }
+
+ public static void setSessionTitleColor(long blockStart, long blockEnd, TextView title,
+ TextView subtitle) {
+ long currentTimeMillis = System.currentTimeMillis();
+ int colorId = android.R.color.primary_text_light;
+ int subColorId = android.R.color.secondary_text_light;
+
+ if (currentTimeMillis > blockEnd &&
+ currentTimeMillis < CONFERENCE_END_MILLIS) {
+ colorId = subColorId = R.color.session_foreground_past;
+ }
+
+ final Resources res = title.getResources();
+ title.setTextColor(res.getColor(colorId));
+ subtitle.setTextColor(res.getColor(subColorId));
+ }
+
+ /**
+ * Given a snippet string with matching segments surrounded by curly
+ * braces, turn those areas into bold spans, removing the curly braces.
+ */
+ public static Spannable buildStyledSnippet(String snippet) {
+ final SpannableStringBuilder builder = new SpannableStringBuilder(snippet);
+
+ // Walk through string, inserting bold snippet spans
+ int startIndex = -1, endIndex = -1, delta = 0;
+ while ((startIndex = snippet.indexOf('{', endIndex)) != -1) {
+ endIndex = snippet.indexOf('}', startIndex);
+
+ // Remove braces from both sides
+ builder.delete(startIndex - delta, startIndex - delta + 1);
+ builder.delete(endIndex - delta - 1, endIndex - delta);
+
+ // Insert bold style
+ builder.setSpan(sBoldSpan, startIndex - delta, endIndex - delta - 1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ delta += 2;
+ }
+
+ return builder;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/.svn/text-base/WorksheetEntry.java.svn-base b/src/com/google/android/apps/iosched/util/.svn/text-base/WorksheetEntry.java.svn-base
new file mode 100644
index 0000000..4b4eae8
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/.svn/text-base/WorksheetEntry.java.svn-base
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.HREF;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.LINK;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.REL;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.TITLE;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.UPDATED;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.text.format.DateUtils;
+
+import java.io.IOException;
+
+public class WorksheetEntry {
+ private static final String REL_LISTFEED = "http://schemas.google.com/spreadsheets/2006#listfeed";
+
+ private long mUpdated;
+ private String mTitle;
+ private String mListFeed;
+
+ public long getUpdated() {
+ return mUpdated;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public String getListFeed() {
+ return mListFeed;
+ }
+
+ @Override
+ public String toString() {
+ return "title=" + mTitle + ", updated=" + mUpdated + " ("
+ + DateUtils.getRelativeTimeSpanString(mUpdated) + ")";
+ }
+
+ public static WorksheetEntry fromParser(XmlPullParser parser) throws XmlPullParserException,
+ IOException {
+ final int depth = parser.getDepth();
+ final WorksheetEntry entry = new WorksheetEntry();
+
+ String tag = null;
+ int type;
+ while (((type = parser.next()) != END_TAG ||
+ parser.getDepth() > depth) && type != END_DOCUMENT) {
+ if (type == START_TAG) {
+ tag = parser.getName();
+ if (LINK.equals(tag)) {
+ final String rel = parser.getAttributeValue(null, REL);
+ final String href = parser.getAttributeValue(null, HREF);
+ if (REL_LISTFEED.equals(rel)) {
+ entry.mListFeed = href;
+ }
+ }
+ } else if (type == END_TAG) {
+ tag = null;
+ } else if (type == TEXT) {
+ final String text = parser.getText();
+ if (TITLE.equals(tag)) {
+ entry.mTitle = text;
+ } else if (UPDATED.equals(tag)) {
+ entry.mUpdated = ParserUtils.parseTime(text);
+ }
+ }
+ }
+ return entry;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/DetachableResultReceiver.java b/src/com/google/android/apps/iosched/util/DetachableResultReceiver.java
new file mode 100644
index 0000000..a5933f3
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/DetachableResultReceiver.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+/**
+ * Proxy {@link ResultReceiver} that offers a listener interface that can be
+ * detached. Useful for when sending callbacks to a {@link Service} where a
+ * listening {@link Activity} can be swapped out during configuration changes.
+ */
+public class DetachableResultReceiver extends ResultReceiver {
+ private static final String TAG = "DetachableResultReceiver";
+
+ private Receiver mReceiver;
+
+ public DetachableResultReceiver(Handler handler) {
+ super(handler);
+ }
+
+ public void clearReceiver() {
+ mReceiver = null;
+ }
+
+ public void setReceiver(Receiver receiver) {
+ mReceiver = receiver;
+ }
+
+ public interface Receiver {
+ public void onReceiveResult(int resultCode, Bundle resultData);
+ }
+
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (mReceiver != null) {
+ mReceiver.onReceiveResult(resultCode, resultData);
+ } else {
+ Log.w(TAG, "Dropping result on floor for code " + resultCode + ": "
+ + resultData.toString());
+ }
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/FractionalTouchDelegate.java b/src/com/google/android/apps/iosched/util/FractionalTouchDelegate.java
new file mode 100644
index 0000000..fe01f2a
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/FractionalTouchDelegate.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.MotionEvent;
+import android.view.TouchDelegate;
+import android.view.View;
+
+/**
+ * {@link TouchDelegate} that gates {@link MotionEvent} instances by comparing
+ * then against fractional dimensions of the source view.
+ *
+ * This is particularly useful when you want to define a rectangle in terms of
+ * the source dimensions, but when those dimensions might change due to pending
+ * or future layout passes.
+ *
+ * One example is catching touches that occur in the top-right quadrant of
+ * {@code sourceParent}, and relaying them to {@code targetChild}. This could be
+ * done with:
+ * FractionalTouchDelegate.setupDelegate(sourceParent, targetChild, new RectF(0.5f, 0f, 1f, 0.5f));
+ *
+ */
+public class FractionalTouchDelegate extends TouchDelegate {
+
+ private View mSource;
+ private View mTarget;
+
+ private RectF mSourceFraction;
+
+ private Rect mScrap = new Rect();
+
+ /** Cached full dimensions of {@link #mSource}. */
+ private Rect mSourceFull = new Rect();
+ /** Cached projection of {@link #mSourceFraction} onto {@link #mSource}. */
+ private Rect mSourcePartial = new Rect();
+
+ private boolean mDelegateTargeted;
+
+ public FractionalTouchDelegate(View source, View target, RectF sourceFraction) {
+ super(new Rect(0, 0, 0, 0), target);
+ mSource = source;
+ mTarget = target;
+ mSourceFraction = sourceFraction;
+ }
+
+ /**
+ * Helper to create and setup a {@link FractionalTouchDelegate} between the
+ * given {@link View}.
+ *
+ * @param source Larger source {@link View}, usually a parent, that will be
+ * assigned {@link View#setTouchDelegate(TouchDelegate)}.
+ * @param target Smaller target {@link View} which will receive
+ * {@link MotionEvent} that land in requested fractional area.
+ * @param sourceFraction Fractional area projected onto source {@link View}
+ * which determines when {@link MotionEvent} will be passed to
+ * target {@link View}.
+ */
+ public static void setupDelegate(View source, View target, RectF sourceFraction) {
+ source.setTouchDelegate(new FractionalTouchDelegate(source, target, sourceFraction));
+ }
+
+ /**
+ * Consider updating {@link #mSourcePartial} when {@link #mSource}
+ * dimensions have changed.
+ */
+ private void updateSourcePartial() {
+ mSource.getHitRect(mScrap);
+ if (!mScrap.equals(mSourceFull)) {
+ // Copy over and calculate fractional rectangle
+ mSourceFull.set(mScrap);
+
+ final int width = mSourceFull.width();
+ final int height = mSourceFull.height();
+
+ mSourcePartial.left = (int) (mSourceFraction.left * width);
+ mSourcePartial.top = (int) (mSourceFraction.top * height);
+ mSourcePartial.right = (int) (mSourceFraction.right * width);
+ mSourcePartial.bottom = (int) (mSourceFraction.bottom * height);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ updateSourcePartial();
+
+ // The logic below is mostly copied from the parent class, since we
+ // can't update private mBounds variable.
+
+ // http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;
+ // f=core/java/android/view/TouchDelegate.java;hb=eclair#l98
+
+ final Rect sourcePartial = mSourcePartial;
+ final View target = mTarget;
+
+ int x = (int)event.getX();
+ int y = (int)event.getY();
+
+ boolean sendToDelegate = false;
+ boolean hit = true;
+ boolean handled = false;
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ if (sourcePartial.contains(x, y)) {
+ mDelegateTargeted = true;
+ sendToDelegate = true;
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_MOVE:
+ sendToDelegate = mDelegateTargeted;
+ if (sendToDelegate) {
+ if (!sourcePartial.contains(x, y)) {
+ hit = false;
+ }
+ }
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ sendToDelegate = mDelegateTargeted;
+ mDelegateTargeted = false;
+ break;
+ }
+
+ if (sendToDelegate) {
+ if (hit) {
+ event.setLocation(target.getWidth() / 2, target.getHeight() / 2);
+ } else {
+ event.setLocation(-1, -1);
+ }
+ handled = target.dispatchTouchEvent(event);
+ }
+ return handled;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/Lists.java b/src/com/google/android/apps/iosched/util/Lists.java
new file mode 100644
index 0000000..5b5cb5e
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/Lists.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Provides static methods for creating {@code List} instances easily, and other
+ * utility methods for working with lists.
+ */
+public class Lists {
+
+ /**
+ * Creates an empty {@code ArrayList} instance.
+ *
+ *
Note: if you only need an immutable empty List, use
+ * {@link Collections#emptyList} instead.
+ *
+ * @return a newly-created, initially-empty {@code ArrayList}
+ */
+ public static ArrayList newArrayList() {
+ return new ArrayList();
+ }
+
+ /**
+ * Creates a resizable {@code ArrayList} instance containing the given
+ * elements.
+ *
+ *
Note: due to a bug in javac 1.5.0_06, we cannot support the
+ * following:
+ *
+ *
{@code List list = Lists.newArrayList(sub1, sub2);}
+ *
+ *
where {@code sub1} and {@code sub2} are references to subtypes of
+ * {@code Base}, not of {@code Base} itself. To get around this, you must
+ * use:
+ *
+ *
{@code List list = Lists.newArrayList(sub1, sub2);}
+ *
+ * @param elements the elements that the list should contain, in order
+ * @return a newly-created {@code ArrayList} containing those elements
+ */
+ public static ArrayList newArrayList(E... elements) {
+ int capacity = (elements.length * 110) / 100 + 5;
+ ArrayList list = new ArrayList(capacity);
+ Collections.addAll(list, elements);
+ return list;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/Maps.java b/src/com/google/android/apps/iosched/util/Maps.java
new file mode 100644
index 0000000..f51c3b8
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/Maps.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+
+/**
+ * Provides static methods for creating mutable {@code Maps} instances easily.
+ */
+public class Maps {
+ /**
+ * Creates a {@code HashMap} instance.
+ *
+ * @return a newly-created, initially-empty {@code HashMap}
+ */
+ public static HashMap newHashMap() {
+ return new HashMap();
+ }
+
+ /**
+ * Creates a {@code LinkedHashMap} instance.
+ *
+ * @return a newly-created, initially-empty {@code HashMap}
+ */
+ public static LinkedHashMap newLinkedHashMap() {
+ return new LinkedHashMap();
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/MathUtils.java b/src/com/google/android/apps/iosched/util/MathUtils.java
new file mode 100644
index 0000000..632348a
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/MathUtils.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+/**
+ * Math utility functions.
+ */
+public final class MathUtils {
+ private MathUtils() {}
+
+ /**
+ * Equivalent to Math.max(low, Math.min(high, amount));
+ */
+ public static int constrain(int amount, int low, int high) {
+ return amount < low ? low : (amount > high ? high : amount);
+ }
+
+ /**
+ * Equivalent to Math.max(low, Math.min(high, amount));
+ */
+ public static float constrain(float amount, float low, float high) {
+ return amount < low ? low : (amount > high ? high : amount);
+ }
+}
\ No newline at end of file
diff --git a/src/com/google/android/apps/iosched/util/NotesExporter.java b/src/com/google/android/apps/iosched/util/NotesExporter.java
new file mode 100644
index 0000000..e9feab3
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/NotesExporter.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import com.google.android.apps.iosched.provider.ScheduleContract.Notes;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.text.format.Time;
+import android.util.Xml;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+/**
+ * Simple exporter that writes {@link Notes} contents into a {@link File} using
+ * {@link Xml#newSerializer()}.
+ */
+public class NotesExporter {
+
+ public static File writeExportedNotes(Context context) throws IOException {
+ // TODO: allow customization by accepting dir uri and output file
+ final ContentResolver resolver = context.getContentResolver();
+ final File notesFile = context.getFileStreamPath("notes.xml");
+ final BufferedWriter out = new BufferedWriter(new FileWriter(notesFile));
+ final XmlSerializer serializer = Xml.newSerializer();
+
+ serializer.setOutput(out);
+ serializer.startDocument("UTF-8", true);
+ serializer.startTag("", Tags.NOTES);
+
+ final Time time = new Time();
+
+ final Uri notesUri = Notes.CONTENT_URI;
+ final Cursor cursor = resolver.query(notesUri, NotesQuery.PROJECTION, null, null,
+ Notes.DEFAULT_SORT);
+ try {
+ while (cursor.moveToNext()) {
+ serializer.startTag("", Tags.NOTE);
+ {
+ serializer.startTag("", Tags.SESSION_ID);
+ serializer.text(cursor.getString(NotesQuery.SESSION_ID));
+ serializer.endTag("", Tags.SESSION_ID);
+ }
+ {
+ serializer.startTag("", Tags.TIME);
+ time.set(cursor.getLong(NotesQuery.NOTE_TIME));
+ final String timeString = time.format3339(false);
+ serializer.text(timeString);
+ serializer.endTag("", Tags.TIME);
+ }
+ {
+ serializer.startTag("", Tags.CONTENT);
+ serializer.text(cursor.getString(NotesQuery.NOTE_CONTENT));
+ serializer.endTag("", Tags.CONTENT);
+ }
+ serializer.endTag("", Tags.NOTE);
+ }
+ } finally {
+ cursor.close();
+ }
+
+ serializer.endTag("", Tags.NOTES);
+ serializer.endDocument();
+
+ out.flush();
+ out.close();
+
+ return notesFile;
+ }
+
+ private interface Tags {
+ String NOTES = "notes";
+ String NOTE = "note";
+ String SESSION_ID = "sessionId";
+ String CONTENT = "content";
+ String TIME = "time";
+ }
+
+ private interface NotesQuery {
+ String[] PROJECTION = {
+ Notes.SESSION_ID,
+ Notes.NOTE_CONTENT,
+ Notes.NOTE_TIME,
+ };
+
+ int SESSION_ID = 0;
+ int NOTE_CONTENT = 1;
+ int NOTE_TIME = 2;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/NotifyingAsyncQueryHandler.java b/src/com/google/android/apps/iosched/util/NotifyingAsyncQueryHandler.java
new file mode 100644
index 0000000..5d56be3
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/NotifyingAsyncQueryHandler.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Slightly more abstract {@link AsyncQueryHandler} that helps keep a
+ * {@link WeakReference} back to a listener. Will properly close any
+ * {@link Cursor} if the listener ceases to exist.
+ *
+ * This pattern can be used to perform background queries without leaking
+ * {@link Context} objects.
+ *
+ * @hide pending API council review
+ */
+public class NotifyingAsyncQueryHandler extends AsyncQueryHandler {
+ private WeakReference mListener;
+
+ /**
+ * Interface to listen for completed query operations.
+ */
+ public interface AsyncQueryListener {
+ void onQueryComplete(int token, Object cookie, Cursor cursor);
+ }
+
+ public NotifyingAsyncQueryHandler(ContentResolver resolver, AsyncQueryListener listener) {
+ super(resolver);
+ setQueryListener(listener);
+ }
+
+ /**
+ * Assign the given {@link AsyncQueryListener} to receive query events from
+ * asynchronous calls. Will replace any existing listener.
+ */
+ public void setQueryListener(AsyncQueryListener listener) {
+ mListener = new WeakReference(listener);
+ }
+
+ /**
+ * Clear any {@link AsyncQueryListener} set through
+ * {@link #setQueryListener(AsyncQueryListener)}
+ */
+ public void clearQueryListener() {
+ mListener = null;
+ }
+
+ /**
+ * Begin an asynchronous query with the given arguments. When finished,
+ * {@link AsyncQueryListener#onQueryComplete(int, Object, Cursor)} is
+ * called if a valid {@link AsyncQueryListener} is present.
+ */
+ public void startQuery(Uri uri, String[] projection) {
+ startQuery(-1, null, uri, projection, null, null, null);
+ }
+
+ /**
+ * Begin an asynchronous query with the given arguments. When finished,
+ * {@link AsyncQueryListener#onQueryComplete(int, Object, Cursor)} is called
+ * if a valid {@link AsyncQueryListener} is present.
+ *
+ * @param token Unique identifier passed through to
+ * {@link AsyncQueryListener#onQueryComplete(int, Object, Cursor)}
+ */
+ public void startQuery(int token, Uri uri, String[] projection) {
+ startQuery(token, null, uri, projection, null, null, null);
+ }
+
+ /**
+ * Begin an asynchronous query with the given arguments. When finished,
+ * {@link AsyncQueryListener#onQueryComplete(int, Object, Cursor)} is called
+ * if a valid {@link AsyncQueryListener} is present.
+ */
+ public void startQuery(Uri uri, String[] projection, String sortOrder) {
+ startQuery(-1, null, uri, projection, null, null, sortOrder);
+ }
+
+ /**
+ * Begin an asynchronous query with the given arguments. When finished,
+ * {@link AsyncQueryListener#onQueryComplete(int, Object, Cursor)} is called
+ * if a valid {@link AsyncQueryListener} is present.
+ */
+ public void startQuery(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String orderBy) {
+ startQuery(-1, null, uri, projection, selection, selectionArgs, orderBy);
+ }
+
+ /**
+ * Begin an asynchronous update with the given arguments.
+ */
+ public void startUpdate(Uri uri, ContentValues values) {
+ startUpdate(-1, null, uri, values, null, null);
+ }
+
+ public void startInsert(Uri uri, ContentValues values) {
+ startInsert(-1, null, uri, values);
+ }
+
+ public void startDelete(Uri uri) {
+ startDelete(-1, null, uri, null, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ final AsyncQueryListener listener = mListener == null ? null : mListener.get();
+ if (listener != null) {
+ listener.onQueryComplete(token, cookie, cursor);
+ } else if (cursor != null) {
+ cursor.close();
+ }
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/ParserUtils.java b/src/com/google/android/apps/iosched/util/ParserUtils.java
new file mode 100644
index 0000000..7c90fb1
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/ParserUtils.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import com.google.android.apps.iosched.io.XmlHandler;
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.provider.ScheduleContract.SyncColumns;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import android.content.ContentProvider;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.text.format.Time;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * Various utility methods used by {@link XmlHandler} implementations.
+ */
+public class ParserUtils {
+ // TODO: consider refactor to HandlerUtils?
+
+ // TODO: localize this string at some point
+ public static final String BLOCK_TITLE_BREAKOUT_SESSIONS = "Breakout sessions";
+
+ public static final String BLOCK_TYPE_FOOD = "food";
+ public static final String BLOCK_TYPE_SESSION = "session";
+ public static final String BLOCK_TYPE_OFFICE_HOURS = "officehours";
+
+ public static final Set LOCAL_TRACK_IDS = Sets.newHashSet(
+ "android", "appengine", "chrome", "enterprise", "firesidechats", "geo",
+ "googleapis", "googlewebtoolkit", "socialweb", "techtalks", "wave");
+
+ /** Used to sanitize a string to be {@link Uri} safe. */
+ private static final Pattern sSanitizePattern = Pattern.compile("[^a-z0-9-_]");
+ private static final Pattern sParenPattern = Pattern.compile("\\(.*?\\)");
+
+ /** Used to split a comma-separated string. */
+ private static final Pattern sCommaPattern = Pattern.compile("\\s*,\\s*");
+
+ private static Time sTime = new Time();
+ private static XmlPullParserFactory sFactory;
+
+ /**
+ * Sanitize the given string to be {@link Uri} safe for building
+ * {@link ContentProvider} paths.
+ */
+ public static String sanitizeId(String input) {
+ return sanitizeId(input, false);
+ }
+
+ /**
+ * Sanitize the given string to be {@link Uri} safe for building
+ * {@link ContentProvider} paths.
+ */
+ public static String sanitizeId(String input, boolean stripParen) {
+ if (input == null) return null;
+ if (stripParen) {
+ // Strip out all parenthetical statements when requested.
+ input = sParenPattern.matcher(input).replaceAll("");
+ }
+ return sSanitizePattern.matcher(input.toLowerCase()).replaceAll("");
+ }
+
+ /**
+ * Split the given comma-separated string, returning all values.
+ */
+ public static String[] splitComma(CharSequence input) {
+ if (input == null) return new String[0];
+ return sCommaPattern.split(input);
+ }
+
+ /**
+ * Build and return a new {@link XmlPullParser} with the given
+ * {@link InputStream} assigned to it.
+ */
+ public static XmlPullParser newPullParser(InputStream input) throws XmlPullParserException {
+ if (sFactory == null) {
+ sFactory = XmlPullParserFactory.newInstance();
+ }
+ final XmlPullParser parser = sFactory.newPullParser();
+ parser.setInput(input, null);
+ return parser;
+ }
+
+ /**
+ * Parse the given string as a RFC 3339 timestamp, returning the value as
+ * milliseconds since the epoch.
+ */
+ public static long parseTime(String time) {
+ sTime.parse3339(time);
+ return sTime.toMillis(false);
+ }
+
+ /**
+ * Return a {@link Blocks#BLOCK_ID} matching the requested arguments.
+ */
+ public static String findBlock(String title, long startTime, long endTime) {
+ // TODO: in future we might check provider if block exists
+ return Blocks.generateBlockId(startTime, endTime);
+ }
+
+ /**
+ * Return a {@link Blocks#BLOCK_ID} matching the requested arguments,
+ * inserting a new {@link Blocks} entry as a
+ * {@link ContentProviderOperation} when none already exists.
+ */
+ public static String findOrCreateBlock(String title, String type, long startTime, long endTime,
+ ArrayList batch, ContentResolver resolver) {
+ // TODO: check for existence instead of always blindly creating. it's
+ // okay for now since the database replaces on conflict.
+ final ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newInsert(Blocks.CONTENT_URI);
+ final String blockId = Blocks.generateBlockId(startTime, endTime);
+ builder.withValue(Blocks.BLOCK_ID, blockId);
+ builder.withValue(Blocks.BLOCK_TITLE, title);
+ builder.withValue(Blocks.BLOCK_START, startTime);
+ builder.withValue(Blocks.BLOCK_END, endTime);
+ builder.withValue(Blocks.BLOCK_TYPE, type);
+ batch.add(builder.build());
+ return blockId;
+ }
+
+ /**
+ * Query and return the {@link SyncColumns#UPDATED} time for the requested
+ * {@link Uri}. Expects the {@link Uri} to reference a single item.
+ */
+ public static long queryItemUpdated(Uri uri, ContentResolver resolver) {
+ final String[] projection = { SyncColumns.UPDATED };
+ final Cursor cursor = resolver.query(uri, projection, null, null, null);
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getLong(0);
+ } else {
+ return ScheduleContract.UPDATED_NEVER;
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Query and return the newest {@link SyncColumns#UPDATED} time for all
+ * entries under the requested {@link Uri}. Expects the {@link Uri} to
+ * reference a directory of several items.
+ */
+ public static long queryDirUpdated(Uri uri, ContentResolver resolver) {
+ final String[] projection = { "MAX(" + SyncColumns.UPDATED + ")" };
+ final Cursor cursor = resolver.query(uri, projection, null, null, null);
+ try {
+ cursor.moveToFirst();
+ return cursor.getLong(0);
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Translate an incoming {@link Tracks#TRACK_ID}, usually passing directly
+ * through, but returning a different value when a local alias is defined.
+ */
+ public static String translateTrackIdAlias(String trackId) {
+ if ("gwt".equals(trackId)) {
+ return "googlewebtoolkit";
+ } else {
+ return trackId;
+ }
+ }
+
+ /**
+ * Translate a possibly locally aliased {@link Tracks#TRACK_ID} to its real value;
+ * this usually is a pass-through.
+ */
+ public static String translateTrackIdAliasInverse(String trackId) {
+ if ("googlewebtoolkit".equals(trackId)) {
+ return "gwt";
+ } else {
+ return trackId;
+ }
+ }
+
+ /** XML tag constants used by the Atom standard. */
+ public interface AtomTags {
+ String ENTRY = "entry";
+ String UPDATED = "updated";
+ String TITLE = "title";
+ String LINK = "link";
+ String CONTENT = "content";
+
+ String REL = "rel";
+ String HREF = "href";
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/SelectionBuilder.java b/src/com/google/android/apps/iosched/util/SelectionBuilder.java
new file mode 100644
index 0000000..0d3ba4e
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/SelectionBuilder.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+/*
+ * Modifications:
+ * -Imported from AOSP frameworks/base/core/java/com/android/internal/content
+ * -Changed package name
+ */
+
+package com.google.android.apps.iosched.util;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * Helper for building selection clauses for {@link SQLiteDatabase}. Each
+ * appended clause is combined using {@code AND}. This class is not
+ * thread safe.
+ */
+public class SelectionBuilder {
+ private static final String TAG = "SelectionBuilder";
+ private static final boolean LOGV = false;
+
+ private String mTable = null;
+ private Map mProjectionMap = Maps.newHashMap();
+ private StringBuilder mSelection = new StringBuilder();
+ private ArrayList mSelectionArgs = Lists.newArrayList();
+
+ /**
+ * Reset any internal state, allowing this builder to be recycled.
+ */
+ public SelectionBuilder reset() {
+ mTable = null;
+ mSelection.setLength(0);
+ mSelectionArgs.clear();
+ return this;
+ }
+
+ /**
+ * Append the given selection clause to the internal state. Each clause is
+ * surrounded with parenthesis and combined using {@code AND}.
+ */
+ public SelectionBuilder where(String selection, String... selectionArgs) {
+ if (TextUtils.isEmpty(selection)) {
+ if (selectionArgs != null && selectionArgs.length > 0) {
+ throw new IllegalArgumentException(
+ "Valid selection required when including arguments=");
+ }
+
+ // Shortcut when clause is empty
+ return this;
+ }
+
+ if (mSelection.length() > 0) {
+ mSelection.append(" AND ");
+ }
+
+ mSelection.append("(").append(selection).append(")");
+ if (selectionArgs != null) {
+ for (String arg : selectionArgs) {
+ mSelectionArgs.add(arg);
+ }
+ }
+
+ return this;
+ }
+
+ public SelectionBuilder table(String table) {
+ mTable = table;
+ return this;
+ }
+
+ private void assertTable() {
+ if (mTable == null) {
+ throw new IllegalStateException("Table not specified");
+ }
+ }
+
+ public SelectionBuilder mapToTable(String column, String table) {
+ mProjectionMap.put(column, table + "." + column);
+ return this;
+ }
+
+ public SelectionBuilder map(String fromColumn, String toClause) {
+ mProjectionMap.put(fromColumn, toClause + " AS " + fromColumn);
+ return this;
+ }
+
+ /**
+ * Return selection string for current internal state.
+ *
+ * @see #getSelectionArgs()
+ */
+ public String getSelection() {
+ return mSelection.toString();
+ }
+
+ /**
+ * Return selection arguments for current internal state.
+ *
+ * @see #getSelection()
+ */
+ public String[] getSelectionArgs() {
+ return mSelectionArgs.toArray(new String[mSelectionArgs.size()]);
+ }
+
+ private void mapColumns(String[] columns) {
+ for (int i = 0; i < columns.length; i++) {
+ final String target = mProjectionMap.get(columns[i]);
+ if (target != null) {
+ columns[i] = target;
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SelectionBuilder[table=" + mTable + ", selection=" + getSelection()
+ + ", selectionArgs=" + Arrays.toString(getSelectionArgs()) + "]";
+ }
+
+ /**
+ * Execute query using the current internal state as {@code WHERE} clause.
+ */
+ public Cursor query(SQLiteDatabase db, String[] columns, String orderBy) {
+ return query(db, columns, null, null, orderBy, null);
+ }
+
+ /**
+ * Execute query using the current internal state as {@code WHERE} clause.
+ */
+ public Cursor query(SQLiteDatabase db, String[] columns, String groupBy,
+ String having, String orderBy, String limit) {
+ assertTable();
+ if (columns != null) mapColumns(columns);
+ if (LOGV) Log.v(TAG, "query(columns=" + Arrays.toString(columns) + ") " + this);
+ return db.query(mTable, columns, getSelection(), getSelectionArgs(), groupBy, having,
+ orderBy, limit);
+ }
+
+ /**
+ * Execute update using the current internal state as {@code WHERE} clause.
+ */
+ public int update(SQLiteDatabase db, ContentValues values) {
+ assertTable();
+ if (LOGV) Log.v(TAG, "update() " + this);
+ return db.update(mTable, values, getSelection(), getSelectionArgs());
+ }
+
+ /**
+ * Execute delete using the current internal state as {@code WHERE} clause.
+ */
+ public int delete(SQLiteDatabase db) {
+ assertTable();
+ if (LOGV) Log.v(TAG, "delete() " + this);
+ return db.delete(mTable, getSelection(), getSelectionArgs());
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/Sets.java b/src/com/google/android/apps/iosched/util/Sets.java
new file mode 100644
index 0000000..7f93c4e
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/Sets.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Provides static methods for creating mutable {@code Set} instances easily and
+ * other static methods for working with Sets.
+ *
+ */
+public class Sets {
+
+ /**
+ * Creates an empty {@code HashSet} instance.
+ *
+ *
Note: if {@code E} is an {@link Enum} type, use {@link
+ * EnumSet#noneOf} instead.
+ *
+ *
Note: if you only need an immutable empty Set,
+ * use {@link Collections#emptySet} instead.
+ *
+ * @return a newly-created, initially-empty {@code HashSet}
+ */
+ public static HashSet newHashSet() {
+ return new HashSet();
+ }
+
+ /**
+ * Creates a {@code HashSet} instance containing the given elements.
+ *
+ *
Note: due to a bug in javac 1.5.0_06, we cannot support the
+ * following:
+ *
+ *
{@code Set set = Sets.newHashSet(sub1, sub2);}
+ *
+ *
where {@code sub1} and {@code sub2} are references to subtypes of {@code
+ * Base}, not of {@code Base} itself. To get around this, you must use:
+ *
+ *
{@code Set set = Sets.newHashSet(sub1, sub2);}
+ *
+ * @param elements the elements that the set should contain
+ * @return a newly-created {@code HashSet} containing those elements (minus
+ * duplicates)
+ */
+ public static HashSet newHashSet(E... elements) {
+ int capacity = elements.length * 4 / 3 + 1;
+ HashSet set = new HashSet(capacity);
+ Collections.addAll(set, elements);
+ return set;
+ }
+
+ /**
+ * Creates a {@code SortedSet} instance containing the given elements.
+ *
+ * @param elements the elements that the set should contain
+ * @return a newly-created {@code SortedSet} containing those elements (minus
+ * duplicates)
+ */
+ public static SortedSet newSortedSet(E... elements) {
+ SortedSet set = new TreeSet();
+ Collections.addAll(set, elements);
+ return set;
+ }
+
+}
diff --git a/src/com/google/android/apps/iosched/util/SpreadsheetEntry.java b/src/com/google/android/apps/iosched/util/SpreadsheetEntry.java
new file mode 100644
index 0000000..3336e5c
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/SpreadsheetEntry.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.CONTENT;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.UPDATED;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class SpreadsheetEntry extends HashMap {
+
+ private static final Pattern sContentPattern = Pattern.compile(
+ "(?:^|, )([_a-zA-Z0-9]+): (.*?)(?=\\s*$|, [_a-zA-Z0-9]+: )", Pattern.DOTALL);
+
+ private static Matcher sContentMatcher;
+
+ private static Matcher getContentMatcher(CharSequence input) {
+ if (sContentMatcher == null) {
+ sContentMatcher = sContentPattern.matcher(input);
+ } else {
+ sContentMatcher.reset(input);
+ }
+ return sContentMatcher;
+ }
+
+ private long mUpdated;
+
+ public long getUpdated() {
+ return mUpdated;
+ }
+
+ public static SpreadsheetEntry fromParser(XmlPullParser parser) throws XmlPullParserException,
+ IOException {
+ final int depth = parser.getDepth();
+ final SpreadsheetEntry entry = new SpreadsheetEntry();
+
+ String tag = null;
+ int type;
+ while (((type = parser.next()) != END_TAG ||
+ parser.getDepth() > depth) && type != END_DOCUMENT) {
+ if (type == START_TAG) {
+ tag = parser.getName();
+ } else if (type == END_TAG) {
+ tag = null;
+ } else if (type == TEXT) {
+ if (UPDATED.equals(tag)) {
+ final String text = parser.getText();
+ entry.mUpdated = ParserUtils.parseTime(text);
+ } else if (CONTENT.equals(tag)) {
+ final String text = parser.getText();
+ final Matcher matcher = getContentMatcher(text);
+ while (matcher.find()) {
+ final String key = matcher.group(1);
+ final String value = matcher.group(2).trim();
+ entry.put(key, value);
+ }
+ }
+ }
+ }
+ return entry;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/UIUtils.java b/src/com/google/android/apps/iosched/util/UIUtils.java
new file mode 100644
index 0000000..c199ba9
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/UIUtils.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import com.google.android.apps.iosched.R;
+import com.google.android.apps.iosched.provider.ScheduleContract.Blocks;
+import com.google.android.apps.iosched.provider.ScheduleContract.Rooms;
+import com.google.android.apps.iosched.ui.HomeActivity;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.graphics.drawable.LevelListDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.format.DateUtils;
+import android.text.method.LinkMovementMethod;
+import android.text.style.StyleSpan;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.TextView;
+
+import java.util.Formatter;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class UIUtils {
+
+ /**
+ * Time zone to use when formatting all session times. To always use the
+ * phone local time, use {@link TimeZone#getDefault()}.
+ */
+ public static TimeZone CONFERENCE_TIME_ZONE = TimeZone.getTimeZone("America/Los_Angeles");
+
+ public static final long CONFERENCE_START_MILLIS = ParserUtils.parseTime(
+ "2010-05-19T09:00:00.000-07:00");
+ public static final long CONFERENCE_END_MILLIS = ParserUtils.parseTime(
+ "2010-05-20T17:30:00.000-07:00");
+
+ public static final Uri CONFERENCE_URL = Uri.parse("http://code.google.com/events/io/2010/");
+
+ /** Flags used with {@link DateUtils#formatDateRange}. */
+ private static final int TIME_FLAGS = DateUtils.FORMAT_SHOW_TIME
+ | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_WEEKDAY;
+
+ private static final int BRIGHTNESS_THRESHOLD = 150;
+
+ /** {@link StringBuilder} used for formatting time block. */
+ private static StringBuilder sBuilder = new StringBuilder(50);
+ /** {@link Formatter} used for formatting time block. */
+ private static Formatter sFormatter = new Formatter(sBuilder, Locale.getDefault());
+
+ private static StyleSpan sBoldSpan = new StyleSpan(Typeface.BOLD);
+
+ public static void setTitleBarColor(View titleBarView, int color) {
+ final ViewGroup titleBar = (ViewGroup) titleBarView;
+ titleBar.setBackgroundColor(color);
+
+ /*
+ * Calculate the brightness of the titlebar color, based on the commonly known
+ * brightness formula:
+ *
+ * http://en.wikipedia.org/wiki/HSV_color_space%23Lightness
+ */
+ int brColor = (30 * Color.red(color) +
+ 59 * Color.green(color) +
+ 11 * Color.blue(color)) / 100;
+ if (brColor > BRIGHTNESS_THRESHOLD) {
+ ((TextView) titleBar.findViewById(R.id.title_text)).setTextColor(
+ titleBar.getContext().getResources().getColor(R.color.title_text_alt));
+
+ // Iterate through all children of the titlebar and if they're a LevelListDrawable,
+ // set their level to 1 (alternate).
+ // TODO: find a less hacky way of doing this.
+ titleBar.post(new Runnable() {
+ public void run() {
+ final int childCount = titleBar.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = titleBar.getChildAt(i);
+ if (child instanceof ImageButton) {
+ final ImageButton childButton = (ImageButton) child;
+ if (childButton.getDrawable() != null &&
+ childButton.getDrawable() instanceof LevelListDrawable) {
+ ((LevelListDrawable) childButton.getDrawable()).setLevel(1);
+ }
+ }
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Invoke "home" action, returning to {@link HomeActivity}.
+ */
+ public static void goHome(Context context) {
+ final Intent intent = new Intent(context, HomeActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ context.startActivity(intent);
+ }
+
+ /**
+ * Invoke "search" action, triggering a default search.
+ */
+ public static void goSearch(Activity activity) {
+ activity.startSearch(null, false, Bundle.EMPTY, false);
+ }
+
+ /**
+ * Format and return the given {@link Blocks} and {@link Rooms} values using
+ * {@link #CONFERENCE_TIME_ZONE}.
+ */
+ public static String formatSessionSubtitle(long blockStart, long blockEnd,
+ String roomName, Context context) {
+ TimeZone.setDefault(CONFERENCE_TIME_ZONE);
+
+ // NOTE: There is an efficient version of formatDateRange in Eclair and
+ // beyond that allows you to recycle a StringBuilder.
+ final CharSequence timeString = DateUtils.formatDateRange(context,
+ blockStart, blockEnd, TIME_FLAGS);
+
+ return context.getString(R.string.session_subtitle, timeString, roomName);
+ }
+
+ /**
+ * Populate the given {@link TextView} with the requested text, formatting
+ * through {@link Html#fromHtml(String)} when applicable. Also sets
+ * {@link TextView#setMovementMethod} so inline links are handled.
+ */
+ public static void setTextMaybeHtml(TextView view, String text) {
+ if (text.contains("<") && text.contains(">")) {
+ view.setText(Html.fromHtml(text));
+ view.setMovementMethod(LinkMovementMethod.getInstance());
+ } else {
+ view.setText(text);
+ }
+ }
+
+ public static void setSessionTitleColor(long blockStart, long blockEnd, TextView title,
+ TextView subtitle) {
+ long currentTimeMillis = System.currentTimeMillis();
+ int colorId = android.R.color.primary_text_light;
+ int subColorId = android.R.color.secondary_text_light;
+
+ if (currentTimeMillis > blockEnd &&
+ currentTimeMillis < CONFERENCE_END_MILLIS) {
+ colorId = subColorId = R.color.session_foreground_past;
+ }
+
+ final Resources res = title.getResources();
+ title.setTextColor(res.getColor(colorId));
+ subtitle.setTextColor(res.getColor(subColorId));
+ }
+
+ /**
+ * Given a snippet string with matching segments surrounded by curly
+ * braces, turn those areas into bold spans, removing the curly braces.
+ */
+ public static Spannable buildStyledSnippet(String snippet) {
+ final SpannableStringBuilder builder = new SpannableStringBuilder(snippet);
+
+ // Walk through string, inserting bold snippet spans
+ int startIndex = -1, endIndex = -1, delta = 0;
+ while ((startIndex = snippet.indexOf('{', endIndex)) != -1) {
+ endIndex = snippet.indexOf('}', startIndex);
+
+ // Remove braces from both sides
+ builder.delete(startIndex - delta, startIndex - delta + 1);
+ builder.delete(endIndex - delta - 1, endIndex - delta);
+
+ // Insert bold style
+ builder.setSpan(sBoldSpan, startIndex - delta, endIndex - delta - 1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ delta += 2;
+ }
+
+ return builder;
+ }
+}
diff --git a/src/com/google/android/apps/iosched/util/WorksheetEntry.java b/src/com/google/android/apps/iosched/util/WorksheetEntry.java
new file mode 100644
index 0000000..4b4eae8
--- /dev/null
+++ b/src/com/google/android/apps/iosched/util/WorksheetEntry.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.HREF;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.LINK;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.REL;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.TITLE;
+import static com.google.android.apps.iosched.util.ParserUtils.AtomTags.UPDATED;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.text.format.DateUtils;
+
+import java.io.IOException;
+
+public class WorksheetEntry {
+ private static final String REL_LISTFEED = "http://schemas.google.com/spreadsheets/2006#listfeed";
+
+ private long mUpdated;
+ private String mTitle;
+ private String mListFeed;
+
+ public long getUpdated() {
+ return mUpdated;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public String getListFeed() {
+ return mListFeed;
+ }
+
+ @Override
+ public String toString() {
+ return "title=" + mTitle + ", updated=" + mUpdated + " ("
+ + DateUtils.getRelativeTimeSpanString(mUpdated) + ")";
+ }
+
+ public static WorksheetEntry fromParser(XmlPullParser parser) throws XmlPullParserException,
+ IOException {
+ final int depth = parser.getDepth();
+ final WorksheetEntry entry = new WorksheetEntry();
+
+ String tag = null;
+ int type;
+ while (((type = parser.next()) != END_TAG ||
+ parser.getDepth() > depth) && type != END_DOCUMENT) {
+ if (type == START_TAG) {
+ tag = parser.getName();
+ if (LINK.equals(tag)) {
+ final String rel = parser.getAttributeValue(null, REL);
+ final String href = parser.getAttributeValue(null, HREF);
+ if (REL_LISTFEED.equals(rel)) {
+ entry.mListFeed = href;
+ }
+ }
+ } else if (type == END_TAG) {
+ tag = null;
+ } else if (type == TEXT) {
+ final String text = parser.getText();
+ if (TITLE.equals(tag)) {
+ entry.mTitle = text;
+ } else if (UPDATED.equals(tag)) {
+ entry.mUpdated = ParserUtils.parseTime(text);
+ }
+ }
+ }
+ return entry;
+ }
+}
diff --git a/tests/.svn/all-wcprops b/tests/.svn/all-wcprops
new file mode 100644
index 0000000..da7a373
--- /dev/null
+++ b/tests/.svn/all-wcprops
@@ -0,0 +1,35 @@
+K 25
+svn:wc:ra_dav:version-url
+V 27
+/svn/!svn/ver/2/trunk/tests
+END
+default.properties
+K 25
+svn:wc:ra_dav:version-url
+V 46
+/svn/!svn/ver/2/trunk/tests/default.properties
+END
+local.properties
+K 25
+svn:wc:ra_dav:version-url
+V 44
+/svn/!svn/ver/2/trunk/tests/local.properties
+END
+AndroidManifest.xml
+K 25
+svn:wc:ra_dav:version-url
+V 47
+/svn/!svn/ver/2/trunk/tests/AndroidManifest.xml
+END
+build.properties
+K 25
+svn:wc:ra_dav:version-url
+V 44
+/svn/!svn/ver/2/trunk/tests/build.properties
+END
+build.xml
+K 25
+svn:wc:ra_dav:version-url
+V 37
+/svn/!svn/ver/2/trunk/tests/build.xml
+END
diff --git a/tests/.svn/entries b/tests/.svn/entries
new file mode 100644
index 0000000..fc8a448
--- /dev/null
+++ b/tests/.svn/entries
@@ -0,0 +1,210 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/tests
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+default.properties
+file
+
+
+
+
+2010-06-03T15:23:15.282655Z
+b53da05dcecb5aba8e60105472da8e70
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+449
+
+assets
+dir
+
+local.properties
+file
+
+
+
+
+2010-06-03T15:23:15.282655Z
+89e0b3a2c2aafb770e7788b734db6e93
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+42
+
+AndroidManifest.xml
+file
+
+
+
+
+2010-06-03T15:23:15.282655Z
+4a045412a6045a3b06f691ef7d4b680d
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1085
+
+src
+dir
+
+libs
+dir
+
+res
+dir
+
+build.properties
+file
+
+
+
+
+2010-06-03T15:23:15.282655Z
+6af00d624e8f3a69968254a55fa3ef31
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+719
+
+build.xml
+file
+
+
+
+
+2010-06-03T15:23:15.282655Z
+1f413203f943376315a2f4a86dfa0328
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+711
+
diff --git a/tests/.svn/text-base/AndroidManifest.xml.svn-base b/tests/.svn/text-base/AndroidManifest.xml.svn-base
new file mode 100644
index 0000000..298b490
--- /dev/null
+++ b/tests/.svn/text-base/AndroidManifest.xml.svn-base
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/.svn/text-base/build.properties.svn-base b/tests/.svn/text-base/build.properties.svn-base
new file mode 100644
index 0000000..6522258
--- /dev/null
+++ b/tests/.svn/text-base/build.properties.svn-base
@@ -0,0 +1,18 @@
+# This file is used to override default values used by the Ant build system.
+#
+# This file must be checked in Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# You can use this to override default values such as
+# 'source.dir' for the location of your java source folder and
+# 'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+# 'key.store' for the location of your keystore and
+# 'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+
+tested.project.dir=..
diff --git a/tests/.svn/text-base/build.xml.svn-base b/tests/.svn/text-base/build.xml.svn-base
new file mode 100644
index 0000000..b2a5518
--- /dev/null
+++ b/tests/.svn/text-base/build.xml.svn-base
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/.svn/text-base/default.properties.svn-base b/tests/.svn/text-base/default.properties.svn-base
new file mode 100644
index 0000000..6017a4b
--- /dev/null
+++ b/tests/.svn/text-base/default.properties.svn-base
@@ -0,0 +1,13 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Indicates whether an apk should be generated for each density.
+split.density=false
+# Project target.
+target=android-6
diff --git a/tests/.svn/text-base/local.properties.svn-base b/tests/.svn/text-base/local.properties.svn-base
new file mode 100644
index 0000000..8c4030b
--- /dev/null
+++ b/tests/.svn/text-base/local.properties.svn-base
@@ -0,0 +1,2 @@
+sdk.dir=/home/jsharkey/android-sdk-linux
+
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
new file mode 100644
index 0000000..298b490
--- /dev/null
+++ b/tests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/assets/.svn/all-wcprops b/tests/assets/.svn/all-wcprops
new file mode 100644
index 0000000..b3bd83f
--- /dev/null
+++ b/tests/assets/.svn/all-wcprops
@@ -0,0 +1,65 @@
+K 25
+svn:wc:ra_dav:version-url
+V 34
+/svn/!svn/ver/2/trunk/tests/assets
+END
+remote-sessions1.xml
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/2/trunk/tests/assets/remote-sessions1.xml
+END
+remote-sessions2.xml
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/2/trunk/tests/assets/remote-sessions2.xml
+END
+spreadsheet-empty.xml
+K 25
+svn:wc:ra_dav:version-url
+V 56
+/svn/!svn/ver/2/trunk/tests/assets/spreadsheet-empty.xml
+END
+local-blocks.xml
+K 25
+svn:wc:ra_dav:version-url
+V 51
+/svn/!svn/ver/2/trunk/tests/assets/local-blocks.xml
+END
+spreadsheet-single.xml
+K 25
+svn:wc:ra_dav:version-url
+V 57
+/svn/!svn/ver/2/trunk/tests/assets/spreadsheet-single.xml
+END
+local-rooms.xml
+K 25
+svn:wc:ra_dav:version-url
+V 50
+/svn/!svn/ver/2/trunk/tests/assets/local-rooms.xml
+END
+spreadsheet-emptyfield.xml
+K 25
+svn:wc:ra_dav:version-url
+V 61
+/svn/!svn/ver/2/trunk/tests/assets/spreadsheet-emptyfield.xml
+END
+spreadsheet-normal.xml
+K 25
+svn:wc:ra_dav:version-url
+V 57
+/svn/!svn/ver/2/trunk/tests/assets/spreadsheet-normal.xml
+END
+local-sessions.xml
+K 25
+svn:wc:ra_dav:version-url
+V 53
+/svn/!svn/ver/2/trunk/tests/assets/local-sessions.xml
+END
+local-tracks.xml
+K 25
+svn:wc:ra_dav:version-url
+V 51
+/svn/!svn/ver/2/trunk/tests/assets/local-tracks.xml
+END
diff --git a/tests/assets/.svn/entries b/tests/assets/.svn/entries
new file mode 100644
index 0000000..12912de
--- /dev/null
+++ b/tests/assets/.svn/entries
@@ -0,0 +1,368 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/tests/assets
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+remote-sessions1.xml
+file
+
+
+
+
+2010-06-03T15:23:14.701646Z
+6d5de74a71c6ebb170c396fce8ee3d95
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10629
+
+remote-sessions2.xml
+file
+
+
+
+
+2010-06-03T15:23:14.701646Z
+0b7a5ce101a1473c317abfb2dcfd5e37
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10668
+
+spreadsheet-empty.xml
+file
+
+
+
+
+2010-06-03T15:23:14.701646Z
+a3aab51d36e90a5616bb2b59b8876e54
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+92
+
+local-blocks.xml
+file
+
+
+
+
+2010-06-03T15:23:14.701646Z
+af8374864dd2f8202152c8435400251d
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+456
+
+spreadsheet-single.xml
+file
+
+
+
+
+2010-06-03T15:23:14.701646Z
+f2c12aa1cba42f98ca4aee546c75fe7b
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+121
+
+local-rooms.xml
+file
+
+
+
+
+2010-06-03T15:23:14.701646Z
+fc2f17f5f4205b11e91b18161d039c0f
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1384
+
+spreadsheet-emptyfield.xml
+file
+
+
+
+
+2010-06-03T15:23:14.701646Z
+f240762d8289094aabd51b293ba9dc1b
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+145
+
+spreadsheet-normal.xml
+file
+
+
+
+
+2010-06-03T15:23:14.701646Z
+6709ee0c6a38b9f5aaf14468b8b5e14b
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1560
+
+local-sessions.xml
+file
+
+
+
+
+2010-06-03T15:23:14.701646Z
+27f42abff15ab075b88245dd6f8cdb22
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+610
+
+local-tracks.xml
+file
+
+
+
+
+2010-06-03T15:23:14.701646Z
+ac6df67e4b442c9189126e27e5110456
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5440
+
diff --git a/tests/assets/.svn/text-base/local-blocks.xml.svn-base b/tests/assets/.svn/text-base/local-blocks.xml.svn-base
new file mode 100644
index 0000000..6a9e432
--- /dev/null
+++ b/tests/assets/.svn/text-base/local-blocks.xml.svn-base
@@ -0,0 +1,17 @@
+
+
+
+
+ 2010-05-19T10:45:00.000-07:00
+ 2010-05-19T11:45:00.000-07:00
+ Office hours
+ officehours
+
+
+ 2010-05-19T05:00:00.000-07:00
+ 2010-05-19T12:00:00.000-07:00
+ Office hours
+ officehours
+
+
+
diff --git a/tests/assets/.svn/text-base/local-rooms.xml.svn-base b/tests/assets/.svn/text-base/local-rooms.xml.svn-base
new file mode 100644
index 0000000..dc36159
--- /dev/null
+++ b/tests/assets/.svn/text-base/local-rooms.xml.svn-base
@@ -0,0 +1,24 @@
+
+
+
+ 1Room 12
+ 2Room 22
+ 3Room 32
+ 4Room 42
+ 5Room 52
+ 6Room 62
+ 7Room 72
+ 8Room 82
+ 9Room 92
+
+ firesidechatroomFireside Chat Room2
+
+ officehoursspaceaOffice Hours Space A2
+ officehoursspacebOffice Hours Space B2
+ officehoursspacecOffice Hours Space C2
+ officehoursspacedOffice Hours Space D2
+ officehoursspaceeOffice Hours Space E2
+ officehoursspacefOffice Hours Space F2
+ officehoursspacegOffice Hours Space G2
+
+
diff --git a/tests/assets/.svn/text-base/local-sessions.xml.svn-base b/tests/assets/.svn/text-base/local-sessions.xml.svn-base
new file mode 100644
index 0000000..34cb4e5
--- /dev/null
+++ b/tests/assets/.svn/text-base/local-sessions.xml.svn-base
@@ -0,0 +1,21 @@
+
+
+
+
+ beginners-guide-android
+ 2010-05-19T10:45:00.000-07:00
+ 2010-05-19T11:45:00.000-07:00
+ 4
+
+ A beginner's guide to Android
+
+
+
+ 2010-05-19T05:00:00.000-07:00
+ 2010-05-19T12:00:00.000-07:00
+ 6
+
+ Writing real-time games for Android redux
+
+
+
diff --git a/tests/assets/.svn/text-base/local-tracks.xml.svn-base b/tests/assets/.svn/text-base/local-tracks.xml.svn-base
new file mode 100644
index 0000000..26e60a7
--- /dev/null
+++ b/tests/assets/.svn/text-base/local-tracks.xml.svn-base
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/assets/.svn/text-base/remote-sessions1.xml.svn-base b/tests/assets/.svn/text-base/remote-sessions1.xml.svn-base
new file mode 100644
index 0000000..284c87f
--- /dev/null
+++ b/tests/assets/.svn/text-base/remote-sessions1.xml.svn-base
@@ -0,0 +1,41 @@
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic2010-05-06T23:03:04.251Z
+ sessions
+
+
+
+ christine.leechristine.lee@gmail.com
+
+ 821
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cokwr2010-05-06T23:03:04.251Z
+ Row: 2sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 6, product: Android, track: Android, sessiontype: 101, sessiontitle: A beginner's guide to Android, tags: Android, Mobile, Java, sessionspeakers: Reto Meier, speakers: retomeier, sessionabstract: This session will introduce some of the basic concepts involved in Android development. Starting with an overview of the SDK APIs available to developers, we will work through some simple code examples that explore some of the more common user features including using sensors, maps, and geolocation., sessionrequirements: Proficiency in Java and a basic understanding of embedded environments like mobile phones , sessionlink: beginners-guide-android, sessionhashtag: #android1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.45, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B-Xhdu7ZkBHw, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B-Xhdu7ZkBHw, waveid: w+-Xhdu7ZkBHw
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ckd7g2010-05-06T23:03:04.251Z
+ Row: 3sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: Writing real-time games for Android redux, tags: Android, Mobile, Java, Games, 2D, 3D, C++, sessionspeakers: Chris Pruett, speakers: pruett, sessionabstract: This session is a crash course in Android game development: everything you need to know to get started writing 2D and 3D games, as well as tips, tricks, and benchmarks to help your code reach optimal performance. In addition, we'll discuss hot topics related to game development, including hardware differences across devices, using C++ to write Android games, and the traits of the most popular games on Market., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: writing-real-time-games-android, sessionhashtag: #android2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.9B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BWDgRyLZkaR, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BWDgRyLZkaR, waveid: w+WDgRyLZkaR
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/clrrx2010-05-06T23:03:04.251Z
+ Row: 4sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: The world of ListView, tags: Android, Mobile, Java, sessionspeakers: Romain Guy, Adam Powell, speakers: romainguy, adamp, sessionabstract: ListView is one of the most widely used Android widgets but also the most complex one. Join us to learn how to master ListView and learn all about its features, optimizations, quirks and limitations.
+ , sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: world-of-listview-android, sessionhashtag: #android3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.47, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv7BXJrZkBJT, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv7BXJrZkBJT, waveid: w+v7BXJrZkBJT
+
+
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d180g2010-05-06T23:03:04.251Z
+ Row: 5sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: Casting a wide net: how to target all Android devices, tags: Android, Mobile, Java, sessionspeakers: Justin Mattson, speakers: jmatt, sessionabstract: One of Android's strengths is its flexibility to run on a wide variety of devices. In this session, we will explore the facilities the Android resource system provides to developers to make supporting many devices from one application binary easier, as well as common pitfalls. In addition to hardware heterogeneity, more than one version of Android may exist in the wild at any given time. We will go over strategies for providing cross-version compatibility., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: casting-wide-net-android-devices, sessionhashtag: #android4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.48, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BfZ2urbZk7o, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BfZ2urbZk7o, waveid: w+fZ2urbZk7o
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d2mkx2010-05-06T23:03:04.251Z
+ Row: 6sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 6, product: Android, track: Android, Enterprise, sessiontype: 201, sessiontitle: Android UI design patterns, tags: Android, Mobile, Java, sessionspeakers: Chris Nesladek, German Bauer, Richard Fulcher, Christian Robertson, Jim Palmer, speakers: cnesladek, germanb, rfulcher, robertsonc, jimpalmer, sessionabstract: In this session, the Android User Experience team will show the types of patterns you can use to build a great Android application. We'll cover things like how to use Interactive Titlebars, Quick Contacts, and Bottom bars as well some new patterns which will get an I/O-only preview. The team will be also available for a no holds barred Q&A session., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: android-ui-design-patterns, sessionhashtag: #android5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.49, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkP1, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkP1, waveid: w+e6j2obZkP1
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cssly2010-05-06T23:03:04.251Z
+ Row: 7sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 6, product: Android, track: Android, sessiontype: 301, sessiontitle: Developing Android REST client applications, tags: Android, Mobile, Java, sessionspeakers: Virgil Dobjanschi, speakers: virgild, sessionabstract: This session will present architectural considerations for developing RESTful applications on the Android platform. It focuses on design patterns, platform integration and performance issues specific to the Android platform.
+ , sessionrequirements: Advanced knowledge of Java & Android concepts, sessionlink: developing-RESTful-android-apps, sessionhashtag: #android6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6C, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkNc, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkNc, waveid: w+e6j2obZkNc
+
+
+
+
diff --git a/tests/assets/.svn/text-base/remote-sessions2.xml.svn-base b/tests/assets/.svn/text-base/remote-sessions2.xml.svn-base
new file mode 100644
index 0000000..f17b85b
--- /dev/null
+++ b/tests/assets/.svn/text-base/remote-sessions2.xml.svn-base
@@ -0,0 +1,41 @@
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic2010-05-06T23:03:04.251Z
+ sessions
+
+
+
+ christine.leechristine.lee@gmail.com
+
+ 821
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cokwr2010-05-06T23:03:04.251Z
+ Row: 2sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 6, product: Android, track: Android, sessiontype: 101, sessiontitle: A beginner's guide to Android, tags: Android, Mobile, Java, sessionspeakers: Reto Meier, speakers: retomeier, sessionabstract: This session will introduce some of the basic concepts involved in Android development. Starting with an overview of the SDK APIs available to developers, we will work through some simple code examples that explore some of the more common user features including using sensors, maps, and geolocation., sessionrequirements: Proficiency in Java and a basic understanding of embedded environments like mobile phones , sessionlink: beginners-guide-android, sessionhashtag: #android1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.45, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B-Xhdu7ZkBHw, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B-Xhdu7ZkBHw, waveid: w+-Xhdu7ZkBHw
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ckd7g2010-05-10T23:03:04.251Z
+ Row: 3sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 6, product: Android, track: Android, sessiontype: 301, sessiontitle: Writing real-time games for Android redux, tags: Android, Mobile, Java, Games, 2D, 3D, C++, sessionspeakers: Chris Pruett, speakers: pruett, sessionabstract: This session is a crash course in Android game development: everything you need to know to get started writing 2D and 3D games, as well as tips, tricks, and benchmarks to help your code reach optimal performance. In addition, we'll discuss hot topics related to game development, including hardware differences across devices, using C++ to write Android games, and the traits of the most popular games on Market., sessionrequirements: Proficiency in Java and Python and Ruby and Scheme and Bash and Ada and a solid grasp of Android's fundamental concepts, sessionlink: writing-real-time-games-android, sessionhashtag: #android2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.9B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BWDgRyLZkaR, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BWDgRyLZkaR, waveid: w+WDgRyLZkaR
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/clrrx2010-05-06T23:03:04.251Z
+ Row: 4sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: The world of ListView, tags: Android, Mobile, Java, sessionspeakers: Romain Guy, Adam Powell, speakers: romainguy, adamp, sessionabstract: ListView is one of the most widely used Android widgets but also the most complex one. Join us to learn how to master ListView and learn all about its features, optimizations, quirks and limitations.
+ , sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: world-of-listview-android, sessionhashtag: #android3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.47, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv7BXJrZkBJT, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv7BXJrZkBJT, waveid: w+v7BXJrZkBJT
+
+
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d180g2010-05-06T23:03:04.251Z
+ Row: 5sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: Casting a wide net: how to target all Android devices, tags: Android, Mobile, Java, sessionspeakers: Justin Mattson, speakers: jmatt, sessionabstract: One of Android's strengths is its flexibility to run on a wide variety of devices. In this session, we will explore the facilities the Android resource system provides to developers to make supporting many devices from one application binary easier, as well as common pitfalls. In addition to hardware heterogeneity, more than one version of Android may exist in the wild at any given time. We will go over strategies for providing cross-version compatibility., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: casting-wide-net-android-devices, sessionhashtag: #android4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.48, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BfZ2urbZk7o, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BfZ2urbZk7o, waveid: w+fZ2urbZk7o
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d2mkx2010-05-06T23:03:04.251Z
+ Row: 6sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: Android UI design patterns, tags: Android, Mobile, Java, sessionspeakers: Chris Nesladek, German Bauer, Richard Fulcher, Christian Robertson, Jim Palmer, speakers: cnesladek, germanb, rfulcher, robertsonc, jimpalmer, sessionabstract: In this session, the Android User Experience team will show the types of patterns you can use to build a great Android application. We'll cover things like how to use Interactive Titlebars, Quick Contacts, and Bottom bars as well some new patterns which will get an I/O-only preview. The team will be also available for a no holds barred Q&A session., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: android-ui-design-patterns, sessionhashtag: #android5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.49, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkP1, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkP1, waveid: w+e6j2obZkP1
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cssly2010-05-06T23:03:04.251Z
+ Row: 7sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 6, product: Android, track: Android, sessiontype: 102401, sessiontitle: Developing Android REST client applications, tags: Android, Mobile, Java, sessionspeakers: Virgil Dobjanschi, speakers: virgild, sessionabstract: This session will present architectural considerations for developing RESTful applications on the Android platform. It focuses on design patterns, platform integration and performance issues specific to the Android platform.
+ , sessionrequirements: Advanced knowledge of Java & Android concepts, sessionlink: developing-RESTful-android-apps, sessionhashtag: #android6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6C, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkNc, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkNc, waveid: w+e6j2obZkNc
+
+
+
+
diff --git a/tests/assets/.svn/text-base/spreadsheet-empty.xml.svn-base b/tests/assets/.svn/text-base/spreadsheet-empty.xml.svn-base
new file mode 100644
index 0000000..88a338e
--- /dev/null
+++ b/tests/assets/.svn/text-base/spreadsheet-empty.xml.svn-base
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/tests/assets/.svn/text-base/spreadsheet-emptyfield.xml.svn-base b/tests/assets/.svn/text-base/spreadsheet-emptyfield.xml.svn-base
new file mode 100644
index 0000000..f743060
--- /dev/null
+++ b/tests/assets/.svn/text-base/spreadsheet-emptyfield.xml.svn-base
@@ -0,0 +1,4 @@
+
+
+ sessiondate: Wednesday May 19, sessiontime: , room: 6
+
diff --git a/tests/assets/.svn/text-base/spreadsheet-normal.xml.svn-base b/tests/assets/.svn/text-base/spreadsheet-normal.xml.svn-base
new file mode 100644
index 0000000..e463e8b
--- /dev/null
+++ b/tests/assets/.svn/text-base/spreadsheet-normal.xml.svn-base
@@ -0,0 +1,5 @@
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cokwr2010-05-06T23:03:04.251Z
+ Row: 2sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 6, product: Android, track: Android, sessiontype: 101, sessiontitle: A beginner's guide to Android, tags: Android, Mobile, Java, sessionspeakers: Reto Meier, speakers: retomeier, sessionabstract: This session will introduce some of the basic concepts involved in Android development. Starting with an overview of the SDK APIs available to developers, we will work through some simple code examples that explore some of the more common user features including using sensors, maps, and geolocation., sessionrequirements: Proficiency in Java and a basic understanding of embedded environments like mobile phones , sessionlink: beginners-guide-android, sessionhashtag: #android1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.45, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B-Xhdu7ZkBHw, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B-Xhdu7ZkBHw, waveid: w+-Xhdu7ZkBHw
+
diff --git a/tests/assets/.svn/text-base/spreadsheet-single.xml.svn-base b/tests/assets/.svn/text-base/spreadsheet-single.xml.svn-base
new file mode 100644
index 0000000..b16dda8
--- /dev/null
+++ b/tests/assets/.svn/text-base/spreadsheet-single.xml.svn-base
@@ -0,0 +1,4 @@
+
+
+ sessiondate: Wednesday May 19
+
diff --git a/tests/assets/local-blocks.xml b/tests/assets/local-blocks.xml
new file mode 100644
index 0000000..6a9e432
--- /dev/null
+++ b/tests/assets/local-blocks.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ 2010-05-19T10:45:00.000-07:00
+ 2010-05-19T11:45:00.000-07:00
+ Office hours
+ officehours
+
+
+ 2010-05-19T05:00:00.000-07:00
+ 2010-05-19T12:00:00.000-07:00
+ Office hours
+ officehours
+
+
+
diff --git a/tests/assets/local-rooms.xml b/tests/assets/local-rooms.xml
new file mode 100644
index 0000000..dc36159
--- /dev/null
+++ b/tests/assets/local-rooms.xml
@@ -0,0 +1,24 @@
+
+
+
+ 1Room 12
+ 2Room 22
+ 3Room 32
+ 4Room 42
+ 5Room 52
+ 6Room 62
+ 7Room 72
+ 8Room 82
+ 9Room 92
+
+ firesidechatroomFireside Chat Room2
+
+ officehoursspaceaOffice Hours Space A2
+ officehoursspacebOffice Hours Space B2
+ officehoursspacecOffice Hours Space C2
+ officehoursspacedOffice Hours Space D2
+ officehoursspaceeOffice Hours Space E2
+ officehoursspacefOffice Hours Space F2
+ officehoursspacegOffice Hours Space G2
+
+
diff --git a/tests/assets/local-sessions.xml b/tests/assets/local-sessions.xml
new file mode 100644
index 0000000..34cb4e5
--- /dev/null
+++ b/tests/assets/local-sessions.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ beginners-guide-android
+ 2010-05-19T10:45:00.000-07:00
+ 2010-05-19T11:45:00.000-07:00
+ 4
+
+ A beginner's guide to Android
+
+
+
+ 2010-05-19T05:00:00.000-07:00
+ 2010-05-19T12:00:00.000-07:00
+ 6
+
+ Writing real-time games for Android redux
+
+
+
diff --git a/tests/assets/local-tracks.xml b/tests/assets/local-tracks.xml
new file mode 100644
index 0000000..26e60a7
--- /dev/null
+++ b/tests/assets/local-tracks.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/assets/remote-sessions1.xml b/tests/assets/remote-sessions1.xml
new file mode 100644
index 0000000..284c87f
--- /dev/null
+++ b/tests/assets/remote-sessions1.xml
@@ -0,0 +1,41 @@
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic2010-05-06T23:03:04.251Z
+ sessions
+
+
+
+ christine.leechristine.lee@gmail.com
+
+ 821
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cokwr2010-05-06T23:03:04.251Z
+ Row: 2sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 6, product: Android, track: Android, sessiontype: 101, sessiontitle: A beginner's guide to Android, tags: Android, Mobile, Java, sessionspeakers: Reto Meier, speakers: retomeier, sessionabstract: This session will introduce some of the basic concepts involved in Android development. Starting with an overview of the SDK APIs available to developers, we will work through some simple code examples that explore some of the more common user features including using sensors, maps, and geolocation., sessionrequirements: Proficiency in Java and a basic understanding of embedded environments like mobile phones , sessionlink: beginners-guide-android, sessionhashtag: #android1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.45, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B-Xhdu7ZkBHw, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B-Xhdu7ZkBHw, waveid: w+-Xhdu7ZkBHw
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ckd7g2010-05-06T23:03:04.251Z
+ Row: 3sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: Writing real-time games for Android redux, tags: Android, Mobile, Java, Games, 2D, 3D, C++, sessionspeakers: Chris Pruett, speakers: pruett, sessionabstract: This session is a crash course in Android game development: everything you need to know to get started writing 2D and 3D games, as well as tips, tricks, and benchmarks to help your code reach optimal performance. In addition, we'll discuss hot topics related to game development, including hardware differences across devices, using C++ to write Android games, and the traits of the most popular games on Market., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: writing-real-time-games-android, sessionhashtag: #android2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.9B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BWDgRyLZkaR, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BWDgRyLZkaR, waveid: w+WDgRyLZkaR
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/clrrx2010-05-06T23:03:04.251Z
+ Row: 4sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: The world of ListView, tags: Android, Mobile, Java, sessionspeakers: Romain Guy, Adam Powell, speakers: romainguy, adamp, sessionabstract: ListView is one of the most widely used Android widgets but also the most complex one. Join us to learn how to master ListView and learn all about its features, optimizations, quirks and limitations.
+ , sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: world-of-listview-android, sessionhashtag: #android3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.47, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv7BXJrZkBJT, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv7BXJrZkBJT, waveid: w+v7BXJrZkBJT
+
+
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d180g2010-05-06T23:03:04.251Z
+ Row: 5sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: Casting a wide net: how to target all Android devices, tags: Android, Mobile, Java, sessionspeakers: Justin Mattson, speakers: jmatt, sessionabstract: One of Android's strengths is its flexibility to run on a wide variety of devices. In this session, we will explore the facilities the Android resource system provides to developers to make supporting many devices from one application binary easier, as well as common pitfalls. In addition to hardware heterogeneity, more than one version of Android may exist in the wild at any given time. We will go over strategies for providing cross-version compatibility., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: casting-wide-net-android-devices, sessionhashtag: #android4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.48, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BfZ2urbZk7o, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BfZ2urbZk7o, waveid: w+fZ2urbZk7o
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d2mkx2010-05-06T23:03:04.251Z
+ Row: 6sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 6, product: Android, track: Android, Enterprise, sessiontype: 201, sessiontitle: Android UI design patterns, tags: Android, Mobile, Java, sessionspeakers: Chris Nesladek, German Bauer, Richard Fulcher, Christian Robertson, Jim Palmer, speakers: cnesladek, germanb, rfulcher, robertsonc, jimpalmer, sessionabstract: In this session, the Android User Experience team will show the types of patterns you can use to build a great Android application. We'll cover things like how to use Interactive Titlebars, Quick Contacts, and Bottom bars as well some new patterns which will get an I/O-only preview. The team will be also available for a no holds barred Q&A session., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: android-ui-design-patterns, sessionhashtag: #android5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.49, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkP1, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkP1, waveid: w+e6j2obZkP1
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cssly2010-05-06T23:03:04.251Z
+ Row: 7sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 6, product: Android, track: Android, sessiontype: 301, sessiontitle: Developing Android REST client applications, tags: Android, Mobile, Java, sessionspeakers: Virgil Dobjanschi, speakers: virgild, sessionabstract: This session will present architectural considerations for developing RESTful applications on the Android platform. It focuses on design patterns, platform integration and performance issues specific to the Android platform.
+ , sessionrequirements: Advanced knowledge of Java & Android concepts, sessionlink: developing-RESTful-android-apps, sessionhashtag: #android6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6C, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkNc, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkNc, waveid: w+e6j2obZkNc
+
+
+
+
diff --git a/tests/assets/remote-sessions2.xml b/tests/assets/remote-sessions2.xml
new file mode 100644
index 0000000..f17b85b
--- /dev/null
+++ b/tests/assets/remote-sessions2.xml
@@ -0,0 +1,41 @@
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic2010-05-06T23:03:04.251Z
+ sessions
+
+
+
+ christine.leechristine.lee@gmail.com
+
+ 821
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cokwr2010-05-06T23:03:04.251Z
+ Row: 2sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 6, product: Android, track: Android, sessiontype: 101, sessiontitle: A beginner's guide to Android, tags: Android, Mobile, Java, sessionspeakers: Reto Meier, speakers: retomeier, sessionabstract: This session will introduce some of the basic concepts involved in Android development. Starting with an overview of the SDK APIs available to developers, we will work through some simple code examples that explore some of the more common user features including using sensors, maps, and geolocation., sessionrequirements: Proficiency in Java and a basic understanding of embedded environments like mobile phones , sessionlink: beginners-guide-android, sessionhashtag: #android1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.45, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B-Xhdu7ZkBHw, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B-Xhdu7ZkBHw, waveid: w+-Xhdu7ZkBHw
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/ckd7g2010-05-10T23:03:04.251Z
+ Row: 3sessiondate: Wednesday May 19, sessiontime: 12:30pm-1:30pm, room: 6, product: Android, track: Android, sessiontype: 301, sessiontitle: Writing real-time games for Android redux, tags: Android, Mobile, Java, Games, 2D, 3D, C++, sessionspeakers: Chris Pruett, speakers: pruett, sessionabstract: This session is a crash course in Android game development: everything you need to know to get started writing 2D and 3D games, as well as tips, tricks, and benchmarks to help your code reach optimal performance. In addition, we'll discuss hot topics related to game development, including hardware differences across devices, using C++ to write Android games, and the traits of the most popular games on Market., sessionrequirements: Proficiency in Java and Python and Ruby and Scheme and Bash and Ada and a solid grasp of Android's fundamental concepts, sessionlink: writing-real-time-games-android, sessionhashtag: #android2, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.9B, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BWDgRyLZkaR, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BWDgRyLZkaR, waveid: w+WDgRyLZkaR
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/clrrx2010-05-06T23:03:04.251Z
+ Row: 4sessiondate: Wednesday May 19, sessiontime: 1:45pm-2:45pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: The world of ListView, tags: Android, Mobile, Java, sessionspeakers: Romain Guy, Adam Powell, speakers: romainguy, adamp, sessionabstract: ListView is one of the most widely used Android widgets but also the most complex one. Join us to learn how to master ListView and learn all about its features, optimizations, quirks and limitations.
+ , sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: world-of-listview-android, sessionhashtag: #android3, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.47, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Bv7BXJrZkBJT, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Bv7BXJrZkBJT, waveid: w+v7BXJrZkBJT
+
+
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d180g2010-05-06T23:03:04.251Z
+ Row: 5sessiondate: Wednesday May 19, sessiontime: 3:00pm-4:00pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: Casting a wide net: how to target all Android devices, tags: Android, Mobile, Java, sessionspeakers: Justin Mattson, speakers: jmatt, sessionabstract: One of Android's strengths is its flexibility to run on a wide variety of devices. In this session, we will explore the facilities the Android resource system provides to developers to make supporting many devices from one application binary easier, as well as common pitfalls. In addition to hardware heterogeneity, more than one version of Android may exist in the wild at any given time. We will go over strategies for providing cross-version compatibility., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: casting-wide-net-android-devices, sessionhashtag: #android4, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.48, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252BfZ2urbZk7o, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252BfZ2urbZk7o, waveid: w+fZ2urbZk7o
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/d2mkx2010-05-06T23:03:04.251Z
+ Row: 6sessiondate: Wednesday May 19, sessiontime: 4:15pm-5:15pm, room: 6, product: Android, track: Android, sessiontype: 201, sessiontitle: Android UI design patterns, tags: Android, Mobile, Java, sessionspeakers: Chris Nesladek, German Bauer, Richard Fulcher, Christian Robertson, Jim Palmer, speakers: cnesladek, germanb, rfulcher, robertsonc, jimpalmer, sessionabstract: In this session, the Android User Experience team will show the types of patterns you can use to build a great Android application. We'll cover things like how to use Interactive Titlebars, Quick Contacts, and Bottom bars as well some new patterns which will get an I/O-only preview. The team will be also available for a no holds barred Q&A session., sessionrequirements: Proficiency in Java and a solid grasp of Android's fundamental concepts, sessionlink: android-ui-design-patterns, sessionhashtag: #android5, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.49, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkP1, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkP1, waveid: w+e6j2obZkP1
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cssly2010-05-06T23:03:04.251Z
+ Row: 7sessiondate: Thursday May 20, sessiontime: 10:15am-11:15am, room: 6, product: Android, track: Android, sessiontype: 102401, sessiontitle: Developing Android REST client applications, tags: Android, Mobile, Java, sessionspeakers: Virgil Dobjanschi, speakers: virgild, sessionabstract: This session will present architectural considerations for developing RESTful applications on the Android platform. It focuses on design patterns, platform integration and performance issues specific to the Android platform.
+ , sessionrequirements: Advanced knowledge of Java & Android concepts, sessionlink: developing-RESTful-android-apps, sessionhashtag: #android6, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.6C, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252Be6j2obZkNc, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252Be6j2obZkNc, waveid: w+e6j2obZkNc
+
+
+
+
diff --git a/tests/assets/spreadsheet-empty.xml b/tests/assets/spreadsheet-empty.xml
new file mode 100644
index 0000000..88a338e
--- /dev/null
+++ b/tests/assets/spreadsheet-empty.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/tests/assets/spreadsheet-emptyfield.xml b/tests/assets/spreadsheet-emptyfield.xml
new file mode 100644
index 0000000..f743060
--- /dev/null
+++ b/tests/assets/spreadsheet-emptyfield.xml
@@ -0,0 +1,4 @@
+
+
+ sessiondate: Wednesday May 19, sessiontime: , room: 6
+
diff --git a/tests/assets/spreadsheet-normal.xml b/tests/assets/spreadsheet-normal.xml
new file mode 100644
index 0000000..e463e8b
--- /dev/null
+++ b/tests/assets/spreadsheet-normal.xml
@@ -0,0 +1,5 @@
+
+
+ http://spreadsheets.google.com/feeds/list/t0bDxnEqbFO4XuYpkA070Nw/od6/public/basic/cokwr2010-05-06T23:03:04.251Z
+ Row: 2sessiondate: Wednesday May 19, sessiontime: 10:45am-11:45am, room: 6, product: Android, track: Android, sessiontype: 101, sessiontitle: A beginner's guide to Android, tags: Android, Mobile, Java, sessionspeakers: Reto Meier, speakers: retomeier, sessionabstract: This session will introduce some of the basic concepts involved in Android development. Starting with an overview of the SDK APIs available to developers, we will work through some simple code examples that explore some of the more common user features including using sensors, maps, and geolocation., sessionrequirements: Proficiency in Java and a basic understanding of embedded environments like mobile phones , sessionlink: beginners-guide-android, sessionhashtag: #android1, moderatorlink: http://www.google.com/moderator/#15/e=68f0&t=68f0.45, wavelink: https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B-Xhdu7ZkBHw, _e8rn7: https://wave.google.com/wave/#restored:wave:googlewave.com, _dmair: w%252B-Xhdu7ZkBHw, waveid: w+-Xhdu7ZkBHw
+
diff --git a/tests/assets/spreadsheet-single.xml b/tests/assets/spreadsheet-single.xml
new file mode 100644
index 0000000..b16dda8
--- /dev/null
+++ b/tests/assets/spreadsheet-single.xml
@@ -0,0 +1,4 @@
+
+
+ sessiondate: Wednesday May 19
+
diff --git a/tests/build.properties b/tests/build.properties
new file mode 100644
index 0000000..6522258
--- /dev/null
+++ b/tests/build.properties
@@ -0,0 +1,18 @@
+# This file is used to override default values used by the Ant build system.
+#
+# This file must be checked in Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# You can use this to override default values such as
+# 'source.dir' for the location of your java source folder and
+# 'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+# 'key.store' for the location of your keystore and
+# 'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+
+tested.project.dir=..
diff --git a/tests/build.xml b/tests/build.xml
new file mode 100644
index 0000000..b2a5518
--- /dev/null
+++ b/tests/build.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/default.properties b/tests/default.properties
new file mode 100644
index 0000000..6017a4b
--- /dev/null
+++ b/tests/default.properties
@@ -0,0 +1,13 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Indicates whether an apk should be generated for each density.
+split.density=false
+# Project target.
+target=android-6
diff --git a/tests/libs/.svn/all-wcprops b/tests/libs/.svn/all-wcprops
new file mode 100644
index 0000000..ba626b5
--- /dev/null
+++ b/tests/libs/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 32
+/svn/!svn/ver/2/trunk/tests/libs
+END
diff --git a/tests/libs/.svn/entries b/tests/libs/.svn/entries
new file mode 100644
index 0000000..4461790
--- /dev/null
+++ b/tests/libs/.svn/entries
@@ -0,0 +1,28 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/tests/libs
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
diff --git a/tests/local.properties b/tests/local.properties
new file mode 100644
index 0000000..8c4030b
--- /dev/null
+++ b/tests/local.properties
@@ -0,0 +1,2 @@
+sdk.dir=/home/jsharkey/android-sdk-linux
+
diff --git a/tests/res/.svn/all-wcprops b/tests/res/.svn/all-wcprops
new file mode 100644
index 0000000..eb531b5
--- /dev/null
+++ b/tests/res/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 31
+/svn/!svn/ver/2/trunk/tests/res
+END
diff --git a/tests/res/.svn/entries b/tests/res/.svn/entries
new file mode 100644
index 0000000..4fb11fd
--- /dev/null
+++ b/tests/res/.svn/entries
@@ -0,0 +1,28 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/tests/res
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
diff --git a/tests/src/.svn/all-wcprops b/tests/src/.svn/all-wcprops
new file mode 100644
index 0000000..7d9127b
--- /dev/null
+++ b/tests/src/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 31
+/svn/!svn/ver/2/trunk/tests/src
+END
diff --git a/tests/src/.svn/entries b/tests/src/.svn/entries
new file mode 100644
index 0000000..a5d3c59
--- /dev/null
+++ b/tests/src/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/tests/src
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+com
+dir
+
diff --git a/tests/src/com/.svn/all-wcprops b/tests/src/com/.svn/all-wcprops
new file mode 100644
index 0000000..40450a7
--- /dev/null
+++ b/tests/src/com/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 35
+/svn/!svn/ver/2/trunk/tests/src/com
+END
diff --git a/tests/src/com/.svn/entries b/tests/src/com/.svn/entries
new file mode 100644
index 0000000..4ab73e0
--- /dev/null
+++ b/tests/src/com/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/tests/src/com
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+google
+dir
+
diff --git a/tests/src/com/google/.svn/all-wcprops b/tests/src/com/google/.svn/all-wcprops
new file mode 100644
index 0000000..c569225
--- /dev/null
+++ b/tests/src/com/google/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 42
+/svn/!svn/ver/2/trunk/tests/src/com/google
+END
diff --git a/tests/src/com/google/.svn/entries b/tests/src/com/google/.svn/entries
new file mode 100644
index 0000000..cdda622
--- /dev/null
+++ b/tests/src/com/google/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/tests/src/com/google
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+android
+dir
+
diff --git a/tests/src/com/google/android/.svn/all-wcprops b/tests/src/com/google/android/.svn/all-wcprops
new file mode 100644
index 0000000..b2a6ef0
--- /dev/null
+++ b/tests/src/com/google/android/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 50
+/svn/!svn/ver/2/trunk/tests/src/com/google/android
+END
diff --git a/tests/src/com/google/android/.svn/entries b/tests/src/com/google/android/.svn/entries
new file mode 100644
index 0000000..15ed5b0
--- /dev/null
+++ b/tests/src/com/google/android/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/tests/src/com/google/android
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+apps
+dir
+
diff --git a/tests/src/com/google/android/apps/.svn/all-wcprops b/tests/src/com/google/android/apps/.svn/all-wcprops
new file mode 100644
index 0000000..115ab1e
--- /dev/null
+++ b/tests/src/com/google/android/apps/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 55
+/svn/!svn/ver/2/trunk/tests/src/com/google/android/apps
+END
diff --git a/tests/src/com/google/android/apps/.svn/entries b/tests/src/com/google/android/apps/.svn/entries
new file mode 100644
index 0000000..7e7c08d
--- /dev/null
+++ b/tests/src/com/google/android/apps/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/tests/src/com/google/android/apps
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+iosched
+dir
+
diff --git a/tests/src/com/google/android/apps/iosched/.svn/all-wcprops b/tests/src/com/google/android/apps/iosched/.svn/all-wcprops
new file mode 100644
index 0000000..ad93341
--- /dev/null
+++ b/tests/src/com/google/android/apps/iosched/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 63
+/svn/!svn/ver/2/trunk/tests/src/com/google/android/apps/iosched
+END
diff --git a/tests/src/com/google/android/apps/iosched/.svn/entries b/tests/src/com/google/android/apps/iosched/.svn/entries
new file mode 100644
index 0000000..d00041c
--- /dev/null
+++ b/tests/src/com/google/android/apps/iosched/.svn/entries
@@ -0,0 +1,37 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/tests/src/com/google/android/apps/iosched
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+io
+dir
+
+provider
+dir
+
+util
+dir
+
diff --git a/tests/src/com/google/android/apps/iosched/io/.svn/all-wcprops b/tests/src/com/google/android/apps/iosched/io/.svn/all-wcprops
new file mode 100644
index 0000000..6112ee9
--- /dev/null
+++ b/tests/src/com/google/android/apps/iosched/io/.svn/all-wcprops
@@ -0,0 +1,17 @@
+K 25
+svn:wc:ra_dav:version-url
+V 66
+/svn/!svn/ver/2/trunk/tests/src/com/google/android/apps/iosched/io
+END
+SessionsHandlerTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 91
+/svn/!svn/ver/2/trunk/tests/src/com/google/android/apps/iosched/io/SessionsHandlerTest.java
+END
+StubHttpClient.java
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/svn/!svn/ver/2/trunk/tests/src/com/google/android/apps/iosched/io/StubHttpClient.java
+END
diff --git a/tests/src/com/google/android/apps/iosched/io/.svn/entries b/tests/src/com/google/android/apps/iosched/io/.svn/entries
new file mode 100644
index 0000000..7e119b5
--- /dev/null
+++ b/tests/src/com/google/android/apps/iosched/io/.svn/entries
@@ -0,0 +1,96 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/tests/src/com/google/android/apps/iosched/io
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+SessionsHandlerTest.java
+file
+
+
+
+
+2010-06-03T15:23:14.785625Z
+7b4e2dd17f7d1738abb00cd9d609b515
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+16845
+
+StubHttpClient.java
+file
+
+
+
+
+2010-06-03T15:23:14.789629Z
+6cba1635b096d15505d3b8fbdeef1e14
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7043
+
diff --git a/tests/src/com/google/android/apps/iosched/io/.svn/text-base/SessionsHandlerTest.java.svn-base b/tests/src/com/google/android/apps/iosched/io/.svn/text-base/SessionsHandlerTest.java.svn-base
new file mode 100644
index 0000000..0dcd2cf
--- /dev/null
+++ b/tests/src/com/google/android/apps/iosched/io/.svn/text-base/SessionsHandlerTest.java.svn-base
@@ -0,0 +1,349 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleProvider;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.util.ParserUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+import android.test.ProviderTestCase2;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+
+public class SessionsHandlerTest extends ProviderTestCase2 {
+
+ public SessionsHandlerTest() {
+ super(ScheduleProvider.class, ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ public void testLocalHandler() throws Exception {
+ parseBlocks();
+ parseTracks();
+ parseRooms();
+
+ final ContentResolver resolver = getMockContentResolver();
+ final XmlPullParser parser = openAssetParser("local-sessions.xml");
+ new LocalSessionsHandler().parseAndApply(parser, resolver);
+
+ Cursor cursor = resolver.query(Sessions.CONTENT_URI, null, null, null,
+ Sessions.DEFAULT_SORT);
+ try {
+ assertEquals(2, cursor.getCount());
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("writingreal-timegamesforandroidredux", getString(cursor,
+ Sessions.SESSION_ID));
+ assertEquals("Writing real-time games for Android redux", getString(cursor,
+ Sessions.TITLE));
+ assertEquals(6, getInt(cursor, Sessions.ROOM_ID));
+ assertEquals(2, getInt(cursor, Sessions.ROOM_FLOOR));
+ assertEquals(ParserUtils.BLOCK_TYPE_OFFICE_HOURS,
+ getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1274270400000L, getLong(cursor, Sessions.BLOCK_START));
+ assertEquals(1274295600000L, getLong(cursor, Sessions.BLOCK_END));
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("beginners-guide-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals("A beginner's guide to Android", getString(cursor, Sessions.TITLE));
+ assertEquals(4, getInt(cursor, Sessions.ROOM_ID));
+ assertEquals(2, getInt(cursor, Sessions.ROOM_FLOOR));
+ assertEquals(ParserUtils.BLOCK_TYPE_OFFICE_HOURS,
+ getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1274291100000L, getLong(cursor, Sessions.BLOCK_START));
+ assertEquals(1274294700000L, getLong(cursor, Sessions.BLOCK_END));
+
+ } finally {
+ cursor.close();
+ }
+
+ final Uri sessionTracks = Sessions.buildTracksDirUri("beginners-guide-android");
+ cursor = resolver.query(sessionTracks, null, null, null, Tracks.DEFAULT_SORT);
+ try {
+ assertEquals(1, cursor.getCount());
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("beginners-guide-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals("android", getString(cursor, Tracks.TRACK_ID));
+ assertEquals(-14002535, getInt(cursor, Tracks.TRACK_COLOR));
+
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public void testRemoteHandler() throws Exception {
+ parseTracks();
+ parseRooms();
+
+ final ContentResolver resolver = getMockContentResolver();
+ final XmlPullParser parser = openAssetParser("remote-sessions1.xml");
+ new RemoteSessionsHandler().parseAndApply(parser, resolver);
+
+ Cursor cursor = resolver.query(Sessions.CONTENT_URI, null, null, null,
+ Sessions.DEFAULT_SORT);
+ try {
+ assertEquals(6, cursor.getCount());
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("beginners-guide-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals(6, getInt(cursor, Sessions.ROOM_ID));
+ assertEquals(2, getInt(cursor, Sessions.ROOM_FLOOR));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1274291100000L, getLong(cursor, Sessions.BLOCK_START));
+ assertEquals(1274294700000L, getLong(cursor, Sessions.BLOCK_END));
+ assertEquals("http://www.google.com/moderator/#15/e=68f0&t=68f0.45", getString(cursor,
+ Sessions.MODERATOR_URL));
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("writing-real-time-games-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+
+ assertTrue(cursor.moveToLast());
+ assertEquals("developing-restful-android-apps", getString(cursor, Sessions.SESSION_ID));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+
+ } finally {
+ cursor.close();
+ }
+
+ final Uri sessionTracks = Sessions.buildTracksDirUri("android-ui-design-patterns");
+ cursor = resolver.query(sessionTracks, null, null, null, Tracks.DEFAULT_SORT);
+ try {
+ assertEquals(2, cursor.getCount());
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("android-ui-design-patterns", getString(cursor, Sessions.SESSION_ID));
+ assertEquals("android", getString(cursor, Tracks.TRACK_ID));
+ assertEquals(-14002535, getInt(cursor, Tracks.TRACK_COLOR));
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("android-ui-design-patterns", getString(cursor, Sessions.SESSION_ID));
+ assertEquals("enterprise", getString(cursor, Tracks.TRACK_ID));
+ assertEquals(-15750145, getInt(cursor, Tracks.TRACK_COLOR));
+
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public void testLocalRemoteUpdate() throws Exception {
+ parseBlocks();
+ parseTracks();
+ parseRooms();
+
+ // first, insert session data from local source
+ final ContentResolver resolver = getMockContentResolver();
+ final XmlPullParser parser = openAssetParser("local-sessions.xml");
+ new LocalSessionsHandler().parseAndApply(parser, resolver);
+
+ // now, star one of the sessions
+ final Uri sessionUri = Sessions.buildSessionUri("beginners-guide-android");
+ final ContentValues values = new ContentValues();
+ values.put(Sessions.STARRED, 1);
+ resolver.update(sessionUri, values, null, null);
+
+ Cursor cursor = resolver.query(Sessions.CONTENT_URI, null, null, null,
+ Sessions.DEFAULT_SORT);
+ try {
+ assertEquals(2, cursor.getCount());
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("writingreal-timegamesforandroidredux", getString(cursor,
+ Sessions.SESSION_ID));
+ assertEquals(ParserUtils.BLOCK_TYPE_OFFICE_HOURS,
+ getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(0, getInt(cursor, Sessions.STARRED));
+ assertEquals(0L, getLong(cursor, Sessions.UPDATED));
+
+ // make sure session is starred
+ assertTrue(cursor.moveToNext());
+ assertEquals("beginners-guide-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals(4, getInt(cursor, Sessions.ROOM_ID));
+ assertEquals(2, getInt(cursor, Sessions.ROOM_FLOOR));
+ assertEquals(ParserUtils.BLOCK_TYPE_OFFICE_HOURS,
+ getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1274291100000L, getLong(cursor, Sessions.BLOCK_START));
+ assertEquals(1274294700000L, getLong(cursor, Sessions.BLOCK_END));
+ assertEquals(1, getInt(cursor, Sessions.STARRED));
+ assertEquals(0L, getLong(cursor, Sessions.UPDATED));
+
+ } finally {
+ cursor.close();
+ }
+
+ // second, perform remote sync to pull in updates
+ final XmlPullParser parser1 = openAssetParser("remote-sessions1.xml");
+ new RemoteSessionsHandler().parseAndApply(parser1, resolver);
+
+ cursor = resolver.query(Sessions.CONTENT_URI, null, null, null, Sessions.DEFAULT_SORT);
+ try {
+ // six sessions from remote sync, plus one original
+ assertEquals(7, cursor.getCount());
+
+ // original local-only session is still hanging around
+ assertTrue(cursor.moveToNext());
+ assertEquals("writingreal-timegamesforandroidredux", getString(cursor,
+ Sessions.SESSION_ID));
+ assertEquals(ParserUtils.BLOCK_TYPE_OFFICE_HOURS,
+ getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(0, getInt(cursor, Sessions.STARRED));
+ assertEquals(0L, getLong(cursor, Sessions.UPDATED));
+
+ // existing session was updated with remote values, and has star
+ assertTrue(cursor.moveToNext());
+ assertEquals("beginners-guide-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals(1, getInt(cursor, Sessions.STARRED));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1273186984000L, getLong(cursor, Sessions.UPDATED));
+
+ // make sure session block was updated from remote
+ assertTrue(cursor.moveToNext());
+ assertEquals("writing-real-time-games-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals(6, getInt(cursor, Sessions.ROOM_ID));
+ assertEquals(2, getInt(cursor, Sessions.ROOM_FLOOR));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1274297400000L, getLong(cursor, Sessions.BLOCK_START));
+ assertEquals(1274301000000L, getLong(cursor, Sessions.BLOCK_END));
+ assertEquals("This session is a crash course in Android game development: everything "
+ + "you need to know to get started writing 2D and 3D games, as well as tips, "
+ + "tricks, and benchmarks to help your code reach optimal performance. In "
+ + "addition, we'll discuss hot topics related to game development, including "
+ + "hardware differences across devices, using C++ to write Android games, "
+ + "and the traits of the most popular games on Market.", getString(cursor,
+ Sessions.ABSTRACT));
+ assertEquals("Proficiency in Java and a solid grasp of Android's fundamental concepts",
+ getString(cursor, Sessions.REQUIREMENTS));
+ assertEquals("http://www.google.com/moderator/#15/e=68f0&t=68f0.9B", getString(cursor,
+ Sessions.MODERATOR_URL));
+ assertEquals(0, getInt(cursor, Sessions.STARRED));
+ assertEquals(1273186984000L, getLong(cursor, Sessions.UPDATED));
+
+ assertTrue(cursor.moveToLast());
+ assertEquals("developing-restful-android-apps", getString(cursor, Sessions.SESSION_ID));
+ assertEquals("301", getString(cursor, Sessions.TYPE));
+ assertEquals(0, getInt(cursor, Sessions.STARRED));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1273186984000L, getLong(cursor, Sessions.UPDATED));
+
+ } finally {
+ cursor.close();
+ }
+
+ // third, perform another remote sync
+ final XmlPullParser parser2 = openAssetParser("remote-sessions2.xml");
+ new RemoteSessionsHandler().parseAndApply(parser2, resolver);
+
+ cursor = resolver.query(Sessions.CONTENT_URI, null, null, null, Sessions.DEFAULT_SORT);
+ try {
+ // six sessions from remote sync, plus one original
+ assertEquals(7, cursor.getCount());
+
+ // original local-only session is still hanging around
+ assertTrue(cursor.moveToNext());
+ assertEquals("writingreal-timegamesforandroidredux", getString(cursor,
+ Sessions.SESSION_ID));
+ assertEquals(ParserUtils.BLOCK_TYPE_OFFICE_HOURS,
+ getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(0, getInt(cursor, Sessions.STARRED));
+ assertEquals(0L, getLong(cursor, Sessions.UPDATED));
+
+ // existing session was updated with remote values, and has star
+ assertTrue(cursor.moveToNext());
+ assertEquals("beginners-guide-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1, getInt(cursor, Sessions.STARRED));
+ assertEquals(1273186984000L, getLong(cursor, Sessions.UPDATED));
+
+ // existing session was updated with remote values, and has star
+ assertTrue(cursor.moveToNext());
+ assertEquals("writing-real-time-games-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals("Proficiency in Java and Python and Ruby and Scheme and "
+ + "Bash and Ada and a solid grasp of Android's fundamental concepts",
+ getString(cursor, Sessions.REQUIREMENTS));
+ assertEquals(0, getInt(cursor, Sessions.STARRED));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1273532584000L, getLong(cursor, Sessions.UPDATED));
+
+ // last session should remain unchanged, since updated flag didn't
+ // get touched. the remote spreadsheet said "102401", but we should
+ // still have "301".
+ assertTrue(cursor.moveToLast());
+ assertEquals("developing-restful-android-apps", getString(cursor, Sessions.SESSION_ID));
+ assertEquals("301", getString(cursor, Sessions.TYPE));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1273186984000L, getLong(cursor, Sessions.UPDATED));
+
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private void parseBlocks() throws Exception {
+ final XmlPullParser parser = openAssetParser("local-blocks.xml");
+ new LocalBlocksHandler().parseAndApply(parser, getMockContentResolver());
+ }
+
+ private void parseTracks() throws Exception {
+ final XmlPullParser parser = openAssetParser("local-tracks.xml");
+ new LocalTracksHandler().parseAndApply(parser, getMockContentResolver());
+ }
+
+ private void parseRooms() throws Exception {
+ final XmlPullParser parser = openAssetParser("local-rooms.xml");
+ new LocalRoomsHandler().parseAndApply(parser, getMockContentResolver());
+ }
+
+ private String getString(Cursor cursor, String column) {
+ return cursor.getString(cursor.getColumnIndex(column));
+ }
+
+ private long getInt(Cursor cursor, String column) {
+ return cursor.getInt(cursor.getColumnIndex(column));
+ }
+
+ private long getLong(Cursor cursor, String column) {
+ return cursor.getLong(cursor.getColumnIndex(column));
+ }
+
+ private XmlPullParser openAssetParser(String assetName) throws XmlPullParserException,
+ IOException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+ final Context testContext = getTestContext(this);
+ return ParserUtils.newPullParser(testContext.getResources().getAssets().open(assetName));
+ }
+
+ /**
+ * Exposes method {@code getTestContext()} in {@link AndroidTestCase}, which
+ * is hidden for now. Useful for obtaining access to the test assets.
+ */
+ public static Context getTestContext(AndroidTestCase testCase) throws IllegalAccessException,
+ InvocationTargetException, NoSuchMethodException {
+ return (Context) AndroidTestCase.class.getMethod("getTestContext").invoke(testCase);
+ }
+
+}
diff --git a/tests/src/com/google/android/apps/iosched/io/.svn/text-base/StubHttpClient.java.svn-base b/tests/src/com/google/android/apps/iosched/io/.svn/text-base/StubHttpClient.java.svn-base
new file mode 100644
index 0000000..07e5c35
--- /dev/null
+++ b/tests/src/com/google/android/apps/iosched/io/.svn/text-base/StubHttpClient.java.svn-base
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.client.AuthenticationHandler;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.client.RedirectHandler;
+import org.apache.http.client.RequestDirector;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.message.BasicStatusLine;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpRequestExecutor;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Stub {@link HttpClient} that will provide a single {@link HttpResponse} to
+ * any incoming {@link HttpRequest}. This single response can be set using
+ * {@link #setResponse(HttpResponse)}.
+ */
+class StubHttpClient extends DefaultHttpClient {
+ private static final String TAG = "StubHttpClient";
+
+ private HttpResponse mResponse;
+ private HttpRequest mLastRequest;
+
+ public StubHttpClient() {
+ resetState();
+ }
+
+ /**
+ * Set the default {@link HttpResponse} to always return for any
+ * {@link #execute(HttpUriRequest)} calls.
+ */
+ public void setResponse(HttpResponse currentResponse) {
+ mResponse = currentResponse;
+ }
+
+ /**
+ * Set the default {@link HttpResponse} to always return for any
+ * {@link #execute(HttpUriRequest)} calls. This is a shortcut instead of
+ * calling {@link #buildResponse(int, String, AndroidTestCase)}.
+ */
+ public void setResponse(int statusCode, String assetName, AndroidTestCase testCase)
+ throws Exception {
+ setResponse(buildResponse(statusCode, assetName, testCase));
+ }
+
+ /**
+ * Return the last {@link HttpRequest} that was requested through
+ * {@link #execute(HttpUriRequest)}, exposed for testing purposes.
+ */
+ public HttpRequest getLastRequest() {
+ return mLastRequest;
+ }
+
+ /**
+ * Reset any internal state, usually so this heavy {@link HttpClient} can be
+ * reused across tests.
+ */
+ public void resetState() {
+ mResponse = buildInternalServerError();
+ }
+
+ private static HttpResponse buildInternalServerError() {
+ final StatusLine status = new BasicStatusLine(HttpVersion.HTTP_1_1,
+ HttpStatus.SC_INTERNAL_SERVER_ERROR, null);
+ return new BasicHttpResponse(status);
+ }
+
+ /**
+ * Build a stub {@link HttpResponse}, probably for use with
+ * {@link #setResponse(HttpResponse)}.
+ *
+ * @param statusCode {@link HttpStatus} code to use.
+ * @param assetName Name of asset that should be included as a single
+ * {@link HttpEntity} to be included in the response, loaded
+ * through {@link AssetManager}.
+ */
+ public static HttpResponse buildResponse(int statusCode, String assetName,
+ AndroidTestCase testCase) throws Exception {
+ final Context testContext = getTestContext(testCase);
+ return buildResponse(statusCode, assetName, testContext);
+ }
+
+ /**
+ * Exposes method {@code getTestContext()} in {@link AndroidTestCase}, which
+ * is hidden for now. Useful for obtaining access to the test assets.
+ */
+ public static Context getTestContext(AndroidTestCase testCase) throws IllegalAccessException,
+ InvocationTargetException, NoSuchMethodException {
+ return (Context) AndroidTestCase.class.getMethod("getTestContext").invoke(testCase);
+ }
+
+ /**
+ * Build a stub {@link HttpResponse}, probably for use with
+ * {@link #setResponse(HttpResponse)}.
+ *
+ * @param statusCode {@link HttpStatus} code to use.
+ * @param assetName Name of asset that should be included as a single
+ * {@link HttpEntity} to be included in the response, loaded
+ * through {@link AssetManager}.
+ */
+ public static HttpResponse buildResponse(int statusCode, String assetName, Context context)
+ throws IOException {
+ final StatusLine status = new BasicStatusLine(HttpVersion.HTTP_1_1, statusCode, null);
+ final HttpResponse response = new BasicHttpResponse(status);
+ if (assetName != null) {
+ final InputStream entity = context.getAssets().open(assetName);
+ response.setEntity(new InputStreamEntity(entity, entity.available()));
+ }
+ return response;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected RequestDirector createClientRequestDirector(
+ final HttpRequestExecutor requestExec,
+ final ClientConnectionManager conman,
+ final ConnectionReuseStrategy reustrat,
+ final ConnectionKeepAliveStrategy kastrat,
+ final HttpRoutePlanner rouplan,
+ final HttpProcessor httpProcessor,
+ final HttpRequestRetryHandler retryHandler,
+ final RedirectHandler redirectHandler,
+ final AuthenticationHandler targetAuthHandler,
+ final AuthenticationHandler proxyAuthHandler,
+ final UserTokenHandler stateHandler,
+ final HttpParams params) {
+ return new RequestDirector() {
+ /** {@inheritDoc} */
+ public HttpResponse execute(HttpHost target, HttpRequest request,
+ HttpContext context) throws HttpException, IOException {
+ Log.d(TAG, "Intercepted: " + request.getRequestLine().toString());
+ mLastRequest = request;
+ return mResponse;
+ }
+ };
+ }
+}
diff --git a/tests/src/com/google/android/apps/iosched/io/SessionsHandlerTest.java b/tests/src/com/google/android/apps/iosched/io/SessionsHandlerTest.java
new file mode 100644
index 0000000..0dcd2cf
--- /dev/null
+++ b/tests/src/com/google/android/apps/iosched/io/SessionsHandlerTest.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import com.google.android.apps.iosched.provider.ScheduleContract;
+import com.google.android.apps.iosched.provider.ScheduleProvider;
+import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
+import com.google.android.apps.iosched.provider.ScheduleContract.Tracks;
+import com.google.android.apps.iosched.util.ParserUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+import android.test.ProviderTestCase2;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+
+public class SessionsHandlerTest extends ProviderTestCase2 {
+
+ public SessionsHandlerTest() {
+ super(ScheduleProvider.class, ScheduleContract.CONTENT_AUTHORITY);
+ }
+
+ public void testLocalHandler() throws Exception {
+ parseBlocks();
+ parseTracks();
+ parseRooms();
+
+ final ContentResolver resolver = getMockContentResolver();
+ final XmlPullParser parser = openAssetParser("local-sessions.xml");
+ new LocalSessionsHandler().parseAndApply(parser, resolver);
+
+ Cursor cursor = resolver.query(Sessions.CONTENT_URI, null, null, null,
+ Sessions.DEFAULT_SORT);
+ try {
+ assertEquals(2, cursor.getCount());
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("writingreal-timegamesforandroidredux", getString(cursor,
+ Sessions.SESSION_ID));
+ assertEquals("Writing real-time games for Android redux", getString(cursor,
+ Sessions.TITLE));
+ assertEquals(6, getInt(cursor, Sessions.ROOM_ID));
+ assertEquals(2, getInt(cursor, Sessions.ROOM_FLOOR));
+ assertEquals(ParserUtils.BLOCK_TYPE_OFFICE_HOURS,
+ getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1274270400000L, getLong(cursor, Sessions.BLOCK_START));
+ assertEquals(1274295600000L, getLong(cursor, Sessions.BLOCK_END));
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("beginners-guide-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals("A beginner's guide to Android", getString(cursor, Sessions.TITLE));
+ assertEquals(4, getInt(cursor, Sessions.ROOM_ID));
+ assertEquals(2, getInt(cursor, Sessions.ROOM_FLOOR));
+ assertEquals(ParserUtils.BLOCK_TYPE_OFFICE_HOURS,
+ getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1274291100000L, getLong(cursor, Sessions.BLOCK_START));
+ assertEquals(1274294700000L, getLong(cursor, Sessions.BLOCK_END));
+
+ } finally {
+ cursor.close();
+ }
+
+ final Uri sessionTracks = Sessions.buildTracksDirUri("beginners-guide-android");
+ cursor = resolver.query(sessionTracks, null, null, null, Tracks.DEFAULT_SORT);
+ try {
+ assertEquals(1, cursor.getCount());
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("beginners-guide-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals("android", getString(cursor, Tracks.TRACK_ID));
+ assertEquals(-14002535, getInt(cursor, Tracks.TRACK_COLOR));
+
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public void testRemoteHandler() throws Exception {
+ parseTracks();
+ parseRooms();
+
+ final ContentResolver resolver = getMockContentResolver();
+ final XmlPullParser parser = openAssetParser("remote-sessions1.xml");
+ new RemoteSessionsHandler().parseAndApply(parser, resolver);
+
+ Cursor cursor = resolver.query(Sessions.CONTENT_URI, null, null, null,
+ Sessions.DEFAULT_SORT);
+ try {
+ assertEquals(6, cursor.getCount());
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("beginners-guide-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals(6, getInt(cursor, Sessions.ROOM_ID));
+ assertEquals(2, getInt(cursor, Sessions.ROOM_FLOOR));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1274291100000L, getLong(cursor, Sessions.BLOCK_START));
+ assertEquals(1274294700000L, getLong(cursor, Sessions.BLOCK_END));
+ assertEquals("http://www.google.com/moderator/#15/e=68f0&t=68f0.45", getString(cursor,
+ Sessions.MODERATOR_URL));
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("writing-real-time-games-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+
+ assertTrue(cursor.moveToLast());
+ assertEquals("developing-restful-android-apps", getString(cursor, Sessions.SESSION_ID));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+
+ } finally {
+ cursor.close();
+ }
+
+ final Uri sessionTracks = Sessions.buildTracksDirUri("android-ui-design-patterns");
+ cursor = resolver.query(sessionTracks, null, null, null, Tracks.DEFAULT_SORT);
+ try {
+ assertEquals(2, cursor.getCount());
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("android-ui-design-patterns", getString(cursor, Sessions.SESSION_ID));
+ assertEquals("android", getString(cursor, Tracks.TRACK_ID));
+ assertEquals(-14002535, getInt(cursor, Tracks.TRACK_COLOR));
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("android-ui-design-patterns", getString(cursor, Sessions.SESSION_ID));
+ assertEquals("enterprise", getString(cursor, Tracks.TRACK_ID));
+ assertEquals(-15750145, getInt(cursor, Tracks.TRACK_COLOR));
+
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public void testLocalRemoteUpdate() throws Exception {
+ parseBlocks();
+ parseTracks();
+ parseRooms();
+
+ // first, insert session data from local source
+ final ContentResolver resolver = getMockContentResolver();
+ final XmlPullParser parser = openAssetParser("local-sessions.xml");
+ new LocalSessionsHandler().parseAndApply(parser, resolver);
+
+ // now, star one of the sessions
+ final Uri sessionUri = Sessions.buildSessionUri("beginners-guide-android");
+ final ContentValues values = new ContentValues();
+ values.put(Sessions.STARRED, 1);
+ resolver.update(sessionUri, values, null, null);
+
+ Cursor cursor = resolver.query(Sessions.CONTENT_URI, null, null, null,
+ Sessions.DEFAULT_SORT);
+ try {
+ assertEquals(2, cursor.getCount());
+
+ assertTrue(cursor.moveToNext());
+ assertEquals("writingreal-timegamesforandroidredux", getString(cursor,
+ Sessions.SESSION_ID));
+ assertEquals(ParserUtils.BLOCK_TYPE_OFFICE_HOURS,
+ getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(0, getInt(cursor, Sessions.STARRED));
+ assertEquals(0L, getLong(cursor, Sessions.UPDATED));
+
+ // make sure session is starred
+ assertTrue(cursor.moveToNext());
+ assertEquals("beginners-guide-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals(4, getInt(cursor, Sessions.ROOM_ID));
+ assertEquals(2, getInt(cursor, Sessions.ROOM_FLOOR));
+ assertEquals(ParserUtils.BLOCK_TYPE_OFFICE_HOURS,
+ getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1274291100000L, getLong(cursor, Sessions.BLOCK_START));
+ assertEquals(1274294700000L, getLong(cursor, Sessions.BLOCK_END));
+ assertEquals(1, getInt(cursor, Sessions.STARRED));
+ assertEquals(0L, getLong(cursor, Sessions.UPDATED));
+
+ } finally {
+ cursor.close();
+ }
+
+ // second, perform remote sync to pull in updates
+ final XmlPullParser parser1 = openAssetParser("remote-sessions1.xml");
+ new RemoteSessionsHandler().parseAndApply(parser1, resolver);
+
+ cursor = resolver.query(Sessions.CONTENT_URI, null, null, null, Sessions.DEFAULT_SORT);
+ try {
+ // six sessions from remote sync, plus one original
+ assertEquals(7, cursor.getCount());
+
+ // original local-only session is still hanging around
+ assertTrue(cursor.moveToNext());
+ assertEquals("writingreal-timegamesforandroidredux", getString(cursor,
+ Sessions.SESSION_ID));
+ assertEquals(ParserUtils.BLOCK_TYPE_OFFICE_HOURS,
+ getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(0, getInt(cursor, Sessions.STARRED));
+ assertEquals(0L, getLong(cursor, Sessions.UPDATED));
+
+ // existing session was updated with remote values, and has star
+ assertTrue(cursor.moveToNext());
+ assertEquals("beginners-guide-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals(1, getInt(cursor, Sessions.STARRED));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1273186984000L, getLong(cursor, Sessions.UPDATED));
+
+ // make sure session block was updated from remote
+ assertTrue(cursor.moveToNext());
+ assertEquals("writing-real-time-games-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals(6, getInt(cursor, Sessions.ROOM_ID));
+ assertEquals(2, getInt(cursor, Sessions.ROOM_FLOOR));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1274297400000L, getLong(cursor, Sessions.BLOCK_START));
+ assertEquals(1274301000000L, getLong(cursor, Sessions.BLOCK_END));
+ assertEquals("This session is a crash course in Android game development: everything "
+ + "you need to know to get started writing 2D and 3D games, as well as tips, "
+ + "tricks, and benchmarks to help your code reach optimal performance. In "
+ + "addition, we'll discuss hot topics related to game development, including "
+ + "hardware differences across devices, using C++ to write Android games, "
+ + "and the traits of the most popular games on Market.", getString(cursor,
+ Sessions.ABSTRACT));
+ assertEquals("Proficiency in Java and a solid grasp of Android's fundamental concepts",
+ getString(cursor, Sessions.REQUIREMENTS));
+ assertEquals("http://www.google.com/moderator/#15/e=68f0&t=68f0.9B", getString(cursor,
+ Sessions.MODERATOR_URL));
+ assertEquals(0, getInt(cursor, Sessions.STARRED));
+ assertEquals(1273186984000L, getLong(cursor, Sessions.UPDATED));
+
+ assertTrue(cursor.moveToLast());
+ assertEquals("developing-restful-android-apps", getString(cursor, Sessions.SESSION_ID));
+ assertEquals("301", getString(cursor, Sessions.TYPE));
+ assertEquals(0, getInt(cursor, Sessions.STARRED));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1273186984000L, getLong(cursor, Sessions.UPDATED));
+
+ } finally {
+ cursor.close();
+ }
+
+ // third, perform another remote sync
+ final XmlPullParser parser2 = openAssetParser("remote-sessions2.xml");
+ new RemoteSessionsHandler().parseAndApply(parser2, resolver);
+
+ cursor = resolver.query(Sessions.CONTENT_URI, null, null, null, Sessions.DEFAULT_SORT);
+ try {
+ // six sessions from remote sync, plus one original
+ assertEquals(7, cursor.getCount());
+
+ // original local-only session is still hanging around
+ assertTrue(cursor.moveToNext());
+ assertEquals("writingreal-timegamesforandroidredux", getString(cursor,
+ Sessions.SESSION_ID));
+ assertEquals(ParserUtils.BLOCK_TYPE_OFFICE_HOURS,
+ getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(0, getInt(cursor, Sessions.STARRED));
+ assertEquals(0L, getLong(cursor, Sessions.UPDATED));
+
+ // existing session was updated with remote values, and has star
+ assertTrue(cursor.moveToNext());
+ assertEquals("beginners-guide-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1, getInt(cursor, Sessions.STARRED));
+ assertEquals(1273186984000L, getLong(cursor, Sessions.UPDATED));
+
+ // existing session was updated with remote values, and has star
+ assertTrue(cursor.moveToNext());
+ assertEquals("writing-real-time-games-android", getString(cursor, Sessions.SESSION_ID));
+ assertEquals("Proficiency in Java and Python and Ruby and Scheme and "
+ + "Bash and Ada and a solid grasp of Android's fundamental concepts",
+ getString(cursor, Sessions.REQUIREMENTS));
+ assertEquals(0, getInt(cursor, Sessions.STARRED));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1273532584000L, getLong(cursor, Sessions.UPDATED));
+
+ // last session should remain unchanged, since updated flag didn't
+ // get touched. the remote spreadsheet said "102401", but we should
+ // still have "301".
+ assertTrue(cursor.moveToLast());
+ assertEquals("developing-restful-android-apps", getString(cursor, Sessions.SESSION_ID));
+ assertEquals("301", getString(cursor, Sessions.TYPE));
+ assertEquals(ParserUtils.BLOCK_TYPE_SESSION, getString(cursor, Sessions.BLOCK_TYPE));
+ assertEquals(1273186984000L, getLong(cursor, Sessions.UPDATED));
+
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private void parseBlocks() throws Exception {
+ final XmlPullParser parser = openAssetParser("local-blocks.xml");
+ new LocalBlocksHandler().parseAndApply(parser, getMockContentResolver());
+ }
+
+ private void parseTracks() throws Exception {
+ final XmlPullParser parser = openAssetParser("local-tracks.xml");
+ new LocalTracksHandler().parseAndApply(parser, getMockContentResolver());
+ }
+
+ private void parseRooms() throws Exception {
+ final XmlPullParser parser = openAssetParser("local-rooms.xml");
+ new LocalRoomsHandler().parseAndApply(parser, getMockContentResolver());
+ }
+
+ private String getString(Cursor cursor, String column) {
+ return cursor.getString(cursor.getColumnIndex(column));
+ }
+
+ private long getInt(Cursor cursor, String column) {
+ return cursor.getInt(cursor.getColumnIndex(column));
+ }
+
+ private long getLong(Cursor cursor, String column) {
+ return cursor.getLong(cursor.getColumnIndex(column));
+ }
+
+ private XmlPullParser openAssetParser(String assetName) throws XmlPullParserException,
+ IOException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+ final Context testContext = getTestContext(this);
+ return ParserUtils.newPullParser(testContext.getResources().getAssets().open(assetName));
+ }
+
+ /**
+ * Exposes method {@code getTestContext()} in {@link AndroidTestCase}, which
+ * is hidden for now. Useful for obtaining access to the test assets.
+ */
+ public static Context getTestContext(AndroidTestCase testCase) throws IllegalAccessException,
+ InvocationTargetException, NoSuchMethodException {
+ return (Context) AndroidTestCase.class.getMethod("getTestContext").invoke(testCase);
+ }
+
+}
diff --git a/tests/src/com/google/android/apps/iosched/io/StubHttpClient.java b/tests/src/com/google/android/apps/iosched/io/StubHttpClient.java
new file mode 100644
index 0000000..07e5c35
--- /dev/null
+++ b/tests/src/com/google/android/apps/iosched/io/StubHttpClient.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.io;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.client.AuthenticationHandler;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.client.RedirectHandler;
+import org.apache.http.client.RequestDirector;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.message.BasicStatusLine;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpRequestExecutor;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Stub {@link HttpClient} that will provide a single {@link HttpResponse} to
+ * any incoming {@link HttpRequest}. This single response can be set using
+ * {@link #setResponse(HttpResponse)}.
+ */
+class StubHttpClient extends DefaultHttpClient {
+ private static final String TAG = "StubHttpClient";
+
+ private HttpResponse mResponse;
+ private HttpRequest mLastRequest;
+
+ public StubHttpClient() {
+ resetState();
+ }
+
+ /**
+ * Set the default {@link HttpResponse} to always return for any
+ * {@link #execute(HttpUriRequest)} calls.
+ */
+ public void setResponse(HttpResponse currentResponse) {
+ mResponse = currentResponse;
+ }
+
+ /**
+ * Set the default {@link HttpResponse} to always return for any
+ * {@link #execute(HttpUriRequest)} calls. This is a shortcut instead of
+ * calling {@link #buildResponse(int, String, AndroidTestCase)}.
+ */
+ public void setResponse(int statusCode, String assetName, AndroidTestCase testCase)
+ throws Exception {
+ setResponse(buildResponse(statusCode, assetName, testCase));
+ }
+
+ /**
+ * Return the last {@link HttpRequest} that was requested through
+ * {@link #execute(HttpUriRequest)}, exposed for testing purposes.
+ */
+ public HttpRequest getLastRequest() {
+ return mLastRequest;
+ }
+
+ /**
+ * Reset any internal state, usually so this heavy {@link HttpClient} can be
+ * reused across tests.
+ */
+ public void resetState() {
+ mResponse = buildInternalServerError();
+ }
+
+ private static HttpResponse buildInternalServerError() {
+ final StatusLine status = new BasicStatusLine(HttpVersion.HTTP_1_1,
+ HttpStatus.SC_INTERNAL_SERVER_ERROR, null);
+ return new BasicHttpResponse(status);
+ }
+
+ /**
+ * Build a stub {@link HttpResponse}, probably for use with
+ * {@link #setResponse(HttpResponse)}.
+ *
+ * @param statusCode {@link HttpStatus} code to use.
+ * @param assetName Name of asset that should be included as a single
+ * {@link HttpEntity} to be included in the response, loaded
+ * through {@link AssetManager}.
+ */
+ public static HttpResponse buildResponse(int statusCode, String assetName,
+ AndroidTestCase testCase) throws Exception {
+ final Context testContext = getTestContext(testCase);
+ return buildResponse(statusCode, assetName, testContext);
+ }
+
+ /**
+ * Exposes method {@code getTestContext()} in {@link AndroidTestCase}, which
+ * is hidden for now. Useful for obtaining access to the test assets.
+ */
+ public static Context getTestContext(AndroidTestCase testCase) throws IllegalAccessException,
+ InvocationTargetException, NoSuchMethodException {
+ return (Context) AndroidTestCase.class.getMethod("getTestContext").invoke(testCase);
+ }
+
+ /**
+ * Build a stub {@link HttpResponse}, probably for use with
+ * {@link #setResponse(HttpResponse)}.
+ *
+ * @param statusCode {@link HttpStatus} code to use.
+ * @param assetName Name of asset that should be included as a single
+ * {@link HttpEntity} to be included in the response, loaded
+ * through {@link AssetManager}.
+ */
+ public static HttpResponse buildResponse(int statusCode, String assetName, Context context)
+ throws IOException {
+ final StatusLine status = new BasicStatusLine(HttpVersion.HTTP_1_1, statusCode, null);
+ final HttpResponse response = new BasicHttpResponse(status);
+ if (assetName != null) {
+ final InputStream entity = context.getAssets().open(assetName);
+ response.setEntity(new InputStreamEntity(entity, entity.available()));
+ }
+ return response;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected RequestDirector createClientRequestDirector(
+ final HttpRequestExecutor requestExec,
+ final ClientConnectionManager conman,
+ final ConnectionReuseStrategy reustrat,
+ final ConnectionKeepAliveStrategy kastrat,
+ final HttpRoutePlanner rouplan,
+ final HttpProcessor httpProcessor,
+ final HttpRequestRetryHandler retryHandler,
+ final RedirectHandler redirectHandler,
+ final AuthenticationHandler targetAuthHandler,
+ final AuthenticationHandler proxyAuthHandler,
+ final UserTokenHandler stateHandler,
+ final HttpParams params) {
+ return new RequestDirector() {
+ /** {@inheritDoc} */
+ public HttpResponse execute(HttpHost target, HttpRequest request,
+ HttpContext context) throws HttpException, IOException {
+ Log.d(TAG, "Intercepted: " + request.getRequestLine().toString());
+ mLastRequest = request;
+ return mResponse;
+ }
+ };
+ }
+}
diff --git a/tests/src/com/google/android/apps/iosched/provider/.svn/all-wcprops b/tests/src/com/google/android/apps/iosched/provider/.svn/all-wcprops
new file mode 100644
index 0000000..74177b7
--- /dev/null
+++ b/tests/src/com/google/android/apps/iosched/provider/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 72
+/svn/!svn/ver/2/trunk/tests/src/com/google/android/apps/iosched/provider
+END
diff --git a/tests/src/com/google/android/apps/iosched/provider/.svn/entries b/tests/src/com/google/android/apps/iosched/provider/.svn/entries
new file mode 100644
index 0000000..f720951
--- /dev/null
+++ b/tests/src/com/google/android/apps/iosched/provider/.svn/entries
@@ -0,0 +1,28 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/tests/src/com/google/android/apps/iosched/provider
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
diff --git a/tests/src/com/google/android/apps/iosched/util/.svn/all-wcprops b/tests/src/com/google/android/apps/iosched/util/.svn/all-wcprops
new file mode 100644
index 0000000..e766016
--- /dev/null
+++ b/tests/src/com/google/android/apps/iosched/util/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 68
+/svn/!svn/ver/2/trunk/tests/src/com/google/android/apps/iosched/util
+END
+SpreadsheetEntryTest.java
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/svn/!svn/ver/2/trunk/tests/src/com/google/android/apps/iosched/util/SpreadsheetEntryTest.java
+END
diff --git a/tests/src/com/google/android/apps/iosched/util/.svn/entries b/tests/src/com/google/android/apps/iosched/util/.svn/entries
new file mode 100644
index 0000000..6272dc9
--- /dev/null
+++ b/tests/src/com/google/android/apps/iosched/util/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+2
+http://iosched.googlecode.com/svn/trunk/tests/src/com/google/android/apps/iosched/util
+http://iosched.googlecode.com/svn
+
+
+
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+fd5f40e8-4bb1-d160-99a7-c87ac5d78a59
+
+SpreadsheetEntryTest.java
+file
+
+
+
+
+2010-06-03T15:23:15.169621Z
+68534068bf7cfdfe8f0d5d623e9c129d
+2010-06-01T01:04:33.352282Z
+2
+jsharkey@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4971
+
diff --git a/tests/src/com/google/android/apps/iosched/util/.svn/text-base/SpreadsheetEntryTest.java.svn-base b/tests/src/com/google/android/apps/iosched/util/.svn/text-base/SpreadsheetEntryTest.java.svn-base
new file mode 100644
index 0000000..9268101
--- /dev/null
+++ b/tests/src/com/google/android/apps/iosched/util/.svn/text-base/SpreadsheetEntryTest.java.svn-base
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+
+public class SpreadsheetEntryTest extends AndroidTestCase {
+
+ public void testParseNormal() throws Exception {
+ final XmlPullParser parser = openAssetParser("spreadsheet-normal.xml");
+ final SpreadsheetEntry entry = SpreadsheetEntry.fromParser(parser);
+
+ assertEquals("unexpected columns", 19, entry.size());
+ assertEquals("Wednesday May 19", entry.get("sessiondate"));
+ assertEquals("10:45am-11:45am", entry.get("sessiontime"));
+ assertEquals("6", entry.get("room"));
+ assertEquals("Android", entry.get("product"));
+ assertEquals("Android", entry.get("track"));
+ assertEquals("101", entry.get("sessiontype"));
+ assertEquals("A beginner's guide to Android", entry.get("sessiontitle"));
+ assertEquals("Android, Mobile, Java", entry.get("tags"));
+ assertEquals("Reto Meier", entry.get("sessionspeakers"));
+ assertEquals("retomeier", entry.get("speakers"));
+ assertEquals("This session will introduce some of the basic concepts involved in "
+ + "Android development. Starting with an overview of the SDK APIs available "
+ + "to developers, we will work through some simple code examples that "
+ + "explore some of the more common user features including using sensors, "
+ + "maps, and geolocation.", entry.get("sessionabstract"));
+ assertEquals("Proficiency in Java and a basic understanding of embedded "
+ + "environments like mobile phones", entry.get("sessionrequirements"));
+ assertEquals("beginners-guide-android", entry.get("sessionlink"));
+ assertEquals("#android1", entry.get("sessionhashtag"));
+ assertEquals("http://www.google.com/moderator/#15/e=68f0&t=68f0.45",
+ entry.get("moderatorlink"));
+ assertEquals(
+ "https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B-Xhdu7ZkBHw",
+ entry.get("wavelink"));
+ assertEquals("https://wave.google.com/wave/#restored:wave:googlewave.com",
+ entry.get("_e8rn7"));
+ assertEquals("w%252B-Xhdu7ZkBHw", entry.get("_dmair"));
+ assertEquals("w+-Xhdu7ZkBHw", entry.get("waveid"));
+ }
+
+ public void testParseEmpty() throws Exception {
+ final XmlPullParser parser = openAssetParser("spreadsheet-empty.xml");
+ final SpreadsheetEntry entry = SpreadsheetEntry.fromParser(parser);
+
+ assertEquals("unexpected columns", 0, entry.size());
+ }
+
+ public void testParseEmptyField() throws Exception {
+ final XmlPullParser parser = openAssetParser("spreadsheet-emptyfield.xml");
+ final SpreadsheetEntry entry = SpreadsheetEntry.fromParser(parser);
+
+ assertEquals("unexpected columns", 3, entry.size());
+ assertEquals("Wednesday May 19", entry.get("sessiondate"));
+ assertEquals("", entry.get("sessiontime"));
+ assertEquals("6", entry.get("room"));
+
+ }
+
+ public void testParseSingle() throws Exception {
+ final XmlPullParser parser = openAssetParser("spreadsheet-single.xml");
+ final SpreadsheetEntry entry = SpreadsheetEntry.fromParser(parser);
+
+ assertEquals("unexpected columns", 1, entry.size());
+ assertEquals("Wednesday May 19", entry.get("sessiondate"));
+ }
+
+ private XmlPullParser openAssetParser(String assetName) throws XmlPullParserException,
+ IOException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+ final Context testContext = getTestContext(this);
+ return ParserUtils.newPullParser(testContext.getResources().getAssets().open(assetName));
+ }
+
+ /**
+ * Exposes method {@code getTestContext()} in {@link AndroidTestCase}, which
+ * is hidden for now. Useful for obtaining access to the test assets.
+ */
+ public static Context getTestContext(AndroidTestCase testCase) throws IllegalAccessException,
+ InvocationTargetException, NoSuchMethodException {
+ return (Context) AndroidTestCase.class.getMethod("getTestContext").invoke(testCase);
+ }
+}
diff --git a/tests/src/com/google/android/apps/iosched/util/SpreadsheetEntryTest.java b/tests/src/com/google/android/apps/iosched/util/SpreadsheetEntryTest.java
new file mode 100644
index 0000000..9268101
--- /dev/null
+++ b/tests/src/com/google/android/apps/iosched/util/SpreadsheetEntryTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2010 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.
+ */
+
+package com.google.android.apps.iosched.util;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+
+public class SpreadsheetEntryTest extends AndroidTestCase {
+
+ public void testParseNormal() throws Exception {
+ final XmlPullParser parser = openAssetParser("spreadsheet-normal.xml");
+ final SpreadsheetEntry entry = SpreadsheetEntry.fromParser(parser);
+
+ assertEquals("unexpected columns", 19, entry.size());
+ assertEquals("Wednesday May 19", entry.get("sessiondate"));
+ assertEquals("10:45am-11:45am", entry.get("sessiontime"));
+ assertEquals("6", entry.get("room"));
+ assertEquals("Android", entry.get("product"));
+ assertEquals("Android", entry.get("track"));
+ assertEquals("101", entry.get("sessiontype"));
+ assertEquals("A beginner's guide to Android", entry.get("sessiontitle"));
+ assertEquals("Android, Mobile, Java", entry.get("tags"));
+ assertEquals("Reto Meier", entry.get("sessionspeakers"));
+ assertEquals("retomeier", entry.get("speakers"));
+ assertEquals("This session will introduce some of the basic concepts involved in "
+ + "Android development. Starting with an overview of the SDK APIs available "
+ + "to developers, we will work through some simple code examples that "
+ + "explore some of the more common user features including using sensors, "
+ + "maps, and geolocation.", entry.get("sessionabstract"));
+ assertEquals("Proficiency in Java and a basic understanding of embedded "
+ + "environments like mobile phones", entry.get("sessionrequirements"));
+ assertEquals("beginners-guide-android", entry.get("sessionlink"));
+ assertEquals("#android1", entry.get("sessionhashtag"));
+ assertEquals("http://www.google.com/moderator/#15/e=68f0&t=68f0.45",
+ entry.get("moderatorlink"));
+ assertEquals(
+ "https://wave.google.com/wave/#restored:wave:googlewave.com!w%252B-Xhdu7ZkBHw",
+ entry.get("wavelink"));
+ assertEquals("https://wave.google.com/wave/#restored:wave:googlewave.com",
+ entry.get("_e8rn7"));
+ assertEquals("w%252B-Xhdu7ZkBHw", entry.get("_dmair"));
+ assertEquals("w+-Xhdu7ZkBHw", entry.get("waveid"));
+ }
+
+ public void testParseEmpty() throws Exception {
+ final XmlPullParser parser = openAssetParser("spreadsheet-empty.xml");
+ final SpreadsheetEntry entry = SpreadsheetEntry.fromParser(parser);
+
+ assertEquals("unexpected columns", 0, entry.size());
+ }
+
+ public void testParseEmptyField() throws Exception {
+ final XmlPullParser parser = openAssetParser("spreadsheet-emptyfield.xml");
+ final SpreadsheetEntry entry = SpreadsheetEntry.fromParser(parser);
+
+ assertEquals("unexpected columns", 3, entry.size());
+ assertEquals("Wednesday May 19", entry.get("sessiondate"));
+ assertEquals("", entry.get("sessiontime"));
+ assertEquals("6", entry.get("room"));
+
+ }
+
+ public void testParseSingle() throws Exception {
+ final XmlPullParser parser = openAssetParser("spreadsheet-single.xml");
+ final SpreadsheetEntry entry = SpreadsheetEntry.fromParser(parser);
+
+ assertEquals("unexpected columns", 1, entry.size());
+ assertEquals("Wednesday May 19", entry.get("sessiondate"));
+ }
+
+ private XmlPullParser openAssetParser(String assetName) throws XmlPullParserException,
+ IOException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+ final Context testContext = getTestContext(this);
+ return ParserUtils.newPullParser(testContext.getResources().getAssets().open(assetName));
+ }
+
+ /**
+ * Exposes method {@code getTestContext()} in {@link AndroidTestCase}, which
+ * is hidden for now. Useful for obtaining access to the test assets.
+ */
+ public static Context getTestContext(AndroidTestCase testCase) throws IllegalAccessException,
+ InvocationTargetException, NoSuchMethodException {
+ return (Context) AndroidTestCase.class.getMethod("getTestContext").invoke(testCase);
+ }
+}