1

[Question]

On Windows Phone 8.1, what exactly happens in between the time when the user leaves the app and the OnSuspended event fires? I'm having trouble with the ability to manage objects in that span, in particular MediaCpture object.

To better explain the problem, here is the scenario:

  1. The user is on a page with a video preview being pumped to a CaptureElement
  2. The user taps the Start button
  3. The user taps Back button and returns to the page with a broken MediaCapture

With WinRT there isn't an ObscuredEvent and OnNavigatingFrom doesn’t fire unless you’re going to another page in the same Frame. After some investigation, I've found that the only event that fires is Window.Current.VisibilityChanged

I've gone ahead and hook it when the page is NavigatedTo and unhooked in OnNavigatedFrom (see ex2 below). Inside the event, I check for parameter that tells if the app is hiding or showing and dispose/initialize accordingly(see ex.1 below).

[Problem]

However, this only works with the debugger attached. If I do this without the debugger attached, it doesn't reinitialize and frequently crashes the camera and I have to literally reboot the device.

Code Example 1 (note: e.Visible == false is leaving the app and true when returning)

async void Current_VisibilityChanged(object sender, VisibilityChangedEventArgs e)
{
     if (!e.Visible) //means leaving the app
     {
         await DisposeAll(); //cleans the MediaCapture and CaptureElement
     }
     else
     {
         if(mediaCaptureManager != null) await DisposeAll();

         await Initialization(); //set up camera again
     }
}

Example 2 (hooking into the event)

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    Window.Current.VisibilityChanged += Current_VisibilityChanged;

    this.navigationHelper.OnNavigatedTo(e);
}

protected async override void OnNavigatedFrom(NavigationEventArgs e)
{
    Window.Current.VisibilityChanged -= Current_VisibilityChanged;

    this.navigationHelper.OnNavigatedFrom(e);
}

[Update: Resolution]

Instead of using VisibilityChanged, hook into Window.Current.Activated on the page's constructor. With the debugger completely detached, the Activated event will provide the WindowActivationState parameter in the WindowActivatedEventArgs. Like this:

private async void CurrentOnActivated(object sender, WindowActivatedEventArgs e)
{
    if(e.WindowActivationState == CoreWindowActivationState.Deactivated)
    {
        //dispose MediaCapture here
    }
    else if(e.WindowActivationState == CoreWindowActivationState.CodeActivated || e.WindowActivationState == CoreWindowActivationState.PointerActivated)
    {
        //initialize MediaCapture here
    }
}
Lance McCarthy
  • 1,884
  • 1
  • 20
  • 40

2 Answers2

2

See my answer in https://stackoverflow.com/a/28592882/3998132. Using Window.VisibilityChanged in conjunction with your Page\UserControl Loaded\Unloaded handler should solve your issue I believe.

Using Window.Activated is less desirable than Window.VisibilityChanged because Activated relates to being visible AND having focus where as VisibilityChanged only pertains to visibility. For showing a preview having focus is not applicable. Since Windows Store apps on Windows Phone can only have one Window showing there is no difference in using either however if your app becomes universal and runs on let's say on Windows 8+ Modern shell (which can show multiple Store apps with the Snap window feature) or Windows 10 desktop (which can support multiple Store apps showing at the same time) you will not want to stop preview when a user changes focus from your app but your app is still showing.

Community
  • 1
  • 1
Jimmy Alexander
  • 445
  • 2
  • 9
  • Funny thing is I started with VisibilityChanged and ran into a bunch of problems (which led to this post), after much headache I had to move the MediaCapture object to the App class. So in the PC version VisibilityChanged is still managing that capture page but the phone was left with Activated. I'll probably move it back to the Pc approach soon. Thanks for the confirmation! – Lance McCarthy Feb 18 '15 at 21:14
0

I'm not sure if it wouldn't be more suitable to use Suspending/Resuming events. Note only that in this case, you will have to debug it properly - it behaves little different while being run with/without debugger attached.

As for the code - hooking your event in OnNavigatedTo/OnNavigatedFrom is not a good idea - when the OS suspends the app and you are using SuspensionManager then OnNavigatedFrom will be called, but when you go back to your app (resume it), then OnNavigatedTo will not be called.

Using Window events may also work here, but why not subscribe it once, somewhere in constructor? - it's window-wide and hence in phone there is only one window, which stands for app, then subscribe once. In this case, you may add a line that recognizes the current page in window and if that page contains mediacapture then dispose (create similar). Then you can also dispose/initialize in navigation events in case user doesn't leave your app and just navigate.

Community
  • 1
  • 1
Romasz
  • 29,662
  • 13
  • 79
  • 154
  • The reason I use the navigation to hook and unhook from VisibilityChanged is because I only want this behavior on this view. Also the VisibilityChanged event is perfect for my needs (I've confirmed w/MS that this is the only way to detect when the app is closed, but before OnSuspended is called). My problem is soley the MediaCapture object. It doesn't get disposed properly. – Lance McCarthy Jan 24 '15 at 19:14
  • The reason I can't use OnSuspending is because it doesn't always fire right away. So if the user taps the home button, then back right away, the camera is wrecked but the app never suspended. – Lance McCarthy Jan 24 '15 at 19:19
  • @LanceMcCarthy Can you subscribe to VisibilityChanged for the whole app, and then check inside the event if Window.Current.Content (Page) contains your view or other? – Romasz Jan 24 '15 at 21:03
  • Might have found a solution. A combination of App.Current.Resuming and Window.Current.Activated. I'll update my Question once I've confirmed with further testing. – Lance McCarthy Jan 26 '15 at 13:19
  • @LanceMcCarthy I wonder why you have decided Acivated and Resuming event? Why not single VisibilityChanged? – Romasz Jan 26 '15 at 13:53
  • VisibilityChanged doesn't fire on that page when returning to the app. The only thing that will fire is "resuming". Nothing here is intuitive, things you'd expect to behave a certain way, don't. – Lance McCarthy Jan 26 '15 at 16:29
  • @LanceMcCarthy where do you subscribe to VisibilityChanged? Check if you are not facing the problem mentioned in my answer. Try without unsubscribing in OnNavigatedFrom and see if it fires upon returning to the app. – Romasz Jan 26 '15 at 16:55
  • @LanceMcCarthy It's the case I'm talking about - with debugger attached the app is not being suspended and your OnNavigatedFrom is not fired. Without debugger both events are fired once you tap start button - the app is suspended and your OnNavigatedFrom is fired - which unsubscribes resuming and activated events - then when you resume there is no event to fire. – Romasz Jan 26 '15 at 19:15
  • Indeed it is being called. Dammit, that's how all this nonsense started. I was not getting anything to work in OnNavigatingFrom, even with the debugger's suspend and resume lifecycle events. I think with the combination of OnNavigatedFrom and Resuming, I can manage the MediaCapture. – Lance McCarthy Jan 26 '15 at 19:44