Category Archives: Android

Using SMS Retriever API in Xamarin Android!

Let’s use the SMS Retriever API in Xamarin Android, and possibly avoid the troubles of new Google Play Store SMS/Call Log access restrictions! 😉 Is your Android App ready for the new Google Play Store permission policy?

SMS Retriever API, in a nutshell…

Android SMS Retriever API allows you to access the SMS Messages in the phone without having to request SMS read access permission, thus giving a complete peace of mind for the user. 🙂 Allowing specific SMS messages to be read from the Inbox, which are specifically targeted to be delivered for the  app we’re requesting from. This is the perfect solution for One Time Password (OTP) mobile number verification implementation in our Android apps.

Furthermore from Google Docs: https://developers.google.com/identity/sms-retriever/

Now that the introduction is out of the way… Let’s get started!

How it works, in short…

NO! I’m not gonna get into detailed explanation, but in simplest terms we are going to have an object called SmsRetrieverClient that’s going to wait for an incoming SMS message with the matching hash key to the app we are using. This active waiting is going to execute for 5 minutes and automatically dispose itself.

When the certain SMS arrived at the inbox during the 5 minute waiting , the SmsRetrieverClient then sends a broadcast to the app with the captured message content, for any listening broadcast receivers registered in the app.  From there we pick up the message inside our broadcast receiver and we process it or do whatever the heck we want with it. 😛

But in case if the SMS wasn’t received by the phone it would still execute the broadcast but with a time out failure code, so that we know the 5 minutes waiting exceeded and the SMS wasn’t received or read.

So there’s mainly 2 components to it, SmsRetrieverClient object, and the Broadcast Receiver object. Not to mention the App Hash key, which is also very crucial element here, you need to make sure the SMS that’s sent to the device has the App hash key that’s derived from the signing keystore of the app’s running instance.

In Xamarin…

Yes this is completely available in Xamarin Android as well, right out of the box! Just a matter of figuring out the right namespaces and method calls comparing to the official Google Android Docs! 🙂

Although I’m quite surprised I could not find any official Documentation from Xamarin regarding this important API. Thus here I am filling that void! 😀

Let’s get started!

Add em Nuget!

To use SMS Retriever API in Xamarin we need to install Xamarin.GooglePlayServices.Auth nuget package to our Android project.

Just pick the latest version as you prefer and hit install.

Activate the SMSRetrieverClient…

Then we need to implement the executing of the activation of SMSRetrieverClient that is the client object that’s going to actively watch out for any incoming SMS messages with the hash key that is similar to the app we are requesting it from.

You could initiate the SMSRetrieverClient inside a button click event or invoke it from a Service instance method, choice is up to you. 🙂

private void btnStartSMSRetreiver_OnClick(object sender, EventArgs eventArgs)
{
	// Get an instance of SmsRetrieverClient, used to start listening for a matching SMS message.
	SmsRetrieverClient _client = SmsRetriever.GetClient(this.ApplicationContext);
	// Starts SmsRetriever, which waits for ONE matching SMS message until timeout
	// (5 minutes). The matching SMS message will be sent via a Broadcast Intent with
	// action SmsRetriever#SMS_RETRIEVED_ACTION.
	_client.StartSmsRetriever();
}

As you can see we’re instantiating the SmsRetrieverClient using the Application Context, and calling the StartSmsRetriever() method to activate listening to the incoming SMS messages with matching app hash key included.

A little cherry on top…

Here are some extra bits you could adopt if needed regarding the SmsRetrieverClient. You could get the return result of the SmsRetrieverClient initiation as follows…

...

	var task = _client.StartSmsRetriever();

	// You could also Listen for success/failure of StartSmsRetriever initiation
	task.AddOnSuccessListener(new SuccessListener());
	task.AddOnFailureListener(new FailureListener());
}

...

internal class SuccessListener : Java.Lang.Object, IOnSuccessListener
{
	public void OnSuccess(Object result)
	{
		// do as you wish on Success init
	}
}

internal class FailureListener : Java.Lang.Throwable, IOnFailureListener
{
	public void OnFailure(Exception e)
	{
		// do as you wish on Failure init
	}
}

And even await for the init result asynchronously as follows…

var task = await _client.StartSmsRetriever();

But don’t forget to switch the caller method to an async method! 😉

Well that’s just some cherry on top features if you ever wanted to try!

Next the BroadcastReceiver…

So here’s the other piece of the puzzle, the BroadcastReceiver that will be listening to the broadcasts of the above SmsRetreieverClient.

SMS Retriever API has provided us with an intent filter SmsRetriever.SmsRetrievedAction which we will use to register our BroadcastReceiver, which we’re going name as SMSBroadcastReceiver, and implement as following…

[BroadcastReceiver(Enabled = true)]
[IntentFilter(new[] { SmsRetriever.SmsRetrievedAction })]
public class SMSBroadcastReceiver : BroadcastReceiver
{
	public override void OnReceive(Context context, Intent intent)
	{
		if (intent.Action != SmsRetriever.SmsRetrievedAction)
			return;

		var extrasBundleundle = intent.Extras;
		if (extrasBundleundle == null) return;
		var status = (Statuses)extrasBundleundle.Get(SmsRetriever.ExtraStatus);
		switch (status.StatusCode)
		{
			case CommonStatusCodes.Success:
				// Get SMS message contents
				var messageContent = (string)extrasBundleundle.Get(SmsRetriever.ExtraSmsMessage);
				// Extract one-time code from the message and complete verification
				// by sending the code back to your server.
				ShowResultOnUI("SMS retrieved: " + messageContent);
				break;

			case CommonStatusCodes.Timeout:
				// Waiting for SMS timed out (5 minutes)
				// Handle the error ...
				ShowResultOnUI("Timed Out Error! SMS retrieval failed!");
				break;
		}
	}
}

So once the SmsRetrieverClient does its job, either success or failure it will hit our Broadcast Receiver above and pass in the results, with Status code and Content of the Message if success, which will be included in the Extra Bundle as you can see above.

Once we receive the results, its up to you to do whatever you want with it, as you can see in the code itself! 😉

That’s pretty much it! simple and straightforward eh! 😀 But I wouldn’t end there without mentioning some important other bits that you need to make sure to get it right!

Other important bits…

Here are some helpful tips for you to troubleshoot any issues you’re having with getting the SMS Retriever API to function as it should.

SMS format..

You need to make sure you’re sending the correct SMS format as mentioned in the Google Android Documentation.

<#> Your ExampleApp code is: 123ABC78
FA+9qCX9VSu

It should be same format as above, although you could make some alterations to the message content, but the structure should be same. Start with “<#>” followed by the content of the SMS and ending with the App Hash key. You can learn more about it from their documentation: SMS Retriever API: Construct a verification message

