Xamarin.Forms Speakerphone NOT Activating on Android 10, 11, 12 - xamarin.forms

I have App that has been working for years on Android 10, 11, 12 to Turn On/Off SpeakerPhone for Inbound and Outbound calls, but now it works sometimes on Inbound calls but never on Outbound calls. Here is a simple BroadcastReciever and MainActivity.
[BroadcastReceiver(Enabled = true, Exported = true)]
[IntentFilter(new[] { TelephonyManager.ActionPhoneStateChanged })]
public class PhonecallReceiver : BroadcastReceiver
{
public override async void OnReceive(Context context, Intent intent)
{
Toast.MakeText(context, "Received intent!", ToastLength.Long).Show();
var state = intent.GetStringExtra(TelephonyManager.ExtraState);
if (intent.Action == TelephonyManager.ActionPhoneStateChanged)
{
if (state == TelephonyManager.ExtraStateRinging)
{
//Task.Run(async () =>
//{
// await Task.Delay(1);
// Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
// {
// MainActivity.audioManager = (AudioManager)Application.Context.GetSystemService(Context.AudioService);
// if (!MainActivity.audioManager.SpeakerphoneOn)
// { MainActivity.audioManager.SpeakerphoneOn = true; }
// var mode = Mode.Normal;// | Mode.Ringtone | Mode.InCommunication | Mode.Normal;
// MainActivity.audioManager.SetRouting(mode, Route.Speaker, Route.All);
// });
//});
MainActivity.audioManager.SpeakerphoneOn = true;
}
else if (state == TelephonyManager.ExtraStateOffhook)
{
MainActivity.audioManager.SpeakerphoneOn = true;
}
else if (state == TelephonyManager.ExtraStateIdle)
{
MainActivity.audioManager.SpeakerphoneOn = false;
}
}
}
}
In the MainActivity
public static AudioManager audioManager { get; set; }
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
var permissions = new string[]
{
Manifest.Permission.ReadPhoneState,
Manifest.Permission.ModifyAudioSettings,
Manifest.Permission.ModifyPhoneState
};
ActivityCompat.RequestPermissions(this, permissions, 123);
audioManager = (AudioManager)Application.Context.GetSystemService(Context.AudioService);
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 123 && grantResults.Length > 0 && grantResults[0] == Permission.Granted)
{
RegisterReceiver(new PhonecallReceiver(), new IntentFilter(TelephonyManager.ActionPhoneStateChanged));
}
}
The above code used to Turn On/Off SpeakerPhone for Inbound/Outbound calls. Now it does not Turn ON the Speakerphone for Outbound Call and sometimes work for Inbound call. My problem is occurring on LG-K51 Android 12 mobile phone that I have Factory Reset.
Thanks

Related

Is there a way to use the camera functions in Xamarin Forms without downloading any extra NuGet Packages?

