Xamarin Pinning Certificate ServicePointManager Callback does not fire Xamarin android and iOS - xamarin

I have been looking at xamarin pinning certificate and I have tried the code below but Service callback never gets called.
I just added a button that makes an http call and I was expecting for the callback to be invoked
**using xamarin.forms.4.6.0.800 and .net standard 2.0 **
AppXaml
protected override async void OnInitialized()
{
InitializeComponent();
CertificateValidator.Initialize();
await NavigationService.NavigateAsync("NavigationPage/MainPage");
}
ViewModel
public class MainPageViewModel : ViewModelBase
{
private readonly HttpClient httpClient;
public MainPageViewModel(INavigationService navigationService)
: base(navigationService)
{
this.httpClient = new HttpClient(new HttpClientHandler());
}
private DelegateCommand makeCallCommand;
public DelegateCommand MakeCallCommand =>
makeCallCommand ?? (makeCallCommand = new DelegateCommand(async () => await MakeCall()));
async Task MakeCall()
{
var result= await httpClient.GetStringAsync("https://www.xamarin.com");
}
}
CertificateValidator
public static class CertificateValidator
{
private const string PublicKey = "whatever";
public static void Initialize()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = ValidateCertificate;
}
public static bool ValidateCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
Debugger.Break();
return PublicKey == certificate?.GetPublicKeyString();
}
}
In Android I have tried all below and none trigger a callback .
Happy to use native handlers but not sure where to start. Is there a nuget?

Related

MAUI send sms without user interaction in Maui

I want to send a SMS in MAUI without opening the default messages App, I want to send the SMS silently in background. Does anyone know how to implement it?
Here is the implementation in MAUI.
Tested for Android and it works without opening the messages app. Here is the implementation for Android and iOS (not tested).
in the shared project create this class:
public partial class SmsService
{
public partial void Send(string address, string message);
}
Implementation for Android platform:
public partial class SmsService
{
public partial void Send(string phonenbr, string message)
{
SmsManager smsM = SmsManager.Default;
smsM.SendTextMessage(phonenbr, null, message, null, null);
}
}
Implementation for iOS platform (not tested):
public partial class SmsService
{
public partial void Send(string address, string message)
{
if (!MFMailComposeViewController.CanSendMail)
return;
MFMessageComposeViewController smsController = new MFMessageComposeViewController();
smsController.Recipients = new[] { address };
smsController.Body = message;
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);
}
}

How to implement / register singleton HttClientFactory in Xamarin Forms

