Monday, April 30, 2012

Using Portable Library tools for MonoTouch and MonoDroid part 1....

Warning: This is only a part 1 post - I haven't worked out how to do part 2 yet!

To get MonoTouch and MonoDroid building with portable library project references in VS2010:
  1. Install the Portable Library tools -  http://visualstudiogallery.msdn.microsoft.com/b0e0b5e9-e138-410b-ad10-00cb3caf4981
  2. Install VSMonoTouch -  https://github.com/follesoe/VSMonoTouch
    - be sure to also copy across the v1.0 framework assemblies in the instructions there
  3. Follow the instructions on  http://jpobst.blogspot.co.uk/2012/04/mono-for-android-portable-libraries-in.html
    - which basically say to install a file called "MonoAndroid,Version=v1.6+.xml" in "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.0\Profile\Profile2\SupportedFrameworks" with content:

    <?xml version="1.0" encoding="utf-8"?><Framework DisplayName="Mono for Android" Identifier="MonoAndroid" MaximumVersion="*" MinimumVersion="1.6" Profile="*"/>
  4. Installl a similar file called "VSMonoTouch,Version=v1.0+.xml" in the same directory and for its content use:

    <?xml version="1.0" encoding="utf-8"?><Framework  DisplayName="VS MonoTouch" Identifier=".NETFramework" MaximumVersion="1.0" MinimumVersion="1.0" Profile="*" />(Note: this file is even hackier than the MonoDroid one - it points the portable tools at Full .Net v1.0 in line with what the VSMonoTouch plugin does!)
  5. That's it!

Although.... if you do want to use System.Net classes within MonoDroid, then you'll also need to add a proxy assembly for it - a MonoDroid class library project producing a DLL called System.Net and containing a single C# file with contents:

using System.Runtime.CompilerServices;
 
/*
 * not available in MonoDroid :/
[assembly: TypeForwardedTo(typeof(System.Net.Sockets.UdpAnySourceMulticastClient))]
[assembly: TypeForwardedTo(typeof(System.Net.Sockets.UdpSingleSourceMulticastClient))]
[assembly: TypeForwardedTo(typeof(System.Net.DnsEndPoint))]
[assembly: TypeForwardedTo(typeof(System.Net.WriteStreamClosedEventHandler))]
[assembly: TypeForwardedTo(typeof(System.Net.WriteStreamClosedEventArgs))]
*/
 
