Blog

Xamarin.Forms – Mock Locations

This Project you can find at: https://github.com/officialdoniald/Xamarin.Forms.MockLocation.

Forms:

We can detect Location changing in the Forms Project: https://github.com/officialdoniald/Xamarin.Forms.MockLocation/blob/master/Xamarin.Forms.MockLocation.Mobile/Xamarin.Forms.MockLocation.Mobile/MainPage.xaml.cs. If we will get a Mock Location this will detect it.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace Xamarin.Forms.MockLocation.Mobile
{
    // Learn more about making custom code visible in the Xamarin.Forms previewer
    // by visiting https://aka.ms/xamarinforms-previewer
    [DesignTimeVisible(false)]
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            Thread thread = new Thread(async () =>
            {
                while (true)
                {
                    var request = new GeolocationRequest(GeolocationAccuracy.Medium);
                    var location = await Geolocation.GetLocationAsync(request);

                    if (location != null)
                    {
                        if (location.IsFromMockProvider)
                        {
                            //Detect mocking location
                        }
                        else
                        {
                            //Detect normal location
                        }
                    }
                }
            });
                    }
    }
}

The two platforms need to be discussed separately. In Android we can make an Implementation for Mock Location, but in iOS, we have to use a script file and we have to add to the emulator, so we can’t make this programmatically.

Android:

First, we have to request permissions in the Manifest and in the MainActivity (Runtime).

Manifest: https://github.com/officialdoniald/Xamarin.Forms.MockLocation/blob/master/Xamarin.Forms.MockLocation.Mobile/Xamarin.Forms.MockLocation.Mobile.Android/Properties/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.xamarin.forms.mocklocation.mobile">
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
  <application android:label="Xamarin.Forms.MockLocation.Mobile.Android"></application>
  
  <uses-permission android:name="android.permission.PRIORITY_HIGH_ACCURACY" />
  <uses-feature android:name="android.hardware.location" android:required="false" />
  <uses-feature android:name="android.hardware.location.gps" android:required="false" />
  <uses-feature android:name="android.hardware.location.network" android:required="false" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <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_MOCK_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>

MainActivity: https://github.com/officialdoniald/Xamarin.Forms.MockLocation/blob/master/Xamarin.Forms.MockLocation.Mobile/Xamarin.Forms.MockLocation.Mobile.Android/MainActivity.cs

protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);

            Context = this.ApplicationContext;

            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

            ActivityCompat.RequestPermissions(this, new String[] { Manifest.Permission.AccessFineLocation }, 1);
            
            LoadApplication(new App());
        }

Allright! We have to send the mock(test) location via LocationManager. Let’s create an interface in the Forms Project and call the Android implementation via DependencyService.

Forms: https://github.com/officialdoniald/Xamarin.Forms.MockLocation/blob/master/Xamarin.Forms.MockLocation.Implementation/IMockLocationProvider.cs.

namespace Xamarin.Forms.MockLocation.Implementation
{
    public interface IMockLocationProvider
    {
        void SendMockLocation(MockPosition mockPosition);
    }

    public class MockPosition
    {
        public double Longitude { get; set; }

        public double Latitude { get; set; }

        /// <summary>
        /// Default value: 1.0.
        /// </summary>
        public double Altitude { get; set; } = 1.0;
    }
}

Android: https://github.com/officialdoniald/Xamarin.Forms.MockLocation/blob/master/Xamarin.Forms.MockLocation.Mobile/Xamarin.Forms.MockLocation.Mobile.Android/Implementations/MockLocationProvider.cs.

using System;
using Android.Content;
using Android.Hardware;
using Android.Locations;
using Xamarin.Forms.MockLocation.Implementation;
using Xamarin.Forms.MockLocation.Mobile.Droid.Implementations;

[assembly: Xamarin.Forms.Dependency(typeof(MockLocationProvider))]
namespace Xamarin.Forms.MockLocation.Mobile.Droid.Implementations
{
    public class MockLocationProvider : IMockLocationProvider
    {
        public void SendMockLocation(MockPosition position)
        {
            Location location = new Location(LocationManager.GpsProvider)
            {
                Latitude = position.Latitude,
                Longitude = position.Longitude,
                Altitude = position.Altitude,
                Time = DateTime.Now.Ticks,
                ElapsedRealtimeNanos = 100,
                Speed = 0.0f,
                Bearing = 0.0f,
                Accuracy = 0
            };

            LocationManager locationManager = MainActivity.Context.GetSystemService(Context.LocationService) as LocationManager;

            locationManager.AddTestProvider(LocationManager.GpsProvider, false, false, false, false, false, false, false, Power.Low, SensorStatus.AccuracyHigh);
            locationManager.SetTestProviderLocation(LocationManager.GpsProvider, location);
            locationManager.SetTestProviderEnabled(LocationManager.GpsProvider, true);
        }
    }
}

Deploy your app to the phone and close it.

One more important thing: only the System app can send mock locations, so we have to call for the operation system, that this will send mock locations. So if you haven’t already, let’s enable developer mode on your phones and go to developer options.

Select the Mock Location, and select you app. Run the app again and it will work.

iOS:

So, as above said, we can mock the Location programmatically. We have to create a script fiel and add it to the Emulator. Here is a good website for the gpx file generating: https://www.gpxgenerator.com/.

<?xml version="1.0"?>
<gpx version="1.1" creator="gpxgenerator.com">
<wpt lat="50.84633694747007" lon="4.364619255065918">
    <time>2017-02-10T10:48:50Z</time>
</wpt>
<wpt lat="50.845889847299254" lon="4.36431884765625">
    <time>2017-02-10T10:49:28Z</time>
</wpt>
<wpt lat="50.84507692692001" lon="4.363868236541748">
    <time>2017-02-10T10:50:36Z</time>
</wpt>
<wpt lat="50.84460271682405" lon="4.363675117492676">
    <time>2017-02-10T10:51:14Z</time>
</wpt>
<wpt lat="50.84374912650492" lon="4.363245964050293">
    <time>2017-02-10T10:52:25Z</time>
</wpt>
<wpt lat="50.84296326764228" lon="4.36281681060791">
    <time>2017-02-10T10:53:31Z</time>
</wpt>
<wpt lat="50.84281578200512" lon="4.36181902885437">
    <time>2017-02-10T10:54:22Z</time>
</wpt>
<wpt lat="50.84310709422066" lon="4.360542297363281">
    <time>2017-02-10T10:55:30Z</time>
</wpt>
<wpt lat="50.84268028733167" lon="4.359984397888184">
    <time>2017-02-10T10:56:13Z</time>
</wpt>
<wpt lat="50.84245672026023" lon="4.359222650527954">
    <time>2017-02-10T10:56:55Z</time>
</wpt>
<wpt lat="50.843019023277556" lon="4.35808539390564">
    <time>2017-02-10T10:58:07Z</time>
</wpt>
<wpt lat="50.84339840461739" lon="4.357527494430542">
    <time>2017-02-10T10:58:48Z</time>
</wpt>
<wpt lat="50.84356777029096" lon="4.357130527496338">
    <time>2017-02-10T10:59:12Z</time>
</wpt>
<wpt lat="50.844069089081536" lon="4.356368780136108">
    <time>2017-02-10T11:00:07Z</time>
</wpt>
<wpt lat="50.84437550047113" lon="4.3559181690216064">
    <time>2017-02-10T11:00:40Z</time>
</wpt>
<wpt lat="50.84555269015878" lon="4.354877471923828">
    <time>2017-02-10T11:02:27Z</time>
</wpt>
<wpt lat="50.84620302096338" lon="4.354770183563232">
    <time>2017-02-10T11:03:18Z</time>
</wpt>
<wpt lat="50.8467178597545" lon="4.354383945465088">
    <time>2017-02-10T11:04:03Z</time>
</wpt>
<wpt lat="50.84665428412764" lon="4.3536436557769775">
    <time>2017-02-10T11:04:40Z</time>
</wpt>
<wpt lat="50.84662380036855" lon="4.353042840957642">
    <time>2017-02-10T11:05:10Z</time>
</wpt>
<wpt lat="50.846430736098434" lon="4.3524473905563354">
    <time>2017-02-10T11:05:43Z</time>
</wpt>
<wpt lat="50.846213961280625" lon="4.351991415023804">
    <time>2017-02-10T11:06:11Z</time>
</wpt>
<wpt lat="50.84603783095749" lon="4.3516212701797485">
    <time>2017-02-10T11:06:34Z</time>
</wpt>
<wpt lat="50.845837989977895" lon="4.351256489753723">
    <time>2017-02-10T11:06:58Z</time>
</wpt>
<wpt lat="50.84563476098514" lon="4.350859522819519">
    <time>2017-02-10T11:07:23Z</time>