Please, get your App Hash Key right!

I’ve seen people get this wrong all the time. It is such an easy simple process but a delicate one. So if you can’t get your implementation of SMS Retriever API to function properly, you should definitely take a look at your app hash key, whether you’ve generated the correct key and using the properly in the SMS content.

Given Xamarin Android development, there’s no official documentation provided by Xamarin yet, that’s why I have written a step by step blog article to how to easily generate the accurate App hash key, give it a try if it could be of any help…

You can check it out here: App Hash Key Helper extension for Xamarin Android apps!

Let’s see it in action! 😀

Let’s fire it up and see the beauty of Android SMS Retriever API in Xamarin!

Look at the beauty eh! 😀 No SMS Read permission required 😉 way to give some peace of mind for your users for a simple functionalities such as OTP or Phone number verification!

You can try out my full blown demo in my github repo:

https://github.com/UdaraAlwis/XAndroidSMSRetrieverAPIDemo

It’s got all the bits I talked about above fully functional 🙂

in Xamarin.Forms Android?

Even if you’re using Xamarin.Forms the same implementation above can be applied easily. Just simply create a platform specific service to invoke the SMSRetrieverAPIClient on Android and call it from Xamarin.Forms at run time, then from the BroadcastReceiver just return back the results by using Xamarin.Forms MessagingCenter… 😉

Simple eh! 😀

There you have it fellas!

Cheers! Share the love! 😀 ❤

Advertisements

App Hash Key Helper extension for Xamarin Android apps!

Remember my last post “Generating App Hash key for SMS Retriever API for (Xamarin incl.)…” where I shared with you guys how to successfully  generating the App hash key in both MacOs and Windows environments without any hassle?

Now this is the improved alternative of it! making the whole process much easier specially for Xamarin Android development!

Previously…

So earlier we looked at how we could generate the App Hash key straight away from MacOS Terminal or on Windows with the keystore hex generation followed by the Xamarin Android helper method execution to retrieve the app hash key!

That specific helper method I had extracted partially from the Google’s official documentation sample regarding the SMS Retriever API, which I couldn’t completely port to Xamarin Android at that time while I was working on my private project.

But later I took a proper look at it and figured out how to completely port it to Xamarin Android, with a few minutes of back and forth between the Java code and C# Xamarin namespaces! 😀

Easier and Faster method!

Now with the fully ported code, as an extension helper class, we could easily generate the Hash key for our Xamarin Android app either on Windows or Mac using Visual Studio, without having to use any external Terminal or Command prompt.

but you need to follow some pre-requisites to get it to properly work..

Let’s get started!

Get your pre-requisites right!

You need to make sure you’re using the correct Keystore file to sign your APK during build, therefore navigate to your Xamarin Android project properties and set the correct Keystore file path and configuration.

In Visual Studio on Windows:

In Visual Studio on Mac:

That’s done!

Finally make sure you have given the proper app package name or app id to your app in the same project properties. ex: com.testapp.xyz

Why you ask? Since we’re directly generating the app hash code from the project itself, we need to make sure during the compilation the app, the final APK is signed using the keystore we are hoping to use in production. So we can retrieved the signed hash hex from the app Context at run time and the app id to generate the app hash code. 🙂

Then let’s begin the AppHashKeyHelper implementation… 😉

Let the implementation begin…

Let the implementation begin! lol 😀 So here is the complete ported helper class for generating the app hash key for your Xamarin Android app!

Simply copy the whole code to anywhere in your Xamarin Android project, and you could straight away call it from anywhere in the app to generate the app hash key!

We’re simply passing in the Android App Context, which will then first retrieve the signed kestore hash string from the Package Manager.

Then we retrieve the app package name from the Context itself, and move ahead with the usual encrypted key generation.

I would recommend calling this from your MainActivity itself, since this is just a one time execution in your local debug environment.

There you go! Simple as that! 😀

Now once again, keeping in mind to make sure to remove the above helper class call from your project code, before you submit your final APK to the Play Store, since google does not recommend generating the App’s hash key during user run time. Just simply retrive your hash key and remove the code, and then set it up in your SMS API gateway execution.

Thats it! 😀

Cheers!

Share the love! 😀 ❤

Generating App Hash key for SMS Retriever API for (Xamarin incl.)…

Trying to generate your app’s Hash Key for the implementation of new Android SMS Retriever API? Still couldn’t get it to work? Well the problem could be in the actual generation process of the has string!

And here are some ways you could actually make sure to generate the valid hash string to get the functionality working.

Specially if you’re developing on Windows, or better working on a Xamarin Android app? I’ve got some goodies to make it work as well! 😉

App Hash string for SMS Retriever API

Anyhow, to use the SMS Retriever API we need generate our App’s unique Hash string which is a combination of app’s package name and your app’s public key certificate. Once the SMS is received into the Inbox the SMS Retriever API looks for any matching hash key in the message corresponding to the app that requested to read the SMS, if it finds a perfect match it will kick in the execution of retrieving the SMS string, otherwise it will time out. So without the accurate app hash key the SMS Retriever API will not kick in at all. That is why it is very crucial we generate the correct app hash key for our app using the App package name and keystore hash.

Troublesome!

Although according to the official Google Documentation, it should be easy and straight forward, but it is not, specially if you’re using a Windows Dev Environment, which could be troublesome at times. To add some topping on to that, if you’re working on a Xamarin Android project in Windows, you’re in deep troublesome waters, since even Google Docs only provides samples for the Android Java implementation.

Then the sample command that Google Docs provide doesn’t really throw any error if anything goes wrong during, wrong parameter, or a wrong keyword, during hash string generation, it simply returns a false hash string, which makes things worse!

This is actually something I experienced while I was developing one of our Xamarin Android applications. Oh yes! that’s even more annoying since there’s no official documentation or Xamarin sample of the implementation code regarding that API. 😦

Hence Google’s doc command failed me I had to play around with the parameters we’re passing in to the command, re-arrange and modify to get it to work. But anyhow with a walk in the midst of the dark forest of troubleshooting, I figured out how to properly generate the App Hash key!

Get your pre-requisites right!

First of all prepare your keystore file path, keystore alias name and keystore password, and double check those values are being properly set. This is where most of them goes wrong. Then the obvious requirement, make sure you have Androd

Method 1: On MacOS Terminal

So this is the easiest and most straight forward way to generate the hash string, by simply executing the below command on a MacOS Terminal.