[assemblyTypeForwardedTo(typeof(System.Net.Cookie))]
[assemblyTypeForwardedTo(typeof(System.Net.CookieCollection))]
[assemblyTypeForwardedTo(typeof(System.Net.CookieContainer))]
[assemblyTypeForwardedTo(typeof(System.Net.CookieException))]
[assemblyTypeForwardedTo(typeof(System.Net.Dns))]
[assemblyTypeForwardedTo(typeof(System.Net.EndPoint))]
[assemblyTypeForwardedTo(typeof(System.Net.WebProxy))]
[assemblyTypeForwardedTo(typeof(System.Net.HttpRequestHeader))]
[assemblyTypeForwardedTo(typeof(System.Net.HttpStatusCode))]
[assemblyTypeForwardedTo(typeof(System.Net.HttpVersion))]
[assemblyTypeForwardedTo(typeof(System.Net.DecompressionMethods))]
[assemblyTypeForwardedTo(typeof(System.Net.WebRequest))]
[assemblyTypeForwardedTo(typeof(System.Net.HttpWebRequest))]
[assemblyTypeForwardedTo(typeof(System.Net.WebResponse))]
[assemblyTypeForwardedTo(typeof(System.Net.HttpWebResponse))]
[assemblyTypeForwardedTo(typeof(System.Net.ICertificatePolicy))]
[assemblyTypeForwardedTo(typeof(System.Net.ICredentials))]
[assemblyTypeForwardedTo(typeof(System.Net.HttpContinueDelegate))]
[assemblyTypeForwardedTo(typeof(System.Net.IPAddress))]
[assemblyTypeForwardedTo(typeof(System.Net.IPEndPoint))]
[assemblyTypeForwardedTo(typeof(System.Net.IPHostEntry))]
[assemblyTypeForwardedTo(typeof(System.Net.IWebRequestCreate))]
[assemblyTypeForwardedTo(typeof(System.Net.NetworkCredential))]
[assemblyTypeForwardedTo(typeof(System.Net.ProtocolViolationException))]
[assemblyTypeForwardedTo(typeof(System.Net.ServicePoint))]
[assemblyTypeForwardedTo(typeof(System.Net.ServicePointManager))]
[assemblyTypeForwardedTo(typeof(System.Net.WebHeaderCollection))]
[assemblyTypeForwardedTo(typeof(System.Net.SocketAddress))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.SocketException))]
[assemblyTypeForwardedTo(typeof(System.Net.WebClient))]
[assemblyTypeForwardedTo(typeof(System.Net.OpenReadCompletedEventHandler))]
[assemblyTypeForwardedTo(typeof(System.Net.OpenReadCompletedEventArgs))]
[assemblyTypeForwardedTo(typeof(System.Net.OpenWriteCompletedEventHandler))]
[assemblyTypeForwardedTo(typeof(System.Net.OpenWriteCompletedEventArgs))]
[assemblyTypeForwardedTo(typeof(System.Net.DownloadStringCompletedEventHandler))]
[assemblyTypeForwardedTo(typeof(System.Net.DownloadStringCompletedEventArgs))]
[assemblyTypeForwardedTo(typeof(System.Net.UploadStringCompletedEventHandler))]
[assemblyTypeForwardedTo(typeof(System.Net.UploadStringCompletedEventArgs))]
[assemblyTypeForwardedTo(typeof(System.Net.DownloadProgressChangedEventHandler))]
[assemblyTypeForwardedTo(typeof(System.Net.DownloadProgressChangedEventArgs))]
[assemblyTypeForwardedTo(typeof(System.Net.UploadProgressChangedEventHandler))]
[assemblyTypeForwardedTo(typeof(System.Net.UploadProgressChangedEventArgs))]
[assemblyTypeForwardedTo(typeof(System.Net.WebException))]
[assemblyTypeForwardedTo(typeof(System.Net.WebExceptionStatus))]
[assemblyTypeForwardedTo(typeof(System.Net.NetworkInformation.NetworkAddressChangedEventHandler))]
[assemblyTypeForwardedTo(typeof(System.Net.NetworkInformation.NetworkChange))]
[assemblyTypeForwardedTo(typeof(System.Net.NetworkInformation.NetworkInterface))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.AddressFamily))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.LingerOption))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.MulticastOption))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.IPv6MulticastOption))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.Socket))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.ProtocolType))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.SelectMode))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.SocketAsyncOperation))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.SocketAsyncEventArgs))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.SocketError))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.SocketFlags))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.SocketOptionLevel))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.SocketOptionName))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.SocketShutdown))]
[assemblyTypeForwardedTo(typeof(System.Net.Sockets.SocketType))]


This approach based on a hint in  http://stackoverflow.com/questions/10361457/is-it-possible-to-use-a-portable-class-library-that-references-system-net-in-mon

I will blog more about this stuff... when I get the final part working - which is building these projects in MonoDevelop on the Mac - currently that's #fail :(

Sunday, April 29, 2012

Using Flurry Analytics from MonoTouch, MonoDroid and WP7 (MvvmCross)

UPDATE - want to do more - someone took this code on to the next level - http://blog.martinlegris.com/2012/11/14/flurry-api-in-mono-for-android/

----

Within a recent MvvmCross project, we used www.flurry.com for our analytics.


Basically what we did was log all important events from the ViewModel using an IAnalytics interface, and then each platform supplied an implementation of IAnalytics like:
WP7:
public class FlurryAnalytics : IAnalytics
{
    public const string ApiKeyValue = "--- your key ---";

    public void StartSession()
    {
        FlurryWP7SDK.Api.StartSession(ApiKeyValue);
    }

    public void LogEvent(string eventName)
    {
        FlurryWP7SDK.Api.LogEvent(eventName);
    }
}
public class FlurryAnalytics : IAnalytics
{
    public const string ApiKeyValue = "37SHD8L8VATPBS88AMHU";

    public void StartSession()
    {
        FlurryAPI.StartSession (ApiKeyValue);
    }

    public void LogEvent(string eventName)
    {
        FlurryAPI.LogEvent(eventName);
    }
}
Android (a bit more complicated - it needs a hook from every start/stop of an activity):
public class FlurryAnalytics : IAnalytics, IAndroidActivityTracker
{
    public const string ApiKeyValue = "--- your key ---";

    private readonly IntPtr _flurryClass;
    private readonly IntPtr _flurryOnStartSession;
    private readonly IntPtr _flurryOnEndSession;
    private readonly IntPtr _flurryLogEvent;