I am currently building a project which allows the user to take a photo of something and use that photo. I was wondering if there were any other methods out there that does not require me to download any Plugins or NuGet Packages?
You need to create a ICameraPickerService in Xamarin Forms :
public interface IPhotoPickerService
{
Task<byte[]> GetImageStreamAsync();
}
In iOS , create the CameraPickerService :
[assembly: Dependency(typeof(CameraPickerService))]
namespace DependencyServiceDemos.iOS
{
public class CameraPickerService: ICameraPickerService
{
TaskCompletionSource<byte[]> taskCompletionSource;
UIImagePickerController imagePicker;
public Task<byte[]> GetImageStreamAsync()
{
// Create and define UIImagePickerController
imagePicker = new UIImagePickerController
{
SourceType = UIImagePickerControllerSourceType.Camera,
MediaTypes = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.Camera)
};
// Set event handlers
imagePicker.FinishedPickingMedia += OnImagePickerFinishedPickingMedia;
imagePicker.Canceled += OnImagePickerCancelled;
// Present UIImagePickerController;
UIWindow window = UIApplication.SharedApplication.KeyWindow;
var viewController = window.RootViewController;
viewController.PresentModalViewController(imagePicker, true);
// Return Task object
taskCompletionSource = new TaskCompletionSource<byte[]>();
return taskCompletionSource.Task;
}
void OnImagePickerFinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs args)
{
UIImage image = args.EditedImage ?? args.OriginalImage;
if (image != null)
{
// Convert UIImage to .NET Stream object
NSData data;
if (args.ReferenceUrl.PathExtension.Equals("PNG") || args.ReferenceUrl.PathExtension.Equals("png"))
{
data = image.AsPNG();
}
else
{
data = image.AsJPEG(1);
}
Stream stream = data.AsStream();
UnregisterEventHandlers();
// Set the Stream as the completion of the Task
taskCompletionSource.SetResult(data.ToArray());
}
else
{
UnregisterEventHandlers();
taskCompletionSource.SetResult(null);
}
imagePicker.DismissModalViewController(true);
}
void OnImagePickerCancelled(object sender, EventArgs args)
{
UnregisterEventHandlers();
taskCompletionSource.SetResult(null);
imagePicker.DismissModalViewController(true);
}
void UnregisterEventHandlers()
{
imagePicker.FinishedPickingMedia -= OnImagePickerFinishedPickingMedia;
imagePicker.Canceled -= OnImagePickerCancelled;
}
}
}
Not forgetting to add permission in Info.plist :
<key>NSCameraUsageDescription</key>
<string>Use Camera</string>
In addition , iOS need to run in a physical device.
In Android , create the CameraPickerService :
[assembly: Dependency(typeof(CameraPickerService))]
namespace DependencyServiceDemos.Droid
{
public class CameraPickerService : ICameraPickerService
{
public Task<byte[]> GetImageStreamAsync()
{
// Define the Intent for getting images
Intent getImageByCamera = new Intent("android.media.action.IMAGE_CAPTURE");
// Start the camera (resumes in MainActivity.cs)
MainActivity.Instance.StartActivityForResult(
getImageByCamera,
MainActivity.PickImageId);
// Save the TaskCompletionSource object as a MainActivity property
MainActivity.Instance.PickImageTaskCompletionSource = new TaskCompletionSource<byte[]>();
// Return Task object
return MainActivity.Instance.PickImageTaskCompletionSource.Task;
}
}
}
Adding permission in AndroidMainfest.xml :
<uses-permission android:name= "android.permission.CAMERA" />
<uses-permission android:name= "android.permission.WRITE_EXTERNAL_STORAGE" />
Get Image data in MainActivity :
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
internal static MainActivity Instance { get; private set; }
public int CAMERA_JAVA_REQUEST_CODE = 1;
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Instance = this;
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
DependencyService.Register<ITextToSpeechService, TextToSpeechService>();
}
// Field, property, and method for Picture Picker
public static readonly int PickImageId = 1000;
public TaskCompletionSource<byte[]> PickImageTaskCompletionSource { set; get; }
protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
{
base.OnActivityResult(requestCode, resultCode, intent);
if (requestCode == PickImageId)
{
if ((resultCode == Result.Ok) && (intent != null))
{
Bundle bundle = intent.Extras;
Bitmap bitmap = (Bitmap)bundle.Get("data");
//// Set the Stream as the completion of the Task
MemoryStream memoryStream = new MemoryStream();
bitmap.Compress(Bitmap.CompressFormat.Jpeg, 50, memoryStream);
PickImageTaskCompletionSource.SetResult(memoryStream.ToArray());
}
else
{
PickImageTaskCompletionSource.SetResult(null);
}
}
}
}
Finally , show image in ContentPage of Forms :
async void OnPickPhotoButtonClicked(object sender, EventArgs e)
{
(sender as Button).IsEnabled = false;
byte[] data = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
MemoryStream stream = new MemoryStream(data);
if (stream != null)
{
image.Source = ImageSource.FromStream(() => stream) ;
}
(sender as Button).IsEnabled = true;
}
The effect :
Note : If want to pick a Photo from the Picture Library, you can have a look at this official document .

Request camera permissions xamarin forms