keytool -exportcert -alias <keystore alias name> -keystore <keystore file with extension> | xxd -p | tr -d "[:space:]" | echo -n <your app id> `cat` | sha256sum | tr -d "[:space:]-" | xxd -r -p | base64 | cut -c1-11
  • Replace the <keystore alias name> with your keystore alias name, ex: mykeystorename
  • Replace the <keystore file with extension> with the full path to your keystore file, ex: /Users/username/Desktop/mykeystorefile.keystore
  • Replace the <your app id> with your app id: com.mytest.app

If everything went well, next line Terminal will immediately prompt you to enter the keystore password, which will generate the accurate app hash key!

Yep simple as that! 😀

Now you might wonder how I shared the above screenshot so bravely without worrying about the security, oh well its just using the default Xamarin Android keystore generated by VS for Mac with its default settings and a fake app id! 😛

Troublesome? Get Utils installed!

Unless you got some missing utils in your mac developer machine, such as the following error: sha256sum: command not found, which indicates missing coreutils in your Mac. Therefore you need to fist install Homebrew if you don’t have already.

Install Homebrew: https://brew.sh/

Restart Terminal and run the below command!

brew install coreutils

Restart Terminal and re-run the has generation command! 🙂

Method 2: On Windows CMD Prompt

Now on Windows its going to be a little bit of more work specially most of the command parameters which are used in unix cannot be executed straight up in Windows Command Prompt.

So in hope of avoiding at least a little bit of pain I would recommend using the Visual Studio Command Prompt or the Android ADB Command Prompt in your Windows PC. Specially since the java keytool path should already be configured in the Path property there.

Step 1: Generate the hex string from keystore file

keytool -alias <keystore alias name> -exportcert -keystore "<keystore file with extension>" -storepass "<keystore password>" | xxd -p

Now the parameters that you need to replace are the same as we did on MacOS command, but as you can see keystore file path and password are provided within brackets or quotations. So make sure to add that detail.

If everything went well, it will return you the hex string from your keystore file!

Now that’s me trying out the default debug keystore file in Windows. If everything executes properly, it should return a super long hex string as shown above, actually even much longer than that lol, I just cropped it out 😛

Copy that hex string and keep it, we are gonna use it in the next step.

Step 2: Implement the Hash key generate code script

Next we’re going to port the Google Doc’s Android Java implementation of the Hash key generation execution to Xamarin Android C# code snippet, thanks to my few minutes of trail and error efforts 😛

Simply copy that snippet into a Xamarin Android project with the specific imports mentioned at the top of the snippet. You could use this as a little helper extension method to generate the app hash key by passing in the app package name and keystore hash value that we generated in the step 1, as mentioned in the parameters.

That should work like a charm! 😀

Although keep in mind once you generate the app hash string, you should remove the keystore hex and package name hex from the code before you submit it to the app store. Google does not recommend those sensitive information inside the code as magic strings due to obvious security reasons.

Now the above code snippet can be used in either Visual Studio on Windows or Mac as long as you got the keystore hex string. 😉

TADAAA! 😀 That’s it!

You’re welcome!

Share the love! 😉 ❤

Is your Android App ready for the new Google Play Store permission policy?

Not so long ago in October, 2018 Google has announced a new policy regarding the use of SMS and Call Log access permissions in your Android apps.

“Providing a safe and secure experience for our users. – Android Developers Blog, Google”

“Google is restricting which apps can request Call Log and SMS permissions – XDA Developers”

Which created quite a buzz amongst the Android Developer community, by short it means Google is restricting the access to the user’s SMS and Call logs from our Android apps, which is actually a step forward to protect the user’s privacy and security.

Root cause…

Since the beginning on Android you could gain access directly to read the user’s SMS and Call logs on android upon permission request, and your app didn’t have to be registered as a Phone or SMS handler app to perform this action. Any app could easily ask for permission from user and gain access to SMS functionality and Call logs seamlessly.

After a while,

some apps had actually started to misuse this open access, and violate user’s private data continuously…

As a fellow developer, speaking of this matter it was just a matter of getting the user to permit the one time permission and setting up the broadcast receiver to reading the messages forever even in background, which was a very easy and open access which could have easily misused for any illegal activity.  And frankly since this was just a one time permission access thing, the user wouldn’t really be aware of what’s going on in the background. :O

And frankly even for a small feature such as OTP we had to gain access to reading the whole list of SMS data in the phone, which was kind of way too much access in my opinion for such a simple task.

Restricting it up!

So Google has started to crack down on this issue as such..

“Some Android apps ask for permission to access a user’s phone (including call logs) and SMS data. Going forward, Google Play will limit which apps are allowed to ask for these permissions. Only an app that has been selected as a user’s default app for making calls or text messages will be able to access call logs and SMS, respectively.”

– Google

Well done Google! now that’s how it’s done. 😀

So frankly what Google is saying as the new policy is that your app will not be given SMS and Call log access permission unless user has selected your app as the default system SMS or Call provider app.

Unless your app’s “core functionality” is related to SMS or Call logs, such as a SMS Sender/Reader or a Call log recording app or something similar of sorts, your app will not be granted access. Otherwise you’re going to have to revert to other API’s Google has provided to access SMS and Call logs for minor functionalities such as OTP verification and so on. 🙂

Now that was back in October, 2018!

Don’t comply? App will be blocked!

Yep they first gave a grace period of 90 days and now they are bringing the rain down upon the apps…

“Reminder SMS/Call Log Policy Changes – Android Developers Blog, Google”

“Google will remove unapproved apps that use call log/SMS permissions in the next few weeks – XDA Developers”

So they have begun cracking down on the apps that are already in the Play Store which does not comply with the new policy of accessing the SMS/Call logs information.

It’s now being striclty mentioned in the Play Store Developer Policy Center: https://play.google.com/about/privacy-security-deception/permissions/

But this doesn’t mean they wouldn’t give any exceptions, in cases like if it is heavily required for the app’s core functionality, as if it’s a sub-functionality that could break the core flow of the app. In which case you will be able to submit a special review for an exception.

So what solution?

This is where you need to take a hard look at your app’s functionality and decide what is core functionality and what’s a side feature.

They have defined this very well in their new updated help document:

“Use of SMS or Call Log permission groups – Play Console Help, Google”

let me put it frankly.

Not Core Functionality but still needed!

This are the instances where it would really not break the core functionality of your app, but it is still required for some other sub-functionality in your app?

Such as for an example, let’s say SMS OTP verification? In that case you could use Google’s alternate API to access SMS OTP retriever API, which doesn’t require SMS access Permission from user. 😀

So they have also given a list of alternate possible APIs you could use to fulfil the requirement without having need the access to SMS/Call logs information.

https://support.google.com/googleplay/android-developer/answer/9047303

Once you have implemented the alternate API use, then you should submit a new version to Play Store without the mentioned permissions.