    public FlurryAnalytics()
    {
        _flurryClass = JNIEnv.FindClass("com/flurry/android/FlurryAgent");
        _flurryOnStartSession = JNIEnv.GetStaticMethodID(_flurryClass, "onStartSession",
                                                         "(Landroid/content/Context;Ljava/lang/String;)V");
        _flurryOnEndSession = JNIEnv.GetStaticMethodID(_flurryClass, "onEndSession", "(Landroid/content/Context;)V");
        _flurryLogEvent = JNIEnv.GetStaticMethodID(_flurryClass, "logEvent", "(Ljava/lang/String;)V");
    }

    public void StartSession()
    {
        // not used in Android - Android relies on Activity tracking instead
    }

    public void LogEvent(string eventName)
    {
        ExceptionSafe(() => JNIEnv.CallStaticVoidMethod(_flurryClass, _flurryLogEvent, new JValue(new Java.Lang.String(eventName))));
    }

    private static void ExceptionSafe(Action action)
    {
        try
        {
            action();
        }
        catch (ThreadAbortException)
        {
            throw;
        }
        catch (Exception exception)
        {
            UITrace.Trace("Exception seen in calling Flurry through JNI " + exception.ToLongString());
        }
    }

    public void OnStartActivity(Activity activity)
    {
        ExceptionSafe(() => JNIEnv.CallStaticVoidMethod(_flurryClass, _flurryOnStartSession, new JValue(activity), new JValue(new Java.Lang.String(ApiKeyValue))));
    }

    public void OnStopActivity(Activity activity)
    {
        ExceptionSafe(() => JNIEnv.CallStaticVoidMethod(_flurryClass, _flurryOnEndSession, new JValue(activity)));
    }
}

Each of these platforms simply injected their implementation into IoC during setup - .i.e:

    this.RegisterServiceInstance (new FlurryAnalytics());
Among the messages recorded were the creation of every ViewModel - which gave us an easy to see picture of which pages were being used in our app. We did this through a BaseViewModel implementation which logged its type name on creation - simple, quick and clean:

public abstract class BaseViewModel 
    : MvxViewModel
    , IMvxServiceConsumer
{
    #region Protected API

    protected BaseViewModel()
    {
        Analytics.LogEvent("ViewModelCreate:" + GetType().Name);
    }

    protected IAnalytics Analytics
    {
        get { return this.GetService(); }
    }
}

Thursday, April 26, 2012

Calculating distance between lat/lng points in MvvmCross


Currently mvvmcross has deliberately not included lat/lng calculations.
The motivation for this was:
  • to keep the code size down - not every app needs those calculations
  • further I have in my mind that some calculations are better done by native libraries rather than generic C# - e.g. some polygon calculations would benefit from native hardware acceleration. This type of thought is there not only for geo-calcuations, but also for other areas - e.g. whereever things like image manipulation are required, then using native acceleration makes sense.
It may be that mvvmcross provides an "official" location helper IoC plugin (or set of extension methods) at some point in the future... e.g. it maybe that we build an IoC plugin around http://xamarin.com/mobileapi... and I'm also very happy for others to build and publish them too.
So....
At a very simple level, working out the distance between two lat/lng points is easy to do in C# - there are heaps of fairly simple code example available like the excellent javascript ones at http://www.movable-type.co.uk/scripts/latlong.html - and there are heaps of libraries on github and codeplex likehttp://sharpmap.codeplex.com/
In RunSat (including on iPhone) I use:
using System;

namespace Cirrious.NewRunSat.Core.Models.Utils
{
    public class DistanceCalcs
    {
        /// 
        /// Calculates the distance between two points of latitude and longitude.
        /// Great Link - http://www.movable-type.co.uk/scripts/latlong.html
        /// 
        /// 
First coordinate.
        /// 
First coordinate.
        /// 
Second coordinate.
        /// 
Second coordinate.
        /// the distance in metres
        public static Double DistanceInMetres(double lat1, double lon1, double lat2, double lon2)
        {

            if (lat1 == lat2 && lon1 == lon2)
                return 0.0;

            var theta = lon1 - lon2;

            var distance = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) +
                           Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) *
                           Math.Cos(deg2rad(theta));

            distance = Math.Acos(distance);
            if (double.IsNaN(distance))
                return 0.0;

            distance = rad2deg(distance);
            distance = distance * 60.0 * 1.1515 * 1609.344;

