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 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 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 – 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.Forms – change cursor’s color – Android

This code (https://github.com/officialdoniald/Xamarin.Forms.CustomControls/blob/master/XamarinForms.CustomControls/XamarinForms.CustomControls.Android/CustomRenderer/CustomEntryRenderer.cs ) :

IntPtr IntPtrtextViewClass = JNIEnv.FindClass(typeof(TextView));

IntPtr mCursorDrawableResProperty = JNIEnv.GetFieldID(IntPtrtextViewClass, “mCursorDrawableRes”, “I”);

will change the cursor’s color. If you want to really change the color of the cursor, you have to create in the Resources/Drawable folder a my_cursor.xml file: https://github.com/officialdoniald/Xamarin.Forms.CustomControls/blob/master/XamarinForms.CustomControls/XamarinForms.CustomControls.Android/Resources/drawable/my_cursor.xml

<shape xmlns:android=”http://schemas.android.com/apk/res/android” android:shape=”rectangle”>
<solid android:color=”@color/colorCursors”></solid>
<size android:width=”2dp” />
</shape>

@color/colorCursors is a color from the Resources/values/color.xml. If you don’t have this file, just create it: https://github.com/officialdoniald/Xamarin.Forms.CustomControls/blob/master/XamarinForms.CustomControls/XamarinForms.CustomControls.Android/Resources/values/colors.xml

<?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=”colorCursors”>#000000</color>
</resources>

<color name=”colorCursors”>#000000</color> here can you gain the color of the cursor.

Xamarin.Forms – NavigationPage Horizontal Alignment Center Title on Android

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

We have to create some platforms specific code (custom NavigationPage Renderer), because in the base Xamarin.Forms code, we can’t find this implementation. So first step, we have to create a class in the .NET Standard/PCL project: CustomNavigationPage ( https://github.com/officialdoniald/Xamarin.Forms.CustomControls/blob/master/XamarinForms.CustomControls/XamarinForms.CustomControls/NavigationPage/CustomNavigationPage.cs ).

using Xamarin.Forms;
using XamarinForms.CustomControls.Enums;
namespace XamarinForms.CustomControls.NavigationPage
{
public class CustomNavigationPage : Xamarin.Forms.NavigationPage
{
public CustomNavigationPage() : base() { }
public CustomNavigationPage(Xamarin.Forms.Page page) : base(page) { }
public static readonly BindableProperty TitleFontFamilyProperty =
BindableProperty.Create(“TitleFontFamily”, typeof(string), typeof(Xamarin.Forms.NavigationPage), default(string));
public static readonly BindableProperty TitleFontSizeProperty =
BindableProperty.Create(“TitleFontSize”, typeof(float), typeof(Xamarin.Forms.NavigationPage), default(float));
public static readonly BindableProperty TitleHorizontalAlignmentProperty =
BindableProperty.Create(“TitleHorizontalAlignment”, typeof(HorizontalAlignment), typeof(Xamarin.Forms.NavigationPage), default(HorizontalAlignment));
public static readonly BindableProperty TitleFontAttributesProperty =
BindableProperty.Create(“TitleFontAttributes”, typeof(Enums.FontAttributes), typeof(Xamarin.Forms.NavigationPage), default(Enums.FontAttributes));
/// <summary>
/// Set the Horizontal Alignment of the Title.
/// </summary>
public HorizontalAlignment TitleHorizontalAlignment
{
get { return (HorizontalAlignment)GetValue(TitleHorizontalAlignmentProperty); }
set { SetValue(TitleHorizontalAlignmentProperty, value); }
}
/// <summary>
/// Set the Font Size of the Title.
/// </summary>
public float TitleFontSize
{
get { return (float)GetValue(TitleFontSizeProperty); }
set { SetValue(TitleFontSizeProperty, value); }
}
/// <summary>
/// Set the Font Family of the Title.
/// </summary>
public string TitleFontFamily
{
get { return (string)GetValue(TitleFontFamilyProperty); }
set { SetValue(TitleFontFamilyProperty, value); }
}
/// <summary>
/// Set the Font Attributes of the Title.
/// </summary>
public Enums.FontAttributes TitleFontAttributes
{
get { return (Enums.FontAttributes)GetValue(TitleFontAttributesProperty); }
set { SetValue(TitleFontAttributesProperty, value); }
}
}
}

Enums.FontAttributes is a custom enum, we can skip it, but here is the code: https://github.com/officialdoniald/Xamarin.Forms.CustomControls/blob/master/XamarinForms.CustomControls/XamarinForms.CustomControls/Enums/HelperEnums.cs

Second step: we have to implement in the Android code. Create a new class in the Droid Porject, call it: CustomNavigationPageRenderer ( https://github.com/officialdoniald/Xamarin.Forms.CustomControls/blob/master/XamarinForms.CustomControls/XamarinForms.CustomControls.Android/CustomRenderer/CustonNavigationPageRenderer.cs).

Implement the base class’s contructors, because it may not working:

public CustonNavigationPageRenderer() : base() { }
public CustonNavigationPageRenderer(Context context) : base(context) {}
public CustonNavigationPageRenderer(IntPtr a, JniHandleOwnership b) : base() { }

We have to override to base function:

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var page = (CustomNavigationPage)sender;
FontFamily = page.TitleFontFamily;
TitleHorizontalAlignment = page.TitleHorizontalAlignment;
TitleFontAttributes = page.TitleFontAttributes;
TitleFontSize = page.TitleFontSize;
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
SetTitle();
//if (view is LoginPage || view is PasswordResetPage || view is RegisterPage || view is SMSPage)
//{
// SetIcon();
//}
}

And create the SetTitle function:

private void SetTitle()
{
toolbar = GetToolbar();
if (toolbar != null)
{
for (var i = 0; i < toolbar.ChildCount; i++)
{
var Title = toolbar.GetChildAt(i);
if (Title is TextView)
{
var title = toolbar.GetChildAt(i) as TextView;
if (TitleHorizontalAlignment == HorizontalAlignment.Center)
{
//Title in center
float toolbarCenter = toolbar.Width / 2;
float titleCenter = title.Width / 2;
title.SetX(toolbarCenter – titleCenter);
}
//Title Font “HelveticaNeueLTStd”
if (!string.IsNullOrEmpty(FontFamily))
{
var typeface = Typeface.Create(FontFamily, ConvertFontAttributesToTypefaceStyle(TitleFontAttributes));
title.Typeface = typeface;
}
if (TitleFontSize != 0)
{
title.TextSize = TitleFontSize;
}
}
}
}
}

The default position of the Title on Android is Left, so we have to check just that, if the TitleHorizontalAlignment Property is Center. Or if we don’t create an enum, we just have to write without the if condition:

float toolbarCenter = toolbar.Width / 2;

float titleCenter = title.Width / 2;

title.SetX(toolbarCenter – titleCenter);

You can find the iOS implementation too: https://github.com/officialdoniald/Xamarin.Forms.CustomControls/blob/master/XamarinForms.CustomControls/XamarinForms.CustomControls.iOS/CustomRenderer/CustomNavigationPageRenderer.cs