11 December 2013

Build for both–introducing WpWinNl

Intro

As I alluded to in a few previous posts, I have been working on a merged version of both my Windows Phone and Windows 8 libraries on Codeplex: wp7nl and win8nl. I have now come to the point that I think it is ready for use and/or some community feedback, so I just released WpWinNl to CodePlex and NuGet. The first ‘good’ version is 1.0.1, immediately superseded by 1.0.3 containing the new cross platform DevicePairConnectHelper.

I’ve cut some old stuff that merely introduced dependencies and that no-one used anyway, and I ported stuff that was missing in Win8nl from Windows Phone and back. Provided it makea sense – since there is no FlipView in Windows Phone, I did not port the FlipViewPanoramaBehavior

People may be shocked to see that it is mainly a Windows 8.1 project, and that the Windows Phone project contains mainly links. No, I am not trying leave Windows Phone – I just found out that most Windows 8 code compiles unchanged on Windows Phone, but not the other way around. See the things that I ran into when I was porting reflection-riddle code – it’s better to bite the bullet on Windows 8, then Windows Phone usually follows suit. The investment the Phone team made moving to a common kernel really pays off that way

It is still a work in progress, as any good library should be.

FAQ

  • What? Are you abandoning Windows Phone 7?
    • Yup. Sorry. Gotta move forward. I push you forward into the new era whether you want it or not ;-) . Seriously, I have enough #ifdef code as it is now. And for those that don’t want to go along – the old library wp7nl still exists, and most of the code will still compile under Windows Phone 7.
  • What? Only support for Windows 8.1, no Windows 8 anymore?
    • Yup. See previous question.
  • Is this fully and thoroughly tested?
    • Nope. This is the tool I needed to make to make cross-platform apps myself. I will probably run into errors. So will you. Contributions and fixes are accepted
  • You are now the only dev registered on the project. Are you taking sole control of the project again?
    • Don’t be silly – if you want to contribute as a developer, just let me know. I just did not get around to adding the people that contributed to the ‘parent projects’.
  • Why only platform specific libraries? Why not PCL?
    • Most of the code I write makes at some point use of the System.Windows or the Windows.UI.Xaml namespace. That does not fit into PCL anyway, so at this point I think the benefits of having PCL do not outweigh the extra hassle of having source code in three potential spots.

Enjoy!

09 December 2013

Understanding and preventing Multilingual App Toolkit ‘lost translations’

Update: the MAT was updated on February 7, 2014, adding new capabilities to handle this issue better

In the old days, making apps in multiple languages was quite a hassle, maintaining multiple resource files and keeping them in sync. In 2012 came the Multilingual App Toolkit, a brilliant piece of technology that changed everything. Suddenly professional localization tooling landed into the lap of John Developer. The tool has a lot of great capabilities – for instance, it can generate translations automatically using Bing Translator. For short texts this works very well, for longer texts your mileage may vary – but you can request for translation from inside the tool, mail it off to a buddy and later import it again. The machine translation at the very least greatly decreases the work of your translator – he or she can skip the 12 instances of Ok, Cancel, Yes, No and stuff like that.

So why wouldn’t you use this? Well, I have heard rumors of people claiming to have lost translations. After getting translated texts and importing that translations into your app – at some point the imported translations where partly reset back to English. So I know of some people who kind of avoid the MAT. I did not understand what their problem was, until I ran into it myself. Fortunately I have had the honor of actually meeting the MAT Product Manager twice this year (at both MVP Summits), and that gives me kind of an advantage in gaining understanding what was going on. So I will try to explain to you what happens, and how you can prevent yourself from shooting in the foot with MAT.

If you start making a localized app, you start out with just making one resource file (using the aforementioned resource file editor), for the default language – usually US/English. Once your apps is done, you fire up the MAT, and start adding languages. The MAT makes an XLF file for every language you add, and – in a Windows Phone app – automatically generates resx files for that. You can prefill those translations using the machine translation option, and like I said, optionally send it off to someone else to review the translation – using the much more translator-friendly editor than the resx file editor. What’s more, they don’t have to have Visual Studio at all – the MAT can run from Visual Studio but also as a simple stand-alone application.