I have an app that needs permissions for the camera, this is well implemented, but asks for permission on the home screen (splash). What I would like is to appear after logging in or on a specific page (Is this possible?). And as I could implement the same for IOS, I thank you very much for your help and have a great day.
Thanks for you help.
Here my code
MainActivity.cs
const int requestCameraId = 0;
const int requestStorageId = 1;
const int requestId = 2;
readonly string[] permissions =
{
Android.Manifest.Permission.Camera,
Android.Manifest.Permission.ReadExternalStorage,
Android.Manifest.Permission.WriteExternalStorage,
Android.Manifest.Permission.Internet,
Android.Manifest.Permission.ForegroundService,
Android.Manifest.Permission.RequestCompanionUseDataInBackground,
Android.Manifest.Permission.RequestCompanionRunInBackground,
Android.Manifest.Permission.StatusBar,
Android.Manifest.Permission.Vibrate,
Android.Manifest.Permission.Flashlight
};
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
switch (requestCode)
{
case requestCameraId:
{
if (grantResults[0] == (int)Android.Content.PM.Permission.Granted)
{
Toast.MakeText(this, "Permiso concedido para la camara", ToastLength.Short).Show();
}
else
{
//Permission Denied :(
Toast.MakeText(this, "Permiso denegado para la camara", ToastLength.Short).Show();
}
}
break;
case requestStorageId:
{
if (grantResults[0] == (int)Android.Content.PM.Permission.Granted)
{
Toast.MakeText(this, "Permiso concedido para el almacenamiento", ToastLength.Short).Show();
}
else
{
//Permission Denied :(
Toast.MakeText(this, "Permiso denegado para el almacenamiento", ToastLength.Short).Show();
}
}
break;
}
}
async Task GetCameraPermissionAsync()
{
const string permission = Manifest.Permission.Camera;
if (CheckSelfPermission(permission) == (int)Android.Content.PM.Permission.Granted)
{
//TODO change the message to show the permissions name
Toast.MakeText(this, "Permisos para la camara listos", ToastLength.Short).Show();
return;
}
if (ShouldShowRequestPermissionRationale(permission))
{
//set alert for executing the task
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.SetTitle("Permisos necesarios");
alert.SetMessage("La aplicación necesita acceder a la camara para tomar una fotografía del trabajo terminado");
alert.SetPositiveButton("Conceder permiso", (senderAlert, args) =>
{
RequestPermissions(permissions, requestCameraId);
});
alert.SetNegativeButton("Cancelar", (senderAlert, args) =>
{
Toast.MakeText(this, "Cancelado", ToastLength.Short).Show();
});
Dialog dialog = alert.Create();
dialog.Show();
return;
}
}
async Task GetStoragePermissionAsync()
{
const string permission = Manifest.Permission.ReadExternalStorage;
if (CheckSelfPermission(permission) == (int)Android.Content.PM.Permission.Granted)
{
//TODO change the message to show the permissions name
Toast.MakeText(this, "Permisos para leer carpetas listos", ToastLength.Short).Show();
return;
}
if (ShouldShowRequestPermissionRationale(permission))
{
//set alert for executing the task
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.SetTitle("Permisos necesarios");
alert.SetMessage("La aplicación necesita acceder a sus archivos para subir una imagen con el trabajo terminado");
alert.SetPositiveButton("Conceder permiso", (senderAlert, args) =>
{
RequestPermissions(permissions, requestStorageId);
});
alert.SetNegativeButton("Cancelar", (senderAlert, args) =>
{
Toast.MakeText(this, "Cancelado", ToastLength.Short).Show();
});
Dialog dialog = alert.Create();
dialog.Show();
return;
}
}
async Task GetPermissionsAsync()
{
await GetCameraPermissionAsync();
await GetStoragePermissionAsync();
RequestPermissions(permissions, requestId);
}
async Task TryToGetPermissions()
{
if ((int)Build.VERSION.SdkInt >= 23)
{
await GetPermissionsAsync();
return;
}
}
protected async override void OnCreate(Bundle savedInstanceState)
{
await TryToGetPermissions();
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
// RequestPermissions(permissions, requestId);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
Xamarin.Forms.Application.Current.On<Xamarin.Forms.PlatformConfiguration.Android>().UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize);
CreateNotificationFromIntent(Intent);
//notificationServiceIntent = new Intent(this.BaseContext, typeof(PDANotificationService));
//StartService(notificationServiceIntent);
WireUpLongRunningTask();
var message = new StartLongRunningTaskMessage();
MessagingCenter.Send(message, "StartLongRunningTaskMessage");
}
As #Jason said, your code requests permission on main Activity create, so as soon as you open your app, it will ask for all the permissions you listed. You need to request permission separately.
Say camera permission:
if (CheckSelfPermission(Manifest.Permission.Camera) != (int)Permission.Granted)
{
//request permission
}else
{
//call camera
}
And OnRequestPermissionsResult will be called after user granted/denied the permission request, you can check the result and call camera if the permission was granted.
Please refer to this for detail workflow about permission.

