<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Did You Win Yet?</title>
	<atom:link href="http://www.c99.org/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.c99.org</link>
	<description>Workin&#039; on it…</description>
	<lastBuildDate>Wed, 24 Feb 2010 17:30:49 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Writing an Android Sync Provider: Part 2</title>
		<link>http://www.c99.org/2010/01/23/writing-an-android-sync-provider-part-2/</link>
		<comments>http://www.c99.org/2010/01/23/writing-an-android-sync-provider-part-2/#comments</comments>
		<pubDate>Sat, 23 Jan 2010 20:58:34 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://www.c99.org/?p=1262</guid>
		<description><![CDATA[One of the great new user-facing features of Android 2.0 is the is the new Facebook app, which brings your Facebook contacts and statuses into your Android contacts database:

So, how exactly does my Nexus One know that Chris is excited about the upcoming launch of his new mobile apps?  The answer is a Contacts [...]]]></description>
			<content:encoded><![CDATA[<p>One of the great new user-facing features of Android 2.0 is the is the new Facebook app, which brings your Facebook contacts and statuses into your Android contacts database:<br />
<a href="http://www.c99.org/wp-content/uploads/2010/01/sms.png"><img src="http://www.c99.org/wp-content/uploads/2010/01/sms-300x229.png" alt="" title="Messaging" width="300" height="229" class="aligncenter size-medium wp-image-1263" /></a><br />
So, how exactly does my Nexus One know that Chris is excited about the upcoming launch of his new mobile apps?  The answer is a Contacts sync provider in the Facebook app.  Read on to learn how to create your own!<br />
<span id="more-1262"></span></p>
<h3>Sync Providers</h3>
<p>Sync providers are services that allow an Account to synchronize data on the device on a regular basis.  Not quite sure how to create an Account?  Read <a href="http://www.c99.org/2010/01/23/writing-an-android-sync-provider-part-1/">part one</a> first!  To implement a Contacts sync provider, we&#8217;ll need a service, some xml files, and the following permissions added to the AndroidManifest.xml:</p>
<div class="geshi no xml">
<div class="head">
<h4>AndroidManifest.xml snippet</h4>
</div>
<ol>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;uses-permission</span> <span class="re0">android:name</span>=<span class="st0">&quot;android.permission.INTERNET&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;uses-permission</span> <span class="re0">android:name</span>=<span class="st0">&quot;android.permission.ACCESS_NETWORK_STATE&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;uses-permission</span> <span class="re0">android:name</span>=<span class="st0">&quot;android.permission.READ_CONTACTS&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;uses-permission</span> <span class="re0">android:name</span>=<span class="st0">&quot;android.permission.WRITE_CONTACTS&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;uses-permission</span> <span class="re0">android:name</span>=<span class="st0">&quot;android.permission.GET_ACCOUNTS&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;uses-permission</span> <span class="re0">android:name</span>=<span class="st0">&quot;android.permission.MANAGE_ACCOUNTS&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;uses-permission</span> <span class="re0">android:name</span>=<span class="st0">&quot;android.permission.AUTHENTICATE_ACCOUNTS&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;uses-permission</span> <span class="re0">android:name</span>=<span class="st0">&quot;android.permission.READ_SYNC_SETTINGS&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;uses-permission</span> <span class="re0">android:name</span>=<span class="st0">&quot;android.permission.WRITE_SYNC_SETTINGS&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
</ol>
</div>
<h3>The Service</h3>
<p>Similar to our Account Authenticator service, our Contacts Sync Provider service will return a subclass of <a href="http://developer.android.com/reference/android/content/AbstractThreadedSyncAdapter.html">AbstractThreadedSyncAdapter</a> from the onBind method.</p>
<div class="geshi no java">
<div class="head">
<h4>ContactsSyncAdapterService.java</h4>
</div>
<ol>
<li class="li1">
<div class="de1"><span class="kw2">public</span> <span class="kw2">class</span> ContactsSyncAdapterService <span class="kw2">extends</span> Service <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">private</span> <span class="kw2">static</span> <span class="kw2">final</span> <span class="kw3">String</span> TAG = <span class="st0">&quot;ContactsSyncAdapterService&quot;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">private</span> <span class="kw2">static</span> SyncAdapterImpl sSyncAdapter = <span class="kw2">null</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">private</span> <span class="kw2">static</span> ContentResolver mContentResolver = <span class="kw2">null</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">public</span> ContactsSyncAdapterService<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">super</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">private</span> <span class="kw2">static</span> <span class="kw2">class</span> SyncAdapterImpl <span class="kw2">extends</span> AbstractThreadedSyncAdapter <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">private</span> <span class="kw3">Context</span> mContext<span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">public</span> SyncAdapterImpl<span class="br0">&#40;</span><span class="kw3">Context</span> context<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="kw2">super</span><span class="br0">&#40;</span>context, <span class="kw2">true</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;mContext = context<span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; @Override</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">public</span> <span class="kw4">void</span> onPerformSync<span class="br0">&#40;</span>Account account, Bundle extras, <span class="kw3">String</span> authority, ContentProviderClient provider, SyncResult syncResult<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="kw2">try</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; ContactsSyncAdapterService.<span class="me1">performSync</span><span class="br0">&#40;</span>mContext, account, extras, authority, provider, syncResult<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="br0">&#125;</span> <span class="kw2">catch</span> <span class="br0">&#40;</span>OperationCanceledException e<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;@Override</div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">public</span> IBinder onBind<span class="br0">&#40;</span>Intent intent<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; IBinder ret = <span class="kw2">null</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; ret = getSyncAdapter<span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">getSyncAdapterBinder</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">return</span> ret<span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">private</span> SyncAdapterImpl getSyncAdapter<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>sSyncAdapter == <span class="kw2">null</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;sSyncAdapter = <span class="kw2">new</span> SyncAdapterImpl<span class="br0">&#40;</span><span class="kw2">this</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">return</span> sSyncAdapter<span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">private</span> <span class="kw2">static</span> <span class="kw4">void</span> performSync<span class="br0">&#40;</span><span class="kw3">Context</span> context, Account account, Bundle extras, <span class="kw3">String</span> authority, ContentProviderClient provider, SyncResult syncResult<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="kw2">throws</span> OperationCanceledException <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; mContentResolver = context.<span class="me1">getContentResolver</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; Log.<span class="me1">i</span><span class="br0">&#40;</span>TAG, <span class="st0">&quot;performSync: &quot;</span> + account.<span class="me1">toString</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="co1">//This is where the magic will happen!</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<p>The service is defined in AndroidManifest.xml like so:</p>
<div class="geshi no xml">
<div class="head">
<h4>AndroidManifest.xml snippet</h4>
</div>
<ol>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;service</span> <span class="re0">android:name</span>=<span class="st0">&quot;.sync.ContactsSyncAdapterService&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="re0">android:exported</span>=<span class="st0">&quot;true&quot;</span> <span class="re0">android:process</span>=<span class="st0">&quot;:contacts&quot;</span><span class="re2">&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="sc3"><span class="re1">&lt;intent-filter<span class="re2">&gt;</span></span></span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="sc3"><span class="re1">&lt;action</span> <span class="re0">android:name</span>=<span class="st0">&quot;android.content.SyncAdapter&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="sc3"><span class="re1">&lt;/intent-filter<span class="re2">&gt;</span></span></span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="sc3"><span class="re1">&lt;meta-data</span> <span class="re0">android:name</span>=<span class="st0">&quot;android.content.SyncAdapter&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="re0">android:resource</span>=<span class="st0">&quot;@xml/sync_contacts&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;/service<span class="re2">&gt;</span></span></span></div>
</li>
</ol>
</div>
<p>Finally, we need an xml file to let Android know that our sync provider handles Contacts for the Account type we defined in part 1.</p>
<div class="geshi no xml">
<div class="head">
<h4>sync_contacts.xml</h4>
</div>
<ol>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;sync-adapter</span> <span class="re0">xmlns:android</span>=<span class="st0">&quot;http://schemas.android.com/apk/res/android&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re0">android:contentAuthority</span>=<span class="st0">&quot;com.android.contacts&quot;</span> </div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re0">android:accountType</span>=<span class="st0">&quot;fm.last.android.account&quot;</span><span class="re2">/&gt;</span></span></div>
</li>
</ol>
</div>
<p>At this point we have a sync provider that doesn&#8217;t do anything, but also shouldn&#8217;t crash the Android system.  Just in case, lets test it in Dev Tools first.  Start the Android emulator and launch the &#8220;Dev Tools&#8221; app, then scroll down to &#8220;Sync Tester&#8221;.<br />
<a href="http://www.c99.org/wp-content/uploads/2010/01/synctester.png"><img src="http://www.c99.org/wp-content/uploads/2010/01/synctester-300x241.png" alt="" title="Sync Tester" width="300" height="241" class="aligncenter size-medium wp-image-1268" /></a><br />
The drop-down should confirm that com.android.contacts can be synced with the account type you&#8217;ve created.  Select the entry for your account type and press the &#8220;Bind&#8221; button to connect to your sync service.  If all goes well, Sync Tester will say it&#8217;s connected to your service.  Now click &#8220;Start Sync&#8221; and select your account from the popup.  Sync Tester will let you know that the sync succeeded (even though we didn&#8217;t actually do anything).  At this point it should be safe to enable contact syncing from the Accounts &#038; Sync settings screen without Android crashing and rebooting.<br />
<a href="http://www.c99.org/wp-content/uploads/2010/01/contactsync.png"><img src="http://www.c99.org/wp-content/uploads/2010/01/contactsync-300x225.png" alt="" title="Contacts Syncing" width="300" height="225" class="aligncenter size-medium wp-image-1269" /></a></p>
<h3>Contacts</h3>
<p>Lets take a moment to discuss how Contacts on Android work.  Each sync account, such as Google or Facebook, creates its own set of RawContacts which the Android system then aggregates into the single list of contacts you see in the Dialer.  The RawContacts table contains several fields that Sync Providers can use for whatever they like, and in this implementation we will use the SYNC1 field to store the RawContact&#8217;s Last.fm username.</p>
<h3>Contact Data</h3>
<p>Data, such as name, phone number, email address, etc. is stored in a table that references a RawContact ID.  The Data table can contain anything you like, and there are several <a href="http://developer.android.com/reference/android/provider/ContactsContract.CommonDataKinds.html">predefined MIME-types available</a> for phone numbers, email addresses, names, etc.  Android will automatically try to combine RawContacts that contain the same name, email address, etc. into a single Contact, and the user can also combine and split Contacts manually from the contact edit screen.<br />
<a href="http://www.c99.org/wp-content/uploads/2010/01/aggregate.png"><img src="http://www.c99.org/wp-content/uploads/2010/01/aggregate-180x300.png" alt="" title="Aggregated Contact" width="180" height="300" class="aligncenter size-medium wp-image-1270" /></a></p>
<h3>Custom Data Types</h3>
<p>A sync provider can store additional data about a RawContact in the Data table, and provide an xml file to tell the Contacts app how to format this row.  To create a custom MIME type, we need to add an additional meta-data tag to our service entry in AndroidManifest.xml to reference a new ContactsSource XML file:</p>
<div class="geshi no xml">
<div class="head">
<h4>AndroidManifest.xml snippet</h4>
</div>
<ol>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;meta-data</span> <span class="re0">android:name</span>=<span class="st0">&quot;android.provider.CONTACTS_STRUCTURE&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="re0">android:resource</span>=<span class="st0">&quot;@xml/contacts&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
</ol>
</div>
<p>This ContactsSource xml file will tell the Contacts app how to format our custom MIME-types.  In the example below, we specify an icon, and tell the Contacts app to use the DATA2 and DATA3 columns to render the fields.  Note that this file&#8217;s format doesn&#8217;t appear to be documented outside of the <a href="http://www.google.com/codesearch/p?hl=en#oOy_5JrVRNM/trunk/packages/apps/Contacts/src/com/android/contacts/model/ExternalSource.java&#038;q=ContactsDataKind&#038;sa=N&#038;cd=1&#038;ct=rc">source code for the Contacts app</a>.</p>
<div class="geshi no xml">
<div class="head">
<h4>contacts.xml</h4>
</div>
<ol>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;ContactsSource</span> <span class="re0">xmlns:android</span>=<span class="st0">&quot;http://schemas.android.com/apk/res/android&quot;</span><span class="re2">&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="sc3"><span class="re1">&lt;ContactsDataKind</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="re0">android:icon</span>=<span class="st0">&quot;@drawable/icon&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="re0">android:mimeType</span>=<span class="st0">&quot;vnd.android.cursor.item/vnd.fm.last.android.profile&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="re0">android:summaryColumn</span>=<span class="st0">&quot;data2&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="re0">android:detailColumn</span>=<span class="st0">&quot;data3&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="re0">android:detailSocialSummary</span>=<span class="st0">&quot;true&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;/ContactsSource<span class="re2">&gt;</span></span></span></div>
</li>
</ol>
</div>
<p>Here&#8217;s how Facebook&#8217;s custom &#8220;Facebook Profile&#8221; field looks when rendered by the Contacts app:<br />
<a href="http://www.c99.org/wp-content/uploads/2010/01/facebookprofile.png"><img src="http://www.c99.org/wp-content/uploads/2010/01/facebookprofile-300x185.png" alt="" title="facebookprofile" width="300" height="185" class="aligncenter size-medium wp-image-1271" /></a></p>
<h3>Creating a RawContact</h3>
<p>Now lets put all the above information together to create a RawContact for a Last.fm user.  Our contacts will display only a name and a custom field that links to the user&#8217;s Last.fm profile.  We store the user&#8217;s username in the RawContact&#8217;s SYNC1 field so we can easily look it up later.  We will batch together the creation of the RawContact and the insertion of the Data, as Android will run an aggregation pass after each batch completes.</p>
<div class="geshi no java">
<div class="head">
<h4>addContact method</h4>
</div>
<ol>
<li class="li1">
<div class="de1"><span class="kw2">private</span> <span class="kw2">static</span> <span class="kw4">void</span> addContact<span class="br0">&#40;</span>Account account, <span class="kw3">String</span> name, <span class="kw3">String</span> username<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;Log.<span class="me1">i</span><span class="br0">&#40;</span>TAG, <span class="st0">&quot;Adding contact: &quot;</span> + name<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;ArrayList<span class="sy0">&lt;</span>ContentProviderOperation<span class="sy0">&gt;</span> operationList = <span class="kw2">new</span> ArrayList<span class="sy0">&lt;</span>ContentProviderOperation<span class="sy0">&gt;</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="co1">//Create our RawContact</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;ContentProviderOperation.<span class="me1">Builder</span> builder = ContentProviderOperation.<span class="me1">newInsert</span><span class="br0">&#40;</span>RawContacts.<span class="me1">CONTENT_URI</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;builder.<span class="me1">withValue</span><span class="br0">&#40;</span>RawContacts.<span class="me1">ACCOUNT_NAME</span>, account.<span class="me1">name</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;builder.<span class="me1">withValue</span><span class="br0">&#40;</span>RawContacts.<span class="me1">ACCOUNT_TYPE</span>, account.<span class="me1">type</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;builder.<span class="me1">withValue</span><span class="br0">&#40;</span>RawContacts.<span class="me1">SYNC1</span>, username<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;operationList.<span class="me1">add</span><span class="br0">&#40;</span>builder.<span class="me1">build</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="co1">//Create a Data record of common type &#39;StructuredName&#39; for our RawContact</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;builder = ContentProviderOperation.<span class="me1">newInsert</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">Data</span>.<span class="me1">CONTENT_URI</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;builder.<span class="me1">withValueBackReference</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">CommonDataKinds</span>.<span class="me1">StructuredName</span>.<span class="me1">RAW_CONTACT_ID</span>, <span class="nu0">0</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;builder.<span class="me1">withValue</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">Data</span>.<span class="me1">MIMETYPE</span>, ContactsContract.<span class="me1">CommonDataKinds</span>.<span class="me1">StructuredName</span>.<span class="me1">CONTENT_ITEM_TYPE</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;builder.<span class="me1">withValue</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">CommonDataKinds</span>.<span class="me1">StructuredName</span>.<span class="me1">DISPLAY_NAME</span>, name<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;operationList.<span class="me1">add</span><span class="br0">&#40;</span>builder.<span class="me1">build</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="co1">//Create a Data record of custom type &quot;vnd.android.cursor.item/vnd.fm.last.android.profile&quot; to display a link to the Last.fm profile</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;builder = ContentProviderOperation.<span class="me1">newInsert</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">Data</span>.<span class="me1">CONTENT_URI</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;builder.<span class="me1">withValueBackReference</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">Data</span>.<span class="me1">RAW_CONTACT_ID</span>, <span class="nu0">0</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;builder.<span class="me1">withValue</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">Data</span>.<span class="me1">MIMETYPE</span>, <span class="st0">&quot;vnd.android.cursor.item/vnd.fm.last.android.profile&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;builder.<span class="me1">withValue</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">Data</span>.<span class="me1">DATA1</span>, username<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;builder.<span class="me1">withValue</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">Data</span>.<span class="me1">DATA2</span>, <span class="st0">&quot;Last.fm Profile&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;builder.<span class="me1">withValue</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">Data</span>.<span class="me1">DATA3</span>, <span class="st0">&quot;View profile&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;operationList.<span class="me1">add</span><span class="br0">&#40;</span>builder.<span class="me1">build</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">try</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; mContentResolver.<span class="me1">applyBatch</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">AUTHORITY</span>, operationList<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span> <span class="kw2">catch</span> <span class="br0">&#40;</span><span class="kw3">Exception</span> e<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; Log.<span class="me1">e</span><span class="br0">&#40;</span>TAG, <span class="st0">&quot;Something went wrong during creation! &quot;</span> + e<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; e.<span class="me1">printStackTrace</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<h3>Social status updates</h3>
<p>Android keeps another table for social networking status updates.  Inserting a record into this table will replace any previous status if the timestamp of the insert is newer than the previous timestamp, otherwise the previous record will remain and the new insert will be discarded.  A status update record is associated with a Data record, in our implementation we will associate it with our Last.fm profile record.  Status records contain the status text, a package name where resources are located, an icon resource, and a label resource.  Below is a function that will insert a status update, as well as updating our Last.fm Profile Data record to display the last track the user listened to.  Note that for efficiency purposes, we will send all the updates in a single batch, so Android will only run a single aggregation pass at the end.</p>
<div class="geshi no java">
<div class="head">
<h4>updateContactStatus method</h4>
</div>
<ol>
<li class="li1">
<div class="de1"><span class="kw2">private</span> <span class="kw2">static</span> <span class="kw4">void</span> updateContactStatus<span class="br0">&#40;</span>ArrayList<span class="sy0">&lt;</span>ContentProviderOperation<span class="sy0">&gt;</span> operationList, <span class="kw4">long</span> rawContactId, <span class="kw3">Track</span> track<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;Uri rawContactUri = ContentUris.<span class="me1">withAppendedId</span><span class="br0">&#40;</span>RawContacts.<span class="me1">CONTENT_URI</span>, rawContactId<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;Uri entityUri = Uri.<span class="me1">withAppendedPath</span><span class="br0">&#40;</span>rawContactUri, <span class="kw3">Entity</span>.<span class="me1">CONTENT_DIRECTORY</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw3">Cursor</span> c = mContentResolver.<span class="me1">query</span><span class="br0">&#40;</span>entityUri, <span class="kw2">new</span> <span class="kw3">String</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="br0">&#123;</span> RawContacts.<span class="me1">SOURCE_ID</span>, <span class="kw3">Entity</span>.<span class="me1">DATA_ID</span>, <span class="kw3">Entity</span>.<span class="me1">MIMETYPE</span>, <span class="kw3">Entity</span>.<span class="me1">DATA1</span> <span class="br0">&#125;</span>, <span class="kw2">null</span>, <span class="kw2">null</span>, <span class="kw2">null</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">try</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">while</span> <span class="br0">&#40;</span>c.<span class="me1">moveToNext</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span>c.<span class="me1">isNull</span><span class="br0">&#40;</span><span class="nu0">1</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw3">String</span> mimeType = c.<span class="me1">getString</span><span class="br0">&#40;</span><span class="nu0">2</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw3">String</span> status = <span class="st0">&quot;&quot;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>track.<span class="me1">getNowPlaying</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">!</span>= <span class="kw2">null</span> <span class="sy0">&amp;&amp;</span> track.<span class="me1">getNowPlaying</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">equals</span><span class="br0">&#40;</span><span class="st0">&quot;true&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;status = <span class="st0">&quot;Listening to &quot;</span> + track.<span class="me1">getName</span><span class="br0">&#40;</span><span class="br0">&#41;</span> + <span class="st0">&quot; by &quot;</span> + track.<span class="me1">getArtist</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">else</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;status = <span class="st0">&quot;Listened to &quot;</span> + track.<span class="me1">getName</span><span class="br0">&#40;</span><span class="br0">&#41;</span> + <span class="st0">&quot; by &quot;</span> + track.<span class="me1">getArtist</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>mimeType.<span class="me1">equals</span><span class="br0">&#40;</span><span class="st0">&quot;vnd.android.cursor.item/vnd.fm.last.android.profile&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;ContentProviderOperation.<span class="me1">Builder</span> builder = ContentProviderOperation.<span class="me1">newInsert</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">StatusUpdates</span>.<span class="me1">CONTENT_URI</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;builder.<span class="me1">withValue</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">StatusUpdates</span>.<span class="me1">DATA_ID</span>, c.<span class="me1">getLong</span><span class="br0">&#40;</span><span class="nu0">1</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;builder.<span class="me1">withValue</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">StatusUpdates</span>.<span class="me1">STATUS</span>, status<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;builder.<span class="me1">withValue</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">StatusUpdates</span>.<span class="me1">STATUS_RES_PACKAGE</span>, <span class="st0">&quot;fm.last.android&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;builder.<span class="me1">withValue</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">StatusUpdates</span>.<span class="me1">STATUS_LABEL</span>, R.<span class="me1">string</span>.<span class="me1">app_name</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;builder.<span class="me1">withValue</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">StatusUpdates</span>.<span class="me1">STATUS_ICON</span>, R.<span class="me1">drawable</span>.<span class="me1">icon</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;<span class="kw1">if</span> <span class="br0">&#40;</span>track.<span class="me1">getDate</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">!</span>= <span class="kw2">null</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw4">long</span> date = <span class="kw3">Long</span>.<span class="me1">parseLong</span><span class="br0">&#40;</span>track.<span class="me1">getDate</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy0">*</span> <span class="nu0">1000</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; builder.<span class="me1">withValue</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">StatusUpdates</span>.<span class="me1">STATUS_TIMESTAMP</span>, date<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;operationList.<span class="me1">add</span><span class="br0">&#40;</span>builder.<span class="me1">build</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;builder = ContentProviderOperation.<span class="me1">newUpdate</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">Data</span>.<span class="me1">CONTENT_URI</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;builder.<span class="me1">withSelection</span><span class="br0">&#40;</span>BaseColumns._ID + <span class="st0">&quot; = &#39;&quot;</span> + c.<span class="me1">getLong</span><span class="br0">&#40;</span><span class="nu0">1</span><span class="br0">&#41;</span> + <span class="st0">&quot;&#39;&quot;</span>, <span class="kw2">null</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;builder.<span class="me1">withValue</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">Data</span>.<span class="me1">DATA3</span>, status<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;operationList.<span class="me1">add</span><span class="br0">&#40;</span>builder.<span class="me1">build</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span> <span class="kw2">finally</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; c.<span class="me1">close</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<p>Here&#8217;s how the contact record looks after we&#8217;ve inserted a status update record and updated our data record:<br />
<a href="http://www.c99.org/wp-content/uploads/2010/01/lastfmstatus.png"><img src="http://www.c99.org/wp-content/uploads/2010/01/lastfmstatus-300x257.png" alt="" title="Last.fm Status" width="300" height="257" class="aligncenter size-medium wp-image-1272" /></a></p>
<h3>Putting It All Together</h3>
<p>Now that we have our utility functions written, we can fill in the body of our performSync method.  We&#8217;re going to take a very simple approach to syncing: we&#8217;ll grab the list of RawContact IDs currently associated with Last.fm usernames, and we&#8217;ll create a RawContact for any Last.fm friend that doesn&#8217;t currently have one.  If a RawContact already exists, we&#8217;ll fetch their most recent track and post a status update.  Note that this is a one-way sync, we&#8217;re not dealing with local deletions or changes.</p>
<div class="geshi no java">
<div class="head">
<h4>performSync method</h4>
</div>
<ol>
<li class="li1">
<div class="de1"><span class="kw2">private</span> <span class="kw2">static</span> <span class="kw4">void</span> performSync<span class="br0">&#40;</span><span class="kw3">Context</span> context, Account account, Bundle extras, <span class="kw3">String</span> authority, ContentProviderClient provider, SyncResult syncResult<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">throws</span> OperationCanceledException <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;HashMap<span class="sy0">&lt;</span>String, Long<span class="sy0">&gt;</span> localContacts = <span class="kw2">new</span> HashMap<span class="sy0">&lt;</span>String, Long<span class="sy0">&gt;</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;mContentResolver = context.<span class="me1">getContentResolver</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;Log.<span class="me1">i</span><span class="br0">&#40;</span>TAG, <span class="st0">&quot;performSync: &quot;</span> + account.<span class="me1">toString</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="co1">// Load the local Last.fm contacts</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;Uri rawContactUri = RawContacts.<span class="me1">CONTENT_URI</span>.<span class="me1">buildUpon</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">appendQueryParameter</span><span class="br0">&#40;</span>RawContacts.<span class="me1">ACCOUNT_NAME</span>, account.<span class="me1">name</span><span class="br0">&#41;</span>.<span class="me1">appendQueryParameter</span><span class="br0">&#40;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;RawContacts.<span class="me1">ACCOUNT_TYPE</span>, account.<span class="me1">type</span><span class="br0">&#41;</span>.<span class="me1">build</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw3">Cursor</span> c1 = mContentResolver.<span class="me1">query</span><span class="br0">&#40;</span>rawContactUri, <span class="kw2">new</span> <span class="kw3">String</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="br0">&#123;</span> BaseColumns._ID, RawContacts.<span class="me1">SYNC1</span> <span class="br0">&#125;</span>, <span class="kw2">null</span>, <span class="kw2">null</span>, <span class="kw2">null</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw1">while</span> <span class="br0">&#40;</span>c1.<span class="me1">moveToNext</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; localContacts.<span class="me1">put</span><span class="br0">&#40;</span>c1.<span class="me1">getString</span><span class="br0">&#40;</span><span class="nu0">1</span><span class="br0">&#41;</span>, c1.<span class="me1">getLong</span><span class="br0">&#40;</span><span class="nu0">0</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;ArrayList<span class="sy0">&lt;</span>ContentProviderOperation<span class="sy0">&gt;</span> operationList = <span class="kw2">new</span> ArrayList<span class="sy0">&lt;</span>ContentProviderOperation<span class="sy0">&gt;</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;LastFmServer server = AndroidLastFmServerFactory.<span class="me1">getServer</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">try</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; Friends friends = server.<span class="me1">getFriends</span><span class="br0">&#40;</span>account.<span class="me1">name</span>, <span class="st0">&quot;&quot;</span>, <span class="st0">&quot;50&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span>User user : friends.<span class="me1">getFriends</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span>localContacts.<span class="me1">containsKey</span><span class="br0">&#40;</span>user.<span class="me1">getName</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>user.<span class="me1">getRealName</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">length</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">&gt;</span> <span class="nu0">0</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;addContact<span class="br0">&#40;</span>account, user.<span class="me1">getRealName</span><span class="br0">&#40;</span><span class="br0">&#41;</span>, user.<span class="me1">getName</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">else</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;addContact<span class="br0">&#40;</span>account, user.<span class="me1">getName</span><span class="br0">&#40;</span><span class="br0">&#41;</span>, user.<span class="me1">getName</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw3">Track</span><span class="br0">&#91;</span><span class="br0">&#93;</span> tracks = server.<span class="me1">getUserRecentTracks</span><span class="br0">&#40;</span>user.<span class="me1">getName</span><span class="br0">&#40;</span><span class="br0">&#41;</span>, <span class="st0">&quot;true&quot;</span>, <span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>tracks.<span class="me1">length</span> <span class="sy0">&gt;</span> <span class="nu0">0</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp;updateContactStatus<span class="br0">&#40;</span>operationList, localContacts.<span class="me1">get</span><span class="br0">&#40;</span>user.<span class="me1">getName</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>, tracks<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">if</span><span class="br0">&#40;</span>operationList.<span class="me1">size</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">&gt;</span> <span class="nu0">0</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;mContentResolver.<span class="me1">applyBatch</span><span class="br0">&#40;</span>ContactsContract.<span class="me1">AUTHORITY</span>, operationList<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span> <span class="kw2">catch</span> <span class="br0">&#40;</span><span class="kw3">Exception</span> e1<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="co1">// TODO Auto-generated catch block</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; e1.<span class="me1">printStackTrace</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<h3>Contact visibility</h3>
<p>By default, new contacts that you create from your sync provider are not shown by the Contacts app.  Instead, it will show only contacts it has aggregated with other visible sources.  To enable the display of contacts that only exist in your provider, press the Menu button and select &#8220;Display options&#8221;, then expand the entry for your account and tap the checkbox next to &#8220;All Contacts&#8221;.  This is a good default, as I don&#8217;t really want my address book to be littered with hundreds of twitter contacts!</p>
<h3>Final Thoughts</h3>
<p>The Android platform is awesome.  The lack of documentation, however, is quite disappointing.  While it&#8217;s great that I can dig through the system source code while troubleshooting and figuring things out, I shouldn&#8217;t have to!</p>
<p>Sync providers require Android 2.0, which is currently only available on 2 devices.  The Account modifications I made to our login activity will need to be reimplemented using class introspection in order to keep compatibility with Android 1.5.</p>
<p>This sync provider is far from complete &#8212; I still need to figure out how to control the sync interval, set my sync provider as read-only since local changes are ignored, I might grab the user&#8217;s avatar if they don&#8217;t currently have an icon, and add a fancy &#8220;Do you want to sync your contacts?&#8221; popup to the login process similar to Facebook&#8217;s.  </p>
<p>Hopefully this will help other developers out there struggling with the lack of documentation, as I&#8217;m looking forward to seeing twitter status updates in the contacts app in the future!</p>
<p>The source code for the implementation referenced here is available in my <a href="http://github.com/c99koder/lastfm-android/">Last.fm github project</a> under the terms of the GNU General Public License.  A standalone sample project is also available <a href="http://github.com/c99koder/AndroidSyncProviderDemo">here</a> under the terms of the Apache License 2.0.  Google has also released their own sample sync provider on the <a href="http://developer.android.com/resources/samples/SampleSyncAdapter/index.html">Android developer portal</a> that&#8217;s a bit more complete than mine.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2010/01/23/writing-an-android-sync-provider-part-2/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>Writing an Android Sync Provider: Part 1</title>
		<link>http://www.c99.org/2010/01/23/writing-an-android-sync-provider-part-1/</link>
		<comments>http://www.c99.org/2010/01/23/writing-an-android-sync-provider-part-1/#comments</comments>
		<pubDate>Sat, 23 Jan 2010 16:50:36 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://www.c99.org/?p=1249</guid>
		<description><![CDATA[One of the highlights of the Android 2.0 SDK is that you can write custom sync providers to integrate with the system contacts, calendars, etc.  The only problem is that there&#8217;s very little documentation on how it all fits together.  And worse, if you mess up in certain places, the Android system will [...]]]></description>
			<content:encoded><![CDATA[<p>One of the highlights of the Android 2.0 SDK is that you can write custom sync providers to integrate with the system contacts, calendars, etc.  The only problem is that there&#8217;s very little documentation on how it all fits together.  And worse, if you mess up in certain places, the Android system will crash and reboot!  Always up for a challenge, I&#8217;ve navigated through the sparse documentation, vague mailing list posts, and the Android source code itself to build a sync provider for our Last.fm app.  Want to know how to build your own? Read on!<br />
<span id="more-1249"></span></p>
<h3>Account Authenticators</h3>
<p>The first piece of the puzzle is called an <a href="http://developer.android.com/reference/android/accounts/AbstractAccountAuthenticator.html">Account Authenticator</a>, which defines how the user&#8217;s account will appear in the &#8220;Accounts &#038; Sync&#8221; settings.  Implementing an Account Authenticator requires 3 pieces: a service that returns a subclass of AbstractAccountAuthenticator from the onBind method, an activity to prompt the user to enter their credentials, and an xml file describing how your account should look when displayed to the user.  You&#8217;ll also need to add the <b>android.permission.AUTHENTICATE_ACCOUNTS</b> permission to your AndroidManifest.xml.</p>
<h3>The Service</h3>
<p>The authenticator service is expected to return a subclass of AbstractAccountAuthenticator from the onBind method &#8212; if you don&#8217;t, Android will crash and reboot when you try to add a new account to the system.  The only method in AbstractAccountAuthenticator we really need to implement is addAccount, which returns an Intent that the system will use to display the login dialog to the user.  The implementation below will launch our app&#8217;s main launcher activity with an action of &#8220;fm.last.android.sync.LOGIN&#8221; and an extra containing the AccountAuthenticatorResponse object we use to pass data back to the system after the user has logged in.</p>
<div class="geshi no java">
<div class="head">
<h4>AccountAuthenticatorService.java</h4>
</div>
<ol>
<li class="li1">
<div class="de1"><span class="co2">import fm.last.android.LastFm;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">import android.accounts.AbstractAccountAuthenticator;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">import android.accounts.Account;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">import android.accounts.AccountAuthenticatorResponse;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">import android.accounts.AccountManager;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">import android.accounts.NetworkErrorException;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">import android.app.Service;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">import android.content.Context;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">import android.content.Intent;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">import android.os.Bundle;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">import android.os.IBinder;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="co2">import android.util.Log;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="coMULTI">/**</span></div>
</li>
<li class="li1">
<div class="de1"><span class="coMULTI">&nbsp;* Authenticator service that returns a subclass of AbstractAccountAuthenticator in onBind()</span></div>
</li>
<li class="li1">
<div class="de1"><span class="coMULTI">&nbsp;*/</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw2">public</span> <span class="kw2">class</span> AccountAuthenticatorService <span class="kw2">extends</span> Service <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">private</span> <span class="kw2">static</span> <span class="kw2">final</span> <span class="kw3">String</span> TAG = <span class="st0">&quot;AccountAuthenticatorService&quot;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">private</span> <span class="kw2">static</span> AccountAuthenticatorImpl sAccountAuthenticator = <span class="kw2">null</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">public</span> AccountAuthenticatorService<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">super</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">public</span> IBinder onBind<span class="br0">&#40;</span>Intent intent<span class="br0">&#41;</span> <span class="br0">&#123;</span> </div>
</li>
<li class="li1">
<div class="de1">&nbsp; IBinder ret = <span class="kw2">null</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>intent.<span class="me1">getAction</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">equals</span><span class="br0">&#40;</span>android.<span class="me1">accounts</span>.<span class="me1">AccountManager</span>.<span class="me1">ACTION_AUTHENTICATOR_INTENT</span><span class="br0">&#41;</span><span class="br0">&#41;</span> </div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;ret = getAuthenticator<span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">getIBinder</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">return</span> ret<span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">private</span> AccountAuthenticatorImpl getAuthenticator<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> </div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>sAccountAuthenticator == <span class="kw2">null</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;sAccountAuthenticator = <span class="kw2">new</span> AccountAuthenticatorImpl<span class="br0">&#40;</span><span class="kw2">this</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">return</span> sAccountAuthenticator<span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw2">private</span> <span class="kw2">static</span> <span class="kw2">class</span> AccountAuthenticatorImpl <span class="kw2">extends</span> AbstractAccountAuthenticator <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">private</span> <span class="kw3">Context</span> mContext<span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">public</span> AccountAuthenticatorImpl<span class="br0">&#40;</span><span class="kw3">Context</span> context<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="kw2">super</span><span class="br0">&#40;</span>context<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;mContext = context<span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="coMULTI">/*</span></div>
</li>
<li class="li1">
<div class="de1"><span class="coMULTI">&nbsp; &nbsp;* &nbsp;The user has requested to add a new account to the system. &nbsp;We return an intent that will launch our login screen if the user has not logged in yet,</span></div>
</li>
<li class="li1">
<div class="de1"><span class="coMULTI">&nbsp; &nbsp;* &nbsp;otherwise our activity will just pass the user&#39;s credentials on to the account manager.</span></div>
</li>
<li class="li1">
<div class="de1"><span class="coMULTI">&nbsp; &nbsp;*/</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; @Override</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">public</span> Bundle addAccount<span class="br0">&#40;</span>AccountAuthenticatorResponse response, <span class="kw3">String</span> accountType, <span class="kw3">String</span> authTokenType, <span class="kw3">String</span><span class="br0">&#91;</span><span class="br0">&#93;</span> requiredFeatures, Bundle options<span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw2">throws</span> NetworkErrorException <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;Bundle reply = <span class="kw2">new</span> Bundle<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;Intent i = <span class="kw2">new</span> Intent<span class="br0">&#40;</span>mContext, LastFm.<span class="kw2">class</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;i.<span class="me1">setAction</span><span class="br0">&#40;</span><span class="st0">&quot;fm.last.android.sync.LOGIN&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;i.<span class="me1">putExtra</span><span class="br0">&#40;</span>AccountManager.<span class="me1">KEY_ACCOUNT_AUTHENTICATOR_RESPONSE</span>, response<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;reply.<span class="me1">putParcelable</span><span class="br0">&#40;</span>AccountManager.<span class="me1">KEY_INTENT</span>, i<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="kw2">return</span> reply<span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; @Override</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">public</span> Bundle confirmCredentials<span class="br0">&#40;</span>AccountAuthenticatorResponse response, Account account, Bundle options<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="kw2">return</span> <span class="kw2">null</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; @Override</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">public</span> Bundle editProperties<span class="br0">&#40;</span>AccountAuthenticatorResponse response, <span class="kw3">String</span> accountType<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="kw2">return</span> <span class="kw2">null</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; @Override</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">public</span> Bundle getAuthToken<span class="br0">&#40;</span>AccountAuthenticatorResponse response, Account account, <span class="kw3">String</span> authTokenType, Bundle options<span class="br0">&#41;</span> <span class="kw2">throws</span> NetworkErrorException <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="kw2">return</span> <span class="kw2">null</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; @Override</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">public</span> <span class="kw3">String</span> getAuthTokenLabel<span class="br0">&#40;</span><span class="kw3">String</span> authTokenType<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="kw2">return</span> <span class="kw2">null</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; @Override</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">public</span> Bundle hasFeatures<span class="br0">&#40;</span>AccountAuthenticatorResponse response, Account account, <span class="kw3">String</span><span class="br0">&#91;</span><span class="br0">&#93;</span> features<span class="br0">&#41;</span> <span class="kw2">throws</span> NetworkErrorException <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="kw2">return</span> <span class="kw2">null</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; @Override</div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw2">public</span> Bundle updateCredentials<span class="br0">&#40;</span>AccountAuthenticatorResponse response, Account account, <span class="kw3">String</span> authTokenType, Bundle options<span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp;<span class="kw2">return</span> <span class="kw2">null</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<p>The account authenticator service should be defined in your AndroidManifest.xml, with a meta-data tag referencing an xml definition file, as follows:</p>
<div class="geshi no xml">
<div class="head">
<h4>Snippet from AndroidManifest.xml</h4>
</div>
<ol>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;service</span> <span class="re0">android:name</span>=<span class="st0">&quot;AccountAuthenticatorService&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="re0">android:exported</span>=<span class="st0">&quot;true&quot;</span> <span class="re0">android:process</span>=<span class="st0">&quot;:auth&quot;</span><span class="re2">&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="sc3"><span class="re1">&lt;intent-filter<span class="re2">&gt;</span></span></span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="sc3"><span class="re1">&lt;action</span> <span class="re0">android:name</span>=<span class="st0">&quot;android.accounts.AccountAuthenticator&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="sc3"><span class="re1">&lt;/intent-filter<span class="re2">&gt;</span></span></span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="sc3"><span class="re1">&lt;meta-data</span> <span class="re0">android:name</span>=<span class="st0">&quot;android.accounts.AccountAuthenticator&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="re0">android:resource</span>=<span class="st0">&quot;@xml/authenticator&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;/service<span class="re2">&gt;</span></span></span></div>
</li>
</ol>
</div>
<h3>The Activity</h3>
<p>If you don&#8217;t already have a login screen, there&#8217;s a convenience class <a href="http://developer.android.com/reference/android/accounts/AccountAuthenticatorActivity.html">AccountAuthenticatorActivity</a> you can subclass that will pass your response back to the authentication manager for you, however if you already have a login activity in place you may find it easier to just pass the data back yourself, as I have done here.  When the user has successfully been authenticated, we create an Account object for the user&#8217;s credentials.  An account has an account name, such as the username or email address, and an account type, which you will define in your xml file next.  You may find it easier to store your account type in strings.xml and use getString() to fetch it, as it is used in multiple places.</p>
<div class="geshi no java">
<div class="head">
<h4>Snippet from the Last.fm login activity</h4>
</div>
<ol>
<li class="li1">
<div class="de1">Account account = <span class="kw2">new</span> Account<span class="br0">&#40;</span>username, getString<span class="br0">&#40;</span>R.<span class="me1">string</span>.<span class="me1">ACCOUNT_TYPE</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">AccountManager am = AccountManager.<span class="me1">get</span><span class="br0">&#40;</span><span class="kw2">this</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw4">boolean</span> accountCreated = am.<span class="me1">addAccountExplicitly</span><span class="br0">&#40;</span>account, password, <span class="kw2">null</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">Bundle extras = getIntent.<span class="me1">getExtras</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw1">if</span> <span class="br0">&#40;</span>extras <span class="sy0">!</span>= <span class="kw2">null</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="kw1">if</span> <span class="br0">&#40;</span>accountCreated<span class="br0">&#41;</span> <span class="br0">&#123;</span> &nbsp;<span class="co1">//Pass the new account back to the account manager</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; AccountAuthenticatorResponse response = extras.<span class="me1">getParcelable</span><span class="br0">&#40;</span>AccountManager.<span class="me1">KEY_ACCOUNT_AUTHENTICATOR_RESPONSE</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; Bundle result = <span class="kw2">new</span> Bundle<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; result.<span class="me1">putString</span><span class="br0">&#40;</span>AccountManager.<span class="me1">KEY_ACCOUNT_NAME</span>, username<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; result.<span class="me1">putString</span><span class="br0">&#40;</span>AccountManager.<span class="me1">KEY_ACCOUNT_TYPE</span>, getString<span class="br0">&#40;</span>R.<span class="me1">string</span>.<span class="me1">ACCOUNT_TYPE</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; response.<span class="me1">onResult</span><span class="br0">&#40;</span>result<span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;<span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;finish<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
<h3>The XML definition file</h3>
<p>The account xml file defines what the user will see when they&#8217;re interacting with your account.  It contains a user-readable name, the system account type you&#8217;re defining, various icons, and a reference to an xml file containing PreferenceScreens the user will see when modifying your account.</p>
<div class="geshi no xml">
<div class="head">
<h4>authenticator.xml</h4>
</div>
<ol>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;account-authenticator</span> <span class="re0">xmlns:android</span>=<span class="st0">&quot;http://schemas.android.com/apk/res/android&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re0">android:accountType</span>=<span class="st0">&quot;fm.last.android.account&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re0">android:icon</span>=<span class="st0">&quot;@drawable/icon&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re0">android:smallIcon</span>=<span class="st0">&quot;@drawable/icon&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re0">android:label</span>=<span class="st0">&quot;@string/app_name&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="re0">android:accountPreferences</span>=<span class="st0">&quot;@xml/account_preferences&quot;</span><span class="re2">/&gt;</span></span></div>
</li>
</ol>
</div>
<div class="geshi no xml">
<div class="head">
<h4>account_preferences.xml</h4>
</div>
<ol>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;PreferenceScreen</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="re0">xmlns:android</span>=<span class="st0">&quot;http://schemas.android.com/apk/res/android&quot;</span><span class="re2">&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;PreferenceCategory</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">android:title</span>=<span class="st0">&quot;General Settings&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;PreferenceScreen</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">android:key</span>=<span class="st0">&quot;account_settings&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">android:title</span>=<span class="st0">&quot;Account Settings&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">android:summary</span>=<span class="st0">&quot;Sync frequency, notifications, etc.&quot;</span><span class="re2">&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;intent</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">android:action</span>=<span class="st0">&quot;fm.last.android.activity.Preferences.ACCOUNT_SETUP&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">android:targetPackage</span>=<span class="st0">&quot;fm.last.android&quot;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">android:targetClass</span>=<span class="st0">&quot;fm.last.android.activity.Preferences&quot;</span> <span class="re2">/&gt;</span></span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/PreferenceScreen<span class="re2">&gt;</span></span></span></div>
</li>
<li class="li1">
<div class="de1"><span class="sc3"><span class="re1">&lt;/PreferenceScreen<span class="re2">&gt;</span></span></span></div>
</li>
</ol>
</div>
<h3>Putting it all together</h3>
<p>Now we&#8217;re ready for testing!  The Android accounts setting screen doesn&#8217;t handle exceptions very well &#8212; if something goes wrong, your device will reboot! A better way to test is to launch the emulator, run the &#8220;Dev Tools&#8221; app, and pick &#8220;AccountsTester&#8221;.<br />
<a href="http://www.c99.org/wp-content/uploads/2010/01/accountstester1.png"><img src="http://www.c99.org/wp-content/uploads/2010/01/accountstester1-211x300.png" alt="" title="Accounts Tester" width="211" height="300" class="alignnone size-medium wp-image-1252" /></a><br />
You should see your new account type in the list, along with the built-in &#8220;Corporate&#8221; account type.  Go ahead and select your account type from the drop-down list, and then press the &#8220;Add&#8221; button, and you should be presented with your login activity.  After authenticating, your account should appear in a list below the buttons.  At this point, it should be safe to use the system &#8220;Accounts &#038; Sync&#8221; settings screen to remove or modify your account.<br />
<a href="http://www.c99.org/wp-content/uploads/2010/01/accountsandsync1.png"><img src="http://www.c99.org/wp-content/uploads/2010/01/accountsandsync1-210x300.png" alt="" title="Account List" width="210" height="300" class="alignnone size-medium wp-image-1253" /></a> <a href="http://www.c99.org/wp-content/uploads/2010/01/accountsandsync2.png"><img src="http://www.c99.org/wp-content/uploads/2010/01/accountsandsync2-210x300.png" alt="" title="Account Details" width="210" height="300" class="alignnone size-medium wp-image-1254" /></a></p>
<p>Ready to fill in that section below &#8220;Data &#038; synchronization&#8221;?  Let&#8217;s move on to <a href="http://www.c99.org/2010/01/23/writing-an-android-sync-provider-part-2/">part 2</a>!</p>
<p>The source code for the implementation referenced here is available in my <a href="http://github.com/c99koder/lastfm-android/">Last.fm github project</a> under the terms of the GNU General Public License.  A standalone sample project is also available <a href="http://github.com/c99koder/AndroidSyncProviderDemo">here</a> under the terms of the Apache License 2.0.  Google has also released their own sample sync provider on the <a href="http://developer.android.com/resources/samples/SampleSyncAdapter/index.html">Android developer portal</a> that&#8217;s a bit more complete than mine.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2010/01/23/writing-an-android-sync-provider-part-1/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>DCSquares Mobile</title>
		<link>http://www.c99.org/2009/01/27/dcsquares-mobile/</link>
		<comments>http://www.c99.org/2009/01/27/dcsquares-mobile/#comments</comments>
		<pubDate>Wed, 28 Jan 2009 03:27:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[iPhone]]></category>
		<category><![CDATA[dcsquares]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=592</guid>
		<description><![CDATA[The first version of DCSquares Mobile is now available through the iTunes App Store for iPhone and iPod Touch.  The mobile version uses the accelerometer to control the player and has its own separate high score table.

Check out DCSquares Mobile in the iTunes App Store, or at the new DCSquares Mobile website at http://www.dcsquaresapp.com/iphone/
]]></description>
			<content:encoded><![CDATA[<p>The first version of DCSquares Mobile is now available through the iTunes App Store for iPhone and iPod Touch.  The mobile version uses the accelerometer to control the player and has its own separate high score table.<br />
<img src="http://dcsquares.c99.org/iphone/images/title-thumb.jpg"><br />
Check out DCSquares Mobile in the iTunes App Store, or at the new DCSquares Mobile website at http://www.dcsquaresapp.com/iphone/</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2009/01/27/dcsquares-mobile/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>New Zealand</title>
		<link>http://www.c99.org/2008/11/25/new-zealand-2/</link>
		<comments>http://www.c99.org/2008/11/25/new-zealand-2/#comments</comments>
		<pubDate>Tue, 25 Nov 2008 21:11:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[Life]]></category>
		<category><![CDATA[new zealand]]></category>
		<category><![CDATA[vacation]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=587</guid>
		<description><![CDATA[Just returned from a vacation to New Zealand with Jay.  Hadn&#8217;t been back there since we left in 1992.  It was great to see the country again!  Photos are up on flickr here: http://www.flickr.com/photos/samsteele/collections/72157610097527600/
]]></description>
			<content:encoded><![CDATA[<p>Just returned from a vacation to New Zealand with <a href='http://jayyy.livejournal.com/'>Jay</a>.  Hadn&#8217;t been back there since we left in 1992.  It was great to see the country again!  Photos are up on flickr here: http://www.flickr.com/photos/samsteele/collections/72157610097527600/</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/11/25/new-zealand-2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Watching MythTV recordings on AppleTV with Boxee</title>
		<link>http://www.c99.org/2008/10/19/watching-mythtv-recordings-on-appletv-with-boxee/</link>
		<comments>http://www.c99.org/2008/10/19/watching-mythtv-recordings-on-appletv-with-boxee/#comments</comments>
		<pubDate>Sun, 19 Oct 2008 18:03:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[MythTV]]></category>
		<category><![CDATA[appletv]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=583</guid>
		<description><![CDATA[A few weeks ago I decided to get an AppleTV to use as a mythtv frontend, as my current backend freezes up while playing back recordings.  While the Mac OS X version of mythfrontend runs fine on the AppleTV, the playback performance is a bit choppy and the Apple remote does not work.  [...]]]></description>
			<content:encoded><![CDATA[<p>A few weeks ago I decided to get an AppleTV to use as a mythtv frontend, as my current backend freezes up while playing back recordings.  While the Mac OS X version of mythfrontend runs fine on the AppleTV, the playback performance is a bit choppy and the Apple remote does not work.  I rent movies and watch podcasts through iTunes, so I didn&#8217;t want to give up the ability to play back iTunes purchases by formatting the AppleTV and installing linux (which would resolve both of the mythfrontend issues).</p>
<p>Instead, here&#8217;s a much better solution: <a href="http://www.boxee.tv/">Boxee</a>.  Boxee is a media player based on XBMC, and it runs on Mac OS X, AppleTV, and Linux.  There are two ways you can access your mythtv recordings through Boxee, UPNP and SMB.  There&#8217;s a third way, the xbmc mythtv plugin that communicates directly with your myth backend, however I was unable to get this to work with my backend on my AppleTV.</p>
<p>Browsing your recordings over UPNP is easy, simply select &#8220;Network Sources&#8221; from the Boxee video menu, wait a few seconds for UPNP to discover the backend, then go back out of the network browser and back in (there&#8217;s no option to refresh the list here).  You should now see &#8220;Recordings&#8221; as an available source, which will give you access to all your mythtv recordings.  I found this to be very unreliable on my MythTV 0.21 backend, streaming over UPNP would cause the mythbackend process to hang after several recordings.  But I was determined to get this working!</p>
<p>Enter mythrename.pl and samba!  mythrename is a perl script that is included with mythbackend (it&#8217;s available in /usr/share/doc/mythtv-backend/contrib/ on Ubuntu) that will either rename or symblink your mythtv recording files, which are normally just a bunch of numbers, to whatever format you specify.  I&#8217;ve set up a cron job to run the following command every 30 minutes:</p>
<p>/usr/share/doc/mythtv-backend/contrib/mythrename.pl &#8211;link &#8211;format %T/%m%-%d%-%Y\ %S</p>
<p>The command above symblinks my recordings inside the mythtv recordings folder to show_names/(show name)/(month)-(day)-(year) (episode title).  For example, last night&#8217;s episode of Life is available at /share/MythTV/show_names/Life/10-17-2008 Crushed.mpg.  I simply export this folder over samba and add it as a source in Boxee (set as private to avoid indexing, which will fail because MythTV does not keep track of the season / episode numbers required by Boxee&#8217;s tv show detector).</p>
<p>I can now watch all my MythTV recordings on the AppleTV, controlled with the Apple remote, and without any choppiness during playback, while still having access to all my iTunes content through the AppleTV menus.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/10/19/watching-mythtv-recordings-on-appletv-with-boxee/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Last.fm for iPhone and iPod Touch 2.0 Released</title>
		<link>http://www.c99.org/2008/09/27/last-fm-for-iphone-and-ipod-touch-2-0-released/</link>
		<comments>http://www.c99.org/2008/09/27/last-fm-for-iphone-and-ipod-touch-2-0-released/#comments</comments>
		<pubDate>Sat, 27 Sep 2008 21:51:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[iPhone]]></category>
		<category><![CDATA[last.fm]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=571</guid>
		<description><![CDATA[Last.fm for iPhone and iPod Touch version 2.0.0 is now available on iTunes. Here&#8217;s what&#8217;s new:
• Streamlined user interface
• Tagging
• Personal tag radio
• Common artists when viewing a profile
• A brand new calendar-based events view
• Support for firmware 2.1
• And much, much more!
To install the update, click &#8220;Check for updates&#8221; at the bottom of your [...]]]></description>
			<content:encoded><![CDATA[<p>Last.fm for iPhone and iPod Touch version 2.0.0 is now available on iTunes. Here&#8217;s what&#8217;s new:</p>
<p>• Streamlined user interface<br />
• Tagging<br />
• Personal tag radio<br />
• Common artists when viewing a profile<br />
• A brand new calendar-based events view<br />
• Support for firmware 2.1<br />
• And much, much more!</p>
<p>To install the update, click &#8220;Check for updates&#8221; at the bottom of your iTunes Applications list, or tap the Updates tab of the App Store on your device.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/09/27/last-fm-for-iphone-and-ipod-touch-2-0-released/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Yay!</title>
		<link>http://www.c99.org/2008/09/27/yay-2/</link>
		<comments>http://www.c99.org/2008/09/27/yay-2/#comments</comments>
		<pubDate>Sat, 27 Sep 2008 21:47:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[Life]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=570</guid>
		<description><![CDATA[Steven is awesome.
]]></description>
			<content:encoded><![CDATA[<p><a href="http://steventroughtonsmith.blogspot.com/">Steven</a> is awesome.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/09/27/yay-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>DCSquares: GOAT Games Edition</title>
		<link>http://www.c99.org/2008/08/30/dcsquares-goat-games-edition-2/</link>
		<comments>http://www.c99.org/2008/08/30/dcsquares-goat-games-edition-2/#comments</comments>
		<pubDate>Sat, 30 Aug 2008 20:06:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[Dreamcast]]></category>
		<category><![CDATA[dcsquares]]></category>
		<category><![CDATA[goat]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=563</guid>
		<description><![CDATA[Several years ago, GOAT Store Publishing contacted a bunch of homebrew Dreamcast developers about putting together a commercial disc with homebrew games on it.  DCSquares to be included on GOAT Games, Volume 1, and I started working on several improvements to the game, improving single player mode by adding challenge levels, and adding multiplayer [...]]]></description>
			<content:encoded><![CDATA[<p>Several years ago, GOAT Store Publishing contacted a bunch of homebrew Dreamcast developers about putting together a commercial disc with homebrew games on it.  DCSquares to be included on GOAT Games, Volume 1, and I started working on several improvements to the game, improving single player mode by adding challenge levels, and adding multiplayer support.</p>
<p>GOAT Games Volume 1 was never released, but below is a video showing off the updated DCSquares. The sound effects were not working on this Mac build, as it was only tested on the Dreamcast. Multiplayer is also difficult to demo on the Mac, as it requires multiple controllers.<br />
<lj-embed id="2"/></p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/08/30/dcsquares-goat-games-edition-2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Random Stats</title>
		<link>http://www.c99.org/2008/08/24/random-stats-2/</link>
		<comments>http://www.c99.org/2008/08/24/random-stats-2/#comments</comments>
		<pubDate>Mon, 25 Aug 2008 05:26:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[Dreamcast]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[dcsquares]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=556</guid>
		<description><![CDATA[Two years ago I gathered some interesting stats from the DCSquares score database.  Here&#8217;s an updated version of those stats using the data as of today:
General Stats
* Total users that have submitted a score: 457
* Total games played: 54,132
* Total squares collected: 1,564,992
* Total time spent playing: 38 days (note: scores entered through the [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://c99koder.livejournal.com/74448.html">Two years ago</a> I gathered some interesting stats from the DCSquares score database.  Here&#8217;s an updated version of those stats using the data as of today:</p>
<p><b>General Stats</b></p>
<p>* Total users that have submitted a score: 457<br />
* Total games played: 54,132<br />
* Total squares collected: 1,564,992<br />
* Total time spent playing: 38 days (note: scores entered through the website do not record a time, so this is actually higher)<br />
* Average games played per user: 118<br />
* Average number of squares collected per game: 48<br />
* Average score per game: 30,556<br />
* Average combo per game: 47<br />
* Average game time: 61 seconds<br />
* The most users that have scored the same score is 74. They all scored 1,080 points.</p>
<p><b>Players by platform</b></p>
<p>* Windows: 55%<br />
* Mac: 43%<br />
* Linux: 2%<br />
* Dreamcast: 0% (only 23 scores ever submitted for Dreamcast)</p>
<p><b>Hall of fame:</b></p>
<p>* Highest score: Harryfronman scored 677,472 points<br />
* Highest combo: captain collected 354 squares in a row<br />
* Most squares: Harryfronman collected 731 squares<br />
* Longest time: jason survived for 16:47 minutes</p>
<p><b>Hall of shame:</b></p>
<p>* Lowest score: runkennyrun scored 1,000 points (scores below 1,000 are rejected)<br />
* Shortest time: Baphmomet survived for 2.3 seconds</p>
<p>16 players only collected 1 square but managed to score more than 1000 points.<br />
99% of players use the in-game score client, only 1% of scores were entered through the web.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/08/24/random-stats-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Twitteresque</title>
		<link>http://www.c99.org/2008/08/24/twitteresque/</link>
		<comments>http://www.c99.org/2008/08/24/twitteresque/#comments</comments>
		<pubDate>Mon, 25 Aug 2008 04:19:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[Life]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=555</guid>
		<description><![CDATA[Chocolate soda is surprisingly good.
]]></description>
			<content:encoded><![CDATA[<p>Chocolate soda is surprisingly good.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/08/24/twitteresque/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>DCSquares for iPhone</title>
		<link>http://www.c99.org/2008/08/24/dcsquares-for-iphone/</link>
		<comments>http://www.c99.org/2008/08/24/dcsquares-for-iphone/#comments</comments>
		<pubDate>Mon, 25 Aug 2008 01:10:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[iPhone]]></category>
		<category><![CDATA[dcsquares]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=554</guid>
		<description><![CDATA[I&#8217;m putting the finishing touches on porting DCSquares to the iPhone.  I&#8217;ll have more info soon, but in the meantime here&#8217;s a teaser:

]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m putting the finishing touches on porting <a href="http://dcsquares.c99.org/">DCSquares</a> to the iPhone.  I&#8217;ll have more info soon, but in the meantime here&#8217;s a teaser:<br />
<lj-embed id="1"/></p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/08/24/dcsquares-for-iphone/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Last.fm for iPhone and iPod Touch version 1.0.2 Released</title>
		<link>http://www.c99.org/2008/08/19/last-fm-for-iphone-and-ipod-touch-version-1-0-2-released/</link>
		<comments>http://www.c99.org/2008/08/19/last-fm-for-iphone-and-ipod-touch-version-1-0-2-released/#comments</comments>
		<pubDate>Tue, 19 Aug 2008 16:51:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[iPhone]]></category>
		<category><![CDATA[last.fm]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=553</guid>
		<description><![CDATA[Last.fm for iPhone and iPod Touch version 1.0.2 is now available on iTunes. Here&#8217;s what&#8217;s new:
* Bug fixes and stability improvements
* Defer error popups until device is unlocked
* Disable auto-lock during playback
* Improve playback of tracks while device is locked
* Start buffering the next track near the end of the current track
* Fix display of [...]]]></description>
			<content:encoded><![CDATA[<p>Last.fm for iPhone and iPod Touch version 1.0.2 is now available on iTunes. Here&#8217;s what&#8217;s new:</p>
<p>* Bug fixes and stability improvements<br />
* Defer error popups until device is unlocked<br />
* Disable auto-lock during playback<br />
* Improve playback of tracks while device is locked<br />
* Start buffering the next track near the end of the current track<br />
* Fix display of events for non-English locales<br />
* Properly display ampersands in radio station titles</p>
<p>To install the update, click &#8220;Check for updates&#8221; at the bottom of your iTunes Applications list, or tap the Updates tab of the App Store on your device.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/08/19/last-fm-for-iphone-and-ipod-touch-version-1-0-2-released/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Last.fm for iPhone and iPod Touch version 1.0.1 Released</title>
		<link>http://www.c99.org/2008/07/25/last-fm-for-iphone-and-ipod-touch-version-1-0-1-released/</link>
		<comments>http://www.c99.org/2008/07/25/last-fm-for-iphone-and-ipod-touch-version-1-0-1-released/#comments</comments>
		<pubDate>Sat, 26 Jul 2008 06:11:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[iPhone]]></category>
		<category><![CDATA[last.fm]]></category>
		<category><![CDATA[mobilescrobbler]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=552</guid>
		<description><![CDATA[Last.fm for iPhone and iPod Touch version 1.0.1 is now available on iTunes.  Here&#8217;s what&#8217;s new:

Better recommendations
Support for multiple playlists for subscribers
Support for My Library
Song name on lock screen
Less buffering over 3G
Retain the search results if you come back to them
Bug, stability and performance fixes

 To install the update, click &#8220;Check for updates&#8221; at [...]]]></description>
			<content:encoded><![CDATA[<p>Last.fm for iPhone and iPod Touch version 1.0.1 is now available on iTunes.  Here&#8217;s what&#8217;s new:
<ul>
<li>Better recommendations</li>
<li>Support for multiple playlists for subscribers</li>
<li>Support for My Library</li>
<li>Song name on lock screen</li>
<li>Less buffering over 3G</li>
<li>Retain the search results if you come back to them</li>
<li>Bug, stability and performance fixes</li>
</ul>
<p> To install the update, click &#8220;Check for updates&#8221; at the bottom of your iTunes Applications list, or tap the Updates tab of the App Store on your device.</p>
<p>Also, we&#8217;ve set up an official group for support requests and feedback here: http://www.last.fm/group/Last.fm+for+iPhone+and+iPod+Touch</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/07/25/last-fm-for-iphone-and-ipod-touch-version-1-0-1-released/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Last.fm for iPhone and iPod Touch now available on iTunes</title>
		<link>http://www.c99.org/2008/07/13/last-fm-for-iphone-and-ipod-touch-now-available-on-itunes/</link>
		<comments>http://www.c99.org/2008/07/13/last-fm-for-iphone-and-ipod-touch-now-available-on-itunes/#comments</comments>
		<pubDate>Mon, 14 Jul 2008 00:28:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[iPhone]]></category>
		<category><![CDATA[ipod]]></category>
		<category><![CDATA[itunes]]></category>
		<category><![CDATA[last.fm]]></category>
		<category><![CDATA[mobilescrobbler]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=546</guid>
		<description><![CDATA[We are pleased to announce the launch of Last.fm on the iPhone and iPod Touch! Sam Steele, our iPhone development army of one, has been cranking away at a full blown Last.fm app on Apple’s mobile platforms for months now and the results are nothing short of insanely great.
Read the rest of the official announcement [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>We are pleased to announce the launch of Last.fm on the iPhone and iPod Touch! Sam Steele, our iPhone development army of one, has been cranking away at a full blown Last.fm app on Apple’s mobile platforms for months now and the results are nothing short of insanely great.</p></blockquote>
<p>Read the rest of the official announcement over at the <a href="http://blog.last.fm/2008/07/13/lastfm-for-iphone-and-ipod-touch">Last.fm blog</a> and <a href="http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=284916679&amp;mt=8">grab Last.fm from the iTunes Store</a> (US, UK, Canada, France, Germany, and Spain for now).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/07/13/last-fm-for-iphone-and-ipod-touch-now-available-on-itunes/feed/</wfw:commentRss>
		<slash:comments>24</slash:comments>
		</item>
		<item>
		<title>Last.fm for iPhone and iPod Touch</title>
		<link>http://www.c99.org/2008/07/10/last-fm-for-iphone-and-ipod-touch/</link>
		<comments>http://www.c99.org/2008/07/10/last-fm-for-iphone-and-ipod-touch/#comments</comments>
		<pubDate>Thu, 10 Jul 2008 18:59:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[iPhone]]></category>
		<category><![CDATA[last.fm]]></category>
		<category><![CDATA[mobilescrobbler]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=603</guid>
		<description><![CDATA[Listen to free streaming radio on your iPhone with Last.fm. Last.fm allows you to listen to your favourite artists, discover new music, and share music with the friends in your iPhone contact list. (http://www.last.fm) Coming soon to the iTunes App Store! Listen to more than 4 million songs over EDGE, 3G, or WiFi.
Digg it
]]></description>
			<content:encoded><![CDATA[<p>Listen to free streaming radio on your iPhone with Last.fm. Last.fm allows you to listen to your favourite artists, discover new music, and share music with the friends in your iPhone contact list. (http://www.last.fm) Coming soon to the iTunes App Store! Listen to more than 4 million songs over EDGE, 3G, or WiFi.</p>
<p><a href="http://digg.com/apple/Last_fm_iPhone_App_Preview/">Digg it</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/07/10/last-fm-for-iphone-and-ipod-touch/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>trac mysql schema optimization</title>
		<link>http://www.c99.org/2008/06/15/trac-mysql-schema-optimization/</link>
		<comments>http://www.c99.org/2008/06/15/trac-mysql-schema-optimization/#comments</comments>
		<pubDate>Sun, 15 Jun 2008 21:29:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[mobilescrobbler]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[trac]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=545</guid>
		<description><![CDATA[For anyone using Trac with the MySQL database backend, check out this ticket: http://trac.edgewall.org/ticket/6986
I applied the database schema changes on the MobileScrobbler trac site, and it&#8217;s reduced the time it takes to modify or create a ticket from several seconds down to less than a second. Finally, no more error 500 pages when an operation [...]]]></description>
			<content:encoded><![CDATA[<p>For anyone using Trac with the MySQL database backend, check out this ticket: http://trac.edgewall.org/ticket/6986</p>
<p>I applied the database schema changes on the MobileScrobbler trac site, and it&#8217;s reduced the time it takes to modify or create a ticket from several seconds down to less than a second. Finally, no more error 500 pages when an operation takes too long to complete!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/06/15/trac-mysql-schema-optimization/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>I win!</title>
		<link>http://www.c99.org/2008/05/29/i-win/</link>
		<comments>http://www.c99.org/2008/05/29/i-win/#comments</comments>
		<pubDate>Fri, 30 May 2008 05:54:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[Life]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=544</guid>
		<description><![CDATA[Yay! I beat adventure mode in Peggle!  Final score: 15,659,720!
]]></description>
			<content:encoded><![CDATA[<p>Yay! I beat adventure mode in <a href="http://www.popcap.com/games/peggle">Peggle</a>!  Final score: 15,659,720!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/05/29/i-win/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MobileScrobbler 1.4.3</title>
		<link>http://www.c99.org/2008/04/07/mobilescrobbler-1-4-3/</link>
		<comments>http://www.c99.org/2008/04/07/mobilescrobbler-1-4-3/#comments</comments>
		<pubDate>Tue, 08 Apr 2008 00:49:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[iPhone]]></category>
		<category><![CDATA[mobilescrobbler]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=541</guid>
		<description><![CDATA[New Features

New radio player HUD contributed by Smooquai
New radio icons contributed by CompC
Add an option to queue tracks while device is asleep
Updated French, German, Italian, Norwegian, and Russian translations

Bug Fixes

Fix a crash that can occur when playing a non-existant playlist
Fix a crash that can occur when the last.fm server is unreachable
Stop caching playlists
Improved detection of [...]]]></description>
			<content:encoded><![CDATA[<p><b>New Features</b>
<ul>
<li>New radio player HUD contributed by Smooquai</li>
<li>New radio icons contributed by CompC</li>
<li>Add an option to queue tracks while device is asleep</li>
<li>Updated French, German, Italian, Norwegian, and Russian translations</li>
</ul>
<p><b>Bug Fixes</b>
<ul>
<li>Fix a crash that can occur when playing a non-existant playlist</li>
<li>Fix a crash that can occur when the last.fm server is unreachable</li>
<li>Stop caching playlists</li>
<li>Improved detection of duplicate scrobbles</li>
</ul>
<p>Head on over to the <a href="http://dev.c99.org/MobileScrobbler/">MobileScrobbler website</a> for installation instructions, screenshots, and more!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/04/07/mobilescrobbler-1-4-3/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>MythTV Upcoming Recordings 1.0.1</title>
		<link>http://www.c99.org/2008/03/02/mythtv-upcoming-recordings-1-0-1/</link>
		<comments>http://www.c99.org/2008/03/02/mythtv-upcoming-recordings-1-0-1/#comments</comments>
		<pubDate>Sun, 02 Mar 2008 19:17:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[MythTV]]></category>
		<category><![CDATA[dashboard]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=539</guid>
		<description><![CDATA[
I&#8217;ve updated my MythTV Upcoming Recordings dashboard widget to work with the latest changes to the MythTV protocol.  You can grab the latest version from http://dev.c99.org/mythupcoming/
]]></description>
			<content:encoded><![CDATA[<p><a href="http://dev.c99.org/mythupcoming/"><img src="http://dev.c99.org/mythupcoming/mythupcoming.png" border=0></a><br />
I&#8217;ve updated my MythTV Upcoming Recordings dashboard widget to work with the latest changes to the MythTV protocol.  You can grab the latest version from http://dev.c99.org/mythupcoming/</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/03/02/mythtv-upcoming-recordings-1-0-1/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Fail Whale</title>
		<link>http://www.c99.org/2008/02/10/fail-whale/</link>
		<comments>http://www.c99.org/2008/02/10/fail-whale/#comments</comments>
		<pubDate>Mon, 11 Feb 2008 04:27:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[Life]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=536</guid>
		<description><![CDATA[General Tso&#8217;s Shrimp = AMAZING
]]></description>
			<content:encoded><![CDATA[<p>General Tso&#8217;s Shrimp = AMAZING</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/02/10/fail-whale/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>West</title>
		<link>http://www.c99.org/2008/02/02/west/</link>
		<comments>http://www.c99.org/2008/02/02/west/#comments</comments>
		<pubDate>Sat, 02 Feb 2008 13:03:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[Life]]></category>
		<category><![CDATA[jaycation]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=531</guid>
		<description><![CDATA[I will be spending the week down in Key West with Jay!  If you need to reach me, I&#8217;ll still be checking my email occasionally.  See you all in a week!
]]></description>
			<content:encoded><![CDATA[<p>I will be spending the week down in Key West with <a href='http://jayyy.livejournal.com/'>Jay</a>!  If you need to reach me, I&#8217;ll still be checking my email occasionally.  See you all in a week!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/02/02/west/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>MobileScrobbler 1.4.0</title>
		<link>http://www.c99.org/2008/01/30/mobilescrobbler-1-4-0/</link>
		<comments>http://www.c99.org/2008/01/30/mobilescrobbler-1-4-0/#comments</comments>
		<pubDate>Thu, 31 Jan 2008 05:20:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[iPhone]]></category>
		<category><![CDATA[mobilescrobbler]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=530</guid>
		<description><![CDATA[New Features

Redesigned layout
Redesigned Now Playing screens by Jan Garcia
Browse your friends and listen to their radio stations
Keeps a list of your recent radio stations so you can easily restart them
Configure MobileScrobbler settings through Preferences.app
Multitouch gestures to ban, love, and skip tracks
View your top artists, top tracks, and top albums
Ban and love tracks from the Recent [...]]]></description>
			<content:encoded><![CDATA[<p><b>New Features</b>
<ul>
<li>Redesigned layout</li>
<li>Redesigned Now Playing screens by Jan Garcia</li>
<li>Browse your friends and listen to their radio stations</li>
<li>Keeps a list of your recent radio stations so you can easily restart them</li>
<li>Configure MobileScrobbler settings through Preferences.app</li>
<li>Multitouch gestures to ban, love, and skip tracks</li>
<li>View your top artists, top tracks, and top albums</li>
<li>Ban and love tracks from the Recent Tracks list</li>
<li>Remove ban and love from the Recently Banned and Recently Loved lists</li>
<li>Browse a top listener&#8217;s profile by tapping their username</li>
<li>Display arbitrary track metadata by passing command line arguments -track, -artist, and -album</li>
<li>Empty the metadata and image caches with the tap of a button</li>
<li>Compatible with firmware 1.1.3</li>
</ul>
<p><b>Bug Fixes</b>
<ul>
<li>Increase scrobbler daemon startup delay</li>
<li>Continue updating the progress scrubber while scrolling</li>
<li>Hide the &#8216;Now Playing&#8217; button if nothing is playing</li>
<li>Don&#8217;t scrobble podcasts and audio books</li>
<li>Require confirmation for loving / banning tracks</li>
<li>Plug a few memory leaks</li>
<li>Faster loading of track and user data</li>
</ul>
<p><a href="http://dev.c99.org/MobileScrobbler/">MobileScrobbler</a> 1.4.0 should be available in Commuity Sources in a few days.  Until then, it is available from my beta repository at http://dev.c99.org/iphone-beta/</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/01/30/mobilescrobbler-1-4-0/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Upcoming server downtime</title>
		<link>http://www.c99.org/2008/01/26/upcoming-server-downtime/</link>
		<comments>http://www.c99.org/2008/01/26/upcoming-server-downtime/#comments</comments>
		<pubDate>Sun, 27 Jan 2008 00:34:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[Life]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=528</guid>
		<description><![CDATA[DreamHost will be moving the server that hosts most of my sites on February 8th, and they&#8217;ll be unreachable during that time.  This also affects those of you with sites on my hosting plan.
Due to continued space and power constraints in our primary data center, we will be moving the “randy” cluster to one [...]]]></description>
			<content:encoded><![CDATA[<p>DreamHost will be moving the server that hosts most of my sites on February 8th, and they&#8217;ll be unreachable during that time.  This also affects those of you with sites on my hosting plan.</p>
<blockquote><p>Due to continued space and power constraints in our primary data center, we will be moving the “randy” cluster to one of our newer data centers.  This move will begin Friday, February 8, at 10PM PST, and is expected to last up to 8 hours, until Saturday, February 9, 6AM PST.  All web servers, mail servers, file servers, and MySQL servers in the randy cluster will be unreachable during this time.</p>
<p>The following web servers will be off line during this move:</p>
<p>alexander arkanoid attila baht bam bob bomberman breakout caesar centipede contra cornelius cortes crack cruisin cssmania deblume defender digdug dinar dinero dollar donkeykong ellipsis euro fidget forint franc gage galaga gap go grant guilder happy herod ikari ike io ivan jezebel joust karnov katamari khan klax korben kroner lamlam leeloo lei limbo-randy limbo-randy2 limbo-randy3 lira lucky mark marvin morgan napoleon nexus nimitz orbital pacman paperboy patton peso pizarro pound qbert quid rhod ruble rupee rygar scagnetti schilling scipio shekel slaughter slimy tetris ugh ultima venice vex vito wasabi yen yoda zorg</p>
<p>If your site is on any of these servers this status post applies to you. Also, if you check the “Account Status” button on the upper right hand of the webpanel ( https://panel.dreamhost.com ) you should see your cluster under the section “Your Email Server:”. We are also sending out an email announcement about this downtime window to affected customers.</p>
<p>We do apologize for this inconvenience. Rest assured we will do everything possible to keep downtime to a minimum.</p>
<p>If you have any questions regarding the server move, please contact our support team.</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/01/26/upcoming-server-downtime/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>MobileScrobbler on firmware 1.1.3</title>
		<link>http://www.c99.org/2008/01/24/mobilescrobbler-on-firmware-1-1-3/</link>
		<comments>http://www.c99.org/2008/01/24/mobilescrobbler-on-firmware-1-1-3/#comments</comments>
		<pubDate>Fri, 25 Jan 2008 05:58:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[iPhone]]></category>
		<category><![CDATA[mobilescrobbler]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=527</guid>
		<description><![CDATA[MobileScrobbler 1.4 will be compatible with firmware 1.1.3 using Nate True&#8217;s soft update jailbreak.
Starting with MobileScrobbler 1.4 beta 2 which should be out tomorrow, there will be two separate packages, one for firmware 1.1.1/1.1.2 and one for firmware 1.1.3, due to slight differences in the new firmware that are not compatible with previous versions.
Update: MobileScrobbler [...]]]></description>
			<content:encoded><![CDATA[<p>MobileScrobbler 1.4 will be compatible with firmware 1.1.3 using <a href="http://cre.ations.net/blog/post/iphone-113-jailbreak-released">Nate True&#8217;s soft update jailbreak</a>.</p>
<p>Starting with MobileScrobbler 1.4 beta 2 which should be out tomorrow, there will be two separate packages, one for firmware 1.1.1/1.1.2 and one for firmware 1.1.3, due to slight differences in the new firmware that are not compatible with previous versions.</p>
<p><b>Update:</b> MobileScrobbler 1.4 beta 2 is now available in my beta repository for both firmware 1.1.1/1.1.2 and the new firmware 1.1.3.  You can grab it by adding the following repository and installing the MobileScrobbler package:
<pre>http://dev.c99.org/iphone-beta/</pre>
<p><b>Update #2:</b> I&#8217;ve removed 1.4b2 for firmware 1.1.1/1.1.2 as it appears to be crashing.  I&#8217;ll downgrade my iPhone tonight and get that resolved.  The package for firmware 1.1.3 is still working fine.</p>
<p><b>Update #3:</b> MobileScrobbler 1.4b2-3 should resolve the &#8220;Database too busy&#8221; and &#8220;Scrobbler is working offline&#8221; issues.  Please refresh your sources in Installer.app and install this update.</p>
<p><b>Update #4:</b> MobileScrobbler 1.4b3 is now available.  This release is compatible with the dev team&#8217;s official 1.1.3 jailbreak.  Also, a separate package is no longer required for the 1.1.3 version.  Please remove the &#8220;MobileScrobbler (1.1.3)&#8221; package and use the standard MobileScrobbler package instead.  <b>NOTE: If you use the dev team&#8217;s official 1.1.3 jailbreak, the preferences application will not be able to start / stop the scrobbler when you press the toggle button.  You must reboot your device for the change to take affect.</b></p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/01/24/mobilescrobbler-on-firmware-1-1-3/feed/</wfw:commentRss>
		<slash:comments>34</slash:comments>
		</item>
		<item>
		<title>MobileScrobbler 1.4 beta 1</title>
		<link>http://www.c99.org/2008/01/22/mobilescrobbler-1-4-beta-1/</link>
		<comments>http://www.c99.org/2008/01/22/mobilescrobbler-1-4-beta-1/#comments</comments>
		<pubDate>Wed, 23 Jan 2008 01:55:00 +0000</pubDate>
		<dc:creator>Sam Steele</dc:creator>
				<category><![CDATA[iPhone]]></category>
		<category><![CDATA[mobilescrobbler]]></category>

		<guid isPermaLink="false">http://www.c99.org/blog/?p=526</guid>
		<description><![CDATA[The first public beta of MobileScrobbler 1.4 is ready for testing!  Add the following repository to Installer.app to upgrade to 1.4b1:
http://dev.c99.org/iphone-beta/
Please report any issues into the bug tracker, and if you want to revert back to 1.2.6 you can safely downgrade using the Community Sources package.
]]></description>
			<content:encoded><![CDATA[<p>The first public beta of <a href="http://dev.c99.org/MobileScrobbler/1.4/">MobileScrobbler 1.4</a> is ready for testing!  Add the following repository to Installer.app to upgrade to 1.4b1:
<pre>http://dev.c99.org/iphone-beta/</pre>
<p>Please report any issues into the <a href="http://dev.c99.org/MobileScrobbler/newticket">bug tracker</a>, and if you want to revert back to 1.2.6 you can safely downgrade using the Community Sources package.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.c99.org/2008/01/22/mobilescrobbler-1-4-beta-1/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 22.273 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2010-03-12 06:38:04 -->