            return (distance);
        }

        private static double deg2rad(double deg) {
          return (deg * Math.PI / 180.0);
        }

        private static double rad2deg(double rad) {
          return (rad / Math.PI * 180.0);
        }
    }
}
Note that the license on this snippet is CC attribution - because its derived from movable type -http://www.movable-type.co.uk/scripts/latlong.html:
I offer these formulæ & scripts for free use and adaptation as my contribution to the open-source info-sphere from which I have received so much. You are welcome to re-use these scripts [under a simple attribution license, without any warranty express or implied] provided solely that you retain my copyright notice and a reference to this page.

Supplying separate iPad and iPhone views in a single Universal iOS MvvmCross project (MonoTouch)


The default convention used by the MvvmCross platform is to automatically register all views using reflection.
    protected virtual void InitializeViews()
    {
        var container = this.GetService();

        foreach (var pair in GetViewModelViewLookup())
        {
            Add(container, pair.Key, pair.Value);
        }
    }
where GetViewModelViewLookup returns a dictionary of ViewModel type to View type:
    protected virtual IDictionary GetViewModelViewLookup(Assembly assembly, Type expectedInterfaceType)
    {
        var views = from type in assembly.GetTypes()
                    let viewModelType = GetViewModelTypeMappingIfPresent(type, expectedInterfaceType)
                    where viewModelType != null
                    select new { type, viewModelType };

        return views.ToDictionary(x => x.viewModelType, x => x.type);
    }
In universal iPad/iPhone apps you do occasionally want to include multiple views for each viewmodel - using one view in the iPad and one view in the iPhone.
To do this, there are now (literally just now!) some attributes available to mark your views as being "unconventional" - these are:
The last of these is probably what you want in this case - you could implement simple iPhone/iPad switching for a MainViewModel using two views declared like:
[MvxFormFactorSpecificView(MvxTouchFormFactor.Phone)]
public class MyIPhoneView : BaseView
{
    // iphone specific view ...
}

[MvxFormFactorSpecificView(MvxTouchFormFactor.Pad)]
public class MyIPadView : BaseView
{
    // ipad specific view ...
}

Alternatively if you want a very custom configuration, you can override all 'convention-based' behaviour - you can implement your own override of GetViewModelViewLookup - e.g.:
protected override IDictionary GetViewModelViewLookup(Assembly assembly, Type expectedInterfaceType)
{
    if (IsIPad)
    {
        return new Dictionary() 
        {
            { typeof(HomeViewModel), typeof(IPadHomeView) },
            { typeof(DetailViewModel), typeof(IPadDetailView) },
            { typeof(AboutViewModel), typeof(SharedAboutView) },
        };
    }
    else
    {
        return new Dictionary() 
        {
            { typeof(HomeViewModel), typeof(IPhoneHomeView) },
            { typeof(DetailViewModel), typeof(IPhoneDetailView) },
            { typeof(AboutViewModel), typeof(SharedAboutView) },
        };
    }
}

Note that eventually you may decide that you need additional ViewModels as well as Views for the iPad app - the iPad has, after all, a much bigger screen - in this case you can add them manually. Ultimately, when your app hits a few million users, you may even decide to completely branch the tablet code away from the phone code - but that can generally wait until you hit that few million mark...

Getting Location information in MvvmCross

I've added a lesson in the tutorial that shows the IMvxGeoLocationWatcher location fetching interface being used.
      private void DoStartStop()
      {
          if (!IsStarted)
          {
              _watcher.Start(new MvxGeoLocationOptions() { EnableHighAccuracy = true }, OnNewLocation, OnError);
          }
          else
          {
              _watcher.Stop();
          }
          IsStarted = !IsStarted;
      }
      private void OnError(MvxLocationError error)
      {
          // TODO - shuold handle the error better than this really!
          LastError = error.Code.ToString();
      }
      private void OnNewLocation(MvxGeoLocation location)
      {
          if (location != null && location.Coordinates != null)
          {
              Lat = location.Coordinates.Latitude;
              Lng = location.Coordinates.Longitude;
          }
      }  
This seems to work OK on:
I haven't yet written the code for WinRT...

Saturday, April 21, 2012

Experiments with Portable Class LIbraries - mvvmcross

I've tried to refactor mvx into portable class libraries today.

Initial results have been quite promising - so I think it will happen for real at some point soon.

The basic approach I've taken is to create a "core" mvvmcross portable library:


This single project contains 90% of the mvx code.

The code that doesn't fit into this is all platform specific and interface driven - i.e. it's things like the platform specific views, navigation and container code; or platform specific services like file IO, GPS/location information, launchers and choosers, etc. This code is now moved to small platform specific library projects:




The Binding and Dialog projects for now remain untouched - that's OK because they were never shared by the "core" projects any way - so they are always platform specific and there's no advantage in making those portable.

