Using gps location system services dependency injection - c#

I am looking to get platform specific location details using Xamarin's dependency injection but running into issues. More than likely from doing it wrong.
Here is my current setup:
nuMaps/Interfaces/ILocationService.cs
using Xamarin.Forms.Maps;
namespace nuMaps
{
public interface ILocationService
{
void initLocationService();
Position getCurrentPosition();
}
}
nuMaps/nuMaps.Droid/Interfaces/LocationService.cs
using System;
using nuMaps;
using nuMaps.Droid;
using Xamarin.Forms.Maps;
using Android.App;
using Android.Gms.Common;
using Android.Gms.Common.Apis;
using Android.Gms.Location;
using Android.Locations;
using Android.Widget;
[assembly: Xamarin.Forms.Dependency (typeof (LocationService))]
namespace nuMaps.Droid
{
public class LocationService : Java.Lang.Object, ILocationService, IGoogleApiClientConnectionCallbacks, IGoogleApiClientOnConnectionFailedListener, Android.Gms.Location.ILocationListener
{
readonly IGoogleApiClient _googleApiClient;
readonly LocationRequest _locRequest;
Position _currentPosition;
Location _currentLocation;
public LocationService()
{
Console.WriteLine ("LocationService constructor");
_currentPosition = new Position (0, 0);
_googleApiClient = new GoogleApiClientBuilder (Xamarin.Forms.Forms.Context)
.AddApi (LocationServices.Api)
.AddConnectionCallbacks (this)
.AddOnConnectionFailedListener (this)
.Build ();
_locRequest = new LocationRequest ();
}
#region ILocationService implementation
public void initLocationService()
{
_googleApiClient.Connect ();
}
public Position getCurrentPosition ()
{
if (_googleApiClient.IsConnected) {
_currentLocation = LocationServices.FusedLocationApi.GetLastLocation (_googleApiClient);
_currentPosition = new Position (_currentLocation.Latitude, _currentLocation.Longitude);
}
_googleApiClient.Disconnect ();
return new Position (_currentLocation.Latitude, _currentLocation.Longitude);
}
#endregion
public void OnLocationChanged(Location l)
{
Console.WriteLine ("OnLocationChanged");
_currentLocation = l;
}
public void OnConnectionFailed (ConnectionResult result)
{
Console.WriteLine ("ConnectionFailed");
}
#region IGoogleApiClientConnectionCallbacks implementation
public void OnConnected (Android.OS.Bundle connectionHint)
{
Console.WriteLine ("OnConnected");
if (!_googleApiClient.IsConnected)
LocationServices.FusedLocationApi.RequestLocationUpdates (_googleApiClient, _locRequest, this);
_currentLocation = LocationServices.FusedLocationApi.GetLastLocation (_googleApiClient);
}
public void OnConnectionSuspended (int cause)
{
Console.WriteLine ("OnConnectionSuspended");
}
#endregion
}
}
Usage in nuMaps/Views/MapPage.xaml.cs
using Xamarin.Forms;
using Xamarin.Forms.Maps;
using System;
using System.Diagnostics;
namespace nuMaps
{
public partial class MapPage : ContentPage
{
public MapPage ()
{
InitializeComponent ();
Position p = DependencyService.Get<ILocationService>().getCurrentPosition();
MyMap.MoveToRegion (
MapSpan.FromCenterAndRadius (
p, Distance.FromMiles (1)
)
);
}
}
}
nuMaps/Views/Loginpage.xaml.cs
using System;
using Xamarin.Forms;
using nuMaps;
namespace nuMaps
{
public partial class LoginPage : ContentPage
{
public LoginPage ()
{
InitializeComponent ();
/*
* Init platform specific location service.
* TODO: This *shouldn't* need to happen, but we don't get location information until
* the OnConnected callback happens which is too slow to put in getCurrentLocation method.
*/
DependencyService.Get<ILocationService>().initLocationService();
}
}
}