Core Functionality!

So this is where you need to define the use of your app, is your app an actual SMS Sender/Reader app such as Messenger? or a Call Log keeping app? which then can be defined as a the core functionality of your app. In that case you could continue using the access to the information as long as the user has set your app as the default phone SMS or Call service provider.

But if it is not a default Messenger or Caller app and still needed the access permissions for core functionality then…

“Submit a new version of your app that retains the permissions. Doing so will require you to complete a permissions declaration form inside the Play Console (coming soon) and will give you an extension until March 9th to remove the permissions or receive approval for your use case.”

– Android Developers Blog, Google

If accessing SMS/Call logs is required for a main functionality and you believe it does not violate the policies then you could submit a Declaration Form in Google Play Store, requesting for manual review to exclude your app from removal from Play Store.

They may provide a temporary exception to apps that aren’t Default SMS, Phone, or Assistant handlers when certain scenarios aren’t possible to achieve without such permission access, which is defined in the Exceptions section in “Use of SMS or Call Log permission groups – Play Console Help, Google”

In Conclusion…

Now this actually is going to break a lot of cool features of apps, which may or may not be violating user’s sensitive information. But still it’s a one big step forward for gaining the Android User’s trust and making them feel positive about using your app in long term. 🙂

Again that I’m saying this is going to prevent some really cool apps being pushed to public market via Play Store though. 😐 but a much needed restriction…

I encountered “GenerateJavaStubs” task failed unexpectedly in Xamarin Android

I encountered this during error during a compilation of one of my Xamarin Android apps. After installation of a new 3rd party library, I tried compiling my project, but BOOM! this happened!

What happened ?

So I took a little look-around. Well my project solution is a multi-hierarchical project, where as I have an Android Library, Core Library, and Client Project App referenced in a hierarchical manner .

So what has happened was, after some third party library installation, a duplicate MainApplication class had been generated somehow. 😮

Solution ?

Remove the duplicate  MainApplication class in your project, hence Android requires only one point to fire up. 😉

Make sure you don’t have duplicate MainApplication classes that derives from Application base class in your Xamarin.Android project. Specially if you have a hierarchical project implementation, such as a Android Library project attached to your Main application project.

In such cases make sure you maintain only one MainApplication class that derives from Application base class, in either of your projects. 😀

TADAAA! 😀

Is your Visual Studio stuck at, Emulator is already running?

So few days back after doing a fresh installation on my WIndows 10 laptop, and installing Visual Studio, I tried running a Xamarin Forms project, with the Visual Studio Android Emulator.

The project was successfully building itself, and the Visual Studio Android Emulator also started running successfully, but then the build process was on a hang state after the Emulator started running. Specifically the build log was on a hang state after “Emulator is already running” step, as I saw in the output log in VS.

So after Googling a while I figured out the problem thanks to wasting a bunch on hours on google search and forums, since many others were also having the same issue.

So let me post the solution for you right here.

Cause?

You must keep in mind that my project was building fine, and the Emulator also started running successfully and booted up itself. I was having a already built solution and up and running Emulator, but the VS couldn’t connect to the Emulator.

The cause for this is Visual Studio debugger was unable to connect to the started Emulator through the ADB (Android Debug Bridge).

This is due a wrong registry entry in the system, where as Visual Studio is looking to start up the ADB and connect to it, in order to connect to the Emulator, but due to this wrong registry entry, VS is unable to find the path to ADB, to start it up.

This is very well explained in this Visual Studio troubleshoot article:

https://msdn.microsoft.com/en-us/library/mt228282.aspx?f=255&MSPPError=-2147217396#ADB

This seem to be happening by default with fresh installations of VS lately, where as the Android SDK installation path in the above registry entry doesn’t seem to be matching with the actual SDK installation location in our system, which is probably done by default when installing Visual Studio 2015.

Solution!

So yeah as the article explains we just have to update the registry entry with the accurate path to the Android SDK installation in our system, so that Visual Studio can start up ADB to connect to the Emulator.

Step1 : Copy the actual Android SDK installation path

to find the actual Android SDK path in your system, that is referred by Visual Studio, go to the top tool bar of Visual Studio, and open the Android SDK Manager as shown below.

capture-3

If you did the default Visual Studio installation with the Cross-Platform mobile dev tools, then your path location should also look similar as above.

Copy that path, or type the same exact path on Run command and open the folder and copy the full path from there.

Step2 : Update the invalid registry entry

Open registry editor, by “regedit” in Run window and navigate to the following Registry.

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Android SDK Tools

Now open the Path registry key and change the value with the above Android SDK path which you copied earlier.

capture-2

Well, that’s it.

There goes the solution. Now you should close the Emulator and Visual Studio and reopen your solution, hit F5! you’re all good! 😀

Cheers!

Listening to the Layout on Xamarin Android PageRenderer! (LayoutListener)

So when you create a Custom Renderer for your Xamarin Forms ContentPage, on Android, sometimes you have to tap into the Android Layout changes on your current page.

For instances like, Orientation changes, Keyboard visibility changes on Android and so on.

This is done by Adding a Layout Listener to our Layout’s ViewTree. 🙂 Confusing? Let’s get to work!

Create a Layout Listener…

First let’s create our Layout Listener, the guy who get’s the call back when the Layout Changes on the ViewTree of our Layout.

public class CustomLayoutListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
{
	public void OnGlobalLayout()
	{
		// do whatever the stuff you wanna do here
	}
}

 

We derive to from the IOnGlobalLayoutListener interface, where as you need to implement its OnGlobalLayout() method which get’s the call back when a Layout change occurs.

Add the Listener to the ViewTree…

Next add it to the ViewTreeObserver of your Layout.

[assembly: ExportRenderer(typeof(CustomContentPage), typeof(CustomContentPageRenderer))]
namespace WhateverYourNamespace
{
    public class CustomContentPageRenderer : PageRenderer
    {
        protected override async void OnElementChanged(ElementChangedEventArgs<Page> e)
        {
			var rootView = CrossCurrentActivity.Current.Activity.Window.DecorView.RootView;
			rootView.ViewTreeObserver.AddOnGlobalLayoutListener(new CustomLayoutListener());
		}
	}
}

 

Since I implemented this on a Xamarin Forms Custom Renderer, first I have gained access to the current Activity’s RootView (from a 3rd party library CurrentActivityPlugin) and then I have added my listener to the ViewTreeObserver of my root view. 😀

TADAA! 😀

but you could also…

Oh well good things comes to those who wait.. lol Xamarin Android provides you an event called GlobalLayout where you could also directly subscribe to Layout changes without having to implement your own listener.

