Images captured by iOS show up rotated on Android

When you capture a photo with TakePhotoAsync on the iOS, regardless of the SaveMetaData flag or RotateImage, it might be stored rotated. However, you won’t notice it on the iOS itself, or on Windows, since the orientation information is stored in the EXIF metadata of the captured JPEG file.

iOS or Windows show the image correctly because they take into account the EXIF orientation, but the Image view of Xamarin.Forms on Android ignores this flag, thus making the image show up as-is, i.e. rotated.

If you store the image in a different format than JPEG (e.g. PNG) on the iOS, you can also see the image rotated there, because PNG doesn’t have an orientation flag.

If you are using Xamarin.Forms, consider using CachedImage from FFImageLoading instead of the built-in Image view. CachedImage looks at the orientation information on Android, and rotates the image accordingly before rendering it.

However, if you are uploading the image to an ASP.NET core application and need to process it, you can rotate it manually based on the EXIF information. .NET core by itself doesn’t have image manipulation capabilities, but you can install the CoreCompat.System.Drawing NuGet package, which is a port of System.Drawing for .NET core.

First, load the image as an Image object:

var image = Image.FromFile(path);

Then see if it has the rotation value, and rotate accordingly:

if (image.PropertyIdList.Contains(0x112))
{
    int rotationValue = image.GetPropertyItem(0x112).Value[0];
    if (rotationValue == 8) 
        image.RotateFlip(rotateFlipType: RotateFlipType.Rotate270FlipNone);
    else if (rotationValue == 3) 
        image.RotateFlip(rotateFlipType: RotateFlipType.Rotate180FlipNone);
    else if (rotateValue == 6)
        image.RotateFlip(rotateFlipType: RotateFlipType.Rotate90FlipNone);
}

Optionally, you can save the image on the original file:

image.Save(path);

 

Preventing multiple clicks on Xamarin.Forms buttons

I encountered this issue many times where a button was supposed to open a modal page, and tapping on that button repeatedly would open the modal page several times.

To prevent this, you can create a simple custom renderer for the Button class, and put a timer which disables the button for a short period of time after each successful tap:

#if __ANDROID__
    using Xamarin.Forms.Platform.Android;
    using Android.Views;
#elif __IOS__
    using Xamarin.Forms.Platform.iOS;
    using UIKit;
#endif

using Xamarin.Forms;
using System.Threading.Tasks;
using Neat.Renderers;

[assembly: ExportRenderer(typeof(Button), typeof(BlockingButtonRenderer))]
namespace Neat.Renderers
{
    public class BlockingButtonRenderer : ButtonRenderer
    {
        bool _isPressed = false;
        
#if __ANDROID__
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
        {
            base.OnElementChanged(e);
            
            var control = Control as Android.Widget.Button;
            control?.SetOnClickListener(ButtonClickListener.Instance.Value);
        }
        
        public async void SendBlockingClicked()
        {
            if (_isPressed) return;
            _isPressed = true;
            ((IButtonController)Element).SendClicked();
            await Task.Delay(500);
            _isPressed = false;
        }
        
        class ButtonClickListener : Java.Lang.Object, IOnClickListener
        {
            public static readonly Lazy<ButtonClickListener> Instance =
                new Lazy<ButtonClickListener>(() =>
                    new ButtonClickListener());
                    
            public void OnClick(Android.Views.View view)
            {
                var renderer = view.Tag as ButtonRenderer;
                (renderer as BlockingButtonRenderer)?.SendBlockingClicked();
            }
        }
#elif __IOS__
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
        {
            base.OnElementChanged(e);
            
            var control = Control as UIButton;
            if (control != null)
            {
                control.TouchUpInside += async (sender, ev) =>
                {
                    if (_isPressed) return;
                    _isPressed = true;
                    var oldInteractionFlag = control.UserInteractionEnabled;
                    control.UserInteractionEnabled = false;
                    await Task.Delay(500);
                    control.UserInteractionEnabled = oldInteractionFlag;
                    _isPressed = false;
                };
            }
        }
#endif
    }
}

Xamarin.Android Rounded Corners Masked Layout

If you want to create a FrameLayout that has real rounded corners and clips its children beyond its corners, you can use the following class, which is a port of an answer on StackOverflow. You should replace Constants.PackageName with your Android package name, e.g. com.mycompany.myapp.

using Android.Content;
using Android.Graphics;
using Android.Runtime;
using Android.Util;
using Android.Widget;