I believe the problem is in your implementation of ILocationService.
I would remove implementing activity (why do you want to use OnCreate?) and take a look at http://developer.xamarin.com/guides/android/platform_features/maps_and_location/location/.
I'd recommend on Android getting the GPS location via the google play apis, which will require implementing ILocationListener, IGoogleApiClientConnectionCallbacks, and IGoogleApiClientOnConnectionFailedListener. Hope that helps!
Edit for comments:
If the LocationService in the question is up to date, I don't see that you're implementing IGoogleApiClientConnectionCallbacks or ILocationListener. It may be that because the mappage is using gps, GetLastKnownLocation works, because a location has recently been obtained.
GPS location requesting is an async operation - one of the methods with IGoogleApiClientConnectionCallbacks is OnConnected, where you should call something like:
LocationServices.FusedLocationApi.RequestLocationUpdates(googleApiClient, locationRequest, this);
This will actively request location updates, and then fire OnLocationChanged(Location location) when an location update is returned. This is all async, so you'll likely need to expose these events in your LocationService and subscribe to them.

You could give TinyIoC a try instead of the Xamarin Dependency Service. It works well for platform-specific implementations, like location services.
Maybe the Xamarin Dependency Service can be used for these types of things, but I've only used it for custom Renderers so far. For more service-based stuff, I use TinyIoC.

Related

Xamarin Forms - Implementing Singleton

I am actually trying to archive global variables in Xamarin where any page can consume it. After a lot of research, looks like the best way to archive such thing is using the Singleton design pattern. I am facing difficulty to implement this. take a look...
global.cs
using System;
namespace xamarin_forms
{
sealed class Global
{
public string test { get; set; }
private static Global _instance = null;
private Global()
{
}
static internal Global Instance()
{
if (_instance == null)
{
_instance = new Global();
}
return _instance;
}
}
}
App.xaml.cs
using Xamarin.Forms;
namespace xamarin_forms
{
public partial class App : Application
{
Global global = Global.Instance();
public App()
{
InitializeComponent();
MainPage = new PageWelcome();
global.test = "123";
}
protected override void OnStart()
{
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}
Ok, so far, I just created my singleton class with a simple test property. I set this to 123 when I initialize my application.
Now, on another page, welcome page...I'd like to read the value that I set previously on the initialization...
PageWelcome.xaml.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace xamarin_forms
{
public partial class PageWelcome : ContentPage
{
public PageWelcome()
{
InitializeComponent();
Global global = Global.Instance();
DisplayAlert("Alert", global.test, "OK");
}
}
}
Actually this is not working. It's returns me a null. So, how to use this correctly ? Thanks !
In your App's constructor, you first create an instance of PageWelcome. This instance reads the test property of your Global singleton and displays its contents in an alert. At this point, no value has been assigned to that property as far as I can see.
It is only after the PageWelcome constructor finishes that you actually assign a value to the test property of your singleton. Change your App constructor to
public App()
{
InitializeComponent();
global.test = "123";
MainPage = new PageWelcome();
}
and it should work as expected.
You don't need a Singleton.
Just create a static class with your variables static and you would be able to use them on any Page, like you want global variables.
// 1. Create static class Global with string _Test
public static class Global
{
public static void Init()
{
// your init class
}
private static string _Test { get; set; }
public static string Test
{
get => return _Test;
set => _Test = value;
}
}
// 2. Init Global in your App.cs
public App()
{
Global.Init();
}
// 3. Then use them on any page
public PageWelcome()
{
Global.Test = "123";
}

Avoid Dependency cycles in Dependency Injection

C# Windows Form application.
I have an Hub and a class. Both should reference each other.
This is because:
from the hub I need to call the class' methods
from the class I need to retrieve my Hub
Right now I'm able to do the first point using Autofac:
using Autofac;
using Autofac.Integration.SignalR;
using Microsoft.AspNet.SignalR;
using Microsoft.Owin.Cors;
using Microsoft.Owin.Hosting;
using Owin;
using MyProject.Classes;
using System;
using System.Reflection;
using System.Windows.Forms;
namespace MyProject
{
static class Program
{
static IDisposable webApp;
[STAThread]
static void Main()
{
string url = "http://localhost:8080";
webApp = WebApp.Start(url);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Engine());
}
}
class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
var builder = new ContainerBuilder();
var config = new HubConfiguration();
builder.RegisterHubs(Assembly.GetExecutingAssembly()).PropertiesAutowired();
builder.RegisterType<Erp>().PropertiesAutowired().InstancePerLifetimeScope();
var container = builder.Build();
config.Resolver = new AutofacDependencyResolver(container);
app.UseAutofacMiddleware(container);
app.MapSignalR(config);
}
}
}
here the Hub:
using Microsoft.AspNet.SignalR;
using MyProject.Classes;
using System;
using System.Threading.Tasks;
namespace MyProject.Hubs
{
public class LiveHub : Hub
{
private readonly Erp _erp;
public LiveHub(Erp erp)
{
_erp = erp;
}
public override Task OnConnected()
{
_erp.someMethod();
return base.OnConnected();
}
}
}
and here Erp.cs:
public class Erp
{
public Erp()
{
}
public void SomeMethod()
{
// do something
}
public void SomeOtherMethod()
{
// usually I do:
var hub = GlobalHost.ConnectionManager.GetHubContext<LiveHub>();
hub.Clients.All.foo();
}
}
but here I read:
A common error in OWIN integration is use of the GlobalHost. In OWIN you create the configuration from scratch. You should not reference GlobalHost anywhere when using the OWIN integration. Microsoft has documentation about this and other IoC integration concerns here.
If I cannot use the "old" method, how should I retrieve my Hub?
I tried to add another DI in Erp for LiveHub but it doesn't work. In my form I create an instance of Erp:
public partial class Engine : Form
{
private Erp _erp = new Erp();
public Engine()
{
InitializeComponent();
}
}
if I add that DI the declaration will be impossible because I need to define the LiveHub in constructor, that requires itself the Erp parameter...
What am I not seeing?
You can decouple the Hub from the object (Erp in your case) by emitting events.
namespace MyProject.Hubs
{
public class LiveHub : Hub
{
public event Action SomethingHappened;
public override Task OnConnected()
{
SomethingHappened?.Invoke();
return base.OnConnected();
}
}
}
Now you can connect the Erp without the Hub having to know it. You will have to subscribe to the event somewhere else in your code. But the circular reference is broken.
To decouple Engine from Form you could do something like this:
public partial class EngineForm : Form
{
public EngineForm()
{
InitializeComponent();
}
}
public class Engine
{
public Engine(EngineForm form, Erp erp)
{
this.form = form;
this.erp = erp;
}
// Here is where you'll write some code to coordinate
// communication between the Erp and the EngineForm.
//
// The main advantage is that you can inject the Erp
// and have it preconfigured.
}

