<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8001153144156969687</id><updated>2012-02-15T23:18:46.193-08:00</updated><category term='piracy'/><category term='android'/><category term='webos'/><category term='passwords'/><category term='palm'/><title type='text'>Daniel Lew's Coding Thoughts</title><subtitle type='html'>I post random things I've learned while coding with the hope that it will save people time and effort.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>38</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-1200614978424849742</id><published>2012-01-26T11:00:00.000-08:00</published><updated>2012-01-26T11:00:03.493-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Another App Widget Compatibility Trick</title><content type='html'>I've written previously on &lt;a href="http://daniel-codes.blogspot.com/2011/06/honeycomb-app-widget-and-backwards.html"&gt;the issue of preventing app widgets from appearing on particular versions of Android&lt;/a&gt;. &amp;nbsp;This is useful, for example, when you have a Honeycomb-style widget but support pre-Honeycomb devices.&lt;br /&gt;&lt;br /&gt;I've got a second version of this trick which I think is a bit easier now. &amp;nbsp;Instead of preventing the system from seeing your&amp;nbsp;AppWidgetProviderInfo, you can actually enable/disable the widget receiver itself based on configuration resource selectors.&lt;br /&gt;&lt;br /&gt;First, setup your app widget receiver thus (important part bolded):&lt;br /&gt;&lt;pre&gt;&amp;lt;receiver&lt;br /&gt;    &lt;b&gt;android:enabled="@bool/widget_enabled"&lt;/b&gt;&lt;br /&gt;    android:name="com.company.appwidget.MyAppWidgetProvider" &amp;gt;&lt;br /&gt;    &amp;lt;intent-filter &amp;gt;&lt;br /&gt;        &amp;lt;action android:name="android.appwidget.action.APPWIDGET_UPDATE" /&amp;gt;&lt;br /&gt;    &amp;lt;/intent-filter&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;meta-data&lt;br /&gt;        android:name="android.appwidget.provider"&lt;br /&gt;        android:resource="@xml/appwidget_info" /&amp;gt;&lt;br /&gt;&amp;lt;/receiver&amp;gt;&lt;/pre&gt;&lt;br /&gt;Then all you need to do is create a config.xml (in your /res/values/ directory) file that sets "widget_enabled":&lt;br /&gt;&lt;pre&gt;&amp;lt;resources&amp;gt;&lt;br /&gt;    &amp;lt;bool name="widget_enabled"&amp;gt;true&amp;lt;/bool&amp;gt;&lt;br /&gt;&amp;lt;/resources&amp;gt;&lt;/pre&gt;&lt;br /&gt;You can then leverage the resource qualifier system to enable or disable the widget as necessary for different configurations. &amp;nbsp;For example, if you wanted your widget enabled on all versions of Android except for v11 through v13, you could set it up like this:&lt;br /&gt;&lt;br /&gt;/res/values/config.xml &amp;lt;-- widget_enabled=true&lt;br /&gt;/res/values-v11/config.xml &amp;lt;-- widget_enabled=false&lt;br /&gt;/res/values-v14/config.xml &amp;lt;-- widget_enabled=true&lt;br /&gt;&lt;br /&gt;The main advantage this provides over my last solution is that you need only modify a bool; the other solution required you to create duplicate versions of the&amp;nbsp;AppWidgetProviderInfo XML file (if you supported, say, v3 through v10 and v14+).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-1200614978424849742?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/1200614978424849742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2012/01/another-app-widget-compatibility-trick.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/1200614978424849742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/1200614978424849742'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2012/01/another-app-widget-compatibility-trick.html' title='Another App Widget Compatibility Trick'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-6613419394085621753</id><published>2012-01-24T11:00:00.001-08:00</published><updated>2012-01-24T11:28:09.160-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>GridLayout Child View Clipping Issues</title><content type='html'>&lt;a href="https://github.com/dlew/android-gridlayout"&gt;After back-porting GridLayout to 1.5+&lt;/a&gt;, I finally feel comfortable using it in my apps. &amp;nbsp;There's a bit of an initial learning curve, but I think that is mostly a function of un-learning the LinearLayout/RelativeLayout state of mind. &amp;nbsp;I've converted a number of my own layouts to use GridLayout and found them to be much faster/easier to understand afterwards.&lt;br /&gt;&lt;br /&gt;There was one particular issue that I ran into that is worth noting, for its solution was not immediately obvious. &amp;nbsp;I was making a two-column GridLayout, where an image was on the left and text was on the right. &amp;nbsp;Here's a simplified version of this layout:&lt;br /&gt;&lt;pre&gt;&amp;lt;GridLayout xmlns:android="http://schemas.android.com/apk/res/android"&lt;br /&gt;    android:layout_width="match_parent"&lt;br /&gt;    android:layout_height="wrap_content" &amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;Space android:layout_width="100dp" /&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;TextView android:text="@string/two_cities" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/GridLayout&amp;gt;&lt;/pre&gt;&lt;br /&gt;The two Views would align next to each other nicely, but the TextView on the right would end up overflowing off the edge (the example below is not cropped - this is how it appears on the device):&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-QAmQDFE-Uhg/Tx39slrQ39I/AAAAAAAAAVM/XrZ4Dl7hLGQ/s1600/clipping.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="188" src="http://2.bp.blogspot.com/-QAmQDFE-Uhg/Tx39slrQ39I/AAAAAAAAAVM/XrZ4Dl7hLGQ/s320/clipping.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;What was happening was that the TextView would take up the entire width of the GridLayout, instead of taking up just the width of the cell (as I expected).&lt;br /&gt;&lt;insert fail="" here="" image=""&gt;&lt;/insert&gt;&lt;br /&gt;&lt;br /&gt;It turns out the solution is to set the width of the TextView to zero, then let the View fill its space dynamically using layout_gravity:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;GridLayout xmlns:android="http://schemas.android.com/apk/res/android"&lt;br /&gt;    android:layout_width="match_parent"&lt;br /&gt;    android:layout_height="wrap_content" &amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;Space android:layout_width="100dp" /&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;TextView&lt;br /&gt;        &lt;b&gt;android:layout_width="0dip"&lt;br /&gt;        android:layout_gravity="fill_horizontal"&lt;/b&gt;&lt;br /&gt;        android:text="@string/two_cities" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/GridLayout&amp;gt;&lt;/pre&gt;&lt;br /&gt;Now the problem is solved:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-JXmoK_Ue3JA/Tx3-w29UmhI/AAAAAAAAAVU/TXouu6MR8No/s1600/correct.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="246" src="http://1.bp.blogspot.com/-JXmoK_Ue3JA/Tx3-w29UmhI/AAAAAAAAAVU/TXouu6MR8No/s320/correct.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;You need both of the highlighted attributes above; one or the other won't fix the problem. &amp;nbsp;However, it will nicely solve the issue and should be kept in mind anytime you setup a GridLayout child to fill horizontally or vertically.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-6613419394085621753?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/6613419394085621753/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2012/01/gridlayout-view-clipping-issues.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/6613419394085621753'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/6613419394085621753'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2012/01/gridlayout-view-clipping-issues.html' title='GridLayout Child View Clipping Issues'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-QAmQDFE-Uhg/Tx39slrQ39I/AAAAAAAAAVM/XrZ4Dl7hLGQ/s72-c/clipping.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-5256546140543271720</id><published>2012-01-13T07:09:00.000-08:00</published><updated>2012-01-13T07:15:44.509-08:00</updated><title type='text'>Highlights from Android's New Design Docs</title><content type='html'>I've just finished reading through the wonderful new &lt;a href="http://developer.android.com/design/"&gt;Android Design site&lt;/a&gt; Google just released. &amp;nbsp;A lot of the information is stuff you should already know if you're an Android developer. &amp;nbsp;However, I took notes of pages/information that I found particularly interesting (or new for ICS). &amp;nbsp;Listed in order of appearance on the site:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://developer.android.com/design/style/metrics-grids.html"&gt;&lt;b&gt;48dp Rhythm&lt;/b&gt;&lt;/a&gt; - Like me, you may have implicitly noticed this pattern before, but it's good to see it stated so explicitly. &amp;nbsp;(Same with the 8dp gap.)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://developer.android.com/design/patterns/navigation.html"&gt;&lt;b&gt;Back vs. Up&lt;/b&gt;&lt;/a&gt; - Having been confused on the exact distinction for a while, this page is a breath of fresh air.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://developer.android.com/design/patterns/actionbar.html"&gt;&lt;b&gt;Action Bar Pattern&lt;/b&gt;&lt;/a&gt; - This entire page should be required reading for everyone. &amp;nbsp;It definitely looks like the Action Bar is the future, and this is the instruction manual.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://developer.android.com/design/patterns/selection.html"&gt;&lt;b&gt;Long Touch Multiselect&lt;/b&gt;&lt;/a&gt; - Breaking news: long touch no longer activates the contextual menu. &amp;nbsp;Now, it is purely for activating selection (aka multiselect). &amp;nbsp;Every longtime Android developer should be aware of this change.&lt;br /&gt;&lt;br /&gt;(Personally, I didn't even notice this change despite owning a Galaxy Nexus for a month. &amp;nbsp;Perhaps that's because not even every Google app has switched to the new design yet. &amp;nbsp;Or perhaps it's because long touch is just not an intuitive way to design any functionality - I've long since given up putting any core functionality into long touch. &amp;nbsp;Still, it's worth noting the design shift.)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://developer.android.com/design/patterns/pure-android.html"&gt;&lt;b&gt;Android is not the iPhone&lt;/b&gt;&lt;/a&gt; - At least, that's what this section *should* be called. &amp;nbsp;This page will be most useful for showing to your designer or boss how Android differs from iPhone and why you shouldn't just port assets and design directly from one to the other.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-5256546140543271720?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/5256546140543271720/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2012/01/highlights-from-androids-new-design.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/5256546140543271720'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/5256546140543271720'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2012/01/highlights-from-androids-new-design.html' title='Highlights from Android&apos;s New Design Docs'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-6553109940614440299</id><published>2012-01-10T07:52:00.000-08:00</published><updated>2012-01-10T07:53:12.039-08:00</updated><title type='text'>GridLayout Library for Android 1.5+ Support</title><content type='html'>If you're like me, when you first heard about &lt;a href="http://developer.android.com/reference/android/widget/GridLayout.html"&gt;GridLayout&lt;/a&gt;, you were excited. &amp;nbsp;Then disappointed, because it's currently only available for Ice Cream Sandwich. &amp;nbsp;That is true no longer - I've taken the time to port a working version of it all the way back to Android 1.5 and above.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://github.com/dlew/android-gridlayout"&gt;Check out the android-gridlayout library here.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The one caveat is that there is one method in GridLayout that I was not able to port back. &amp;nbsp;As a result, when you change the visibility of a child View of a compatibility GridLayout, you should also call GridLayout.notifyChildVisibilityChanged().&lt;br /&gt;&lt;br /&gt;Let me know what you think, especially if there are any bugs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-6553109940614440299?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/6553109940614440299/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2012/01/if-youre-like-me-when-you-first-heard.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/6553109940614440299'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/6553109940614440299'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2012/01/if-youre-like-me-when-you-first-heard.html' title='GridLayout Library for Android 1.5+ Support'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-1196520234039815556</id><published>2011-12-20T11:00:00.000-08:00</published><updated>2011-12-20T11:00:05.681-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Sharing with Gmail</title><content type='html'>I read through &lt;a href="http://developer.android.com/training/index.html"&gt;the Android Training docs&lt;/a&gt; recently and came across &lt;a href="http://developer.android.com/training/sharing/send.html#send-binary-content"&gt;a section&lt;/a&gt; which I thought I could use to improve sharing on &lt;a href="https://github.com/dlew/android-ragefaces"&gt;Rage Faces&lt;/a&gt; (by getting rid of the need for an SD card-based share system):&lt;br /&gt;&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="background-color: white; color: #333333; font-family: arial, sans-serif; font-size: 13px; line-height: 16px;"&gt;Write the data to a file in your own application directory using&amp;nbsp;&lt;/span&gt;&lt;code style="background-color: white; color: #007000; font-size: 13px; line-height: 1em;"&gt;&lt;a href="http://developer.android.com/reference/android/content/Context.html#openFileOutput(java.lang.String, int)" style="color: #006699;"&gt;openFileOutput()&lt;/a&gt;&lt;/code&gt;&lt;span style="background-color: white; color: #333333; font-family: arial, sans-serif; font-size: 13px; line-height: 16px;"&gt;&amp;nbsp;with mode&amp;nbsp;&lt;/span&gt;&lt;code style="background-color: white; color: #007000; font-size: 13px; line-height: 1em;"&gt;&lt;a href="http://developer.android.com/reference/android/content/Context.html#MODE_WORLD_READABLE" style="color: #006699;"&gt;MODE_WORLD_READABLE&lt;/a&gt;&lt;/code&gt;&lt;span style="background-color: white; color: #333333; font-family: arial, sans-serif; font-size: 13px; line-height: 16px;"&gt;&amp;nbsp;after which&amp;nbsp;&lt;/span&gt;&lt;code style="background-color: white; color: #007000; font-size: 13px; line-height: 1em;"&gt;&lt;a href="http://developer.android.com/reference/android/content/Context.html#getFileStreamPath(java.lang.String)" style="color: #006699;"&gt;getFileStreamPath()&lt;/a&gt;&lt;/code&gt;&lt;span style="background-color: white; color: #333333; font-family: arial, sans-serif; font-size: 13px; line-height: 16px;"&gt;&amp;nbsp;can be used to return a&amp;nbsp;&lt;/span&gt;&lt;code style="background-color: white; color: #007000; font-size: 13px; line-height: 1em;"&gt;&lt;a href="http://developer.android.com/reference/java/io/File.html" style="color: #006699;"&gt;File&lt;/a&gt;&lt;/code&gt;&lt;span style="background-color: white; color: #333333; font-family: arial, sans-serif; font-size: 13px; line-height: 16px;"&gt;. As with the previous option,&amp;nbsp;&lt;/span&gt;&lt;code style="background-color: white; color: #007000; font-size: 13px; line-height: 1em;"&gt;&lt;a href="http://developer.android.com/reference/android/net/Uri.html#fromFile(java.io.File)" style="color: #006699;"&gt;Uri.fromFile()&lt;/a&gt;&lt;/code&gt;&lt;span style="background-color: white; color: #333333; font-family: arial, sans-serif; font-size: 13px; line-height: 16px;"&gt;&amp;nbsp;will create a&amp;nbsp;&lt;/span&gt;&lt;code style="background-color: white; color: #007000; font-size: 13px; line-height: 1em;"&gt;file://&lt;/code&gt;&lt;span style="background-color: white; color: #333333; font-family: arial, sans-serif; font-size: 13px; line-height: 16px;"&gt;&amp;nbsp;style&amp;nbsp;&lt;/span&gt;&lt;code style="background-color: white; color: #007000; font-size: 13px; line-height: 1em;"&gt;&lt;a href="http://developer.android.com/reference/android/net/Uri.html" style="color: #006699;"&gt;Uri&lt;/a&gt;&lt;/code&gt;&lt;span style="background-color: white; color: #333333; font-family: arial, sans-serif; font-size: 13px; line-height: 16px;"&gt;&amp;nbsp;for your share intent.&lt;/span&gt;&lt;/blockquote&gt;However, this setup fails to work with the Gmail. &amp;nbsp;When you try to share with Gmail in this manner, an error message pops up in the logs:&lt;br /&gt;&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="background-color: #fbfbfb; color: #333333; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 12px; line-height: 18px;"&gt;file:// attachment paths must point to file:///mnt/sdcard. Ignoring attachment file:///data/data/com.idunnolol.ragefaces/files/share.png&lt;/span&gt;&lt;/blockquote&gt;I'm not sure why Gmail arbitrarily rejects attachments not on the SD card, but it pretty much cuts you off from sharing files with MODE_WORLD_READABLE. &amp;nbsp;Unfortunately sharing with Gmail feels like an essential part of the app so I'm going to have to stick with SD card sharing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-1196520234039815556?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/1196520234039815556/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2011/12/sharing-with-gmail.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/1196520234039815556'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/1196520234039815556'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2011/12/sharing-with-gmail.html' title='Sharing with Gmail'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-4823082232234785229</id><published>2011-10-31T09:00:00.000-07:00</published><updated>2011-10-31T09:00:10.851-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>TextWatchers and onRestoreInstanceState()</title><content type='html'>There's a small timing issue I'd like to mention because it's bitten me a few times now.&lt;br /&gt;&lt;br /&gt;When you want to observe changes to an &lt;a href="http://developer.android.com/reference/android/widget/EditText.html"&gt;EditText&lt;/a&gt;, you add a &lt;a href="http://developer.android.com/reference/android/text/TextWatcher.html"&gt;TextWatcher&lt;/a&gt; to it (via &lt;a href="http://developer.android.com/reference/android/widget/TextView.html#addTextChangedListener(android.text.TextWatcher)"&gt;TextView. addTextChangedListener()&lt;/a&gt;).  There are a lot of uses for TextWatchers, like implementing your own autocomplete or filters based on a dynamic EditText.  The only thing you have to be careful of is whether the text was changed by the user or the code - the listener fires either way.&lt;br /&gt;&lt;br /&gt;What's worth knowing about an EditText is that it will save and restore the state of the text inside of it within your Activity.  That means that when you rotate the screen, the EditText will restore the text that was inside of it.  And most importantly, &lt;span style="font-weight:bold;"&gt;the automated restoring of the text on rotation causes the TextWatcher's methods to fire&lt;/span&gt;.  Like I said - the TextWatcher doesn't discern between whether the user changed the EditText or the system did.&lt;br /&gt;&lt;br /&gt;The solution is simple - just don't add the TextWatcher until after the EditText's content has been restored.  It restores itself in Activity.onRestoreInstanceState(), which makes Activity.onResume() the preferred time to add TextWatchers to any EditTexts.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-4823082232234785229?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/4823082232234785229/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2011/10/textwatchers-and-onrestoreinstancestate.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/4823082232234785229'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/4823082232234785229'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2011/10/textwatchers-and-onrestoreinstancestate.html' title='TextWatchers and onRestoreInstanceState()'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-3457673789312769703</id><published>2011-10-10T10:00:00.000-07:00</published><updated>2011-10-10T10:00:01.206-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Android Drawable XML Talk</title><content type='html'>I recently gave a talk on the basics of Android drawable resources (via XML).&lt;br /&gt;&lt;br /&gt;There's a recording I made of the talk - not super, but functional.  I had to split it into two parts because of time restrictions on YouTube.  Here's &lt;a href="http://www.youtube.com/watch?v=pxeqA0lW158"&gt;part one&lt;/a&gt; and &lt;a href="http://www.youtube.com/watch?v=DNC-OpFWxuE"&gt;part two&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I also have a few links that may be useful (whether you watch the talk or not):&lt;br /&gt;&lt;br /&gt;- &lt;a href="https://github.com/dlew/android-drawable-xml-demo/raw/master/slides.pptx "&gt;The slides for the presentation&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;- &lt;a href="http://developer.android.com/guide/topics/resources/drawable-resource.html"&gt;Official Android drawable resource documentation&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;- &lt;a href="http://idunnolol.com/android/drawables.html"&gt;My drawable XML documentation&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;- &lt;a href="https://github.com/dlew/android-drawable-xml-demo"&gt;The github project with my samples&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-3457673789312769703?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/3457673789312769703/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2011/10/android-drawable-xml-talk.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/3457673789312769703'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/3457673789312769703'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2011/10/android-drawable-xml-talk.html' title='Android Drawable XML Talk'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-6903555158674046901</id><published>2011-09-26T13:23:00.000-07:00</published><updated>2011-09-26T13:31:25.997-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Logc.at</title><content type='html'>A few developers and I have formed a new Android development blog.  We snagged what I think is an awesome and nerdy url: &lt;a href="http://logc.at"&gt;logc.at&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I will still be writing here - this blog is intended for one-off coding issues that I want to document.  The new blog will be a place for me to write about Android development in a more general manner.  Instead of assisting with individual hurdles, I hope my articles there will help one become a better Android developer (or at least pick up a few tips).&lt;br /&gt;&lt;br /&gt;My first post is about &lt;a href="http://logc.at/2011/09/26/working-with-json/"&gt;working with JSON on Android&lt;/a&gt;.  Check it out!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-6903555158674046901?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/6903555158674046901/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2011/09/logcat.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/6903555158674046901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/6903555158674046901'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2011/09/logcat.html' title='Logc.at'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-7770361860787519815</id><published>2011-08-02T11:00:00.000-07:00</published><updated>2011-08-02T11:19:08.186-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>New to Android: More Style Restrictions</title><content type='html'>There have been two changes the Android platform build tools which have caught me off guard.  You may run into these problems when you next update your aapt.  Both of them cause compilation errors, so they're not easy to ignore, but it's not immediately obvious how to fix either.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Change #1: Implicit parenting now requires a parent&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Suppose I have this style defined in styles.xml:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;style name="Widget.MyWidget.Small"&gt; ... &amp;lt;/style&gt;&lt;/pre&gt;&lt;br /&gt;It was named that way after how Android sets up their widget styles (for example, "Widget.RatingBar.Small").  One thing to know is the implicit aspects to using dots in the naming is that you automatically parent all the styles before the dot.  So "Widget" is the parent of "Widget.MyWidget", and "Widget.MyWidget" is the parent of "Widget.MyWidget.Small".&lt;br /&gt;&lt;br /&gt;In the new version of aapt, you'll see this error:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Error retrieving parent for item: No resource found that matches the given name '@Style/Widget.MyWidget'.&lt;/pre&gt;&lt;br /&gt;This happens now because aapt is stricter with regards to style parenting.  Before, if there was no parent, it would just ignore this oversight; now it requires a parent.&lt;br /&gt;&lt;br /&gt;There are two possible solutions:&lt;br /&gt;&lt;br /&gt;1. Rename the style so that it doesn't use implicit parenting with dots.&lt;br /&gt;&lt;br /&gt;2. Create the parent styles.  They could either be empty, or you could actually use them for something.&lt;br /&gt;&lt;br /&gt;I think #1 makes more sense, unless you actually have a use for the parent styles.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Change #2: Some android styles are now enforced as private&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Before, you used to be able to get away with parenting some non-public Android styles:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;style name="MyRatingBar" parent="@android:style/Widget.RatingBar.Small"&gt; ... &amp;lt;/style&gt;&lt;/pre&gt;&lt;br /&gt;However, these styles have always been intended to be private, and the latest aapt will not allow you to build with a private parent defined.  &lt;a href="http://code.google.com/p/android/issues/detail?id=18659#c8"&gt;A Google employee explained the reason why this is no longer allowed:&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;For the framework, only public resources are guaranteed to only have the same integer, build after build. The integer of private resources integer will change from build to build.  This means that your custom style is referencing a parent that *will not* be valid once installed on a device. It'll referenced either another resources or none at all, and it won't do what you want.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;That seems reasonable to me.  The solution is to just import the entire style into your styles.xml; all the styles are &lt;a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=tree;f=core/res/res/values"&gt;open source&lt;/a&gt; anyways.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-7770361860787519815?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/7770361860787519815/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2011/08/new-to-android-more-style-restrictions.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/7770361860787519815'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/7770361860787519815'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2011/08/new-to-android-more-style-restrictions.html' title='New to Android: More Style Restrictions'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-7532081945239595146</id><published>2011-06-30T12:01:00.000-07:00</published><updated>2012-01-26T11:42:51.308-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Honeycomb App Widget and Backwards Compatibility</title><content type='html'>Honeycomb added &lt;a href="http://developer.android.com/guide/topics/appwidgets/index.html#collections"&gt;some neat functionality&lt;/a&gt; to Android widgets.  However, there's a problem that arises from adding a widget using collections to your app - what if your APK is supposed to work on all versions of Android?  How do you prevent the Honeycomb-only widget from appearing in previous versions?&lt;br /&gt;&lt;br /&gt;Normally when you want to do something different between versions of Android, you either use &lt;a href="http://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources"&gt;resource qualifiers&lt;/a&gt; (for XML) or check &lt;a href="http://developer.android.com/reference/android/os/Build.html"&gt;Build&lt;/a&gt; (for code).  But in this case, the widget itself is defined in AndroidManifest.xml, which can only be at the root of your project.&lt;br /&gt;&lt;br /&gt;There's a way of removing the app widget from the listing in previous versions: use resource qualifiers on the AppWidgetProviderInfo resource.&lt;br /&gt;&lt;br /&gt;When you define an app widget in AndroidManifest.xml, you must also define a &amp;lt;meta-data&amp;gt; element which points towards the AppWidgetProviderInfo (that is usually contained inside of a file in /res/xml/).  Simply move that provider file to a directory that only Honeycomb can see - &lt;span style="font-weight: bold;"&gt;/res/xml-v11/&lt;/span&gt;.  Earlier versions of Android will try to load the widget only to find no AppWidgetProviderInfo and will thus ignore the widget.&lt;br /&gt;&lt;br /&gt;UDPATE (Jan 26th, 2012): &lt;a href="http://daniel-codes.blogspot.com/2012/01/another-app-widget-compatibility-trick.html"&gt;I've now written about an improved solution to this problem.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-7532081945239595146?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/7532081945239595146/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2011/06/honeycomb-app-widget-and-backwards.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/7532081945239595146'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/7532081945239595146'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2011/06/honeycomb-app-widget-and-backwards.html' title='Honeycomb App Widget and Backwards Compatibility'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-8743878054768311470</id><published>2011-05-31T11:00:00.000-07:00</published><updated>2011-05-31T14:27:29.656-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='passwords'/><title type='text'>Passwords: Longer, Not More Complex</title><content type='html'>I was going through a mandatory security training course for my company when I came across a slide about passwords.  Its advice was that the password should be at least 7 characters long and include a good mixture of lower/upper case characters, plus numbers and special symbols.  Sony is using the same tactics.  I had to change my PSN password and it forced me to use at least one number and one letter in the password.&lt;br /&gt;&lt;br /&gt;This is totally bunk advice.  &lt;span style="font-weight:bold;"&gt;What you want is a long password, and it doesn't have to be complex&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Suppose there's a server with your credentials that has the bare essentials of good security (&lt;a href="http://en.wikipedia.org/wiki/Cryptographic_hash_function"&gt;hash&lt;/a&gt; + &lt;a href="http://en.wikipedia.org/wiki/Salt_(cryptography)"&gt;salt&lt;/a&gt;).  If someone compromises the server, they will have to crack your password by checking every single password against the hash in the database.  Therefore, the more possible passwords your scheme contains, the longer it takes to crack.&lt;br /&gt;&lt;br /&gt;You can calculate an approximation on number of attempted passwords a cracker will have to attempt with this simple formula: (size of character set)^&lt;sup&gt;(password length)&lt;/sup&gt;.  Given a rudimentary understanding of exponents, it's pretty evident that increasing the password length will have a lot more impact than expanding the character set size.&lt;br /&gt;&lt;br /&gt;Let's look at some numbers.  On my keyboard, there are 95 characters I could insert into a password.  Let's compare a length eight password using that character set to a slightly longer password using only lower case letters (a 26 size character set):&lt;br /&gt;&lt;br /&gt;95^&lt;sup&gt;8&lt;/sup&gt; ~= &lt;span style="font-weight:bold;"&gt;6 quadrillion&lt;/span&gt; (or ~10^&lt;sup&gt;15&lt;/sup&gt;)&lt;br /&gt;26^&lt;sup&gt;12&lt;/sup&gt; ~= &lt;span style="font-weight:bold;"&gt;90 quadrillion&lt;/span&gt; (or ~10^&lt;sup&gt;16&lt;/sup&gt;)&lt;br /&gt;26^&lt;sup&gt;15&lt;/sup&gt; ~= &lt;span style="font-weight:bold;"&gt;1 sextillion&lt;/span&gt; (or ~10^&lt;sup&gt;21&lt;/sup&gt;)&lt;br /&gt;&lt;br /&gt;With four extra characters, my simpler password is already harder to crack than the complex eight character password.&lt;br /&gt;&lt;br /&gt;The best part about using a limited character set is that you can create a pass &lt;span style="font-weight:bold;"&gt;phrases&lt;/span&gt; instead of pass &lt;span style="font-weight:bold;"&gt;words&lt;/span&gt;.  It's much easier to remember "please do not break into my account" than "x82@.Zij", and it's also far more resilient to cracking, too.&lt;br /&gt;&lt;br /&gt;Unfortunately, not all sites allow for long passwords.  In those cases, it's better to use a more complex character set.  But when given the opportunity, a long and simple password is vastly superior to a short but complicated one.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Addendum&lt;/span&gt;: A friend pointed out that pass phrases are more vulnerable to dictionary attacks.  This is true, but only if your pass phrase contains very few words.  The approximate effort for pass phrases is (number of possible words)^&lt;sup&gt;(number of words)&lt;/sup&gt;.  &lt;a href="http://oxforddictionaries.com/page/93"&gt;Given how many possible words there are&lt;/a&gt;, it only takes a phrase of a few words to be as secure as 15 simple characters of nonsense.  So while "supercalifragilisticexpialidocious" is a crappy password despite how long it is, my example from above ("please do not break into my account") is still very secure.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-8743878054768311470?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/8743878054768311470/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2011/05/passwords-longer-not-more-complex.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/8743878054768311470'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/8743878054768311470'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2011/05/passwords-longer-not-more-complex.html' title='Passwords: Longer, Not More Complex'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-4793483449850095097</id><published>2011-05-24T10:00:00.000-07:00</published><updated>2011-05-24T11:10:50.231-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Using Subquery Columns on Android with SQLite</title><content type='html'>Take a look at this (contrived) query, as might be passed to &lt;a href="http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#rawQuery(java.lang.String, java.lang.String[])"&gt;SQLiteDatabase.rawQuery()&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SELECT *&lt;br /&gt;FROM t1 A, (SELECT T2.id FROM t2 T2) B&lt;br /&gt;WHERE A.id = B.id&lt;/pre&gt;&lt;br /&gt;On Android 2.1 and below, this query will cause your app to crash.  You'll get an error like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Caused by: android.database.sqlite.SQLiteException: no such column: B.id: , while compiling: SELECT * FROM t1 A, (SELECT T2.id FROM t2 T2) B WHERE A.id = B.id&lt;br /&gt;    at android.database.sqlite.SQLiteProgram.native_compile(Native Method)&lt;br /&gt;    at android.database.sqlite.SQLiteProgram.compile(SQLiteProgram.java:110)&lt;br /&gt;    at android.database.sqlite.SQLiteProgram.&amp;lt;init&gt;(SQLiteProgram.java:59)&lt;br /&gt;    at android.database.sqlite.SQLiteQuery.&amp;lt;init&gt;(SQLiteQuery.java:49)&lt;br /&gt;    at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:49)&lt;br /&gt;    at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1220)&lt;br /&gt;    at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1193)&lt;br /&gt;    at com.mycompany.myapp.MyActivity.onCreate(QuickTestActivity.java:22)&lt;br /&gt;    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)&lt;br /&gt;    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459)&lt;br /&gt;    ... 11 more&lt;/pre&gt;&lt;br /&gt;It turns out that SQLite throws this error if two conditions are met:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;You use a subquery (in this case, the select subquery from table t2).&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Your subquery has a table alias (in this case, referencing table t2 as "T2").&lt;/ol&gt;&lt;br /&gt;The solution is simple: &lt;span style="font-weight:bold;"&gt;alias the selected field names&lt;/span&gt;.  This query will work:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SELECT *&lt;br /&gt;FROM t1 A, (SELECT T2.id AS id FROM t2 T2) B&lt;br /&gt;WHERE A.id = B.id&lt;/pre&gt;&lt;br /&gt;I'm hardly an expert on SQL or SQLite, so I don't know whether this is a bug or just me running into undefined behavior.  I ran into this problem when doing JOINs on multiple tables, so as contrived as my example is, it can happen.  Regardless, it is easy to work around.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-4793483449850095097?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/4793483449850095097/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2011/05/using-subquery-columns-on-android-with.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/4793483449850095097'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/4793483449850095097'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2011/05/using-subquery-columns-on-android-with.html' title='Using Subquery Columns on Android with SQLite'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-8452161552720628027</id><published>2011-04-27T11:00:00.000-07:00</published><updated>2011-04-27T11:00:07.181-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Detecting the Keystore Signature in Code</title><content type='html'>Here's a problem I'm sure many of us have faced: you've got code that runs one way when you're developing and another way when you're releasing.  A few examples I've run into:&lt;br /&gt;&lt;br /&gt;- Logging during development, but disabled for release.&lt;br /&gt;- Targeting development vs. production servers.&lt;br /&gt;- &lt;a href="http://code.google.com/android/add-ons/google-apis/reference/com/google/android/maps/MapView.html"&gt;MapView&lt;/a&gt; requires an API key tied to the signing key.&lt;br /&gt;&lt;br /&gt;Before, I just setup static parameters in code that I'd have to remember to change before making a release build.  It's a pain and error prone.&lt;br /&gt;&lt;br /&gt;I've just determined a much better (and more automatic) way - detecting the &lt;a href="http://developer.android.com/guide/publishing/app-signing.html"&gt;signing keystore&lt;/a&gt; in code.  Through use of the &lt;a href="http://developer.android.com/reference/android/content/pm/PackageManager.html"&gt;PackageManager&lt;/a&gt; you can actually determine the &lt;a href="http://developer.android.com/reference/android/content/pm/Signature.html"&gt;Signature&lt;/a&gt; of the app that's currently running.&lt;br /&gt;&lt;br /&gt;First, you need to get the Signature of your release keystore.  Put this code into your application temporarily, then sign it with your release keystore and run it:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);&lt;br /&gt;Log.i("test", pi.signatures[0].toCharsString());&lt;/pre&gt;&lt;br /&gt;Take the output from logcat and put it into this method:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;private static final Signature SIG_RELEASE = new Signature("&amp;lt;YOUR SIGNATURE HERE&gt;");&lt;br /&gt;&lt;br /&gt;public static boolean isRelease(Context context) {&lt;br /&gt; try {&lt;br /&gt;  PackageManager pm = context.getPackageManager();&lt;br /&gt;  PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);&lt;br /&gt;  for (Signature sig : pi.signatures) {&lt;br /&gt;   if (sig.equals(SIG_RELEASE)) {&lt;br /&gt;    return true;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; catch (Exception e) {&lt;br /&gt;  Log.w("Exception thrown when detecting if app is signed by a release keystore.", e);&lt;br /&gt;&lt;br /&gt;  // Return true if we can't figure it out, just to play it safe&lt;br /&gt;  return true;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; return false;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Now you can always call isRelease() and check whether your app was signed by your release keystore.  No more having to edit files to create a release version of your app - just sign it with the correct keystore and you're ready to go!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-8452161552720628027?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/8452161552720628027/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2011/04/detecting-keystore-signature-in-code.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/8452161552720628027'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/8452161552720628027'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2011/04/detecting-keystore-signature-in-code.html' title='Detecting the Keystore Signature in Code'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-2677832292863924182</id><published>2011-04-13T09:00:00.001-07:00</published><updated>2011-06-28T14:55:04.683-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>HTML in TextViews</title><content type='html'>There's a trick I've been increasingly using as I've learned how powerful it is: HTML in TextViews.&lt;br /&gt;&lt;br /&gt;I used to think that TextViews were pretty plain; they contained a CharSequence, and that was it.  But it can actually contain a  &lt;a href="http://developer.android.com/reference/android/text/Spanned.html"&gt;Spanned&lt;/a&gt; (or its mutable cousin, &lt;a href="http://developer.android.com/reference/android/text/Spannable.html"&gt;Spannable&lt;/a&gt;) buffer type as well.  This means that there are a host of HTML tags that you can use to modify your TextView, which allows for different typefaces/functionality - all in the same TextView.  Check out what you can do with just one string resource in a TextView:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-EYTU2tBpJ0g/TaRsK3OzigI/AAAAAAAAAIA/NRTNknH8T44/s1600/html-textview.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 84px;" src="http://1.bp.blogspot.com/-EYTU2tBpJ0g/TaRsK3OzigI/AAAAAAAAAIA/NRTNknH8T44/s320/html-textview.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5594715570981538306" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Here's the string resource for above:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;string name="html"&amp;gt;This &amp;lt;b&amp;gt;is&amp;lt;/b&amp;gt; &amp;lt;i&amp;gt;all&amp;lt;/i&amp;gt; &amp;lt;sub&amp;gt;in&amp;lt;/sub&amp;gt; &amp;lt;u&amp;gt;&amp;lt;sup&amp;gt;one&amp;lt;/sup&amp;gt;&amp;lt;/u&amp;gt; &amp;lt;a href="http://google.com"&amp;gt;TextView&amp;lt;/a&amp;gt;.&amp;lt;/string&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://developer.android.com/resources/faq/commontasks.html#selectingtext"&gt;Android has tips on the basics of Html in TextViews&lt;/a&gt;, but it doesn't nearly cover some of the use cases.  It also doesn't have a reference on which tags are supported (scroll to bottom for a reference I created).&lt;br /&gt;&lt;br /&gt;Here are a few tips:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Links in TextViews&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The simplest way to add links to TextView is to use &lt;a href="http://developer.android.com/reference/android/widget/TextView.html#attr_android:autoLink"&gt;the android:autoLink attribute&lt;/a&gt;.  However, this only allows you to link visible URIs in the text and can sometimes lead to undesired situations (I had an app that was detecting the copyright dates "2009-2011" as a phone number).  By using the &amp;lt;a&amp;gt; tag, you can create links with any text that leads to any URI:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;string name="my_site"&amp;gt;&amp;lt;a href="http://idunnolol.com"&amp;gt;My Website&amp;lt;/a&amp;gt;&amp;lt;/string&amp;gt;&lt;/pre&gt;&lt;br /&gt;There's only one catch: when you try to use this in a TextView, it won't be clickable unless you set the movement method in code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;TextView myTextView = (TextView) findViewById(R.id.my_textview);&lt;br /&gt;myTextView.setMovementMethod(LinkMovementMethod.getInstance());&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Dynamic Html&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;All of the above examples have been with static text - that is, when the TextView's &lt;a href="http://developer.android.com/reference/android/widget/TextView.html#attr_android:text"&gt;android:text&lt;/a&gt; attribute is set in XML from a string resource.  What if you want to set a TextView's string resource in code?  This is especially important when you use string formatting (which I'm a big fan of).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling"&gt;Android discusses this already&lt;/a&gt;, but for posterity I'll repeat the process here:&lt;br /&gt;&lt;br /&gt;1. HTML-escape the string resource:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;string name="loud"&amp;gt;Loud text here: &amp;amp;lt;b&amp;gt;%s&amp;amp;lt;/b&amp;gt;&amp;lt;/string&amp;gt;&lt;/pre&gt;&lt;br /&gt;2. Use &lt;a href="http://developer.android.com/reference/android/text/Html.html#fromHtml(java.lang.String)"&gt;Html.fromHtml()&lt;/a&gt; to dynamically create a Spanned that a TextView can be styled with:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Spanned spanned = Html.fromHtml(context.getString(R.string.loud, "this is loud"));&lt;br /&gt;myTextView.setText(spanned);&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Handling Custom Tags&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So far so good, but I ran into a problem the other day: while the &amp;lt;strike&amp;gt; tag works if it's linked as a static string, it doesn't work when using Html.fromHtml().  I looked through the source code and discovered that Html.fromHtml() handles a different set of tags from static resources.  Luckily, there's a way to handle tags that Html.fromHtml() doesn't: &lt;a href="http://developer.android.com/reference/android/text/Html.TagHandler.html"&gt;the TagHandler interface&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;It's a little complex, but &lt;a href="http://stackoverflow.com/questions/4044509/android-how-to-use-the-html-taghandler"&gt;here's one possible solution for how to handle the &amp;lt;strike&amp;gt; tag&lt;/a&gt;.  (My own solution differed a bit, but the essentials are the same.)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Tags Supported in String Resources&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Tags in static string resources are parsed by &lt;a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob_plain;f=core/java/android/content/res/StringBlock.java;hb=HEAD"&gt;android.content.res.StringBlock&lt;/a&gt;, which is a hidden class.  I've looked through the class and determined which tags are supported:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&amp;lt;a&amp;gt; (supports attributes "href")&lt;br /&gt;&lt;li&gt;&amp;lt;annotation&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;b&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;big&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;font&amp;gt; (supports attributes "height", "size", "fgcolor" and "bicolor", as integers)&lt;br /&gt;&lt;li&gt;&amp;lt;i&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;li&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;marquee&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;small&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;strike&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;sub&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;sup&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;tt&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;u&amp;gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Tags Supported by Html.fromHtml()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;For some reason, Html.fromHtml() handles a different set of of tags than static text supports.  Here's a list of the tags (gleaned from &lt;a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob_plain;f=core/java/android/text/Html.java;hb=HEAD"&gt;Html.java's source code&lt;/a&gt;):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&amp;lt;a&amp;gt; (supports attribute "href")&lt;br /&gt;&lt;li&gt;&amp;lt;b&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;big&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;blockquote&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;br&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;cite&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;dfn&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;div&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;em&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;font&amp;gt; (supports attributes "color" and "face")&lt;br /&gt;&lt;li&gt;&amp;lt;i&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;img&amp;gt; (supports attribute "src".  Note: you have to include &lt;a href="http://developer.android.com/reference/android/text/Html.ImageGetter.html"&gt;an ImageGetter&lt;/a&gt; to handle retrieving a Drawable for this tag)&lt;br /&gt;&lt;li&gt;&amp;lt;p&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;small&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;strong&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;sub&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;sup&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;tt&amp;gt;&lt;br /&gt;&lt;li&gt;&amp;lt;u&amp;gt;&lt;/ul&gt;&lt;br /&gt;The font "color" attribute supports some color names (along with the normal integer-based color scheme):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;aqua&lt;br /&gt;&lt;li&gt;black&lt;br /&gt;&lt;li&gt;blue&lt;br /&gt;&lt;li&gt;fuchsia&lt;br /&gt;&lt;li&gt;green&lt;br /&gt;&lt;li&gt;grey&lt;br /&gt;&lt;li&gt;lime&lt;br /&gt;&lt;li&gt;maroon&lt;br /&gt;&lt;li&gt;navy&lt;br /&gt;&lt;li&gt;olive&lt;br /&gt;&lt;li&gt;purple&lt;br /&gt;&lt;li&gt;red&lt;br /&gt;&lt;li&gt;silver&lt;br /&gt;&lt;li&gt;teal&lt;br /&gt;&lt;li&gt;white&lt;br /&gt;&lt;li&gt;yellow&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-2677832292863924182?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/2677832292863924182/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2011/04/html-in-textviews.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/2677832292863924182'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/2677832292863924182'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2011/04/html-in-textviews.html' title='HTML in TextViews'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-EYTU2tBpJ0g/TaRsK3OzigI/AAAAAAAAAIA/NRTNknH8T44/s72-c/html-textview.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-830181159135320684</id><published>2011-04-06T09:00:00.000-07:00</published><updated>2011-04-12T09:04:36.475-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Image Sharing on Android</title><content type='html'>One of the sillier projects I've worked on in my spare time is &lt;a href="https://market.android.com/details?id=com.idunnolol.ragefaces"&gt;Rage Faces for Android&lt;/a&gt;.  It has almost no value to anyone, which is why it's free (&lt;a href="https://github.com/dlew/android-ragefaces"&gt;in every sense of the word&lt;/a&gt;).  However, it's been quite useful to me, as it's taught me a few things about sharing images on Android.  Overall, I think this is a good reason for Android developers (or any developer, really) to have fun side projects to work on - there's almost no app so trivial that you can't learn something from doing it.&lt;br /&gt;&lt;br /&gt;Here's three big ones I learned from working on Rage Faces.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Sharing Images&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The first thing it taught me is that you need space on the SD card in order to share images.  This makes sense, as sending images (typically) uses the &lt;a href="http://developer.android.com/reference/android/content/Intent.html#ACTION_SEND"&gt;ACTION_SEND&lt;/a&gt; intent along with a URI to the image.  You can't link a URI to a resource inside your application package, so you have to put it somewhere else (I chose the SD card).&lt;br /&gt;&lt;br /&gt;It's also possible to put the media into a ContentProvider using &lt;a href="http://developer.android.com/reference/android/provider/MediaStore.Images.Media.html#insertImage(android.content.ContentResolver, android.graphics.Bitmap, java.lang.String, java.lang.String)"&gt;MediaStore.Images.Media.insertImage()&lt;/a&gt;.  The downside is that the image is then inserted into the Gallery, which I didn't want just for sharing images (the whole point of the app is to avoid having to fill up your gallery with images you don't want, instead keeping them in one organized place).  You can avoid putting images in the Gallery by putting them into a folder on the SD card and creating a file ".nomedia".&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Picture Frame and ACTION_GET_CONTENT&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://developer.android.com/reference/android/content/Intent.html#ACTION_SEND"&gt;ACTION_SEND&lt;/a&gt; can be used to send the images to other applications.  The opposite of that is having the intent filter &lt;a href="http://developer.android.com/reference/android/content/Intent.html#ACTION_GET_CONTENT"&gt;ACTION_GET_CONTENT&lt;/a&gt; enabled; that way, other applications can request data from your app.  They both work essentially the same way, by passing a URI to the image in an Intent.  In the case of ACTION_SEND, you pass the URI in EXTRA_STREAM.  For ACTION_GET_CONTENT, it's actually provided in the return Intent's data.&lt;br /&gt;&lt;br /&gt;However, there is one exception: the standard Picture Frame widget doesn't play by these rules.  It uses ACTION_GET_CONTENT to allow anyone to hook into their system, but they won't read a stream URI.  If you try it, the picture frame widget crashes.  The relevant part of the crash log is here:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt; Caused by: java.lang.NullPointerException&lt;br /&gt;     at com.cooliris.media.PhotoAppWidgetProvider$PhotoDatabaseHelper.setPhoto(PhotoAppWidgetProvider.java:121)&lt;br /&gt;     at com.cooliris.media.PhotoAppWidgetConfigure.onActivityResult(PhotoAppWidgetConfigure.java:92)&lt;br /&gt;     at android.app.Activity.dispatchActivityResult(Activity.java:3908)&lt;br /&gt;     at android.app.ActivityThread.deliverResults(ActivityThread.java:2528)&lt;br /&gt;     ... 11 more&lt;/pre&gt;&lt;br /&gt;What they expect is for you to pass an extra called "data", as a Bitmap.  This means you need to pass back both the URI and the Bitmap as a Parcelable in the return Intent:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Intent return = new Intent();&lt;br /&gt;return.setData(myImageUri);&lt;br /&gt;return.putExtra("data", myBitmap);&lt;br /&gt;setResult(RESULT_OK, return);&lt;br /&gt;finish();&lt;/pre&gt;&lt;br /&gt;This neglect in following the rules of the road is, I assume, due to &lt;a href="http://www.cooliris.com/"&gt;Cool Iris&lt;/a&gt; both writing the standard Picture Frame widget and the standard Gallery application.  As a result, the two play together nicely, but you'll have to follow their rules to be part of the gang.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Messaging on HTC&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;There's another player who doesn't quite follow the rules: HTC Sense's messaging application.  Instead of filtering on the ACTION_SEND action like everyone else, HTC Sense's messaging application listens to "android.intent.action.SEND_MSG".  This means two things for you as a developer: You have to test for the existence of an app that accepts the action "android.intent.action.SEND_MSG", and then give the user an explicit option to send to HTC Sense's messaging application, in addition to the normal picker.&lt;br /&gt;&lt;br /&gt;You can test whether the HTC Sense messaging app is on the phone by querying for the intent filter:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Intent dummy = new Intent("android.intent.action.SEND_MSG");&lt;br /&gt;dummy.putExtra(Intent.EXTRA_STREAM, myImageUri);&lt;br /&gt;dummy.setType("image/png");&lt;br /&gt;PackageManager packageManager = getPackageManager();&lt;br /&gt;List&lt;ResolveInfo&gt; list = packageManager.queryIntentActivities(dummy, PackageManager.MATCH_DEFAULT_ONLY);&lt;br /&gt;if (list.size() &gt; 0) { // This means the HTC Sense messaging app is on the phone }&lt;/pre&gt;&lt;br /&gt;If you do have HTC Sense, you'll have to give your users the option to send messages to it.  This means another layer of dialogs, which is a pain to users.  At this point, I think the best solution is to stop using &lt;a href="http://developer.android.com/reference/android/content/Intent.html#createChooser(android.content.Intent, java.lang.CharSequence)"&gt;the Android chooser&lt;/a&gt; altogether.  As useful as it is, the Android ecosystem is too abused to keep using it.  In an ideal world, you'd only need one Intent to pass your data around, but that's just not been the case.  If you roll your own chooser (which is not particularly difficult), you can include multiple possible Intents to pass out.&lt;br /&gt;&lt;br /&gt;It's also worth noting that HTC Sense's messaging system doesn't implement ACTION_GET_CONTENT, so you can't have users insert your app's content into a conversation already in progress.  This is not an insignificant oversight - people have told me they switched text messaging apps because of this lacking feature!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-830181159135320684?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/830181159135320684/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2011/04/picture-frame-widget-and-getcontent.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/830181159135320684'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/830181159135320684'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2011/04/picture-frame-widget-and-getcontent.html' title='Image Sharing on Android'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-7476219324448867323</id><published>2011-03-09T11:00:00.001-08:00</published><updated>2011-03-09T11:00:03.961-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Tiled bitmaps in layer drawables</title><content type='html'>This is a bizarre bug that a coworker came across.&lt;br /&gt;&lt;br /&gt;There's a cool feature of XML drawables that allows you to tile bitmaps, using &lt;a href="http://developer.android.com/intl/fr/guide/topics/resources/drawable-resource.html#Bitmap"&gt;android:tileMode&lt;/a&gt;.  There is also the ability to layer drawables in XML, via &lt;a href="http://developer.android.com/intl/fr/guide/topics/resources/drawable-resource.html#LayerList"&gt;the layer list&lt;/a&gt;.  Naturally, there are some circumstances where you might want to layer bitmaps that are tiled.&lt;br /&gt;&lt;br /&gt;It turns out there is a bug in Android with trying to layer a tiled bitmap.  Every once in a while, a tiled bitmap in a layer list won't tile.  It's not every time (nor even that common) but anything less than a 100% success rate for graphics is problematic.&lt;br /&gt;&lt;br /&gt;The solution is to not define the bitmap drawable tileMode in XML, instead defining it dynamically in code.  Inflate the drawable in code somehow (either directly, or by inflating a layout that uses the drawable).  Grab the drawable, cast it to a &lt;a href="http://developer.android.com/intl/fr/reference/android/graphics/drawable/LayerDrawable.html"&gt;LayerDrawable&lt;/a&gt;, then retrieve the &lt;a href="http://developer.android.com/intl/fr/reference/android/graphics/drawable/BitmapDrawable.html"&gt;BitmapDrawable&lt;/a&gt; from the layer list (there are a few methods for it, like &lt;a href="http://developer.android.com/intl/fr/reference/android/graphics/drawable/LayerDrawable.html#getDrawable(int)"&gt;LayerDrawable.getDrawable()&lt;/a&gt;).  Then, set the tileMode in code via &lt;a href="http://developer.android.com/intl/fr/reference/android/graphics/drawable/BitmapDrawable.html#setTileModeXY(android.graphics.Shader.TileMode, android.graphics.Shader.TileMode)"&gt;BitmapDrawable.setTileModeXY()&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I've looked through the code and haven't figured out exactly why this bug occurs.  I would appreciate any insight; in the meantime, the fix works well enough.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-7476219324448867323?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/7476219324448867323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2011/03/tiled-bitmaps-in-layer-drawables.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/7476219324448867323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/7476219324448867323'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2011/03/tiled-bitmaps-in-layer-drawables.html' title='Tiled bitmaps in layer drawables'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-5405278083907663834</id><published>2011-03-03T11:00:00.000-08:00</published><updated>2011-03-03T11:00:02.166-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Custom styles in Android libraries</title><content type='html'>About nine months ago, Android added &lt;a href="http://developer.android.com/intl/fr/guide/developing/projects/projects-eclipse.html#ReferencingLibraryProject"&gt;library support&lt;/a&gt;.  It allows you to define multiple Android projects where (at build time) it combines them all into one.  These libraries have proven very useful in my work because there's a lot of common code between applications.  In some cases, I'm even reusing entire Activities.&lt;br /&gt;&lt;br /&gt;But this brings up a problem - how do you restyle an Activity in a library?  Say you've got a custom date picker in your library which you want to customize to each application's look and feel.  You could subclass the library's Activity and (in code) change the style, but then you miss out on all the advantages of &lt;a href="http://developer.android.com/intl/fr/guide/topics/resources/providing-resources.html#AlternativeResources"&gt;resource qualifiers&lt;/a&gt;.  You could try subclassing but using your own XML, but then you're tightly coupling your code to the library and will likely end up duplicating code.&lt;br /&gt;&lt;br /&gt;The answer is to use attributes and styles/themes.  Chances are you've already worked with styles and themes before (if you haven't, &lt;a href="http://developer.android.com/intl/fr/guide/topics/ui/themes.html"&gt;you should read up on them&lt;/a&gt;), but attributes aren't commonly needed when you're working on a one-off application.  Attributes are a tad under-documented, but with attributes, you can define references which are filled in later by styles.&lt;br /&gt;&lt;br /&gt;Attributes are one level of abstraction beyond the normal use of XML references in Android.  Let's take the simple example of a background: the most direct way to set a background is directly in code (android:background="#FF0000").  Instead of putting the background in the code, you can create a color resource (&amp;lt;color name="red"&amp;gt;#FF0000&amp;lt;/color&amp;gt;) then reference that (android:background="@color/red").  Going one level of abstraction higher is to create an attribute(&amp;lt;attr name="red" format="color" /&amp;gt;), which doesn't directly define the color but can be set by a style or theme (android:background="?style/red").&lt;br /&gt;&lt;br /&gt;A high-level layout of the solution looks like this:&lt;br /&gt;&lt;br /&gt;1. Define attributes for what XML values you want to customize.&lt;br /&gt;&lt;br /&gt;2. Link to those attributes in the library's XML.&lt;br /&gt;&lt;br /&gt;3. In the application using the library, create a style which defines values for those attributes.&lt;br /&gt;&lt;br /&gt;4. Apply that style as a theme for the application.&lt;br /&gt;&lt;br /&gt;Here's a specific example.  Suppose we want the background of an Activity to be black in one application and white in another.&lt;br /&gt;&lt;br /&gt;1. Create a file in /values/ called "attrs.xml" and add an attribute for the background color:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;resources&amp;gt;&lt;br /&gt; &amp;lt;attr name="myBackground" format="reference|color" /&amp;gt;&lt;br /&gt;&amp;lt;/resources&amp;gt;&lt;/pre&gt;&lt;br /&gt;The "format" field defines what sort of values can be entered for this attribute.  There's no official documentation, but &lt;a href="http://stackoverflow.com/questions/3441396/android-defining-custom-attrs/3441986#3441986"&gt;people have written about possible values&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;2. Now let's create a simple layout in /layout/:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;LinearLayout android:layout_height="fill_parent" android:layout_width="fill_parent"&lt;br /&gt;   android:background="?attr/myBackground" /&amp;gt;&lt;/pre&gt;&lt;br /&gt;Note how you use the "?" instead of "@" when referencing an attribute.&lt;br /&gt;&lt;br /&gt;3. Now switch away from the library to the application using it.  Inside, we create a "styles.xml" in /values/, and create a style to fill in the values for the attributes we defined in the library:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;resources&amp;gt;&lt;br /&gt; &amp;lt;style name="LibraryTheme"&amp;gt;&lt;br /&gt;  &amp;lt;item name="myBackground"&amp;gt;@android:color/black&amp;lt;/item&amp;gt;&lt;br /&gt; &amp;lt;/style&amp;gt;&lt;br /&gt;&amp;lt;resources&amp;gt;&lt;/pre&gt;&lt;br /&gt;4. Now in AndroidManifest.xml, we set the overall application theme:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;application android:icon="@drawable/icon" android:label="My App"&lt;br /&gt; android:theme="@style/LibraryTheme"&amp;gt;&lt;/pre&gt;&lt;br /&gt;If you wanted to set another theme for the application already, use &lt;a href="http://developer.android.com/intl/fr/guide/topics/ui/themes.html#DefiningStyles"&gt;style parenting&lt;/a&gt; to lump both styles together.&lt;br /&gt;&lt;br /&gt;Ta-da!  Now you've written a layout in a library that can be customized in each application that uses it.  You can repeat steps #3 and #4 for any number of applications, setting the background to a different color for each.&lt;br /&gt;&lt;br /&gt;There is only one caveat about this method, which is that you need to define &lt;span style="font-weight:bold;"&gt;all&lt;/span&gt; the attributes in each application that uses the library.  If you miss an attribute, your app will blow up in your face when the app tries to use it.  What's worse, the stack trace when this occurs for missing attributes is complete gibberish and unrelated to the actual problem, making it difficult to debug.&lt;br /&gt;&lt;br /&gt;This technique can be used for any attributes from a library.  Use your imagination; at this point I've just been using it to style layouts, but you can apply attributes to just anything you can define in XML.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-5405278083907663834?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/5405278083907663834/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2011/03/custom-styles-in-android-libraries.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/5405278083907663834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/5405278083907663834'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2011/03/custom-styles-in-android-libraries.html' title='Custom styles in Android libraries'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-458128312554039391</id><published>2011-02-25T11:00:00.000-08:00</published><updated>2011-02-25T11:00:05.758-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Sending Emails on Android, Take 2</title><content type='html'>A while back, I wrote &lt;a href="http://daniel-codes.blogspot.com/2010/08/sending-emails-on-android.html"&gt;a post about how to send specifically send only emails on Android&lt;/a&gt;.  Unfortunately, the solution I posted does not work 100% of the time.&lt;br /&gt;&lt;br /&gt;My previous solution was a hack and (as hacks are wont to do) it came back to bite my in the ass.  It turns out that some email programs have buggy implementations of the mailto protocol.  This led us to some users not getting the body content when using the mailto trick.&lt;br /&gt;&lt;br /&gt;The correct solution is to use the normal ACTION_SEND intent, but setting the type to "message/rfc822", so only programs that implement emails will accept the intent.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Intent intent = new Intent(Intent.ACTION_SEND);&lt;br /&gt;intent.setType("message/rfc822");&lt;br /&gt;intent.putExtra(Intent.EXTRA_EMAIL, "foo@bar.com");&lt;br /&gt;intent.putExtra(Intent.EXTRA_SUBJECT, "A Subject");&lt;br /&gt;intent.putExtra(Intent.EXTRA_TEXT, "Here's my message.");&lt;br /&gt;startActivity(intent);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Not only does this work more often than my hack, but it's far more elegant, too.&lt;br /&gt;&lt;br /&gt;One other choice I've made recently is to just use startActivity() on the original intent instead of creating a chooser first.  That way, the user can opt for a default email program to use instead of being asked each time which app should handle the intent.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-458128312554039391?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/458128312554039391/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2011/02/sending-emails-on-android-take-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/458128312554039391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/458128312554039391'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2011/02/sending-emails-on-android-take-2.html' title='Sending Emails on Android, Take 2'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-4211713326407596025</id><published>2011-02-23T11:00:00.000-08:00</published><updated>2011-02-23T11:00:10.362-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Drop shadow text</title><content type='html'>Here's a little tip for creating a decent drop shadow effect in a TextView.&lt;br /&gt;&lt;br /&gt;The issue I ran into initially when trying the shadow effects on TextView is that I could rarely get it to look right - instead of a clean, single pixel shadow, it'd always come out fuzzy.  It turns out that the secret is that shadowRadius can be a float, and that you should make that float very small.  Here's an example style you can apply to TextViews:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;style name="GreyEtchedText"&amp;gt;&lt;br /&gt; &amp;lt;item name="android:textColor"&amp;gt;#2F3541&amp;lt;/item&amp;gt;&lt;br /&gt; &amp;lt;item name="android:shadowColor"&amp;gt;#88FFFFFF&amp;lt;/item&amp;gt;&lt;br /&gt; &amp;lt;item name="android:shadowRadius"&amp;gt;.1&amp;lt;/item&amp;gt;&lt;br /&gt; &amp;lt;item name="android:shadowDx"&amp;gt;0&amp;lt;/item&amp;gt;&lt;br /&gt; &amp;lt;item name="android:shadowDy"&amp;gt;1&amp;lt;/item&amp;gt;&lt;br /&gt;&amp;lt;/style&amp;gt;&lt;/pre&gt;&lt;br /&gt;Of course, you can use whatever colors you want for the foreground/shadow.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-4211713326407596025?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/4211713326407596025/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2011/02/drop-shadow-text.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/4211713326407596025'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/4211713326407596025'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2011/02/drop-shadow-text.html' title='Drop shadow text'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-4606276523377475997</id><published>2010-10-11T09:00:00.000-07:00</published><updated>2010-10-11T09:03:40.904-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='piracy'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>The True Problem With Google's License Verification Library (LVL)</title><content type='html'>Google recently introduced a new method to fight piracy on the Android Market, the License Verification Library (LVL).  It replaces the old system copy protection system, wherein your APKs would be put in a folder that you can't access.  Unless you root.  Oh, and anyone who can copy that APK off can then give it to someone else to put on their device, too.  It was so weak, it was almost non-existant.&lt;br /&gt;&lt;br /&gt;At first I took the news of LVL to be a great next step.  In case you've not been paying attention, LVL puts the copy protection methods in the app itself - a form of DRM.  Your app would now communicate with Google's servers to authorize use of the app.  You'd no longer prevent a user from copying/transferring APKs, but doing so would be pointless because they wouldn't run on someone else's phone unless they were authorized.&lt;br /&gt;&lt;br /&gt;However, any DRM measure can be cracked, it's just a matter effort.  Only a few weeks after LVL was released &lt;a href="http://www.androidpolice.com/2010/08/23/exclusive-report-googles-android-market-license-verification-easily-circumvented-will-not-stop-pirates/"&gt;it was shown how easy it was to modify bytecode to bypass the authorization altogether&lt;/a&gt;.  Google responded with a series of posts (&lt;a href="http://android-developers.blogspot.com/2010/08/licensing-server-news.html"&gt;1&lt;/a&gt;, &lt;a href="http://android-developers.blogspot.com/2010/09/securing-android-lvl-applications.html"&gt;2&lt;/a&gt;, &lt;a href="http://android-developers.blogspot.com/2010/09/proguard-android-and-licensing-server.html"&gt;3&lt;/a&gt;) that explained how to circumvent this early crack in their armor.  Essentially, it all boils down to different methods of obsfucating your code, &lt;a href="http://en.wikipedia.org/wiki/Security_through_obscurity#Arguments_against"&gt;which as we all know can still fail&lt;/a&gt; - and the steps Google proposes for protecting your APKs complicates your code and build process.  As happens with most DRM, this has become a game of cat-and-mouse between Google and pirates, using ever-escalating tactics to one-up the other.&lt;br /&gt;&lt;br /&gt;But there's a much bigger issue at the root of the entire system: &lt;span style="font-weight:bold;"&gt;The LVL library only works on apps sold through Google's Android Market.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The whole point of LVL is that the APK itself has some protection built in, so copying it around will not be an issue.  But the LVL library only authenticates users who bought the app using the Android Market.  If a user buys your app on a third-party store, then the LVL library will reject them.  This means that in order to release on a third-party store, you'd have to ditch the LVL library for those builds - and thus defeat your own DRM.&lt;br /&gt;&lt;br /&gt;There are a lot of Android Market alternatives now and more on the way.  There are some publicly available are sites like &lt;a href="http://slideme.org/"&gt;SlideME&lt;/a&gt; and &lt;a href="http://appslib.com/"&gt;AppsLib&lt;/a&gt; (and possibly soon, &lt;a href="http://www.wired.com/gadgetlab/2010/10/amazon-android-app-store/"&gt;Amazon&lt;/a&gt;).  There are also some carrier-specific app stores - &lt;a href="http://androidandme.com/2010/09/carriers/v-cast-app-store-to-compete-with-official-android-market/"&gt;Verizon just launched V-Cast&lt;/a&gt; and in Europe, both &lt;a href="http://www.androidguys.com/2009/12/11/orange-application-store-with-click-and-go/"&gt;Orange&lt;/a&gt; and &lt;a href="http://mobile.slashdot.org/story/10/04/10/1915256/Android-Gets-Carrier-Operated-European-App-Store"&gt;Vodafone&lt;/a&gt; have their own offerings.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://android-developers.blogspot.com/2010/09/more-countries-more-sellers-more-buyers.html"&gt;Android Market's recent expansion&lt;/a&gt; is, I believe, partially a response to this problem, but it comes too late.  If the Market was more widespread and accessible early on then there would not have been an incentive to create and sell apps on alternative app stores in the first place; but now the momentum is there.&lt;br /&gt;&lt;br /&gt;As much as it pains me, my conclusion is that it's better off to just go without any copy protection whatsoever.  It does not makes sense to limit yourself to one store when potential customers could browsing another.  Also, people who pirate will find a way to pirate, regardless your DRM; don't worry about them, and just worry about the people who would buy legit in the first place.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-4606276523377475997?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/4606276523377475997/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2010/10/true-problem-with-googles-license.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/4606276523377475997'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/4606276523377475997'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2010/10/true-problem-with-googles-license.html' title='The True Problem With Google&apos;s License Verification Library (LVL)'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-5119174162260505166</id><published>2010-09-13T07:30:00.001-07:00</published><updated>2010-09-13T07:36:58.946-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='palm'/><title type='text'>Palm Shaking</title><content type='html'>This is a small point, but I could find no one else who has ever commented on this issue before: minor filtering with Palm shake events via the Accelerometer.&lt;br /&gt;&lt;br /&gt;It's pretty easy to setup an event listener for the "shaking" event, just follow &lt;a href="http://developer.palm.com/index.php?option=com_content&amp;view=article&amp;id=1554&amp;Itemid=261"&gt;the instructions here&lt;/a&gt;.  However there are two partially tricky points to getting it right.&lt;br /&gt;&lt;br /&gt;First, you don't want to listen to every shake event.  When listening to the "shaking" event, there's a parameter event.magnitude that you should use to filter.  If you listen to all shaking events, then it's easy for your app to pick up shakes that are unintentional, such as someone putting their phone on a table or back in their pocket.  I've found that a good number to pick is 2.08 - the reason for this being that the emulator, when you press "f5", simulates a shake event of magnitude ~2.09.  Set your filter any higher and you won't be able to test with the emulator, but with a magnitude of at least 2, the user's shake has to be relatively intentional.&lt;br /&gt;&lt;br /&gt;The second catch is that the shake event, unlike most events you might listen to in a scene, can happen in any scene on the stack.  With buttons and the like, you can be lazy and not disable their listeners when you add another scene to the stack because those buttons are not accessible.  You cannot be so lazy with the "shaking" event; even if you push another scene, your previous event listener will still fire off whenever the phone shakes.  So make sure to disable any "shaking" handlers before you launch a new scene.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-5119174162260505166?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/5119174162260505166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2010/09/palm-shaking.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/5119174162260505166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/5119174162260505166'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2010/09/palm-shaking.html' title='Palm Shaking'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-7789513877792998559</id><published>2010-08-19T11:00:00.000-07:00</published><updated>2011-02-26T06:59:33.329-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Sending Emails on Android</title><content type='html'>UPDATE 2/25/2011: &lt;a href="http://daniel-codes.blogspot.com/2011/02/sending-emails-on-android-take-2.html"&gt;There is a much better solution to the problem I have now posted&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In this post I outline how to share information solely via email.  There's some background information up front; if you're interested in just how to share data only with email apps, skip to the bottom.&lt;br /&gt;&lt;br /&gt;One thing that users like to do a lot is share information.  They want to be able to post things to Twitter, Facebook, or email their friends.  Android makes this really simple via the ACTION_SEND intent:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Intent intent = new Intent(Intent.ACTION_SEND);&lt;br /&gt;intent.setType("text/plain");&lt;br /&gt;intent.putExtra(Intent.EXTRA_SUBJECT, "A Subject");&lt;br /&gt;intent.putExtra(Intent.EXTRA_TEXT, "Here's my message.");&lt;br /&gt;Intent mailer = Intent.createChooser(intent, null);&lt;br /&gt;startActivity(mailer);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;There's only one issue that arises from this method: any app that's registered to receive the ACTION_SEND intent will show up in the chooser.  This means some oddball choices pop up ("Bluetooth" being the weirdest one for me), but that's kind of unavoidable.  The bigger issue that I ran into was a matter of message length; Twitter messages are inherently shorter than what I might send via email.  If I knew I only had 140 characters, the share message should be a lot shorter; but with the freedom of email, I can template out detailed information.&lt;br /&gt;&lt;br /&gt;One solution that I've used is to pop up a dialog which asks the user whether they want a short template message or a long one, with examples of where to use each - short (Twitter, Facebook) vs. long (email).&lt;br /&gt;&lt;br /&gt;A bigger problem arises when you want to only share information via emails.  Suppose the information your app shares is just too complex for a short Twitter message - an unfortunate circumstance for the Twitter population but a fact of some apps.  How do you force the chooser to select email?  It turns out that there's a very roundabout way of handling this issue by taking advantage of the mailto protocol in Android:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Intent intent = new Intent(Intent.ACTION_VIEW);&lt;br /&gt;Uri data = Uri.parse("mailto:?subject=" + subject + "&amp;body=" + body);&lt;br /&gt;intent.setData(data);&lt;br /&gt;startActivity(intent);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Using the data scheme that you do, only email apps pick up on this scheme.  As a result, only email apps will open.  You may not even force the user to go through a chooser if they have a default email app chosen already.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-7789513877792998559?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/7789513877792998559/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2010/08/sending-emails-on-android.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/7789513877792998559'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/7789513877792998559'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2010/08/sending-emails-on-android.html' title='Sending Emails on Android'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-6054776361825272239</id><published>2010-08-11T18:00:00.000-07:00</published><updated>2010-08-11T18:00:01.424-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='palm'/><title type='text'>Palm and CRUD</title><content type='html'>I've been working with some Calendar integration on Palm and it's a mess because their documentation is out of line with reality.  The basic CRUD API I have some issues with because of their ill-defined error codes - for example, if I want to find out if I've already created a calendar event or not I use service getEvent, but the error it returns if the event doesn't exist is the same error as if you input an invalid parameter.  But beyond that there's simply mis-documented code or bad samples they give out that don't work.&lt;br /&gt;&lt;br /&gt;Some things I've found:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;createAccount&lt;/b&gt; - There's a lot of different hogwash in the documentation and samples, all of it wrong in one form or another.  The icons parameter should actually be setup thus:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;icons: {&lt;br /&gt;    "32x32": Mojo.appPath + "icon32.png",&lt;br /&gt;    "48x48": Mojo.appPath + "icon48.png"&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Mojo.appPath is required, and then after that you link your icons from wherever they are in your project.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Accounts Linked to Email&lt;/b&gt; - There was some initial confusion because when I created an account with domain of "idunnolol" with username "dlew", Palm would give me the option to log into an email account "dlew@idunnolol.com".  The fact of the matter is, Palm always thinks the account you create is linked to an email, even if you're just using for email.  Sorry!  You'll just have to get over this.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;updateEvent&lt;/b&gt; - The documentation would have you believe that it takes two parameters - event and trackChanges.  But when you do that, you get the error "ErrorGenericInvalidParameter: Missing Required Parameter: event.startTimestamp".  What you really need to do is setup your event as the entire parameters.  I have no idea where trackChanges could go (but I don't use it):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;parameters: {&lt;br /&gt;    eventId: '1241251801',&lt;br /&gt;    startTimestamp: '92912911212',&lt;br /&gt;    endTimestamp: '1299592929291',&lt;br /&gt;    ... etc&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Besides those three things, I found that the code was much easier to work with if I setup a chain of onSuccess(), onFailure() calls that always guaranteed that I had a valid account and calendar.  When I try to create a calendar event, first it checks that I have an account, then it checks if I have a calendar, and creates them if it doesn't exist.  It's nice to have this built into your implementation because users can (at any time) delete your account if they feel like it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-6054776361825272239?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/6054776361825272239/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2010/08/palm-and-crud.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/6054776361825272239'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/6054776361825272239'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2010/08/palm-and-crud.html' title='Palm and CRUD'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-3112933697650020857</id><published>2010-07-23T10:52:00.000-07:00</published><updated>2010-07-23T11:00:49.925-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>The Curious Case of the Missing HttpsURLConnection</title><content type='html'>I ran into an Android OS bug recently that is pretty harsh related to HTTPS connections.  Basically, what happens is this:&lt;br /&gt;&lt;br /&gt;1. You want to setup a connection between the phone and a server, and you need to control both the input and the output.  As a result, you use URL.openConnection(), with setDoInput() and setDoOutput() set to true:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;URL url = new URL("https://blahblahblah.com");&lt;br /&gt;URLConnection conn = url.openConnection();&lt;br /&gt;conn.setDoInput(true);&lt;br /&gt;conn.setDoOutput(true);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;At some point you use both conn.getOutputStream() to write to the stream, then conn.getInputStream() to get the response.&lt;br /&gt;&lt;br /&gt;2. You're doing an HTTPS connection.  Some people report this happening on normal HTTP, but I've only seen it happen on HTTPS.&lt;br /&gt;&lt;br /&gt;3. The first request goes through fine and dandy.&lt;br /&gt;&lt;br /&gt;4. The second time you try to make the request, the connection doesn't send any data out and doesn't receive any data; it looks like it happens instantly.  If you cast to an HttpURLConnection, conn.getResponseCode() returns -1 instead of anything meaningful.&lt;br /&gt;&lt;br /&gt;In other words, every other request, the request fails outright.  &lt;a href="http://code.google.com/p/android/issues/detail?id=7786&amp;can=1&amp;q=httpsurlconnection&amp;colspec=ID%20Type%20Status%20Owner%20Summary%20Stars"&gt;This is a noted bug in Android&lt;/a&gt;, but it isn't fixed yet in any released versions.  Even when it's fixed, you'll still have to deal with this on older versions of Android.&lt;br /&gt;&lt;br /&gt;There are a few workarounds.  The first is to simply not use URLConnection; if you can find some way around it, avoid it.  The second is to repeatedly make the same request until it works; it's a little too much of a hack for my tastes, but it'll work.&lt;br /&gt;&lt;br /&gt;Then there's the third workaround, which I do not claim to understand why it fixes the issue but it does.  Just set this setting when your application begins:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;System.setProperty("http.keepAlive", "false");&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Unfortunately this has some drawbacks (keep-alive is a good thing normally), but in comparison to mysteriously failed requests I'll ditch it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-3112933697650020857?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/3112933697650020857/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2010/07/curious-case-of-missing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/3112933697650020857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/3112933697650020857'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2010/07/curious-case-of-missing.html' title='The Curious Case of the Missing HttpsURLConnection'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-9123119136509045420</id><published>2010-07-15T14:14:00.001-07:00</published><updated>2010-07-15T14:27:06.731-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>How to Change a ListView Row's Background but Keep Normal Android Selector</title><content type='html'>Here's a problem which I thought would be super complex to solve but is actually rather simple.  The solution assumes a bit of knowledge of ListView row types, though; if you've never dealt with them, &lt;a href="http://android.amberfog.com/?p=296"&gt;here's a primer&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Suppose I want to make a ListView composed of similar rows with different backgrounds.  This is simple enough; create a ListView with multiple item view types and then as each one loads, set a different background resource based on their type.  The catch is that you want the selected/pressed graphics to still look the same.  An example of this would be an email app; you want read and unread emails to look different, but when the item is selected or pressed you still want that tacky orange (or whatever your system uses).&lt;br /&gt;&lt;br /&gt;The naive solution of simply setting the background resource does not work, because it completely blocks the standard selector:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;// Bad; don't use this!&lt;br /&gt;convertView.setBackgroundResrouce(R.color.some_color);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I was able to glean the correct answer by examining the Email app's source code (thank goodness for open source).  What you want to do is create a background which is transparent whenever the list's background selector kicks in, but uses your custom background color (or resource) when it's not.  Here's what the drawable xml looks like:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&amp;lt;selector xmlns:android=&amp;quot;http://schemas.android.com/apk/res/android&amp;quot;&amp;gt;&lt;br /&gt;  &amp;lt;item android:state_window_focused=&amp;quot;false&amp;quot; android:state_selected=&amp;quot;true&amp;quot; android:drawable=&amp;quot;@android:color/transparent&amp;quot; /&amp;gt; &lt;br /&gt;  &amp;lt;item android:state_selected=&amp;quot;true&amp;quot; android:drawable=&amp;quot;@android:color/transparent&amp;quot; /&amp;gt; &lt;br /&gt;  &amp;lt;item android:state_pressed=&amp;quot;true&amp;quot; android:state_selected=&amp;quot;false&amp;quot; android:drawable=&amp;quot;@android:color/transparent&amp;quot; /&amp;gt; &lt;br /&gt;  &amp;lt;item android:state_selected=&amp;quot;false&amp;quot; android:drawable=&amp;quot;@color/some_color&amp;quot; /&amp;gt; &lt;br /&gt;  &amp;lt;/selector&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With this in hand, you can set your view's background like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;convertView.setBackgroundResrouce(R.drawable.some_row_background);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Just apply the above XML to multiple files, and you'll be able to set different types of backgrounds but still preserve the ListView's selector background.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-9123119136509045420?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/9123119136509045420/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2010/07/how-to-change-listview-rows-background.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/9123119136509045420'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/9123119136509045420'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2010/07/how-to-change-listview-rows-background.html' title='How to Change a ListView Row&apos;s Background but Keep Normal Android Selector'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-2661630467866623238</id><published>2010-06-09T13:22:00.000-07:00</published><updated>2010-06-09T13:26:17.206-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='webos'/><title type='text'>app-assistant handleLaunch and large numbers</title><content type='html'>I've been working in the world of webOS for the last month, and while there's a lot of new APIs to learn I've not really run into an amazing "gotcha" moment until now.&lt;br /&gt;&lt;br /&gt;In our app, we show some banner notifications.  When you click on that banner notification, it takes you to a page with some information.  On that page is some information about time, which means we're passing a timestamp in launchArguments (part of &lt;a href="http://www.weboshelp.net/webos-mojo-development-resources/api-reference/555-mojocontrollerappcontrollershowbannerbannerparamsbannerparams-launcharguments-category-?catid=65:mojocontrollerappcontroller"&gt;showBanner()&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;The intriguing thing that happens is that, unlike the rest of Palm (nay, JS in general), integers passed using launchArguments are limited to 32 bits.  Guess what's typically larger than 32 bits: timestamps measured in milliseconds from the epoch.  It confounds me that such a limitation exists, but any number larger than 2^31 - 1 is converted to the max integer instead.&lt;br /&gt;&lt;br /&gt;The solution?  Wrap the number as a string.  Bizarre that they'll let any length string through, but not a number larger than an integer.  If anyone can explain, please do.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-2661630467866623238?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/2661630467866623238/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2010/06/app-assistant-handlelaunch-and-large.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/2661630467866623238'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/2661630467866623238'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2010/06/app-assistant-handlelaunch-and-large.html' title='app-assistant handleLaunch and large numbers'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-1999291093909279143</id><published>2010-06-03T11:00:00.000-07:00</published><updated>2010-06-08T10:32:48.837-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Android 1.5 Widgets, Google Maps API and VerifyError</title><content type='html'>Here are two things that don't get along: Android 1.5 widgets and the Google Maps API.  Trying to include both in your application results in a dreaded VerifyError when you try to create a widget on the home screen.&lt;br /&gt;&lt;br /&gt;The basic cause, &lt;a href="http://groups.google.com/group/android-developers/msg/904fae350cda3ebc"&gt;according to Dianne Hackborn&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Hi, it looks like there is a bug initializing a process when it is launched to handle a broadcast sent to an explicit component.  In this case its shared libraries are not linked to it. &lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;This manifests itself in two ways, basically whenever a class imports any com.google.android.maps classes.  The first is if your widget needs to initialize any classes that import maps.  The second is if any of your broadcast receivers initialize any classes that import maps.  In both cases, the widget will attempt to load com.google.android.maps and fail, throwing a VerifyError.&lt;br /&gt;&lt;br /&gt;The solution to the first is to not have any references to com.google.android.maps.  This is sometimes easier said than done, but if you divide out your maps classes from your widget classes this is doable.  Worst-case scenario, you can use reflection to call your maps classes; they will throw exceptions when the widget tries to load them, but hopefully you don't need them anyways.  (This happened when I had my Application try to clear some maps cache data onLowMemory()).&lt;br /&gt;&lt;br /&gt;The solution to the second is &lt;a href="http://code.google.com/p/android/issues/detail?id=2886#c3"&gt;to put all of your receivers in a different process&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-1999291093909279143?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/1999291093909279143/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2010/06/android-15-widgets-google-maps-api-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/1999291093909279143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/1999291093909279143'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2010/06/android-15-widgets-google-maps-api-and.html' title='Android 1.5 Widgets, Google Maps API and VerifyError'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-5368026105993501876</id><published>2010-06-02T07:26:00.000-07:00</published><updated>2010-06-02T07:36:05.658-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Headset Blocker</title><content type='html'>If you own a Nexus One, you may be familiar with this scenario: you load up your N1 with some rocking tunes.  You plug in your headphones and go out exercising with it.  Suddenly, the tunes start skipping.  Why?&lt;br /&gt;&lt;br /&gt;The problem is that the headset jack is interpreting what you are doing as a control signal.  The exact cause hasn't been shown to me yet; there are a number of theories from a lack of software noise filtering to the difference between TRS and TRRS connectors.  Regardless, it's a serious issue - some people can't even listen to music in their cars with the N1 because it skips so much.&lt;br /&gt;&lt;br /&gt;Luckily, in the last few days I was shown an answer.  The control commands from headsets are actually sent out as a chain broadcast, MEDIA_BUTTON.  As with any chain broadcast, your receiver can abort the broadcast.  So the simple solution is to make a BroadcastReceiver with an ultra-high priority that intercepts and aborts the MEDIA_BUTTON broadcast.&lt;br /&gt;&lt;br /&gt;To that end, I've put a small app on the Market, Headset Blocker.  It's a small, free widget that allows you to enable/disable blocking of the MEDIA_BUTTON broadcast.  You can get it here:  &lt;br /&gt;&lt;br /&gt;&lt;img src="http://chart.apis.google.com/chart?cht=qr&amp;chs=200x200&amp;chld=L|0&amp;chl=market://details%3Fid%3Dcom.idunnolol.headsetblocker" border="0" alt="QR code for Headset Blocker" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/android-headset-blocker/"&gt;The source code is here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;One interesting choice I had to make when writing this app was how to toggle the blocking.  I had one of two options:&lt;br /&gt;&lt;br /&gt;1. Use SharedPreferences to track when blocking is enabled.  Capture all MEDIA_BUTTON broadcasts, but only abort when the preference is enabled.&lt;br /&gt;&lt;br /&gt;2. Enable/disable my app's BroadcastReceiver.  It will always abort when enabled, but is only enabled via the widget.&lt;br /&gt;&lt;br /&gt;I went with the latter option, as I felt that would save just a little bit more battery when disabled (as the BroadcastReceiver itself would be disabled).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-5368026105993501876?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/5368026105993501876/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2010/06/headset-blocker.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/5368026105993501876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/5368026105993501876'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2010/06/headset-blocker.html' title='Headset Blocker'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-3920204685356386181</id><published>2010-05-23T12:12:00.000-07:00</published><updated>2010-05-23T10:12:36.666-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Android: Serializable Considered Harmful</title><content type='html'>This is an old problem, but anyone I can convince not to use Serializable on Android will help the system as a whole.&lt;br /&gt;&lt;br /&gt;There is some data saved between launches of my application - a list of complex objects.  When I first learned Android I assumed that the fastest way to get the app up and running would be to slap &lt;a href="http://developer.android.com/reference/java/io/Serializable.html"&gt;Serializable&lt;/a&gt; on my data classes and call it a day.  It certainly got things up and running quickly, but with some serious performance issues.  I never noticed this in early testing because I only saved a few objects at a time.  However, when I got up to 5+ objects the system started to slow down, and at 10+ it ran like molasses.  There's not normally that many objects, but it's still something to be concerned about.  It's not like I was doing anything complex, however - it was simply that Serializable is that slow.&lt;br /&gt;&lt;br /&gt;Not only that, but I had to live in constant fear of changing the data structure.  If enough things changed I would have to write my own serialization processes to work with backwards compatibility.  Yuck.&lt;br /&gt;&lt;br /&gt;I switched to using JSON to store all my data and this decision has made my life so much better.  Android comes with &lt;a href="http://developer.android.com/reference/org/json/package-summary.html"&gt;a good JSON library&lt;/a&gt; and it's pretty simple to add JSON support to classes.  JSON is much, much faster than serialization and comes out with much smaller results, too (you can even GZip the results to make them smaller still).  To put it in perspective, the save time is now unnoticeable, whereas I could count the seconds using Serializable; and the file size was reduced by an order of magnitude.&lt;br /&gt;&lt;br /&gt;Not only is it better for saving data, it's a very easy way to pass data between components.  When I first started, I saw three ways to pass data between components: Serializing, parceling, or storing data in the Application context.  Serializing was proving too slow, parceling is really a pain in the ass, and the last solution is harder to integrate with later on (no one else can call your component).  JSON has proven a much better solution - it's fast enough to get by, but not nearly the pain in the ass that Parcels are.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-3920204685356386181?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/3920204685356386181/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2010/01/android-serializable-considered-harmful.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/3920204685356386181'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/3920204685356386181'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2010/01/android-serializable-considered-harmful.html' title='Android: Serializable Considered Harmful'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-744114330148549509</id><published>2010-05-03T07:46:00.000-07:00</published><updated>2010-05-25T13:52:29.947-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>ViewFlipper "Receiver not registered" Error</title><content type='html'>There is this error that plagued me for a few months.  If you use a ViewFlipper, this error will pop up on Android 2.1 devices every once in a while:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;java.lang.IllegalArgumentException: Receiver not registered: android.widget.ViewFlipper$1@44b6ab90&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It does not affect any devices before 2.1, and so what was troubling at first was that the 2.1 source code wasn't yet released so I could't diagnose the issue.  Everyone knew it had to do with orientation changes on an Activity with a ViewFlipper, but why exactly it happens isn't clear (&lt;a href="http://code.google.com/p/android/issues/detail?id=6191"&gt;I took my own stab at it, but was incorrect about the exact cause&lt;/a&gt;; it just seems to happen randomly).&lt;br /&gt;&lt;br /&gt;Thankfully, &lt;a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob_plain;f=core/java/android/widget/ViewFlipper.java;hb=HEAD"&gt;the source has been available now for a while&lt;/a&gt;.  Obviously, the problem is that onDetachedFromWindow() is somehow being called before onAttachedToWindow(); but how do we come up with a workaround until it is fixed at Google?&lt;br /&gt;&lt;br /&gt;One simple solution is to override onDetachedFromWindow() and catch the error from super:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;@Override&lt;br /&gt;protected void onDetachedFromWindow() {&lt;br /&gt;    try {&lt;br /&gt;        super.onDetachedFromWindow();&lt;br /&gt;    }&lt;br /&gt;    catch (IllegalArgumentException e) {&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The only problem with this is that the error is thrown before calling updateRunning().  A simple workaround for this is to call stopFlipping(), as that will kick off updateRunning() without any negative side effects:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;@Override&lt;br /&gt;protected void onDetachedFromWindow() {&lt;br /&gt;    try {&lt;br /&gt;        super.onDetachedFromWindow();&lt;br /&gt;    }&lt;br /&gt;    catch (IllegalArgumentException e) {&lt;br /&gt;        stopFlipping();&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You should only use this version of the class on Android 2.1 phones, as they are the only ones requiring the fix.&lt;br /&gt;&lt;br /&gt;EDIT May 25, 2010: ViewFlipper bug still occurs in Android 2.2.  I sure hope you didn't filter based on apiLevel == 7; better to use apiLevel &gt;= 7.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-744114330148549509?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/744114330148549509/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2010/05/viewflipper-receiver-not-registered.html#comment-form' title='18 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/744114330148549509'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/744114330148549509'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2010/05/viewflipper-receiver-not-registered.html' title='ViewFlipper &quot;Receiver not registered&quot; Error'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>18</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-3131465382607091565</id><published>2010-05-03T07:25:00.000-07:00</published><updated>2010-05-03T07:41:04.829-07:00</updated><title type='text'>Why They Can't Find Your App</title><content type='html'>The Android Market is a mixed blessing; on the one hand, I much prefer the near-instantaneous publishing time for applications.  It makes things a lot less stressful; if there's a terrible bug you can fix it quickly instead of having to go through Apple's approval process all over again.  On the other hand, it has a number of bugs and limitations that are frustrating.&lt;br /&gt;&lt;br /&gt;One of the most common support emails I get is "why can't I find your app on the Market?"  This is a sad email, one of a customer lost; and over time (from contact with Google or perusing the net) I've come up with a good list of reasons why customers can't find my applications.  Here it is, to help others diagnose the dreaded customer-who-would-be (in order of most likely to least likely):&lt;br /&gt;&lt;br /&gt;1. The customer has an older version of Android that your app does not support; for example, if you support 2.x but the user is on a 1.5 phone.&lt;br /&gt;&lt;br /&gt;2. Paid apps are only supported in some countries; if the customer is not in one of these countries, then you will not be able to find or purchase any paid applications. &lt;a href="http://market.android.com/support/bin/answer.py?hl=en&amp;answer=143779"&gt;The list of supported countries can be found here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Note that the customer can't simply move to a different country, either; this limitation is dependent on where your phone plan originally comes from.  (Thanks to a European customer who confirmed this for me on a business trip!)&lt;br /&gt;&lt;br /&gt;3. The customer is using a Google Apps account, rather than a gmail.com account.  This can either cause the app not to appear at all, or in some cases make it unpurchaseable. &lt;a href="http://www.google.com/support/forum/p/Android+Market/thread?tid=5be7ec2291114a13&amp;hl=en"&gt;More details can be found here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;4. The customer has a recently released phone and Google hasn't "fingerprinted" that device yet.  This blocks all paid apps from that device.  The only solution is to wait until Google fingerprints; &lt;a href="http://www.google.com/support/forum/p/Android+Market/thread?tid=72687bc93eba9a1b&amp;hl=en"&gt;it was kind of a pain with HTC Desire&lt;/a&gt;, at it took them a few weeks to get around to it.&lt;br /&gt;&lt;br /&gt;5. The customer is using a non-standard build of Android (that is, rooted and installed a custom ROM). Some of these builds cause the Market to malfunction and fail to display paid apps.&lt;br /&gt;&lt;br /&gt;6. The customer is using an Android Developer Phone and your application has copyright protection enabled.  ADPs aren't allowed to view copyright protection applications.  &lt;a href="http://market.android.com/support/bin/answer.py?hl=en&amp;answer=141659"&gt;More info here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;7. This is by far the least likely situation, but you can't buy your own app under the same account as you published from!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-3131465382607091565?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/3131465382607091565/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2010/05/why-they-cant-find-your-app.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/3131465382607091565'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/3131465382607091565'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2010/05/why-they-cant-find-your-app.html' title='Why They Can&apos;t Find Your App'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-3801227376048152698</id><published>2010-04-11T13:57:00.001-07:00</published><updated>2010-04-11T14:02:13.386-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>The Missing Manual - Android Drawables from XML</title><content type='html'>I have been fairly frustrated with a gap in the Android documentation recently - the file formats for creating Drawables from XML.  Drawables XML has been pretty useful to me in the past in creating gradients; I'd rather use it than create 9-patches all the time.  However, the Android documentation is strangely silent about the format; they provide a few samples, but as far as I know there's nothing beyond that.&lt;br /&gt;&lt;br /&gt;I've taken it upon myself to study the source code and create a missing manual for all the possibilities for Drawable XMLs.  The data was gathered from the Android source code, from the &lt;a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=tree;f=graphics/java/android/graphics/drawable;h=edf3142b151e2dfc2c84dffc07206ee0447a5cb7;hb=HEAD"&gt;android.graphics.drawables&lt;/a&gt; package.  I was surprised to find a lot of really useful code I wish I'd known about a long time ago; regardless, I was able to figure out all of how Drawables XML works.&lt;br /&gt;&lt;br /&gt;Without further ado, here it is: &lt;a href="http://idunnolol.com/android/drawables.html"&gt;the missing manual on Android Drawables via XML&lt;/a&gt;.  I hope this lessens the pain someone else has slightly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-3801227376048152698?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/3801227376048152698/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2010/04/missing-manual-android-drawables-from.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/3801227376048152698'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/3801227376048152698'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2010/04/missing-manual-android-drawables-from.html' title='The Missing Manual - Android Drawables from XML'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-3275626970699372694</id><published>2010-03-10T08:40:00.000-08:00</published><updated>2010-03-11T12:42:50.419-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Uri and Android 1.5</title><content type='html'>I came across an interesting Android bug the other day that deserves some mention.&lt;br /&gt;&lt;br /&gt;I was creating an Intent, setting some data for it, then calling startActivity().  The data Uri was created via Uri.Builder.  Let's say, for example, the Uri I wanted was "http://google.com".  The code would look like thus:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Intent intent = new Intent(context, TestActivity.class);&lt;br /&gt;Uri.Builder builder = new Uri.Builder();&lt;br /&gt;builder.scheme("http");&lt;br /&gt;builder.authority("google.com");&lt;br /&gt;intent.setData(builder.build());&lt;br /&gt;startActivity(intent);&lt;/pre&gt;&lt;br /&gt;This works in 1.6+.  But an interesting thing happens if you try this in 1.5...  it blows up in your face with NullPointerExceptions!  To answer why, I ended up digging around &lt;a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=history;f=core/java/android/net/Uri.java;h=9a1b65de15915ddf5f0897cfada93913ccb3247a;hb=HEAD"&gt;previous versions of the android.net.Uri class&lt;/a&gt;.  It turns out that, previous to 1.6, Uri expects you to fill out &lt;span style="font-weight:bold;"&gt;every&lt;/span&gt; part of the Uri - including the query parameters (parts following ?) as well as the fragment (part following #).&lt;br /&gt;&lt;br /&gt;The fix is to add dummy parameters for the sake of Android 1.5:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Intent intent = new Intent(context, TestActivity.class);&lt;br /&gt;Uri.Builder builder = new Uri.Builder();&lt;br /&gt;builder.scheme("http");&lt;br /&gt;builder.authority("google.com");&lt;br /&gt;builder.appendQueryParameter("ignore", "ignore");&lt;br /&gt;builder.fragment("ignore");&lt;br /&gt;intent.setData(builder.build());&lt;br /&gt;startActivity(intent);&lt;/pre&gt;&lt;br /&gt;This isn't a problem if you already are using all parts of a Uri, or if you only support Android 1.6+.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-3275626970699372694?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/3275626970699372694/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2010/03/uri-and-android-15.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/3275626970699372694'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/3275626970699372694'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2010/03/uri-and-android-15.html' title='Uri and Android 1.5'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-1228061785201273594</id><published>2010-01-18T12:03:00.000-08:00</published><updated>2010-01-18T12:21:18.750-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>ViewFlipper and Memory Leaks</title><content type='html'>Have you ever had a mysterious memory leak on Android when your code seemed clean as a whistle?  I sure have, and I finally figured out why: ViewFlipper.&lt;br /&gt;&lt;br /&gt;It turns out that when you call ViewFlipper.startFlipping(), it sets up a loop via messages which flips between its child Views.  This is all well and good, but there's no code which detects that the ViewFlipper itself is no longer being displayed and should be canned.  So the ViewFlipper keeps a hold on itself and its children, who inevitably keep a hold on their Context, and bam - gigantic memory leak.  Depending on how much memory your Activity takes up, all you need to do is start changing the orientation of the ViewFlipper's containing Activity a few times and you could crash your application.&lt;br /&gt;&lt;br /&gt;The solution to this is relatively simple: just call ViewFlipper.stopFlipping() in your Activity's onPause(), and then have it start flipping again in onResume().&lt;br /&gt;&lt;br /&gt;There is, however, one small catch.  Since ViewFlipper uses delayed messages, it's possible that a user can pause your activity then resume within the amount of time between flips (if they hit power then menu in quick succession, for example).  The way ViewFlipper is setup, this means you can end up with multiple flip messages being passed around, resulting in faster flipping than intended.  Unfortunately, the only solution I can think of to this is a real hassle (you would have to intentionally delay some time before resuming flipping in onResume()), so I'm just ignoring it for now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-1228061785201273594?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/1228061785201273594/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2010/01/viewflipper-and-memory-leaks.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/1228061785201273594'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/1228061785201273594'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2010/01/viewflipper-and-memory-leaks.html' title='ViewFlipper and Memory Leaks'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-2373992093871153391</id><published>2009-12-30T12:32:00.001-08:00</published><updated>2009-12-30T12:41:17.963-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Android Dialogs Created From Threads</title><content type='html'>There are a lot of issues with Dialogs, but I've run into one recently that deserves mention.&lt;br /&gt;&lt;br /&gt;Suppose you've got a background task running with AsyncTask.  At the end of this task (in onPostExecute()), you display a Dialog informing the user of success or failure.  This seems to be pretty cut and dry, but there's a catch - sometimes you'll get this error:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@4479b390 is not valid; is your activity running?&lt;br /&gt;    at android.view.ViewRoot.setView(ViewRoot.java:468)&lt;br /&gt;    at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:177)&lt;br /&gt;    at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)&lt;br /&gt;    at android.view.Window$LocalWindowManager.addView(Window.java:424)&lt;br /&gt;    at android.app.Dialog.show(Dialog.java:239)&lt;br /&gt;    at android.app.Activity.showDialog(Activity.java:2488)&lt;br /&gt;    ...&lt;/pre&gt;&lt;br /&gt;The problem lies in the fact that the AsyncTask can continue running past when the Activity itself has been finished.  If a user hits the "back" button on your Activity while the AsyncTask is running, you will end up calling showDialog() after the Activity itself is finished.&lt;br /&gt;&lt;br /&gt;My initial fix was to try to augment onCreateDialog() to handle finished activities, but unfortunately, there's no way to cancel dialog creation once you've called showDialog(); you'll cause errors returning null from onCreateDialog().  Thus, the only solution is to play it safe before you call showDialog():&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;protected void onPostExecute(Object result) {&lt;br /&gt;    if (!isFinishing()) {&lt;br /&gt;        showDialog(MY_DIALOG_ID);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-2373992093871153391?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/2373992093871153391/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2009/12/activities-and-dialogs.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/2373992093871153391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/2373992093871153391'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2009/12/activities-and-dialogs.html' title='Android Dialogs Created From Threads'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-1371821214251291726</id><published>2009-12-27T09:06:00.000-08:00</published><updated>2009-12-29T08:52:21.888-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Dynamically Retrieving Resources in Android</title><content type='html'>Normally, when retrieving resources (drawables, strings, what have you) in code you use the automatically generated R.java to do so.  However, I recently wrote a reference application wherein each item in a ListView has a different icon next to it.  The data for all this is stored in JSON as an asset, which meant that there was no way for me to link my data to R.java.&lt;br /&gt;&lt;br /&gt;Still, I needed some way to get a hold of the Drawable by name, and so I first turned to &lt;a href="http://developer.android.com/reference/android/content/res/Resources.html#getIdentifier(java.lang.String,%20java.lang.String,%20java.lang.String)"&gt;Resources.getIdentifier()&lt;/a&gt;.  This method does the job well of finding the resource id of anything you desire, in any package:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Resources r = getResources();&lt;br /&gt;int drawableId = r.getIdentifier("drawableName", "drawable", "com.mypackage.myapp");&lt;/pre&gt;&lt;br /&gt;This is all well and good, but: getIdentifier() is a general tool and as a result, slow.  Running through 10,000 iterations, it took about 3200 ms on my G1.  Also, it requires that you pass around a Context (or a Resources) wherever you need to use getIdentifier() which is an annoyance and seems unnecessary given that you can access R.java just fine normally in code.&lt;br /&gt;&lt;br /&gt;The better solution - as long as you are retrieving data from your application's own R.java - is to use reflection.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;try {&lt;br /&gt;    Class res = R.drawable.class;&lt;br /&gt;    Field field = res.getField("drawableName");&lt;br /&gt;    int drawableId = field.getInt(null);&lt;br /&gt;}&lt;br /&gt;catch (Exception e) {&lt;br /&gt;    Log.e("MyTag", "Failure to get drawable id.", e);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;In testing, the second method is about 5x faster than getIdentifier().  Not only that, but it means you don't have to pass a Resources object around to use it.  Obviously this cannot be used if you're accessing resources outside of your application's permissions and you'll have to rely on getIdentifier() then.&lt;br /&gt;&lt;br /&gt;One last note - simply linking to R.java is way faster than either of these two methods, so only fall back on these if you need to link to resource identifiers dynamically by name.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-1371821214251291726?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/1371821214251291726/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2009/12/dynamically-retrieving-resources-in.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/1371821214251291726'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/1371821214251291726'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2009/12/dynamically-retrieving-resources-in.html' title='Dynamically Retrieving Resources in Android'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-8819125981011932893</id><published>2009-12-16T14:22:00.000-08:00</published><updated>2009-12-16T14:40:48.668-08:00</updated><title type='text'>Android Add-On Applications</title><content type='html'>I recently developed an add-on package for an Android application and thought I'd share some findings.  That is, there is a base application and an add-on application which unlocks extras features in the base app.&lt;br /&gt;&lt;br /&gt;I chose to put all the functionality in the base application and simply have the add-on unlock it.  It turns out that this is very easy to do due to Android's dependence on &lt;a href="http://developer.android.com/guide/publishing/app-signing.html"&gt;signing keys&lt;/a&gt;.  First, I made an application that has no components - it has no launcher, code, or layouts.  All it has is a launcher icon (as this is viewable from the market and applications settings).  Next, I signed both the base application and the add-on with the same key.  Then, you can use the &lt;a href="http://developer.android.com/reference/android/content/pm/PackageManager.html"&gt;PackageManager&lt;/a&gt; to verify if the other application is installed:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public static boolean isAddOnInstalled(Context context) {&lt;br /&gt;   PackageManager pm = context.getPackageManager();&lt;br /&gt;   return pm.checkSignatures(BASE_APPLICATION_PACKAGE, ADDON_APPLICATION_PACKAGE) == PackageManager.SIGNATURE_MATCH;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;The neat part of this solution is that it relies on the PackageManager's signature checking to verify the add-on.  If the add-on application doesn't exist, or it was signed by a different key, this will return false.&lt;br /&gt;&lt;br /&gt;An alternative way to do add-ons would be to put actual functionality or data in the add-on package.  Again, you can use signing keys to share data.  Beyond this, you can use &lt;a href="http://developer.android.com/guide/topics/manifest/manifest-element.html#uid"&gt;sharedUserId&lt;/a&gt; to put both the base application and the add-on application in the same user ID.  This will allow the applications to access each others' data files directory.  A warning, though: it appears that adding a sharedUserId after the initial install causes the old data directory to still be under the old user ID, thus making all of your old files inaccessible (nor can you start writing new files - it kind of sucks).  &lt;a href="http://code.google.com/p/android/issues/detail?id=1227"&gt;Google isn't acknowledging this issue&lt;/a&gt;, unfortunately.&lt;br /&gt;&lt;br /&gt;One might think the way to go would be to use a ContentProvider, but if you're just doing an add-on for your own application, do you need to share the data with others?  Besides that, ContentProviders are a much more difficult solution for a simple problem when you're working with just your own data.  This isn't to say you shouldn't use ContentProviders, just that you should use the right tool for the job.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-8819125981011932893?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/8819125981011932893/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2009/12/android-add-on-applications.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/8819125981011932893'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/8819125981011932893'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2009/12/android-add-on-applications.html' title='Android Add-On Applications'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8001153144156969687.post-3507056700341206623</id><published>2009-12-08T15:56:00.000-08:00</published><updated>2009-12-08T15:57:26.940-08:00</updated><title type='text'>Welcome</title><content type='html'>Starting a new blog because I keep coming across old blog posts other coders make that help me...  I figure it's time for me to give back to the community.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8001153144156969687-3507056700341206623?l=daniel-codes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://daniel-codes.blogspot.com/feeds/3507056700341206623/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://daniel-codes.blogspot.com/2009/12/welcome.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/3507056700341206623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8001153144156969687/posts/default/3507056700341206623'/><link rel='alternate' type='text/html' href='http://daniel-codes.blogspot.com/2009/12/welcome.html' title='Welcome'/><author><name>Daniel</name><uri>http://www.blogger.com/profile/17003436808680466962</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/-m2hfLnZeSME/Tb2U_W6fYsI/AAAAAAAAAIc/AFjncuCIv8w/s220/194414_541001476732_19100182_31564585_6425728_o.jpg'/></author><thr:total>0</thr:total></entry></feed>