I am using Xamarin Forms with Prism.
How can I register a service for HttpClientFactory?
Is there a equivalent in xamarin/prism to the ConfigureServices method in .net core?
I would like to use the equivalent of this code in Xamarin forms:
services.AddHttpClient<MyClient>("MyHttpClient",
x => { x.BaseAddress = new Uri("example.com"); }
).AddPolicyHandler(GetRetryPolicy());
services.AddSingleton<MyClientFactory>();
I have been trying different ways and I cannot find the right way to do it.
Thanks a lot.
UPDATE
I have got these clases in a netstandard ClassLibrary called BusinessLogic
MyClientFactory.cs:
namespace BusinessLogic.Services
{
public class MyClientFactory
{
private readonly IServiceProvider _serviceProvider;
public MyClientFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public MyClient Create()
{
return _serviceProvider.GetRequiredService<MyClient>();
}
}
}
MyClient.cs:
namespace ServiceBusinessLogic.Services
{
public class MyClient : IMyClient
{
private readonly HttpClient _httpClient;
private readonly ILogger<MyClient> _logger;
public MyClient(ILogger<MyClient> logger, HttpClient httpClient)
{
_logger = logger;
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
public async Task<Result<Token>> GetToken(CancellationToken cancellationToken, string userName, string password)
{
try
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "Token");
request.Content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", userName),
new KeyValuePair<string, string>("password", password)
});
HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
if (!result.IsSuccessStatusCode)
{
_logger.LogError("the status code is: {0}", (int)result.StatusCode);
// CODE abbreviated for reading
}
Interface IMyClient.cs:
namespace MyBusinessLogic.Services
{
public interface IMyClient
{
Task<Result<Token>> GetToken(CancellationToken cancellationToken, string userName, string password);
// CODE abbreviated for reading
MyClientQueries.cs:
namespace MyBusinessLogic.Services
{
public class MyClientQueries
{
private readonly MyClientFactory _myClientFactory;
public MyClientQueries(MyClientFactory myClientFactory)
{
_MyClientFactory = myClientFactory ?? throw new ArgumentNullException(nameof(myClientFactory));
}
public async Task<Result<Token>> GetToken(CancellationToken cancellationToken, string username, string password)
{
var mysClient = _myClientFactory.Create();
var response = await tsClient.GetToken(cancellationToken, username, password).ConfigureAwait(true);
return response;
}
// CODE abbreviated
Then I have got a xamarin forms Project with Prism called App_Mobile_test
App.xaml.cs:
public partial class App : PrismApplication
{
public App() : this(null) { }
public App(IPlatformInitializer initializer) : base(initializer) { }
protected override async void OnInitialized()
{
InitializeComponent();
await NavigationService.NavigateAsync("NavigationPage/MainPage");
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<MainPage>();
}
}
I would like to use MyClientQueries which uses DI for HttpClientFactory and HttpClient in the Xamarin Forms project as a service (singleton) so I can call GetToken and all the other methods that are in that class.
But I do not know how to do this in Xamarin Forms.
MyBusinessProject that contains MyClientQueries which I want to use, is already used in a asp.net core mvc and the services were added in the startup.cs in .net core like this:
public void ConfigureServices(IServiceCollection services)
{
#region "api service"
services.AddSingleton<MyClientQueries>();
services.AddHttpClient<MyClient>("MyHttpClient",
x => { x.BaseAddress = new Uri(Configuration["APIConfiguration:BaseAddress"]); }
).AddPolicyHandler(GetRetryPolicy());
services.AddSingleton<MyClientFactory>();
#endregion
//CODE abbreviated
}
How can I register the same objects I am doing in the .net Core app, but in Xamarin Forms and use the dependency injection for the view models?
The shared project "MyBusinessLogic" works fine in the asp.net core, and I would like to reuse this project in the Xamarin project. Specially MyClientQueries which has got HttpClientFactory dependency.
It will depend on what DI you are using as to what the syntax will be, but generally that sort of code will be in the App constructor or a method called by the constructor.
So for example you might have a method called RegisterServices in your App.xaml.cs that is called from the constructor, after the Xamarin Init() has been called.
Inside that RegisterServices Method you would have something like this:
FreshIOC.Container.Register<IHttpManager, HttpManager>().AsSingleton();
We use FreshMvvm which uses TinyIOC for Dependancy Injection, so the syntax may differ, but the concept will be the same.

How to use an interface

I'm trying to build my first xamarin app, which I'm building using forms. One of the features of the app is sending users locations and have to do that even if the app is in the background. So I came across James Montemagno's GeolocatorPlugin, which promised to do just that.
As the documentation was not that clear on how to implement his plugin in the background I looked through the projects closed issues and found a guy which gave an example of a simple case of using the plugin with a service. (https://github.com/jamesmontemagno/GeolocatorPlugin/issues/272)
I've adopted the code and created the service. The service are using an interface to start the service and now my problem is how to make use of the interface to make the service run.
In my shared project I put the interface and the viewmodel and in xamarin.android project I put the service.
The interface - IGeolocationBackgroundService:
public interface IGeolocationBackgroundService {
void StartService();
void StartTracking();
}
The viewmodel - GeolocatorPageViewModel:
public class GeolocatorPageViewModel
{
public Position _currentUserPosition { get; set; }
public string CoordinatesString { get; set; }
public List<string> userPositions { get; set; }
public ICommand StartTrackingCommand => new Command(async () =>
{
if (CrossGeolocator.Current.IsListening)
{
await CrossGeolocator.Current.StopListeningAsync();
}
CrossGeolocator.Current.DesiredAccuracy = 25;
CrossGeolocator.Current.PositionChanged += Geolocator_PositionChanged;
await CrossGeolocator.Current.StartListeningAsync(
TimeSpan.FromSeconds(3), 5);
});
private void Geolocator_PositionChanged(object sender, PositionEventArgs e)
{
var position = e.Position;
_currentUserPosition = position;
var positionString = $"Latitude: {position.Latitude}, Longitude: {position.Longitude}";
CoordinatesString = positionString;
Device.BeginInvokeOnMainThread(() => CoordinatesString = positionString);
userPositions.Add(positionString);
Debug.WriteLine($"Position changed event. User position: {CoordinatesString}");
}
}
The service - GeolocationService:
[assembly: Xamarin.Forms.Dependency(typeof(GeolocationService))]
namespace MyApp.Droid.Services
{
[Service]
public class GeolocationService : Service, IGeolocationBackgroundService
{
Context context;
private static readonly string CHANNEL_ID = "geolocationServiceChannel";
public GeolocatorPageViewModel ViewModel { get; private set; }
public override IBinder OnBind(Intent intent)
{
return null;
}
public GeolocationService(Context context)
{
this.context = context;
CreateNotificationChannel();
}
private void CreateNotificationChannel()
{
NotificationChannel serviceChannel = new NotificationChannel(CHANNEL_ID,
"GeolocationService", Android.App.NotificationImportance.Default);
NotificationManager manager = context.GetSystemService(Context.NotificationService) as NotificationManager;
manager.CreateNotificationChannel(serviceChannel);
}
//[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
var newIntent = new Intent(this, typeof(MainActivity));
newIntent.AddFlags(ActivityFlags.ClearTop);
newIntent.AddFlags(ActivityFlags.SingleTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, newIntent, 0);
var builder = new Notification.Builder(this, CHANNEL_ID);
var notification = builder.SetContentIntent(pendingIntent)
.SetSmallIcon(Resource.Drawable.ic_media_play_light)
.SetAutoCancel(false)
.SetTicker("Locator is recording")
.SetContentTitle("GeolocationService")
.SetContentText("Geolocator is recording for position changes.")
.Build();
StartForeground(112, notification);
//ViewModel = new GeolocatorPageViewModel();
return StartCommandResult.Sticky;
}
public void StartService()
=> context.StartService(new Intent(context, typeof(GeolocationService)));
public void StartTracking()
{
ViewModel = new GeolocatorPageViewModel();
ViewModel.StartTrackingCommand.Execute(null);
}
}
}
So be clear, I need to start the service and I'm not used to interfaces, so how do I call the interface?
use DependencyService to get a reference to your service and then start it
var svc = DependencyService.Get<IGeolocationBackgroundService>();
svc.StartService();
svc.StartTracking();

android media picker from Xamarin forms

I am writting an application with xamarin forms for iOS and Android.
I want to pick a photo from image gallery.
I have created an android specific static helper:
var i = new Intent();
i.SetType("*/*");
Forms.Context.StartActivity(Intent.CreateChooser(i, ""));
But i have no way to get the selected picture bytes.
I have seen on android tutorials i should implement onActivityResult, but i am not on an activity, this is a specific static call...
Thanks
Via a Form's dependency service:
Create your dependency interface (IMediaPicker)
Create a Activity subclass (MediaChooserActivityProxy) that will act as your Intent.ActionPick proxy
In your Xamarin.Android implementation of the IMediaPicker, use a AutoResetEvent to convert the Android StartActivityForResult / OnActivityResult callback to an await-able synchronous flow.
Dependency Service Interace:
public interface IMediaPicker
{
Task<string> ChooseAFileAsync();
}
Android Dependency Implementation:
public class MediaPicker : IMediaPicker
{
public static string filePickedPath;
public static AutoResetEvent waitHandle;
public async Task<string> ChooseAFileAsync()
{
waitHandle = new AutoResetEvent(false);
filePickedPath = "";
Forms.Context.StartActivity(typeof(MediaChooserActivityProxy));
await Task.Run(() => waitHandle.WaitOne());
return filePickedPath;
}
}
The Proxy/Pseudo Activity to capture OnActivityResult:
public class MediaChooserActivityProxy : Activity
{
const string mimeType = "image/*";
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
var intent = new Intent(Intent.ActionPick);
intent.SetType(mimeType);
if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
{
intent.PutExtra(Intent.ExtraMimeTypes, mimeType);
}
StartActivityForResult(Intent.CreateChooser(intent, "StackOverflow"), 73);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if (requestCode == 73)
if (resultCode == Result.Ok)
{
string[] filePathColumn = { MediaStore.Images.ImageColumns.Data };
var cursor = ContentResolver.Query(data.Data, filePathColumn, null, null, null);
cursor.MoveToFirst();
var colummIndex = cursor.GetColumnIndex(filePathColumn[0]);
MediaPicker.filePickedPath = cursor.GetString(colummIndex);
}
MediaPicker.waitHandle.Set();
Finish();
}
}
Note: This can be implemented on the MainActivity/FormsAppCompatActivity to avoid this additional Activity if desired...
Usage:
var filePath = await DependencyService.Get<IMediaPicker>().ChooseAFileAsync();
System.Diagnostics.Debug.WriteLine(filePath);

Query Firebase Value OnChange

I am trying to register a Xamarin app to listen for value changes on a particular value change. I can access the value, but for some reason when I listen to the value change, it will fire once but never again until I reboot.
I am using Xamarin Studio, and FireSharp libraries. This is the API code in the C# library portion of the app. The reason I have removed the delegate was to check if it wasn't the delegate being cleaned up after the first call or something.
public class ValueAPI
{
private IFirebaseClient _client;
private ITemperatureListener _listener;
private EventStreamResponse _response;
public ValueAPI()
{
IFirebaseConfig config = new FirebaseConfig
{
AuthSecret = "...",
BasePath = "https://[value-api].firebaseio.com/"
};
_client = new FirebaseClient(config);
}
public async Task<string> getValue()
{
FirebaseResponse response = await _client.GetAsync("VALUE");
return response.Body;
}
public async Task<string> registerForUpdates(IValueListener listener)
{
_listener = listener;
_response = await _client.OnAsync("VALUE", null, this.OnValueChange, null, null);
return _response.ToString();
}
private void OnValueChange(object sender, ValueChangedEventArgs args, object context)
{
if (_listener != null)
{
_listener.OnValueUpdated(args.Data);
}
}
}
ANDROID CODE
ValueAPI api = new ValueAPI();
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
getValue();
getValueUpdates();
}
private async void getValue()
{
Task<string> task = api.getValue();
string result = await task;
TextView label = (TextView)FindViewById(Resource.Id.label_value);
label.Text = result;
}
private async void getValueUpdates()
{
Task<string> task = api.registerForUpdates(this);
await task;
}
public void OnValueUpdated(string value)
{
TextView label = (TextView)FindViewById(Resource.Id.label_value_updated);
label.Text = value;
}

Resources