</wpt>
<wpt lat="50.84547556432245" lon="4.350618124008179">
    <time>2017-02-10T11:07:40Z</time>
</wpt>
<wpt lat="50.84531975429677" lon="4.350387454032898">
    <time>2017-02-10T11:07:56Z</time>
</wpt>
<wpt lat="50.845167330942075" lon="4.350119233131409">
    <time>2017-02-10T11:08:14Z</time>
</wpt>
<wpt lat="50.84499536286211" lon="4.3499743938446045">
    <time>2017-02-10T11:08:29Z</time>
</wpt>
</gpx>

If we get a gpx file, we have to script the Emulator. Open the AppleScript App on the Mac and create a new script:

on replace_chars(this_text, search_string, replacement_string)
    set AppleScript's text item delimiters to the search_string
    set the item_list to every text item of this_text
    set AppleScript's text item delimiters to the replacement_string
    set this_text to the item_list as string
    set AppleScript's text item delimiters to ""
    return this_text
end replace_chars
 
on run arg
--> arg is a collection of the command line parameters
    
    set xmlPathParam to item 1 of arg --> first param is the location of the gpx file
    set sleepParam to item 2 of arg --> second param is the time (seconds) to sleep between each iteration
 
    tell application "System Events"
        tell XML file xmlPathParam
            tell XML element "gpx"
                set wptElements to every XML element whose name = "wpt" -->put wpt elements in a collection for later use
            end tell
    end tell
    
    tell process "Simulator"
        set frontmost to true
            repeat with c from 1 to count of wptElements
                set wptElement to item c of wptElements
                tell wptElement
                    set lon to value of XML attribute "lon" of wptElement --> putting the lon value of the wpt element in a variable for later use
                    set lat to value of XML attribute "lat" of wptElement --> putting the lan value of the wpt element in a variable for later use
                end tell
                    
                click menu item "Custom Location…" of menu of menu item "Location" of menu "Debug" of menu bar 1
                set popup to window "Custom Location"
                set value of text field 1 of popup to my replace_chars(lat, ".", ",")
                set value of text field 2 of popup to my replace_chars(lon, ".", ",")
                click button "OK" of popup
                    
                delay sleepParam --> sleep to simulate 'natural' movement
            end repeat
        end tell
    end tell
end run

With this script this will open our gpx file and processes it.

iOS Blog Source: https://blog.pieeatingninjas.be/2017/02/10/custom-location-and-movement-on-xamarin-remoted-ios-simulator-for-windows/.

Another way: you can create a Xamarin UI Test Project and you can mock the location too. Just call the SetLocation function. (On Android it is still required that change the Mock Location app and request permissions.)

Xamarin.Forms Twilio implementation

This project you can find at: https://github.com/officialdoniald/Xamarin.Forms.Twilio

Twilio created just for Android and iOS libs and not for Xamarin. So we need to create Bindings for Xamarin.Forms and native.

