whoisip
HTML source viewer


HTML source viewer

Write webpage URL To view source code of any webpage
mspn.in/main.php (do not add http://)

Source code
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head profile="http://gmpg.org/xfn/11">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title> TurboManage</title>

<link rel="stylesheet" href="http://s2.wp.com/wp-content/themes/pub/andreas09/style.css?m=1393008274g" type="text/css" media="screen" />
<link rel="stylesheet" href="http://s2.wp.com/wp-content/themes/pub/andreas09/red2.css?m=1315885645g" type="text/css" media="screen" />
<link rel="pingback" href="http://turbomanage.wordpress.com/xmlrpc.php" />

<link rel="alternate" type="application/rss+xml" title="TurboManage &raquo; Feed" href="http://turbomanage.wordpress.com/feed/" />
<link rel="alternate" type="application/rss+xml" title="TurboManage &raquo; Comments Feed" href="http://turbomanage.wordpress.com/comments/feed/" />
<script type="text/javascript">
/* <![CDATA[ */
function addLoadEvent(func){var oldonload=window.onload;if(typeof window.onload!='function'){window.onload=func;}else{window.onload=function(){oldonload();func();}}}
/* ]]> */
</script>
<link rel='stylesheet' id='all-css-0' href='http://s2.wp.com/_static/??-eJx9kNFuwjAMRX+IYo3BNh7QviUkxk1x6ihOVfH3pIWJok55ynVyj30dGGNjpc/YZwhDE3kg3yto8Iy3mKRDm9+rrVXdwP8Y+ysqdJijsddmrlb2Mwu9ACFCJ0NuLsIsI4zeEVZnWElY7kM0eXIEdN4gYyi2Ghbi1x81ybYEXGdb+BNOQYskKK5FWYOiaNmEjU+grUkT/DxrFGGPyZeHpVwBuS07KrR7IJaz4VrHxy/qnJxEXELj5o6/4fTxefze7fY/h0N3BzZ2v3g=' type='text/css' media='all' />
<link rel='stylesheet' id='print-css-0' href='http://s0.wp.com/wp-content/mu-plugins/global-print/global-print.css?m=1387483371g' type='text/css' media='print' />
<script type='text/javascript'>
/* <![CDATA[ */
var LoggedOutFollow = {"invalid_email":"Your subscription did not succeed, please try again with a valid email address."};
/* ]]> */
</script>
<script type='text/javascript' src='http://s1.wp.com/_static/??-eJyFjd0KQiEQhF8ojyfORXkRPYvpJmv+5a5JPX0GdRFFwcAszDc7sheByYRmgaQfOjeo16dNnlbyFyAiuqoZpojpBZucGBI/2JKJIxBpB1/S9ylMF4T+F/PARZuTqEB4+/h6CNmJEprDRHLcDmxuLI45hNxlR+uAR2cfd+tFbeatmhfl798SYMI='></script>
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://turbomanage.wordpress.com/xmlrpc.php?rsd" />
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://turbomanage.wordpress.com/wp-includes/wlwmanifest.xml" />
<meta name="generator" content="WordPress.com" />
<link rel='shortlink' href='http://wp.me/1qLI' />

<!-- Jetpack Open Graph Tags -->
<meta property="og:type" content="website" />
<meta property="og:title" content="TurboManage" />
<meta property="og:description" content="David Chandler&#039;s Journal of Java Web and Mobile Development" />
<meta property="og:url" content="http://turbomanage.wordpress.com/" />
<meta property="og:site_name" content="TurboManage" />
<meta property="og:image" content="http://wordpress.com/i/blank.jpg?m=1383295312g" />
<meta name="twitter:site" content="@wordpressdotcom" />
<meta property="fb:app_id" content="249643311490" />
<meta property="article:publisher" content="https://www.facebook.com/WordPresscom" />
<link rel="shortcut icon" type="image/x-icon" href="http://s2.wp.com/i/favicon.ico?m=1311975824g" sizes="16x16 24x24 32x32 48x48" />
<link rel="icon" type="image/x-icon" href="http://s2.wp.com/i/favicon.ico?m=1311975824g" sizes="16x16 24x24 32x32 48x48" />
<link rel="apple-touch-icon-precomposed" href="http://s0.wp.com/i/webclip.png?m=1391188133g" />
<link rel='openid.server' href='http://turbomanage.wordpress.com/?openidserver=1' />
<link rel='openid.delegate' href='http://turbomanage.wordpress.com/' />
<link rel="search" type="application/opensearchdescription+xml" href="http://turbomanage.wordpress.com/osd.xml" title="TurboManage" />
<link rel="search" type="application/opensearchdescription+xml" href="http://wordpress.com/opensearch.xml" title="WordPress.com" />
<meta name="application-name" content="TurboManage" /><meta name="msapplication-window" content="width=device-width;height=device-height" /><meta name="msapplication-tooltip" content="David Chandler&#039;s Journal of Java Web and Mobile Development" /><meta name="msapplication-task" content="name=Subscribe;action-uri=http://turbomanage.wordpress.com/feed/;icon-uri=http://s2.wp.com/i/favicon.ico" /><meta name="msapplication-task" content="name=Sign up for a free blog;action-uri=http://wordpress.com/signup/;icon-uri=http://s2.wp.com/i/favicon.ico" /><meta name="msapplication-task" content="name=WordPress.com Support;action-uri=http://support.wordpress.com/;icon-uri=http://s2.wp.com/i/favicon.ico" /><meta name="msapplication-task" content="name=WordPress.com Forums;action-uri=http://forums.wordpress.com/;icon-uri=http://s2.wp.com/i/favicon.ico" /><meta name="title" content="TurboManage on WordPress.com" />
<meta name="description" content="David Chandler&#039;s Journal of Java Web and Mobile Development (by David Chandler)" />
<style type="text/css" id="syntaxhighlighteranchor"></style>

</head>

<body class="home blog mp6 highlander-enabled highlander-light">

<div id="container">
<div id="sitename">
<h1><a href="http://turbomanage.wordpress.com/">TurboManage</a></h1>
<h2>David Chandler&#039;s Journal of Java Web and Mobile Development</h2>
</div>

<div id="mainmenu">
<ul class="level1">
<li class="current_page_item"><a href="http://turbomanage.wordpress.com/">Home</a></li>
<li class="page_item page-item-309"><a href="http://turbomanage.wordpress.com/dmc/">DMC</a></li>
<li class="page_item page-item-293"><a href="http://turbomanage.wordpress.com/gae/">GAE</a></li>
<li class="page_item page-item-277"><a href="http://turbomanage.wordpress.com/gwt/">GWT</a></li>
<li class="page_item page-item-447"><a href="http://turbomanage.wordpress.com/jdo/">JDO</a></li>
<li class="page_item page-item-295"><a href="http://turbomanage.wordpress.com/jsf/">JSF</a></li>
<li class="page_item page-item-218"><a href="http://turbomanage.wordpress.com/mvp/">MVP</a></li>
<li class="page_item page-item-319 page_item_has_children"><a href="http://turbomanage.wordpress.com/phi/">Phi</a></li>
<li class="page_item page-item-34 page_item_has_children"><a href="http://turbomanage.wordpress.com/writings/">PUB</a></li>
<li class="page_item page-item-132"><a href="http://turbomanage.wordpress.com/slr/">SLR</a></li>
</ul>
</div>

<div id="wrap">
<div id="leftside">

<ul>

<li id="gravatar-2" class="widget widget_gravatar"><h2 class="widgettitle">David M. Chandler</h2>
<p><img alt='' src='http://0.gravatar.com/avatar/614e792082e78cd852d0b60ae0f28bef?s=128&#038;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D128&#038;r=G' class='avatar avatar-128 grav-widget-none' height='128' width='128' /></p>
<p><a rel="author" href="https://profiles.google.com/102717421433762219474"> <img src="https://ssl.gstatic.com/images/icons/gplus-32.png" align="left" style="vertical-align:top;padding-right:5px;"></a><br />
Web app developer since 1994 and ex-Googler now residing in Peru with the wife of my youth and our three youngest children. I am working on a software development company and hoping to do more teaching as my Spanish improves. My current side project is a not-for-profit startup using <a href="/gwt">GWT</a> on <a href="/gae">AppEngine</a>. In my "spare" time, I take pictures, preferably of Rocky Mountain National Park... or Peru.</p>
</li>
<li><h2>Subscribe</h2>
<ul>
<li class="feed"><a href="http://turbomanage.wordpress.com/feed/">Entries (RSS)</a></li>
<li class="feed"><a href="http://turbomanage.wordpress.com/comments/feed/">Comments (RSS)</a></li>
</ul>
</li>
<li id="blog_subscription-3" class="widget widget_blog_subscription"><h2 class="widgettitle"><label for="subscribe-field">Email Subscription</label></h2>

<form action="https://subscribe.wordpress.com" method="post" accept-charset="utf-8" id="subscribe-blog">
<p>Enter your email address to subscribe to this blog and receive notifications of new posts by email.</p>
<p>Join 220 other followers</p>
<p><input type="text" name="email" style="width: 95%; padding: 1px 2px" value="" id="subscribe-field"/></p>

<p>
<input type="hidden" name="action" value="subscribe"/>
<input type="hidden" name="blog_id" value="341230"/>
<input type="hidden" name="source" value="http://turbomanage.wordpress.com/"/>
<input type="hidden" name="sub-type" value="widget"/>
<input type="hidden" name="redirect_fragment" value="blog_subscription-3" />
<input type="hidden" id="_wpnonce" name="_wpnonce" value="7e29739e82" /> <input type="submit" value="Sign me up!" />
</p>
</form>

</li>
<li id="calendar-2" class="widget widget_calendar"><h2 class="widgettitle">Sleepless Nights&#8230;</h2>
<div id="calendar_wrap"><table id="wp-calendar">
<caption>April 2014</caption>
<thead>
<tr>
<th scope="col" title="Sunday">S</th>
<th scope="col" title="Monday">M</th>
<th scope="col" title="Tuesday">T</th>
<th scope="col" title="Wednesday">W</th>
<th scope="col" title="Thursday">T</th>
<th scope="col" title="Friday">F</th>
<th scope="col" title="Saturday">S</th>
</tr>
</thead>

<tfoot>
<tr>
<td colspan="3" id="prev"><a href="http://turbomanage.wordpress.com/2014/03/" title="View posts for March 2014">&laquo; Mar</a></td>
<td class="pad">&nbsp;</td>
<td colspan="3" id="next" class="pad">&nbsp;</td>
</tr>
</tfoot>

<tbody>
<tr>
<td colspan="2" class="pad">&nbsp;</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td>
</tr>
<tr>
<td>6</td><td>7</td><td>8</td><td>9</td><td>10</td><td>11</td><td>12</td>
</tr>
<tr>
<td>13</td><td>14</td><td>15</td><td>16</td><td>17</td><td>18</td><td>19</td>
</tr>
<tr>
<td id="today">20</td><td>21</td><td>22</td><td>23</td><td>24</td><td>25</td><td>26</td>
</tr>
<tr>
<td>27</td><td>28</td><td>29</td><td>30</td>
<td class="pad" colspan="3">&nbsp;</td>
</tr>
</tbody>
</table></div></li>
<li id="blog-stats-2" class="widget widget_blog-stats"><h2 class="widgettitle">Blog Stats</h2>
<ul>
<li>608,275 hits</li>
</ul>
</li>
</ul>
</div>
<!-- Right Sidebar Template -->
<div id="rightside">
<ul>
<li><form method="get" id="searchform" action="http://turbomanage.wordpress.com/">
<div><input id="searchbox" type="text" value="" name="s"/>
<input type="submit" id="searchbutton" value="Search"/>
</div>
</form>
</li>
<li id="text-3" class="widget widget_text"><h2 class="widgettitle">Quick Reference</h2>
<div class="textwidget"><a href="https://docs.google.com/document/d/1R_ItRe90gf6vKrORsJoOS3M2idGP_Qnkdp4pftKjosk/edit">Eclipse Cheat Sheet</a><br>
</div>
</li>
<li id="categories-1" class="widget widget_categories"><h2 class="widgettitle">Categories</h2>
<ul>
<li class="cat-item cat-item-641922"><a href="http://turbomanage.wordpress.com/category/android/" title="View all posts filed under Android">Android</a> (17)
</li>
<li class="cat-item cat-item-6022068"><a href="http://turbomanage.wordpress.com/category/appengine/" title="View all posts filed under AppEngine">AppEngine</a> (51)
</li>
<li class="cat-item cat-item-277901"><a href="http://turbomanage.wordpress.com/category/art-of-programming/" title="View all posts filed under Art of Programming">Art of Programming</a> (4)
</li>
<li class="cat-item cat-item-277875"><a href="http://turbomanage.wordpress.com/category/business-of-software/" title="View all posts filed under Business of Software">Business of Software</a> (11)
</li>
<li class="cat-item cat-item-14133"><a href="http://turbomanage.wordpress.com/category/coldfusion/" title="View all posts filed under ColdFusion">ColdFusion</a> (4)
</li>
<li class="cat-item cat-item-325481"><a href="http://turbomanage.wordpress.com/category/dart/" title="View all posts filed under Dart">Dart</a> (10)
</li>
<li class="cat-item cat-item-18466"><a href="http://turbomanage.wordpress.com/category/eclipse/" title="View all posts filed under Eclipse">Eclipse</a> (10)
</li>
<li class="cat-item cat-item-657"><a href="http://turbomanage.wordpress.com/category/economics/" title="View all posts filed under Economics">Economics</a> (3)
</li>
<li class="cat-item cat-item-277879"><a href="http://turbomanage.wordpress.com/category/ergonomics/" title="View all posts filed under Ergonomics">Ergonomics</a> (5)
</li>
<li class="cat-item cat-item-28767541"><a href="http://turbomanage.wordpress.com/category/gin-guice/" title="View all posts filed under GIN / Guice">GIN / Guice</a> (13)
</li>
<li class="cat-item cat-item-204985"><a href="http://turbomanage.wordpress.com/category/google-web-toolkit/" title="View all posts filed under Google Web Toolkit">Google Web Toolkit</a> (77)
</li>
<li class="cat-item cat-item-6387356"><a href="http://turbomanage.wordpress.com/category/headsmack/" title="View all posts filed under Headsmack">Headsmack</a> (11)
</li>
<li class="cat-item cat-item-4403413"><a href="http://turbomanage.wordpress.com/category/java-data-objects/" title="View all posts filed under Java Data Objects">Java Data Objects</a> (4)
</li>
<li class="cat-item cat-item-25512"><a href="http://turbomanage.wordpress.com/category/javaserver-faces/" title="View all posts filed under JavaServer Faces">JavaServer Faces</a> (16)
</li>
<li class="cat-item cat-item-2194995"><a href="http://turbomanage.wordpress.com/category/model-view-presenter/" title="View all posts filed under Model-View-Presenter">Model-View-Presenter</a> (23)
</li>
<li class="cat-item cat-item-219374"><a href="http://turbomanage.wordpress.com/category/pc-tech/" title="View all posts filed under PC Tech">PC Tech</a> (17)
</li>
<li class="cat-item cat-item-436"><a href="http://turbomanage.wordpress.com/category/photography/" title="View all posts filed under Photography">Photography</a> (3)
</li>
<li class="cat-item cat-item-279765"><a href="http://turbomanage.wordpress.com/category/web-app-security/" title="View all posts filed under Web App Security">Web App Security</a> (8)
</li>
</ul>
</li>
<li id="recent-posts-2" class="widget widget_recent_entries"> <h2 class="widgettitle">Recent Posts</h2>
<ul>
<li>
<a href="http://turbomanage.wordpress.com/2014/03/20/storm-gen-has-moved/">storm-gen has moved</a>
</li>
<li>
<a href="http://turbomanage.wordpress.com/2014/02/18/open-for-business-in-peru-2/">Open for business in&nbsp;Peru</a>
</li>
<li>
<a href="http://turbomanage.wordpress.com/2013/10/21/a-long-pause/">A long pause</a>
</li>
<li>
<a href="http://turbomanage.wordpress.com/2013/05/21/google-cloud-android-with-mobile-backend-starter/">Android + Cloud with Mobile Backend&nbsp;Starter</a>
</li>
<li>
<a href="http://turbomanage.wordpress.com/2013/05/20/android-demo-tips-behind-the-scenes-at-google-io/">Android demo tips: behind the scenes at Google&nbsp;I/O</a>
</li>
<li>
<a href="http://turbomanage.wordpress.com/2013/03/26/progressdialog-considered-harmful/">ProgressDialog considered harmful</a>
</li>
<li>
<a href="http://turbomanage.wordpress.com/2013/02/28/optimizing-your-mobile-web-app/">Optimizing your mobile Web&nbsp;app</a>
</li>
<li>
<a href="http://turbomanage.wordpress.com/2013/02/20/testing-multi-user-support-in-the-android-emulator/">Enable multi-user support in the Android&nbsp;emulator</a>
</li>
<li>
<a href="http://turbomanage.wordpress.com/2012/12/18/storm-implementation-notes/">stORM implementation notes</a>
</li>
<li>
<a href="http://turbomanage.wordpress.com/2012/12/11/storm-preview/">stORM: a lightweight DAO generator for Android&nbsp;SQLite</a>
</li>
</ul>
</li>
<li id="image-3" class="widget widget_image"><h2 class="widgettitle">Support the EFF</h2>
<div style="overflow:hidden;"><a href="http://www.eff.org"><img src="https://www.eff.org/sites/default/files/EFF-badge-1c.png" alt="Electronic Frontier Foundation Member 2103" class="alignnone" width="150" height="150" /></a></div>
</li>
</ul>
</div>

<div id="content">

<div class="post-4471 post type-post status-publish format-standard hentry" id="post-4471">
<h2><a href="http://turbomanage.wordpress.com/2014/03/20/storm-gen-has-moved/" rel="bookmark">storm-gen has moved</a></h2>
<p class="date">Posted by <a href="http://turbomanage.wordpress.com/">David Chandler</a> on March 20, 2014</p>
<div class="entry">
<p>In order to more easily support pull requests, I&#8217;ve moved storm-gen, the annotation-based open source ORM for Android SQLite, to Github. You can now find it here:</p>
<p><a href="https://github.com/turbomanage/storm-gen">https://github.com/turbomanage/storm-gen</a></p>
<p>I&#8217;ve migrated all open issues to the Github tracker and, following six months of language school in Peru, have recently resumed work on the framework. Thanks to Alex G, we&#8217;re about to add support for maven, gradle, and Android Studio. I&#8217;m also adding more operators and the long-awaited .order() method to FilterBuilder.</p>
<p>Also, there is now a <a href="https://plus.google.com/u/0/communities/111849422096213317275">G+ community</a> for feature discussion and a <a href="http://stackoverflow.com/questions/tagged/storm-gen">StackOverflow tag</a>.</p>
<p>Enjoy!</p>
<div id="jp-post-flair" class="sharedaddy sd-like-enabled sd-sharing-enabled"><div class="sharedaddy sd-sharing-enabled"><div class="robots-nocontent sd-block sd-social sd-social-icon-text sd-sharing"><h3 class="sd-title">Share this:</h3><div class="sd-content"><ul><li class="share-google-plus-1"><a rel="nofollow" class="share-google-plus-1 sd-button share-icon" href="http://turbomanage.wordpress.com/2014/03/20/storm-gen-has-moved/?share=google-plus-1" title="Click to share on Google+" id="sharing-google-4471"><span>Google</span></a></li><li class="share-twitter"><a rel="nofollow" class="share-twitter sd-button share-icon" href="http://turbomanage.wordpress.com/2014/03/20/storm-gen-has-moved/?share=twitter" title="Click to share on Twitter" id="sharing-twitter-4471"><span>Twitter</span></a></li><li class="share-linkedin"><a rel="nofollow" class="share-linkedin sd-button share-icon" href="http://turbomanage.wordpress.com/2014/03/20/storm-gen-has-moved/?share=linkedin" title="Click to share on LinkedIn" id="sharing-linkedin-4471"><span>LinkedIn</span></a></li><li class="share-email"><a rel="nofollow" class="share-email sd-button share-icon" href="http://turbomanage.wordpress.com/2014/03/20/storm-gen-has-moved/?share=email" title="Click to email this to a friend"><span>Email</span></a></li><li class="share-end"></li></ul></div></div></div><div class='sharedaddy sd-block sd-like jetpack-likes-widget-wrapper jetpack-likes-widget-unloaded' id='like-post-wrapper-341230-4471-53545f640461d' data-src='//widgets.wp.com/likes/#blog_id=341230&amp;post_id=4471&amp;origin=turbomanage.wordpress.com&amp;obj_id=341230-4471-53545f640461d' data-name='like-post-frame-341230-4471-53545f640461d'><h3 class='sd-title'>Like this:</h3><div class='likes-widget-placeholder post-likes-widget-placeholder' style='height:55px'><span class='button'><span>Like</span></span> <span class="loading">Loading...</span></div><span class='sd-text-color'></span><a class='sd-link-color'></a></div></div> </div>

<p class="category">Posted in Uncategorized | <a href="http://turbomanage.wordpress.com/2014/03/20/storm-gen-has-moved/#respond" title="Comment on storm-gen has moved">Leave a Comment &#187;</a></p>
</div>


<div class="post-4385 post type-post status-publish format-standard hentry category-business-of-software" id="post-4385">
<h2><a href="http://turbomanage.wordpress.com/2014/02/18/open-for-business-in-peru-2/" rel="bookmark">Open for business in&nbsp;Peru</a></h2>
<p class="date">Posted by <a href="http://turbomanage.wordpress.com/">David Chandler</a> on February 18, 2014</p>
<div class="entry">
<p>This is my last week in language school (at least until I need a refresher course) and with it come some big changes. My family has settled in to life in Arequipa, at least to the extent that can be expected after five months in a foreign country speaking a new language. I have received my work visa and have started a Peruvian company to focus on training / consulting for software developers.</p>
<p>My first client / partner is Lima-based <a href="http://tektonlabs.com">Tekton Labs</a>, which has hired me to provide technical training and architectural guidance to the cloud and mobile development teams. I am looking forward to working with the team for several reasons:</p>
<ol>
<li>Tekton has a talented team and strongly believes in personal development. This is very important because, while there are a few good computer science programs in the country, there is no substitute for continuing education in our field.</li>
<li>The company has a mature, agile software development process (Scrum), which in my view is a prerequisite for taking on large projects.</li>
<li>Tekton has a sizable portfolio of work for large enterprises, but still offers significantly lower prices than US-based companies.</li>
</ol>
<p>In the US, some of the most coveted jobs in programming are with Internet and software companies. In Peru, on the other hand, they are in finance and telecommunications because they typically pay the best ($500 &#8211; $2,000 / mo. + benefits). Unfortunately, this means there are few cloud and mobile jobs here; however, there ARE talented developers with the necessary training as well as self-taught hackers just like in the US. My aim is to help provide further training for these developers and to connect them with US-based companies like startups who may have great ideas, but can&#8217;t afford US-based labor. Over the next year or two, I&#8217;ll keep you posted on how it&#8217;s going. In the mean time, if you have an idea which might be a fit for a Peruvian development team, ping me on G+ and let&#8217;s talk.</p>
<div id="jp-post-flair" class="sharedaddy sd-like-enabled sd-sharing-enabled"><div class="sharedaddy sd-sharing-enabled"><div class="robots-nocontent sd-block sd-social sd-social-icon-text sd-sharing"><h3 class="sd-title">Share this:</h3><div class="sd-content"><ul><li class="share-google-plus-1"><a rel="nofollow" class="share-google-plus-1 sd-button share-icon" href="http://turbomanage.wordpress.com/2014/02/18/open-for-business-in-peru-2/?share=google-plus-1" title="Click to share on Google+" id="sharing-google-4385"><span>Google</span></a></li><li class="share-twitter"><a rel="nofollow" class="share-twitter sd-button share-icon" href="http://turbomanage.wordpress.com/2014/02/18/open-for-business-in-peru-2/?share=twitter" title="Click to share on Twitter" id="sharing-twitter-4385"><span>Twitter</span></a></li><li class="share-linkedin"><a rel="nofollow" class="share-linkedin sd-button share-icon" href="http://turbomanage.wordpress.com/2014/02/18/open-for-business-in-peru-2/?share=linkedin" title="Click to share on LinkedIn" id="sharing-linkedin-4385"><span>LinkedIn</span></a></li><li class="share-email"><a rel="nofollow" class="share-email sd-button share-icon" href="http://turbomanage.wordpress.com/2014/02/18/open-for-business-in-peru-2/?share=email" title="Click to email this to a friend"><span>Email</span></a></li><li class="share-end"></li></ul></div></div></div><div class='sharedaddy sd-block sd-like jetpack-likes-widget-wrapper jetpack-likes-widget-unloaded' id='like-post-wrapper-341230-4385-53545f6407982' data-src='//widgets.wp.com/likes/#blog_id=341230&amp;post_id=4385&amp;origin=turbomanage.wordpress.com&amp;obj_id=341230-4385-53545f6407982' data-name='like-post-frame-341230-4385-53545f6407982'><h3 class='sd-title'>Like this:</h3><div class='likes-widget-placeholder post-likes-widget-placeholder' style='height:55px'><span class='button'><span>Like</span></span> <span class="loading">Loading...</span></div><span class='sd-text-color'></span><a class='sd-link-color'></a></div></div> </div>

<p class="category">Posted in <a href="http://turbomanage.wordpress.com/category/business-of-software/" title="View all posts in Business of Software" rel="category tag">Business of Software</a> | <a href="http://turbomanage.wordpress.com/2014/02/18/open-for-business-in-peru-2/#comments" title="Comment on Open for business in&nbsp;Peru">2 Comments &#187;</a></p>
</div>


<div class="post-4361 post type-post status-publish format-standard hentry" id="post-4361">
<h2><a href="http://turbomanage.wordpress.com/2013/10/21/a-long-pause/" rel="bookmark">A long pause</a></h2>
<p class="date">Posted by <a href="http://turbomanage.wordpress.com/">David Chandler</a> on October 21, 2013</p>
<div class="entry">
<p><a href="http://turbomanage.files.wordpress.com/2013/10/img_5806_dxo.jpg"><img class="alignright size-medium wp-image-4378" alt="IMG_5806_DxO" src="http://turbomanage.files.wordpress.com/2013/10/img_5806_dxo.jpg?w=300&#038;h=199" width="300" height="199" /></a>Hello faithful readers! I apologize for my long absence on these pages. I have not fallen off the edge of cyberspace, just below the equator. In the preceding months, my family has sold most of our earthly possessions (save the small electronic ones :-)) and moved to Peru. We are currently attending language school in fulfillment of a lifetime goal to become fluent in Spanish. I am currently on unpaid leave with status to be determined after language school. Until then, I have precious little time for coding, but hope to squeeze in some work on storm-gen shortly and to continue writing about Android, App Engine, GWT, and other topics as time permits.</p>
<p>At some point, I&#8217;d also like to write more about how technology has facilitated our move, but suffice it to say that a scanner, Google Drive, Google Play Books, the Kindle app, and a host of phones and tablets make possible a near weightless existence, which is extremely helpful when conducting an international move. Thankfully, Internet connectivity (both mobile and fixed) is quite good in the major cities of Peru, so our &#8220;everything in the cloud&#8221; strategy has worked well thus far, although my wife doesn&#8217;t think the kitchen made the move very well in the cloud.</p>
<div id="jp-post-flair" class="sharedaddy sd-like-enabled sd-sharing-enabled"><div class="sharedaddy sd-sharing-enabled"><div class="robots-nocontent sd-block sd-social sd-social-icon-text sd-sharing"><h3 class="sd-title">Share this:</h3><div class="sd-content"><ul><li class="share-google-plus-1"><a rel="nofollow" class="share-google-plus-1 sd-button share-icon" href="http://turbomanage.wordpress.com/2013/10/21/a-long-pause/?share=google-plus-1" title="Click to share on Google+" id="sharing-google-4361"><span>Google</span></a></li><li class="share-twitter"><a rel="nofollow" class="share-twitter sd-button share-icon" href="http://turbomanage.wordpress.com/2013/10/21/a-long-pause/?share=twitter" title="Click to share on Twitter" id="sharing-twitter-4361"><span>Twitter</span></a></li><li class="share-linkedin"><a rel="nofollow" class="share-linkedin sd-button share-icon" href="http://turbomanage.wordpress.com/2013/10/21/a-long-pause/?share=linkedin" title="Click to share on LinkedIn" id="sharing-linkedin-4361"><span>LinkedIn</span></a></li><li class="share-email"><a rel="nofollow" class="share-email sd-button share-icon" href="http://turbomanage.wordpress.com/2013/10/21/a-long-pause/?share=email" title="Click to email this to a friend"><span>Email</span></a></li><li class="share-end"></li></ul></div></div></div><div class='sharedaddy sd-block sd-like jetpack-likes-widget-wrapper jetpack-likes-widget-unloaded' id='like-post-wrapper-341230-4361-53545f640ae1d' data-src='//widgets.wp.com/likes/#blog_id=341230&amp;post_id=4361&amp;origin=turbomanage.wordpress.com&amp;obj_id=341230-4361-53545f640ae1d' data-name='like-post-frame-341230-4361-53545f640ae1d'><h3 class='sd-title'>Like this:</h3><div class='likes-widget-placeholder post-likes-widget-placeholder' style='height:55px'><span class='button'><span>Like</span></span> <span class="loading">Loading...</span></div><span class='sd-text-color'></span><a class='sd-link-color'></a></div></div> </div>

<p class="category">Posted in Uncategorized | <a href="http://turbomanage.wordpress.com/2013/10/21/a-long-pause/#comments" title="Comment on A long pause">6 Comments &#187;</a></p>
</div>


<div class="post-4299 post type-post status-publish format-standard hentry category-android category-appengine" id="post-4299">
<h2><a href="http://turbomanage.wordpress.com/2013/05/21/google-cloud-android-with-mobile-backend-starter/" rel="bookmark">Android + Cloud with Mobile Backend&nbsp;Starter</a></h2>
<p class="date">Posted by <a href="http://turbomanage.wordpress.com/">David Chandler</a> on May 21, 2013</p>
<div class="entry">
<p>The Google Cloud Solutions team has made it easier than ever to create a cloud backend for your Android application. Brad Abrams and I presented it last week at Google I/O, and Brad has published a gigantic step-by-step blog post with copious screenshots on how we built our sample app, Geek Serendipity. Here&#8217;s all the related content in one handy list:</p>
<ul>
<li><a title="Building Geek Serendipity" href="http://bradabrams.com/2013/05/google-io-2013-session-overview-from-nothing-to-nirvana-in-minutes-cloud-backend-for-your-android-application-building-geek-serendipity/">Building Geek Serendipity</a> (BradAbrams.com)</li>
<li><a title="Google I/O session" href="https://developers.google.com/events/io/sessions/333508149">From Nothing to Nirvana in Minutes: Cloud Backend for Your Android Application</a> (I/O talk)</li>
<li><a style="font-size:13px;" title="Geek Serendipity source" href="https://github.com/bradabrams/GeekSerendipity-io13">Geek Serendipity source</a><span style="font-size:13px;"> (github)</span></li>
<li><a title="Geek Serendipity app" href="https://play.google.com/store/apps/details?id=com.turbomanage.sample.geekwatch">Geek Serendipity app</a> in Google Play Store</li>
<li><a title="Android demo tips: behind the scenes at Google I/O" href="http://turbomanage.wordpress.com/2013/05/20/android-demo-tips-behind-the-scenes-at-google-io/">Android demo tips: behind the scenes at Google I/O</a> (lessons learned)</li>
</ul>
<div id="jp-post-flair" class="sharedaddy sd-like-enabled sd-sharing-enabled"><div class="sharedaddy sd-sharing-enabled"><div class="robots-nocontent sd-block sd-social sd-social-icon-text sd-sharing"><h3 class="sd-title">Share this:</h3><div class="sd-content"><ul><li class="share-google-plus-1"><a rel="nofollow" class="share-google-plus-1 sd-button share-icon" href="http://turbomanage.wordpress.com/2013/05/21/google-cloud-android-with-mobile-backend-starter/?share=google-plus-1" title="Click to share on Google+" id="sharing-google-4299"><span>Google</span></a></li><li class="share-twitter"><a rel="nofollow" class="share-twitter sd-button share-icon" href="http://turbomanage.wordpress.com/2013/05/21/google-cloud-android-with-mobile-backend-starter/?share=twitter" title="Click to share on Twitter" id="sharing-twitter-4299"><span>Twitter</span></a></li><li class="share-linkedin"><a rel="nofollow" class="share-linkedin sd-button share-icon" href="http://turbomanage.wordpress.com/2013/05/21/google-cloud-android-with-mobile-backend-starter/?share=linkedin" title="Click to share on LinkedIn" id="sharing-linkedin-4299"><span>LinkedIn</span></a></li><li class="share-email"><a rel="nofollow" class="share-email sd-button share-icon" href="http://turbomanage.wordpress.com/2013/05/21/google-cloud-android-with-mobile-backend-starter/?share=email" title="Click to email this to a friend"><span>Email</span></a></li><li class="share-end"></li></ul></div></div></div><div class='sharedaddy sd-block sd-like jetpack-likes-widget-wrapper jetpack-likes-widget-unloaded' id='like-post-wrapper-341230-4299-53545f640e2ba' data-src='//widgets.wp.com/likes/#blog_id=341230&amp;post_id=4299&amp;origin=turbomanage.wordpress.com&amp;obj_id=341230-4299-53545f640e2ba' data-name='like-post-frame-341230-4299-53545f640e2ba'><h3 class='sd-title'>Like this:</h3><div class='likes-widget-placeholder post-likes-widget-placeholder' style='height:55px'><span class='button'><span>Like</span></span> <span class="loading">Loading...</span></div><span class='sd-text-color'></span><a class='sd-link-color'></a></div></div> </div>

<p class="category">Posted in <a href="http://turbomanage.wordpress.com/category/android/" title="View all posts in Android" rel="category tag">Android</a>, <a href="http://turbomanage.wordpress.com/category/appengine/" title="View all posts in AppEngine" rel="category tag">AppEngine</a> | <a href="http://turbomanage.wordpress.com/2013/05/21/google-cloud-android-with-mobile-backend-starter/#comments" title="Comment on Android + Cloud with Mobile Backend&nbsp;Starter">8 Comments &#187;</a></p>
</div>


<div class="post-4293 post type-post status-publish format-standard hentry category-android category-eclipse category-headsmack" id="post-4293">
<h2><a href="http://turbomanage.wordpress.com/2013/05/20/android-demo-tips-behind-the-scenes-at-google-io/" rel="bookmark">Android demo tips: behind the scenes at Google&nbsp;I/O</a></h2>
<p class="date">Posted by <a href="http://turbomanage.wordpress.com/">David Chandler</a> on May 20, 2013</p>
<div class="entry">
<p>Last week at Google I/O, Brad Abrams and I decided to go for the gold and give a talk on <a title="From Nothing to Nirvana in Minutes: Building a Cloud Backend for Your Android Application" href="https://developers.google.com/events/io/sessions/333508149">building a cloud backend for your Android app</a>, complete with live coding on stage. For a more interesting example, we chose to build a sample app which relies on location services and Google Cloud Messaging. It all seemed like a good idea at the time. For the benefit of other Android presenters, I wanted to give a behind-the-scenes view of how we overcame some significant challenges to pull off the demos. Also, I will explain the anomalies we saw.</p>
<p><strong>Device display.</strong> The easiest way to show an app is using an emulator, but Maps V2 API is part of Google Play Services, which doesn&#8217;t currently run on the Android emulator, so we needed physical devices. <a title="Droid@Screen" href="http://droid-at-screen.ribomation.com/">Droid@Screen</a> works, but it updates slowly and requires the USB cable, which we needed for hardwired Ethernet. Some devices support HDMI out (Galaxy Nexus, N10) which can be used with a <a title="BlackMagic Design Intensity Shuttle Thunderbolt" href="http://www.amazon.com/gp/product/B007ZDHDRS/">BlackMagic</a> or similar HDMI capture box to show the device display on your Mac screen. Note that this requires a Mac with two Thunderbolt ports (hello Retina! BlackMagic in, Mac display adapter out). We could not use this because the phone&#8217;s HDMI connector is shared with the USB connector, which we needed for hardwired Ethernet. Fortunately, we had a Wolfvision projector camera available and a 4-port video switcher to show our Macs or the camera. For best results with a projector, set the phone display brightness to max.</p>
<p><strong>The network.</strong> Gather 5000+ geeks in a massive concrete structure, give them all a new wifi device, and then try to get a working Internet connection&#8230; Despite Google&#8217;s stellar efforts, getting wireless Internet at Moscone Center remains very challenging. 3G / 4G / LTE is inhibited by the concrete and steel, and even 5GHz wifi suffered from RF interference or sheer demand. Thus, we knew in advance that we would need hardwired Internet. This was no problem for our laptops, but we also needed 2 phones for the demos to show continuous queries in action. Fortunately, Galaxy Nexus phones support wired connections using a USB OTG adapter paired with a USB to Ethernet adapter, and it just worked. So far so good.</p>
<p><strong>Deferred launch.</strong> Because we were coding live in Eclipse, we deployed the code to the phones in real time via the USB cable. When you run an Android app in Eclipse, the default action is to install and run the app. But this  created a problem for the simple version of our demo app, which needed an Internet connection on launch to send a location update immediately. We had to disconnect the USB cable, walk the phone over to the camera, plug in the hardwired Ethernet, and only then launch the app via the home screen. Fortunately, Eclipse has a way to support this. In the project&#8217;s run configuration on the Android tab, simply select &#8220;Do nothing&#8221; for the launch action. This installs the app, but doesn&#8217;t launch it.</p>
<p><strong>Live coding.</strong> Speaking of live coding, we have all seen those demos with lots of tedious cut and paste operations from a text file to the IDE. As a presenter, I want something visible in the IDE to prompt me for the fully manually steps as well as an easy way to paste in blocks of code. Eclipse snippets are just the thing. You can show them off to the side of your main editor window, then double-click on a snippet to paste it in. I used snippet titles without code to prompt me for the manual steps. Another challenge of live coding is that you might make a mistake at an early stage of the demo which ruins a later stage. In order to facilitate recovery, we created snapshots of working demo code at six different stages during our presentation. It would have been nice to preinstall these app versions on the devices; however, they all use the same package name and the Android installer doesn&#8217;t allow more than one at the same time. Hence the need for adb install and the deferred launch technique. Also we had to uninstall the prior version of the app on the device before each coding stage. Furthermore, Eclipse doesn&#8217;t let you create multiple projects with the same name. There is a workaround, but it kind of spoils the magic of our talk and I&#8217;ve already spilled enough secrets&#8230; let&#8217;s just say Eclipse workspaces and &#8220;Copy project&#8221; are a beautiful thing.</p>
<p><strong>Location services.</strong> This is the one that snagged us in the first demo. It turns out that location services are not in fact magic, but require GPS (in Moscone?!) or wifi, which we turned off just before speaking to eliminate the possibility of conflict with the hardwired Ethernet. Fortunately, we were able to get a good enough wifi signal in a later demo to show location. On desktop devices, geolocation works with a hard-wired connection, so I&#8217;m not yet sure whether this is a bug or a feature on mobile&#8230; it is admittedly an uncommon use case.</p>
<p><strong>The unanticipated.</strong> After recovering from the first demo glitch by turning on wifi, I demonstrated that location services worked, but it never sent the location to the server. It turns out the code was working as intended&#8211;I had already enabled authentication for my next segment, but the app from the previous segment which I revisited did not have auth turned on. Finally, the last demo showed two markers when it should have showed only one. The second marker turned out be a fellow Googler in the audience who had helped dogfood an early version of the demo app. So once again, the code worked as intended, just not as I intended at the moment. But&#8230; this is the stuff live demos are made of. I still prefer live demos with a few glitches to an hour full of slides <span class='wp-smiley emoji emoji-smile' title=':-)'>:-)</span></p>
<p>So now you know what Brad and I were busily doing while the other was talking and why talks of this complexity are pretty much impossible without two people and a lot of presentation gear. It was a teeny bit intimidating knowing that our session was part of the Google I/O live stream with thousands of viewers, but in the end, it came off (almost) without glitches and our gracious audience was wowed by the Mobile Backend Starter project. Mission accomplished!</p>
<p>See my <a title="Google Cloud + Android with Mobile Backend Starter" href="http://turbomanage.wordpress.com/2013/05/21/google-cloud-android-with-mobile-backend-starter/">next post</a> for a complete list of resources on Mobile Backend Starter.</p>
<div id="jp-post-flair" class="sharedaddy sd-like-enabled sd-sharing-enabled"><div class="sharedaddy sd-sharing-enabled"><div class="robots-nocontent sd-block sd-social sd-social-icon-text sd-sharing"><h3 class="sd-title">Share this:</h3><div class="sd-content"><ul><li class="share-google-plus-1"><a rel="nofollow" class="share-google-plus-1 sd-button share-icon" href="http://turbomanage.wordpress.com/2013/05/20/android-demo-tips-behind-the-scenes-at-google-io/?share=google-plus-1" title="Click to share on Google+" id="sharing-google-4293"><span>Google</span></a></li><li class="share-twitter"><a rel="nofollow" class="share-twitter sd-button share-icon" href="http://turbomanage.wordpress.com/2013/05/20/android-demo-tips-behind-the-scenes-at-google-io/?share=twitter" title="Click to share on Twitter" id="sharing-twitter-4293"><span>Twitter</span></a></li><li class="share-linkedin"><a rel="nofollow" class="share-linkedin sd-button share-icon" href="http://turbomanage.wordpress.com/2013/05/20/android-demo-tips-behind-the-scenes-at-google-io/?share=linkedin" title="Click to share on LinkedIn" id="sharing-linkedin-4293"><span>LinkedIn</span></a></li><li class="share-email"><a rel="nofollow" class="share-email sd-button share-icon" href="http://turbomanage.wordpress.com/2013/05/20/android-demo-tips-behind-the-scenes-at-google-io/?share=email" title="Click to email this to a friend"><span>Email</span></a></li><li class="share-end"></li></ul></div></div></div><div class='sharedaddy sd-block sd-like jetpack-likes-widget-wrapper jetpack-likes-widget-unloaded' id='like-post-wrapper-341230-4293-53545f641371e' data-src='//widgets.wp.com/likes/#blog_id=341230&amp;post_id=4293&amp;origin=turbomanage.wordpress.com&amp;obj_id=341230-4293-53545f641371e' data-name='like-post-frame-341230-4293-53545f641371e'><h3 class='sd-title'>Like this:</h3><div class='likes-widget-placeholder post-likes-widget-placeholder' style='height:55px'><span class='button'><span>Like</span></span> <span class="loading">Loading...</span></div><span class='sd-text-color'></span><a class='sd-link-color'></a></div></div> </div>

<p class="category">Posted in <a href="http://turbomanage.wordpress.com/category/android/" title="View all posts in Android" rel="category tag">Android</a>, <a href="http://turbomanage.wordpress.com/category/eclipse/" title="View all posts in Eclipse" rel="category tag">Eclipse</a>, <a href="http://turbomanage.wordpress.com/category/headsmack/" title="View all posts in Headsmack" rel="category tag">Headsmack</a> | <a href="http://turbomanage.wordpress.com/2013/05/20/android-demo-tips-behind-the-scenes-at-google-io/#comments" title="Comment on Android demo tips: behind the scenes at Google&nbsp;I/O">10 Comments &#187;</a></p>
</div>


<div class="post-4286 post type-post status-publish format-standard hentry category-android category-headsmack" id="post-4286">
<h2><a href="http://turbomanage.wordpress.com/2013/03/26/progressdialog-considered-harmful/" rel="bookmark">ProgressDialog considered harmful</a></h2>
<p class="date">Posted by <a href="http://turbomanage.wordpress.com/">David Chandler</a> on March 26, 2013</p>
<div class="entry">
<p>Roman Nurik recently wrote a <a title="Stop using ProgressDialog" href="https://plus.google.com/u/0/113735310430199015092/posts/eSCfnyTzFUx">G+ post</a> favoring the use of inline ProgressBar over the pop-up ProgressDialog. Here are some additional reasons to consider.</p>
<p>To be sure, ProgressDialog is very convenient. It has a nice spinner and does its job very simply. You can create one as simply as</p>
<pre class="brush: java; title: ; notranslate" title="">
ProgressDialog pd = ProgressDialog.show(this, &quot;Please wait&quot;, &quot;working...&quot;);
...
pd.cancel();
</pre>
<p>The first problem is that <strong>by default, there is no way for the user to dismiss a ProgressDialog</strong>. A common scenario is to show the ProgressDialog before making a network request. But what happens if the user gives up before the request times out? The back button doesn&#8217;t work. The home button works as usual, but if you return to the app, the ProgressDialog is still there. To get rid of it, you have to force close the app.</p>
<p>This is easily remedied by setting an additional property:</p>
<pre class="brush: java; title: ; notranslate" title="">
ProgressDialog pd = ProgressDialog.show(this, &quot;Please wait&quot;, &quot;working...&quot;);
pd.setCancelable(true);
...
pd.cancel();
</pre>
<p>However, there is another problem which leads to a lot of confusion for Android or Java newbies. Consider this code which I have inadvertently written myself:</p>
<pre class="brush: java; title: ; notranslate" title="">
ProgressDialog pd = new ProgressDialog(this);
pd.setCancelable(true);
pd.show(this, &quot;Please wait&quot;, &quot;working...&quot;);
</pre>
<p>Like the first example, this will show a ProgressDialog which <strong>cannot be dismissed</strong> with the back button. Why? Because the show() method used here is a static method which returns a new ProgressDialog. The instance named pd is never shown. The compiler will show a warning that a static method is being invoked in a non-static way so you might get a clue that something is wrong; still, this is a really confusing API with <a title="ProgressDialog reference" href="http://developer.android.com/reference/android/app/ProgressDialog.html">no documentation</a>. If you want to use the ProgressDialog constructor directly (as you must in order to subclass it), you must do this instead:</p>
<pre class="brush: java; title: ; notranslate" title="">
ProgressDialog pd = new ProgressDialog(this);
pd.setTitle(&quot;Please wait&quot;);
pd.setMessage(&quot;working...&quot;);
pd.setCancelable(true);
pd.show();
</pre>
<p>Note that the no-arg show() method used here is an instance method inherited from the Dialog class. The resulting ProgressDialog may indeed be canceled.</p>
<p>StackOverflow indicates that ProgressDialog has been a headache for <a title="78 results on StackOverflow" href="http://stackoverflow.com/search?q=android+can%27t+dismiss+progressdialog">more than a few</a> developers.</p>
<p>So, like Roman said, use ProgressBar instead. Or if you don&#8217;t, make sure you&#8217;re using ProgressDialog in such a way that it can in fact be dismissed.</p>
<div id="jp-post-flair" class="sharedaddy sd-like-enabled sd-sharing-enabled"><div class="sharedaddy sd-sharing-enabled"><div class="robots-nocontent sd-block sd-social sd-social-icon-text sd-sharing"><h3 class="sd-title">Share this:</h3><div class="sd-content"><ul><li class="share-google-plus-1"><a rel="nofollow" class="share-google-plus-1 sd-button share-icon" href="http://turbomanage.wordpress.com/2013/03/26/progressdialog-considered-harmful/?share=google-plus-1" title="Click to share on Google+" id="sharing-google-4286"><span>Google</span></a></li><li class="share-twitter"><a rel="nofollow" class="share-twitter sd-button share-icon" href="http://turbomanage.wordpress.com/2013/03/26/progressdialog-considered-harmful/?share=twitter" title="Click to share on Twitter" id="sharing-twitter-4286"><span>Twitter</span></a></li><li class="share-linkedin"><a rel="nofollow" class="share-linkedin sd-button share-icon" href="http://turbomanage.wordpress.com/2013/03/26/progressdialog-considered-harmful/?share=linkedin" title="Click to share on LinkedIn" id="sharing-linkedin-4286"><span>LinkedIn</span></a></li><li class="share-email"><a rel="nofollow" class="share-email sd-button share-icon" href="http://turbomanage.wordpress.com/2013/03/26/progressdialog-considered-harmful/?share=email" title="Click to email this to a friend"><span>Email</span></a></li><li class="share-end"></li></ul></div></div></div><div class='sharedaddy sd-block sd-like jetpack-likes-widget-wrapper jetpack-likes-widget-unloaded' id='like-post-wrapper-341230-4286-53545f6417e45' data-src='//widgets.wp.com/likes/#blog_id=341230&amp;post_id=4286&amp;origin=turbomanage.wordpress.com&amp;obj_id=341230-4286-53545f6417e45' data-name='like-post-frame-341230-4286-53545f6417e45'><h3 class='sd-title'>Like this:</h3><div class='likes-widget-placeholder post-likes-widget-placeholder' style='height:55px'><span class='button'><span>Like</span></span> <span class="loading">Loading...</span></div><span class='sd-text-color'></span><a class='sd-link-color'></a></div></div> </div>

<p class="category">Posted in <a href="http://turbomanage.wordpress.com/category/android/" title="View all posts in Android" rel="category tag">Android</a>, <a href="http://turbomanage.wordpress.com/category/headsmack/" title="View all posts in Headsmack" rel="category tag">Headsmack</a> | <a href="http://turbomanage.wordpress.com/2013/03/26/progressdialog-considered-harmful/#respond" title="Comment on ProgressDialog considered harmful">Leave a Comment &#187;</a></p>
</div>


<div class="post-4283 post type-post status-publish format-standard hentry category-android" id="post-4283">
<h2><a href="http://turbomanage.wordpress.com/2013/02/28/optimizing-your-mobile-web-app/" rel="bookmark">Optimizing your mobile Web&nbsp;app</a></h2>
<p class="date">Posted by <a href="http://turbomanage.wordpress.com/">David Chandler</a> on February 28, 2013</p>
<div class="entry">
<p>I frequently meet with mobile app developers using WebView who are looking for guidance on performance optimization. Besides going native, which typically provides the very best user experience, here is an assortment of tips and tools you can use to build better mobile Web pages. These are very rough notes from recent meetings with third party developers and folks on the Chrome and Android teams; nevertheless, I hope there are some helpful nuggets here.</p>
<p>Because most Web developer tooling is for mobile browsers, not WebView per se, the best approach is to optimize your Web site running in Android Browser or Chrome for Android. Most improvements you make should carry over to WebView  as it’s also WebKit-based.<b><b><br />
</b></b></p>
<h3>Debugging</h3>
<ul>
<li><a href="https://developers.google.com/chrome-developer-tools/docs/remote-debugging">Chrome Remote Debugging</a> with Chrome for Android</li>
<li><a href="http://people.apache.org/~pmuellr/weinre/docs/latest/">WEb INspector REmote</a> targeting the Android Browser. This 3rd party tool works with WebKit-based browsers and has an elements panel and console similar to Chrome Developer Tools.</li>
</ul>
<h3>Profiling</h3>
<p><a title="Chrome Developer Tools overview" href="https://developers.google.com/chrome-developer-tools/docs/overview">Chrome Developer Tools</a> has several features which can be used to measure performance.<b><b><br />
</b></b></p>
<ul>
<li>Inspect Element -&gt; Timelines -&gt; Frames -&gt; Record to see how long it takes to construct and render a frame. To avoid dropping frames, keep your render time under 30ms.</li>
<li>Hit h on any DOM element to hide it so doesn’t contribute to layout</li>
<li>Uncheck any CSS styles to see which ones are expensive (like border-radius + box-shadow)</li>
<li>Use timeline to see where slowness is occurring; i.e., don’t jump straight into JS performance as it might be paint time, layout, etc.</li>
<li>Inspect Element -&gt; Settings (bottom right corner)
<ul>
<li>Can show frame rates and memory usage in real time. Good for seeing how much certain effects cost on your rendering and how the cost of multiple effects multiplies up.</li>
<li><strong>Show FPS</strong> frames per second on pages with animation / loops</li>
<li><strong>Show paint rectangles</strong> to see if you are doing large repaints when not necessary</li>
<li><strong>Continuous page refresh</strong> setting available to help profile (available in Chrome dev channel)</li>
</ul>
</li>
<li><a href="http://dev.chromium.org/developers/how-tos/trace-event-profiling-tool">http://dev.chromium.org/developers/how-tos/trace-event-profiling-tool</a></li>
<li>Web Tracing Framework
<ul>
<li><a href="https://docs.google.com/presentation/d/1PvmvZt0U58uf1IipIQPSzXRIqjkIajch1RtxvEZjHvI/edit">slide deck</a></li>
<li><a href="https://github.com/google/tracing-framework">https://github.com/google/tracing-framework</a></li>
<li>works on WebView</li>
</ul>
</li>
<li><a href="https://developers.google.com/speed/pagespeed/insights">PageSpeed Insights</a> extension</li>
<li><a href="http://jankfree.com/">http://jankfree.com</a></li>
</ul>
<h3>Performance tips</h3>
<ul>
<li>Be careful about requesting info from the DOM like element heights. Bundle these up as much as possible as they require separate calculation.</li>
<li><a href="http://kellegous.com/j/2013/01/26/layout-performance/">http://kellegous.com/j/2013/01/26/layout-performance/</a></li>
<li><a href="https://gist.github.com/desandro/4657744">https://gist.github.com/desandro/4657744</a></li>
<li><a href="https://developers.google.com/events/io/sessions#chrome">Google I/O 2012 Chome Sessions</a>
<ul>
<li><a href="https://developers.google.com/events/io/sessions/gooio2012/209/">Building Performant Web Apps</a></li>
<li><a href="https://developers.google.com/events/io/sessions/gooio2012/207/">Building High Performance Mobile Web Apps </a></li>
</ul>
</li>
<li><a title="Web Performance Best Practices (Google Developers)" href="https://developers.google.com/speed/docs/best-practices/rules_intro">https://developers.google.com/speed/docs/best-practices/rules_intro</a></li>
<li><a title="HTML5 Techniques for Optimizing Mobile Performance" href="http://www.html5rocks.com/en/mobile/optimization-and-performance/">http://www.html5rocks.com/en/mobile/optimization-and-performance/</a></li>
<li><a href="http://www.html5rocks.com/en/tutorials/speed/scrolling/">http://www.html5rocks.com/en/tutorials/speed/scrolling/</a></li>
</ul>
<h3>Android</h3>
<ul>
<li><a href="http://www.chromium.org/developers/how-tos/trace-event-profiling-tool/recording-tracing-runs">Chrome for Android Tracing</a> explains the mobile tracing tool and how to understand the tracing results. See how much time spent in V8 to represent JS processing, layouts, etc.
<ul>
<li>Avoid unnecessary image resizing. Serving multiple devices is a challenge.
<ul>
<li>Image decode, resize ~50 ms each on Galaxy Nexus</li>
</ul>
</li>
<li>Chrome for Android &amp; Android Browser with fixed viewport doesn’t need fastclick</li>
<li>Swiping between pages: ViewPager + multiple WebViews faster than CSS</li>
</ul>
</li>
<li>Hardware accelerated transitions
<ul>
<li>Many devs report issues with views flickering when HW acceleration turned on.  Workaround is to have ‘next’ view peek in 1px so that it renders earlier so doesn’t flash</li>
<li>Many devs report inconsistent glitches with acceleration turned on (especially when using JS scrolling vs. native)</li>
</ul>
</li>
<li>ViewPager
<ul>
<li>Issues with bezel swipe… HW accelerated swipe doesn’t complete (bounces) because first WebView has to render before the next View will handle the swipe event (because the JS is busy doing something else). Only an issue when swiping from the edge.</li>
</ul>
</li>
<li>WebView JS interface
<ul>
<li>Single thread used for all JS can be a bottleneck.; e.g., have three WebViews in a ViewPager, each trying to do DOM manipulation. This can cause slowdowns.</li>
</ul>
</li>
<li><a href="http://www.youtube.com/watch?v=HbOtn5VhGZU">WebView performance talk from I/O ‘12</a></li>
<li><a title="Best Practices for Web Apps" href="http://developer.android.com/guide/webapps/best-practices.html">Best Practices for Web Apps</a> from developer.android.com (see especially links at end)</li>
<li>Hybrid approach popular: download content outside the WebView and inject it in, mainly for offline support</li>
</ul>
<p>If you know of any additional resources that have been helpful to you, please feel free to add them in the comments.</p>
<div id="jp-post-flair" class="sharedaddy sd-like-enabled sd-sharing-enabled"><div class="sharedaddy sd-sharing-enabled"><div class="robots-nocontent sd-block sd-social sd-social-icon-text sd-sharing"><h3 class="sd-title">Share this:</h3><div class="sd-content"><ul><li class="share-google-plus-1"><a rel="nofollow" class="share-google-plus-1 sd-button share-icon" href="http://turbomanage.wordpress.com/2013/02/28/optimizing-your-mobile-web-app/?share=google-plus-1" title="Click to share on Google+" id="sharing-google-4283"><span>Google</span></a></li><li class="share-twitter"><a rel="nofollow" class="share-twitter sd-button share-icon" href="http://turbomanage.wordpress.com/2013/02/28/optimizing-your-mobile-web-app/?share=twitter" title="Click to share on Twitter" id="sharing-twitter-4283"><span>Twitter</span></a></li><li class="share-linkedin"><a rel="nofollow" class="share-linkedin sd-button share-icon" href="http://turbomanage.wordpress.com/2013/02/28/optimizing-your-mobile-web-app/?share=linkedin" title="Click to share on LinkedIn" id="sharing-linkedin-4283"><span>LinkedIn</span></a></li><li class="share-email"><a rel="nofollow" class="share-email sd-button share-icon" href="http://turbomanage.wordpress.com/2013/02/28/optimizing-your-mobile-web-app/?share=email" title="Click to email this to a friend"><span>Email</span></a></li><li class="share-end"></li></ul></div></div></div><div class='sharedaddy sd-block sd-like jetpack-likes-widget-wrapper jetpack-likes-widget-unloaded' id='like-post-wrapper-341230-4283-53545f641faeb' data-src='//widgets.wp.com/likes/#blog_id=341230&amp;post_id=4283&amp;origin=turbomanage.wordpress.com&amp;obj_id=341230-4283-53545f641faeb' data-name='like-post-frame-341230-4283-53545f641faeb'><h3 class='sd-title'>Like this:</h3><div class='likes-widget-placeholder post-likes-widget-placeholder' style='height:55px'><span class='button'><span>Like</span></span> <span class="loading">Loading...</span></div><span class='sd-text-color'></span><a class='sd-link-color'></a></div></div> </div>

<p class="category">Posted in <a href="http://turbomanage.wordpress.com/category/android/" title="View all posts in Android" rel="category tag">Android</a> | <a href="http://turbomanage.wordpress.com/2013/02/28/optimizing-your-mobile-web-app/#comments" title="Comment on Optimizing your mobile Web&nbsp;app">1 Comment &#187;</a></p>
</div>


<div class="post-4268 post type-post status-publish format-standard hentry category-android tag-android-2" id="post-4268">
<h2><a href="http://turbomanage.wordpress.com/2013/02/20/testing-multi-user-support-in-the-android-emulator/" rel="bookmark">Enable multi-user support in the Android&nbsp;emulator</a></h2>
<p class="date">Posted by <a href="http://turbomanage.wordpress.com/">David Chandler</a> on February 20, 2013</p>
<div class="entry">
<p>Android 4.2 (API 17) offers <a title="Multiple user support in Android 4.2" href="http://developer.android.com/about/versions/android-4.2.html#MultipleUsers">multi-user support</a> for tablet sharing. To add a user on a physical tablet running 4.2 or later, go to Settings | Users. You can also do this in an emulator, but you first have to set an emulator property as follows:</p>
<ol>
<li>Create a new AVD based on a tablet (say, the Nexus 7 device definition in the latest ADT) with API 17.</li>
<li>Start the emulator</li>
<li>Run these adb commands:</li>
</ol>
<p>&gt; adb shell setprop fw.max_users 4<br />
&gt; adb shell stop<br />
&gt; adb shell start</p>
<div>You can now add a user in the emulator. To see the new user bubble on the lock screen, press F7. You may have to press it twice for the slide lock to become active.</div>
<div id="jp-post-flair" class="sharedaddy sd-like-enabled sd-sharing-enabled"><div class="sharedaddy sd-sharing-enabled"><div class="robots-nocontent sd-block sd-social sd-social-icon-text sd-sharing"><h3 class="sd-title">Share this:</h3><div class="sd-content"><ul><li class="share-google-plus-1"><a rel="nofollow" class="share-google-plus-1 sd-button share-icon" href="http://turbomanage.wordpress.com/2013/02/20/testing-multi-user-support-in-the-android-emulator/?share=google-plus-1" title="Click to share on Google+" id="sharing-google-4268"><span>Google</span></a></li><li class="share-twitter"><a rel="nofollow" class="share-twitter sd-button share-icon" href="http://turbomanage.wordpress.com/2013/02/20/testing-multi-user-support-in-the-android-emulator/?share=twitter" title="Click to share on Twitter" id="sharing-twitter-4268"><span>Twitter</span></a></li><li class="share-linkedin"><a rel="nofollow" class="share-linkedin sd-button share-icon" href="http://turbomanage.wordpress.com/2013/02/20/testing-multi-user-support-in-the-android-emulator/?share=linkedin" title="Click to share on LinkedIn" id="sharing-linkedin-4268"><span>LinkedIn</span></a></li><li class="share-email"><a rel="nofollow" class="share-email sd-button share-icon" href="http://turbomanage.wordpress.com/2013/02/20/testing-multi-user-support-in-the-android-emulator/?share=email" title="Click to email this to a friend"><span>Email</span></a></li><li class="share-end"></li></ul></div></div></div><div class='sharedaddy sd-block sd-like jetpack-likes-widget-wrapper jetpack-likes-widget-unloaded' id='like-post-wrapper-341230-4268-53545f6423155' data-src='//widgets.wp.com/likes/#blog_id=341230&amp;post_id=4268&amp;origin=turbomanage.wordpress.com&amp;obj_id=341230-4268-53545f6423155' data-name='like-post-frame-341230-4268-53545f6423155'><h3 class='sd-title'>Like this:</h3><div class='likes-widget-placeholder post-likes-widget-placeholder' style='height:55px'><span class='button'><span>Like</span></span> <span class="loading">Loading...</span></div><span class='sd-text-color'></span><a class='sd-link-color'></a></div></div> </div>

<p class="category">Posted in <a href="http://turbomanage.wordpress.com/category/android/" title="View all posts in Android" rel="category tag">Android</a> | Tagged: <a href="http://turbomanage.wordpress.com/tag/android-2/" rel="tag">android</a> | <a href="http://turbomanage.wordpress.com/2013/02/20/testing-multi-user-support-in-the-android-emulator/#comments" title="Comment on Enable multi-user support in the Android&nbsp;emulator">1 Comment &#187;</a></p>
</div>


<div class="post-4261 post type-post status-publish format-standard hentry category-android" id="post-4261">
<h2><a href="http://turbomanage.wordpress.com/2012/12/18/storm-implementation-notes/" rel="bookmark">stORM implementation notes</a></h2>
<p class="date">Posted by <a href="http://turbomanage.wordpress.com/">David Chandler</a> on December 18, 2012</p>
<div class="entry">
<p>In my <a title="stORM: a lightweight DAO generator for Android SQLite" href="http://turbomanage.wordpress.com/2012/12/11/storm-preview/">previous post</a>, I omitted discussion of how <a title="stORM project hosting" href="http://storm-gen.googlecode.com">stORM</a> works in the interest of getting the word out about my new DAO tool for Android. In the current post, we&#8217;ll take a deeper look at the code generated by the stORM annotation processor.</p>
<h3>DbFactory class</h3>
<p>Let&#8217;s start with the root of all runtime activity, the DbFactory class. This class is generated from your DatabaseHelper class annotated with @Database. As you can see in the TestDbFactory class below, the database name and version from the annotation are hard-coded as static fields. Also, there is an array of TableHelpers associated with this DatabaseFactory, one for each class annotated with @Entity. It is possible for your app to include multiple databases, in which case each @Entity must include the dbName attribute to indicate in which database to store the entity.</p>
<pre class="brush: java; title: ; notranslate" title="">
public class TestDbFactory implements DatabaseFactory {

private static final String DB_NAME = &quot;testDb&quot;;
private static final int DB_VERSION = 2;
private static final TableHelper[] TABLE_HELPERS = new TableHelper[] {
new com.turbomanage.storm.entity.dao.SimpleEntityTable()
};
private static DatabaseHelper mInstance;

/**
* Provides a singleton instance of the DatabaseHelper per application
* to prevent threading issues. See
* https://github.com/commonsguy/cwac-loaderex#notes-on-threading
*
* @param ctx Application context
* @return {@link SQLiteOpenHelper} instance
*/
public static DatabaseHelper getDatabaseHelper(Context ctx) {
if (mInstance==null) {
// in case this is called from an AsyncTask or other thread
synchronized(TestDbFactory.class) {
if (mInstance == null)
mInstance = new com.turbomanage.storm.TestDatabaseHelper(
ctx.getApplicationContext(),
new TestDbFactory());
}
}
return mInstance;
}
...
}
</pre>
<p>The purpose of the generated DbFactory class is to provide a singleton instance of your DatabaseHelper class. The DatabaseHelper base class in stORM extends <a title="SQLiteOpenHelper Reference" href="http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html">SQLiteOpenHelper</a>, which is used to connect to a SQLite database. A singleton instance of SQLiteOpenHelper is important because it allows SQLiteDatabase to synchronize all operations. In the DbFactory class, getDatabaseHelper() is implemented using the double-lock pattern to efficiently prevent a race condition. For more information on the use of singletons vs. Application, see <a title="Singletons vs. application context in Android" href="http://stackoverflow.com/questions/3826905/singletons-vs-application-context-in-android">this post</a>.</p>
<p>The DbFactory class allows you to connect to the database from anywhere in your app like this:</p>
<pre class="brush: java; title: ; notranslate" title="">
TestDbFactory.getDatabaseHelper(ctx).getWritableDatabase();
</pre>
<p>The Context supplied as an argument to getDatabaseHelper() ultimately gets passed through to SQLiteOpenHelper.</p>
<h3>Entity DAO</h3>
<p>The annotation processor generates a DAO class for each @Entity. Here&#8217;s an example:</p>
<pre class="brush: java; title: ; notranslate" title="">
public class SimpleEntityDao extends SQLiteDao&lt;SimpleEntity&gt;{

public DatabaseHelper getDbHelper(Context ctx) {
return com.turbomanage.storm.TestDbFactory.getDatabaseHelper(ctx);
}

@SuppressWarnings(&quot;rawtypes&quot;)
public TableHelper getTableHelper() {
return new com.turbomanage.storm.entity.dao.SimpleEntityTable();
}

public SimpleEntityDao(Context ctx) {
super(ctx);
}

}
</pre>
<p>The entity DAO is a thin subclass of the SqliteDao base class and mainly exists to supply the generic type parameter for the entity type. The generated DAO also uses the template method design pattern to associate the entity with its DbFactory and TableHelper classes via the getDbHelper() and getTableHelper() methods. The super constructor maintains a reference to the provided Context in the superclass which is used to obtain the singelton SQLiteOpenHelper instance whenever it is needed.</p>
<h3>SQLiteDao&lt;T&gt;</h3>
<p>The DAO base class contains the meat of the runtime. This is where you&#8217;ll find the insert, update, delete, and query methods. The value of using a generified DAO is that all the moderately complex code which actually touches the database is centralized in one place vs. generated for each entity. Thanks to syntax highlighting and other IDE support, it&#8217;s much easier to write and debug Java code than a generator template containing Java code.</p>
<p>Note that the base DAO obtains the readable or writable database from the singleton DatabaseHelper in each DAO method. It is not necessary to cache the database obtained from SQLiteOpenHelper.getWritableDatabase() because we&#8217;re using a singleton instance of SQLiteOpenHelper (the DatabaseHelper superclass), which itself caches the database connection. This approach keeps the DAO stateless, which reduces memory usage and prevents stateful bugs.</p>
<h3>TableHelper&lt;T&gt;</h3>
<p>The TableHelper class generated for each entity contains only code specific to the entity, including column definitions, Cursor bindings, and all SQL. This keeps the generated code as small as possible. There are a couple of features worth pointing out.</p>
<p>First, column definitions are stored as an enum. Here&#8217;s an example from the SimpleEntityTable class generated for <a title="SimpleEntity source" href="https://code.google.com/p/storm-gen/source/browse/test/src/com/turbomanage/storm/entity/SimpleEntity.java">SimpleEntity</a> in the unit tests:</p>
<pre class="brush: java; title: ; notranslate" title="">
public enum Columns implements TableHelper.Column {
BLOBFIELD,
BOOLEANFIELD,
BYTEFIELD,
CHARFIELD,
DOUBLEFIELD,
ENUMFIELD,
FLOATFIELD,
_ID,
...
}
</pre>
<p>The Columns enum permits typesafe column references throughout your app and also maintains column order for methods that need to iterate over all the columns. Because the enum implements the marker interface TableHelper.Column, you can refer to the actual enum values in the various methods of <a title="FilterBuilder source" href="https://code.google.com/p/storm-gen/source/browse/api/src/com/turbomanage/storm/query/FilterBuilder.java">FilterBuilder</a> or create your own methods that take a column name as an argument. By convention, the column names are just the name of the underlying entity field in all caps. At some point, I may add an @Column annotation for entity fields to allow column renaming.</p>
<p>Now let&#8217;s look at the binding methods of the TableHelper class. The first, newInstance(Cursor c), creates a new entity instance from a Cursor. It is used to convert one row to its object representation. Here&#8217;s an example, again from the generated SimpleEntityTable in the unit tests:</p>
<pre class="brush: java; title: ; notranslate" title="">
@Override
public SimpleEntity newInstance(Cursor c) {
SimpleEntity obj = new SimpleEntity();
obj.setBlobField(c.getBlob(0));
obj.setBooleanField(c.getInt(1) == 1 ? true : false);
obj.setByteField((byte) c.getShort(2));
obj.setCharField((char) c.getInt(3));
obj.setDoubleField(c.getDouble(4));
obj.setEnumField(c.isNull(5) ? null : com.turbomanage.storm.entity.SimpleEntity.EnumType.valueOf(c.getString(5)));
obj.setFloatField(c.getFloat(6));
obj.setId(c.getLong(7));
obj.setIntField(c.getInt(8));
obj.setLongField(c.getLong(9));
obj.setPrivateField(c.getInt(10));
obj.setShortField(c.getShort(11));
obj.setwBooleanField(BooleanConverter.GET.fromSql(getIntOrNull(c, 12)));
obj.setwByteField(ByteConverter.GET.fromSql(getShortOrNull(c, 13)));
obj.setwCharacterField(CharConverter.GET.fromSql(getIntOrNull(c, 14)));
obj.setwDateField(DateConverter.GET.fromSql(getLongOrNull(c, 15)));
obj.setwDoubleField(DoubleConverter.GET.fromSql(getDoubleOrNull(c, 16)));
obj.setwFloatField(FloatConverter.GET.fromSql(getFloatOrNull(c, 17)));
obj.setwIntegerField(IntegerConverter.GET.fromSql(getIntOrNull(c, 18)));
obj.setwLatitudeField(LatitudeConverter.GET.fromSql(getDoubleOrNull(c, 19)));
obj.setwLongField(LongConverter.GET.fromSql(getLongOrNull(c, 20)));
obj.setwShortField(ShortConverter.GET.fromSql(getShortOrNull(c, 21)));
obj.setwStringField(c.getString(22));
return obj;
}
</pre>
<p>Fields of primitive type are mapped directly via the corresponding primitive getter methods on the Cursor, which preserves the efficiency of using primitive types in your entities. Wrapper types like Double call the primitive getter on the Cursor but then get converted to a Double instance via the getDoubleOrNull() method in the TableHelper base class. This introduces object creation overhead; however, that overhead is implied by the entity&#8217;s use of the wrapper type in the first place. Either it happens explicitly in the getDoubleOrNull() method, or would happen implicitly via auto-boxing if you were to assign the result of Cursor.getDouble() to an entity field of type Double. Of course, the newInstance() method must create an instance of the entity itself; however, this is inherent in the idea of object relational mapping. If you are pursuing absolute maximum efficiency using SQLite without any object representations, stORM is not for you. Admittedly, there are some cases, especially queries, where you are only interested in one or two fields of an object and it would just be waste to populate all fields as above. In these cases, you can simply bypass stORM. It&#8217;s perfectly safe to obtain the readable or writable database using the DatabaseFactory class as shown in the first section of this article.</p>
<p>The corollary method to newInstance() is getEditableValues(T obj), which returns a <a title="ContentValues reference" href="http://developer.android.com/reference/android/content/ContentValues.html">ContentValues</a> map populated with the object&#8217;s fields. The ContentValues object can then be passed to the insert() or update() methods on a writable SQLiteDatabase. Here&#8217;s the generated getEditableValues() method in SimpleEntityTable:</p>
<pre class="brush: java; title: ; notranslate" title="">
@Override
public ContentValues getEditableValues(SimpleEntity obj) {
ContentValues cv = new ContentValues();
cv.put(&quot;BLOBFIELD&quot;, obj.getBlobField());
cv.put(&quot;BOOLEANFIELD&quot;, obj.isBooleanField() ? 1 : 0);
cv.put(&quot;BYTEFIELD&quot;, (short) obj.getByteField());
cv.put(&quot;CHARFIELD&quot;, (int) obj.getCharField());
cv.put(&quot;DOUBLEFIELD&quot;, obj.getDoubleField());
cv.put(&quot;ENUMFIELD&quot;, obj.getEnumField() == null ? null : obj.getEnumField().name());
cv.put(&quot;FLOATFIELD&quot;, obj.getFloatField());
cv.put(&quot;_ID&quot;, obj.getId());
cv.put(&quot;INTFIELD&quot;, obj.getIntField());
cv.put(&quot;LONGFIELD&quot;, obj.getLongField());
cv.put(&quot;PRIVATEFIELD&quot;, obj.getPrivateField());
cv.put(&quot;SHORTFIELD&quot;, obj.getShortField());
cv.put(&quot;WBOOLEANFIELD&quot;, BooleanConverter.GET.toSql(obj.getwBooleanField()));
cv.put(&quot;WBYTEFIELD&quot;, ByteConverter.GET.toSql(obj.getwByteField()));
cv.put(&quot;WCHARACTERFIELD&quot;, CharConverter.GET.toSql(obj.getwCharacterField()));
cv.put(&quot;WDATEFIELD&quot;, DateConverter.GET.toSql(obj.getwDateField()));
cv.put(&quot;WDOUBLEFIELD&quot;, DoubleConverter.GET.toSql(obj.getwDoubleField()));
cv.put(&quot;WFLOATFIELD&quot;, FloatConverter.GET.toSql(obj.getwFloatField()));
cv.put(&quot;WINTEGERFIELD&quot;, IntegerConverter.GET.toSql(obj.getwIntegerField()));
cv.put(&quot;WLATITUDEFIELD&quot;, LatitudeConverter.GET.toSql(obj.getwLatitudeField()));
cv.put(&quot;WLONGFIELD&quot;, LongConverter.GET.toSql(obj.getwLongField()));
cv.put(&quot;WSHORTFIELD&quot;, ShortConverter.GET.toSql(obj.getwShortField()));
cv.put(&quot;WSTRINGFIELD&quot;, obj.getwStringField());
return cv;
}
</pre>
<p>As in the newInstance() method, primitive types are bound with no conversion overhead by calling the variant of the overloaded put() method which takes the primitive type. In the case of wrapper types like Double, the object returned by the TypeConverter&#8217;s toSql() method will be implicitly unwrapped. I suspect that using ContentValues in an insert or update may be slightly less efficient than using a prepared statement because that&#8217;s no doubt what&#8217;s happening under the covers. If this becomes an issue, it would be easy enough to modify stORM to generate the code and bindings for a prepared statement instead.</p>
<h3>FilterBuilder</h3>
<p>The FilterBuilder object returned by the DAO&#8217;s filter() method is currently an elementary query builder that can AND together a series of equality conditions. The query-by-example methods in the base DAO class use FilterBuilder. I&#8217;m not completely happy with this API yet, particularly the overloaded eq() methods for every wrapper type. However, it seems necessary to minimize lookup overhead for the appropriate TypeConverter, as well as to handle special cases like Double and byte[], which require special treatment for equality conditions. Consider this a work in progress.</p>
<h3>TypeConverter&lt;J,S&gt;</h3>
<p>The TypeConverter interface is at the heart of all the code to convert fields to/from SQL. SQLite has only four native data types. In stORM, these are represented by the TypeConverter.SqlType enum: INTEGER, REAL, BLOB, and TEXT. Closely related to these are the seven primitive types available in the Cursor.getX() and ContentValues.put() methods. These are represented by the TypeConverter.BindType enum: BLOB, DOUBLE, FLOAT, INT, LONG, SHORT, and STRING. The TypeConverter interface and its corresponding @Converter annotation offer an extensible mechanism by which you can convert any Java type, including your own classes, to a SQL representation. Simply extend TypeConverter and annotate it with @Converter. As an example, here&#8217;s the built-in BooleanConverter:</p>
<pre class="brush: java; title: ; notranslate" title="">
package com.turbomanage.storm.types;

import com.turbomanage.storm.api.Converter;
import com.turbomanage.storm.types.TypeConverter.BindType;
import com.turbomanage.storm.types.TypeConverter.SqlType;

@Converter(forTypes = { boolean.class, Boolean.class }, bindType = BindType.INT, sqlType = SqlType.INTEGER)
public class BooleanConverter extends TypeConverter&lt;Boolean,Integer&gt; {

public static final BooleanConverter GET = new BooleanConverter();

@Override
public Integer toSql(Boolean javaValue) {
if (javaValue == null)
return null;
return (javaValue==Boolean.TRUE) ? 1 : 0;
}

@Override
public Boolean fromSql(Integer sqlValue) {
if (sqlValue == null)
return null;
return sqlValue==0 ? Boolean.FALSE : Boolean.TRUE;
}

@Override
public Integer fromString(String strValue) {
if (strValue == null)
return null;
return Integer.valueOf(strValue);
}

}
</pre>
<p>A TypeConverter must implement methods to convert to and from the type&#8217;s SQL representation, or more precisely, the Java type which is directly bindable via one of the Cursor.getX() methods. In addition, it must be able to convert to/from a String. The String methods are used only by the CSV utilities. All TypeConverter return types and argument types must be object types, not primitives. In the case of queries using FilterBuilder, this may result in one unnecessary object creation per field when primitives are implicitly wrapped; however, the use of object types throughout allows the TypeConverter interface to be parameterized, which, besides its elegance, makes it easier to create custom TypeConverters from scratch.</p>
<p>Note that the @Converter annotation must specify the Java types for which the converter may be used (forTypes) as well as the corresponding SQL representation type and binding type. The latter is used by the annotation processor to generate the name of the corresponding Cursor.getX() method. Fortunately, byte[] is an Object type so byte arrays can be handled the same way as all the wrapper types. Also fortunately, primitive types do have an associated class even though they are not Objects, so boolean.class is valid in the annotation&#8217;s forTypes attribute. In addition to implementing the TypeConverter interface and providing the @Converter annotation, a custom TypeConverter must declare a static field named GET which returns a singleton instance of the converter. This is used by the generated TableHelper methods to minimize the number of converter instances overall.</p>
<p>One final note about TypeConverter: the reason that SQL type and bind type are specified as annotation attributes vs. methods in TypeConverter is because the latter would require the annotation processor to obtain an instance of a TypeConverter in order to invoke a method like getBindType(). But the annotation processor can only inspect source code using the TypeMirror API, which doesn&#8217;t permit direct reference to Class objects such as would be needed to get a new instance via reflection. Thus, including these attributes in the @Converter annotation allows you to include custom TypeConverters in your application project directly vs. packaging them in a jar as would be required otherwise.</p>
<h3>Supporting incremental compilation</h3>
<p>One of the biggest surprises when writing the annotation processor  (see stORM&#8217;s <a title="MainProcessor source" href="https://code.google.com/p/storm-gen/source/browse/impl/src/com/turbomanage/storm/apt/MainProcessor.java">MainProcessor</a>) was the realization that incremental compilation support (in Eclipse, at least) exposes to the annotation processor only those source files which have been modified in the current save operation. Thus, if you add an @Entity annotation, the annotation processor can only inspect the code for that entity. This is problematic for stORM because it needs to know about all the annotated converters, databases, and entities in order to update the generated code for DbFactory and TableHelper classes with each save. In order to preserve state about previously-inspected annotations, it was necessary to utilize a file to maintain state. This file, called <code>stormEnv</code>, is in the same directory as the generated code (in Eclipse, <code>.apt_generated</code>). The annotation processor reads/writes to it before/after each run. If you experience problems with generated code or extraneous artifacts, just clean the project. This will wipe the <code>stormEnv</code> file and trigger a complete rebuild which processes all the annotations at once.</p>
<h3>Summary</h3>
<p>Now that you&#8217;re aware of some of stORM&#8217;s nuances and implementation details, do give it a whirl and let me know what you think. Comments are welcome here as well as on G+, where I&#8217;ve also shared this post. Also note that you can click on the title of this post in WordPress to link directly to it, which enables the Like and Share buttons for G+, Twitter, and LinkedIn. Enjoy!</p>
<div id="jp-post-flair" class="sharedaddy sd-like-enabled sd-sharing-enabled"><div class="sharedaddy sd-sharing-enabled"><div class="robots-nocontent sd-block sd-social sd-social-icon-text sd-sharing"><h3 class="sd-title">Share this:</h3><div class="sd-content"><ul><li class="share-google-plus-1"><a rel="nofollow" class="share-google-plus-1 sd-button share-icon" href="http://turbomanage.wordpress.com/2012/12/18/storm-implementation-notes/?share=google-plus-1" title="Click to share on Google+" id="sharing-google-4261"><span>Google</span></a></li><li class="share-twitter"><a rel="nofollow" class="share-twitter sd-button share-icon" href="http://turbomanage.wordpress.com/2012/12/18/storm-implementation-notes/?share=twitter" title="Click to share on Twitter" id="sharing-twitter-4261"><span>Twitter</span></a></li><li class="share-linkedin"><a rel="nofollow" class="share-linkedin sd-button share-icon" href="http://turbomanage.wordpress.com/2012/12/18/storm-implementation-notes/?share=linkedin" title="Click to share on LinkedIn" id="sharing-linkedin-4261"><span>LinkedIn</span></a></li><li class="share-email"><a rel="nofollow" class="share-email sd-button share-icon" href="http://turbomanage.wordpress.com/2012/12/18/storm-implementation-notes/?share=email" title="Click to email this to a friend"><span>Email</span></a></li><li class="share-end"></li></ul></div></div></div><div class='sharedaddy sd-block sd-like jetpack-likes-widget-wrapper jetpack-likes-widget-unloaded' id='like-post-wrapper-341230-4261-53545f642d7e1' data-src='//widgets.wp.com/likes/#blog_id=341230&amp;post_id=4261&amp;origin=turbomanage.wordpress.com&amp;obj_id=341230-4261-53545f642d7e1' data-name='like-post-frame-341230-4261-53545f642d7e1'><h3 class='sd-title'>Like this:</h3><div class='likes-widget-placeholder post-likes-widget-placeholder' style='height:55px'><span class='button'><span>Like</span></span> <span class="loading">Loading...</span></div><span class='sd-text-color'></span><a class='sd-link-color'></a></div></div> </div>

<p class="category">Posted in <a href="http://turbomanage.wordpress.com/category/android/" title="View all posts in Android" rel="category tag">Android</a> | <a href="http://turbomanage.wordpress.com/2012/12/18/storm-implementation-notes/#comments" title="Comment on stORM implementation notes">1 Comment &#187;</a></p>
</div>


<div class="post-4252 post type-post status-publish format-standard hentry category-android" id="post-4252">
<h2><a href="http://turbomanage.wordpress.com/2012/12/11/storm-preview/" rel="bookmark">stORM: a lightweight DAO generator for Android&nbsp;SQLite</a></h2>
<p class="date">Posted by <a href="http://turbomanage.wordpress.com/">David Chandler</a> on December 11, 2012</p>
<div class="entry">
<p>In my <a title="Getting Started with Java APT for Android in Eclipse" href="http://turbomanage.wordpress.com/2012/09/28/annotation-processor-android-eclipse/">previous blog post</a> on writing a Java annotation processor, I mentioned that I’d been working on a lightweight ORM for Android SQLite. I’m finally ready to offer a preview of project stORM with this caveat: at this stage, it’s more of a DAO (data access object) code generator than a full-fledged ORM. The generated DAO classes offer a simple interface to save and retrieve objects to SQLite without having to write any SQL code; however, stORM doesn’t attempt to offer features of full-fledged ORMs such as lazy loading of an object graph.</p>
<h3>Goals</h3>
<p>stORM is intended as an easy way to create a basic object store with SQLite. It lets you insert, update, delete, and query objects without having to write any SQL. It can persist fields of any primitive or wrapper type, including enums. in addition, for more complex types, you can provide your own custom TypeConverter with @Converter. stORM does not yet support modeling of relations. Support for foreign keys and indexes is planned.</p>
<p>stORM is implemented as a Java annotation processor so it can be used with or without an IDE. To use it, you simply annotate POJO (plain old Java object) classes that you want to persist in a SQLite database and stORM automatically generates a SQLiteOpenHelper class and DAO classes for you.</p>
<h3>Example</h3>
<p>Here&#8217;s an example of stORM usage. The Person class is a POJO with a few String and primitive types (firstName, lastName, weight, and height). Note that it includes an id field of type long. An ID field of type long is required along with corresponding accessor methods. To name the ID field differently, place the @Id annotation on any field of type long.</p>
<pre class="brush: java; title: ; notranslate" title="">
package com.turbomanage.demo.entity;

import com.turbomanage.storm.api.Entity;

@Entity
public class Person {

private long id;
private String firstName, lastName;
private int weight;
private double height;

// accessor methods omitted
}
</pre>
<p>The Person class is annotated with @Entity, which enables the annotation processor to generate a corresponding DAO class. To insert a new Person in the database, we simply use the generated DAO class. The DAO package name is derived by appending &#8220;.dao&#8221; to your entity package, and the DAO classname is the entity classname + &#8220;Dao&#8221;.</p>
<pre class="brush: java; title: ; notranslate" title="">
import com.turbomanage.demo.entity.dao.PersonDao;
...
PersonDao dao = new PersonDao(getContext());
Person newPerson = new Person();
newPerson.setFirstName(&quot;David&quot;);
newPerson.setLastName(&quot;Chandler&quot;);
// insert
long newPersonId = dao.insert(newPerson);
// query by example
Person examplePerson = new Person();
examplePerson.setFirstName(&quot;David&quot;);
List allDavids = dao.listByExample(examplePerson);
</pre>
<p>The insert() method returns the id of the newly inserted row. In addition, the id field of the Person object is populated with this value.</p>
<p>The DAO&#8217;s listByExample() method is the simplest way to do a query. It lets you retrieve all rows matching the fields of an example object with non-default values. There is also a filter() method which lets you specify conditions directly.</p>
<h3>Setup</h3>
<p>To get started with stORM:</p>
<ol>
<li>Download the storm-api and storm-apt jars from <a title="storm-gen project hosting" href="http://storm-gen.googlecode.com">storm-gen.googlecode.com</a>.</li>
<li>Add storm-api.jar to your project&#8217;s libs/ folder. This contains the stORM annotations and runtime.</li>
<li>Add storm-apt.jar to your project&#8217;s annotation factory class path. In Eclipse, you can find this under project properties | Java Compiler | Annotation Processing | Factory Path. The apt jar contains the annotation processor which inspects your project&#8217;s source code and generates the required database classes.</li>
</ol>
<div id="attachment_4253" style="width: 1034px" class="wp-caption alignnone"><img class="size-large wp-image-4253" alt="Eclipse properties dialog" src="http://turbomanage.files.wordpress.com/2012/12/storm-apt.png?w=1024&#038;h=862" width="1024" height="862" /><p class="wp-caption-text">Add storm-apt.jar to your project&#8217;s annotation factory classpath in Eclipse.</p></div>
<h3>Usage</h3>
<p>Once you’ve added the jars to your project, it’s easy to use stORM. You just need a DatabaseHelper class annotated with @Database and one or more POJO classes annotated with @Entity.</p>
<ol>
<li>Create a new class that extends DatabaseHelper.</li>
<li>Add @Database and supply a database name and version.</li>
<li>Override getUpgradeStrategy() to choose one of the available strategies (DROP_CREATE for new projects).</li>
<li>Create a POJO class you want to persist to the database.</li>
<li>Make sure it has a field of type long named &#8220;id&#8221; or annotated with @Id.</li>
<li>Add the @Entity annotation to the class.</li>
</ol>
<p>Following these steps, you&#8217;ll see 3 generated classes under .apt_generated (you might need to unfilter hidden resources in the Eclipse Project Explorer to see it):</p>
<ul>
<li>a DbFactory class. This provides a singleton instance of your DatabaseHelper class (which in turn extends SQLiteOpenHelper). Having only one instance of SQLiteOpenHelper ensures that SQLite’s own threadsafe mechanisms work as intended.</li>
<li>a DAO class for each entity. This extends SQLiteDao and mainly supplies the necessary type parameter to the base class.</li>
<li>a Table class for each entity. This contains the generated code to convert an entity instance to and from a SQL row.</li>
</ul>
<p>You can use the DAO simply by creating a new instance. You must pass it the application context as this is required by the underlying SQLiteOpenHelper.</p>
<pre class="brush: java; title: ; notranslate" title="">
PersonDao dao = new PersonDao(getContext());
</pre>
<p>The DAO constructor obtains the singleton instance of your DatabaseHelper class from the generated factory class. Because of this, it’s safe to create new DAO instances anywhere you need them. You can now invoke any of the public methods on the DAO, such as insert(), update(), and listAll(). There are also listByExample() and getByExample() methods that build a query from an example object.</p>
<p>You can also use the FilterBuilder API (still under contruction) to run a query like this:</p>
<pre class="brush: java; title: ; notranslate" title="">
List&lt;Person&gt; allDavids = dao.filter().eq(Columns.FIRSTNAME, &quot;David&quot;).list();
</pre>
<h3>Version upgrades</h3>
<p>One of the more challenging aspects of using SQLite can be upgrading your app’s database schema with new versions. stORM makes this easier by providing several upgrade strategies which you can choose in your DatabaseHelper class:<b><b><br />
</b></b></p>
<ul>
<li>DROP_CREATE will drop all tables and create the database again using the new schema implied by your new entity definitions. This will destroy all data in the database, so should only be used in testing, not after your app is in production.</li>
<li>BACKUP_RESTORE will first backup the database to CSV files, then drop and recreate the database, and finally restore all the data from the backup CSV files. Any new fields (columns) in your entities will receive default values, and dropped columns will simply disappear. It is not yet possible to rename a field or change the field type.</li>
<li>UPGRADE lets you implement your own strategy (probably an ALTER TABLE statement) by overriding the onUpgrade() method in the DatabaseHelper class.</li>
</ul>
<h3>Dump to CSV</h3>
<p>Finally, stORM can automatically backup your database to CSV files and restore it from the same. This is used by the BACKUP_RESTORE UpgradeStrategy. You can also manually dump to CSV or restore from CSV by calling methods directly on your DatabaseHelper class. Example:</p>
<pre class="brush: java; title: ; notranslate" title="">
DemoDbFactory.getDatabaseHelper(getContext()).backupAllTablesToCsv();
DemoDbFactory.getDatabaseHelper(getContext()).restoreAllTablesFromCsv();
</pre>
<p>You can browse the CSV files using adb shell under /data/data/your_app_package/files. Be aware that any byte[] fields are Base64 encoded, and fields of type double are encoded using their exact hex representation. See the code in BlobConverter.fromString() and DoubleConverter.fromString() if you want to read and parse these values independently of stORM.<b><b><br />
</b></b></p>
<h3>Coming soon: ContentProvider</h3>
<p>Another pain point for many developers is creating a ContentProvider. Stay tuned for a way to generate ContentProvider contract stubs and implementations also.</p>
<h3>Source code</h3>
<p><a title="storm-gen project hosting" href="http://storm-gen.googlecode.com">storm-gen.googlecode.com</a></p>
<p>The issue tracker is open&#8230;</p>
<p>Enjoy!</p>
<div id="jp-post-flair" class="sharedaddy sd-like-enabled sd-sharing-enabled"><div class="sharedaddy sd-sharing-enabled"><div class="robots-nocontent sd-block sd-social sd-social-icon-text sd-sharing"><h3 class="sd-title">Share this:</h3><div class="sd-content"><ul><li class="share-google-plus-1"><a rel="nofollow" class="share-google-plus-1 sd-button share-icon" href="http://turbomanage.wordpress.com/2012/12/11/storm-preview/?share=google-plus-1" title="Click to share on Google+" id="sharing-google-4252"><span>Google</span></a></li><li class="share-twitter"><a rel="nofollow" class="share-twitter sd-button share-icon" href="http://turbomanage.wordpress.com/2012/12/11/storm-preview/?share=twitter" title="Click to share on Twitter" id="sharing-twitter-4252"><span>Twitter</span></a></li><li class="share-linkedin"><a rel="nofollow" class="share-linkedin sd-button share-icon" href="http://turbomanage.wordpress.com/2012/12/11/storm-preview/?share=linkedin" title="Click to share on LinkedIn" id="sharing-linkedin-4252"><span>LinkedIn</span></a></li><li class="share-email"><a rel="nofollow" class="share-email sd-button share-icon" href="http://turbomanage.wordpress.com/2012/12/11/storm-preview/?share=email" title="Click to email this to a friend"><span>Email</span></a></li><li class="share-end"></li></ul></div></div></div><div class='sharedaddy sd-block sd-like jetpack-likes-widget-wrapper jetpack-likes-widget-unloaded' id='like-post-wrapper-341230-4252-53545f6435409' data-src='//widgets.wp.com/likes/#blog_id=341230&amp;post_id=4252&amp;origin=turbomanage.wordpress.com&amp;obj_id=341230-4252-53545f6435409' data-name='like-post-frame-341230-4252-53545f6435409'><h3 class='sd-title'>Like this:</h3><div class='likes-widget-placeholder post-likes-widget-placeholder' style='height:55px'><span class='button'><span>Like</span></span> <span class="loading">Loading...</span></div><span class='sd-text-color'></span><a class='sd-link-color'></a></div></div> </div>

<p class="category">Posted in <a href="http://turbomanage.wordpress.com/category/android/" title="View all posts in Android" rel="category tag">Android</a> | <a href="http://turbomanage.wordpress.com/2012/12/11/storm-preview/#comments" title="Comment on stORM: a lightweight DAO generator for Android&nbsp;SQLite">36 Comments &#187;</a></p>
</div>

<div class="bottomnavigation">
<div class="alignleft"><a href="http://turbomanage.wordpress.com/page/2/" >&laquo; Previous Entries</a></div>
<div class="alignright"></div>
</div>

</div>

</div>
<div class="clearingdiv">&nbsp;</div>
</div>

<div id="footer">
<a href="http://wordpress.com/?ref=footer">Blog at WordPress.com</a>. | <a href="http://theme.wordpress.com/themes/andreas09/" title="Learn more about this theme">The Andreas09 Theme</a>.</div>

<script type='text/javascript' src='//0.gravatar.com/js/gprofiles.js?ver=201416x'></script>
<script type='text/javascript'>
/* <![CDATA[ */
var WPGroHo = {"my_hash":""};
/* ]]> */
</script>
<script type='text/javascript' src='http://s2.wp.com/wp-content/mu-plugins/gravatar-hovercards/wpgroho.js?m=1380573781g'></script>

<script>
//initialize and attach hovercards to all gravatars
jQuery( document ).ready( function( $ ) {
Gravatar.profile_cb = function( hash, id ) {
WPGroHo.syncProfileData( hash, id );
};
Gravatar.my_hash = WPGroHo.my_hash;
Gravatar.init( 'body', '#wp-admin-bar-my-account' );
});
</script>

<div style="display:none">
<div class="grofile-hash-map-614e792082e78cd852d0b60ae0f28bef">
</div>
</div>

<div id="bit" class="loggedout-follow-normal">
<a class="bsub" href="javascript:void(0)"><span id='bsub-text'>Follow</span></a>
<div id="bitsubscribe">

<h3><label for="loggedout-follow-field">Follow &ldquo;TurboManage&rdquo;</label></h3>

<form action="https://subscribe.wordpress.com" method="post" accept-charset="utf-8" id="loggedout-follow">
<p>Get every new post delivered to your Inbox.</p>

<p id="loggedout-follow-error" style="display: none;"></p>

<p class="bit-follow-count">Join 220 other followers</p>
<p><input type="email" name="email" value="Enter your email address" onfocus='this.value=(this.value=="Enter your email address") ? "" : this.value;' onblur='this.value=(this.value=="") ? "Enter email address" : this.value;' id="loggedout-follow-field"/></p>

<input type="hidden" name="action" value="subscribe"/>
<input type="hidden" name="blog_id" value="341230"/>
<input type="hidden" name="source" value="http://turbomanage.wordpress.com/"/>
<input type="hidden" name="sub-type" value="loggedout-follow"/>

<input type="hidden" id="_wpnonce" name="_wpnonce" value="7e29739e82" /><input type="hidden" name="_wp_http_referer" value="/" />
<p id='bsub-subscribe-button'><input type="submit" value="Sign me up" /></p>
</form>
<div id='bsub-credit'><a href="http://wordpress.com/signup/?ref=lof">Powered by WordPress.com</a></div>
</div><!-- #bitsubscribe -->
</div><!-- #bit -->

<script type="text/javascript">
WPCOM_sharing_counts = {"http:\/\/turbomanage.wordpress.com\/2014\/03\/20\/storm-gen-has-moved\/":4471,"http:\/\/turbomanage.wordpress.com\/2014\/02\/18\/open-for-business-in-peru-2\/":4385,"http:\/\/turbomanage.wordpress.com\/2013\/10\/21\/a-long-pause\/":4361,"http:\/\/turbomanage.wordpress.com\/2013\/05\/21\/google-cloud-android-with-mobile-backend-starter\/":4299,"http:\/\/turbomanage.wordpress.com\/2013\/05\/20\/android-demo-tips-behind-the-scenes-at-google-io\/":4293,"http:\/\/turbomanage.wordpress.com\/2013\/03\/26\/progressdialog-considered-harmful\/":4286,"http:\/\/turbomanage.wordpress.com\/2013\/02\/28\/optimizing-your-mobile-web-app\/":4283,"http:\/\/turbomanage.wordpress.com\/2013\/02\/20\/testing-multi-user-support-in-the-android-emulator\/":4268,"http:\/\/turbomanage.wordpress.com\/2012\/12\/18\/storm-implementation-notes\/":4261,"http:\/\/turbomanage.wordpress.com\/2012\/12\/11\/storm-preview\/":4252} </script>
<script type="text/javascript">
jQuery(document).on( 'ready post-load', function(){
jQuery( 'a.share-google-plus-1' ).on( 'click', function() {
window.open( jQuery(this).attr( 'href' ), 'wpcomgoogle-plus-1', 'menubar=1,resizable=1,width=480,height=550' );
return false;
});
});
</script>
<script type="text/javascript">
jQuery(document).on( 'ready post-load', function(){
jQuery( 'a.share-twitter' ).on( 'click', function() {
window.open( jQuery(this).attr( 'href' ), 'wpcomtwitter', 'menubar=1,resizable=1,width=600,height=350' );
return false;
});
});
</script>
<script type="text/javascript">
jQuery(document).on( 'ready post-load', function(){
jQuery( 'a.share-linkedin' ).on( 'click', function() {
window.open( jQuery(this).attr( 'href' ), 'wpcomlinkedin', 'menubar=1,resizable=1,width=580,height=450' );
return false;
});
});
</script>
<div id="sharing_email" style="display: none;">
<form action="/" method="post">
<label for="target_email">Send to Email Address</label>
<input type="email" name="target_email" id="target_email" value="" />


<label for="source_name">Your Name</label>
<input type="text" name="source_name" id="source_name" value="" />

<label for="source_email">Your Email Address</label>
<input type="email" name="source_email" id="source_email" value="" />


<div class="recaptcha" id="sharing_recaptcha"></div><input type="hidden" name="recaptcha_public_key" id="recaptcha_public_key" value="6LcYW8MSAAAAADBAuEH9yaPcF7lWh11Iq62ZKtoo" />
<img style="float: right; display: none" class="loading" src="http://s2.wp.com/wp-content/mu-plugins/post-flair/sharing/images/loading.gif?m=1315610318g" alt="loading" width="16" height="16" />
<input type="submit" value="Send Email" class="sharing_send" />
<a href="#cancel" class="sharing_cancel">Cancel</a>

<div class="errors errors-1" style="display: none;">
Post was not sent - check your email addresses! </div>

<div class="errors errors-2" style="display: none;">
Email check failed, please try again </div>

<div class="errors errors-3" style="display: none;">
Sorry, your blog cannot share posts by email. </div>
</form>
</div>
<iframe src='http://widgets.wp.com/likes/master.html?ver=20140227#ver=20140227&amp;mp6=1' scrolling='no' id='likes-master' name='likes-master' style='display:none;'></iframe>
<div id='likes-other-gravatars'><div class="likes-text"><span>%d</span> bloggers like this:</div><ul class="wpl-avatars sd-like-gravatars"></ul></div>
<script type="text/javascript">
//<![CDATA[
var jetpackLikesWidgetQueue = [];
var jetpackLikesWidgetBatch = [];
var jetpackLikesMasterReady = false;

function JetpackLikespostMessage( message, target ) {
if ( "string" === typeof message ){
try{
message = JSON.parse( message );
}
catch(e) {
return;
}
}

pm( {
target: target,
type: 'likesMessage',
data: message,
origin: '*'
} );
}

function JetpackLikesBatchHandler() {
var requests = [];
jQuery( 'div.jetpack-likes-widget-unloaded' ).each( function( i ) {
if ( jetpackLikesWidgetBatch.indexOf( this.id ) > -1 )
return;
jetpackLikesWidgetBatch.push( this.id );
var regex = /like-(post|comment)-wrapper-(\d+)-(\d+)-(\w+)/;
var match = regex.exec( this.id );
if ( ! match || match.length != 5 )
return;

var info = {
blog_id: match[2],
width: this.width
};

if ( 'post' == match[1] ) {
info.post_id = match[3];
} else if ( 'comment' == match[1] ) {
info.comment_id = match[3];
}

info.obj_id = match[4];

requests.push( info );
});

if ( requests.length > 0 ) {
JetpackLikespostMessage( { event: 'initialBatch', requests: requests }, window.frames['likes-master'] );
}
}

function JetpackLikesMessageListener( event ) {
if ( "undefined" == typeof event.event )
return;

if ( 'masterReady' == event.event ) {
jQuery( document ).ready( function() {
jetpackLikesMasterReady = true;

var stylesData = {
event: 'injectStyles'
};

if ( jQuery( 'iframe.admin-bar-likes-widget' ).length > 0 ) {
JetpackLikespostMessage( { event: 'adminBarEnabled' }, window.frames[ 'likes-master' ] );

stylesData.adminBarStyles = {
background: jQuery( '#wpadminbar .quicklinks li#wp-admin-bar-wpl-like > a' ).css( 'background' ),
isRtl: ( 'rtl' == jQuery( '#wpadminbar' ).css( 'direction' ) )
};
}

if ( !window.addEventListener )
jQuery( '#wp-admin-bar-admin-bar-likes-widget' ).hide();

stylesData.textStyles = {
color: jQuery( '.sd-text-color').css( 'color' ),
fontFamily: jQuery( '.sd-text-color' ).css( 'font-family' ),
fontSize: jQuery( '.sd-text-color' ).css( 'font-size' ),
direction: jQuery( '.sd-text-color' ).css( 'direction' ),
fontWeight: jQuery( '.sd-text-color' ).css( 'font-weight' ),
fontStyle: jQuery( '.sd-text-color' ).css( 'font-style' ),
textDecoration: jQuery( '.sd-text-color' ).css('text-decoration')
};

stylesData.linkStyles = {
color: jQuery( '.sd-link-color' ).css('color'),
fontFamily: jQuery( '.sd-link-color' ).css('font-family'),
fontSize: jQuery( '.sd-link-color' ).css('font-size'),
textDecoration: jQuery( '.sd-link-color' ).css('text-decoration'),
fontWeight: jQuery( '.sd-link-color' ).css( 'font-weight' ),
fontStyle: jQuery( '.sd-link-color' ).css( 'font-style' )
};

JetpackLikespostMessage( stylesData, window.frames[ 'likes-master' ] );

JetpackLikesBatchHandler();

jQuery( document ).on( 'inview', 'div.jetpack-likes-widget-unloaded', function() {
jetpackLikesWidgetQueue.push( this.id );
});
});
}

if ( 'showLikeWidget' == event.event ) {
jQuery( '#' + event.id + ' .post-likes-widget-placeholder' ).fadeOut( 'fast', function() {
jQuery( '#' + event.id + ' .post-likes-widget' ).fadeIn( 'fast', function() {
JetpackLikespostMessage( { event: 'likeWidgetDisplayed', blog_id: event.blog_id, post_id: event.post_id, obj_id: event.obj_id }, window.frames['likes-master'] );
});
});
}

if ( 'clickReblogFlair' == event.event ) {
wpcom_reblog.toggle_reblog_box_flair( event.obj_id );
}

if ( 'showOtherGravatars' == event.event ) {
var $container = jQuery( '#likes-other-gravatars' );
var $list = $container.find( 'ul' );

$container.hide();
$list.html( '' );

$container.find( '.likes-text span' ).text( event.total );

jQuery.each( event.likers, function( i, liker ) {
$list.append( '<li class="' + liker.css_class + '"><a href="' + liker.profile_URL + '" class="wpl-liker" rel="nofollow" target="_parent"><img src="' + liker.avatar_URL + '" alt="' + liker.name + '" width="30" height="30" style="padding-right: 3px;" /></a></li>');
} );

var offset = jQuery( "[name='" + event.parent + "']" ).offset();

$container.css( 'left', offset.left + event.position.left - 10 + 'px' );
$container.css( 'top', offset.top + event.position.top - 33 + 'px' );

var rowLength = Math.floor( event.width / 37 );
var height = ( Math.ceil( event.likers.length / rowLength ) * 37 ) + 13;
if ( height > 204 ) {
height = 204;
}

$container.css( 'height', height + 'px' );
$container.css( 'width', rowLength * 37 - 7 + 'px' );

$list.css( 'width', rowLength * 37 + 'px' );

$container.fadeIn( 'slow' );

var scrollbarWidth = $list[0].offsetWidth - $list[0].clientWidth;
if ( scrollbarWidth > 0 ) {
$container.width( $container.width() + scrollbarWidth );
$list.width( $list.width() + scrollbarWidth );
}
}
}

pm.bind( 'likesMessage', function(e) { JetpackLikesMessageListener(e); } );

jQuery( document ).click( function( e ) {
var $container = jQuery( '#likes-other-gravatars' );

if ( $container.has( e.target ).length === 0 ) {
$container.fadeOut( 'slow' );
}
});

function JetpackLikesWidgetQueueHandler() {
var wrapperID;
if ( ! jetpackLikesMasterReady ) {
setTimeout( JetpackLikesWidgetQueueHandler, 500 );
return;
}

if ( jetpackLikesWidgetQueue.length > 0 ) {
// We may have a widget that needs creating now
var found = false;
while( jetpackLikesWidgetQueue.length > 0 ) {
// Grab the first member of the queue that isn't already loading.
wrapperID = jetpackLikesWidgetQueue.splice( 0, 1 )[0];
if ( jQuery( '#' + wrapperID ).hasClass( 'jetpack-likes-widget-unloaded' ) ) {
found = true;
break;
}
}
if ( ! found ) {
setTimeout( JetpackLikesWidgetQueueHandler, 500 );
return;
}
} else if ( jQuery( 'div.jetpack-likes-widget-unloaded' ).length > 0 ) {
// Grab any unloaded widgets for a batch request
JetpackLikesBatchHandler();

// Get the next unloaded widget
wrapperID = jQuery( 'div.jetpack-likes-widget-unloaded' ).first()[0].id;
if ( ! wrapperID ) {
// Everything is currently loaded
setTimeout( JetpackLikesWidgetQueueHandler, 500 );
return;
}
}

if ( 'undefined' === typeof wrapperID ) {
setTimeout( JetpackLikesWidgetQueueHandler, 500 );
return;
}

var $wrapper = jQuery( '#' + wrapperID );
$wrapper.find( 'iframe' ).remove();

if ( $wrapper.hasClass( 'slim-likes-widget' ) ) {
$wrapper.find( '.post-likes-widget-placeholder' ).after( "<iframe class='post-likes-widget jetpack-likes-widget' name='" + $wrapper.data( 'name' ) + "' height='22px' width='68px' frameBorder='0' scrolling='no' src='" + $wrapper.data( 'src' ) + "'></iframe>" );
} else {
$wrapper.find( '.post-likes-widget-placeholder' ).after( "<iframe class='post-likes-widget jetpack-likes-widget' name='" + $wrapper.data( 'name' ) + "' height='55px' width='100%' frameBorder='0' src='" + $wrapper.data( 'src' ) + "'></iframe>" );
}

$wrapper.removeClass( 'jetpack-likes-widget-unloaded' ).addClass( 'jetpack-likes-widget-loading' );

$wrapper.find( 'iframe' ).load( function( e ) {
var $iframe = jQuery( e.target );
$wrapper.removeClass( 'jetpack-likes-widget-loading' ).addClass( 'jetpack-likes-widget-loaded' );

JetpackLikespostMessage( { event: 'loadLikeWidget', name: $iframe.attr( 'name' ), width: $iframe.width() }, window.frames[ 'likes-master' ] );

if ( $wrapper.hasClass( 'slim-likes-widget' ) ) {
$wrapper.find( 'iframe' ).Jetpack( 'resizeable' );
}
});
setTimeout( JetpackLikesWidgetQueueHandler, 250 );
}
JetpackLikesWidgetQueueHandler();
//]]>
</script>
<script type='text/javascript' src='http://s1.wp.com/_static/??-eJzTLy/QTc7PK0nNK9EvyClNz8wr1i+uzCtJrMjITM/IAeKS1CJMEWP94uSizIISoOIM5/yiVL2sYh19yo1yKiotzvBKLEsEmmefa2tobGlubmZkamSYBQDROkAK'></script>
<script type='text/javascript'>
(function(){
var corecss = document.createElement('link');
var themecss = document.createElement('link');
var corecssurl = "http://s0.wp.com/wp-content/plugins/syntaxhighlighter/syntaxhighlighter3/styles/shCore.css?m=1395343499g&amp;ver=3.0.83c";
if ( corecss.setAttribute ) {
corecss.setAttribute( "rel", "stylesheet" );
corecss.setAttribute( "type", "text/css" );
corecss.setAttribute( "href", corecssurl );
} else {
corecss.rel = "stylesheet";
corecss.href = corecssurl;
}
document.getElementsByTagName("head")[0].insertBefore( corecss, document.getElementById("syntaxhighlighteranchor") );
var themecssurl = "http://s0.wp.com/wp-content/plugins/syntaxhighlighter/syntaxhighlighter3/styles/shThemeDefault.css?m=1363304414g&amp;ver=3.0.83c";
if ( themecss.setAttribute ) {
themecss.setAttribute( "rel", "stylesheet" );
themecss.setAttribute( "type", "text/css" );
themecss.setAttribute( "href", themecssurl );
} else {
themecss.rel = "stylesheet";
themecss.href = themecssurl;
}
//document.getElementById("syntaxhighlighteranchor").appendChild(themecss);
document.getElementsByTagName("head")[0].insertBefore( themecss, document.getElementById("syntaxhighlighteranchor") );
})();
SyntaxHighlighter.config.strings.expandSource = '+ expand source';
SyntaxHighlighter.config.strings.help = '?';
SyntaxHighlighter.config.strings.alert = 'SyntaxHighlighter\n\n';
SyntaxHighlighter.config.strings.noBrush = 'Can\'t find brush for: ';
SyntaxHighlighter.config.strings.brushNotHtmlScript = 'Brush wasn\'t configured for html-script option: ';
SyntaxHighlighter.defaults['pad-line-numbers'] = false;
SyntaxHighlighter.defaults['toolbar'] = false;
SyntaxHighlighter.all();
</script>
<script type='text/javascript'>
/* <![CDATA[ */
var recaptcha_options = {"lang":"en"};
/* ]]> */
</script>
<script type='text/javascript' src='http://s2.wp.com/_static/??/wp-content/js/devicepx.js,/wp-content/mu-plugins/post-flair/sharing/sharing.js?m=1394096990j'></script>
<script type="text/javascript">
// <![CDATA[
(function() {
try{
if ( window.external &&'msIsSiteMode' in window.external) {
if (window.external.msIsSiteMode()) {
var jl = document.createElement('script');
jl.type='text/javascript';
jl.async=true;
jl.src='/wp-content/plugins/ie-sitemode/custom-jumplist.php';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(jl, s);
}
}
}catch(e){}
})();
// ]]>
</script><script src="http://s.stats.wordpress.com/w.js?21" type="text/javascript"></script>
<script type="text/javascript">
st_go({'blog':'341230','v':'wpcom','tz':'-5','user_id':'0','subd':'turbomanage'});
ex_go({'crypt':'UE40eW5QN0p8M2Y/RE1LVmwrVi5vQS5fVFtfdHBbPyw1VXIrU3hWLHhmcmw0bWUwNis5Vk1qSEpzeXBoR2E/dmFIcl9HLjd1TGgrJm9kalImVUZGNEZjYjVlR1gseHYlYnEvLGNwLCt0fH5LbmksQzRhV3ZKemNmbnNZZCZTa0thLGpyTD9FSVJvMy5bMC0zNTJMdnliJTBXSHBnfj0mNyV8WUV6ZHFCek8sVVRzJlArbW1yJkFNY1U9Q0FaaEY5VklXeW9WcF9BOS1MPyZiQkFkYWgyT3owTjlQVEI9al1lbHl+ZHd0V1VZaWhNPW5odXdYbk81fG9dR3MtNEV4LXhqczNlUEVwTC9FQ1lsRC9yfjVjXXhPLEwzZXJ8LTkrcTUzcXZCNVJCXXYxXTU9bGlmLFhxSDF6T2IvU1ROaXxaM2s0eGxoc1JKY1BQeHw='});
addLoadEvent(function(){linktracker_init('341230',0);});
</script>
<noscript><img src="http://stats.wordpress.com/b.gif?v=noscript" style="height:0px;width:0px;overflow:hidden" alt="" /></noscript>
<script>
if ( 'object' === typeof wpcom_mobile_user_agent_info ) {

wpcom_mobile_user_agent_info.init();
var mobileStatsQueryString = "";

if( false !== wpcom_mobile_user_agent_info.matchedPlatformName )
mobileStatsQueryString += "&x_" + 'mobile_platforms' + '=' + wpcom_mobile_user_agent_info.matchedPlatformName;

if( false !== wpcom_mobile_user_agent_info.matchedUserAgentName )
mobileStatsQueryString += "&x_" + 'mobile_devices' + '=' + wpcom_mobile_user_agent_info.matchedUserAgentName;

if( wpcom_mobile_user_agent_info.isIPad() )
mobileStatsQueryString += "&x_" + 'ipad_views' + '=' + 'views';

if( "" != mobileStatsQueryString ) {
new Image().src = document.location.protocol + '//stats.wordpress.com/g.gif?v=wpcom-no-pv' + mobileStatsQueryString + '&baba=' + Math.random();
}

}
</script>
</body>

</html>