Hi guys I am aware that OnSuspending function in the app.xaml file allows me to save information should my app be suspended. I want my user to be automatically logged out in my database should the application be closed by the user. Here is my code:
private async void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
if (CommonVariables.LoggedIn)
{
CommonVariables.LoggedIn = false;
string jsonPayload = "{\"user_id\":\"" + CommonVariables.AuthenticateUserResponseDetails.user.id + "\"}";
HttpClient client = new HttpClient();
string postUrl = CommonVariables.SERVER + CommonVariables.LogOut;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, postUrl);
//encrypt tase
EDecrypt encrypt = new EDecrypt();
jsonPayload = encrypt.AES_Encrypt(jsonPayload, CommonVariables.EncryptionKey);
request.Content = new StreamContent(new MemoryStream(System.Text.Encoding.UTF8.GetBytes(jsonPayload)));
var result = await client.SendAsync(request);
string response = string.Empty;
}
//TODO: Save application state and stop any background activity
deferral.Complete();
}
This works for me well so when my user closes the app in their device the function is called and they are logged out. However my issue the function also logs the user out when they minimise the app or put the app to the background and go into another app. So how can I adjust my code so that my logout function only works when the user shuts down the app in their device?
When you minimize an app, there are actually 2 things happening:
Visibility change
App suspension
When the user switches from your app to another app, your app is no
longer visible but remains in the Running state until Windows suspends
it. If the user switches away from your app but activates or switches
back to it before it can suspended, the app remains in the Running
state.
So if a user minimizes/switches to another app and comes back fast enough, your app will keep running and your OnSuspending handler will never execute. While being in the background, the OS will determine when your app goes to suspension and when/if it is terminated (like Hans mentioned in his comment).
So to fix your problem: keep your OnSuspending handler, and handle the app resume event to log back in. This will cover both minimizing and closing.
Related
I have a method on the sample content page (sample.XAML.cs) and I want to set this method in a timer and run every 10 seconds even after the app is killed or closed.
public async void AlarmStart()
{
HttpClient client = new HttpClient();
HttpResponseMessage response = client.GetAsync("JsonFile").Result;
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<List<OnlineValueClass>>(content);
}
}
In Android, starting in Android 8.0 (API level 26), an Android application no longer have the ability to run freely in the background. For more details about this, you can refer to Background Execution Limits in Android 8.0:
But you can try to restarted the service once it been finished by a BroadcastReceiver . The function of BroadcastReceiver is to receive a signal when someone or something kills the service; its role is to restart the service.
For more details, you can check an relative article by entering key words
Creating a never ending background service in Android. Though it's Java code, it's easy to understand and convert
I am developing an application which is to run as a WinForms thick-client, accessing both an API to be running in the cloud (Azure), and a local SQL Server DB for data.
To allow users to log in, the login screen is triggered as a Modal prompt when the application starts up with the following code in the HomeScreen form which is the 'main' page of the application:
using (Form loginScreen = new LoginForm())
{
loginScreen.ShowDialog(this);
}
Once the login screen has been passed, the user can see the home screen, if they cancel it, the application closes. Once they get to the home screen, another API call is run to retrieve data about the user from the API for display on the home screen.
All API calls execute the same code, which is below (this is very early code for a 'working prototype' and I am aware there are probably issues with it that require a refactor, at this point I'm really only interested in understanding what is causing my call to PostAsJsonAsync to fail:
public async Task<ApiResponse> sendApiRequest(RequestDetail reqDet)
{
//create a variable to track if the action was done or we need to retry after a timeout and login
bool actionDone = false;
//instantiate a variable for the ApiResponse so it can be used later outside of the scope of the actionDone loop
ApiResponse res = null;
while (actionDone == false)
{
//populate the main SessionKey of the packet from the GlobalData var (for initial dev, to be refactored out)
reqDet.SessionKey = GlobalData.SessionKey;
//populate the SessionKey in the array underneath the main object (for future use)
reqDet.strParameters["SessionKey"] = GlobalData.SessionKey;
//instantiate a new ApiRequest object to hold the main request body
ApiRequest req = new ApiRequest("ClientRequest", reqDet);
//Create HttpClient class for communication with the server
HttpClient client = new HttpClient();
//Set URL and Headers (URL will be in a config file in future
client.BaseAddress = new Uri("https://removed.the.url.for.se/api/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
//actually call the service, wait for the response, and read it out into the response object
HttpResponseMessage response = await client.PostAsJsonAsync((string)req.requestBody.ApiLocation, req);
res = await response.Content.ReadAsAsync<ApiResponse>();
//check if the response was successful or we need to show an error
if (res.responseType == "Success")
{
//set action done to TRUE so we exit the loop
actionDone = true;
}
else
{
//Use the MessageService to dispaly the error
Error err = res.responseError;
MessagesService ms = new MessagesService();
await ms.displayErrorPrompt(err);
//trigger a login screen and restart the service call if the user's session has expired
if (err.ErrorText.Equals("Session has expired, please log in again"))
{
using (Form login = new LoginForm())
{
login.ShowDialog();
} // Dispose form
}
else
{
// set ActionDone to True if it's not a login error so we don't endlessly call the service
actionDone = true;
}
}
}
//return the final result
return res;
}
When running the entire stack locally, this all works perfectly, I can login and traverse the rest of my application as normal. When running the client locally in VS and the API in Azure, the first call to the Login API succeeds (I can call it multiple times e.g. with a wrong password and it behaves as normal), however the second call to get the user's data to paint on the home screen fails.If I put a breakpoint on the PostAsJsonAsync line, I can see that the line executes once and continues as normal, but immediately after stepping over the line the second time for the user details call, the entire application exits without executing the subsequent code.
What is strange about this is that it exits with a 0x0 return code, does not throw an exception, or in any way behave abnormally other than shutting down after just that line.
I have tried manually calling the APIs on the Azure service in Postman and they all return exactly the same (correct) results I get when running it locally, so I know it is not the deployment to the App Service that is the issue.
Things I have tried to fix it after Googling, reading other SE posts and looking at comments on this question
I have tried enabling first-chance exceptions in Visual Studio for all CLR exceptions. Nothing is caught or thrown that I can see.
Here is a screenshot of my settings in case I've done something wrong
I have tried wrapping just that line in a try-catch block that catches all exceptions. It still immediately stops executing after the PostAsJsonAsync and never reaches the Catch block
Adding the following code to my Program.cs file to catch unhandled exceptions (is never run when I put a breakpoint on it and nothing is written to the console that I can see):
static void Main()
{
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new HomeScreen());
}
static void MyHandler(object sender, UnhandledExceptionEventArgs args)
{
Exception e = (Exception)args.ExceptionObject;
Console.WriteLine("MyHandler caught : " + e.Message);
}
Setting a DumpFolder that is writable by all users, and a DumpType of 2 in a key named after my executable at Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\ - I've tried both keys named MyApplication and MyApplication.exe and neither results in a file being produced when the app crashes.
The Windows Event Viewer after the 'crash' (nothing from my application)
Reviewing the request/response in Fiddler - the first 'login' request and response is shown correctly, but the second is not shown at all, so it looks like it's crashing before even sending the request
I'd be extremely grateful for any suggestions you can provide, even if it is only a workaround or 'patch' to resolve the issue. It's extremely strange to me both that it exits the program with no exception and without running the subsequent code, that it only does this when the API piece is running in Azure, not when running locally, and finally that it's only when it gets to the subsequent request after the login.
Update
I have tried commenting out the line that runs the RefreshScreen() function to call the web service again and the application still exits in the same way after the login, but just without hitting my breakpoint a second time. However again only when the application is running against the Azure API and not locally. If I break at the last line of the HomeScreen constructor and keep stepping, it goes back to my Main() method and ends the application. Is there something I'm doing wrong here?
I think the PostAsJsonAsync may have been a red herring so have taken it out of the title.
public HomeScreen()
{
InitializeComponent();
if(GlobalData.SessionKey == null)
{
using (Form loginScreen = new LoginForm())
{
loginScreen.ShowDialog(this);
}
// Dispose form
}
refreshScreen();
}
public async Task refreshScreen()
{
ApiService srv = new ApiService();
ApiResponse res = await srv.sendApiRequest(new Sessions_GetUserDetailsRequest());
if (res.responseType == "Success")
{
foreach (dynamic usrItem in JsonConvert.DeserializeObject(res.responseContent))
{
lblUserName.Text = usrItem.UserGivenName + " " + usrItem.UserSurname;
lblSiteName.Text = usrItem.TenantName;
}
}
}
So after doing some research to answer the helpful comments on this question, I stumbled across the answer.
I have an event in the application that is designed to close the entire application if the user exits the login page without logging in, since otherwise it would return to the 'home screen' form in an invalid state. It contained the following code, designed to close the application if the user didn't have a token (i.e. had cancelled the page):
Because my login process is asynchronous (code above) when I was stepping through the process in VS, I was getting to the "PostAsJsonAsync" step, and it was closing the application without showing me it was running the 'on close' event. However, unknown to me when testing locally, the code had a race condition where it would jump ahead to the 'close form' bit while still awaiting the web service call, and therefore execute the following code:
private void DoOnFormClosing(object sender, FormClosingEventArgs e)
{
if(GlobalData.SessionKey == null || GlobalData.SessionExpiry <= DateTime.Now)
{
Application.Exit();
}
}
The solution was to remove this event as part of the login process, after the login had been validated, meaning this code would never be called if the user had successfully logged in.
HandleNotificationOpened is working perfectly if App is on the background or running but it's not fired if i open the notification when app is closed.
I've tried to persist the data from event with SecureStorage, because i'm not sure if the event run but in the wrong time or it doesn't run at all.
public App()
{
OneSignal.Current.StartInit("onesignal-id").HandleNotificationOpened(HandleNotificationOpened).HandleNotificationReceived(HandleNotificationReceived).EndInit();
}
private async void HandleNotificationOpened(OSNotificationOpenedResult result)
{
var data = result.notification.payload.additionalData;
if (data != null)
{
data.TryGetValue("Title", out object Title);
data.TryGetValue("Conteudo", out object Conteudo);
data.TryGetValue("Link", out object RLink);
string lastvar = (Title.ToString().GetHashCode() + Conteudo.ToString().GetHashCode() + RLink.ToString().GetHashCode()).ToString();
if (!ChecarDB(lastvar))
{
InserirDB(Title.ToString(), Conteudo.ToString(), RLink.ToString());
}
await SecureStorage.SetAsync("UrlFromPush", RLink.ToString());
var page = new MainPage();
MessagingCenter.Send<MainPage>(page, "MudarURL");
}
}
Expected result is the application properly handle the event, No error messages at all.
This method will not be called when app is closed.
Although I did not use OneSignal to push notifications , according to the Android/iOS system notification processing mechanism, when the app is closed, when the remote notification is received, the click notification will restart the app, and the notification processing mechanism is processed by the system tray.
So the HandleNotificationOpened method will not be called.
I've solved this issue using URI Scheme to access early init background data, replacing the HandleNotificationOpened method for Intent?.Data?.EncodedQuery; with custom formatting method, that's how i got the expected result.
I am building a Windows 8 application and I'm trying to get a token from the Google data API when the application is starting. I built a function to do this, and it has the following code:
string authCodeUrl = UrlHelpers.BaseUrlFactory(UrlType.OAuth) +
"?client_id=" + _clientId +
"&redirect_uri=" + _redirectUri +
"&response_type=code" +
"&scope=" + _scope;
Uri startUri = new Uri(authCodeUrl);
Uri endUri = new Uri("https://accounts.google.com/o/oauth2/approval?");
WebAuthenticationResult webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.UseTitle, startUri, endUri);
I'm calling the function in my App.xaml.cs OnLaunched(), before the Window.Current.Activate() call. The reason I am doing this is because I already need the token in my MainViewModel.
The strange thing is: when I launch my application the normal way (via Visual Studio) it gets stuck at the splashscreen (the splashscreen stays on for ages), but when I place a breakpoint on this line:
WebAuthenticationResult webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.UseTitle, startUri, endUri);
and step through it, I suddenly get a log in window that allows me to log in and obtain a token, meaning that the splashscreen goes away and I can use my application.
When I remove the call from my App.xaml.cs and just request the token from my ViewModel, I have the same problem: it's still stuck on the SplashScreen. I also have this problem when I do request a token from my App.xaml.cs but move the request after the Window.Current.Activate() call. But in these cases, the splashscreen goes away after logging in, but the screen stays black. I don't see my app.
P.s., this is how I request the token from my App.xaml.cs (OnLaunched is marked as async):
IOAuth2Service oAuth2Service = new OAuth2Service();
await oAuth2Service.GetToken();
OAuth2Service is just an object that has a method GetToken(). This method just does what I described above.
Does anyone know why it works when I step through the app with a breakpoint, but not when I just launch it without stepping through it?
I've isolated the problem and created a Github project that contains just this code. You can find it here: https://github.com/Avalaxy/OAuth2WinRT/tree/master/App1. There is an app.xaml.cs which calls OAuth2.GetToken().
Per the documentation -
If the app or its splash screen is kept on screen there is no time limit, but eventually the app needs to call Activate to progress.
Admittedly a bit vague and subject to interpretation, but moving the call to activate prior to the GetToken request will get you past what seems like a potential race condition.
I have a rather simple question.
We use BackgroundAgents( Periodic agents ) to perform some background tasks which require WiFi(Internet) connectivity in order to perform an HttpWebRequest. As mention in the second reference HttpWebRequest is supported but the problem is that if the Windows Phone is locked or idle for more than 1 minute the WiFi is disabled.
I have two important questions based on what I have read and tried until know:
Does Microsoft periodically enables WiFi according to predefined intervals to check for new emails or other notifications? If the answer is yes will my background agent rescheduled and run during this interval?
Until know I found that HttpWebRequest does not wakes the phone if locked or if is idle for more than 1 minute. Is that the case? A lot of people state that HttpWebRequest works fine if the phone is locked or if the 1 minutes has passed. I was not able to do that.
Thanks.
Sample Code:
protected override void OnInvoke(ScheduledTask task)
{
ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(120));
MakeHttpRequest("test");
}
private void MakeHttpRequest(string position)
{
if (position != null)
{
var request = (HttpWebRequest)WebRequest.Create(
new Uri("http://mydomain.com/Testing/Details/"+position));
request.BeginGetResponse(r =>
{
var httpRequest = (HttpWebRequest)r.AsyncState;
var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);
using (var reader = new StreamReader(httpResponse.GetResponseStream()))
{
var response = reader.ReadToEnd();
Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
{
}));
}
}, request);
}
this.NotifyComplete();
}
PS: Please keep in mind that when I run this code while the application is connected to the computer using a USB cable everything runs fine. That's why I believe its a problem with the background worker which does not wake the phone + WiFi in order to perform tha HttpWebRequest.
It is my experience with using the phone on a day-to-day basis that Windows Phone does not reenable wifi after it locks unless the phone is plugged in and charging. It also allows data transfers that were already started to complete before disabling the wifi.
However, even if the phone tried to reconnect periodically, some wifi providers redirect to an authentication page for authentication every time the user reconnects and Windows Phone does not handle that situation.
In reality, there's no guarantee that you have data services available as the user walks around with the phone, so handling the issue should be part of a normal path for the application code.