rootView.ViewTreeObserver.GlobalLayout += (sender, args) =>
{
	// do whatever the stuff you wanna do here
}

 

Oh well it depends on your implementation requirement though.

Anyways there you have it! 😀

Enjoy!

An improved ScrollView control for Xamarin Forms by me, myself and I…

Alright now when it comes to the default Xamarin Forms ScrollView, its pretty much generic and limited with the simple common attributes and behaviours, and it does not deliver any specific “cool” features with it.

What’s so cool about it?

So I thought of creating my own Custom ScrollView for Xamarin Forms using Custom Renderers, which would include the following awesome features,

Bouncy Effect – Yeah you gotta admit, the bounce effect of a native scrollview is pretty fun to play with, in a User’s perspective. So I thought of enabling this feature in my custom ScrollView even when the Child Element doesn’t exceeds the ScrollView boundaries… 😉
(PS: this effect is interpreted in native Android and iOS differently)

Disabling and Enabling Horizontal and Vertical Scroll Indicators – Now sometimes these scroll bar indicators are useful but there are times which we want to hide them, as it might look ugly on the UI in certain cases. So yeah let’s have some control over it shall we? 😀

Background Image – Of course who wouldn’t like a background Image on a Scroll view eh! 😉 Well to be specific we are going to add a Fixed Background Image for the ScrollView. And note that this background Image would be fixed, and will not be scrolling with the Content of the ScrollView. (I will do another post to enable that feature).

Yes behold, me, myself and I presenting the “BloopyScrollView” why the name “BloopyScrollView”? I don’t even know. lol 😛

Implementation

Alright let’s go ahead and create our Custom ScrollView Control in the PCL project. Along with the following properties, so that we could have direct control over the above said behaviours.

Alright now expect this to be longer, since I have added the properties as Bindable Properties, so you could use them in any MVVM scenario with ease. 😀

namespace WhateverYourNamespace
{
    public class BloopyScrollView : ScrollView
    {
        public static readonly BindableProperty IsHorizontalScrollbarEnabledProperty =
        BindableProperty.Create(
            nameof(IsHorizontalScrollbarEnabled),
            typeof(bool),
            typeof(BloopyScrollView),
            false,
            BindingMode.Default,
            null);
        /// <summary>
        /// Gets or sets the Horizontal scrollbar visibility
        /// </summary>
        public bool IsHorizontalScrollbarEnabled
        {
            get { return (bool)GetValue(IsHorizontalScrollbarEnabledProperty); }
            set { SetValue(IsHorizontalScrollbarEnabledProperty, value); }
        }


        public static readonly BindableProperty IsVerticalScrollbarEnabledProperty =
        BindableProperty.Create(
            nameof(IsVerticalScrollbarEnabled),
            typeof(bool),
            typeof(BloopyScrollView),
            false,
            BindingMode.Default,
            null);
        /// <summary>
        /// Gets or sets the Vertical scrollbar visibility
        /// </summary>
        public bool IsVerticalScrollbarEnabled
        {
            get { return (bool)GetValue(IsVerticalScrollbarEnabledProperty); }
            set { SetValue(IsVerticalScrollbarEnabledProperty, value); }
        }


        public static readonly BindableProperty IsNativeBouncyEffectEnabledProperty =
        BindableProperty.Create(
            nameof(IsNativeBouncyEffectEnabled),
            typeof(bool),
            typeof(BloopyScrollView),
            true,
            BindingMode.Default,
            null);
        /// <summary>
        /// Gets or sets the Native Bouncy effect status
        /// </summary>
        public bool IsNativeBouncyEffectEnabled
        {
            get { return (bool)GetValue(IsNativeBouncyEffectEnabledProperty); }
            set { SetValue(IsNativeBouncyEffectEnabledProperty, value); }
        }


        public static readonly BindableProperty BackgroundImageProperty =
        BindableProperty.Create(
            nameof(BackgroundImage),
            typeof(ImageSource),
            typeof(BloopyScrollView),
            null,
            BindingMode.Default,
            null);
        /// <summary>
        /// Gets or sets the Background Image of the ScrollView
        /// </summary>
        public ImageSource BackgroundImage
        {
            get { return (ImageSource)GetValue(BackgroundImageProperty); }
            set { SetValue(BackgroundImageProperty, value); }
        }
    }
}

 

There we go IsHorizontalScrollbarEnabled, IsVerticalScrollbarEnabled to disable/enable Horizonal and Vertical Scrollbars.

IsNativeBouncyEffectEnabled to control the Native Bouncy effect.

BackgroundImage to set the ImageSource for the ScrollView’s background Image. And make sure to provide a proper image for this hence we will be resizing the given image in our native renderer level to fit to the background of the ScrollView.(You will see in the next steps below)

Alright let’s head over to creating the Custom Renderers associated with our BloopyScrollView.

Something to keep in mind…

So if you’re a frequent reader of my blog, you may remember sometime ago I created an Extention class for handling Xamarin Forms Images in Native code level: https://theconfuzedsourcecode.wordpress.com/2016/12/12/an-awesome-image-helper-to-convert-xamarin-forms-imagesource-to-ios-uiimage-or-android-bitmap/

Why I’m bringing this up, is because we are going to be needing it for this project. You ask why? Because we need to convert the above BackgroundImage, which is of type Xamarin Forms ImageSource.

So we need to convert that ImageSource to native UIImage or Bitmap image respectively in our Custom renderer levels. 😉

So go ahead and grab that code real quick and add it to your Native Projects. 😀

iOS Implementation

Now let’s create the Custom Renderer for the Control in Xamarin.iOS project.

[assembly: ExportRenderer(typeof(BloopyScrollView), typeof(BloopyScrollViewRenderer))]
namespace WhateverYourNamespace.iOS
{
    public class BloopyScrollViewRenderer : ScrollViewRenderer
    {
        private UIImage _uiImageImageBackground;

        protected override async void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            this.ShowsVerticalScrollIndicator = ((BloopyScrollView)e.NewElement).IsVerticalScrollbarEnabled;
            this.ShowsHorizontalScrollIndicator = ((BloopyScrollView)e.NewElement).IsHorizontalScrollbarEnabled;

            if (e.NewElement != null)
            {
                if (((BloopyScrollView)e.NewElement).IsNativeBouncyEffectEnabled)
                {
                    this.Bounces = true;
                    this.AlwaysBounceVertical = true;
                }

                if (((BloopyScrollView)e.NewElement).BackgroundImage != null)
                {
                    // retrieving the UIImage Image from the ImageSource by converting
                    _uiImageImageBackground = await IosImageHelper.GetUIImageFromImageSourceAsync(((BloopyScrollView)e.NewElement).BackgroundImage);
                }

                ((BloopyScrollView)e.NewElement).PropertyChanged += OnPropertyChanged;
            }
        }

