Tag Archives: xamarin

How To Use Google Maps In Xamarin.Forms

Recently I’ve been putting together an app development proposal for a potential client. It’s the sort of app that I think would suit Xamarin.Forms very well, only it has a large mapping component for which the client wants to use Google Maps on both Android and iOS.

At the time of writing the ‘built in’ Xamarin.Forms Map view has limited functionality and defaults to using Apple Maps on iOS. This makes it unsuitable for this project which meant I had to find and (roughly) test out an alternative before I could put a proposal together with any degree of confidence.

I managed to get it working in the end, but not without hitting all sorts of snags which I’ll document here in the hope it might make the process easier for someone else. Most of this information is scattered about the web but it’s difficult to find it all in one place.

1. Find A Suitable Library
Fortunately there’s a third-party open source Google Maps API in development for Xamarin forms which you can access here. Xamarin.Forms.GoogleMaps seems to be pretty full-featured so, rather than trying to reinvent the wheel, this is what I decided to use. Thanks very much to GitHub user amay077 for making this available.

2. Get It To Compile
Once downloaded from GitHub the next step is to get the Xamarin.Forms.GoogleMaps sample project to compile. This was pretty straightforward apart from a few error messages – you may run into the following on iOS:

This version of Xamarin.iOS requires the iOS 10.2 SDK (shipped with Xcode 8.2) when the managed linker is disabled. Either upgrade Xcode, or enable the managed linker. (MT0091) (XFGoogleMapSample.iOS)

To get round this either upgrade Xcode (groan) or do what I did and go to Project Options->Build->iOS Build and set linker behaviour to ‘Link Framework SDKs only’ which should fix it (you may have to clean and rebuild).

On Android you may get something like the following:

Could not find android.jar for API Level 23. This means the Android SDK platform for API Level 23 is not installed. Either install it in the Android SDK Manager (Tools > Open Android SDK Manager…), or change your Xamarin.Android project to target an API version that is installed.

A rare helpful error message this – to fix either do as the message says and go to Tools->SDK Manager and install the appropriate level SDK or go to Project Options->Build->General and select an SDK that you do have installed (I set it to Android 5.0 and it worked fine).

3. Install Google Maps On A GenyMotion Device
OK – I’m going to assume that a) you want to use an emulator for development and b) you are using GenyMotion as it’s by far the fastest. If you try and run the Xamarin.Forms.GoogleMaps sample project as is you will probably see a grey square where the map should be with ‘Xamarin.Forms.GoogleMaps’ in black text. This is because Google Play Services (which includes Google Maps) is not installed on GenyMotion by default. To do this I followed the instructions here (scroll down the page to where it says ‘Setup Google Play Services’). I was using an Android 5.0 GenyMotion device and did not need to do the first step (ARM Translation Installer).

Once you have flashed your virtual device you will get all sorts of irritating popup messages saying ‘Google Play Services Has Stopped’ and the like but if you just soldier on through this and update google play services and google maps via Google Play you should be OK. If you can get the standard Google Maps app running on the device you are sorted.

4. Create An Android API Key
So, assuming you have Google Maps running OK on your GenyMotion emulator, if you now try and run the Xamarin.Forms.GoogleMaps sample project you will just get what looks like a blank map (or a ‘barren featureless desert’ for Black Adder fans). This is because you haven’t supplied a valid API key – if you look through the Application Output of the app in Xamarin Studio you will see something like the following:

