I’ve been asked a number of times about the process to ask for Bluetooth permission for your app. I’ve been meaning to create something reusable for some time but it has languished on the To Do list for rather too long. I recently noticed that as of .NET 8.0, MAUI will incorporate a Bluetooth permission which simplifies the process for asking for permission in your app in a clean cross-platform way.
I used this as an opportunity to refactor the library that I had started so I could create the same pattern for Xamarin Forms and .NET MAUI 7.0 so you can get started with it now and then switch to the in-box equivalent when you update your code. You can request permission with a single line of code:
PermissionStatus status = await Permissions.RequestAsync<InTheHand.Bluetooth.Permissions.Bluetooth>();
It provides a simple way to request permission, which on some platforms won’t do anything noticeable but on others, such as Android, it will correctly prompt for permission as long as you’ve set up your manifest correctly.
I was recently looking at migrating some code from Xamarin Forms to Uno Platform and one of the big changes when moving from Xamarin Forms to .NET 6 and later is a move towards using Microsoft.Extensions.DependencyInjection on all flavours of .NET. The key difference between this approach and DependencyService is that you have one shot to add your dependencies at the beginning of your app’s lifecycle. The DependencyService was more flexible as you could add registrations at any time before instantiating them. This makes it handy for apps which dynamically load optional areas of functionality.
I therefore took the Xamarin code and updated it and built it for .NET 6.0 so it will run on any .NET flavour with no dependencies. I removed a bunch of functionality related to handling Renderers since that was a very specific use-case and instead added support for any constructor parameters for already registered types. This allows you to pass services into your view models with no messy code. The only requirement is that your class has a single constructor, and all the types passed in as parameters have already been registered with DependencyService prior to calling Get<T>().
The result is a small NuGet package and the source is hosted on GitHub. Now, of course, this is not going to be the best approach for everyone but it provides another option when migrating from Xamarin Forms and it’s good to have choices when migrating legacy code.
There is a particular issue when writing any code which will run on Android which presents external UI through an Intent or uses broadcasts in that you need to have a reference to the current Activity. I covered this in my last post.
Following on from that I moved the code to a new library (since it is required in both the Bluetooth Classic and Bluetooth LE libraries). It now provides a simple API which you can use to determine the current Activity without adding any framework dependencies.
InTheHand.AndroidActivity.CurrentActivity
The code can determine this on Xamarin Forms, .NET MAUI and Uno Platform. I haven’t figured out if there is a clean way to do this on Avalonia yet but there is a simple workaround for everything currently unsupported – including if you want to use the libraries from “native” Xamarin Android or .NET 6.0 Android and beyond.
Every Android project contains a main Activity. For an Avalonia mobile app this is in MainActivity.cs and is an empty class derived from AvaloniaMainActivity. Simply override the OnCreate method like so:-
protected override void OnCreate(Bundle savedInstanceState)
{
// provide reference to current activity
InTheHand.AndroidActivity.CurrentActivity = this;
base.OnCreate(savedInstanceState);
}
Natively, Android apps are built around activities. These are distinct parts of an application which include UI and are designed to perform a particular task. Android allows you to start activities and pass data between them. A number of system dialogs are just activities which run and return a result.
Across Platforms
When using a cross-platform framework, whether it be Xamarin Forms, Uno or .NET Maui your app will generally have only a single Activity. You can create a complex application and never touch the template generated activity code. However when you want to call into an Activity, whether it be your own, a third-party library or a system feature, you need to have a reference to your current activity in order to launch another.
Each framework has its own method to help with this. On Xamarin Forms this is exposed in the Xamarin.Essentials library – Xamarin.Essentials.Platform.CurrentActivity. When migrating to .NET Maui this moved to the Microsoft.Maui.Essentials library – Microsoft.Maui.ApplicationModel.Platform.CurrentActivity. The new project templates add a line of code to the default MainActivity to initialize this and store a reference to the running activity.
On Uno the Uno base library includes Uno.UI.ContextHelper.Current which stores a reference to the activity and is initialised in the base Microsoft.UI.Xaml.ApplicationActivity class.
What’s the point of all this?
If we want to create a .NET library which works on all these different frameworks and has access to the activity we have some work to do. We can’t create a NuGet targeting different frameworks because these are all net6.0-android – you can’t specify the UI tech. We don’t want to provide multiple virtually-identical libraries tweaked for each framework, but we also don’t want to reference multiple dependencies in one library. The solution is a bit of good old-fashioned reflection. By checking for the presence of each of these types we can query the property and have our activity reference to use as required. Of course, none of these may be present. It’s entirely property our library is used from a “native” .NET Android app and the developer is working directly with the Android APIs. We can still provide a valuable API here but provide a method to pass the activity context to any API which requires it to display some UI. That allows us to cover all bases from a single dll for all “flavours” of .NET Android development. From your cross-platform code it’s entirely transparent. I’ve put my current code into a Gist here:-
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The good news from this is that InTheHand.BluetoothLE (as of the next NuGet drop) no longer has Xamarin or Maui dependencies but works just the same on these platforms. I’m interested in extending this to support Avalonia too. I may also move the code into its own library as I envision using it from a few places and it will be easier to maintain.
I’ve been working on adding macOS support to 32feet.NET and there are two frameworks in macOS for Classic Bluetooth – IOBluetooth and IOBluetoothUI. I soon discovered that neither of these had bindings in the standard Xamarin.Mac package which is referenced by all Xamarin Mac applications. I decided to build binding libraries for both APIs and publish the code as part of 32feet.NET. This means you can either use the IOBluetooth lower-level APIs yourself or later use the platform-agnostic 32feet API.
Today I’ve published the first release of the InTheHand.IOBluetoothUI package. There are fewer APIs than IOBluetooth and I’ve already begun the manual process of simplifying and making it more “.NET friendly”. There is also documentation to add, though even Apple’s documentation on IOBluetooth is rather thin at best…
These are by no means final and there will be changes to the APIs as names are cleaned up and more is tested and fixed. If you’d like to try them in your projects please let me know your feedback via GitHub. Those NuGet packages are:-
An ongoing issue with 32feet.NET is that it wouldn’t work inside Unity. The reason is that the System.Net.Sockets classes behave slightly differently in the Mono runtime to the desktop .NET framework and you can’t create a Socket using the Bluetooth specific address family.
In order to work around the issue it was necessary to P/Invoke into the native winsock functions, essentially rebuilding a subset of the Socket class. In parallel to this work I’ve been rebuilding 32feet with a more modern API which is less tied to Sockets (primarily just used on desktop Windows) and able to map onto a range of platforms. Another big change for this version is support for Bluetooth LE alongside classic Rfcomm on supported platform. Currently this library supports Xamarin Android and iOS along with UWP, Windows desktop .NET 4.6 and Mono .NET 2.0 for Unity. I’m working on a macOS implementation too. The API is essentially designed to be a more friendly version of the UWP API. In order to support such an old version of .NET, the Unity version is entirely synchronous whereas most of the API is normally async.
In order to test this I wrote a very simple script for Unity which picks a specific paired device, connects to a serial port service over Rfcomm and sends a string. Yes that’s right I have a 3d game that I can print from!
This is currently in preview (but available on NuGet now). There is a lot still to finish including generating the documentation. I’m hoping for some useful feedback, particularly on the Unity work but also any of the other current platforms. Feel free to join in the discussions on GitHub.
Xamarin Forms doesn’t have a specific PasswordBox control – instead you use the Entry (Think TextBox) and set IsPassword to true. Normally this works as expected and provides a masked entry box. However there is a known issue on Windows Runtime (Phone and Desktop) where AutoSuggest and Auto Capitalisation are not disabled for a Password field. Luckily there is a workaround which is to set the input keyboard to Email. The small side effect is the addition of a “.com” button to the keyboard but it will stop the control capitalising the first letter or showing suggestions based on what you are typing. I added the following code behind:-
This issue doesn’t affect Silverlight apps (TargetPlatform.WinPhone) and I decided not to set the keyboard on other platforms as they work correctly with the default keyboard.
In case you missed it there is a great blog post on .NET 4.6 which is a part of Windows 10. Among the various performance and Hi-DPI improvements there are some more subtle enhancements. Perhaps as a nod to Microsoft’s new openness to other platforms there are some helper methods on DateTimeOffset for converting to and from UNIX times. These are represented as the number of seconds since 00:00 on the 1st of January 1970. I’d already come across situations where I needed this and had written a couple of simple conversion methods. They come in useful when doing interop with Android APIs for example. Why not match the .NET 4.6 API I thought so slightly tweaked them and put them in a Gist here:-
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
/// Converts a Unix time expressed as the number of seconds that have elapsed since 1970-01-01T00:00:00Z to a DateTimeOffset value.
/// </summary>
/// <param name="seconds">A Unix time, expressed as the number of seconds that have elapsed since 1970-01-01T00:00:00Z (January 1, 1970, at 12:00 AM UTC).
/// For Unix times before this date, its value is negative.</param>
/// <returns>A date and time value that represents the same moment in time as the Unix time. </returns>
public static DateTimeOffset FromUnixTimeSeconds(long seconds)
{
return dt.AddSeconds(seconds);
}
/// <summary>
/// Returns the number of seconds that have elapsed since 1970-01-01T00:00:00Z.
Recently Xamarin Forms has been expanded to support Windows Phone 8.1 and Windows 8.1. There are instructions online for adding a Windows Phone 8.1 app to your solution and plugging it all together here:-
However there is a small omission which will lead to a build error – #6 tells you to remove the PhonePage base class from your MainPage definition but you actually need to replace it with:-
Firstly, just to clarify, I don’t rap – this is a post about a useful Remote Desktop server for OSX. Wait, I hear you cry, you’re a Windows developer! Well that is true but I also use Xamarin to produce apps for iOS and Android and to build and deploy iOS apps you have to have a Mac in your workflow. Xamarin have done a great job to minimise this – you can do your development in Visual Studio on your PC but you have to connect to a Mac running their Build Server and you need to use the Mac to deploy apps to the iTunes store.
Because it was only for occasional interaction I don’t want the Mac Mini (which is a very nice looking piece of hardware BTW) setup with a Keyboard/Mouse/Monitor taking up space so it’s running as a headless device. I originally used TeamViewer to occasionally connect to the device but randomly last month the PC client started crashing on load and even an uninstall/reinstall wouldn’t get it working again so I looked for an alternative. I came across iRAPP by CodeRebel. I’d not heard of it before but it seemed perfect – it’s a Remote Desktop provider supporting Microsoft’s RDP protocol which means you can connect using the standard Windows Remote Desktop app. The app is available for Windows, Windows Phone, Android, Mac and iOS and since I use other Windows machines I have it installed on every device I use. By installing iRAPP on my Mac it just works in my Windows environment with no hassles. I found it very reliable over the trial period and have just purchased a license – $79 for a year and even that process is straight-forward and just works. Purchase the license online and click Update License from the iRAPP control panel and boom it’s up and running again!
There is also an iRAPP client for Windows which adds an extra dimension – it allows you to “blend” your desktop – switching to it adds the OSX menubar and dock to your desktop and will open apps in windows transparently on your desktop so you can feel like you are running OSX applications side by side with your Windows applications. Fun stuff but I didn’t really need this.
If you’re interested take a look here – http://www.coderebel.com/products/irapp-client/. There’s no ulterior motive here – I’m not receiving a commission or anything, I was just really impressed by the product and it’s proved reliable and useful.