        private void OnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
        {
            if (propertyChangedEventArgs.PropertyName == BloopyScrollView.HeightProperty.PropertyName)
            {
                // check if the Width and Height are assigned
                if (((BloopyScrollView)sender).Width > 0 & ((BloopyScrollView)sender).Height > 0)
                {
                    // resize the UIImage to fit the current UIScrollView's width and height
                    _uiImageImageBackground = ResizeUIImage(_uiImageImageBackground, (float)((BloopyScrollView)sender).Width, (float)((BloopyScrollView)sender).Height);

                    // Set the background Image
                    this.BackgroundColor = UIColor.FromPatternImage(_uiImageImageBackground);
                }
            }
        }

        // We need to override this to have the background image to be fixed
        public override void Draw(CGRect rect)
        {
            base.Draw(rect);
        }

        // Resize the UIImage
        public UIImage ResizeUIImage(UIImage sourceImage, float widthToScale, float heightToScale)
        {
            var sourceSize = sourceImage.Size;
            var maxResizeFactor = Math.Max(widthToScale / sourceSize.Width, heightToScale / sourceSize.Height);
            if (maxResizeFactor > 1) return sourceImage;
            var width = maxResizeFactor * sourceSize.Width;
            var height = maxResizeFactor * sourceSize.Height;
            UIGraphics.BeginImageContext(new CGSize(width, height));
            sourceImage.Draw(new CGRect(0, 0, width, height));
            var resultImage = UIGraphics.GetImageFromCurrentImageContext();
            UIGraphics.EndImageContext();
            return resultImage;
        }
    }
}

 

Inside the method we are assigning the relevant properties of our BloopyScrollView to the native control properties.

The UIScrollView which is associated with the Xamarin Forms ScrollView has the following native properties:

  • ShowsVerticalScrollIndicator: Make the vertical scrollbar visible or hidden
  • ShowsHorizontalScrollIndicator: Make the horizontal scrollbar visible or hidden
  • Bounces: Always enable the native bounce effect on iOS UIScrollView
  • BackgroundColor: Allows to set the background color for the UIScrollView or set an Image as a pattern

Also you may have noted that we are converting our Image Source BackgroundImage to a UIImage using our extension.

And then when the Height and Width are set to the Control, we are resizing the Image to fit those properties and setting that as the Background of the UIScrollView through the UIColor.FromPatternImage() which allows us to set the image as a pattern throughout the canvas of the UIScrollView.

the strange tale of getting the UIScrollView’s Fixed background in Xamarin… :O

Notice that we are overriding the Draw(CGRect rect) method, this is to have the UIScroll Background Image to be fixed within the boundaries, and not to be spanned across the Content area.

Because usually if we set the BackgroundColor property, it will span across the Content area, but strangely if we override the Draw() method, BackgroundColor would only be contained within UIScrollView’s boundaries, without spanning across the Content area. This is something I figured out while playing around with the above implementation. 😀

Alright let’s jump into Android… 😀

Android Implementation

Now let’s create the Custom Renderer for the Control in Xamarin.Android project.

[assembly: ExportRenderer(typeof(BloopyScrollView), typeof(BloopyScrollViewRenderer))]
namespace WhateverYourNamespace.Droid
{
    public class BloopyScrollViewRenderer : ScrollViewRenderer
    {
        private Bitmap _bitmapImageBackground;

        protected override async void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            this.VerticalScrollBarEnabled = ((BloopyScrollView)e.NewElement).IsVerticalScrollbarEnabled;
            this.HorizontalScrollBarEnabled = ((BloopyScrollView)e.NewElement).IsHorizontalScrollbarEnabled;

            if (((BloopyScrollView)e.NewElement).IsNativeBouncyEffectEnabled)
            {
                this.OverScrollMode = OverScrollMode.Always;
            }

            if (((BloopyScrollView) e.NewElement).BackgroundImage != null)
            {
                // retrieving the Bitmap Image from the ImageSource by converting
                _bitmapImageBackground = await AndroidImageHelper.GetBitmapFromImageSourceAsync(((BloopyScrollView)e.NewElement).BackgroundImage, this.Context);

                // resize the Bitmap to fit the current ScrollView's width and height
                var _resizedBitmapImageBackground = new BitmapDrawable(ResizeBitmap(_bitmapImageBackground, this.Width, this.Height));

                // Set the background Image
                this.Background = _resizedBitmapImageBackground;
            }
        }

        // Resize the Bitmap
        private Bitmap ResizeBitmap(Bitmap originalImage, int widthToScae, int heightToScale)
        {
            Bitmap resizedBitmap = Bitmap.CreateBitmap(widthToScae, heightToScale, Bitmap.Config.Argb8888);

            float originalWidth = originalImage.Width;
            float originalHeight = originalImage.Height;

            Canvas canvas = new Canvas(resizedBitmap);

            float scale = this.Width / originalWidth;

            float xTranslation = 0.0f;
            float yTranslation = (this.Height - originalHeight * scale) / 2.0f;

            Matrix transformation = new Matrix();
            transformation.PostTranslate(xTranslation, yTranslation);
            transformation.PreScale(scale, scale);

            Paint paint = new Paint();
            paint.FilterBitmap = true;

            canvas.DrawBitmap(originalImage, transformation, paint);

            return resizedBitmap;
        }
    }
}

 

The Android ScrollView which is associated with the Xamarin Forms ScrollView has the following native properties:

  • VerticalScrollBarEnabled: Make the vertical scrollbar visible or hidden
  • HorizontalScrollBarEnabled: Make the horizontal scrollbar visible or hidden
  • OverScrollMode: Always enable the native bounce effect on Android ScrollView
  • Background: Allows to set the background drawable for the ScrollView

As you may have noticed we are converting our Xamarin Forms Image Source BackgroundImage to a Bitmap image using our extension.

Then we are resizing out Bitmap image according to the Width and Height of the ScrollView to fit to the full background to be wrapped around a BitmapDrawable and set to the Background of ScrollView.

There you go! 😀

Let’s use it… 😉

Alright now that’s done, let’s consume this in our PCL project.

<StackLayout Padding="10,0,10,0">

	<Label Text="Welcome to Xamarin Forms!"
		   VerticalOptions="Center"
		   HorizontalOptions="Center" />

	<local:BloopyScrollView 
	IsNativeBouncyEffectEnabled="True"
	IsVerticalScrollbarEnabled="False"
	IsHorizontalScrollbarEnabled="False">
	<local:BloopyScrollView.BackgroundImage>
	  <FileImageSource File="xamarinBackgroundImage.png"/>
	</local:BloopyScrollView.BackgroundImage>
	
			<Label
			  FontSize="22"
			  HeightRequest="400"
			  Text=
			  "Whatever your text content to be displayed." />
			  
	</local:BloopyScrollView>