Authorization failure. Please see https://developers.google.com/maps/documentation/android-api/start for how to correctly set up the map.
In the Google Developer Console (https://console.developers.google.com)
Ensure that the “Google Maps Android API v2” is enabled.
Ensure that the following Android Key exists:
API Key: your_google_maps_android_api_v2_api_key
Android Application (;): FF:4B:77:38:AB:3A:F3:A8:42:CC:03:27:74:AA:CB:5F:66:A1:5F:D0;net.amay077.xfgooglemapsample

Copy the long hex string (SHA-1 certificate) and package name from this error message as this will save you faffing around with keystore commands later.

Now go to your Google Developer Console. On your Dashboard click ‘Enable API’ and select the Google Maps Android SDK. Now go to ‘Credentials’, click on ‘Create Credentials’ and select ‘API Key’ followed by ‘Restrict Key’.

You should now get a list of restriction options. Select ‘Android apps’ and enter the package name and SHA-1 certificate from the error message I told you to note down earlier. The API key can now be saved.

Note that you will have to enable access for both the ‘Debug’ and ‘Release’ versions of your app as they are signed differently and therefore have a different SHA-1 certificate. Simply run the app in both configurations and grab the application output as above to get the appropriate SHA-1 key for each.

Whilst you’re at it you may want to create an iOS API key (see step 5). It takes a few minutes for these to take effect so, once done, I suggest you go and make yourself a well-earned cup of coffee.

Now you need to embed your API key in your app. The way I suggest doing this is consistent with all the documentation on the matter – go to your AndroidManifest file and enter the following inside the tag:

<meta-data android:name=”com.google.android.geo.API_KEY” android:value=”YOUR_API_KEY” />
<meta-data android:name=”com.google.android.gms.version” android:value=”@integer/google_play_services_version” />

Replace ‘YOUR_API_KEY’ with your actual API key of course. Now your manifest file should look something like this:

<manifest xmlns:android=”http://schemas.android.com/apk/res/android&#8221; android:versionCode=”1″ android:versionName=”1.0″ package=”net.amay077.xfgooglemapsample”>
<uses-sdk android:minSdkVersion=”15″ />
<uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION” />
<uses-permission android:name=”android.permission.ACCESS_LOCATION_EXTRA_COMMANDS” />
<uses-permission android:name=”android.permission.ACCESS_COARSE_LOCATION” />
<uses-permission android:name=”android.permission.ACCESS_CHECKIN_PROPERTIES” />
<uses-permission android:name=”android.permission.ACCESS_WIFI_STATE” />
<uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE” />
<uses-permission android:name=”android.permission.ACCESS_MOCK_LOCATION” />
<uses-permission android:name=”android.permission.INTERNET” />
<application android:label=”OpenHouse”>
<meta-data android:name=”com.google.android.geo.API_KEY” android:value=”AIzaSyDJ7qxHOOh_4A1PodKyU0MlkhFIsyZsNJ7″ />
<meta-data android:name=”com.google.android.gms.version” android:value=”@integer/google_play_services_version” />
</application>
</manifest>

Just one more step – in the Xamarin.Forms.GoogleMaps sample project the API key isn’t entered in this way so you will need to delete the ‘dummy’ API key that been placed there. Do do this simply open MyApp.cs and remove the following line:

[MetaData(“com.google.android.maps.v2.API_KEY”,
Value = Variables.GOOGLE_MAPS_ANDROID_API_KEY)]

Hopefully if you now rebuild and run the Android project, and you’ve waited long enough for your API key to activate, you should now be able to see Google Maps correctly displayed in the GenyMotion emulator. Well done – it’s not the simplest process in the world!

5. Create An iOs API Key
This is pretty much the same process as creating your Android API key. Go to your Google Developer Console. On your Dashboard click ‘Enable API’ and this time select the Google Maps iOS SDK. Again go to ‘Credentials’, click on ‘Create Credentials’ and select ‘API Key’ followed by ‘Restrict Key’. This time choose iOS app restriction and enter the bundle identifier from your Info.plist file. For the Xamarin.Forms.GoogleMaps sample project it’s ‘net.amay077.xfgooglemapsample’.

To embed the API key into your iOS application open AppDelegate.cs and pass the API key as a string in the call to Xamarin.FormsGoogleMaps.Init(). This should be all you need to get the app running on iOS.

6. Embed The Xamarin.Forms.GoogleMaps API In Your Own Projects
Assuming you’ve managed to run the sample project successfully it should be fairly simple to get the API working in your own Xamarin.Forms projects. Xamarin.Forms.GoogleMaps is in NuGet so, in a new Xamarin.Forms solution, right-clicking on the ‘packages’ folder and selecting ‘Add Package’ will allow you to add references to the Xamarin.Forms.GoogleMaps packages. Remember to add them to every project in your solution. You may need to enable ‘show pre-release packages’ in the NuGet browser in order to download versions that are consistent with the sample projects.

If you run into strange issues, such as XAML errors when setting properties on a Xamarin.Forms.GoogleMaps.Map view or ‘Could Not Resolve Type’ and ‘BadImageFormatException’ errors when trying to position the map then you most likely have an incompatible version of the various packages somewhere. Check that all your package references (both to Xamarin.Forms and Xamarin.Forms.GoogleMaps) are consistent in all of your projects and ideally the same as the sample project.

I hope this article saves you some of the pain and grief I endured whilst trying to get all the stuff to work. If it does I always appreciate more followers on Twitter here!

mockup_3x
FAIL – Google Maps Not Installed On GenyMotion Emulator

mockup_3x
FAIL – No Valid Google Maps API Key

mockup_3x
SUCCESS – Xamarin.Forms.GoogleMaps Working on GenyMotion Emulator

Creating Bindable Properties in Xamarin.Forms

Something you’re bound to run into sooner or later when developing with Xamarin.Forms is the issue of how to make a property bindable. Fortunately it’s pretty easy to do – though I found it hard to find a simple example online that worked both for setting the property programmatically and via xaml.

So, without further ado here’s an example of a bindable ‘Foobar’ property that can be set both programmatically and via xaml. Just copy/paste this into your own code whilst changing the relevant bits and you should be fine…

Note: ‘YourClass’ should be replaced by the type name of the class that holds the property. References to ‘bool’ should (obviously) replaced by the type of property you are declaring. The ‘false’ value that is passed to the BindableProperty.Create() method refers to the default value for the property and should be replace by something meaningful.

public static readonly BindableProperty FoobarProperty=BindableProperty.Create<YourClass,bool>( p => p.Foobar, false );

public bool Foobar
{
	get 
	{ 
		return (bool)GetValue(FoobarProperty); 
	}
	set 
	{
		SetValue(FoobarProperty, value); 
	}
}

private void UpdateFoobar()
{
	// Do whatever you need to do when the property 
	// has been set here. By the time this method is
	// called Foobar will already hold the updated value
	// so if you need to reference the old value you will
	// need to store it in a class variable
}

protected override void OnPropertyChanged(string propertyName)
{
	base.OnPropertyChanged(propertyName);

	switch( propertyName )
	{
		case "Foobar":
			UpdateFoobar();
			break;
	}
}

Hopefully that’s been helpful – now why not take a brief break from work to watch our trailer for ‘Attack Of Giant Jumping Man’…


Attack of Giant Jumping Man

Windows Phone Development On A Mac With MonoGame

This week I have been porting ‘Toss The Floppy Frog’ (my first Android and iOS title with MonoGame) to Windows Phone 8 – and it has been painful. Very painful. So painful that I though I’d write a simple step-by-step guide to the process of porting iOS and Android MonoGame titles to Windows Phone. Hopefully this will ease the pain for others.

Part of the suffering has, no doubt, been due to the fact that I am a complete Windows noob. I have been developing on a Mac since around 1995 and have barely touched a Windows machine since then – I even wrote my own JavaME emulator (running on J2SE) so I could do JavaME development on a Mac! Windows, and all its terminologies and conventions, is very much uncharted territory for me.

The largest chunk of pain has, though, been due to what are in my opinion three key deficiencies in the Windows Phone implementation of MonoGame. These are…

1. The Project Templates Are Broken
The MonoGame project templates provided with the 3.2 install don’t work with Visual Studio 2013 (which is the latest version at the time of writing). I don’t know about previous versions. The basic template doesn’t launch the game correctly and doesn’t get to the ‘cornflower blue’ screen we so long for. No errors are given, it just doesn’t work. This issue caused me around half a day of tearing hair out before I found a post on the MonoGame forum that gave a solution (see the ‘step by step’ guide below). If these templates could be fixed I’m sure it would save a lot of developers a lot of grief and avoid casting MonoGame in an unnecessarily bad light.

2. The Content Pipeline Requirement Sucks
I understand the requirement for a content pipeline, really I do. Optimising your assets for use on different platforms is a great idea, but it should be exactly that – part of the optimisation process, NOT a requirement for getting a basic game up and running. The fact that ‘raw’ image and sound assets can’t be loaded in the Windows Phone implementation of MonoGame but can in the Android and iOS versions kind of makes a mockery of the whole ‘Write once, play anywhere’ mantra. Many developers (myself included) will have games that run perfectly fine without the need for content pipeline optimisations and the requirement to use one is just an unnecessary barrier to ‘getting stuff done’.

3. The Content Pipeline Itself Sucks
I couldn’t get any of the MonoGame content projects to load correctly in Visual Studio 2013 and I was unable to find any information online as to how to get the Monogame content tools to work correctly without these. the MonoGame content tools were completely useless to me. Fortunately someone on the MonoGame forums pointed me to an open-source XNB compiler that I was able to edit (ie hack) to convert all my assets to XNB (see the ‘step by step’ guide). That’s at least a day of pain right there. Frankly, if I hadn’t have been pointed to this tool I probably would have given up (and I hardly ever give up).

Now I have the greatest respect for the MonoGame project and team – they have given and continue to give their time for free to provide an awesome product for the IndieDev community. The criticisms above are given in the hope that these areas of MonoGame will be improved which will lead to more developers adopting the platform and more fun/revenue for all of us. Hopefully they’ll be taken in that spirit.

And, once you’ve got past the pain, there is one gigantic plus to all of this, and that is…

100% code re-use!!

Yes, you read that right. Once I was able to iron out a couple of quirks I was able to get 100% resuse of my game code across Android, iOS and Windows Phone. That’s pretty much the holy grail of cross-platform coding and an extremely big upside! The only platform specific code is in the app-launcher stubs and about three lines in my image loader. Nice.

So, without further ado, on to the step by step guide to porting iOS/Android MonoGame titles to Windows Phone using a Mac…

1. Prepare Your Code For Windows Phone
If, like me, you’re more familiar with a Mac and Xamarin Studio than Windows/Visual Studio it’s easiest to get this out of the way before you even touch Windows. Here are the issues I came across:

System.Collections.Hashtable is not available on Windows Phone.

References to this class can be replaced easily by references to:

System.Collections.Generic.Dictionary<Object><Object>

Note that there is a slight ‘gotcha’ here in that Hashtable will return null when trying to retrieve a value for a key that doesn’t exist whereas Dictionary will throw an exception. Your code may need to be altered to account for this.

System.Collections.ArrayList is not available on Windows Phone.

References to this class can be replaced easily by references to:

System.Collections.Generic.List<Object>

System.Runtime.Remoting package is not available on Windows Phone.

If you use this package you will have to work around this somehow. I was using it for dynamic class instantiation and could quite easily replace it by hard-coded calls to instantiate the relevant classes.

The following MonoGame properties return incorrect values on Windows Phone:

GraphicsDeviceManager.PreferredBackBufferWidth
GraphicsDeviceManager.PreferredBackBufferHeight

GraphicsDevice.DisplayMode.Width
GraphicsDevice.DisplayMode.Hight

If you are using any of these to get display width/height the following alternative seems to work:

GraphicsDevice.Viewport.Bounds.Width
GraphicsDevice.Viewport.Bounds.Height

Texture2D.FromStream() is not implemented in MonoGame for Windows Phone.

Bummer. If you are using this (as I was) you will have to work around it. I caught the System.NotImplemented exception and loaded the relevant asset(s) using the content manager instead. I really hope this gets implemented soon as it’s an important function.

Assets are loaded without the file suffix.

On Android/iOS you will probably be using something this:

Content.Load(“myimage.png”)

to load assets which will have to change to:

Content.Load(“myimage”)

on Windows Phone. Ideally write code that catches the exception and tries the alternative method if the first attempt fails.

2. Install Some Kind Of Virtual Machine
You probably want either VMWare Fusion or Parallels. I used Fusion which seems OK though it took quite a bit of farting around to get the ‘shared folder’ feature (which allows you to access files on your ‘host’ mac from the ‘guest’ PC) to work.

3. Set Up The Virtual Machine And Install Windows
Set up an x64 virtual machine and install a 64bit version of Windows otherwise the Windows Phone emulators won’t work. I’m still running a 32bit version of Windows which means I have been unable to launch the emulators so far – I’ve been using an actual device for testing. I believe it must also be the ‘pro’ version of Windows in order for the emulators to work.

4. Install Visual Studio 2013 Twice(!)
From here. You want both the ‘Windows’ version (for Windows Phone) and the ‘Windows Desktop’ version (for the content pipeline stuff). Fortunately both will reside happily side-by-side (unlike my children).

5. Install MonoGame 3.2
From here. May well have been updated since this was written.

6. Install XNA
You will need this for the content pipeline stuff. Follow the instructions and use the PowerShell script kindly provided here.

7. Install Windows Media Player
If it’s not there already – some versions of Windows install it by default, some don’t. You’ll need it for audio processing in the content pipeline and it can be downloaded from here.

8. Set Up A New MonoGame Solution
Launch Visual Studio for Windows and choose the ‘MonoGame Windows Phone’ project template uncder ‘Visual C#’ templates. If it’s not there you need to check MonoGame has installed properly. Call the project something sensible (e.g. the name of your app) and save it anywhere.

9. Update The MonoGame Packages
This is necessary to fix the broken MonoGame template issue and must be done with a solution open in Visual Studio. Launch the NuGet console under Tools->NuGet Package Manager->Package Manager Console and type in the following:

PM: Install-Package MonoGame

This will update the template. It asks if you want to overwrite a few files – click ‘yes’ to each one but ‘no’ to ‘Game1.cs’ as this file will have been altered to reference your app’s namespace.

10. Check For ‘Cornflower Blue’
Now you have done this you should be able to build/run the project and get our old friend the ‘cornflower blue’ screen on your chosen emulator or device. Well done! It took me about a day to get to this point!

11. Import Your Source Code
Having wasted a lot of time trying to get Visual Studio to reference files on my Mac I came to the conclusion that this is impossible. Unlike Xamarin Studio, Visual Studio won’t let you import files as links – they have to reside within the project directory, so the simple-but-far-from-ideal solution is to copy the files from your Mac to the host PC and import. Note that you can ‘copy’ directories from within Windows Explorer and ‘paste’ into Visual Studio which I found the easiest way to import a lot of files at once.

12. Compile Your Source
Hopefully if you followed the suggestions in step one your source just compiles at this point. If not you will have to fix any errors, unfortunately I can’t tell you what these might be as I covered everything I came across in step 1!

13. Convert Your Content To XNB
This should be possible just by using the MonoGame tools but I never succeeded. I had to download an open-source XNB compiler tool from here, open in Visual Studio Desktop, then compile and run to convert my files. It works OK, but the tool only appears to allow you to add one file at a time. Thankfully it was fairly easy for me to modify the code to allow all files in a directory to be added and I’ve included my edited source here. The tool successfully converted PNG, JPG and WAV files for me. If you get an error regarding a missing .dll whilst trying to covert audio you probably need to install Windows Media Player as described in step 7. Whilst MP3 files seem to convert OK I haven’t managed to get them playing correctly yet but I will update this post if and when I do.

14. Import Your XNB Files Into Visual Studio
These go in the ‘Content’ directory exactly as you’d have them organised for iOS or Android. Once imported select the files and under ‘properties’ set ‘build action’ to ‘content’ and ‘copy to output directory’ to ‘copy always’.

15. Change The Template Code To Start Your Game
In Visual Studio find the GamePage.xaml file. Click the little disclosure triangle next to it to reveal GamePage.xaml.cs. Edit this file to replace references to the Game1 with references to your Game class.

16. Run Your App
Congratulations, assuming you followed the tips in stage 1 about loading assets your app should now run on device or emulator. It took me almost three days to get to this point!

I hope this guide is of help to someone – any suggestions for improvement just let me know!

Toss The Floppy Frog – Feels Like My Life’s Work


cornflower
Not Since Yves Klein Has A Blue Square Seemed This Exciting

floppy
At Last – Frog Tossing On Windows

Creating App Promo/Demo Videos With Adobe Premiere

One of the things I’ve had to do with Floppy Frog is create a promo video for uploading to YouTube. I’ve made many promo videos for my JavaME apps and games but these were very simple and I’ve never had to do them in a YouTube-friendly format.

I used to use iMovie for promo videos until Apple changed it from a very simple, flexible and useable tool into the pile of arcane, prescriptive and utterly useless garbage that it is now. For the last few years I’ve been using Apple’s Quicktime 7 pro which, ironically, was much more suited to task than the ‘new and improved (read ‘ruined’) iMovie.

But, Quicktime 7 Pro was not going to cut it (pun intended) for this task so I decided to try Adobe Premiere. Overall I found it a good application for the task in hand though getting the settings right was somewhat time consuming, I’ll therefore detail the process here.


1. Capturing The Video

I thought initially that I’d capture video from the iOS Simulator. Bad idea. It runs much too slowly. Next attempt was to run Floppy Frog on the iPad and capture using Reflector as an Airplay Receiver. Again, bad idea. Frame rate was OK but quality wasn’t up to scratch.

Third attempt was to run the Android version of Floppy Frog using the GenyMotion emulator and capture using the excellent Snapz Pro. Success! GenyMotion runs Floppy Frog just as fast as it would on device and Snapz Pro is a highly configurable and useable screen capture tool. It even captured the audio without a hitch. Had to purchase the full version of GenyMotion to get rid of the ‘free for personal use’ message but I don’t begrudge them that as it’s a fantastic piece of software at a reasonable price.

GenyMotion also has the benefit of being able to configure device display height/width so you can set up a virtual device that’s ideal for the video format you want to capture. In this case my video will run on YouTube at HD 1280*720. Floppy Frog is a portrait game so I wanted a device size that wouldn’t look too ‘squished’ within the HD landscape frame, therefore I set up a virtual device of 600*720 and captured at this size at 30fps which is the frame rate at which the game runs.


2. Import The Video Into Premiere

You’d expect this bit to be easy, and it is easy to simply import the captured video into Premiere. Where I ran into difficulties was that Premiere organises all video into a ‘sequence’ and setting up a ‘sequence’ that matched my video capture settings seemed impossible. All I could do was choose from a series of preset sequences and changing the preset sequence settings was not allowed for some reason. The key issue was that none of the preset sequences ran at 30fps, only 29.97 fps and when Premiere attempted to match my 30fps captured video to the 29.97 sequence settings I was getting horrible interlacing effects.

The solution was to start the Premiere project with any old sequence settings, import the captured video, then select the captured video and choose ‘New Sequence From Clip’. This creates a new sequence matching the captured video settings exactly. Only issue was my video was captured at 600*720 and I wanted a video running at 1280*720! Solution: capture a few seconds of random 1280*720 30fps video using Snapz Pro, import into Premiere, then create the sequence from this. The 600*720 video can now be dragged into this new sequence no problem and the 1280*720 capture can be deleted from the project.

Next issue (which most people probably won’t run into) is that my sound hardware runs at a 48khz sample rate whereas my video was captured at a 48khz sample rate. For some reason Premiere seems pretty flaky about converting between the two (whatever the project Audio settings) so I had to make sure my captured video was saved with the audio running at a 48khz sample rate.

3. Export The Video For YouTube

Once the video is comped together in Premiere it has to be exported at high-quality for uploaded to YouTube. I got and tweaked ‘export media’ settings from a YouTube tutorial and they might be slightly overkill quality-wise but I’ve added screenshots on the right…

4. Sit Back And Watch The Traffic Roll In

Or maybe not. But here’s the finished product anyway…

premiere_vid_youtube
Adobe Premiere YouTube Video Export Media Settings.

premiere_audio_youtube
Adobe Premiere YouTube Audio Export Media Settings.

Floppy Frog – Don’t Flap, Hop!

Recently it has become abundantly clear that the JavaME platform, from which I’ve been deriving a living for more than ten years, is no longer going to be viable platform for a mobile gaming company to support, let alone rely on for a main income stream.

In Spring this year I decided to take the plunge and port all of my library code to Xamarin/Monogame so that, going forward, I can (in theory) develop across the Android, iOS and WinPho platforms with pretty much a single codebase.

There were a number of reasons for choosing Xamarin over other cross-platform solutions such as Marmalade, but the key one was the ability to code in C# which would make the porting of my extremely large (you may read ‘bloated’) existing codebase significantly easier.

I can’t say the process has been easy – there has been much weeping and gnashing of teeth, but overall I am pleased with the Xamarin/Monogame solution. The ‘single codebase’ promise is working out in practice and I find C# a great language to work with.

My first game built ‘from the ground up’ for Monogame has just been released on the App Store and Google Play. Floppy Frog is a deliberately challenging endless jumpy platformer inspired by the likes of Frogger, Flappy Bird, Doodle Jump and Paper Toss, though without being a ‘clone’ of any of these. It’s available for free, currently monetized with AdMob ads. The game is very simple but I’m very pleased with it.

Give it a go – download for iOS or Android. Any positive reviews/ratings are of course much appreciated!