Sigurd Snørteland


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;
        }
	}
}