3using _Microsoft.Android.Resource.Designer;
7using Android.Content.PM;
13using Android.Views.InputMethods;
14using AndroidX.Biometric;
15using AndroidX.Core.App;
16using AndroidX.Core.Graphics;
17using AndroidX.Core.View;
18using AndroidX.Fragment.App;
19using AndroidX.Lifecycle;
20using CommunityToolkit.Mvvm.Messaging;
21using Microsoft.Maui.Devices;
24using Java.Util.Concurrent;
26using Plugin.Firebase.CloudMessaging;
29using Application = Android.App.Application;
30using FileProvider = AndroidX.Core.Content.FileProvider;
31using Resource = Android.Resource;
32using System.Globalization;
41 private bool isDisposed;
48 this.InitializeKeyboard();
57 GC.SuppressFinalize(
this);
63 protected virtual void Dispose(
bool Disposing)
70 protectionTimer?.Dispose();
71 protectionTimer =
null;
72 this.initializeKeyboardHandler?.RemoveCallbacksAndMessages(
null);
73 this.initializeKeyboardHandler =
null;
74 this.DetachInsetsListener();
78 this.isDisposed =
true;
81 private static bool screenProtected =
true;
82 private static Timer? protectionTimer =
null;
94 get => screenProtected;
99 protectionTimer?.Dispose();
100 protectionTimer =
null;
102 if (screenProtected != value)
104 this.SetScreenSecurityProtection(value);
105 screenProtected = value;
115 private void SetScreenSecurityProtection(
bool Enabled)
117 MainThread.BeginInvokeOnMainThread(() =>
121 Activity? Activity = Platform.CurrentActivity;
123 if (Activity is not
null)
125 if (Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu)
127#pragma warning disable CA1416
128 Activity.SetRecentsScreenshotEnabled(!Enabled);
129#pragma warning restore CA1416
134 Activity.Window?.SetFlags(WindowManagerFlags.Secure, WindowManagerFlags.Secure);
135 protectionTimer =
new Timer(this.ProtectionTimerElapsed,
null,
139 Activity.Window?.ClearFlags(WindowManagerFlags.Secure);
149 private void ProtectionTimerElapsed(
object? P)
162 string? DeviceId = SecureStorage.GetAsync(
"DeviceIdentifier").Result;
164 if (!
string.IsNullOrEmpty(DeviceId))
170 string? AndroidId = Android.Provider.Settings.Secure.GetString(
171 Android.App.Application.Context.ContentResolver,
172 Android.Provider.Settings.Secure.AndroidId);
175 if (
string.IsNullOrEmpty(AndroidId) || AndroidId ==
"9774d56d682e549c")
177 AndroidId = Guid.NewGuid().ToString();
180 SecureStorage.SetAsync(
"DeviceIdentifier", AndroidId).Wait();
188 App.SendAlertAsync($
"Unable to get or store device ID: {ex.Message}",
"text/plain").Wait();
193 System.Environment.Exit(0);
206 Activity? Activity = Platform.CurrentActivity;
207 Activity?.FinishAffinity();
209 Java.Lang.JavaSystem.Exit(0);
211 return Task.CompletedTask;
221 public void ShareImage(
byte[] PngFile,
string Message,
string Title,
string FileName)
223 Context Context = Android.App.Application.Context;
224 Java.IO.File? ExternalFilesDir = Context.GetExternalFilesDir(
"");
226 if (ExternalFilesDir is
null)
229 if (!Directory.Exists(ExternalFilesDir.Path))
230 Directory.CreateDirectory(ExternalFilesDir.Path);
232 Java.IO.File FileDir =
new(ExternalFilesDir.AbsolutePath + (Java.IO.File.Separator + FileName));
234 File.WriteAllBytes(FileDir.Path, PngFile);
236 Intent Intent =
new(Intent.ActionSend);
237 Intent.PutExtra(Intent.ExtraText, Message);
240 Intent.AddFlags(ActivityFlags.GrantReadUriPermission);
241 Intent.AddFlags(ActivityFlags.GrantWriteUriPermission);
242 Intent.PutExtra(Intent.ExtraStream, FileProvider.GetUriForFile(Context,
"com.tag.IdApp.fileprovider", FileDir));
244 Intent? MyIntent = Intent.CreateChooser(Intent, Title);
246 if (MyIntent is not
null)
248 MyIntent.AddFlags(ActivityFlags.NewTask);
249 Context.StartActivity(MyIntent);
438 if (!OperatingSystem.IsAndroidVersionAtLeast(23))
442 Context Context = Android.App.Application.Context;
445 if (OperatingSystem.IsAndroidVersionAtLeast(28))
447 if (Context.CheckCallingOrSelfPermission(Manifest.Permission.UseBiometric) != Permission.Granted)
452 if (Context.CheckCallingOrSelfPermission(Manifest.Permission.UseFingerprint) != Permission.Granted)
456 BiometricManager Manager = BiometricManager.From(Context);
457 int Level = BiometricManager.Authenticators.BiometricWeak;
459 return Manager.CanAuthenticate(Level) == BiometricManager.BiometricSuccess;
508 if (OperatingSystem.IsAndroidVersionAtLeast(23))
511 Context Context = Android.App.Application.Context;
513 if (OperatingSystem.IsAndroidVersionAtLeast(28))
516 if (Context.CheckCallingOrSelfPermission(Manifest.Permission.UseBiometric) != Permission.Granted)
524 if (Context.CheckCallingOrSelfPermission(Manifest.Permission.UseFingerprint) != Permission.Granted)
530 BiometricManager Manager = BiometricManager.From(Context);
531 const int Level = BiometricManager.Authenticators.BiometricWeak;
532 return Manager.CanAuthenticate(Level) == BiometricManager.BiometricSuccess
533 ? BiometricMethod.Unknown
547 CancellationToken? CancellationToken)
552 if (
string.IsNullOrWhiteSpace(Title))
553 throw new ArgumentException(
"Title cannot be empty.", nameof(Title));
555 if (Platform.CurrentActivity is not FragmentActivity Activity)
560 BiometricPrompt.PromptInfo.Builder Builder =
new();
562 Builder.SetAllowedAuthenticators(BiometricManager.Authenticators.BiometricWeak);
564 Builder.SetConfirmationRequired(
false);
565 Builder.SetTitle(Title);
566 Builder.SetDescription(Description);
567 Builder.SetNegativeButtonText(Cancel);
569 if (!
string.IsNullOrEmpty(Subtitle))
570 Builder.SetSubtitle(Subtitle);
572 BiometricPrompt.PromptInfo Prompt = Builder.Build();
573 IExecutorService? Executor = Executors.NewSingleThreadExecutor();
574 CallbackHandler Handler =
new();
575 CancellationTokenRegistration? Registration =
null;
577 BiometricPrompt Dialog =
new(Activity, Executor, Handler);
580 Registration = CancellationToken?.Register(Dialog.CancelAuthentication);
581 Dialog.Authenticate(Prompt);
583 return await Handler.Result;
587 if (Registration.HasValue)
588 Registration.Value.Dispose();
594 Java.Lang.Class Class = Java.Lang.Class.FromType(Dialog.GetType());
595 Java.Lang.Reflect.Field[] Fields = Class.GetDeclaredFields();
596 Java.Lang.Reflect.Field? LifecycleObserver = Fields?.FirstOrDefault(f => f.Name ==
"mLifecycleObserver");
598 if (LifecycleObserver is not
null)
600 LifecycleObserver.Accessible =
true;
601 ILifecycleObserver? LastLifecycleObserver = LifecycleObserver.Get(Dialog).JavaCast<ILifecycleObserver>();
602 Lifecycle? Lifecycle = Activity.Lifecycle;
604 if (LastLifecycleObserver is not
null && Lifecycle is not
null)
605 Lifecycle.RemoveObserver(LastLifecycleObserver);
618 private class CallbackHandler : BiometricPrompt.AuthenticationCallback, IDialogInterfaceOnClickListener
620 private readonly TaskCompletionSource<bool> result =
new();
622 public Task<bool> Result => this.result.Task;
624 public override void OnAuthenticationSucceeded(BiometricPrompt.AuthenticationResult Result)
626 base.OnAuthenticationSucceeded(Result);
627 this.result.TrySetResult(
true);
630 public override void OnAuthenticationError(
int ErrorCode, Java.Lang.ICharSequence ErrorString)
632 base.OnAuthenticationError(ErrorCode, ErrorString);
633 this.result.TrySetResult(
false);
636 public override void OnAuthenticationFailed()
638 base.OnAuthenticationFailed();
639 this.result.TrySetResult(
false);
642 public void OnClick(IDialogInterface? Dialog,
int Which)
644 this.result.TrySetResult(
false);
654 string Token =
string.Empty;
658 await CrossFirebaseCloudMessaging.Current.CheckIfValidAsync();
659 Token = await CrossFirebaseCloudMessaging.Current.GetTokenAsync();
670 Service = PushMessagingService.Firebase
690 private Activity? activity;
691 private Android.Views.View? rootView;
692 private double lastKeyboardHeight = 0;
693 private Handler? initializeKeyboardHandler;
694 private KeyboardInsetsListener? windowInsetsListener;
699 if (this.activity is
null || this.rootView is
null)
701 InputMethodManager? InputMethodManager = this.activity.GetSystemService(Context.InputMethodService) as InputMethodManager;
702 InputMethodManager?.HideSoftInputFromWindow(this.rootView.WindowToken, HideSoftInputFlags.None);
703 this.activity.Window?.DecorView.ClearFocus();
706 private void InitializeKeyboard()
710 this.initializeKeyboardHandler =
new Handler(Looper.MainLooper!);
711 this.CheckRootView();
719 private void CheckRootView()
721 this.activity = Platform.CurrentActivity;
723 Android.Views.View? currentRoot = this.activity?.Window?.DecorView?.RootView;
724 if (currentRoot is
null)
726 this.initializeKeyboardHandler?.PostDelayed(this.CheckRootView, 100);
730 if (ReferenceEquals(this.rootView, currentRoot) && this.windowInsetsListener is not
null)
732 ViewCompat.RequestApplyInsets(this.rootView);
736 this.DetachInsetsListener();
738 this.rootView = currentRoot;
739 this.windowInsetsListener =
new KeyboardInsetsListener(
this);
740 ViewCompat.SetOnApplyWindowInsetsListener(this.rootView, this.windowInsetsListener);
741 ViewCompat.RequestApplyInsets(this.rootView);
744 private void DetachInsetsListener()
746 if (this.rootView is not
null)
748 ViewCompat.SetOnApplyWindowInsetsListener(this.rootView,
null);
751 this.windowInsetsListener?.Dispose();
752 this.windowInsetsListener =
null;
755 internal void ProcessWindowInsets(WindowInsetsCompat insets)
759 bool isImeVisible = insets.IsVisible(WindowInsetsCompat.Type.Ime());
760 AndroidX.Core.Graphics.Insets imeInsets = insets.GetInsets(WindowInsetsCompat.Type.Ime());
761 AndroidX.Core.Graphics.Insets systemInsets = insets.GetInsets(WindowInsetsCompat.Type.SystemBars());
762 int keyboardHeightPixels = isImeVisible ? Math.Max(0, imeInsets.Bottom - systemInsets.Bottom) : 0;
763 float keyboardHeightDip = ConvertToDip(keyboardHeightPixels);
765 if (isImeVisible && keyboardHeightDip > 0.5)
767 if (Math.Abs(
this.lastKeyboardHeight - keyboardHeightDip) < 0.5)
770 this.lastKeyboardHeight = keyboardHeightDip;
777 if (Math.Abs(
this.lastKeyboardHeight) < 0.5)
780 this.lastKeyboardHeight = 0;
792 private static float ConvertToDip(
int pixelValue)
794 double density = DeviceDisplay.MainDisplayInfo.Density;
797 return (
float)(pixelValue / density);
800 private sealed
class KeyboardInsetsListener : Java.Lang.Object, IOnApplyWindowInsetsListener
802 private readonly WeakReference<PlatformSpecific> ownerReference;
806 this.ownerReference =
new WeakReference<PlatformSpecific>(owner);
815 public WindowInsetsCompat? OnApplyWindowInsets(Android.Views.View? View, WindowInsetsCompat? Insets)
817 if (Insets is not
null && this.ownerReference.TryGetTarget(out
PlatformSpecific? Owner))
818 Owner.ProcessWindowInsets(Insets);
824 #region Notifications
825 public void ShowMessageNotification(
string Title,
string MessageBody, IDictionary<string, string> Data)
827 Context Context = Application.Context;
828 Intent Intent =
new Intent(Context, typeof(
MainActivity));
829 Intent.AddFlags(ActivityFlags.ClearTop);
830 foreach (
string Key
in Data.Keys)
832 Intent.PutExtra(Key, Data[Key]);
834 PendingIntent? PendingIntent = Android.App.PendingIntent.GetActivity(Context, 100, Intent, PendingIntentFlags.OneShot | PendingIntentFlags.Immutable);
835 int ResIdentifier = Context.Resources?.GetIdentifier(
"app_icon",
"drawable", Context.PackageName) ?? 0;
836 if (ResIdentifier == 0)
842 if (Data.TryGetValue(
"fromJid", out
string? FromJid) && !
string.IsNullOrEmpty(FromJid))
845 Intent.SetAction(Intent.ActionView);
849 .SetSmallIcon(ResIdentifier)
850 .SetContentTitle(Title)
851 .SetContentText(MessageBody)
853 .SetContentIntent(PendingIntent);
855 NotificationManagerCompat NotificationManager = NotificationManagerCompat.From(Context);
856 NotificationManager.Notify(100, Builder.Build());
859 public void ShowIdentitiesNotification(
string Title,
string MessageBody, IDictionary<string, string> Data)
861 Context Context = Application.Context;
862 Intent Intent =
new Intent(Context, typeof(
MainActivity));
864 Intent.AddFlags(ActivityFlags.ClearTop);
865 foreach (
string Key
in Data.Keys)
867 Intent.PutExtra(Key, Data[Key]);
871 string ContentText = MessageBody;
872 if (Data.TryGetValue(
"legalId", out
string? LegalId) && !
string.IsNullOrEmpty(LegalId))
875 Intent.SetAction(Intent.ActionView);
876 ContentText += System.Environment.NewLine + $
"({LegalId})";
879 PendingIntent? PendingIntent = Android.App.PendingIntent.GetActivity(Context, 101, Intent, PendingIntentFlags.OneShot | PendingIntentFlags.Immutable);
881 int ResIdentifier = Context.Resources?.GetIdentifier(
"app_icon",
"drawable", Context.PackageName) ?? 0;
882 if (ResIdentifier == 0)
890 .SetSmallIcon(ResIdentifier)
891 .SetContentTitle(Title)
892 .SetContentText(ContentText)
894 .SetContentIntent(PendingIntent);
896 NotificationManagerCompat NotificationManager = NotificationManagerCompat.From(Context);
897 NotificationManager.Notify(101, Builder.Build());
900 public void ShowPetitionNotification(
string Title,
string MessageBody, IDictionary<string, string> Data)
902 Context Context = Application.Context;
903 Intent Intent =
new Intent(Context, typeof(
MainActivity));
904 Intent.AddFlags(ActivityFlags.ClearTop);
905 foreach (
string Key
in Data.Keys)
907 Intent.PutExtra(Key, Data[Key]);
911 string FromJid = Data.TryGetValue(
"fromJid", out
string? Value) ? Value :
string.Empty;
912 string RosterName = Data.TryGetValue(
"rosterName", out
string? Value1) ? Value1 :
string.Empty;
913 string ContentText = $
"{(string.IsNullOrEmpty(RosterName) ? FromJid : RosterName)}: {MessageBody}";
915 PendingIntent? PendingIntent = Android.App.PendingIntent.GetActivity(Context, 102, Intent, PendingIntentFlags.OneShot | PendingIntentFlags.Immutable);
916 int ResIdentifier = Context.Resources?.GetIdentifier(
"app_icon",
"drawable", Context.PackageName) ?? 0;
917 if (ResIdentifier == 0)
923 .SetSmallIcon(ResIdentifier)
924 .SetContentTitle(Title)
925 .SetContentText(ContentText)
927 .SetContentIntent(PendingIntent);
929 NotificationManagerCompat NotificationManager = NotificationManagerCompat.From(Context);
930 NotificationManager.Notify(102, Builder.Build());
933 public void ShowContractsNotification(
string Title,
string MessageBody, IDictionary<string, string> Data)
935 Context Context = Application.Context;
936 Intent Intent =
new Intent(Context, typeof(
MainActivity));
937 Intent.AddFlags(ActivityFlags.ClearTop);
939 foreach (
string Key
in Data.Keys)
941 Intent.PutExtra(Key, Data[Key]);
944 StringBuilder ContentBuilder =
new StringBuilder();
945 ContentBuilder.Append(MessageBody);
946 if (Data.TryGetValue(
"role", out
string? Role) && !
string.IsNullOrEmpty(Role))
948 ContentBuilder.AppendLine().Append(Role);
950 if (Data.TryGetValue(
"contractId", out
string? ContractId) && !
string.IsNullOrEmpty(ContractId))
952 ContentBuilder.AppendLine().Append(CultureInfo.InvariantCulture, $
"({ContractId})");
954 if (Data.TryGetValue(
"legalId", out
string? LegalId) && !
string.IsNullOrEmpty(LegalId))
956 ContentBuilder.AppendLine().Append(CultureInfo.InvariantCulture, $
"({LegalId})");
959 PendingIntent? PendingIntent = Android.App.PendingIntent.GetActivity(Context, 103, Intent, PendingIntentFlags.OneShot | PendingIntentFlags.Immutable);
960 int ResIdentifier = Context.Resources?.GetIdentifier(
"app_icon",
"drawable", Context.PackageName) ?? 0;
961 if (ResIdentifier == 0)
967 .SetSmallIcon(ResIdentifier)
968 .SetContentTitle(Title)
969 .SetContentText(ContentBuilder.ToString())
971 .SetContentIntent(PendingIntent);
973 NotificationManagerCompat NotificationManager = NotificationManagerCompat.From(Context);
974 NotificationManager.Notify(103, Builder.Build());
977 public void ShowEDalerNotification(
string Title,
string MessageBody, IDictionary<string, string> Data)
979 Context Context = Application.Context;
980 Intent Intent =
new Intent(Context, typeof(
MainActivity));
981 Intent.AddFlags(ActivityFlags.ClearTop);
982 foreach (
string Key
in Data.Keys)
984 Intent.PutExtra(Key, Data[Key]);
987 StringBuilder ContentBuilder =
new StringBuilder();
988 ContentBuilder.Append(MessageBody);
989 if (Data.TryGetValue(
"amount", out
string? Amount) && !
string.IsNullOrEmpty(Amount))
991 ContentBuilder.AppendLine().Append(Amount);
992 if (Data.TryGetValue(
"currency", out
string? Currency) && !
string.IsNullOrEmpty(Currency))
993 ContentBuilder.Append(
" " + Currency);
994 if (Data.TryGetValue(
"timestamp", out
string? Timestamp) && !
string.IsNullOrEmpty(Timestamp))
995 ContentBuilder.Append(
string.Format(CultureInfo.InvariantCulture,
" ({0})", Timestamp));
998 PendingIntent? PendingIntent = Android.App.PendingIntent.GetActivity(Context, 104, Intent, PendingIntentFlags.OneShot | PendingIntentFlags.Immutable);
999 int ResIdentifier = Context.Resources?.GetIdentifier(
"app_icon",
"drawable", Context.PackageName) ?? 0;
1000 if (ResIdentifier == 0)
1006 .SetSmallIcon(ResIdentifier)
1007 .SetContentTitle(Title)
1008 .SetContentText(ContentBuilder.ToString())
1009 .SetAutoCancel(
true)
1010 .SetContentIntent(PendingIntent);
1012 NotificationManagerCompat NotificationManager = NotificationManagerCompat.From(Context);
1013 NotificationManager.Notify(104, Builder.Build());
1016 public void ShowTokenNotification(
string Title,
string MessageBody, IDictionary<string, string> Data)
1018 Context Context = Application.Context;
1019 Intent Intent =
new Intent(Context, typeof(
MainActivity));
1020 Intent.AddFlags(ActivityFlags.ClearTop);
1021 foreach (
string Key
in Data.Keys)
1023 Intent.PutExtra(Key, Data[Key]);
1026 StringBuilder ContentBuilder =
new StringBuilder();
1027 ContentBuilder.Append(MessageBody);
1028 if (Data.TryGetValue(
"value", out
string? Value) && !
string.IsNullOrEmpty(Value))
1030 ContentBuilder.AppendLine().Append(Value);
1031 if (Data.TryGetValue(
"currency", out
string? Currency) && !
string.IsNullOrEmpty(Currency))
1032 ContentBuilder.Append(
" " + Currency);
1035 PendingIntent? PendingIntent = Android.App.PendingIntent.GetActivity(Context, 105, Intent, PendingIntentFlags.OneShot | PendingIntentFlags.Immutable);
1036 int ResIdentifier = Context.Resources?.GetIdentifier(
"app_icon",
"drawable", Context.PackageName) ?? 0;
1037 if (ResIdentifier == 0)
1043 .SetSmallIcon(ResIdentifier)
1044 .SetContentTitle(Title)
1045 .SetContentText(ContentBuilder.ToString())
1046 .SetAutoCancel(
true)
1047 .SetContentIntent(PendingIntent);
1049 NotificationManagerCompat NotificationManager = NotificationManagerCompat.From(Context);
1050 NotificationManager.Notify(105, Builder.Build());
1053 public void ShowProvisioningNotification(
string Title,
string MessageBody, IDictionary<string, string> Data)
1055 Context Context = Application.Context;
1056 Intent Intent =
new Intent(Context, typeof(
MainActivity));
1057 Intent.AddFlags(ActivityFlags.ClearTop);
1058 foreach (
string Key
in Data.Keys)
1060 Intent.PutExtra(Key, Data[Key]);
1062 PendingIntent? PendingIntent = Android.App.PendingIntent.GetActivity(Context, 106, Intent, PendingIntentFlags.OneShot | PendingIntentFlags.Immutable);
1063 int ResIdentifier = Context.Resources?.GetIdentifier(
"app_icon",
"drawable", Context.PackageName) ?? 0;
1064 if (ResIdentifier == 0)
1070 .SetSmallIcon(ResIdentifier)
1071 .SetContentTitle(Title)
1072 .SetContentText(MessageBody)
1073 .SetAutoCancel(
true)
1074 .SetContentIntent(PendingIntent);
1076 NotificationManagerCompat NotificationManager = NotificationManagerCompat.From(Context);
1077 NotificationManager.Notify(106, Builder.Build());
1082 Activity? Activity = Platform.CurrentActivity;
1083 if (Activity?.Window?.DecorView is not Android.Views.View DecorView)
1084 return new Thickness(0);
1086 float Density = Activity.Resources?.DisplayMetrics?.Density ?? 1f;
1089 if (OperatingSystem.IsAndroidVersionAtLeast(30))
1091 WindowInsets? WindowInsets = DecorView.RootWindowInsets;
1092 if (WindowInsets is not
null)
1094 Android.Graphics.Insets Insets = WindowInsets.GetInsets(Android.Views.WindowInsets.Type.SystemBars());
1095 return new Thickness(
1096 Insets.Left / Density,
1097 Insets.Top / Density,
1098 Insets.Right / Density,
1099 Insets.Bottom / Density
1103 else if (OperatingSystem.IsAndroidVersionAtLeast(23))
1105 WindowInsets? WindowInsets = DecorView.RootWindowInsets;
1106 if (WindowInsets is not
null)
1108 return new Thickness(
1109 WindowInsets.SystemWindowInsetLeft / Density,
1110 WindowInsets.SystemWindowInsetTop / Density,
1111 WindowInsets.SystemWindowInsetRight / Density,
1112 WindowInsets.SystemWindowInsetBottom / Density
1117 return new Thickness(0);
Represents an instance of the Neuro-Access app.
const string Png
The PNG MIME type.
const string Provisioning
Provisioning channel
const string Petitions
Petitions channel
const string Identities
Identities channel
const string Messages
Messages channel
const string EDaler
eDaler channel
const string Tokens
Tokens channel
const string Contracts
Contracts channel
const int MaxScreenRecordingTimeSeconds
Maximum number of seconds screen recording is allowed.
const string Xmpp
XMPP URI Scheme (xmpp)
const string IotId
The IoT ID URI Scheme (iotid)
A set of never changing property constants and helpful values.
Base class that references services in the app.
static ILogService LogService
Log service.
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
BiometricMethod
Enum representing the device biometric method for authentication.
class KeyboardSizeMessage(float KeyboardSize)
Keyboard size change message
ClientType
Type of client requesting notification.