First step: we need to create Android Binding Library(https://github.com/officialdoniald/Xamarin.Forms.Twilio/tree/master/Xamarin.Forms.Twilio.PoC/Xamarin.Android.Twilio.Client) and iOS Binding Library(https://github.com/officialdoniald/Xamarin.Forms.Twilio/tree/master/Xamarin.Forms.Twilio.PoC/Xamarin.iOS.Twilio.Client) in an Emptys Solution. We have to add the .aar/.jar file(to Jars folder to Android) and .a files (as native reference to iOS) to the Projects. But if you want to just using the completed DLL, just download theese Project from my GitHub, Clean and Rebuild and use the builded DLLs or just added theese Projects to your Application Project as Reference.

After this we need to create a Xamarin.Forms Application(https://github.com/officialdoniald/Xamarin.Forms.Twilio/tree/master/Xamarin.Forms.Twilio.PoC/Xamarin.Forms.Twilio.PoC).

We need to add the DLLs or the Projects as Reference to the various Platforms Projects.

Super! We can use the Twilio lib from the Android and iOS Projects. But if we want to use theese from the Forms Application we need to create some Platform Specific implementations and we will call theese throught DependencyService.

Let’s create an Interface in the Forms Application: https://github.com/officialdoniald/Xamarin.Forms.Twilio/blob/master/Xamarin.Forms.Twilio.PoC/Xamarin.Forms.Twilio.PoC/Implementation/Twilio/ITwilioClient.cs

Now we have to create some events. We will implement the MakeOutBoundCall, Mute and HangOut functions from Twilio. https://github.com/officialdoniald/Xamarin.Forms.Twilio/blob/master/Xamarin.Forms.Twilio.PoC/Xamarin.Forms.Twilio.PoC/Helper/GlobalEvents.cs

Great! Now just implement theese in the various Platforms and we will talk about what have to do, when we want to use theese functions without crash.

Android:

Let’s create the interface imaplementation: https://github.com/officialdoniald/Xamarin.Forms.Twilio/blob/master/Xamarin.Forms.Twilio.PoC/Xamarin.Forms.Twilio.PoC.Android/Implementation/Twilio/TwilioClient.cs

Open the MainActivity.cs and create the Twilio object.: https://github.com/officialdoniald/Xamarin.Forms.Twilio/blob/master/Xamarin.Forms.Twilio.PoC/Xamarin.Forms.Twilio.PoC.Android/MainActivity.cs

Very important: we need to change our AndroidManifest.xml file: https://github.com/officialdoniald/Xamarin.Forms.Twilio/blob/master/Xamarin.Forms.Twilio.PoC/Xamarin.Forms.Twilio.PoC.Android/Properties/AndroidManifest.xml. We need to add some uses and service.

Done!

iOS:

https://github.com/officialdoniald/Xamarin.Forms.Twilio/tree/master/Xamarin.Forms.Twilio.PoC/Xamarin.Forms.Twilio.PoC.iOS

Don’t worry, if there will be some errors, that’s a Visual Studio bug. The project will be built.

Let’s create the interface imaplementation: https://github.com/officialdoniald/Xamarin.Forms.Twilio/blob/master/Xamarin.Forms.Twilio.PoC/Xamarin.Forms.Twilio.PoC.iOS/Implementation/Twilio/TwilioClient.cs

Open the AppDelegate.cs file: https://github.com/officialdoniald/Xamarin.Forms.Twilio/blob/master/Xamarin.Forms.Twilio.PoC/Xamarin.Forms.Twilio.PoC.iOS/AppDelegate.cs

And here we will use the microphone we have to change the Info.plist file: https://github.com/officialdoniald/Xamarin.Forms.Twilio/blob/master/Xamarin.Forms.Twilio.PoC/Xamarin.Forms.Twilio.PoC.iOS/Info.plist

    <key>NSMicrophoneUsageDescription</key>
    <string>YOUR_APP_NAME will use your microphone, when you call someone.</string>

When you make an OutBoundCall you can use a lots of combination of the parameters. See some of theese at: https://www.twilio.com/docs/voice/client/tutorials/outgoing-calls.

Xamarin.Forms send and receive SMS

This project you can find it at: https://github.com/officialdoniald/Xamarin.Forms.SendAndReceiveSMS

sms various platform

We can easily send SMS from various platforms, but we can’t read SMSs so easily.

Android:

Receive SMS:

In Android we can send and receive/read SMSs with just few line of code. We have to implement a BroadcastReceiver and gain permission in the AndroidManifest.xml.

1. AndroidManifest.xml(https://github.com/officialdoniald/Xamarin.Forms.SendAndReceiveSMS/blob/master/SendAndReceiveSMS/SendAndReceiveSMS.Android/Properties/AndroidManifest.xml):

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="SendAndReceiveSMS.Android" android:versionCode="1" android:versionName="1.0" android:installLocation="auto">
	<uses-sdk />
	<uses-permission android:name="android.permission.RECEIVE_SMS" />
	<uses-permission android:name="android.permission.READ_SMS" />
	<uses-permission android:name="android.permission.BROADCAST_SMS" />
	<uses-permission android:name="android.permission.SEND_SMS" />
	<uses-permission android:name="android.permission.WRITE_SMS" />
	<application android:label="SendAndReceiveSMS.Android"></application>
</manifest>

We have to gain permission to RECEIVE_SMS, READ_SMS, SEND_SMS and WRITE_SMS. (BROADCAST_SMS is optional)

2. Create a BroadcastReceiver(https://github.com/officialdoniald/Xamarin.Forms.SendAndReceiveSMS/blob/master/SendAndReceiveSMS/SendAndReceiveSMS.Android/Receivers/SmsListener.cs)

We have to create a BroadcastReceiver in the Android Application. I created it in a Receivers folder:

using System;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Telephony;
using SendAndReceiveSMS.Events;

namespace SendAndReceiveSMS.Droid.Receivers
{
    [BroadcastReceiver]
    [IntentFilter(new[] { "android.provider.Telephony.SMS_RECEIVED" }, Priority = (int)IntentFilterPriority.HighPriority)]
    public class SmsListener : BroadcastReceiver
    {
        protected string message, address = string.Empty;

        public override void OnReceive(Context context, Intent intent)
        {
            if (intent.Action.Equals("android.provider.Telephony.SMS_RECEIVED"))
            {
                Bundle bundle = intent.Extras;
                if (bundle != null)
                {
                    try
                    {
                        var smsArray = (Java.Lang.Object[])bundle.Get("pdus");

                        foreach (var item in smsArray)
                        {
                            #pragma warning disable CS0618
                            var sms = SmsMessage.CreateFromPdu((byte[])item);
                            #pragma warning restore CS0618
                            address = sms.OriginatingAddress;
                            message = sms.MessageBody;

GlobalEvents.OnSMSReceived_Event(this, new SMSEventArgs() { PhoneNumber = address, Message = message });
                        }
                    }
                    catch (Exception)
                    {
                        //Something went wrong.
                    }
                }
            }
        }
    }
}

If we have to know in the StandardLibrary, what was the Message, we have to create an event in the StandardLibrary and call it from the Android SmsListener BroadcastReceiver. (https://github.com/officialdoniald/Xamarin.Forms.SendAndReceiveSMS/blob/master/SendAndReceiveSMS/SendAndReceiveSMS/Events/GlobalEvents.cs)

GlobalEvents.OnSMSReceived_Event(this, new SMSEventArgs() { PhoneNumber = address, Message = message }); -> this line will call, when an SMS received.
using System;

namespace SendAndReceiveSMS.Events
{
    public class GlobalEvents
    {
        public static event EventHandler<SMSEventArgs> OnSMSReceived;

        public static void OnSMSReceived_Event(object sender, SMSEventArgs sms)
        {
            OnSMSReceived?.Invoke(sender, sms);
        }
    }

    public class SMSEventArgs : EventArgs
    {
        public string PhoneNumber { get; set; }

        public string Message { get; set; }
    }
}

Send SMS

There is two methods: throught Xamarin.Forms.Essentials NuGet Package and throught Dependency Service, some platform specific implementation. We will see just throught Dependency Service.

1. We have to create in the Standard Library an Interface (I created an Interfaces folder): (https://github.com/officialdoniald/Xamarin.Forms.SendAndReceiveSMS/blob/master/SendAndReceiveSMS/SendAndReceiveSMS/Interfaces/ISendSms.cs)

namespace SendAndReceiveSMS.Interfaces
{
    public interface ISendSms
    {
        void Send(string address, string message);
    }
}

2. We have to implement in Android: (https://github.com/officialdoniald/Xamarin.Forms.SendAndReceiveSMS/blob/master/SendAndReceiveSMS/SendAndReceiveSMS.Android/Receivers/SendSms.cs)

using Android.App;
using Android.Content;
using Android.Telephony;
using SendAndReceiveSMS.Droid.Receivers;
using SendAndReceiveSMS.Interfaces;
using Xamarin.Forms;

[assembly: Dependency(typeof(SendSms))]
namespace SendAndReceiveSMS.Droid.Receivers
{
    public class SendSms : ISendSms
    {
        public void Send(string address, string message)
        {
            var pendingIntent = PendingIntent.GetActivity(Android.App.Application.Context, 0, new Intent(Android.App.Application.Context, typeof(MainActivity)).AddFlags(ActivityFlags.ClearTop | ActivityFlags.NewTask), PendingIntentFlags.NoCreate);

            SmsManager smsM = SmsManager.Default;
            smsM.SendTextMessage(address, null, message, pendingIntent, null);
        }
    }
}

And that’s it. We can send and receive SMSs in Android.

iOS

Because of the privcy we can’t read SMSs(Messages) in iOS, just extract from OTP(One Time Password) Messages the OTP.

What is OTP Message?

This Messages contain “code” or “password” and a number.

“Your password is: 012784.” or “You verification code is: 873274.”.

Theese numbers are detected by the operation system and show they above the keyboard.

https://i.stack.imgur.com/9QoVj.png

Receive SMS (OTP Message)

First we have to do a custom Entry in the .NET Standard Library:

using Xamarin.Forms;

namespace SendAndReceiveSMS.CustomRenderer
{
    public class OTPAutoFillControl : Entry
    {

    }
}

And implement it in the iOS Project:

using SendAndReceiveSMS.CustomRenderer;
using SendAndReceiveSMS.iOS.CustomRenderer;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(OTPAutoFillControl), typeof(OTPAutoFillControlRenderer))]
namespace SendAndReceiveSMS.iOS.CustomRenderer
{
    public class OTPAutoFillControlRenderer : EntryRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);

            if (e.NewElement != null)
            {
                Control.TextContentType = UITextContentType.OneTimeCode;
            }
        }
    }
}

Send SMS

There is two methods: throught Xamarin.Forms.Essentials NuGet Package and throught Dependency Service, some platform specific implementation. We will see just throught Dependency Service.

0. You can skip the 1. step if you are already done with the Android part.

1. We have to create in the Standard Library an Interface (I created an Interfaces folder): (https://github.com/officialdoniald/Xamarin.Forms.SendAndReceiveSMS/blob/master/SendAndReceiveSMS/SendAndReceiveSMS/Interfaces/ISendSms.cs)

namespace SendAndReceiveSMS.Interfaces
{
    public interface ISendSms
    {
        void Send(string address, string message);
    }
}

2. We have to implement in iOS

using System;
using MessageUI;
using SendAndReceiveSMS.Interfaces;
using UIKit;

namespace SendAndReceiveSMS.iOS.Receivers
{
    public class SendSms : ISendSms
    {
        public void Send(string body, string phoneNumber)
        {
            if (!MFMailComposeViewController.CanSendMail)
                return;

            MFMessageComposeViewController smsController = new MFMessageComposeViewController();

            smsController.Recipients = new[] { phoneNumber };
            smsController.Body = body;

            EventHandler<MFMessageComposeResultEventArgs> handler = null;
            handler = (sender, args) =>
            {
                smsController.Finished -= handler;

                var uiViewController = sender as UIViewController;
                if (uiViewController == null)
                {
                    throw new ArgumentException("sender");
                }

                uiViewController.DismissViewControllerAsync(true);
            };

            smsController.Finished += handler;

            UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewControllerAsync(smsController, true);
        }
    }
}

Call the Send and Receive SMS from the StandardLibrary: (https://github.com/officialdoniald/Xamarin.Forms.SendAndReceiveSMS/blob/master/SendAndReceiveSMS/SendAndReceiveSMS/MainPage.xaml.cs)

using SendAndReceiveSMS.Events;
using SendAndReceiveSMS.Interfaces;
using System;
using Xamarin.Forms;

namespace SendAndReceiveSMS
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            GlobalEvents.OnSMSReceived += GlobalEvents_OnSMSReceived;
        }

        private void GlobalEvents_OnSMSReceived(object sender, SMSEventArgs e)
        {
            EntryMessage.Text = e.Message;
        }

        /// <summary>
        /// SMS Send.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button_Clicked(object sender, EventArgs e)
        {
            DependencyService.Get<ISendSms>().Send(EntryNumber.Text, EntryMessage.Text);
        }
    }
}

And some UI in the .NET Standard Library:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:SendAndReceiveSMS.CustomRenderer"
             x:Class="SendAndReceiveSMS.MainPage">
    <StackLayout>
        <Entry Keyboard="Numeric" Text="" Placeholder="Phone Number" x:Name="EntryNumber"/>
        <Entry Text="" Placeholder="Android Message" x:Name="EntryMessage"/>
        <local:OTPAutoFillControl Keyboard="Numeric" Text="" Placeholder="iOS OTP Message" x:Name="OTPEntryMessage"/>
        <Button Text="Send" Clicked="Button_Clicked"/>
        <!--Placeholder="XXXXXX"-->
    </StackLayout>
</ContentPage>

Xamarin.Forms using Mqtt

We can using of course mqtt in Xamarin.Form via dependency service / Xamarin.Forms.MessagingCenter, event or simply create a class instance. We are following the last option.

First step, we need to install the MQTTnet NuGet Package for the Solution. (So in each project.)

.NET Standard Library:

We need to create an MqttClientRepository class in the .NET Standard Library.

public class MqttClientRepository
    {
        Dictionary<string, TopicFilter> _topicFilter;

        private static IMqttClient client;

        public IMqttClient Create(string server, int? port, string userName, string password, List<string> topics)
        {
            _topicFilter = new Dictionary<string, TopicFilter>();

            foreach (var topic in topics)
            {
                TopicFilter topicFilter = new TopicFilter
                {
                    Topic = topic,
                    QualityOfServiceLevel = MQTTnet.Protocol.MqttQualityOfServiceLevel.AtMostOnce
                };

                _topicFilter.Add(topic, topicFilter);
            }

            Task.Run(() => MqttClientRunAsync(server, port, userName, password)).Wait();

            return client;
        }

        private async Task MqttClientRunAsync(string server, int? port, string userName, string password)
        {
            try
            {
                var options = new MqttClientOptions
                {
                    ClientId = "YOURCLIENTID",
                    CleanSession = true,
                    ChannelOptions = new MqttClientTcpOptions
                    {
                        Server = server,
                        Port = port
                    },
                    Credentials = new MqttClientCredentials
                    {
                        Username = userName,
                        Password = Encoding.UTF8.GetBytes(password)
                    }
                };

                var factory = new MqttFactory();

                client = factory.CreateMqttClient();

                client.ConnectedHandler = new MqttConnectedHandler(_topicFilter, client);
                client.DisconnectedHandler = new MqttDisconnectedHandler(options, client);

                try
                {
                    await client.ConnectAsync(options);
                }
                catch //(Exception ex)
                {
                    
                }
            }
            catch //(Exception ex)
            {
                
            }
        }
    }

    public class MqttDisconnectedHandler : IMqttClientDisconnectedHandler
    {
        private IMqttClient _client;
        private MqttClientOptions _options;

        public MqttDisconnectedHandler(MqttClientOptions options, IMqttClient client)
        {
            _options = options;
            _client = client;
        }

        public async Task HandleDisconnectedAsync(MqttClientDisconnectedEventArgs eventArgs)
        {
            await Task.Delay(TimeSpan.FromSeconds(5));

            try
            {
                
            }
            catch
            {

            }
        }
    }

    public class MqttConnectedHandler : IMqttClientConnectedHandler
    {
        private IMqttClient _client;
        private Dictionary<string, TopicFilter> _topicFilter;

        public MqttConnectedHandler(Dictionary<string, TopicFilter> topicFilter, IMqttClient client)
        {
            _topicFilter = topicFilter;
            _client = client;
        }

        public async Task HandleConnectedAsync(MqttClientConnectedEventArgs eventArgs)
        {
            await _client.SubscribeAsync(_topicFilter.Values.ToArray());
        }
    }
}