Xamarin ContentObserver

trying to get a content observer to work in Xamarin Android.
It's not currently being executed when an event happens.
I tried converting from a java example and fixed it up for c#
I know there is not code to implement the event mod yet, trying to see it get caught first then will add the code.
This is the observer:
using System;
using Android.Database;
using Android.OS;
using Android.Content;
namespace FileTest
{
public class MyObserver : ContentObserver
{
private readonly Android.Net.Uri _uri;
public MyObserver(Android.Net.Uri uri): base(null)
{
_uri = uri;
}
public override void OnChange(bool selfChange) {
this.OnChange (selfChange, null);
Console.WriteLine ("change that I caught " );
}
// public override void OnChange(bool selfChange, Uri uri) {
//
// Console.WriteLine ("change that I caught " );
//
//}
}
}
this is the call:
ContentObserver observer = new MyObserver(Android.Net.Uri.Parse("content://com.Test/databases/Data.db"));
// TODO Auto-generated method stub
var a = Android.Net.Uri.Parse ("content://com.Test/databases/Data.db");
ContentResolver.RegisterContentObserver(a, false, observer);

Ninject. Binding all interfaces to the same class in singleton scope

I want one class to be both an object, which provides information about the backend, and a class, which a backend informs when servers goes down (e.g. ZooKeeper or WCF).
The problem is when I bind the same class to two different interfaces in in singleton scope, Ninject creates either two instances or throws an error, depending on how I do it.
The following example must print the same Guid and all interfaces must be bound.
Example:
Program.cs
using System;
using Ninject;
using Ninject.Modules;
namespace ConsoleApplication1
{
static class Program
{
static void Main(string[] args)
{
IKernel kernel = new StandardKernel();
kernel.Load(new INinjectModule[] { new Bindings() });
Console.WriteLine("First interface");
var i1 = kernel.Get<IState>();
i1.Inform();
Console.WriteLine("Second interface");
var i2 = kernel.Get<IListener>();
i2.Send();
Console.ReadKey();
}
}
}
IListener.cs
namespace ConsoleApplication1
{
public interface IListener
{
void Send();
}
}
IState.cs
namespace ConsoleApplication1
{
public interface IState
{
void Inform();
}
}
StateClass.cs
using System;
namespace ConsoleApplication1
{
public class StateClass : IState, IListener
{
private readonly String _seed;
public StateClass()
{
_seed = Guid.NewGuid().ToString();
}
public void Send()
{
Console.WriteLine(_seed);
}
public void Inform()
{
Console.WriteLine(_seed);
}
}
}
Bindings.cs - Version 1 In this example everything works, if the code is commented. The problem is I dont know in advance if a class impelments IState interface it will also IListener interface:
using Ninject.Modules;
using Ninject.Extensions.Conventions;
namespace ConsoleApplication1
{
class Bindings : NinjectModule
{
public override void Load()
{
Kernel.Bind(x => x
.FromAssemblyContaining<IState>()
.SelectAllClasses()
.InheritedFrom<IState>()
.BindAllInterfaces()
.Configure(y => y.InSingletonScope()));
//uncomment the following binding to see an exception
//problem is we dont know this in advance
//Kernel.Bind(x => x
// .FromAssemblyContaining<IListener>()
// .SelectAllClasses()
// .InheritedFrom<IListener>()
// .BindAllInterfaces()
// .Configure(y => y.InSingletonScope()));
}
}
}
Bindings.cs - Version 2 - no exception, but Application prints different Guids:
using Ninject.Modules;
using Ninject.Extensions.Conventions;
namespace ConsoleApplication1
{
class Bindings : NinjectModule
{
public override void Load()
{
Kernel.Bind<IListener>().To<StateClass>().InSingletonScope();
Kernel.Bind<IState>().To<StateClass>().InSingletonScope();
}
}
}
So I think in your module you're going to have to tell Ninject that both interfaces are using the same object. if you don't, Ninject will always assume that each interface has its own singleton.
class Bindings : NinjectModule
{
public override void Load()
{
Kernel.Bind<StateClass>().ToSelf().InSingletonScope();
Kernel.Bind<IListener>().ToMethod(ctx => ctx.Kernel.Get<StateClass>());
Kernel.Bind<IState>().ToMethod(ctx => ctx.Kernel.Get<StateClass>());
}
}