So when do you ‘lose’ translations? Very simple – if you go back to your original resx file with US/English and change the text in there, upon the next build, MAT will assume the text will need to be re-translated, replaces the changed texts with US/English again and marks them red (“Needs translation”) again. The logic behind this stems from the way Microsoft itself uses the application – at one point the UI gets frozen, and then localization happens. But in the mobile app development world, sometimes people go back to the original text and change that too. In my case, I wasn’t really happy with the original wording, or I changed a comma or something. And bam – gone were 5 translations of a single text. But now I understand what’s going on and why, the problem is already halfway solved. The other half I get around by using the following procedure:

  • Use source control, for example Visualstudio.com. Simply check in your files before you start messing around in your translations and particularly when you want to change your source language texts.
  • imageUse the MAT state feature – you can mark all translations as ‘signed off’ (green)when you are satisfied
  • The net result – if you suddenly see all your XLF files checked out, MAT has done some potential unwanted processing. You can easily find what is actually changed by finding everything that’s marked red. If German texts are now suddenly English, you have probably been messing around with the original texts. If you want to retrieve the texts that were reset, you can still retrieve them from TFS.

For the sake of completeness - if you add a new text, you will more or less see the same result (all XLF files checked out) and suddenly an English text between your translated texts – but that is logical, because it’s a new text that has to be translated, either by machine translation or human translators (again).

Currently there is no way to change the MATs behavior in this respect, but f you follow this procedure and keep in mind the rules that drive the MAT, you can use this awesome tool without shooting yourself in the foot. Maybe the tool will be changed to accommodate the more flexible/chaotic way mobile development goes, but that’s for later. Stay tuned.

Update: as you might have seen, the MAT team just started their own blog on MSDN. More importantly: the first article mentions a uservoice for MAT! I think I know what I am going to add for a suggestion ;-)

04 December 2013

Building for both: a tap+send/Bluetooth connection helper for Windows Phone 8 AND Windows 8.1

On my way to the November 2013 MVP Summit, at Matthijs Hoekstra’s house in Sammamish, and back again in the Netherlands I spent considerable time trying to get my tap+send and Bluetooth connection helper to not only work on Windows 8.1 as well, but also to make it provide cross-platform communication between Windows Phone 8 and Windows 8.1. Unfortunately the MSDN PixPresenter sample suggest you can connect between Windows Phone and Windows 8 by sharing app ids in the PeerFinder.AlternateIdentities property, and so does the MSDN documentation on said property. That way, my friends, lay only mud pies and frustration. I don’t doubt that it will work for tap+send, but as the majority of the current Windows 8.x devices (including Microsoft’s own excellent Surface devices) do not include NFC capabilities, the chances of making a successful connection between a Windows Phone and a random Windows 8 computer using tap+send are very small.

I seem to have a thing with Italians these days – I had a lot of fun with a bunch of them at the Summit, and it was not until this Italian developer called Andrea Boschin pointed me to a recent blog post by him that I found a way out of this. Using this knowledge I adapted DevicePairConnectionHelper once again, so now it’s not only cross platform, but it can also connect between Windows Phone 8 and Windows 8.1. If you are completely new to this subject, I suggest you read the previous two articles about this subject as well.

What it can do

In the demo solution, that sports both a Windows 8.1 Store application and a Windows Phone 8 application, you will find a shared file DevicePairConnectionHelper2.cs containing the updated DevicePairConnectionHelper – now supporting  the following communication paths:

  • Windows Phone 8 to Windows Phone 8 – tap+send
  • Windows Phone 8 to Windows Phone 8 – Bluetooth browsing
  • Windows 8.1 to Windows 8.1 – WifiDirect browsing
  • Windows 8.1 to Windows Phone 8 – Bluetooth.

In theory it should support Windows 8.1 to Windows 8.1 over tap+send as well, but lacking even a single NFC enabled Windows 8.1 device, this is hard to test for me.

How to set it up and use it

A very important precondition– the devices can only find each other when the phone is paired to the Windows 8.1 computer. Now the odd thing is – as you have paired the phone to the computer, you will see that is “connected” on both the computer and the phone, for a very short time. And then it flashes off again, so they are not connected. This is a bit confusing, but it is normal. They now ‘know’ each other, and that is what’s important.