Next step the platform specific implementations.

Android:

We need to create a MqttTaskService class in the Android Project.

public class MqttTaskService
    {
        private IMqttClient _mqttClient;
        private readonly string _sessionPayedTopic;

        public MqttTaskService()
        {
            _sessionPayedTopic = "YOUR TOPIC";

            _mqttClient = new MqttClientRepository().Create(
             "MqttServer",
             MqttPort IN INTEGER,
             "MqttUserName",
             "MqttPassword",
             new List<string> { _sessionPayedTopic });
            
            _mqttClient.ApplicationMessageReceivedHandler = new SubscribeCallback(_sessionPayedTopic);
        }

        public void UnSubscribe()
        {
            _mqttClient.ApplicationMessageReceivedHandler = null;
        }
    }

    public class SubscribeCallback : IMqttApplicationMessageReceivedHandler
    {
        private readonly string _sessionPayedTopic;

        public SubscribeCallback(string sessionPayedTopic)
        {
            _sessionPayedTopic = sessionPayedTopic;
        }

        public Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs e)
        {
            string message = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
            
            if (e.ApplicationMessage.Topic == _sessionPayedTopic)
            {
                
            }

            return Task.CompletedTask;
        }
    }

We need to create an instance in the Android project, maybe in the MainActivity at the end in the OnCreate function.

 MqttTaskService mqttTaskService = new MqttTaskService();

If we want to unsubscribe:

mqttTaskService.UnSubscribe(); call this

iOS

The same. We need to create a MqttTaskService class in the iOS Project.

public class MqttTaskService
    {
        private IMqttClient _mqttClient;
        private readonly string _sessionPayedTopic;

        public MqttTaskService()
        {
            _sessionPayedTopic = "YOUR TOPIC";

            _mqttClient = new MqttClientRepository().Create(
             "MqttServer",
             MqttPort IN INTEGER,
             "MqttUserName",
             "MqttPassword",
             new List<string> { _sessionPayedTopic });
            
            _mqttClient.ApplicationMessageReceivedHandler = new SubscribeCallback(_sessionPayedTopic);
        }

        public void UnSubscribe()
        {
            _mqttClient.ApplicationMessageReceivedHandler = null;
        }
    }

    public class SubscribeCallback : IMqttApplicationMessageReceivedHandler
    {
        private readonly string _sessionPayedTopic;

        public SubscribeCallback(string sessionPayedTopic)
        {
            _sessionPayedTopic = sessionPayedTopic;
        }

        public Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs e)
        {
            string message = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
            
            if (e.ApplicationMessage.Topic == _sessionPayedTopic)
            {
                
            }

            return Task.CompletedTask;
        }
    }

We need to create an instance in the iOS Project, maybe in the MainActivity at the end in the OnCreate function.

 MqttTaskService mqttTaskService = new MqttTaskService();

If we want to unsubscribe:

mqttTaskService.UnSubscribe(); call this

Okay, okay, we know if a message got, but how can we indicate to the .NET Standard Library? We have to create an event or through MessagingCenter. Use this, if you got a message in the various platform codes:

MessagingCenter.Send("SessionPayed", "SessionPayed", message);

And use this code in the .NET Standard Library, if you want to be notified, when a message got:

MessagingCenter.Subscribe<string, string>("SessionPayed", "SessionPayed", (sender, args) =>
            {
                Device.BeginInvokeOnMainThread(()=> 
                {
                    //You got a message from Mqtt, do something. 
                });
            });

Be wary, in the Subscribe section, your code will run in the background thred, so if you want to do some UI thing, you have to do in the main thread! (above)

Xamarin.Forms Runtime Permission Handling above Android 6.0

After Android 6.0, we have to using Runtime Persmissions. No more enaugth to create an Android.Manifest file. How can we correctly use it?

Step 1: Android.Manifest file

Still we have to create an Android.Manifest file, but a little bit otherwise. We have to use this way: https://developer.android.com/training/permissions/requesting#java. So, if we have used it so far as “uses-permission” before, from now we have to use it as “uses-permisson-sdk-<SDK_Version_Number>”. We can specify the max SDK version too.

<uses-permission-sdk-23 android:maxSdkVersion="28" android:name="android.permission.ACCESS_FINE_LOCATION" />

We have to specify the minimum SDK version of the application (https://developer.android.com/guide/topics/manifest/uses-sdk-element), because, if we don’t do, the minimum SDK version will be the 1.0. We can define the target and the max SDK version as well.

<uses-sdk android:minSdkVersion="23" />

Example Android.Manifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.companyname" android:versionCode="1" android:versionName="1.0.15" android:installLocation="auto">
	<uses-sdk android:minSdkVersion="23" />
	<uses-permission-sdk-23 android:maxSdkVersion="28" android:name="android.permission.ACCESS_FINE_LOCATION" />
	<uses-permission-sdk-23 android:maxSdkVersion="28" android:name="android.permission.ACCESS_COARSE_LOCATION" />
	<uses-permission-sdk-23 android:maxSdkVersion="28" android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission-sdk-23 android:maxSdkVersion="28" android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
	<uses-permission-sdk-23 android:maxSdkVersion="28" android:name="android.permission.ACCESS_MOCK_LOCATION" />
	<uses-permission-sdk-23 android:maxSdkVersion="28" android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission-sdk-23 android:maxSdkVersion="28" android:name="android.permission.ACCESS_WIFI_STATE" />
	<uses-permission-sdk-23 android:maxSdkVersion="28" android:name="android.permission.INTERNET" />
	<uses-permission-sdk-23 android:maxSdkVersion="28" android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
	<uses-permission-sdk-23 android:maxSdkVersion="28" android:name="android.permission.READ_EXTERNAL_STORAGE" />
	<uses-permission-sdk-23 android:maxSdkVersion="28" android:name="android.permission.RECEIVE_SMS" />
	<uses-permission-sdk-23 android:maxSdkVersion="28" android:name="android.permission.SEND_SMS" />
	<uses-permission-sdk-23 android:maxSdkVersion="28" android:name="android.permission.WRITE_SMS" />
	<uses-permission-sdk-23 android:maxSdkVersion="28" android:name="android.permission.READ_SMS" />
	<application android:label="Application" android:icon="@drawable/icon">
		
	</application>
</manifest>

Step 2: Runtime Permission

Open the MainActivity.cs file in the Android Project. We have to create a Request to the user: “hello we have to use the calendar, location, sms, etc. when this app running”. The user will deside that, he/she will use these or not.

                var requiredPermissions = new string[]
                {
                    Manifest.Permission.AccessFineLocation,
                    Manifest.Permission.AccessCoarseLocation,
                    Manifest.Permission.AccessNetworkState,
                    Manifest.Permission.AccessLocationExtraCommands,
                    Manifest.Permission.AccessMockLocation,
                    Manifest.Permission.AccessNetworkState,
                    Manifest.Permission.AccessWifiState,
                    Manifest.Permission.Internet,
                    Manifest.Permission.WriteExternalStorage,
                    Manifest.Permission.ReadExternalStorage,
                    Manifest.Permission.ReceiveSms,
                    Manifest.Permission.SendSms,
                    Manifest.Permission.WriteSms,
                    Manifest.Permission.ReadSms
                };

                RequestPermissions(requiredPermissions, 1);

What have we done? We said that these permissions are neccessary to use at runtime and we created the request to the user. If any of theese permissions is not granted, it will be asked at the start of the application. If all were granted, nothing will happen. Okay, but what is “1” (the second param of the Request)? This is the request code, this can be another too, this is an app-defined code.

If we are corious, that our request is granted or not, we can override the OnRequestPermissionsResult() function.

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }

