tweet search – one code, three mobile platforms (wp7, monodroid, monotouch)

Here is the presentation and the source code from the speech I held at the Norwegian .Net User Group last wednesday.

tweet search

‘tweet search’ is a mobile twitter search app written in c# for Windows Phone 7, iPhone and Android. All three apps share the same twitter search code, but they have custom gui code (WP7=Silverlight, iPhone=MonoTouch, Android=MonoDroid).



Go to the bottom of this article to find links to the source code & presentation.

Shared twitter-code:

public class Twitter { public event EventHandler twitteSearchCompleted; public Twitter() { } public void search(string searchText) { if (searchText != "") { WebClient client = new WebClient(); client.DownloadStringAsync(new Uri("http://search.twitter.com/search.atom?q=" + searchText)); client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted); } } private void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { if (e.Error == null) { List twitteList = new List(); XDocument xml = XDocument.Parse(e.Result); XNamespace atomNS = "http://www.w3.org/2005/Atom"; var tempList = (from entry in xml.Descendants(atomNS + "entry") select new TwitterObject() { ID = entry.Element(atomNS + "id").Value, Title = entry.Element(atomNS + "title").Value, Date = DateTime.Parse(entry.Element(atomNS + "published").Value), AuthorName = entry.Descendants(atomNS + "author").Elements(atomNS + "name").FirstOrDefault().Value, AuthorUri = entry.Descendants(atomNS + "author").Elements(atomNS + "uri").FirstOrDefault().Value, AuthorImage = (from imgElement in entry.Elements(atomNS + "link") where imgElement.Attribute("rel") != null && imgElement.Attribute("rel").Value.Contains("image") && imgElement.Attribute("href") != null select imgElement.Attribute("href").Value).FirstOrDefault() }).ToList(); twitteList = tempList.ToList(); twitteSearchCompleted(twitteList, null); } } } public class TwitterObject { public string ID { get; set; } public string Title { get; set; } public DateTime Date { get; set; } public string AuthorName { get; set; } public string AuthorUri { get; set; } public string AuthorImage { get; set; } }

WP7 code:

namespace tweet_search_wp7 { public partial class MainPage : PhoneApplicationPage { public MainPage() { InitializeComponent(); } #region UI private void txtSearch_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { this.Focus(); } } private void txtSearch_LostFocus(object sender, RoutedEventArgs e) { search(); } private void lboTwitte_SelectionChanged(object sender, SelectionChangedEventArgs e) { lboTwitte.SelectedIndex = -1; } private void imgSearch_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { search(); } #endregion #region Twitter private void search() { lblLoading.Visibility = Visibility.Visible; lboTwitte.Opacity = 0.3; Twitter twitter = new Twitter(); twitter.twitteSearchCompleted += new EventHandler(twitter_downloadCompleted); twitter.search(txtSearch.Text); } private void twitter_downloadCompleted(object sender, EventArgs e) { List list = (List)sender; lboTwitte.ItemsSource = list; lblLoading.Visibility = Visibility.Collapsed; lboTwitte.Opacity = 1; } #endregion } }

MonoTouch code:

namespace tweetsearchiphone { public partial class MainPage : UIViewController { #region Constructors // The IntPtr and initWithCoder constructors are required for items that need // to be able to be created from a xib rather than from managed code public MainPage (IntPtr handle) : base(handle) { Initialize (); } [Export("initWithCoder:")] public MainPage (NSCoder coder) : base(coder) { Initialize (); } public MainPage () : base("MainPage", null) { Initialize (); } void Initialize () { } #endregion #region UI public override void ViewDidLoad() { base.ViewDidLoad(); lblTwitter.Text = ""; txtSearch.ShouldReturn = delegate { txtSearch.ResignFirstResponder(); search(); return true; }; imgSearch.TouchUpInside += HandleImgSearchTouchUpInside; } void HandleImgSearchTouchUpInside (object sender, EventArgs e) { search(); } #endregion #region Twitter private void search() { Twitter twitter = new Twitter(); twitter.twitteSearchCompleted += twitter_downloadCompleted; twitter.search(txtSearch.Text); } void twitter_downloadCompleted (object sender, EventArgs e) { List list = (List) sender; BeginInvokeOnMainThread (delegate { foreach(TwitterObject tweet in list) { lblTwitter.Text += tweet.Date.ToString() + Environment.NewLine + tweet.Title + Environment.NewLine + Environment.NewLine; } }); } #endregion } }

MonoDroid code:

namespace tweet_search_android { [Activity (Label = "tweet search", MainLauncher = true)] public class ButtonActivity : Activity { List list = new List(); Android.Widget.Button btnSearch; TextView lblTwitter; EditText txtSearch; private List photo_ids = new List() { Resource.drawable.bird }; protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); SetContentView(Resource.layout.main); ImageView btnSearch = FindViewById(Resource.id.btnSearch); btnSearch.SetImageResource(photo_ids[0]); btnSearch.Click += new EventHandler(btnSearch_Click); txtSearch = FindViewById(Resource.id.txtSearch); lblTwitter = FindViewById(Resource.id.lblTwitter); } void btnSearch_Click(object sender, EventArgs e) { if (list.Count == 0) { Twitter twitter = new Twitter(); twitter.twitteSearchCompleted += new EventHandler(twitter_twitteSearchCompleted); twitter.search("nnug"); } else { foreach (TwitterObject twitter in list) { lblTwitter.Text += twitter.Date.ToString() + System.Environment.NewLine + twitter.Title + System.Environment.NewLine + System.Environment.NewLine; } list = new List(); } } void twitter_twitteSearchCompleted(object sender, EventArgs e) { list = (List)sender; } } }