Tuesday, September 25, 2012

Cross platform tasks/intents?

I got asked a question on StackOverflow... http://stackoverflow.com/questions/12564272/making-mono-cross-platform-support-for-task-intent/12564548#12564548

-

I have a application for WP7 and Android, and this application must have supporthas support for "any" connection type (WiFi, NFC, Bluetooth etc)

I have then created a layered model with MVVMCross https://github.com/slodge/MvvmCross

I have an interface for example Android Bluetooth must implement

interface IConnectionService
{
    List<TargetDevice> FindDevices();
    void Connect(TargetDevice targetDevice);
    void Disconnect();
    byte[] Read();
    void Write(byte[] command);
}

I want to be able to request the user for Bluetooth Access, but I do not want to program my UI specifically to Android Bluetooth, so the view and view-model should not know which intent is used, all this should be handled by the class implementing IConnectionService

The issue is that it should also work for Windows Phone which do not use intents, it uses tasks, so how do I make an interface that allows me to make either a Intent request or a task request without anyone knowing what type of request is needed?

----------

This is similar to the way MvvmCross allows users to make phone calls.

When using this pattern:

The ViewModel code consumes a platform independent service via an interface - e.g.:

public interface IMvxPhoneCallTask
{
    void MakePhoneCall(string name, string number);
}

consumed by

    protected void MakePhoneCall(string name, string number)
    {
        var task = this.GetService<IMvxPhoneCallTask>();
        task.MakePhoneCall(name, number);
    }

in https://github.com/slodge/MvvmCross/blob/master/Sample%20-%20CirriousConference/Cirrious.Conference.Core/ViewModels/BaseViewModel.cs

The app setup code injects the platform specific implementation for the interface - e.g:

        RegisterServiceType<IMvxPhoneCallTask, MvxPhoneCallTask>();

In WP7 - this uses the PhoneCallTask - https://github.com/slodge/MvvmCross/blob/master/Cirrious/Cirrious.MvvmCross/WindowsPhone/Platform/Tasks/MvxPhoneCallTask.cs

public class MvxPhoneCallTask : MvxWindowsPhoneTask, IMvxPhoneCallTask
{
    #region IMvxPhoneCallTask Members    

    public void MakePhoneCall(string name, string number)
    {
        var pct = new PhoneCallTask {DisplayName = name, PhoneNumber = number};
        DoWithInvalidOperationProtection(pct.Show);
    }

    #endregion
}

In Droid - it uses the ActionDial Intent - https://github.com/slodge/MvvmCross/blob/master/Cirrious/Cirrious.MvvmCross/Android/Platform/Tasks/MvxPhoneCallTask.cs

public class MvxPhoneCallTask : MvxAndroidTask, IMvxPhoneCallTask
{
    #region IMvxPhoneCallTask Members

    public void MakePhoneCall(string name, string number)
    {
        var phoneNumber = PhoneNumberUtils.FormatNumber(number);
        var newIntent = new Intent(Intent.ActionDial, Uri.Parse("tel:" + phoneNumber));
        StartActivity(newIntent);
    }


    #endregion
}

In Touch - it just uses Urls - https://github.com/slodge/MvvmCross/blob/master/Cirrious/Cirrious.MvvmCross/Touch/Platform/Tasks/MvxPhoneCallTask.cs

public class MvxPhoneCallTask : MvxTouchTask, IMvxPhoneCallTask
{
    #region IMvxPhoneCallTask Members

    public void MakePhoneCall(string name, string number)
    {
        var url = new NSUrl("tel:" + number);
        DoUrlOpen(url);
    }

    #endregion
}


In the vnext version of mvvmcross, this approach is further formalized using plugins - see a brief introduction to these in the 'going portable' presentation at http://slodge.blogspot.co.uk/2012/06/mvvm-mvvmcross-monodroid-monotouch-wp7.html

For example, in vNext the phone call code above is contained in the PhoneCall plugin - https://github.com/slodge/MvvmCross/tree/vnext/Cirrious/Plugins/PhoneCall


One of the challenges of your task may be the word "any" - differences in platform implementation might make it hard to define a cross-platform interface that works across all the platforms for any one of NFC, Bluetooth, etc, let alone all of them.

5 comments:

  1. Hello again, me asking the question on stack
    Any chance you could try to share how you go about injecting code, I have made a interface a ViewModel http://pastebin.com/viUabhhA that uses a interface http://pastebin.com/MnYtnfT3 and an implementation http://pastebin.com/JuPLxG6y but where would you inject that and where is the code that "chooses" which implementation is used? I cant figure it out by looking at your example except I think its in the app.cs in the core

    ReplyDelete
  2. Best to ask these questions longhand - using StackOverflow - especially as Blogger comments are pretty poor at WYSIWYW/WYSIWYG

    For injection I just use IMvxServiceConsumer and IMvxServiceProducer - e.g. in the UI.Droid app I can register an instance or type for IDoIt using IMvxServiceProducer and on the ViewModel side I can get hold of a reference using IMvxServiceConsumer.

    If you can speak any French then try: http://www.e-naxos.com/Blog/post/lIoC-avec-MvvmCross-de-WinRT-a-Android-en-passant-par-Windows-Phone.aspx

    ReplyDelete
  3. No French might try google translate, but if I understand you correctly I should place my android implementation in the UI project and not in the Core as I have now (Which also ensures the core do not import any android libraries)

    But then I need to reference the UI project from the Core?

    ReplyDelete
  4. No - you only need to access the INTERFACE in the core project - not the implementation.

    Polite request: Please use StackOverflow :)

    ReplyDelete