I am monitoring the user's location every 15 minutes and I just want the application to continue sending the location even if the user closes the application in the taskbar.
I tried this sample but it's in Xamarin.Android https://learn.microsoft.com/en-us/xamarin/android/app-fundamentals/services/foreground-services i have to create a dependencyservice but i don't know how.
i have to create a dependencyservice but i don't know how.
First, create an Interface in the Xamarin.forms project:
public interface IStartService
{
void StartForegroundServiceCompat();
}
And then create a new file let's call it itstartServiceAndroid in xxx.Android project to implement the service you want:
[assembly: Dependency(typeof(startServiceAndroid))]
namespace DependencyServiceDemos.Droid
{
public class startServiceAndroid : IStartService
{
public void StartForegroundServiceCompat()
{
var intent = new Intent(MainActivity.Instance, typeof(myLocationService));
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
{
MainActivity.Instance.StartForegroundService(intent);
}
else
{
MainActivity.Instance.StartService(intent);
}
}
}
[Service]
public class myLocationService : Service
{
public override IBinder OnBind(Intent intent)
{
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
// Code not directly related to publishing the notification has been omitted for clarity.
// Normally, this method would hold the code to be run when the service is started.
//Write want you want to do here
}
}
}
Once you want to call the StartForegroundServiceCompat method in Xamarin.forms project, you can use:
public MainPage()
{
InitializeComponent();
//call method to start service, you can put this line everywhere you want to get start
DependencyService.Get<IStartService>().StartForegroundServiceCompat();
}
Here is the document about dependency-service
For iOS, if the user closes the application in the taskbar, you will no longer be able to run any service. If the app is running, you can read this document about ios-backgrounding-walkthroughs/location-walkthrough
You might want to have a look at Shiny by Allan Ritchie. It's currently in beta but I would still suggest using it, as it will save you a lot of trouble writing this code yourself. Here's a blog post by Allan, explaining what you can do with Shiny in terms of background tasks - I think Scheduled Jobs are the thing you're looking for.
Related
I'm trying to request a permission at runtime for my app. I use a service provider to talk between the portable class and Android.
I start by calling this code on button press in the PCL:
using (new Busy(this))
{
var locationHelper = scope.Resolve<ILocationHelper>();
locationHelper.GetLocation(this);
}
This calls my Android level service:
public class AndroidLocationHelper : ILocationHelper, ILocationListener
{
readonly string[] PermissionsLocation =
{
Manifest.Permission.AccessCoarseLocation
};
const int RequestLocationId = 0;
public void GetLocation(SearchViewModel viewModel)
{
try
{
const string permission = Manifest.Permission.AccessCoarseLocation;
if (((int)Build.VERSION.SdkInt < 23) || (CheckSelfPermission(permission) == Permission.Granted))
{
}
else
RequestPermissions(PermissionsLocation, RequestLocationId);
}
catch (Exception ex)
{
Debug.WriteLine("Error while getting Location service");
Debug.WriteLine(ex.Message);
Messaging.AlertUser("There was an error with determining your location");
}
}
However, I get two errors on CheckSelfPermission and RequestPermissions. These two methods are only available to activities. The code works fine in MainActivity; however, I want to ask for permissions when the user hits a button, not in OnCreate or OnResume, etc.
Thanks for any help.
In your Android project, You can use this and use the Dependency Service to call it in Xamarin.Forms PCL project later:
var thisActivity = Forms.Context as Activity;
ActivityCompat.RequestPermissions(thisActivity, new string[] {
Manifest.Permission.AccessFineLocation }, 1);
ActivityCompat.RequestPermissions(thisActivity,
new String[] { Manifest.Permission.AccessFineLocation },
1);
You can try with ContextCompat.CheckSelfPermission, passing the application context, like this:
ContextCompat.CheckSelfPermission(Android.App.Application.Context, permission)
Update
In case of ActivityCompat.RequestPermissions, which requires an activity reference, you can keep track of the current activity. There is a very handy lib for that, called "CurrentActivityPlugin". You can find at https://github.com/jamesmontemagno/CurrentActivityPlugin
Rafael came up with a solution but I found another option that is a lot less effort just using MessagingCenter. In the MainActivity's OnCreate add a receiver that runs all the location code, that way you have access to all of the activities methods (and there are a bunch of tutorials on doing location services in MainActivity). Then add the Send inside of your service (the class).
To expound Rafael Steil's answer, I tried the suggested CurrentActivityPlugin and it worked on me. In my case I am trying to execute a voice call which needs CALL_PHONE permission. Here is the code snippet in your case: I used the ContextCompat & ActivityCompat so that I don't need to check the VERSION.SdkInt
using Plugin.CurrentActivity;
public void GetLocation(SearchViewModel viewModel){
var context = CrossCurrentActivity.Current.AppContext;
var activity = CrossCurrentActivity.Current.Activity;
int YOUR_ASSIGNED_REQUEST_CODE = 9;
if (ContextCompat.CheckSelfPermission(context, Manifest.Permission.AccessCoarseLocation) == (int)Android.Content.PM.Permission.Granted)
{
//Permission is granted, execute stuff
}
else
{
ActivityCompat.RequestPermissions(activity, new string[] { Manifest.Permission.AccessCoarseLocation }, YOUR_ASSIGNED_REQUEST_CODE);
}
}
It's dead simple
public bool CheckPermission()
{
const string permission = Manifest.Permission.ReceiveSms;
return ContextCompat.CheckSelfPermission(Forms.Context, permission) == (int) Permission.Granted;
}
I am doing a windows service (call it SampleService), every is fine. When I started then stopped service through Windows Service Management Tool (service.msc), it run properly.
But my service will be request Start and Stop by another application. So I will not use Windows Service Management Tool in this case.
This is my service implement.
using System.ServiceProcess;
public partial class SampleService : ServiceBase
{
public SampleService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
this.WriteLog("OnStart");
// Doing start service logic down here
// Some service logic like create some files.
// Or just leave it empty like a brand new Windows Service.
}
protected override void OnStop()
{
this.WriteLog("OnStop");
// Doing clean service logic down here.
// Some service logic like: delete files.
// Or just leave it empty like a brand new Windows Service.
}
static readonly object synObject = new object();
public void WriteLog(string message)
{
if (string.IsNullOrEmpty(message))
{
return;
}
// Write log.
lock (synObject)
{
using (var wr = new StreamWriter(#"C:\logfile.txt", true))
{
wr.WriteLine(DateTime.Now + "-" + message);
}
}
}
}
And this is code logic use to Start and Stop service inside my another application. I can not modify this another application. The bellow source code simulate what happen.
class Program
{
static void Main(string[] args)
{
ServiceController sc = new ServiceController("SampleService");
// start service
sc.Start();
// doing some logic cost deltaTime or just stand by in deltaTime.
Thread.Sleep(deltaTime);
try
{
// stop service first time, nothing happen.
sc.Stop();
}
catch
{
}
try
{
// stop service second times, by dump people or apllication.
sc.Stop();
}
catch
{
// It got an exception here: "The service cannot accept control messages at this time".
// But the service did stopped.
}
}
}
The problem is:"When deltaTime is too short (bellow 3000ms with empty OnStart(), OnStop()), Service will stop incorrectly. The output log OnStop will never show up, that mean OnStop method did not called.
My service will doing clean up work in OnStop (like delete some file), but if it not be called, these files still there.
I cannot change logic of another application but I can change SampleService.
I want to ask:
Is this an Windows Service base issue and I cant do anything with it?
What ever it is, can I do clean up some where else?
Thank you!
I set up an alarm to show a corresponding Notification. The PendingIntent of the Notification is used to start the Gluon App main class. To show a View other than the homeView, I call switchView(otherView) in the postInit method. OtherView is shown, but without AppBar. While it's possible to make the AppBar appear, I wonder if this is the right approach.
#Override
public void postInit(Scene scene) {
// additional setUp logic
boolean showReadingView = (boolean) PlatformProvider.getPlatform().getLaunchIntentExtra("showReadingView", false);
if (showReadingView) {
switchView(READING_VIEW);
}
}
When triggering anything related to the JavaFX thread from another thread, we have to use Platform.runLater().
Yours is a clear case of this situation: the Android thread is calling some pending intent, and as a result, the app is started again.
This should be done:
#Override
public void postInit(Scene scene) {
// additional setUp logic
boolean showReadingView = (boolean) PlatformProvider.getPlatform().getLaunchIntentExtra("showReadingView", false);
if (showReadingView) {
Platform.runLater(() -> switchView(READING_VIEW));
}
}
I want to make a Xamarin.Forms project, targeting iOS, Android and Windows Phone.
My app needs to authenticate users using Facebook.
Should I implement login for each platform independently, or use a manual flow?
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.0
I prefer to have a single implementation of the login flow, and use it on all platforms.
How can I get a single implementaion of the Facebook login flow?
UPDATE (10/24/17): While this way of doing things was okay a few years ago, I now strongly advocate for using native UI for doing authentication, as opposed to the webview method shown here. Auth0 is a great way to accomplish native UI login for your apps, using a wide variety of identity providers:
https://auth0.com/docs/quickstart/native/xamarin
EDIT: I finally put a sample for this on Gihub
I posted an answer on the Xamarin Forums. I'll repeat it here.
Let's start with the core of the app, the Xamarin.Forms PCL project. Your App class will look something like this:
namespace OAuth2Demo.XForms
{
public class App
{
static NavigationPage _NavPage;
public static Page GetMainPage ()
{
var profilePage = new ProfilePage();
_NavPage = new NavigationPage(profilePage);
return _NavPage;
}
public static bool IsLoggedIn {
get { return !string.IsNullOrWhiteSpace(_Token); }
}
static string _Token;
public static string Token {
get { return _Token; }
}
public static void SaveToken(string token)
{
_Token = token;
}
public static Action SuccessfulLoginAction
{
get {
return new Action (() => {
_NavPage.Navigation.PopModalAsync();
});
}
}
}
}
The first thing to notice is the GetMainPage() method. This tells the app which screen it should load first upon launching.
We also have a simple property and method for storing the Token that is returned from the auth service, as well as a simple IsLoggedIn property.
There's an Action property as well; something I stuck in here in order to have a way for the platform implementations to perform a Xamarin.Forms navigation action. More on this later.
You'll also notice some red in your IDE because we haven't created the ProfilePage class yet. So, let's do that.
Create a very simple ProfilePage class in the Xamarin.Forms PCL project. We're not even going to do anything fancy with it because that will depend on your particular need. For the sake of simplicity in this sample, it will contain a single label:
namespace OAuth2Demo.XForms
{
public class ProfilePage : BaseContentPage
{
public ProfilePage ()
{
Content = new Label () {
Text = "Profile Page",
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
};
}
}
}
Again, you'll probably have some red in your IDE because we seem to be missing the BaseContentPage class. The sole purpose of the BaseContentPage class is to ensure that none of the app's screens can be displayed until the user has logged in. (In this simplified demo, we're just persisting the user info to memory, so you'll need to re-login every time the app is run. In a real-world app, you'd be storing the authenticated user info to the device's keychain, which would eliminate the need to login at each app start.)
Create a BaseContentPage class in the Xamarin.Forms PCL project:
namespace OAuth2Demo.XForms
{
public class BaseContentPage : ContentPage
{
protected override void OnAppearing ()
{
base.OnAppearing ();
if (!App.IsLoggedIn) {
Navigation.PushModalAsync(new LoginPage());
}
}
}
}
There's a few interesting things going on here:
We're overriding the OnAppearing() method, which is similar to the ViewWillAppear method in an iOS UIViewController. You can execute any code here that you'd like to have run immediately before the screen appears.
The only thing we're doing in this method is checking to see if the user is logged in. If they're not, then we perform a modal push to a class called LoginPage. If you're unfamiliar with the concept of a modal, it's simply a view that takes the user out of the normal application flow in order to perform some special task; in our case, to perform a login.
So, let's create the LoginPage class in the Xamarin.Forms PCL project:
namespace OAuth2Demo.XForms
{
public class LoginPage : ContentPage
{
}
}
Wait...why doesn't this class have a body???
Since we're using the Xamatin.Auth component (which does the job of building and presenting a web view that works with the provided OAuth2 info), we actually don't want any kind of implementation in our LoginPage class. I know that seems weird, but bear with me.
The LoginPageRenderer for iOS
Up until this point, we've been working solely in the Xamarin.Forms PCL project. But now we need to provide the platform-specific implementation of our LoginPage in the iOS project. This is where the concept of a Renderer comes in.
In Xamarin.Forms, when you want to provide platform-specific screens and controls (i.e. screens that do not derive their content from the abstract pages in the Xamarin.Forms PCL project), you do so with Renderers.
Create a LoginPageRenderer class in your iOS platform project:
[assembly: ExportRenderer (typeof (LoginPage), typeof (LoginPageRenderer))]
namespace OAuth2Demo.XForms.iOS
{
public class LoginPageRenderer : PageRenderer
{
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
var auth = new OAuth2Authenticator (
clientId: "", // your OAuth2 client id
scope: "", // the scopes for the particular API you're accessing, delimited by "+" symbols
authorizeUrl: new Uri (""), // the auth URL for the service
redirectUrl: new Uri ("")); // the redirect URL for the service
auth.Completed += (sender, eventArgs) => {
// We presented the UI, so it's up to us to dimiss it on iOS.
App.SuccessfulLoginAction.Invoke();
if (eventArgs.IsAuthenticated) {
// Use eventArgs.Account to do wonderful things
App.SaveToken(eventArgs.Account.Properties["access_token"]);
} else {
// The user cancelled
}
};
PresentViewController (auth.GetUI (), true, null);
}
}
}
}
There are important things to note:
The [assembly: ExportRenderer (typeof (LoginPage), typeof (LoginPageRenderer))] line at the top (and importantly before the namespace declaration) is using the Xamarin.Forms DependencyService. It's not the most beautiful thing in the world because it's not IoC/DI, but whatever...it works. This is the mechanism that "maps" our LoginPageRenderer to the LoginPage.
This is the class in which we're actually using the Xamarin.Auth component. That's where the OAuth2Authenticator reference comes from.
Once the login is successful, we fire off a Xamarin.Forms navigation via App.SuccessfulLoginAction.Invoke();. This gets us back to the ProfilePage.
Since we're on iOS, we're doing all of our logic sinde of the ViewDidAppear() method.
The LoginPageRenderer for Android
Create a LoginPageRenderer class in your Android platform project. (Note that class name you're creating is identical to the one in the iOS project, but here in the Android project the PageRenderer inherits from Android classes instead of iOS classes.)
[assembly: ExportRenderer (typeof (LoginPage), typeof (LoginPageRenderer))]
namespace OAuth2Demo.XForms.Android
{
public class LoginPageRenderer : PageRenderer
{
protected override void OnModelChanged (VisualElement oldModel, VisualElement newModel)
{
base.OnModelChanged (oldModel, newModel);
// this is a ViewGroup - so should be able to load an AXML file and FindView<>
var activity = this.Context as Activity;
var auth = new OAuth2Authenticator (
clientId: "", // your OAuth2 client id
scope: "", // the scopes for the particular API you're accessing, delimited by "+" symbols
authorizeUrl: new Uri (""), // the auth URL for the service
redirectUrl: new Uri ("")); // the redirect URL for the service
auth.Completed += (sender, eventArgs) => {
if (eventArgs.IsAuthenticated) {
App.SuccessfulLoginAction.Invoke();
// Use eventArgs.Account to do wonderful things
App.SaveToken(eventArgs.Account.Properties["access_token"]);
} else {
// The user cancelled
}
};
activity.StartActivity (auth.GetUI(activity));
}
}
}
Again, let's take a look at some interesting things:
The [assembly: ExportRenderer (typeof (LoginPage), typeof (LoginPageRenderer))] line at the top (and importantly before the namespace declaration) is using the Xamarin.Forms DependencyService. No difference here from the iOS version of LoginPageRenderer.
Again, this is where we're actually using the Xamarin.Auth component. That's where the OAuth2Authenticator reference comes from.
Just as with the iOS version, once the login is successful, we fire off a Xamarin.Forms navigation via App.SuccessfulLoginAction.Invoke();. This gets us back to the ProfilePage.
Unlike the iOS version, we're doing all of the logic inside of the OnModelChanged() method instead of the ViewDidAppear().
Here it is on iOS:
...and Android:
UPDATE:
I've also provided a detailed sample at my blog: http://www.joesauve.com/using-xamarin-auth-with-xamarin-forms/
You could consume either Xamarin.Social or Xamarin.Auth for that. It allows using the same api whatever the platform is.
As of now, those libs aren't PCL yet, but you still can consume them from a Shared Assets Project, or abstract the API you need in an interface and inject in with DependencyService or any other DI container.
I've created a sample project to show how to create a Facebook login using native Facebook component, not through a webview like the solutions suggested here.
You can check it out in this address:
https://github.com/IdoTene/XamarinFormsNativeFacebook
IOS 8: For those who are using #NovaJoe code and are stuck on view, add the code bellow to workaround:
bool hasShown;
public override void ViewDidAppear(bool animated)
{
if (!hasShown)
{
hasShown = true;
// the rest of #novaJoe code
}
}
Here's a good Xamarin.Forms authentication sample. The documentation in the code is nice. It uses a webview to render the login screen, but you can select what login type you want. It also saves a users token so he doesn't have to keep re-logging in.
https://github.com/rlingineni/Xamarin.Forms_Authentication
Another addition to #NovaJoe's code, on iOS8 with Facebook, you'd need to modify the Renderer class as below to close the View after successful authentication.
auth.Completed += (sender, eventArgs) => {
// We presented the UI, so it's up to us to dimiss it on iOS.
/*Importand to add this line */
DismissViewController (true, null);
/* */
if (eventArgs.IsAuthenticated) {
App.Instance.SuccessfulLoginAction.Invoke ();
// Use eventArgs.Account to do wonderful things
App.Instance.SaveToken (eventArgs.Account.Properties ["access_token"]);
} else {
// The user cancelled
}
};
The correct implementation for the Androids PageRenderer is:
using System;
using Android.App;
using Android.Content;
using OAuth2Demo.XForms.Android;
using Xamarin.Auth;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using XamarinAuth;
[assembly: ExportRenderer(typeof(LoginPage), typeof(LoginPageRenderer))]
namespace OAuth2Demo.XForms.Android
{
public class LoginPageRenderer : PageRenderer
{
public LoginPageRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
// this is a ViewGroup - so should be able to load an AXML file and FindView<>
var activity = this.Context as Activity;
var auth = new OAuth2Authenticator(
clientId: "<Constants.clientId>", // your OAuth2 client id
scope: "<Constants.scope>", // the scopes for the particular API you're accessing, delimited by "+" symbols
authorizeUrl: new Uri("<Constants.authorizeUrl>"), // the auth URL for the service
redirectUrl: new Uri("<Constants.redirectUrl>")); // the redirect URL for the service
auth.Completed += (sender, eventArgs) =>
{
if (eventArgs.IsAuthenticated)
{
App.SuccessfulLoginAction.Invoke();
// Use eventArgs.Account to do wonderful things
App.SaveToken(eventArgs.Account.Properties["access_token"]);
}
else
{
// The user cancelled
}
};
activity.StartActivity(auth.GetUI(activity));
}
}
}
I have searched the web for the correct answer, but I've been failing to achieve this :
In EntryPoint class, I need to manage widgets according to events that occur in nested widgets. I've cleaned of the code to focus only on what is important here.
I have built a few UiBinder widgets, for example, a Login pane where the user can enter his credentials. In my EntryPoint class, I add the widgets in the correct position.
// This is from EntryPoint class
public void onModuleLoad() {
LoginPane lp = new LoginPane();
RootPanel.get("headerRightPane").add(lp);
lp.setFocus();
// Other widgets added in same manner after this point...
}
I would like a successful login to remove the LoginPane and replace it by another widget (AccountPane) that would show the account information for the user that is logged in. I have an onClick event, in LoginPane, that sends a request to a fully functional Servlet that checks the credentials. At this exact point, if the Servlet determines that the login is indeed successful, I would like to fire a "successfulLogin" event (from LoginPane) that could notify the EntryPoint class that the LoginPane can now be replaced by the AccountPane.
// This is from LoginPane class
#UiHandler("loginButton")
void onClick(ClickEvent e) {
checkCredentials(usernameField.getText(), passwordField.getText());
}
public void checkCredentials(String username, String password) {
String usernameToServer = username;
String passwordToServer = password;
credentialsService.credentialsServer(usernameToServer, passwordToServer,
new AsyncCallback<CredentialsPaneContent>() {
public void onFailure(Throwable caught) {
answerLabel.setText(Utilities.SERVER_ERROR);
}
public void onSuccess(CredentialsPaneContent result) {
if ( result == null ) {
answerLabel.setText("Login Failed.");
} else {
// Fire event here (to be caught by EntryPoint class)
answerLabel.setText("Login Successful.");
}
}
});
}
So, the question : How should I proceed to create, fire and listen to the event from my nested widget?
Use an EventBus. Additionally, consider adopting the Model-View-Presenter pattern to keep your application maintainable as it grows:
Large scale application development and MVP, Part I
Large scale application development and MVP, Part II
GWT MVP Development with Activities and Places
Lets have an interface which is implemented by EntryPoint class,
now have a referrence of interface type which actually an object of interface.
Using this interface referrence invoke the listner(interface) mothod, which serves your purpose.