In my WP8 App, I am navigating from one screen to another based on some events. Say, after initiating the event(http call which would take me to a new screen, after successful response), I click on back key, the previous screen is shown to me, but since the earlier event was already fired, after the event completion a new screen is shown.
So, How should I handle this back key press.
I have tried searching it on net as well, but did not get much help.
[Edit :] I want to stop my async calls on the current screen when its back key is pressed,
I did some searching and found that using Cancellationtoken is one such approach. But, I would want to know how exactly to use it if I am using HttpWebRequest request, response classes
Since you did not mention what is the expected behavior, I would assume that what you want is that in some condition you expect that back key press won't lead you to the previous page. If so, you should override the BackKeyPress method in the page where back key is pressed.
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
if (someCondition) {
e.Cancel = true;
}
base.OnBackKeyPress(e);
}
Please let me know if it is what you expected.
Related
So I'm seeing some bizarre behavior in an appwidget that I wrote.
The widget itself is quite simple - it reads a few values from persistent storage and then displays them as text. Nothing fancy in the layout - just a FrameLayout root element with some Linear Layout and TextView children.
The widget has a simple configuration activity associated with it.
The bizarre behavior is that the widget will initially show "Problem Loading Widget" after the user closes the configuration activity, and then after a few seconds it shows a "Google Sound Search" button (and clicking on the button actually does launch Google Sound Search). Then, after a few more seconds, it finally shows the expected display.
I am away from my code right now, so I'll have to wait until tonight to post code snippets. However, in the meantime, can anyone provide some insight into how such a thing could happen? Has anyone else ever experienced this? How could another widget "hijack" mine?
Thanks,
-Ron
Here are some screenshots:
There are a couple of issues with your widget and there are answers to all of them (although you didn't post any code so some of my statements are based on assumptions):
"Problem loading widget": this is the default view Android uses before the widget is initialized and the layout updated. Simply add the following line to your widget xml configuration (to show a loading message instead of the problem message):
android:initialLayout="#layout/my_cool_widget_loading_message"
If the widget shows the wrong layout then you probably have an issue in the widget's onReceive method. onReceive is called for all the widgets no matter whether the broadcast is for that specific widget. Android's AppWidgetProvider filters the broadcasts by appwidget Id and dispatches to the other methods (like onUpdate).
See also: https://developer.android.com/reference/android/appwidget/AppWidgetProvider.html#onReceive(android.content.Context, android.content.Intent).
If you override onReceive (which I assume you do), you need to call through to super.onReceive(Context, Intent) to make sure your other methods don't get calls meant for other widgets.
Now for the configuration of the widget. If you follow the Google documentation then it will all work nicely. The only improvement I'd do is what my other answer that you reference suggests (https://stackoverflow.com/a/14991479/534471). This will NOT send out two broadcasts. The setResult()/finish() part does only terminate the config Activity and let Android know whether to actually add the widget or not (depending on whether the result is RESULT_CANCELED or RESULT_OK.
From your own answer I can see why your code wouldn't work. The code is:
Intent intent = new Intent();
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {mAppWidgetId});
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, intent);
sendBroadcast(intent);
finish();
First of all there's no need to add the appWidgetId twice, use the AppWidgetManager.EXTRA_APPWIDGET_IDS version and you're good. Second you're using the same Intent to return as a result for the Activity. AFAIK it's not documented what happens when you do set an action on that Intent but my experience with Android widgets is that you need to stick exactly to the documentation or you'll end up having strange issues (like the ones you encounter). So please use two different Intents.
Activity result:
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
Broadcast:
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, this, MyWidget.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {mAppWidgetId});
sendBroadcast(intent);
ok, so I figured it out. Posting here in case anyone else runs into this. I think that the Android Developer docs are a little misleading here.
The problem was that in my configuration Activity, I had this code at the end:
Intent intent = new Intent();
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {mAppWidgetId});
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, intent);
sendBroadcast(intent);
finish();
Providing an intent with the extra EXTRA_APPWIDGET_ID is recommended by the documentation provided by google.
However, that same document says that you have to update the widget's view by creating a RemoteView and calling AppWidgetManager.updateAppWidget() like so:
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.example_appwidget);
appWidgetManager.updateAppWidget(mAppWidgetId, views);
I didn't like the idea of placing the presentation logic in both the configuration activity and the widget class, so I instead decided to broadcast an intent at the end of the configuration activity to tell the widget to redraw itself. That's why I have setResult() AND sendBroadcast() at the end of the activity. The documentation further states that the onUpdate() callback will not be called when using a configuration activity. So this seemed neccessary. I added the ACTION_APPWIDGET_UPDATE and the EXTRA_APPWIDGET_IDS to the intent so that it would trigger the onUpdate() method. This practice was recommended by this SO answer (albeit without being included in the activity result intent - but I tried separating the two and it had no effect).
Now I'm not certain exactly how the "Google Sound Search" widget got in there, nor do I fully understand the mechanics of how the intents interacted to produce the observed results. However, as soon as I replaced my code above with the code stated in the docs, the widget was updated properly.
Intent resultIntent = new Intent();
resultIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultIntent);
finish();
This seems to contradict the documentation's statement that the configuration activity must update the widget's view. Simply providing the configuration activity result as below triggers the onUpdate() method in the widget, thus allowing the widget to redraw itself. I confirmed the behavior on an emulator running API 23 and also on a Samsung device running Samsung's android flavor.
How do I make the hardware back button return to the system's Start page when my app is launched from a secondary tile (i.e. deep linking)?
I'm using XAML and C#, by the way.
You can just clear the back stack if app is launched from a secondary tile.
Next press on back button will take user back to Start screen.
EDIT:
To clear the back stack:
If you're in page code behind, do
Frame.BackStack.Clear();
else do
var frame = Window.Current.Content as Frame;
frame.BackStack.Clear();
So, finally answering my own question months later...
The situation
Whenever my app is launched using a secondary tile, I have to make sure that the back stack is cleared by calling rootFrame.BackStack.Clear() on App.xaml.cs. That's necessary because if there is a suspended instance of my app in memory, this navigation from the secondary tile is added to the top of whatever back stack that suspended instance had. This is a problem because it makes a press of the back button return to the previous page on the stack instead of the Start screen, which is the appropriate behavior for secondary tile launches.
The problem
If the user "cold launches" the app using a secondary tile, whenever s/he leaves it Windows will suspend that session of the app with a clear back stack. That means that new launches from the primary tile will restore that page that was called from the secondary tile, and pressing the back button will suspend the app again instead of going to the app's main page.
The solution
It's actually documented by Microsoft, but only textually (https://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn639128.aspx):
Ensure proper Back button behavior when your app supports multiple launch points
Consider creating new instances of navigation journals for launch points such as primary and secondary tiles, file and URI Associations. Save the activation parameters each time the app is launched and compare when the app is being re-launched. If the app is being re-launched with different parameters, consider creating a new navigation history. Do this by creating a new Frame. This will allow the user to quickly navigate back to the launch point when the hardware Back key button is pressed. To maintain a small memory footprint, the Navigation history for launch points other than the Primary tile doesn’t need to be saved when the app is suspended.
In practice, for me, that meant writing the last received launch argument to a text file in my app's storage and reading it back on the beginning of the OnLaunched method in App.xaml.cs. Then I compare the read value to the current launch arguments. If they are different, I simply assign null to the rootFrame variable, so that a new "navigation journal" is created:
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
...
Frame rootFrame = Window.Current.Content as Frame;
var lastArgument = await ReadLastArgument();
if (!string.Equals(lastArgument,currentArgument))
rootFrame = null;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active.
if (rootFrame == null){
...
}
...
if (!String.IsNullOrEmpty(e.Arguments))
{
WriteLastArgument(e.Arguments);
//Navigate to the page...
...
rootFrame.BackStack.Clear();
}
private async Task<string> ReadLastArgument()
{
StorageFolder folder = Windows.Storage.ApplicationData.Current.LocalFolder;
try
{
StorageFile file = await folder.GetFileAsync("lastArgument.txt");
var argument = await Windows.Storage.FileIO.ReadTextAsync(file);
return argument;
}
catch (System.IO.FileNotFoundException)
{
return "";
}
}
private async void WriteLastArgument(string argument)
{
StorageFolder folder = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile file = await folder.CreateFileAsync("lastArgument.txt", CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(file, argument);
}
}
I'm using Contacts.SearchAsync to get all user's contacts, in an WP8 app.
But i realized that, when the user has many contacts (like 1000+), this search takes a long time...
So, I was thinking in add an button, so the user has the option to cancel this search...
But I couldn't find any method that cancels this search...
Is there a way to cancel it, before it finishes?
Thanks
I did not use this, but there is workaround.
If thread is terminated, all of its children-threads are terminated too.
Therefore if you run this method in new thread, you can always terminate it.
I am not aware of any way to cancel the search but an alternative approach is to just ignore the completion of the search.
void Contacts_SearchCompleted(object sender, ContactsSearchEventArgs e)
{
if (!searchWasCancelled)
{
// Process the search results
}
}
I am working on a Windows Phone 8 app and want to save data from a page each time the user navigates away from the page. This works fine if the user presses the back key or navigates within the application, however if I press the start key nothing is saved. My code is shown below:
protected async override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
base.OnNavigatingFrom(e);
if (!toDelete)
{
List<NoteItem> ni = this.getNoteItems();
await SaveNotes(ni, this.Title.Text);
}
}
This calls the method to save the notes. I don't see any other events on a page to save this. Does anybody know where I am going wrong with this?
Thanks,
You can try handling this case in App.xaml.cs.
When the Start button is pressed, Application_Deactivated event is raised.
For example
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
//do the saving here
}
Although, I'm getting the OnNavigatingFrom event called when pressing Start button, too, so it may be the charm of using await in event handlers. Read this article by Andy Wigley, it's packed with useful info.
was trying to encapsulate a partial view to show feedback that i can push back to the client.
This Article shows a method of pushing back data using HTML5 Server-Sent events (SSE).
I noticed that if i opened up several browser tabs and then closed one i got exceptions as the logic didn't remove the respective stream from the ConcurrentQueue. I amended the code as below
private static void TimerCallback(object state)
{
StreamWriter data;
Random randNum = new Random();
// foreach (var data in _streammessage)
for (int x = 0; x < _streammessage.Count; x++)
{
_streammessage.TryDequeue(out data);
data.WriteLine("data:" + randNum.Next(30, 100) + "\n");
try
{
data.Flush();
_streammessage.Enqueue(data);
}
catch (Exception ex)
{
// dont re-add the stream as an error ocurred presumable the client has lost connection
}
}
//To set timer with random interval
_timer.Value.Change(TimeSpan.FromMilliseconds(randNum.Next(1, 3) * 500), TimeSpan.FromMilliseconds(-1));
}
I also had to amend the OnStreamAvailable member as the framework syntax had changed to the second parameter being a HttpContent rather than HttpContentHeaders
public static void OnStreamAvailable(Stream stream, HttpContent headers, TransportContext context)
The problem now is i am still getting inconsistant behaviour if i add or remove clients i.e it times out when trying to initialise a new client. Does anyone have any ideas or more examples of using SSE with WinAPI and the correct "framework of methods" to handle disconnected clients
Cheers
Tim
This article is actually an adaptation of my original article from May - http://www.strathweb.com/2012/05/native-html5-push-notifications-with-asp-net-web-api-and-knockout-js/ (notice even variable names and port numbers are the same :-).
It is a very valid point that you are raising, and detecting a broken connection is something that's not very easy with this setup. The main reason is that while ASP.NET (the host) allows you to check a broken connection, there is no notification mechanism between ASP.NET (host) and Web API informing about that.
That is why in order to detect a broken connection (disconnected client) you should really try writing to the stream, and catch any error - this would mean the client has been disconnected.
I asked the same question to Brad Wilson/Marcin Dobosz/Damien Edwards at aspconf, and Damien suggested using HttpContext.Current.Response.IsClientConnected - so basically bypassing Web API and obtaining the connectivity info from the underlying host directly (however there is still a race condition involved anyway). That is really .NET 4. He also pointed an interesting way in which this problem could be avoided in .NET 4.5 using an async cancellation token. Frankly, I have never got around to test it, but perhaps this is something you should explore.
You can see their response to this problem in this video - http://channel9.msdn.com/Events/aspConf/aspConf/Ask-The-Experts - fast forward to 48:00