This function will response our permission request(s) result(granted or not).

Xamarin.Forms save and open PDF file

This project you can find at: https://github.com/officialdoniald/Xamarin.Forms.Save.Open.PDF.

It could be a real situation that your app need to save or open a pdf file. How can it possible? Throught DependencyService. There are many companies, who deal with generationg PDF Document in Xamarin.Forms. We need to create a PDF Document first and than save it. But if you want to open an exists document, just read this blog post cointinue :D. Companies, whom you can download NuGet Packages to create PDF Document:

First thing we need to do, that create an interface in the StandardLibrary. https://github.com/officialdoniald/Xamarin.Forms.Save.Open.PDF/blob/master/Xamarin.Forms.Save.Open.PDF/Xamarin.Forms.Save.Open.PDF/IPDFSaveAndOpen.cs

using System;
using System.IO;
using System.Threading.Tasks;

namespace Xamarin.Forms.Save.Open.PDF
{
    public interface IPDFSaveAndOpen
    {
        Task SaveAndView(string fileName, String contentType, MemoryStream stream, PDFOpenContext context);
    }

    /// <summary>
    /// Where should the PDF file open. In the app or out of the app.
    /// </summary>
    public enum PDFOpenContext
    {
        InApp,
        ChooseApp
    }
}

Implement it on the various platforms!

Android

https://github.com/officialdoniald/Xamarin.Forms.Save.Open.PDF/blob/master/Xamarin.Forms.Save.Open.PDF/Xamarin.Forms.Save.Open.PDF.Android/PDFSaveAndOpen.cs

using Android;
using Android.Content;
using Android.Content.PM;
using Android.Support.V4.App;
using Android.Support.V4.Content;
using Android.Webkit;
using Java.IO;
using System;
using System.IO;
using System.Threading.Tasks;
using Android.App;
using Xamarin.Forms;
using Xamarin.Forms.Save.Open.PDF;
using Droid.Implementations;

[assembly: Dependency(typeof(PDFSaveAndOpen))]
namespace Droid.Implementations
{
    public class PDFSaveAndOpen : IPDFSaveAndOpen
    {
        public async Task SaveAndView(string fileName, String contentType, MemoryStream stream, PDFOpenContext context)
        {
            string exception = string.Empty;
            string root = null;

            if (ContextCompat.CheckSelfPermission(Forms.Context, Manifest.Permission.WriteExternalStorage) != Permission.Granted)
            {
                ActivityCompat.RequestPermissions((Activity)Forms.Context, new String[] { Manifest.Permission.WriteExternalStorage }, 1);
            }
            
            if (Android.OS.Environment.IsExternalStorageEmulated)
            {
                root = Android.OS.Environment.ExternalStorageDirectory.ToString();
            }
            else
                root = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

            Java.IO.File myDir = new Java.IO.File(root + "/PDFFiles");
            myDir.Mkdir();

            Java.IO.File file = new Java.IO.File(myDir, fileName);

            if (file.Exists()) file.Delete();

            try
            {
                FileOutputStream outs = new FileOutputStream(file);
                outs.Write(stream.ToArray());

                outs.Flush();
                outs.Close();
            }
            catch (Exception e)
            {
                exception = e.ToString();
            }

            if (file.Exists() && contentType != "application/html")
            {
                string extension = MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString());
                string mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
                Intent intent = new Intent(Intent.ActionView);
                intent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.NewTask);
                Android.Net.Uri path = FileProvider.GetUriForFile(Forms.Context, Android.App.Application.Context.PackageName + ".provider", file);
                intent.SetDataAndType(path, mimeType);
                intent.AddFlags(ActivityFlags.GrantReadUriPermission);

                switch (context)
                {
                    case PDFOpenContext.InApp:
                        Forms.Context.StartActivity(intent);
                        break;
                    case PDFOpenContext.ChooseApp:
                        Forms.Context.StartActivity(Intent.CreateChooser(intent, "Choose App"));
                        break;
                    default:
                        break;
                }
            }
        }
    }
}

iOS

We need to create a PreviewController and that we have to show the PDF on this Controller.

https://github.com/officialdoniald/Xamarin.Forms.Save.Open.PDF/blob/master/Xamarin.Forms.Save.Open.PDF/Xamarin.Forms.Save.Open.PDF.iOS/PreviewControllerDS.cs

using Foundation;
using QuickLook;
using System;
using System.IO;

namespace Xamarin.Forms.Save.Open.PDF.iOS
{
    public class PreviewControllerDS : QLPreviewControllerDataSource
    {
        //Document cache
        private QLPreviewItem _item;

        //Setting the document
        public PreviewControllerDS(QLPreviewItem item)
        {
            _item = item;
        }

        //Setting document count to 1
        public override nint PreviewItemCount(QLPreviewController controller)
        {
            return 1;
        }

        //Return the document
        public override IQLPreviewItem GetPreviewItem(QLPreviewController controller, nint index)
        {
            return _item;
        }
    }

    public class QLPreviewItemFileSystem : QLPreviewItem
    {

        string _fileName, _filePath;

        //Setting file name and path
        public QLPreviewItemFileSystem(string fileName, string filePath)
        {
            _fileName = fileName;
            _filePath = filePath;
        }

        //Return file name
        public override string ItemTitle
        {
            get
            {
                return _fileName;
            }
        }

        //Retun file path as NSUrl
        public override NSUrl ItemUrl
        {
            get
            {
                return NSUrl.FromFilename(_filePath);
            }
        }
    }

    public class QLPreviewItemBundle : QLPreviewItem
    {
        string _fileName, _filePath;

        //Setting file name and path
        public QLPreviewItemBundle(string fileName, string filePath)
        {
            _fileName = fileName;
            _filePath = filePath;
        }

        //Return file name
        public override string ItemTitle
        {
            get
            {
                return _fileName;
            }
        }

        //Retun file path as NSUrl
        public override NSUrl ItemUrl
        {
            get
            {
                var documents = NSBundle.MainBundle.BundlePath;
                var lib = Path.Combine(documents, _filePath);
                var url = NSUrl.FromFilename(lib);
                return url;
            }
        }
    }
}

Implementation:

https://github.com/officialdoniald/Xamarin.Forms.Save.Open.PDF/blob/master/Xamarin.Forms.Save.Open.PDF/Xamarin.Forms.Save.Open.PDF.iOS/PDFSaveAndOpen.cs

using System;
using System.Threading.Tasks;
using System.IO;
using Xamarin.Forms;
using UIKit;
using QuickLook;
using Xamarin.Forms.Save.Open.PDF.iOS;

[assembly: Dependency(typeof(PDFSaveAndOpen))]
namespace Xamarin.Forms.Save.Open.PDF.iOS
{
    public class PDFSaveAndOpen : IPDFSaveAndOpen
    {
        //Method to save document as a file and view the saved document
        public async Task SaveAndView(string filename, string contentType, MemoryStream stream, PDFOpenContext context)
        {
            //Get the root path in iOS device.
            string path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
            string filePath = Path.Combine(path, filename);

            //Create a file and write the stream into it.
            FileStream fileStream = File.Open(filePath, FileMode.Create);
            stream.Position = 0;
            stream.CopyTo(fileStream);
            fileStream.Flush();
            fileStream.Close();

            //Invoke the saved document for viewing
            UIViewController currentController = UIApplication.SharedApplication.KeyWindow.RootViewController;
            while (currentController.PresentedViewController != null)
                currentController = currentController.PresentedViewController;
            UIView currentView = currentController.View;

            QLPreviewController qlPreview = new QLPreviewController();
            QLPreviewItem item = new QLPreviewItemBundle(filename, filePath);
            qlPreview.DataSource = new PreviewControllerDS(item);

            currentController.PresentViewController(qlPreview, true, null);
        }
    }
}