As far as the the updated DevicePairConnectionHelper goes, it works nearly the same as the previous one, but it has two extras, as far as using it is concerned.

  • In the constructor, you can now optionally provide a GUID that describes a Bluetooth Service
  • There is a new boolean property ConnectCrossPlatform that you can set to true – then the Window 8 phone will try to connect to a Windows 8 machine using Bluetooth.

To set it up, you have to take the following things into account:

  • In you Windows Phone 8 app manifest, you have to select the ID_CAP_PROXIMITY capability
  • In you Windows 8 app manifest, you will have to the following capabilities
    • Private Networks (Client & Server)
    • Proximity
  • After you have saved you manifest, you have to open the Package.appmanifest manually (right click, hit View Code) and add a Bluetooth rfcomm service. Andrea explains how this is done, and I repeat it here for completeness:
<Capabilities>
  <Capability Name="internetClient" />
  <Capability Name="privateNetworkClientServer" />
  <DeviceCapability Name="proximity" />
  <m2:DeviceCapability Name="bluetooth.rfcomm">
    <m2:Device Id="any">
      <m2:Function Type="serviceId:A7EA96BB-4F95-4A91-9FDD-3CE3CFF1D8BC" />
    </m2:Device>
  </m2:DeviceCapability>
</Capabilities>

The stuff in Italics is what you add – verbatim. Except for the serviceId. Make and id up yourself, generate a guid, but don’t copy this one. Don’t re-use it over applications. But keep it ready, for you will need it in your code.

So, in your Windows 8 app, you now create a DevicePairConnectionHelper and fire it off like this:

var d = new DevicePairConnectionHelper("A7EA96BB-4F95-4A91-9FDD-3CE3CFF1D8BC");
d.ConnectCrossPlatform = true;
d.Start(ConnectMethod.Browse);

And you do exactly the same on Windows Phone 8. Usually the best way to connect is:

  • Start both the Windows 8.1 and the Windows Phone 8 app.
  • First start connect on the Windows 8.1 computer. That usually pretty quickly returns with zero peers found, but it keeps advertising it’s service. Then hit connect on the Windows Phone 8
  • After some time – sometimes half a minute – the Windows Phone 8 gets the peers

Below is the Windows 8 demo app as it has found my main desktop computer “Karamelk”) (that sports a Bluetooth dongle), and next to it the screen of the Windows 8.1 computer, still searching for contacts

wp_ss_20131204_0001     image

Then I hit “Select contact”. The first time, the Windows 8.1 you get a popup asking if the phone can use the connection. And if you give it permission, the apps connect, and it then it looks like this:

wp_ss_20131205_0001      imagewp_ss_20131205_0003

Now you can send messages back and forth.

You can still use this component to connect two Windows Phones to each other like you used to, and you now can also can connect two computers to each other and exchange messages.

A final piece of warning as far as usage is concerned – if you set ConnectCrossPlatform to true, the Windows Phone 8 will do cross-platform connect – but that’s the only thing it will do. Apparently it can’t find other Windows Phone 8 devices in that mode – just Windows 8.1 computers. For a Windows 8.1 computer, it does not matter – whether ConnectCrossPlatform is on or off, it will find other computers as well as phones. The text “Bluetooth” next to the right radio button is actually wrong on Windows 8.1, since the connection between computer actually uses WiFi Direct.

 

How it works

Just like last time – when I added Bluetooth – there was surprisingly little to change. The actual difficult stuff was already found out by Andrea ;-). It turns out that on the Windows Phone side you don’t have to do much, but on the Windows 8.1 side you have to resort to some trickery and use the Bluetooth Rfcomm API.

First of all we need a property to instruct the component to connect cross-platform (or not) and some stuff to hold the service GUID that we need:

public bool ConnectCrossPlatform { get; set; }

private Guid rfcommServiceUuid;

public string RfcommServiceUuid
{
  get
  {
    return rfcommServiceUuid == Guid.Empty ? null : rfcommServiceUuid.ToString();
  }
  set
  {
    Guid tmpGuid;
    if (Guid.TryParse(value, out tmpGuid))
    {
      rfcommServiceUuid = tmpGuid;
    }
  }
}