How to make phone calls using Xamairn forms?

I'm new in xamarin and I want make a phone call directly (without opening the dialler). I tried with this example but it doesn't work.
Click Please help
public class PhoneCall_Droid : IPhoneCall
{
public void MakeQuickCall(string PhoneNumber)
{
try
{
var uri = Android.Net.Uri.Parse(string.Format("tel:{0}", PhoneNumber));
var intent = new Intent(Intent.ActionCall, uri);
Xamarin.Forms.Forms.Context.StartActivity(intent);
}
catch (Exception ex)
{
new AlertDialog.Builder(Android.App.Application.Context).SetPositiveButton("OK", (sender, args) =>
{
//User pressed OK
})
.SetMessage(ex.ToString())
.SetTitle("Android Exception")
.Show();
}
}
}
there are two error in your code above:
1.Xamarin.Forms.Forms.Context could not get the correct context.
you could defined a static variable in MainActiviy like :
public static MainActivity Instance;
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
Instance = this;
LoadApplication(new App());
}
you also could use the Current Activity Plugin,you could refer to Current Activity
2.After Android6.0 you should requests the runtime permissions and the official doucument
here is a simple example:
[assembly: Xamarin.Forms.Dependency(typeof(PhoneCall_Droid))]
namespace App18.Droid
{
class PhoneCall_Droid: IPhoneCall
{
public void MakeQuickCall(string PhoneNumber)
{
try
{
if(ActivityCompat.CheckSelfPermission(MainActivity.Instance, Android.Manifest.Permission.CallPhone) != Android.Content.PM.Permission.Granted ){
ActivityCompat.RequestPermissions(MainActivity.Instance, new string[] {Android.Manifest.Permission.CallPhone }, 1);
return;
}
else
{
var uri = Android.Net.Uri.Parse(string.Format("tel:{0}", PhoneNumber));
var intent = new Intent(Intent.ActionCall, uri);
MainActivity.Instance.StartActivity(intent);
}
}
catch (Exception ex)
{
new AlertDialog.Builder(MainActivity.Instance).SetPositiveButton("OK", (sender, args) =>
{
//User pressed OK
})
.SetMessage(ex.ToString())
.SetTitle("Android Exception")
.Show();
}
}
}
}
and you also could to use the nugetpackage Plugin.Permissions to request runtime permissions(Permission.Location)
refer to Plugin.Permissions
finally you could call like
DependencyService.Get<IPhoneCall>().MakeQuickCall(phonenumber);

Xamarin Forms: Load a contentpage when tap push notification android