Moving on the sample projects, well the core of these can now be made portable:


So there is only one "core" project shared between all three ui projects - which is NICE :)


However, there are some problems currently…

  • I've not quite finished! I've got the tutorial sample up and running, but there are still a few platform specific injection objects to make before all the samples will run... and there's lots more testing needed:
    • A Json object
    • A Native Color object
    • A Visibility Color object
  • ObservableCollections are a bit unpleasant to use - INotifyCollectionChanged doesn't exist as a portable interface :/ Because of this any kind of observable collection will need to be wrapped for presentation on wp7
  • ValueConverters are a bit unpleasant to use – IValueConverter doesn't exists as a portable interface :/  Because of this all value converters need to be wrapped before use on wp7
  • I've no idea how this would work in WinRT yet – there will definitely be reflection and interface issues in .net 4.5
  • I don't know what the next version (v2) of the portable class libraries will look like yet!
  • The monotouch and monodroid visual studio tooling isn't working nicely yet – you can't yet reference a portable library project from mt and md, you can only reference a compiled assembly – which means you can't do multi-platform refactoring.
  • Forcing the core projects to be portable may be too limiting... the list of assemblies you can now reference is small.... 

I need to think about this some more.... I think portable class libraries is probably the right direction moving forwards - in the long term! But it's not all upside....

Thursday, April 19, 2012

Wow - PlayStationSuite Open Beta - C# on the PS Vita

Someone tweeted about this -  http://www.playstation.com/pss/developer/index_e.html

So I downloaded it and had thirty minutes of playtime :)

Here's a video of my experience: http://youtu.be/FSZl1-FvUgE





Firstly I played with the UI Composer (designer) - and set up a tip calc like display (like the one in https://github.com/slodge/MvvmCross/ for MonoTouch, MonoDroid, WP7 and WinRT)

It was easy to do - just drag and drop the UI elements, then set their properties, including the generated variable name to use.

When finished, I just hit "Build" in the designer - this spits out:
  • a language csv resource file
  • a C# wrapper for that resource file
  • a scene.cs file - just a partial class with a constructor - this then becomes your "view controller" file for you to edit as you please
  • a scene.composer.cs file - this contains the generated code for the rest of the partial class - basically lots of layout code which matches what you drew in the designer


With that done I then I opened the main PSS Studio (looks and feels like MonoDevelop)

In this studio I:
- ran the new project wizard
- followed the simple instructions in the bundled help file to add the high level ui reference to the project and to add a little code (3 lines?) to help it render
- used project|add to add the files generated above
- tweaked the BuildAction Property for the Csv file to 'content'
- used the monodevelop editor to add the code for tipCalc - this was normal C# code:





using System;
using System.Collections.Generic;
using Sce.Pss.Core;
using Sce.Pss.Core.Imaging;
using Sce.Pss.Core.Environment;
using Sce.Pss.HighLevel.UI;
 
namespace PssUI1
{
    public partial class Scene1 : Scene
    {
        public Scene1()
        {
            InitializeWidget();
   HookThingsUp();
  }
  
  private void HookThingsUp()
  {
   SliderPercent.ValueChanged += (s, e) => {
    TipPercentText.Text = ((int)e.Value).ToString();
    Recalc();
   };
   TipPercentText.TextChanged += (s, e) => {
    SliderPercent.Value = int.Parse(TipPercentText.Text);
    Recalc();
   };
   TotalText.TextChanged += (s, e) => {
    Recalc();
   };
   Recalc();
  }
  
  private void Recalc()
  {
   var total = double.Parse(TotalText.Text);
   var tip = SliderPercent.Value * total / 100.0;
   total += tip;
   this.TipLabel.Text = tip.ToString();
   this.TotalLabel.Text = total.ToString(); 
  } 
    }
}


That was it!

Then I could hit F5 and the code ran in a PS Vita simulator:

That was it!

:) :) :) :) :) :) :) :) :) :) :) :) :) :) :) :) :) :) :) :) :)

 
For a full blow by blow video: 
http://youtu.be/FSZl1-FvUgE


Really impressed - good job Mono (Xamarin?) and Sony - really good job :)

Sunday, April 15, 2012

video of me talking MvvmCross at wpug - MonoTouch, MonoDroid, WP7 and WinRT MVVM - also... the internet is full of kittens



Not the best audio quality - sorry

Also had some techical issues with the projector resolution... and ran out of time - I blame Matt - he asked me for an hour, then cut me down to 45 minutes on the day!

http://vimeo.com/39019207