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 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();
            }
        }
    }
}