</StackLayout>

 

As you can see I have inserted my BloopyScrollView in a StackLayout and as the content of the ScrollView I have added Label. Well you can add any content you want or set any Height or Width as you wish.

Notice that I have set IsNativeBouncyEffectEnabled to be True as I want to see the native Bouncy effect. Then I have disabled the Vertical and Horizontal Scrollbars from the properties we added earlier. Then finally I have added the BackgroundImage and set the FileImageSource to the ImageSource type, where as I have placed the image in the native Resource folder, as you would do with any defualt Xamarin Forms Image. 😉

Now let’s see the results… 😀

bloopscrollview-on-ios-lowq  bloopscrollview-on-android-lowq

Yaay! 😀

As we expected the Vertical and Horizontal Scrollbars are disabled and our ScrollView has full native bouncy effect accordingly.

Also you can see the Background Image nicely resized itself and fit to the background of the BloopyScrollView. 😀

Happy dance! lol

Recap Stuff…

Now there’s some stuff I wanted to recap, that is you may have noticed that when I was resizing the Image, I needed the Control’s Height and Width, and where I have acquired those properties are in two different places on each Android and iOS renderers.

To be specific I have accessed the Control’s Width and Height on Android right from the OnElementChanged method, but on iOS renderer I have accessed those values from the  OnPropertyChanged method’s Height property event. 

This is because of the differences of the Rendering cycling of Android and iOS, whereas on Android right at the firing of the Custom Renderer it assigns itself Width and Height. But on iOS we have to access them indirectly by waiting till those properties are set, by listening to the OnPropertyChanged event.

Get it on Github! 😀 XFImprovedScrollView

Cheers everyone!

Pass this on to another developer to make them smile! 😀
– Udara Alwis

An awesome Image Helper to convert Xamarin Forms ImageSource to iOS UIImage or Android Bitmap…

The default Xamarin Forms ImageSource is good, but it doesn’t provide all the properties of an Image, not as much as native Image types of Android or iOS. This is why sometimes we need to drill down to the native level of Image handling sometimes, specially when you’re dealing with Custom Renderers, or complex Image manipulation stuff in Xamarin.

So yeah it’s no surprise you will run into the need of converting the default Xamarin Forms ImageSource to native Image type in iOS and Android, respectively UIImage or Bitmap types.

Well I know I did. lol 😛

How ? 😮

So how could you do this ? Well, something intermediate for both Xamarin Forms ImageSource and the native Image types is the byte[] array. 🙂 But I’m gonna take a pass on it, since it’s too much of trouble and process, which could lead to bad memory consumption. 😦 Instead, why don’t we deal with this straight away. 😀

This is how I did it.. 😉

Now when it comes to Xamarin Forms almost everything is handled by drilling down to the native levels of the platform. Likewise ImageSource  during the runtime is handled through three types of Native Handlers that are as follows,

  • ImageLoaderSourceHandler
  • FileImageSourceHandler
  • StreamImagesourceHandler

Now for each Native environment, these handlers implements a LoadImageAsync() method, which loads the type of Native Type image for a given ImageSource.

This is usually done under the hood, but we need to access this same functionality on the go for our own requirements, so let’s do it by our own implementation.

Let’s create our Android Image Helper…

So we are going to create a Public class with static methods that could be used as extension methods in our Native Project levels. So go ahead and create the AndroidImageHelper class in your Xamarin.Android project level.

sameple

 

public class AndroidImageHelper
{
	private static IImageSourceHandler GetHandler(ImageSource source)
	{
		IImageSourceHandler returnValue = null;
		if (source is UriImageSource)
		{
			returnValue = new ImageLoaderSourceHandler();
		}
		else if (source is FileImageSource)
		{
			returnValue = new FileImageSourceHandler();
		}
		else if (source is StreamImageSource)
		{
			returnValue = new StreamImagesourceHandler();
		}
		return returnValue;
	}

	/// <summary>
	/// For converting Xamarin Forms ImageSource object to Native Image type
	/// </summary>
	/// <param name="source"></param>
	/// <param name="context"></param>
	/// <returns></returns>
	public static async Task<Bitmap> GetBitmapFromImageSourceAsync(ImageSource source, Context context)
	{
		var handler = GetHandler(source);
		var returnValue = (Bitmap)null;

		returnValue = await handler.LoadImageAsync(source, context);

		return returnValue;
	}
}

 

Alright there we go, as you would have expected we are using the native level Handlers of the ImageSource and retrieving the native image type as I explained earlier through the LoadImageAsync() method. Of course you need to pass a reference of your current Context since Android requires the context reference to almost every execution related to the UI stuff.

First we are getting the GetHandler type by the ImageSource, then we are calling the method above to retrieve the Bitmap image and returns back to the caller.

Let’s create our iOS Image Helper…

So just like on Android let’s do the same on iOS and create the public class IosImageHelper in your Xamarin Forms iOS project level.

sameple

public class IosImageHelper
{
	private static IImageSourceHandler GetHandler(ImageSource source)
	{
		IImageSourceHandler returnValue = null;
		if (source is UriImageSource)
		{
			returnValue = new ImageLoaderSourceHandler();
		}
		else if (source is FileImageSource)
		{
			returnValue = new FileImageSourceHandler();
		}
		else if (source is StreamImageSource)
		{
			returnValue = new StreamImagesourceHandler();
		}
		return returnValue;
	}

	/// <summary>
	/// For converting Xamarin Forms ImageSource object to Native Image type
	/// </summary>
	/// <param name="source"></param>
	/// <returns></returns>
	public static async Task<UIImage> GetUIImageFromImageSourceAsync(ImageSource source)
	{
		var handler = GetHandler(source);
		var returnValue = (UIImage)null;

		returnValue = await handler.LoadImageAsync(source);

		return returnValue;
	}

}

 

So here also we are using the same implementation as we used on Android, instead the LoadImageAsync() returns an UIImage.

Now let’s use it… 😉

Alright let’s see how to use these extensions in our code. 🙂

// Useage in iOS

UIImage _uiImageconverted = await IosImageHelper.GetUIImageFromImageSourceAsync(yourImageSourceObject);

// Useage in Android

Bitmap _bitmapImageconverted = await AndroidImageHelper.GetBitmapFromImageSourceAsync(yourImageSourceObject, this.Context);

 

There you have it, now you have your Xamarin Forms ImageSource converted to the given Native Image type, either iOS UIImage or Android Bitmap Image type. 😀

Enjoy! 😀