I completed receiving test notification from FCM console. Now I am trying to open a page when tapping the notification. Any ideas about how to achieve this? I have searched the internet but can't find a working solution. I am also able to send the notification through the postman.
I handle the notification tapping in following way. The page loading is handled in App.xaml.cs.
On OnCreate():
//Background or killed mode
if (Intent.Extras != null)
{
foreach (var key in Intent.Extras.KeySet())
{
var value = Intent.Extras.GetString(key);
if (key == "webContentList")
{
if (value?.Length > 0)
{
isNotification = true;
LoadApplication(new App(domainname, value));
}
}
}
}
//Foreground mode
if (FirebaseNotificationService.webContentList.ToString() != "")
{
isNotification = true;
LoadApplication(new App(domainname, FirebaseNotificationService.webContentList.ToString()));
FirebaseNotificationService.webContentList = "";
}
//Normal loading
if (!isNotification)
{
LoadApplication(new App(domainname, string.Empty));
}
On FirebaseNotificationService:
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class FirebaseNotificationService : FirebaseMessagingService
{
public static string webContentList = "";
public override void OnMessageReceived(RemoteMessage message)
{
base.OnMessageReceived(message);
webContentList = message.Data["webContentList"];
try
{
SendNotificatios(message.GetNotification().Body, message.GetNotification().Title);
}
catch (Exception ex)
{
Console.WriteLine("Error:>>" + ex);
}
}
public void SendNotificatios(string body, string Header)
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);
var notificationBuilder = new Android.App.Notification.Builder(this, Utils.CHANNEL_ID)
.SetContentTitle(Header)
.SetSmallIcon(Resource.Drawable.icon)
.SetContentText(body)
.SetAutoCancel(true)
.SetContentIntent(pendingIntent);
var notificationManager = NotificationManager.FromContext(this);
notificationManager.Notify(0, notificationBuilder.Build());
}
else
{
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);
var notificationBuilder = new Android.App.Notification.Builder(this, Utils.CHANNEL_ID)
.SetContentTitle(Header)
.SetSmallIcon(Resource.Drawable.icon)
.SetContentText(body)
.SetAutoCancel(true)
.SetContentIntent(pendingIntent)
.SetChannelId(Utils.CHANNEL_ID);
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
return;
}
var channel = new NotificationChannel(Utils.CHANNEL_ID, "FCM Notifications", NotificationImportance.High)
{
Description = "Firebase Cloud Messages appear in this channel"
};
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
notificationManager.Notify(0, notificationBuilder.Build());
}
}
I don't know what's your actual Firebase implementation, but this might help you.
There is a nice package for Firebase in Xamarin Forms that we use in our production App made by CrossGeeks team. Its working great and has all handlers for your needs. This works with iOS and Android and you don't need to write platform specific code, only configuration and some code in AppDelegate.cs and MainActivity.cs
https://github.com/CrossGeeks/FirebasePushNotificationPlugin/blob/master/docs/FirebaseNotifications.md#notification-events
I wrote a simple PushNotificationService that processes automatic refresh and/or pushes new pages considering push notif data.
When app is closed and user clicks on the notification, I store the push notif data using Akavache.
CrossFirebasePushNotification.Current.OnNotificationOpened += async (s, p) =>
{
if (App.AppBeenResumed)
{
await BlobCache.UserAccount.InsertObject("pushNotifData", p.Data);
}
else
{
await ProcessReceivedPushNotification(p.Data);
}
};
And on the landing page of the app, I check if there is an existing push notif data in the OnAppearing method of the page.
protected override void OnAppearing()
{
base.OnAppearing();
App.AppBeenResumed = false;
HandlePushNotificationIfExists();
}
private async void HandlePushNotificationIfExists()
{
IDictionary<string, object> pushNotifData;
try
{
pushNotifData = await BlobCache.UserAccount.GetObject<IDictionary<string, object>>("pushNotifData");
}
catch (KeyNotFoundException)
{
pushNotifData = null;
}
if (pushNotifData == null) return;
await BlobCache.UserAccount.InvalidateAllObjects<IDictionary<string, object>>();
await PushNotificationService.ProcessReceivedPushNotification(pushNotifData);
}
In the ProcessReceivedPushNotification you can do whatever you want ... push directly the page or whatever... call another service that will do the job of pushing a new page and some business process.
Note that App.AppBeenResumed is a static bool to determine if App has been started or resumed to handle correctly the process of treatment of the push notif (process it instant or store it in blobcache to treat it later when landing page is Appearing).
In MainActivity.cs :
protected override void OnCreate(Bundle bundle)
{
...
LoadApplication(new App(true));
}
In the App.cs :
public App(bool beenResumedOrStarted)
{
...
AppBeenResumed = beenResumedOrStarted;
...
}
protected override void OnResume()
{
AppBeenResumed = false;
}
protected override void OnSleep()
{
//iOS states are not the same so always false when device is iOS
AppBeenResumed = Device.RuntimePlatform != Device.iOS;
}