The RfcommServiceUuid property looks a bit complex, but it’s only to ensure there’s an actual GUID in it. Then comes the actual cross-platform connecting stuff. Most is just simply taken from Andrea, and for an explanation of what he is actually doing I kindly refer to his post (because I understand about half of it).

#if WINDOWS_PHONE
    private async Task InitBrowseWpToWin()
    {
      var t = new Task(() =>
      {
        PeerFinder.AlternateIdentities["Bluetooth:SDP"] = 
          rfcommServiceUuid.ToString();
      });
      t.Start();
      await t;
    }

    private void StopInitBrowseWpToWin()
    {
      if (PeerFinder.AlternateIdentities.ContainsKey("Bluetooth:SDP"))
      {
        PeerFinder.AlternateIdentities.Remove("Bluetooth:SDP");
      }
    }
#endif

#if NETFX_CORE
    // Code in this part largely based on 
    // http://www.silverlightshow.net/items/Windows-8.1-Play-with-Bluetooth-Rfcomm.aspx

    private const uint ServiceVersionAttributeId = 0x0300;
    private const byte ServiceVersionAttributeType = 0x0A;
    private const uint ServiceVersion = 200;

    private RfcommServiceProvider provider;

    private async Task InitBrowseWpToWin()
    {
      provider = await RfcommServiceProvider.CreateAsync(
                       RfcommServiceId.FromUuid(rfcommServiceUuid));

      var listener = new StreamSocketListener();
      listener.ConnectionReceived += HandleConnectionReceived;

      await listener.BindServiceNameAsync(
        provider.ServiceId.AsString(),
        SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);

      using (var writer = new DataWriter())
      {
        writer.WriteByte(ServiceVersionAttributeType);
        writer.WriteUInt32(ServiceVersion);

        var data = writer.DetachBuffer();
        provider.SdpRawAttributes.Add(ServiceVersionAttributeId, data);
        provider.StartAdvertising(listener);
      }
    }

    private void HandleConnectionReceived(StreamSocketListener listener,
      StreamSocketListenerConnectionReceivedEventArgs args)
    {
      provider.StopAdvertising();
      listener.Dispose();
      DoConnect(args.Socket);
    }
    
    // Borrowed code ends
    
    private void StopInitBrowseWpToWin()
    {
      if (provider != null)
      {
        provider.StopAdvertising();
        provider = null;
      }
    }
#endif

Important to notice is that I use Bluetooth:SDP in stead of Bluetooth:Paired like Andrea does. If I use the paired option, I get a list of all devices paired to my phone, including my Jabra headset, my Surface Pro, a Beewi Mini Cooper Coupé Red I got on the last MVP Summit and the desktop computer that I try to connect to. I use SDP (Service Discovery Protocol) to find the device that actually provides the one service I am looking for – not so much a Bluetooth connection from a device that happens to be in the list of paired devices. Notice the Windows Store portion actually makes a “RfcommServiceProvider” with the Guid as identifier, and the Windows Phone portion sets that same Guid as a string to the AlternateIdentities.

Oh – the Windows Phone version of InitBrowseWpToWin method is a bit convoluted – it’s just a way to make whatever is inside async as well, so it’s compatible with the signature of the Windows 8.1 version.

Another important thing to notice is that the Window 8.1 part needs an extra using:

#if NETFX_CORE
using Windows.Devices.Bluetooth.Rfcomm;
#endif

Anyway, where it all starts is here: at the constructor. Not much has changed – there’s only the extra optional parameter that’s being recorded for future use:

public DevicePairConnectionHelper2(string crossPlatformServiceUid = null)
{
  RfcommServiceUuid = crossPlatformServiceUid;
  Messenger.Default.Register<NavigationMessage>(this, ProcessNavigationMessage);
  PeerFinder.TriggeredConnectionStateChanged += PeerFinderTriggeredConnectionStateChanged;
  PeerFinder.ConnectionRequested += PeerFinderConnectionRequested;
}

The Start method has been slightly adapted- it’s now async, to accommodate the async InitBrowseWpToWin method