Cheers everyone!

How to deal with Implementing an iOS optimized UI design on Android with Xamarin Forms?

When it comes to Cross-Platform mobile development, it is very crucial to have Screen Design concepts that are compatible for all the targeting platforms (Android/iOS/WinPhone), or at least to have separate Screen Designs for targeting platforms, when you’re using a platform such as Xamarin Forms (allowing you to develop apps with Native Look and Feels).

but…

What if you are out of luck in such requirements? What if as the developer you’re only given the Design Screens that only optimizes for one specific mobile platform?

and worst…

You’re expected implement the same design for both Android and iOS regardless of the design’s optimization?

my story…

Let me put it this way,

the project I’m currently working is a Cross-Platform Mobile Application building on top of Xamarin Forms. I am given a bunch of Design Screens that are optimized for iOS UI patterns, and my seniors wants me to implement the same UI Design for Android as well, regardless of the Android UI patterns.

So despite Xamarin Forms gives Native Look and Feels I am asked to implement the same design that are optimized for iOS into Android regardless of Android UX patterns.

Oh well.. look at that, so mostly to go against the Android UI Design best practices, just implement the iOS optimized design straightaway. (Well my UX Lead seems to be an iOS geek) 😛

Well this would have been better if we were developing on a Hybrid framework which uses HTML5 so that we could simply throw the same rendered result for both Android and iOS, but since its Xamarin Forms, which allows you to use Native Looks and Feels after compilation, it’s going to be a mess!

…gotta suck it up!

As the developer, I have no choice but to get this done somehow, even though the hell breaks loose! because it’s my JOB! 😛

Well for the User’s perspective, getting rid of the Android looks and feel, will surely disturb the User Experience on Android.

Challenges..

So as the developer, here are some challenges I faced…

Difference between iOS Designs VS Android Designs!

evasion-ios-71

When you take a iOS Mobile App design, its mostly a static UI design, which uses mostly pixel perfect and screen coordination perfect values for placing the components on the Screen. This is because for iOS devices almost all their devices uses a pre-defined common Screen-Size-Resolution or as in Screen Height-Width Ratio.

top-android-phones1

but when it comes to Android devices, there’s a massive sea of fragmented Screen Sizes, and according to researches it only keeps on growing, therefor for Android Mobile App Designs are compiled with non-static flexible UI Design, which has room to stretch and shrink accordingly as much as possible without disrupting the User Experience.

Dealing with the Android sea of Device…

Just to give you a head start take a look at the following illustration which shows the different Android screen sizes that are available in the market by 2016! :O

screen-shot-2014-08-21-at-1-49-11-pm-1280x748

So you have to deal with the completely unexpected screen-sizes and resolutions, when you try to implement an iOS optimized UI Design for Android. Because of the pure difference I mentioned earlier, how could you take a static pixel-perfect, component coordinates fixed design and adopt it and make it flexible for the massive sea of device fragmentation of Android? 😦

…but not giving up!

So as the job role of a Developer we are suppose to solve problems and accept whatever the challenge throws at us, even the hell breaks loose!

So this is how I resolved it with Xamarin Forms!

So how did I do it?

So let’s go through one by one!

1. RelativeLayout for ze rescue…

Yes that’s right, Xamarin Forms provides several types of Layout types for the ease of Developer and luckily they also provide a Layout called Relative Layout. And yes it does exactly from what it’s name shows.

layouts1

RelativeLayout is used to position and size views relative to properties of the layout or sibling views.

Unlike StackLayout and GridLayouts which most developers use in Xamarin Forms, for the ease of use, RelativeLayout allows you to place its Children Relatively to each other or for the parent view.

Therefore when dealing with different screen sizes of Android we could push the UI to render itself Relatively to given parameters while maintaining the expected UI design as much as possible.

Take a look at the below example from Xamarin docs:

flag1

As you can see, using this Layout we could implement and render the same Design across all mobile platforms. Hence we are placing all the components on the screen, RELATIVE to each other.

2. Always use Constraint type as RelativetoParent!

RelativeLayout provdes three types of Constraint type to place your Components inside the layout, that are

  • RelativeToParent: Set the value relatively to the ParentView
  • RelativeToView: Set the value relatively to another View in the same parent Layout
  • Constant: Set the value as a Constant value

So out of these three types, I would recommend always sticking to the RelativeToParent type, which allows you to have more compatibility towards Android screen sizes, because we are relying on the Parent View which takes up the whole screen Heigh/Width/X or Y properties. So its always better to take relative values accordingly to the Parent instead of another View in the same Parent Layout, which may or may not be behaving as we expect, but as of Parent view, we are sure of its layout.

3. Use XConstraints and YConstraints

Now you must remember how I was crying out loud about having to implement a pixel-perfect screen coordinates fixed design on Android. 😛

So we are going to implement the exact same concept but RELATIVELY!

So with RelativeLayout we can set the X Y locations of a component Relatively to the parent or another view, which helps you place the component on the screen without any worries since those values will get set relatively at the run time. Yep no more worries about those coordinates for Android! 😉

4. Do not use StackLayout or GridLayout inside RelativeLayout!

That’s correct do not use them inside the RelativeLayout and place your child components inside them. Which will lead to the same issue we tried to solve at he beginning, because those Child elements will adopt themselves accordingly to the Stack or Grid Layouts, not for the RelativeLayout. So if you can try to place your child components inside RelativeLayout directly by setting the X/Y/Width/ and Height constraints.

5. Avoid Constants and use Factor…

Yes, with Factor you can get the best suited value according to the ratio of the Relative property, whether its X, Y, Width or even Height! 🙂

And please, stop using Constants, unless you really wants to, because then you will be completely killing the purpose of using the RelativeLayout, hence we need to achieve fluid flexibility to render the iOS optimized design on Android with Xamarin Forms! 😉

6. Also you could use Absolute Layout!

This also does something similar but a little bit differently which could also be used in our situation.

AbsoluteLayout positions and sizes child elements proportional to its own size and position or by absolute values.

So there’s something to keep in mind as well. 😉

Something to keep in mind…

Usage of RelativeLayout is going to be very costly during the run time, takes a lot of processing power to render the UI due to its Relative behaviour of the measurements, meanwhile Jason Smith also recommends avoid using RelativeLayout and instead stick to StackLayouts and GridLayouts.

But unfortunately in order to deal with this kind of scenarios, you have to compromise something , not to mention disrupting the native User Experience of the app.

Conclusion

So that’s how deal with my situation when I was given a iOS Optimized mobile design to be implemented on Android with Xamarin Forms! 😀

Very well then, if anyone else has gotten into this situation, I hope this post has helped you in some way! 🙂

Cheers!