Info tech mobileapp week7 assignment
362 ! CHAPTER 12 GETTING STARTED WITH MONOTOUCH AND MONO FOR ANDROID
The intention of this section was to give you an idea of the project structure, and basic interaction between the UI and code. To build upon this, you will now tackle a more complex native MonoTouch and Mono for Android app.
BUILDING THE DERBY APP WITH MONO The idea of the Derby App is to build the same app over all of the mobile platforms covered in this book. The MonoTouch and Mono for Android versions are very similar to the other versions that you have built thus far.
The requirements are to list the roster from the Lansing Derby Vixens roller derby team as the primary function, and then list all the roller derby teams in the world with the ability to see their team rosters.
MonoTouch For the iOS version of the Derby App, you fi rst need to create an iPhone Tabbed MonoTouch application as shown in Figure 12-25. The Tabbed application type provides a template that contains two views linked to a tab controller that you can use to start your project. The iPhone
Certain features of Android apps require permission from the user before the app can gain access to those features. Before users install your app on their mobile devices, they will be prompted with a list of features that your app will be using, providing the opportunity for the user to opt not to install your app because of a particular feature. In the Mono for Android app, to enable the features that your app is going to use, you must use the Mono for Android Project Options dialog box as shown in Figure 12-24.
FIGURE 12!24: Mono for Android permissions
c12.indd 362c12.indd 362 28/07/12 6:09 PM28/07/12 6:09 PM
Building the Derby App with Mono ! 363
User Interface When it boils down to it, the Derby App does not contain a complex user interface. All of the screens are simply lists of data. When the user interface is simple, creating the controls within code is oftentimes an option. For the MonoTouch Derby App, the UITableView controls are created and added programmatically.
To get started, rename the fi rst view code and XIB fi le from FirstViewController to VixensController. For the code fi les, it’s best to right-click the class name and select the Rename option from the Refactor menu as shown in Figure 12-26. This ensures that all of the constructors and places that the class is initiated are changed as well.
With the fi rst ViewController renamed, your project should look similar to the project shown in Figure 12-27.
project type was selected because the only interface to the Derby App that has been shown has been a mobile phone interface, not a tablet interface. The Storyboard option was not selected, just to provide additional examples of how to work with user interfaces within iOS and Interface Builder.
FIGURE 12!25: Tabbed MonoTouch iPhone app creation
FIGURE 12!26: Rename refactor within MonoDevelop
c12.indd 363c12.indd 363 28/07/12 6:09 PM28/07/12 6:09 PM
364 ! CHAPTER 12 GETTING STARTED WITH MONOTOUCH AND MONO FOR ANDROID
Creating the Vixen Table View Just as with a native iOS app created in Objective-C, the ViewDidLoad event is fi red right after the View has loaded. Within the VixensController, this is where the table view that contains the list of all the Lansing Derby Vixens will be created.
Creating a UI Table view is as simple as:
" Creating a new instance of the UITableView object.
" Setting the dimensions and where in the view it will be rendered.
" Adding the newly created table view as a subview to the view (in this case the Vixens View) you are working with.
The following code shows this process.
public override void ViewDidLoad () { base.ViewDidLoad (); UITableView tableView = new UITableView(); tableView.Frame = new RectangleF (0, 0, this.View.Frame.Width,this.View.Frame.Height); this.View.AddSubview(tableView); }
Populating the Vixens Table View After the table view has been created, the data that will populate the view needs to be received. To do this, I have created a helper class named Network that contains static helper functions that will retrieve the data needed to populate the table views throughout the Derby App.
To get the roster of a team, the GetRoster function is called with the team name of the data you are looking for. You have a few different methods within .NET to choose when it comes to mapping a JSON response to an object. The following example simply loops through each of the JSON items returned and manually builds the DerbyName object:
public static List<DerbyName> GetRoster(string leagueName) { List<DerbyName> tmpRtn = new List<DerbyName>(); String requestURL = “http://derbynames.gravityworksdesign.com /DerbyNamesService.svc /DerbyNames?$filter=League%20eq%20’” + leagueName + “’”;
HttpWebResponse response = GetServiceResponse(requestURL); JsonObject fullJsonObject = (JsonObject)JsonObject.Load(response.GetResponseStream());
var rosterData = fullJsonObject[“d”];
foreach (JsonObject singleEntry in rosterData) { tmpRtn.Add(new DerbyName(singleEntry[“DerbyNameId”],
FIGURE 12!27: First view renamed
c12.indd 364c12.indd 364 28/07/12 6:09 PM28/07/12 6:09 PM
Building the Derby App with Mono ! 365
singleEntry[“Name”],singleEntry[“Number”] ,singleEntry[“League”])); }
return tmpRtn; }
private static HttpWebResponse GetServiceResponse (string url) { HttpWebResponse tmpRtn; var request = (HttpWebRequest) WebRequest.Create (url);
tmpRtn = (HttpWebResponse) request.GetResponse ();
return tmpRtn; }
The data that is returned is a list of DerbyName name objects for the team name that was passed in; in this case, “Lansing Derby Vixens.”
After the data has been retrieved, you need to set the DataSource property on the TableView that will display the data. In this case, set the DataSource to a new TableViewDataSource object (which you will create in the future), passing it a list of strings that contains the names of the Lansing Derby Vixens. You can do this by adding the following code to the ViewDidLoad event:
public override void ViewDidLoad () { base.ViewDidLoad (); UITableView tableView; string teamName = “Lansing Derby Vixens”; List<DerbyName> fullRosterData = Network.GetRoster(teamName); List<string> data = new List<string>(); fullRosterData.ForEach(derbyName => data.Add(derbyName.Name));
tableView = new UITableView(); tableView.DataSource = new TableViewDataSource(data); tableView.Frame = new RectangleF (0, 0, this.View.Frame.Width,this.View.Frame.Height); this.View.AddSubview(tableView); }
To fully bind the data to the table view, you need to implement a few more functions. Create these functions in a new class named TableViewDataSource, which was bound to the data source of the table view.
The TableViewDataSource class contains a constructor that contains the data that will be bound to the TableView, in this case a list of strings:
public TableViewDataSource (List<string> list) { this.list = list; }
c12.indd 365c12.indd 365 28/07/12 6:09 PM28/07/12 6:09 PM
366 ! CHAPTER 12 GETTING STARTED WITH MONOTOUCH AND MONO FOR ANDROID
For the table view to know how many rows it needs to select, you must implement the RowsInSection method. In this case, you just return the count of the number of items in the list object, which is the list of strings that you populated when the view loaded:
public override int RowsInSection (UITableView tableview, int section) { return list.Count; }
The magic really happens in the GetCell method. This method is called for the number of times that was returned in the RowsInSection. In your code, create a new cell, get the data for the correct position in the list object, and then return the cell you created, which will be added to the table:
public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath) { UITableViewCell cell = tableView.DequeueReusableCell (kCellIdentifier);
if (cell == null) { cell = new UITableViewCell (UITableViewCellStyle.Default,kCellIdentifier); }
cell.TextLabel.Text = list[indexPath.Row]; return cell; }
When complete, the TableViewDataSource class contains two methods and one constructor:
private class TableViewDataSource : UITableViewDataSource { static NSString kCellIdentifier = new NSString (“DerbyName”); private List<string> list;
public TableViewDataSource (List<string> list) { this.list = list; }
public override int RowsInSection (UITableView tableview, int section) { return list.Count; }
public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath) { UITableViewCell cell = tableView.DequeueReusableCell (kCellIdentifier); if (cell == null) { cell = new UITableViewCell ( UITableViewCellStyle.Default, kCellIdentifier);
c12.indd 366c12.indd 366 28/07/12 6:09 PM28/07/12 6:09 PM
Building the Derby App with Mono ! 367
}
cell.TextLabel.Text = list[indexPath.Row]; return cell; } }
With the table view data wired, you should be able to run the app, (by pressing the run button in the toolbar) and view the Derby Vixen roster as shown in Figure 12-28.
Leagues/Team Name The Leagues tab lists all of the roller derby leagues in a TableView. The name of this controller is LeagueController. Creating the Leagues tab is very similar to the Vixens tab, with two exceptions. The fi rst is the function that is called to obtain the data that is displayed in the list. Because this is a list of leagues, you will call the GetLeagueData function found in the network class. This function returns a list of League objects, returned from the Derby service.
public static List<League> GetLeagueData() { List<League> tmpRtn = new List<League>(); string requestURL = “http://derbynames.gravityworksdesign.com /DerbyNamesService.svc/Leagues”;
HttpWebResponse response = GetServiceResponse(requestURL); JsonObject fullJsonObject = (JsonObject)JsonObject.Load(response.GetResponseStream()); var leagueData = fullJsonObject[“d”];
foreach (JsonObject singleEntry in leagueData) { tmpRtn.Add(new League(singleEntry[“LeagueId”],singleEntry[“LeagueName”])); }
return tmpRtn; }
The second difference between the views is that the League Roster screen that lists the team members for the selected league should appear when a cell is touched on the leagues view. To accomplish this, within the LeaguesController code fi le, create a new delegate that derives from UITableViewDelegate. The RowSelected event is wired up inside this delegate, which simply creates a new LeagueRoster view (which you have not created yet), sets the team name you want to load, and then pushes the view to the iOS navigation stack to make the LeagueRoster view show.
private class TableViewDelegate : UITableViewDelegate { LeaguesController leagueController; private List<string> list;
FIGURE 12!28: Derby Vixens roster rendering in Table View
c12.indd 367c12.indd 367 28/07/12 6:09 PM28/07/12 6:09 PM
368 ! CHAPTER 12 GETTING STARTED WITH MONOTOUCH AND MONO FOR ANDROID
public TableViewDelegate(List<string> list, LeaguesController controller) { this.leagueController = controller; this.list = list; }
public override void RowSelected (UITableView tableView, NSIndexPath indexPath) { LeagueRoster roster = new LeagueRoster(); roster.TeamName = list[indexPath.Row];
leagueController.NavigationController.PushViewController(roster,true); } }
League Roster The LeagueRoster view is shown when a user selects a league/team from the team name page. The name of this view is LeagueRoster. This view is almost identical to the Vixens view with one exception. To know which league/team you are loading the roster for the name needs to be passed into this view. You can do this by adding a property named TeamName to the LeagueRoster class:
public string TeamName { get; set; }
The GetRoster function within the ViewDidLoad event uses the TeamName property instead of the hard-coded “Lansing Derby Vixens” value to retrieve the roster for the team name set in the TeamName property:
public override void ViewDidLoad () { base.ViewDidLoad (); UITableView tableView;
List<DerbyName> fullRosterData = Network.GetRoster(this.TeamName); List<string> data = new List<string>(); fullRosterData.ForEach(derbyName => data.Add(derbyName.Name));
tableView = new UITableView(); tableView.DataSource = new TableViewDataSource(data); tableView.Frame = new RectangleF (0, 0,this.View.Frame.Width, this.View.Frame.Height);
this.View.AddSubview(tableView); }
Figure 12-29 shows the roster view for a selected team.
Mono for Android For the Android version of the Derby App, you fi rst need to create a new Mono for Android application as shown in Figure 12-30. This will provide a simple Android app to which you will then be able to add your specifi c derby logic. FIGURE 12!29: Roster
View for team
c12.indd 368c12.indd 368 28/07/12 6:09 PM28/07/12 6:09 PM
Building the Derby App with Mono ! 369
This version of the Derby App is going to use a few Android features that were introduced with Android version 3.0, Gingerbread. To be on the safe side, for this project you will want to target the latest Android SDK. You will want to make sure that the Android project options have the minimum API level set to 14 for Android 4.0 as shown in Figure 12-31.
FIGURE 12!30: Creating a new Mono for Android project
FIGURE 12!31: Setting the minimum SDK to the latest version
c12.indd 369c12.indd 369 28/07/12 6:09 PM28/07/12 6:09 PM
370 ! CHAPTER 12 GETTING STARTED WITH MONOTOUCH AND MONO FOR ANDROID
User Interface As with Android apps that are created using Eclipse and Java, the user interface XML fi les for Mono for Android apps are located under the Resources # Layouts directory of the project. For the Derby app you are going to have one main view that contains two tabs that will load the interface using a concept called a fragment. A fragment is simply a chunk of user interface logic with its own life cycle. The following lists the fi les required for the Derby App user interface:
" Main.axml: The Main layout fi le for the Mono for Android Derby app contains a linear layout and a frame layout. The frame layout is used as a container where the fragments will be loaded:
<?xml version=”1.0” encoding=”utf-8”?> <LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android” android:orientation=”vertical” android:layout_width=”fill_parent” android:layout_height=”fill_parent”> <FrameLayout android:id=”@+id/fragmentContainer” android:layout_width=”match_parent” android:layout_height=”0dip” android:layout_weight=”1” /> </LinearLayout>
" Tab.axml: Each tab that is created for the Derby app will use Tab.axml for the interface. The tab’s user interface is simply a list view that will render the data:
<ListView xmlns:android=”http://schemas.android.com/apk/res/android” android:id=”@+id/DerbyData” android:layout_width=”wrap_content” android:layout_height=”wrap_content” />
" List_item.axml: Remember from Chapter 6 that each item that is rendered in a list view will have a layout fi le to specify how the data will be rendered in the list view. For the Mono for Android Derby app, just a TextView control is used to render the data. If you wanted to get fancy and add a team icon or player photo, this layout would be modifi ed to accomplish this:
<?xml version=”1.0” encoding=”utf-8”?> <TextView xmlns:android=”http://schemas.android.com/apk/res/android” android:layout_width=”fill_parent” android:layout_height=”fill_parent” />
With the layout fi les completed, you can add the code that is required to display the UI. In the OnCreate method of the Main.cs fi le, the navigation mode of the action must be set to Tabs, which will allow for a tabbed interface to be rendered. Calls to custom methods named AddVixenTab and AddLeagueTab add the fragments that will fi nish creating the interaction of the tab and the main layout:
c12.indd 370c12.indd 370 28/07/12 6:09 PM28/07/12 6:09 PM
Building the Derby App with Mono ! 371
protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle);
SetContentView (Resource.Layout.Main); this.ActionBar.NavigationMode = ActionBarNavigationMode.Tabs; AddVixenTab (“Vixens”); AddLeagueTab (“Teams”); }
To add the tabs to the Action Bar, fi rst create an ActionBar.New tab with the text that will be rendered on the screen:
var tab = this.ActionBar.NewTab (); tab.SetText (tabText);
After the tab has been created, you need to wire the TabSelected event to load the user interface of the tab that was selected. In this case the Vixen Tab was selected, therefore you should remove the LeagueTab from focus and display a new Vixen Tab to the user using fragments.
tab.TabSelected += delegate(object sender, ActionBar.TabEventArgs e) { m_vixenTab = new VixenTab(); e.FragmentTransaction.Add (Resource.Id.fragmentContainer, m_vixenTab); e.FragmentTransaction.Remove(m_leagueTab); };
After the TabSelected event has been wired, you simply need to add the newly created ActionBar .Tab to the ActionBar of the Derby App:
this.ActionBar.AddTab (tab);
Creating the tabs for both the Vixens and the Leagues/Teams names is similar, with the only exception of what is rendered in the tab (VixenTab or LeagueTab). The following code shows the completed logic for adding both the Vixens and Leagues tabs:
private void AddVixenTab (string tabText) { var tab = this.ActionBar.NewTab (); tab.SetText (tabText); tab.TabSelected += delegate(object sender, ActionBar.TabEventArgs e) { m_vixenTab = new VixenTab(); e.FragmentTransaction.Add (Resource.Id.fragmentContainer, m_vixenTab);
e.FragmentTransaction.Remove(m_leagueTab); }; this.ActionBar.AddTab (tab); }
c12.indd 371c12.indd 371 28/07/12 6:09 PM28/07/12 6:09 PM
372 ! CHAPTER 12 GETTING STARTED WITH MONOTOUCH AND MONO FOR ANDROID
private void AddLeagueTab (string tabText) { var tab = this.ActionBar.NewTab (); tab.SetText (tabText); tab.TabSelected += delegate(object sender, ActionBar.TabEventArgs e) { m_leagueTab = new LeagueTab(); e.FragmentTransaction.Add (Resource.Id.fragmentContainer, m_leagueTab);
e.FragmentTransaction.Remove(m_vixenTab); }; this.ActionBar.AddTab (tab); }
Getting The Vixens Roster With the user interface complete, you can now complete the logic to render the data on the screen for the Vixens tab. The user interface that will be infl ated for the Vixens tab is the Tab.axml. If you remember, this layout fi le contains only a ListView control:
var view = inflater.Inflate (Resource.Layout.tab, container, false);
To get the roster for the Lansing Derby Vixens, you call the static GetRoster function found within the Network class. This code is identical to the logic that was created for the MonoTouch app:
List<DerbyName> fullDerbyNameData = Network.GetRoster(teamName);
After the user interface has been infl ated and the data has been received, you can bind the data to the ListView:
derbyData.Adapter = new ArrayAdapter<string> (container.Context, Resource.Layout.list_item, data.ToArray());
The following code shows the entire OnCreateView function for the Vixens tab:
public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { base.OnCreateView (inflater, container, savedInstanceState); var view = inflater.Inflate (Resource.Layout.tab, container, false); var derbyData = view.FindViewById<ListView> (Resource.Id.DerbyData); string teamName = “Lansing Derby Vixens”; List<DerbyName> fullDerbyNameData = Network.GetRoster(teamName); List<string> data = new List<string>(); fullDerbyNameData.ForEach(derbyName => data.Add(derbyName.Name)); derbyData.Adapter = new ArrayAdapter<string> (container.Context, Resource.Layout.list_item, data.ToArray()); return view; }
c12.indd 372c12.indd 372 28/07/12 6:09 PM28/07/12 6:09 PM
Building the Derby App with Mono ! 373
Getting the Leagues and TeamName The logic for creating the Leagues tab is similar to the logic used for creating the Vixens tab. First you call the static function GetLeagues found within the Network class to retrieve a list of leagues. Again this code is identical to the logic used in the MonoTouch Derby App to retrieve the leagues.
Also, the ItemClick event on the derbyData list view has been wired so that when it is clicked, a new activity is started that will display the roster for the team that was selected. To accomplish this, you create a new Intent, and save the TeamName as an extra that is pushed to the newly created activity:
derbyData.ItemClick += delegate (object sender, ItemEventArgs args) { string teamName = ((TextView)args.View).Text; Intent rosterList = new Intent(container.Context, typeof(LeagueRoster)); rosterList.PutExtra(“TeamName”, teamName); this.StartActivity(rosterList); };
The entire OnCreateView function for the Leagues tab is shown here:
public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { base.OnCreateView (inflater, container, savedInstanceState); var view = inflater.Inflate (Resource.Layout.tab, container, false); var derbyData = view.FindViewById<ListView> (Resource.Id.DerbyData); List<League> fullLeagueData = Network.GetLeagueData(); List<string> data = new List<string>(); fullLeagueData.ForEach(league => data.Add(league.LeagueName)); derbyData.Adapter = new ArrayAdapter<string> (container.Context, Resource.Layout.list_item, data.ToArray()); derbyData.ItemClick += delegate (object sender, ItemEventArgs args) { string teamName = ((TextView)args.View).Text; Intent rosterList = new Intent(container.Context, typeof(LeagueRoster)); rosterList.PutExtra(“TeamName”, teamName); this.StartActivity(rosterList); }; return view; }
Getting The Team Roster The pattern of pushing data to a list view should be starting to look very familiar to you by now. The team roster view again is the same concept. The LeagueRoster class inherits from ListActivity so there is no need to infl ate the user interface that contains the ListView control.
c12.indd 373c12.indd 373 28/07/12 6:09 PM28/07/12 6:09 PM
374 ! CHAPTER 12 GETTING STARTED WITH MONOTOUCH AND MONO FOR ANDROID
Also, to load the team roster, you need to get the TeamName that was passed into the Activity. You can do this by calling the GetStringExtra method found within the Intent. In this case, if one is not passed in, you default to “Lansing Derby Vixens”:
string teamName = Intent.GetStringExtra(“TeamName”) ?? “Lansing Derby Vixens”;
The entire onCreate method for the LeagueRoster class is as follows:
protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); string teamName = Intent.GetStringExtra(“TeamName”) ?? “Lansing Derby Vixens”; List<DerbyName> fullRosterData = Network.GetRoster(teamName); List<string> data = new List<string>(); fullRosterData.ForEach(derbyName => data.Add(derbyName.Name)); ListAdapter = new ArrayAdapter<string> (this, Resource.Layout.list_item, data.ToArray()); }
With all of the code in place, the Mono for Android Derby app should look similar to Figure 12-32.
OTHER USEFUL MONOTOUCH/MONO FEATURES
In the two example projects up to this point we have provided the basics for creating MonoTouch and Mono for Android applications that will go out to a web service and render the collected data on the screen. By no means do we feel that we have covered every possible situation you may need to develop a solution for, so we wanted to fi nish this chapter by providing a few more short examples that will help you out when discovering how MonoTouch and Mono for Android work.
Local Storage Even if your application is using a web service for retrieving information, at some point you may need to save information on the device.
MonoTouch plist For apps written in MonoTouch, property lists (plists) are the simplest way to store information on the device. In the Mac world, many applications use the plist format to store
FIGURE 12!32: Mono for Android Derby App
c12.indd 374c12.indd 374 28/07/12 6:09 PM28/07/12 6:09 PM
Other Useful MonoTouch/Mono Features ! 375
application settings, information about the application, and even serialized objects. It’s best to keep the data contained in these fi les simple, though.
Different variable types require different functions to retrieve and get the plist setting. The following example illustrates the different ways to retrieve and then save plist settings within MonoTouch:
var plist = NSUserDefaults.StandardUserDefaults; // get plist item string stringSetting = plist.StringForKey(“StringSetting”); int intSetting = plist.IntForKey(“myIntKey”); bool boolSetting = plist.BoolForKey(“myBoolKey”); // save plist item plist.SetString(“string”, “StringSetting”); plist.SetInt(1, “IntSetting”); plist.SetBool(true, “BoolSetting”); plist.Synchronize();
Mono for Android Shared Preferences For apps written in Mono for Android, shared preferences are the simplest way to store information on the device. The Android framework enables shared preferences to be restricted to a single app, or even shared as world-readable/writable, allowing all apps to access these settings if you choose.
The following code illustrates saving and retrieving shared preferences:
// save shared preference item ISharedPreferences saveSharedPreference = GetPreferences (FileCreationMode.Append); ISharedPreferencesEditor editor = saveSharedPreference.Edit (); editor.PutString (“StringSetting”, “string”); editor.PutInt (“IntSetting”, 1); editor.PutBoolean (“BoolSetting”, false); editor.Commit (); // get shared preference item ISharedPreferences getSharedPreference = GetPreferences (FileCreationMode.Append); string stringSetting = getSharedPreference.GetString (“StringSetting”, “Default Value”);
int intSetting = getSharedPreference.GetInt (“IntSetting”, 1); bool boolSetting = getSharedPreference.GetBoolean (“BoolSetting”, true);
GPS One of the great benefi ts of mobile devices is GPS functionality. Once you are able to get over the hurdles of learning the basic functions within MonoTouch and Mono for Android, working with the GPS functions can be a great deal of fun.
c12.indd 375c12.indd 375 28/07/12 6:09 PM28/07/12 6:09 PM
376 ! CHAPTER 12 GETTING STARTED WITH MONOTOUCH AND MONO FOR ANDROID
MonoTouch GPS For MonoTouch, you can fi nd the GPS functionality in the MonoTouch.CoreLocation namespace. The CLLocationManager is the class you will be using to obtain the GPS information. Keeping with the pattern on MonoTouch trying to match the Objective-C way of doing things, MonoTouch uses the delegate design pattern to handle the location updates. Simply put, GPS in MonoTouch is just a matter of:
" Instantiating a CLLocationManager object.
" Confi guring settings on the CLLocation manager such as accuracy.
" Assigning a delegate that will handle the location updates.
The following code creates the CLLocationManager object, and tells it to start tracking your location. You assign the delegate of the location manager object to an object named LocationDelegate, which is a custom-created class.
CLLocationManager locationManager = new CLLocationManager ();
locationManager.Delegate = new LocationDelegate (); locationManager.StartUpdatingLocation ();
The custom-created LocationDelegate object derives from CLLocationManagerDelegate and will handle location updates when they occur. In this example, you simply write the new location to the console:
public class LocationDelegate : CLLocationManagerDelegate { public LocationDelegate () : base() { } public override void UpdatedLocation (CLLocationManager manager, CLLocation newLocation, CLLocation oldLocation) { Console.WriteLine(newLocation.Speed.ToString () + “meters/s”); Console.WriteLine(newLocation.Coordinate.Longitude.ToString () + “˚”); Console.WriteLine(newLocation.Coordinate.Latitude.ToString () + “˚”); Console.WriteLine(newLocation.Altitude.ToString () + “meters”); } }
Mono for Android GPS Mono for Android provides an interface named ILocationListener located in the Android .Locations namespace that will allow GPS information to be collected from the device:
public class MonoForAndroidOther : Activity,ILocationListener
Before looking at the code, it’s important to note that you must ensure that your app has permission to obtain the level of GPS information you are looking to retrieve. Figure 12-33 shows a Mono for Android app with both ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION permissions set.
c12.indd 376c12.indd 376 28/07/12 6:09 PM28/07/12 6:09 PM
Summary ! 377
With the correct permissions set, you need to create a new LocationManager object when the activity fi rst starts. This object manages the aspects of the location data that is being sent to your app from the operating system. You can set a Criteria object, which contains settings such as Accuracy, on the LocationManager to give you fi ne control of the data that is being sent from the operating system. The RequestLocationUpdates function enables a developer to set how often the GPS data is being sent to your app, which is useful to help conserve battery life.
The following code example creates a new LocationManager with no Accuracy requirements. The last location is also retrieved and written to the console if it exists.
LocationManager locationManager = (LocationManager)GetSystemService(LocationService);
var criteria = new Criteria() { Accuracy = Accuracy.NoRequirement }; string bestProvider = locationManager.GetBestProvider(criteria, true);
Location lastLocation = locationManager.GetLastKnownLocation(bestProvider);
if (lastLocation != null) { Console.WriteLine(“Last location, lat: {0}, long: {1}”, lastLocation.Latitude, lastLocation.Longitude); }
locationManager.RequestLocationUpdates(bestProvider, 5000, 2, this);
When implementing the ILocationListener, the OnLocationChanged event is required. This event is fi red when the location has changed. In the following example, the Latitude and Longitude are written to the console:
void ILocationListener.OnLocationChanged (Location location) { Console.WriteLine(“Location updated, lat: {0}, long: {1}”, location.Latitude, location.Longitude); }
SUMMARY This chapter spent a great deal of time describing the MonoTouch and Mono for Android platform. After reading this chapter, you should be comfortable installing the development tools and getting started developing and debugging your fi rst MonoTouch and Mono for Android app. Every development platform has trade-offs. As discussed in this chapter, the major downside to creating mobile apps with MonoTouch and Mono for Android is that they are third-party commercial
FIGURE 12!33: Android location permission
c12.indd 377c12.indd 377 28/07/12 6:09 PM28/07/12 6:09 PM
378 ! CHAPTER 12 GETTING STARTED WITH MONOTOUCH AND MONO FOR ANDROID
products, which can leave you waiting for updates to the standard device SDKs and out of luck if the company goes out of business.
If you have a good working understanding of .NET and C#, creating mobile apps in MonoTouch and Mono for Android may be a viable option. If you or your team already has this understanding, you may be able to roll out your mobile app faster and cheaper. However, just understanding .NET is not enough. To develop MonoTouch and Mono for Android apps, a good working knowledge of the key components of an iOS and Android app are required to be successful.
c12.indd 378c12.indd 378 28/07/12 6:09 PM28/07/12 6:09 PM