public async Task Start(ConnectMethod connectMethod = ConnectMethod.Tap, 
   string displayAdvertiseName = null)
{
  Reset();
  connectMode = connectMethod;
  if (!string.IsNullOrEmpty(displayAdvertiseName))
  {
    PeerFinder.DisplayName = displayAdvertiseName;
  }

  try
  {
    PeerFinder.Start();
  }
  catch (Exception)
  {
    Debug.WriteLine("Peerfinder error");
  }

  // Enable browse
  if (connectMode == ConnectMethod.Browse)
  {
    if (ConnectCrossPlatform)
    {
      await InitBrowseWpToWin();
    }

    await PeerFinder.FindAllPeersAsync().AsTask().ContinueWith(p =>
    {
      if (!p.IsFaulted)
      {
        FirePeersFound(p.Result);
      }
    });
  }
}}

A couple of things have changed here – first of all, the whole component is reset in stead of just the PeerFinder, because there's now potentially much more set up now than just the PeerFinder. The start of the PeerFinder is now in a try-catch because on Windows 8.1 some combinations of parameters cause an exception – while still the connection is set up. This is a typical ‘yeah whatever works’ solution. Then the cross platform stuff is set up – if that is required, and I also check first if the result of the task that returns from the PeerFinder is not faulted, which I did not do in earlier versions either.

The most important piece of refactoring is the DoConnect method. In the old version there was one – now there are three overloads:

private void DoConnect(PeerInformation peerInformation)
{
#if WINDOWS_PHONE
  if (peerInformation.HostName != null)
  {
    DoConnect(peerInformation.HostName, peerInformation.ServiceName);
    return;
  }
#endif

  PeerFinder.ConnectAsync(peerInformation).AsTask().ContinueWith(p =>
  {
    if (!p.IsFaulted)
    {
      DoConnect(p.Result);
    }
    else
    {
      Debug.WriteLine("connection fault");
      FireConnectionStatusChanged(TriggeredConnectState.Failed);
    }
  });
}

private void DoConnect(HostName hostname, string serviceName)
{
  var streamSocket = new StreamSocket();
  streamSocket.ConnectAsync(hostname, serviceName).AsTask().ContinueWith((p) => 
                            DoConnect(streamSocket));
}

private void DoConnect(StreamSocket receivedSocket)
{
  socket = receivedSocket;
  StartListeningForMessages();
  PeerFinder.Stop();
  FireConnectionStatusChanged(TriggeredConnectState.Completed);
}

It is a bit complicated, but these three methods cover all scenario’s

  1. If a Windows Phone connects to a Windows Phone, it calls the first method. Since this is not a cross-platform call, the HostName will be null, so it will do PeerFinder.ConnectAsync, receive a StreamSocket and proceed to call the 3rd DoConnect method.
  2. If a Windows 8.1 computer connects a Windows 8.1 computer – ditto
  3. If a Windows Phone connects a Windows 8.1 computer – the Phone will fine find that the HostName (and ServiceId) will be set, so it calls the 2nd DoConnect method. This will proceed to connect to the service, as pass on the result StreamSocket again to the 3rd method
  4. The Window 8.1 computer, upon being connected by a Windows Phone, will get a callback in HandleConnectionReceived (see above, in Andrea’s code) which will immediately result in a StreamSocket, so it call the 3rd DoConnect immediately.

For good measure – the 2nd DoConnect method is actually never used in a Windows 8.1 application.

And that’s about what I needed to do. The changes to the sample app are very limited – I added a checkbox on both of them, and a wrapping ConnectCrossPlatform property that is set by said checkbox. Also a minor detail – the Reset method in the NfcConnectViewModel no longer stops the PeerFinder first. This is especially important for the Windows 8.1 app – it never finds the phone, but the phone is still trying to make a connection. If the PeerFinder on Windows 8.1 is already stopped, you get all kind of funky errors when you select the computer on the phone.

For those who find the discussion a bit arcane – there is, as always, a working demo solution for this article. It is built on top of my new not-yet-published WpWinNl library – that’s basically a cross-platform (no, not PCL, just a NuGet Package) version of the wp7nl library. Well as cross platform as it can be. Stay tuned, it will be available soon. But tackling this problem took a bit longer than I hoped, so I am a bit behind schedule