Let’s just create a Page and try it: https://github.com/officialdoniald/Xamarin.Forms.Save.Open.PDF/blob/master/Xamarin.Forms.Save.Open.PDF/Xamarin.Forms.Save.Open.PDF/MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Xamarin.Forms.Save.Open.PDF.MainPage">
    <StackLayout>
        <Button HeightRequest="100" Text="Save and open PDF in app" Clicked="Button_Clicked"/>
        <Button HeightRequest="100" Text="Save and open PDF with choose app" Clicked="Button_Clicked_1"/>
    </StackLayout>
</ContentPage>

https://github.com/officialdoniald/Xamarin.Forms.Save.Open.PDF/blob/master/Xamarin.Forms.Save.Open.PDF/Xamarin.Forms.Save.Open.PDF/MainPage.xaml.cs

using System;
using System.IO;

namespace Xamarin.Forms.Save.Open.PDF
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Save and open the PDF file in the app.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void Button_Clicked(object sender, EventArgs e)
        {
            MemoryStream stream = new MemoryStream();

            await Xamarin.Forms.DependencyService.Get<IPDFSaveAndOpen>().SaveAndView(Guid.NewGuid() + ".pdf", "application / pdf", stream, PDFOpenContext.InApp);
        }

        /// <summary>
        /// Save and open the PDF file with "choose app".
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void Button_Clicked_1(object sender, EventArgs e)
        {
            MemoryStream stream = new MemoryStream();

            await Xamarin.Forms.DependencyService.Get<IPDFSaveAndOpen>().SaveAndView(Guid.NewGuid() + ".pdf", "application / pdf", stream, PDFOpenContext.ChooseApp);
        }
    }
}

Why is just a MemoryStream stream = new MemoryStream(); ?

Because I don’t have a pdf in stream. 😀 This is your task to create a stream (upload a pdf on your phone and create a Stream: pdf files are opened in the same method like other file types) and open it with this method.

Xamarin.Forms get user location when it change

This project you can find at: https://github.com/officialdoniald/Xamarin.Forms.DetectUserLocationCange

When you need to update the user’s location when it change you can make it in Xamarin.Forms too via DependencyService or using Xamarin.Essentials and create a Timer and get the actual Location per seconds (don’t do that :D).

Before coding, we have to satisfy the users permissons.

