July 2011 - Posts - More Wally - Wallace B. McClure
in

MoreWally.com

Giving people what they want, More Wally. This is the technical and personal blog site of
Wallace B. (Wally) McClure.

This Blog

Syndication

News

Please goy buy 3-4 copies of my book on MonoTouch titled "Professional Android Programming with Mono for Android for .NET/C# Developers." They make great gifts all year round. Plus, I get about $.25 when you buy a copy.

Technical Sites

More Wally - Wallace B. McClure

This blog will have all kinds of posts about Wally McClure. In it, there will be tons of .NET and computer programming posts as well as Wally's views on life in general. As you might guess, this site and blog help you get More Wally in your life. What more could anyone want? iPhone, Android, MonoTouch, MonoDroid, Mobile, HTML5, .NET, ADO.NET, ASP.NET, AJAX, jQuery, jQuery Mobile, ASP.NET AJAX, and Windows Azure............follow me on twitter at Wally

July 2011 - Posts

  • Start an application on boot with Mono for Android #Android #monodroid

    A question recently went through the Mono for Android mailing lists regarding how to automatically start an application on Android using Mono for Android.  Basically, how do you load on boot with Mono for Android.  I had this issue a few months ago.  Thankfully, Jon Pryor helped me with it, so I thought I would post it with a little more info.  Basically, its a 3 step process.

    1. The first step is to ask for the boot permission in the AndroidManifest.xml file.  I used the following manifest file.  I am doing some location based stuff in it, so I'll just bold the important part.
      <?xml version="1.0" encoding="utf-8"?>
      <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" android:versionCode="1">
        <application>
        </application>
        <uses-sdk android:minSdkVersion="4" />
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
        <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
      </manifest>
    2. The next step is to create a BroadcastReceiver.  The code I have used is below.  The key part for me is to check for the Android.Content.Intent.ActionBootCompleted Intent via the IntentFilter.
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using Android.App;
      using Android.Content;
      using Android.OS;
      using Android.Runtime;
      using Android.Views;
      using Android.Widget;
      using Android.Util;
      using Android.Locations;
       
      namespace BootCompleted
      {
          [BroadcastReceiver]
          [IntentFilter(new[] { Android.Content.Intent.ActionBootCompleted },
                  Categories = new[] { Android.Content.Intent.CategoryDefault }
          )]
          public class ReceiveBoot : BroadcastReceiver
          {
              public override void OnReceive(Context context, Intent intent)
              {
                  Toast.MakeText(context, "Received intent!", ToastLength.Short).Show();
                  
                  if ((intent.Action != null) && 
                      (intent.Action == 
                      Android.Content.Intent.ActionBootCompleted))   
                  {         // Start the service or activity      
                      context.ApplicationContext.StartService(new Intent(context, typeof(LocationService)));
                  } 
              }
          }
      }
    3. Finally, I have a Service.  An Android Service is not part of this specifically, but I need to start something, so a Service makes sense.  When the device moves, a notification is sent to the UI.
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
       
      using Android.App;
      using Android.Content;
      using Android.OS;
      using Android.Runtime;
      using Android.Views;
      using Android.Widget;
      using Android.Util;
      using Android.Locations;
       
      namespace BootCompleted
      {
          [Service]
          public class LocationService : Service, 
              Android.Locations.ILocationListener
          {
              private NotificationManager nm;
              private IBinder binder;
              string bestProvider;
       
              public LocationService()
              {
                  binder = new LocalBinder(this);
              }
       
              public class LocalBinder : Binder
              {
                  LocationService self;
       
                  public LocalBinder(LocationService self)
                  {
                      this.self = self;
                  }
       
                  public LocationService Service
                  {
                      get { return self; }
                  }
              }
       
              private LocationManager lm;
              public override void OnCreate()
              {
                  nm = (NotificationManager)GetSystemService(NotificationService);
                  ShowNotification();
                  Criteria cr = new Criteria();
                  cr.Accuracy = Accuracy.Coarse;
                  cr.PowerRequirement = Power.Low;
                  cr.AltitudeRequired = false;
                  cr.BearingRequired = false;
                  cr.SpeedRequired = false;
                  cr.CostAllowed = true;
                  String serviceString = Context.LocationService;
                  lm = (LocationManager)GetSystemService(serviceString);
                  bestProvider = lm.GetBestProvider(cr, false);
                  Location l = lm.GetLastKnownLocation(bestProvider);
                  lm.RequestLocationUpdates(bestProvider, 5000, 100f, this);
              }
       
              public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
              {
                  Log.Info("LocationService""Received start id " + startId + ": " + intent);
                  return StartCommandResult.Sticky;
              }
       
              public override void OnDestroy()
              {
                  nm.Cancel(Resource.String.local_service_started);
                  Toast.MakeText(thisResource.String.local_service_stopped, ToastLength.Short).Show();
                  lm.RemoveUpdates(this);
                  StopSelf();
              }
       
              public override IBinder OnBind(Intent intent)
              {
                  return binder;
              }
       
              void ShowNotification()
              {
                  var text = GetText(Resource.String.local_service_started);
                  var notification = new Notification(Resource.Drawable.stat_sample, text, System.Environment.TickCount);
                  PendingIntent contentIntent = PendingIntent.GetActivity(this, 0, 
                      new Intent(thistypeof(Activity1)), 0);
                  notification.SetLatestEventInfo(this, 
                      GetText(Resource.String.local_service_label), text, contentIntent);
                  nm.Notify(Resource.String.local_service_started, notification);
              }
       
              public void OnLocationChanged(Android.Locations.Location location)
              {        
                  var text = "Lat: " + location.Latitude.ToString() +
                      " Lon: " + location.Longitude.ToString();
                  var notification = new Notification(Resource.Drawable.stat_sample, text, System.Environment.TickCount);
                  PendingIntent contentIntent = PendingIntent.GetActivity(this, 0, 
                      new Intent(thistypeof(Activity1)), 0);
                  notification.SetLatestEventInfo(this, GetText(Resource.String.local_service_label), text, contentIntent);
                  nm.Notify(Resource.String.local_service_started, notification);
                  Toast.MakeText(this, text, ToastLength.Short).Show();
              }
       
              public void OnProviderDisabled(string provider)
              {
                  //throw new NotImplementedException();
              }
       
              public void OnProviderEnabled(string provider)
              {
                  //throw new NotImplementedException();
              }
       
              public void OnStatusChanged(string provider, int status, Bundle extras)
              {
                  //throw new NotImplementedException();
              }
          }
       
      }

  • Status of Monotouch and Mono for Android #monotouch #monodroid

    It looks like Xamarin is now the official stewards (once again) of Mono, Monotouch, Mono for Android, Visual Studio Tools for Mono and associated Mono tools.  This is really good news for us in the developer world. 

    1. There is now official support in the products once again from a known source.
    2. Monotouch/Mono for Android will be easier to develop than learning ObjectiveC, XCode, Java, Eclipse, and whatever.

    As Gordon Gekko said in "Wall Street - Money Never Sleeps:" Buy My Books! (they'll help as well)

    Monotouch book

    Mono for Android book

  • Dismissing the iPad UIPopover in the iPad/iOS with Monotouch

    I've been trying to figure out how to dismiss the popover in the iPad/iOS for a couple of days. it hasn't been crucial, just something i kinda needed to do. I spent the last couple of hours digging into this, and boom, I finally found it. I just need to call .Dismiss(bool) on the UIPopoverController. Problem solved.
  • Threading in Monotouch/iPhone/iOS FTW!

    I was writing a sample for showing off some advanced features in Monotouch with the UITableView.  Specifically, I was showing off how to scale a UITableViewCell based on the content within the cell.  One of the items in the cell was a UIImage who's content is being pulled from the Internet.  I was doing this all synchronously.  Unfortunately, the sliding of the table was a little herky/jerky due to the synchronicity of the image download.  I knew I needed to pull this out of the UI thread and perform this on  background thread.  By using the threadpool, the problem has been resolved.  Here's what I did:

            public string TwitterImage{
                set {
                    ThreadPool.QueueUserWorkItem(new WaitCallback(SetTwitterImage), value);
                }
            }
           
            private void SetTwitterImage(object state)
            {
                var img = Convert.ToString(state);
                NSUrl nsUrl = new NSUrl(img);
                NSData data = NSData.FromUrl(nsUrl);
                InvokeOnMainThread(delegate{
                      uitvccImage.Image = new UIImage(data);

                });
            }
     

    FYI, if you manually create your own threads (in other words, use an actual Thread object), you will need to wrap your code in:

       using (var ns = new NSAutoReleasePool ()){  
          // Your code goes here.
        }

     

  • Yayyyyy Cloud, iPhone, and Android with Monotouch and Mono for Android

    In my MonoTouch (iPhone) and Mono for Android code samples, I have a series of examples where I connect up to WCF/SOAP and REST web services. Its actually kinda painful to get the WCF settings right because the danged thing is so configurable. Getting everything configured right can take some time. Once you get all that done, the connection from iPhone and Android to those services is just like talking to any other WCF/SOAP or REST web service. Assuming that you are using Basic Auth, it just seems to work from that point. Given the general complexities of getting communications to work, this seems to be good enough for me.

    BTW, did I mention that all of my services are exposed from Windows Azure? "To the Cloud"...................."Yayyyyyy Cloud."

    PS.  I am hoping that Monotouch and Mono for Android give way to xTouch and xDroid soon.

  • Geocode with Android - MonoDroid

    I want to perform some geocoding with Android, and MonoDroid.  The key issues are:

    • When you get the text value of an EditText, you are not getting a string, but an IEnumerable<char>.  That's an Android-ism.
    • The Geocoder requires permission to use the internet.
    • If the lookup fails, you recieve a null.
    • The geocoder runs on the UI thread.  It is not asynchronous.  This is different that listening for location changes.

    I looked at some Java Code to do this.  I then did a translation and got this:

                        //There is a bug in the emulator that results in an error.
                        //This code works properly on a device.
                        var add = Convert.ToString(et.Text);
                        Geocoder geocoder = new Geocoder(this, Java.Util.Locale.Default);
                        IList<Android.Locations.Address> result = geocoder.GetFromLocationName(add, 10);
                        if (result != null)
                        {
                            tvLat.Text = result[0].Latitude.ToString();
                            tvLon.Text = result[0].Longitude.ToString();
                        }
     
  • Multi-Line UILabel with MonoTouch on the iPhone and iPad

    I've been trying to figure out how to get a multi-line label working with wordwrap and such in MonoTouch.  The code below will help out.  The key thing that I found is that you HAVE TO set the value of the UILabel BEFORE you call the .SizeToFit().

            public string TwtStatus{
                get{ return _TwitterStatus; }
                set{
                    _TwitterStatus = value;
                    TwitterStatus.Text = _TwitterStatus;
                    TwitterStatus.Lines = 0;
                    TwitterStatus.LineBreakMode = UILineBreakMode.WordWrap;
                    TwitterStatus.Font = UIFont.FromName("Helvetica", 12);
                    TwitterStatus.SizeToFit();
                }
            }

     IIRC, I snagged this code from a blog post somewhere.  Of course, I was the one that broke it by setting .Text to a value after I called the .SizeToFit() method.

2006 - Wallace B. McClure
Powered by Community Server (Non-Commercial Edition), by Telligent Systems