I save some preferences in Application.Current.Properties for my Xamarin.Forms app and have a BroadcastReceiver that needs those preferences. The problem is that Xamarin.Forms is not initialized in the BroadcastReceiver so I need a way to access those variables in native Xamain.Android. Is that possible?
I need a way to access those variables in native Xamain.Android
you could use DependencyService ,this functionality enables Xamarin.Forms apps to do anything that a native app can do.
here is a sample for using Android's Toast to show message in shared code:
1.create a interface in shared code:
public interface IToast
{
void LongAlert(string message);//the parameter could pass to the native platform
void ShortAlert(string message);
}
2.Android Implementation:
[assembly: Dependency(typeof(ToastAndroid))]
namespace Demo.Droid
{
class ToastAndroid : IToast
{
public void LongAlert(string message)
{
Toast.MakeText(Android.App.Application.Context, message, ToastLength.Long).Show();
}
public void ShortAlert(string message)
{
Toast.MakeText(Android.App.Application.Context, message, ToastLength.Short).Show();
}
}
}
3.call in shared code:
DependencyService.Get<IToast>().ShortAlert("short toast);
DependencyService.Get<IToast>().LongAlert("long toast);
DependencyService
I found the solution myself.
I used the Xamarin.Forms source (found here) to write a function that reads the values from the same file as they are written to in the forms app. Works like a charm.
Related
I am trying to create a plugin using .Net Maui class libraries that use native Android and iOS code. I tried using the Dependency Service using the Interface, but I get a null point when I try to Register the Interface.
Shared code
public class MyPlugin
{
public static DisplayModel()
{
IDeviceOrientationService service =
DependencyService.Get<IMyPlugin>();
var model = service.GetDeviceName();
}
}
public Interface IMyPlugin
{
string GetDeviceName();
}
Android platform
[assembly: Dependency(typeof(MyPlugin))]
public class MyPlugin:IMyPlugin
{
public string GetDeviceName
{
return "From Android";
}
}
This is just an example. Please help me understand if I am not going about this the correct way and any suggestion would be appreciated.
We are working on service which collect data from AWS SQS then send batch to client. We are using mediator to publish notifications. The diagram of program looks like:
The problem is in first NotificationHandler from Mediatr.
private readonly EventCollectorHostedService _collector;
public CollectIncomingEventNotificationHandler(EventCollectorHostedService collector)
{
_collector = collector;
}
Class EventCollectorHostedService is register after Mediator so is not visible during registering this NotificationHandler and additionally it use Mediator to publish notification that batch is ready to send.
The error is that cannot construct CollectIncomingEventNotificationHandler because -> Unable to resolve service for type 'Api.Services.HostedServices.EventCollectorHostedService'.
services.AddMediatR(typeof(Startup).GetTypeInfo().Assembly);
services.AddHostedService<EventCollectorHostedService>();
The ugly solution is to declare some functionality in EventCollectorHostedService as static or instead of injecting EventCollectorHostedService, inject IServiceProvider.
But these solution don't look clean for me so do you have any other better solution ?
Thanks in advance.
Maybe someone encountered with similar problem so finally i have a brilliant solution.
Background services have to be treat like separate microservices based on event driven architecture so we have to make internal message broker mechanism.
The very simple solution which cover my case is:
public class NotificationChannel : INotificationChannel
{
public event EventHandler<IncomingEventNotificataionEventArgs> IncomingEventReceived;
public void Publish<T>(T notification)
{
if(notification is IncomingEventNotification incomingEventNotification)
{
OnIncomingEventReceived(incomingEventNotification);
}
}
protected virtual void OnIncomingEventReceived(IncomingEventNotification notification)
{
if(IncomingEventReceived != null)
{
var args = new IncomingEventNotificataionEventArgs(notification);
IncomingEventReceived(this, args);
}
}
}
Within my Blazor app, I am trying to determine the amount of available storage of the Android or iOS device.
I have found an answer here that is relevant:
Can I check available storage in the Device on Xamarin Forms?
As per the accepted answer, I have implemented the following:
Defined the Interface in the Blazor project
public interface IStorage
{
double GetRemainingStorage();
}
Created a class in the Android project
[assembly: Xamarin.Forms.Dependency(typeof(AndroidStorageManager))]
namespace MyApp.Android
{
public class AndroidStorageManager : IStorage
{
public double GetRemainingStorage()
{
var freeSpace = Android.OS.Environment.ExternalStorageDirectory.UsableSpace;
return freeSpace;
}
}
}
Created a class in the iOS project
[assembly: Xamarin.Forms.Dependency(typeof(iOSStorageManager))]
namespace MyApp.iOS
{
public class iOSStorageManager : IStorage
{
public double GetRemainingStorage()
{
var freeSpace = NSFileManager.DefaultManager.GetFileSystemAttributes(Environment.GetFolderPath(Environment.SpecialFolder.Personal)).FreeSize;
return freeSpace;
}
}
}
The Issue
Despite the definition of the dependencies with the [assembly: attribute, when my Blazor app tries to inject an instance of IStorage, it throws an exception:
Unable to resolve service for type 'IStorage' ...
Of course, I'm not able to define the dependency injection for IService in the Blazor app on startup, since the AndroidStorageManager and iOSStorageManager are defined in the device-specific projects which are already referencing the Blazor app.
I suspect I need some other way of defining the DI for IService in the Android and iOS apps, but can't see how to do it.
Can anyone offer any advice?
I seem to have found a workaround for my immediate issue, but I'm sure there must be a more elegant solution.
The exception was being generated when trying to inject an instance of the class associated with the IStorage interface.
public class MyManager {
private IStorage _storage;
public MyManager(IStorage storage) {
_storage = storage;
}
...
}
Rather then relying on the Blazor DI to resolve this dependency, I was able to use Xamarin.Forms DI to inject an instance manually, as follows:
public class MyManager {
private IStorage _storage;
public MyManager() {
_storage = DependencyService.Get<IStorage>();
}
...
}
I'm assuming that my original code wasn't working because Xamarin.Forms and Blazor are not sharing the same DI container. I'm not sure.
I'd be interested in anyone that can suggest a more elegant solution.
I'm working on a Xamarin Forms app and am trying to open the the default mail client directly to the Inbox.
I'm able to open and pass data through to compose a message using XF Essentials
Email.ComposeAsync(message);
But I would like the app to open the default mail app's Inbox on a button press. Is this possible in Xamarin Forms?
I think Dependency Service is what you need.
Create an interface in your Forms project:
public interface IOpenManager
{
void openMail();
}
Then implement it on each platform, for iOS:
[assembly: Dependency(typeof(OpenImplementation))]
namespace Demo.iOS
{
public class OpenImplementation : IOpenManager
{
public void openMail()
{
NSUrl mailUrl = new NSUrl("message://");
if (UIApplication.SharedApplication.CanOpenUrl(mailUrl))
{
UIApplication.SharedApplication.OpenUrl(mailUrl);
}
}
}
}
For Android:
[assembly: Dependency(typeof(OpenImplementation))]
namespace Demo.Droid
{
public class OpenImplementation : IOpenManager
{
public void openMail()
{
Intent intent = new Intent(Intent.ActionMain);
intent.AddCategory(Intent.CategoryAppEmail);
Android.App.Application.Context.StartActivity(intent);
}
}
}
At last, call this dependcy via: DependencyService.Get<IOpenManager>().openMail();
I am using a portable project so do not have direct access to native code.
I have an interface in my project that allows me to access native objects in the Android/iOS projects. We use this primarily for playing audio.
Android, for example, has things like
Window w = new Window();
w.SetFlags(WindowManagerFlags.Fullscreen, WindowManagerFlags.KeepScreenOn);
However the main issue would be accessing a Window object. I could pass a Xamarin.Forms.Page object to the native code, but there would be no way (I don't think) to cast it to a native Android Window object to access the flags.
Is there a way to do this with a portable project?
You can't do this without platform specific services or renderers. A portable project will have to call platform specific code in order to achieve this.
From that platform specific code, either as a DependencyService or Renderer, you can access the Window object through the Forms.Context. The Forms.Context is your Android Activity, through which you can reach the Window object.
On Android it works like this:
Android.Views.Window window = (Forms.Context as Activity).Window;
window.SetFlags(WindowManagerFlags.KeepScreenOn);
On iOS you can try this (Apple docs):
UIApplication.SharedApplication.IdleTimerDisabled = true;
Now there is a plugin doing exactly what Tim wrote
https://learn.microsoft.com/en-us/xamarin/essentials/screen-lock
simple source code is here
https://github.com/xamarin/Essentials/blob/main/Samples/Samples/ViewModel/KeepScreenOnViewModel.cs
using System.Windows.Input;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace Samples.ViewModel
{
public class KeepScreenOnViewModel : BaseViewModel
{
public KeepScreenOnViewModel()
{
RequestActiveCommand = new Command(OnRequestActive);
RequestReleaseCommand = new Command(OnRequestRelease);
}
public bool IsActive => DeviceDisplay.KeepScreenOn;
public ICommand RequestActiveCommand { get; }
public ICommand RequestReleaseCommand { get; }
void OnRequestActive()
{
DeviceDisplay.KeepScreenOn = true;
OnPropertyChanged(nameof(IsActive));
}
void OnRequestRelease()
{
DeviceDisplay.KeepScreenOn = false;
OnPropertyChanged(nameof(IsActive));
}
}
}
For Xamarin Forms Android.
Renders file I included below code
Window window = (Forms.Context as Activity).Window;
window.AddFlags(WindowManagerFlags.KeepScreenOn);