namespace UIWidgets.Android
{
[Register(Constants.PackageName + ".UIWidgets.Android.RoundedCornerLayout")]
public class RoundedCornerLayout : FrameLayout
{
const float CornerRadius = 20.0f;

Bitmap maskBitmap;
Paint paint, maskPaint;
float cornerRadius;

public RoundedCornerLayout(Context context) : base(context)
{
Init(context);
}

public RoundedCornerLayout(Context context, IAttributeSet attrs) : base(context, attrs)
{
Init(context, attrs);
}

public RoundedCornerLayout(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
Init(context, attrs, defStyle);
}

void Init(Context context, IAttributeSet attrs = null, int defStyle = 0)
{
DisplayMetrics metrics = Context.Resources.DisplayMetrics;
cornerRadius = TypedValue.ApplyDimension(ComplexUnitType.Dip, CornerRadius, metrics);
paint = new Paint(PaintFlags.AntiAlias);
maskPaint = new Paint(PaintFlags.AntiAlias | PaintFlags.FilterBitmap);
maskPaint.SetXfermode(new PorterDuffXfermode(PorterDuff.Mode.Clear));
SetWillNotDraw(false);
}

public override void Draw(Canvas canvas)
{
Bitmap offscreenBitmap = Bitmap.CreateBitmap(canvas.Width, canvas.Height, Bitmap.Config.Argb8888);
Canvas offscreenCanvas = new Canvas(offscreenBitmap);

base.Draw(offscreenCanvas);

maskBitmap = maskBitmap ?? CreateMask(canvas.Width, canvas.Height);
offscreenCanvas.DrawBitmap(maskBitmap, 0.0f, 0.0f, maskPaint);
canvas.DrawBitmap(offscreenBitmap, 0.0f, 0.0f, paint);
}

Bitmap CreateMask(int width, int height)
{
Bitmap mask = Bitmap.CreateBitmap(width, height, Bitmap.Config.Alpha8);
Canvas canvas = new Canvas(mask);

Paint paint = new Paint(PaintFlags.AntiAlias);
paint.Color = Color.White;

canvas.DrawRect(0, 0, width, height, paint);

paint.SetXfermode(new PorterDuffXfermode(PorterDuff.Mode.Clear));
canvas.DrawRoundRect(new RectF(0, 0, width, height), cornerRadius, cornerRadius, paint);

return mask;
}
}
}

And to use it in your AXML:

 <UIWidgets.Android.RoundedCornerLayout
 android:layout_width="150dp"
 android:layout_height="150dp">

 <RelativeLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="#FFDDAA"
 />
 </UIWidgets.Android.RoundedCornerLayout>

REST requests with Xamarin and RestSharp

RestSharp (available on NuGet) is a clean way to send HTTP requests and handle their response. As a portable package, you can use it in your Xamarin.iOS and Xamarin.Android applications. The following example, POSTs a serialized JSON body, created automatically from a C# class, to an endpoint located at https://myserver.com/api/messages/message. It also sends a token parameter inside the query string:

public class MessageDTO : DTO
{
    public string Sender { get; set; }
    public string Recipient { get; set; }
    public string Body { get; set; }
}
public static class PinHelper
{
    async void SendMessage(MessageDTO dto)
    {
        const string ApiPath = "https://myserver.com/api/";
        var client = new RestClient (ApiPath);
        var request = new RestRequest ("messages/message", Method.POST);
        request.AddQueryParameter ("token", Settings.Token);
        request.AddJsonBody (dto);

        try
        {
            var result = await client.ExecuteTaskAsync(request);
            Debug.WriteLine($"Result Status Code: {result.StatusCode} - {result.Content}");
        }
        catch (Exception e) {
            Debug.WriteLine ($"Error: {e.Message}");
        }
    }
}

On an ASP.NET WebApi controller with EntityFramework, this request can be received and stored using a function that looks like this:

[HttpPost][Route("message")]
public IHTTPActionResult AddMessage(string token, [FromBody] MessageDTO dto)
{
    Message message = new Message ();
    dto.Inject (message);
    db.Messages.Add (message);
    db.SaveChanges ();

    return Ok ();
}

New titles published on Google Play!

My new game “Naturals” which is a set of brain teasing puzzles based on natural numbers is now available on Google Play Store free of charge! This version of the game contains fifteen puzzles, and more puzzles will come in future updates. For more information about Naturals, visit its own page on my blog.

 

In addition, the first Quickie title, “Ball Fall” is also available on Google Play. More information about Ball Fall is available here.

If you are interested, you can get the complete cross-platform source codes for these games by making a donation via PayPal.