Custom Keyboard in Xamarin forms

I've read the many posts on the forum and on StackOverflow and other places on making custom keyboards, but have not found an approach that will work for my Xamarin forms cross-platform project. It is programmatically generated.
For example, I built this keyboard that was recommended in several places:
I try to integrate this into my Xamarin forms app but not able to do this
https://github.com/Vaikesh/CustomKeyboard/blob/master/CustomKeyboard/Activity1.cs
It works fine as a standalone
I want Hebrew language keyboard in my application Like this
I would appreciate any help.
Thank you.
Custom Keyboard in Xamarin forms
You could create a PageRenderer and use native .axml layout file to create the custom Keyboard.
For example, my KeyboardPageRenderer :
[assembly: ExportRenderer(typeof(MyKeyboardPage), typeof(KeyboardPageRenderer))]
...
public class KeyboardPageRenderer : PageRenderer
{
public CustomKeyboardView mKeyboardView;
public EditText mTargetView;
public Android.InputMethodServices.Keyboard mKeyboard;
Activity activity;
global::Android.Views.View view;
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || Element == null)
{
return;
}
try
{
SetupUserInterface();
SetupEventHandlers();
this.AddView(view);
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine(#" ERROR: ", ex.Message);
}
}
void SetupUserInterface()
{
activity = this.Context as Activity;
view = activity.LayoutInflater.Inflate(Resource.Layout.activity_keyboard, this, false);
mKeyboard = new Android.InputMethodServices.Keyboard(Context, Resource.Xml.keyboard);
mTargetView = view.FindViewById<EditText>(Resource.Id.target);
mKeyboardView = view.FindViewById<CustomKeyboardView>(Resource.Id.keyboard_view);
mKeyboardView.Keyboard = mKeyboard;
}
void SetupEventHandlers()
{
mTargetView.Touch += (sender, e) =>
{
ShowKeyboardWithAnimation();
e.Handled = false;
mTargetView.ShowSoftInputOnFocus = false;
};
mKeyboardView.Key += async (sender, e) =>
{
long eventTime = JavaSystem.CurrentTimeMillis();
KeyEvent ev = new KeyEvent(eventTime, eventTime, KeyEventActions.Down, e.PrimaryCode, 0, 0, 0, 0, KeyEventFlags.SoftKeyboard | KeyEventFlags.KeepTouchMode);
DispatchKeyEvent(ev);
await Task.Delay(1);
mTargetView.RequestFocus();
};
}
public void ShowKeyboardWithAnimation()
{
if (mKeyboardView.Visibility == ViewStates.Gone)
{
mKeyboardView.Visibility = ViewStates.Visible;
Android.Views.Animations.Animation animation = AnimationUtils.LoadAnimation(
Context,
Resource.Animation.slide_in_bottom
);
mKeyboardView.ShowWithAnimation(animation);
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
var msw = MeasureSpec.MakeMeasureSpec(r - l, MeasureSpecMode.Exactly);
var msh = MeasureSpec.MakeMeasureSpec(b - t, MeasureSpecMode.Exactly);
view.Measure(msw, msh);
view.Layout(0, 0, r - l, b - t);
}
}
Effect:
.
I wrote up a simple demo about how to implement this feature, you can see it in this GitHub Repository.
I don't know Hebrew, if you need to achieve the effect like the picture you have post, you need custom the layout in keyboard.xml file.
Update :
I am done iOS portion using entry render so only try to do for android portion
I write a EntryRenderer to implement this feature, effect like this, hope this can help you.
public class MyEntry2Renderer : ViewRenderer<MyEntry, TextInputLayout>,
ITextWatcher,
TextView.IOnEditorActionListener
{
private bool _hasFocus;
public CustomKeyboardView mKeyboardView;
public Android.InputMethodServices.Keyboard mKeyboard;
ViewGroup activityRootView;
protected EditText EditText => Control.EditText;
public bool OnEditorAction(TextView v, ImeAction actionId, KeyEvent e)
{
if ((actionId == ImeAction.Done) || ((actionId == ImeAction.ImeNull) && (e.KeyCode == Keycode.Enter)))
{
Control.ClearFocus();
//HideKeyboard();
((IEntryController)Element).SendCompleted();
}
return true;
}
public virtual void AfterTextChanged(IEditable s)
{
}
public virtual void BeforeTextChanged(ICharSequence s, int start, int count, int after)
{
}
public virtual void OnTextChanged(ICharSequence s, int start, int before, int count)
{
if (string.IsNullOrWhiteSpace(Element.Text) && (s.Length() == 0)) return;
((IElementController)Element).SetValueFromRenderer(Entry.TextProperty, s.ToString());
}
protected override TextInputLayout CreateNativeControl()
{
var textInputLayout = new TextInputLayout(Context);
var editText = new EditText(Context);
#region Add the custom Keyboard in your Page
var activity = Forms.Context as Activity;
var rootView = activity.Window.DecorView.FindViewById(Android.Resource.Id.Content);
activity.Window.SetSoftInputMode(SoftInput.StateAlwaysHidden);
activityRootView = ((ViewGroup)rootView).GetChildAt(0) as ViewGroup;
mKeyboardView = new CustomKeyboardView(Forms.Context, null);
Android.Widget.RelativeLayout.LayoutParams layoutParams =
new Android.Widget.RelativeLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.WrapContent); // or wrap_content
layoutParams.AddRule(LayoutRules.AlignParentBottom);
activityRootView.AddView(mKeyboardView, layoutParams);
#endregion
//First open the current page, hide the Keyboard
mKeyboardView.Visibility = ViewStates.Gone;
//Use the custom Keyboard
mKeyboard = new Android.InputMethodServices.Keyboard(Context, Resource.Xml.keyboard2);
mKeyboardView.Keyboard = mKeyboard;
mKeyboardView.Key += async (sender, e) =>
{
long eventTime = JavaSystem.CurrentTimeMillis();
KeyEvent ev = new KeyEvent(eventTime, eventTime, KeyEventActions.Down, e.PrimaryCode, 0, 0, 0, 0, KeyEventFlags.SoftKeyboard | KeyEventFlags.KeepTouchMode);
DispatchKeyEvent(ev);
await Task.Delay(1);
};
textInputLayout.AddView(editText);
return textInputLayout;
}
protected override void OnElementChanged(ElementChangedEventArgs<MyEntry> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
if (Control != null)
EditText.FocusChange -= ControlOnFocusChange;
if (e.NewElement != null)
{
var ctrl = CreateNativeControl();
SetNativeControl(ctrl);
EditText.ShowSoftInputOnFocus = false;
EditText.FocusChange += ControlOnFocusChange;
}
}
private void ControlOnFocusChange(object sender, FocusChangeEventArgs args)
{
_hasFocus = args.HasFocus;
if (_hasFocus)
{
EditText.Post(() =>
{
EditText.RequestFocus();
ShowKeyboardWithAnimation();
});
}
else
{
//Hide the Keyboard
mKeyboardView.Visibility = ViewStates.Gone;
}
}
public void ShowKeyboardWithAnimation()
{
if (mKeyboardView.Visibility == ViewStates.Gone)
{
mKeyboardView.Visibility = ViewStates.Visible;
Android.Views.Animations.Animation animation = AnimationUtils.LoadAnimation(
Context,
Resource.Animation.slide_in_bottom
);
mKeyboardView.ShowWithAnimation(animation);
}
}
}

Resources