Selenium Nunit addin install exception

I don't have enough rep to comment, so I am posting a question here. I read this question Get list of failing tests from Nunit. I am trying to implement the nunit addin, I used this code:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;
using NUnit.Core.Extensibility;
namespace NunitAddin
{
[NUnitAddinAttribute(Type = ExtensionType.Core,
Name = "addin",
Description = "addin")]
public class NunitAddin : IAddin
{
public bool Install(IExtensionHost host)
{
IExtensionPoint listeners = host.GetExtensionPoint("EventListeners");
if (listeners == null)
return false;
listeners.Install(this);
return true;
}
public void TestStarted(NUnit.Core.TestName testName)
{
}
public void TestFinished(NUnit.Core.TestResult result)
{
}
public void RunStarted(NUnit.Core.TestName testName)
{
}
public void RunFinished(NUnit.Core.TestResult result)
{
}
public void UnhandledException(Exception exception)
{
}
public void TestOutput(NUnit.Core.TestOutput testOutput)
{
}
}
}
But when I call it using
var addin = new NunitAddin.NunitAddin();
var a = addin.Install(CoreExtensions.Host);
I get an error
NunitAddin.NunitAddin is not {0} extension point
on
listeners.Install(this);
Does anyone know how to solve this problem?
Never mind, issue solved. Just a stupid mistake, I had NunitAddin : IAddin instead of NunitAddin : IAddin; EventListener

Categories

Resources