Andorid: (https://github.com/officialdoniald/Xamarin.Forms.DetectUserLocationCange/blob/master/Xamarin.Forms.DetectUserLocationCange/Xamarin.Forms.DetectUserLocationCange.Android/Properties/AndroidManifest.xml)

Turn ACCESS_COARSE_LOCATION on.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.Xamarin.Forms.DetectUserLocationCange" android:installLocation="auto">
	<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="27" />
	<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
	<application android:label="Xamarin.Forms.DetectUserLocationCange.Android"></application>
</manifest>

iOS: (https://github.com/officialdoniald/Xamarin.Forms.DetectUserLocationCange/blob/master/Xamarin.Forms.DetectUserLocationCange/Xamarin.Forms.DetectUserLocationCange.iOS/Info.plist)

    <key>NSLocationAlwaysUsageDescription</key>
    <string>Can we use your location at all times?</string>
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>Can we use your location when your app is being used?</string>
    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
    <string>Can we use your location at all times?</string>

ATTENTION: if you will publish on the App Store, you have to pay attention for the privacy. You have to ask the user every time before you will get her/his location. So best way to do that, create a user’s settings, where the user can turn in or off, that you get her/his location. If turned off, you have to show a popup with some text: Can we use your location? and then subcribe to the event. If turned on, you don’t have to show popup.

We have to create an interface in the StandardLibrary. (https://github.com/officialdoniald/Xamarin.Forms.DetectUserLocationCange/blob/master/Xamarin.Forms.DetectUserLocationCange/Xamarin.Forms.DetectUserLocationCange/Services/ILocationUpdateService.cs)

using System;

namespace Xamarin.Forms.DetectUserLocationCange.Services
{
    public interface ILocationUpdateService
    {
        void GetUserLocation();
        event EventHandler<ILocationEventArgs> LocationChanged;
    }

    public interface ILocationEventArgs
    {
        double Latitude { get; set; }
        double Longitude { get; set; }
    }
}

We have to implement this interface in the platforms.

Android: (https://github.com/officialdoniald/Xamarin.Forms.DetectUserLocationCange/blob/master/Xamarin.Forms.DetectUserLocationCange/Xamarin.Forms.DetectUserLocationCange.Android/Services/LocationUpdateService.cs)

using System;
using Android.Content;
using Android.Locations;
using Android.OS;
using Android.Runtime;
using Xamarin.Forms;
using Xamarin.Forms.DetectUserLocationCange.Droid.Services;
using Xamarin.Forms.DetectUserLocationCange.Services;

[assembly: Dependency(typeof(LocationUpdateService))]
namespace Xamarin.Forms.DetectUserLocationCange.Droid.Services
{
    public class LocationEventArgs : EventArgs, ILocationEventArgs
    {
        public double Latitude { get; set; }
        public double Longitude { get; set; }
    }

    public class LocationUpdateService : Java.Lang.Object, ILocationUpdateService, ILocationListener
    {
        LocationManager locationManager;

        public void GetUserLocation()
        {
#pragma warning disable CS0618 // Type or member is obsolete
            locationManager = (LocationManager)Forms.Context.GetSystemService(Context.LocationService);
#pragma warning restore CS0618 // Type or member is obsolete
            locationManager.RequestLocationUpdates(
                provider: LocationManager.NetworkProvider,
                minTime: 0,//millisec
                minDistance: 0,//metres
                listener: this);
        }

        ~LocationUpdateService()
        {
            locationManager.RemoveUpdates(this);
        }

        public void OnLocationChanged(Location location)
        {
            if (location != null)
            {
                LocationEventArgs args = new LocationEventArgs
                {
                    Latitude = location.Latitude,
                    Longitude = location.Longitude
                };
                LocationChanged(this, args);
            };
        }

        public event EventHandler<ILocationEventArgs> LocationChanged;
        
        event EventHandler<ILocationEventArgs>
            ILocationUpdateService.LocationChanged
        {
            add
            {
                LocationChanged += value;
            }
            remove
            {
                LocationChanged -= value;
            }
        }

        public void OnProviderDisabled(string provider) { }

        public void OnProviderEnabled(string provider) { }

        public void OnStatusChanged(string provider, [GeneratedEnum] Availability status, Bundle extras) { }
    }
}

iOS: (https://github.com/officialdoniald/Xamarin.Forms.DetectUserLocationCange/blob/master/Xamarin.Forms.DetectUserLocationCange/Xamarin.Forms.DetectUserLocationCange.iOS/Services/LocationUpdateService.cs)

using System;
using CoreLocation;
using Xamarin.Forms.DetectUserLocationCange.iOS.Services;
using Xamarin.Forms.DetectUserLocationCange.Services;

[assembly: Xamarin.Forms.Dependency(typeof(LocationUpdateService))]
namespace Xamarin.Forms.DetectUserLocationCange.iOS.Services
{
    public class LocationEventArgs : EventArgs, ILocationEventArgs
    {
        public double Latitude { get; set; }
        public double Longitude { get; set; }
    }

    public class LocationUpdateService : ILocationUpdateService
    {
        CLLocationManager locationManager;

        public event EventHandler<ILocationEventArgs> LocationChanged;

        event EventHandler<ILocationEventArgs> ILocationUpdateService.LocationChanged
        {
            add
            {
                LocationChanged += value;
            }
            remove
            {
                LocationChanged -= value;
            }
        }

        public void GetUserLocation()
        {
            locationManager = new CLLocationManager
            {
                DesiredAccuracy = CLLocation.AccuracyBest,
                DistanceFilter = CLLocationDistance.FilterNone
            };

            locationManager.LocationsUpdated +=
                (object sender, CLLocationsUpdatedEventArgs e) =>
                {
                    var locations = e.Locations;
                    var strLocation =locations[locations.Length - 1].Coordinate.Latitude.ToString();

                    strLocation = strLocation + "," + locations[locations.Length - 1].Coordinate.Longitude.ToString();

                    LocationEventArgs args = new LocationEventArgs();
                    args.Latitude = locations[locations.Length - 1].Coordinate.Latitude;
                    args.Longitude = locations[locations.Length - 1].Coordinate.Longitude;

                    LocationChanged(this, args);
                };

            locationManager.AuthorizationChanged += (object sender, CLAuthorizationChangedEventArgs e) =>
            {
                if (e.Status ==
                    CLAuthorizationStatus.AuthorizedWhenInUse)
                {
                    locationManager.StartUpdatingLocation();
                }
            };

            locationManager.RequestWhenInUseAuthorization();
        }

        ~LocationUpdateService()
        {
            locationManager.StopUpdatingLocation();
        }
    }
}

Use theese implementations via DependencyService: (https://github.com/officialdoniald/Xamarin.Forms.DetectUserLocationCange/blob/master/Xamarin.Forms.DetectUserLocationCange/Xamarin.Forms.DetectUserLocationCange/App.xaml.cs)

using System;
using Xamarin.Forms;
using Xamarin.Forms.DetectUserLocationCange.Services;
using Xamarin.Forms.Xaml;

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Xamarin.Forms.DetectUserLocationCange
{
    public partial class App : Application
    {
        public static ILocationUpdateService LocationUpdateService;

        public App()
        {
            InitializeComponent();

            MainPage = new MainPage();

            LocationUpdateService.LocationChanged += LocationUpdateService_LocationChanged; ;
        }

        private void LocationUpdateService_LocationChanged(object sender, ILocationEventArgs e)
        {
            //Here you can get the user's location from "e" -> new Location(e.Latitude, e.Longitude);
            //new Location is from Xamarin.Essentials Location object.
        }

        protected override void OnStart()
        {
            // Handle when your app starts
        }

        protected override void OnSleep()
        {
            // Handle when your app sleeps
        }

        protected override void OnResume()
        {
            // Handle when your app resumes
        }
    }
}

Xamarin.Forms change ListView SelectedItemColor

This project you can find at: https://github.com/officialdoniald/Xamarin.Forms.ChangeListviewSelectedColor

We have to create a Custom Renderer for a ViewCell. So let’s create a class in the .NET Standard,/PCL Project:

https://github.com/officialdoniald/Xamarin.Forms.ChangeListviewSelectedColor/tree/master/Xamarin.Forms.ChangeLvSelectedColor/Xamarin.Forms.ChangeLvSelectedColor/CustomControl/ListViewCustomViewCell.cs/

We created a property for the SelectedItem’s color. Don’t forget: this enum is a cutom enum, we have to create it:

https://github.com/officialdoniald/Xamarin.Forms.ChangeListviewSelectedColor/blob/master/Xamarin.Forms.ChangeLvSelectedColor/Xamarin.Forms.ChangeLvSelectedColor/Helper/Enums.cs

Let’s create the platform specific code.

Android: we have to change the Android’s Theme at runtime via EventHandler.

We have to create a ViewCellRenderer class ( https://github.com/officialdoniald/Xamarin.Forms.ChangeListviewSelectedColor/blob/master/Xamarin.Forms.ChangeLvSelectedColor/Xamarin.Forms.ChangeLvSelectedColor.Android/CustomRenderers/CustomViewCellRenderer.cs ):

using System.ComponentModel;
using Android.Content;
using Android.Views;
using Xamarin.Forms;
using Xamarin.Forms.ChangeLvSelectedColor.CustomControl.ListView;
using Xamarin.Forms.ChangeLvSelectedColor.Droid.CustomRenderers;
using Xamarin.Forms.ChangeLvSelectedColor.Helper;
using Xamarin.Forms.Platform.Android;
using static Xamarin.Forms.ChangeLvSelectedColor.Droid.MainActivity;

[assembly: ExportRenderer(typeof(CustomViewCell), typeof(CustomViewCellRenderer))]
namespace Xamarin.Forms.ChangeLvSelectedColor.Droid.CustomRenderers
{
    public class CustomViewCellRenderer : ViewCellRenderer
    {
        protected override global::Android.Views.View GetCellCore(Cell item, global::Android.Views.View convertView, ViewGroup parent, Context context)
        {
            var _cellCore = base.GetCellCore(item, convertView, parent, context);

            return _cellCore;
        }

        protected override void OnCellPropertyChanged(object sender, PropertyChangedEventArgs args)
        {
            base.OnCellPropertyChanged(sender, args);

            var extendedViewCell = (CustomViewCell)sender;

            if (extendedViewCell.SelectedCellColor == ListViewSelectedBackGroundColor.CianBlue)
            {
                AndroidEvents.OnAndroidThemeChangeNeeded_Event(null, Xamarin.Forms.ChangeLvSelectedColor.Droid.Resource.Style.MainCianBlueTheme);
            }
            else if (extendedViewCell.SelectedCellColor == ListViewSelectedBackGroundColor.Normal)
            {
                AndroidEvents.OnAndroidThemeChangeNeeded_Event(null, Xamarin.Forms.ChangeLvSelectedColor.Droid.Resource.Style.MainTheme);
            }
            else if (extendedViewCell.SelectedCellColor == ListViewSelectedBackGroundColor.Transparent)
            {
                AndroidEvents.OnAndroidThemeChangeNeeded_Event(null, Xamarin.Forms.ChangeLvSelectedColor.Droid.Resource.Style.MainTransparentTheme);
            }
        }
    }
}

AndroidEvents is a static class, and in this class we have to create an event ( https://github.com/officialdoniald/Xamarin.Forms.ChangeListviewSelectedColor/blob/master/Xamarin.Forms.ChangeLvSelectedColor/Xamarin.Forms.ChangeLvSelectedColor.Android/MainActivity.cs ):

public class AndroidEvents
        {
            public static event EventHandler OnAndroidThemeChangeNeeded;

            public static void OnAndroidThemeChangeNeeded_Event(object sender, int id)
            {
                OnAndroidThemeChangeNeeded?.Invoke(sender, id);
            }
        }

This event invoke the Theme Changing at runtime. We have to call it, when the user interact with the ListView(ViewCell). We have to subscribe on this event in the MainActivity:

protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            AndroidEvents.OnAndroidThemeChangeNeeded += AndroidEvents_OnAndroidThemeChangeNeeded; ;
            LoadApplication(new App());
        }

        private void AndroidEvents_OnAndroidThemeChangeNeeded(object sender, int themeid)
        {
            RunOnUiThread(() => {
                SetTheme(themeid);
            });
        }

Finally, we have to create the Theme Shema in the style.xml (MainCianBlueTheme and MainTransparentTheme):

<?xml version="1.0" encoding="utf-8" ?>
<resources>

  <style name="MainTheme" parent="MainTheme.Base">
  </style>
  <style name="MainCianBlueTheme" parent="MainCianBlueTheme.Base">
  </style>
  <style name="MainTransparentTheme" parent="MainTransparentTheme.Base">
  </style>
  <!-- Base theme applied no matter what API -->
  <style name="MainTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
    <!--If you are using revision 22.1 please use just windowNoTitle. Without android:-->
    <item name="windowNoTitle">true</item>
    <!--We will be using the toolbar so no need to show ActionBar-->
    <item name="windowActionBar">false</item>
    <!-- Set theme colors from http://www.google.com/design/spec/style/color.html#color-color-palette -->
    <!-- colorPrimary is used for the default action bar background -->
    <item name="colorPrimary">#2196F3</item>
    <!-- colorPrimaryDark is used for the status bar -->
    <item name="colorPrimaryDark">#1976D2</item>
    <!-- colorAccent is used as the default value for colorControlActivated
         which is used to tint widgets -->
    <item name="colorAccent">#FF4081</item>
    <!-- You can also set colorControlNormal, colorControlActivated
         colorControlHighlight and colorSwitchThumbNormal. -->
    <item name="windowActionModeOverlay">true</item>

    <item name="android:datePickerDialogTheme">@style/AppCompatDialogStyle</item>
  </style>

  <style name="MainCianBlueTheme.Base" parent="MainTheme">
    <item name="android:colorPressedHighlight">@color/anlistviewSelection</item>
    <item name="android:colorLongPressedHighlight">@color/anlistviewSelection</item>
    <item name="android:colorFocusedHighlight">@color/anlistviewSelection</item>
    <item name="android:colorActivatedHighlight">@color/anlistviewSelection</item>
    <item name="android:activatedBackgroundIndicator">@color/anlistviewSelection</item>
  </style>

  <style name="MainTransparentTheme.Base" parent="MainTheme">
    <item name="android:colorPressedHighlight">@color/transparentlistviewSelection</item>
    <item name="android:colorLongPressedHighlight">@color/transparentlistviewSelection</item>
    <item name="android:colorFocusedHighlight">@color/transparentlistviewSelection</item>
    <item name="android:colorActivatedHighlight">@color/transparentlistviewSelection</item>
    <item name="android:activatedBackgroundIndicator">@color/transparentlistviewSelection</item>
  </style>

  <style name="AppCompatDialogStyle" parent="Theme.AppCompat.Light.Dialog">
    <item name="colorAccent">#FF4081</item>
  </style>
</resources>

and their @color variablesin the colors.xml (listviewSelection, anlistviewSelection, transparentlistviewSelection):

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <color name="launcher_background">#FFFFFF</color>
  <color name="colorPrimary">#3F51B5</color>
  <color name="colorPrimaryDark">#303F9F</color>
  <color name="colorAccent">#FF4081</color>
  <color name="listviewSelection">#f17a0a</color>
  <color name="anlistviewSelection">#40E0D0</color>
  <color name="transparentlistviewSelection">#00000000</color>
</resources>

iOS: we just have to create a platform specific version of the CustomViewCell class, and gain two properties a color (https://github.com/officialdoniald/Xamarin.Forms.ChangeListviewSelectedColor/blob/master/Xamarin.Forms.ChangeLvSelectedColor/Xamarin.Forms.ChangeLvSelectedColor.iOS/CustomRenderer/CustomViewCellRenderer.cs):

using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.ChangeLvSelectedColor.CustomControl.ListView;
using Xamarin.Forms.ChangeLvSelectedColor.iOS.CustomRenderer;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(CustomViewCell), typeof(CustomViewCellRenderer))]
namespace Xamarin.Forms.ChangeLvSelectedColor.iOS.CustomRenderer
{
    public class CustomViewCellRenderer : ViewCellRenderer
    {
        public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
        {
            var customViewCell = (CustomViewCell)item;

            var cell = base.GetCell(item, reusableCell, tv);

            cell.SelectedBackgroundView = new UIView { BackgroundColor = GetColor(customViewCell.SelectedCellColor) };

            //You can define a new property in the CustomViewCell class, and you can use it here:
            //This code is set the unselected background color of the ViewCell:
            //cell.BackgroundColor = GetColor(customViewCell.UnSelectedCellColor);

            return cell;
        }

        public UIColor GetColor(Helper.ListViewSelectedBackGroundColor color)
        {
            switch (color)
            {
                case Helper.ListViewSelectedBackGroundColor.CianBlue:
                    return Color.FromHex("#40E0D0").ToUIColor();
                default:
                    return Color.Transparent.ToUIColor();
            }
        }
    }
}

Xamarin.Forms – Android – change the Theme at runtime

This project you can find at: https://github.com/officialdoniald/Xamarin.Forms—Change-Android-Theme-at-runtime

In the .NET Standard/PCL Project we can’t say that, please change the Theme right now, because wehave to implement some platform specific implementation in the Android project.

How can the two project communicate? Now, we will see another way, unlike the previous ones, we will use events, not dependency service.

So, let’s create our event class in the .NET Standard/PCL Project (https://github.com/officialdoniald/Xamarin.Forms—Change-Android-Theme-at-runtime/blob/master/XamarinFormsChangeAndoirdThemeRuntime/XamarinFormsChangeAndoirdThemeRuntime/GlobalEvents.cs):

using System;
namespace XamarinFormsChangeAndoirdThemeRuntime
{
public class GlobalEvents
{
public static event EventHandler OnAndroidThemeChangeNeeded;
    
public static void OnAndroidThemeChangeNeeded_Event(object sender, int themeid)
    {
        OnAndroidThemeChangeNeeded?.Invoke(sender, themeid);
    }
   }
}

In the Android site, we will use the SetTheme(themeid) function, that set the Theme on the Application. We have to subscribe int the event int the MainActivity, and we have to call the SetTheme(themeid) method, in the main thread (https://github.com/officialdoniald/Xamarin.Forms—Change-Android-Theme-at-runtime/blob/master/XamarinFormsChangeAndoirdThemeRuntime/XamarinFormsChangeAndoirdThemeRuntime.Android/MainActivity.cs):

using Android.App;
using Android.Content.PM;
using Android.OS;

namespace XamarinFormsChangeAndoirdThemeRuntime.Droid
{
    [Activity(Label = "XamarinFormsChangeAndoirdThemeRuntime", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

            GlobalVariables.MainThemeResourceID = Resource.Style.MainTheme;
            GlobalVariables.MainTransparentThemeResourceID = Resource.Style.MainTransparentTheme_Base;
            GlobalEvents.OnAndroidThemeChangeNeeded += GlobalEvents_OnAndroidThemeChangeNeeded; ;

            LoadApplication(new App());
        }

        private void GlobalEvents_OnAndroidThemeChangeNeeded(object sender, int themeid)
        {
            RunOnUiThread(() => {
                SetTheme(themeid);
            });
        }
    }
}

As you see, we gain the Theme’s Resource ID to the SetTheme(themeid) function. So we have to store theese Themes, in a global variable. Let’s create a class in the .NET Standard/PCL Project, and define static int variables as the Theme’s IDs (https://github.com/officialdoniald/Xamarin.Forms—Change-Android-Theme-at-runtime/blob/master/XamarinFormsChangeAndoirdThemeRuntime/XamarinFormsChangeAndoirdThemeRuntime/GlobalVariables.cs):

namespace XamarinFormsChangeAndoirdThemeRuntime
{
    public class GlobalVariables
    {
        public static int MainThemeResourceID { get; set; }

        public static int MainTransparentThemeResourceID { get; set; }
    }
}

And now we have to just try theese codes. Let’s create a Page with two Buttons, and subscribe on their Click events:

https://github.com/officialdoniald/Xamarin.Forms—Change-Android-Theme-at-runtime/blob/master/XamarinFormsChangeAndoirdThemeRuntime/XamarinFormsChangeAndoirdThemeRuntime/MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamarinFormsChangeAndoirdThemeRuntime.MainPage">
    <StackLayout>
        <Button x:Name="mainThemeButton" 
                Clicked="MainThemeButton_Clicked" 
                Text="MainTheme"/>
        <Button x:Name="mainTransparentButton" 
                Clicked="MainTransparentButton_Clicked" 
                Text="TransparentTheme"/>
    </StackLayout>
</ContentPage>

https://github.com/officialdoniald/Xamarin.Forms—Change-Android-Theme-at-runtime/blob/master/XamarinFormsChangeAndoirdThemeRuntime/XamarinFormsChangeAndoirdThemeRuntime/MainPage.xaml.cs

using System;
using Xamarin.Forms;

namespace XamarinFormsChangeAndoirdThemeRuntime
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void MainThemeButton_Clicked(object sender, EventArgs e)
        {
            Device.BeginInvokeOnMainThread(() => 
            {
                GlobalEvents.OnAndroidThemeChangeNeeded_Event(null, GlobalVariables.MainThemeResourceID);
            });
        }

        private void MainTransparentButton_Clicked(object sender, EventArgs e)
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                GlobalEvents.OnAndroidThemeChangeNeeded_Event(null, GlobalVariables.MainTransparentThemeResourceID);
            });
        }
    }
}

I defined two Themes, theese Themes change a listview’s color. You can find they at: https://github.com/officialdoniald/Xamarin.Forms—Change-Android-Theme-at-runtime/tree/master/XamarinFormsChangeAndoirdThemeRuntime/XamarinFormsChangeAndoirdThemeRuntime.Android/Resources/values

Their was defined at the color.xml(link above).

Of cource you don’t have to do it this way, that was an extreme way, you can implement in the Android code, but if you want to use it from the .NET Standard/PCL Project, you have to write an event, or write through dependency service.

Fun fact: this GitHub project’s name is too large, so you can’t Debug it without renaming.

Xamarin.Android.SerialPort

This project you can find at: https://github.com/officialdoniald/Xamarin.Android.SerialPort.

Serial Port wrapper for Xamarin.Android.

If you want to change some feature of the Serial Port, just clone this repo and change it. In the SerialPort project you have to go to the SerialPortWrapper folder and the SerialPort.cs.

After the update you have to Rebuild, and if you want to use the recently updatet SerialPort class, you have to add the dll (from bin/Release or Debug) to the target Project.

If you want to create your own SerialPort wrapper just go to https://github.com/chzhong/serial-android and open this project to Android Studio. Create .aar file with grandle: go to grandle tab on the right of the Android Studio (:libserial/build/), select the package and search for clean (double click) and search for assembleRelease (double click). The .aar file youe can find at the project folder and then the \libserial\build\outputs\aar. Then create a Bining Xamarin Android Library, with the grandle (what you gave in the grandle) minimum adroid version, and add it to the Jar folder. (https://developer.xamarin.com/guides/android/advanced_topics/binding-a-java-library/binding-a-jar/ or https://developer.xamarin.com/guides/android/advanced_topics/binding-a-java-library/binding-an-aar/) Rebuild. And that’s it. Watch out for the namespaces. The namespace will be the java’s namespace with upper case at the start.