Last week, Microsoft published new Application content policies for Windows Phone 7 Apps. One of the changes that got attention was the ability to run applications under a locked screen. Refer to section 6.3 of the application certification requirements.
In reality, the change is pretty minor, the capability to run under lock screen has been there since RTM. Microsoft simply published some new ‘guidance’ on the expectations for applications that run under a locked screen. That said, the change was interpreted as Microsoft relaxing the guidelines, since now it is clear that running under locked screen is not going to be frowned upon as long as you meet the requirements.
Why run under a locked screen? What are the benefits?
Many applications are expected to run under a locked screen: music players, run-tracking applications, map applications, etc. These applications will likely not change, as the certification guidelines had already approved these scenarios.
The changes to the certification requirements are aimed at apps that want to avoid tombstoning. For applications that are slow to recover from tombstoning ( maybe they make network calls and download a lot of data at start-up), they now can avoid the overhead of recovering from tombstoning when the application goes under locked screen. You simply let the screen lock come on, and stay running quietly without consuming a lot of battery, and when the lock screen goes away, then you restore doing your business, but the gain to the end-user was they did not wait for a second or more while your app re-hydrated from a tombstone. Since your app was never killed, response time is immediate.
Before we go much further on this topic, I do want to emphasize that running under lock is not an excuse so developers do not implement a super-fast, comprehensive tombstoning strategy. There are plenty of valid reasons why an app tombstones ( Choosers, App switching, etc.) that can’t be avoided, so you should still work hard to get the absolutely best tombstoning you can implement.
Still, running under locked screen is a very nice to have for some apps, so I did a bit of digging and below are my tips, lessons learned, and even sample code, you can reuse to get your app to run under lock.
Running Under Locked Screen
In short, to let your application run under locked screen, you must:
- Set PhoneApplicationService.Current.ApplicationIdleDetectionMode = IdleDetectionMode.Disabled;
- Handle the Application’s RootFrame Obscured and Unobscured events. So you know when the lockscreen comes on.
Obscured will be called when your app is getting locked. Here, you should do as much as you can to minimize CPU or battery consumption. Stop all animations, stop making network calls, stop listening to accelerometer, or location changes, etc..
Unobscured of course gets called when screens us getting unlocked. This is when the user comes back, and you pick where they left-off before phone went under the lock.
- [Optional] To have a great user experience with user in control, you should prompt the user so they opt-in into running under lock.
- [Optional] You should expose as part of your application configuration settings an option for the user to change their mind or simply want to disable it and conserve power.
- There was only one gotcha that I noticed. If your application sets ApplicationDetectionMode to IdleDetectionMode.Disabled, it can not change that back to IdleDetecionMode.Enabled until application is either launched again (so a full restart) or deactivated and then reactivated.
Getting all this to run was trivial, but required a few more lines of code than the four items above (to save settings, fire events, etc.) . I wrote this helper class called ApplicationIdleHelper.
- The class tracks three settings/properties:
- RunsUnderLock is true if the user has elected to run under lock. This setting is persisted across activations and launches of the app so we remember the user’s preference. The property also supports change notification.
- HasUserAgreedToRunUnderLock is true if the user was prompted using UI with a disclaimer that running under lock can consume battery. This setting is serialized to ApplicationSetings, so that you don’t have to prompt the user every time.
- IsRestartRequired is a transient property that does not get persisted. It simply flags whether the user has changed from IdleDetectionMode.Disabled to IdleDetectionMode.Enabled. If we try set this transition, we will get an exception that tells us once Disabled is selected you can’t go back to Enabled. Reality is that you can do it next time application is activated, so you just have to prompt the user to restart the app if they want the change to take place.
- IsRunningUnderLock is a transient read-only property that supports change notification. This property tells you if the app is currently under a locked screen. Good flag to make sure you are not doing work you are not supposed to, remember you are stealth under the lock screen (to preserve battery).
- The application also supports three events:
Locked is fired when the Locked screen comes on. It is an abstraction so you don’t have to listen to RootFrame.Obscured event.
- UnLocked fires when the application transitions from being in Locked state to it being in unlocked state and visible to the user. Again, equivalent to Unobscured event.
RestartRequired so that your classes are notified if the user has made a setting change that requires a restart (mainly going from IdleDetecionMode.Disabled to IdleDetecionMode.Enabled).
That is all there is to it. Plus, a bit of glue and a sample app for you to see it in action.
About the sample app.
It is a very simple pivot app that plays music using MediaElement.
You can toggle your preference to run under lock (or not to do that) by clicking the “Toggle current preference” button.
You can play/stop music by pressing the Play/Stop button.
The app will manage the settings for you. The UI under “Current settings” is data bounds to the ApplicationIdeleModeHelper so you can see it working.
The events are fired properly too.
Here are the behaviors you should see:
If you are in the greedy pivot, and have chosen to Run under Lock, the music will continue playing under lock.
If you are in the greedy pivot, but have not chosen to run under lock, the app will tombstone when the phone locks and there for the music will stop.
If you are in the mellow pivot, even if you have chosen to Run Under Lock, the music will stop. The application is not tombstoned, but it stoppped doing the work. It listens to the Locked event and stops doing work then, and then listens to Unlocked event and resumes there. Again, without tombstoning. I just wanted to show you how you can “decrease” the work you do under lock, to avoid consuming battery.
NOTE: To exercise the app you will need a device, as I don’t think the emulator runs under lock or simulates lock. Also, when running this, make sure your device is not attached to Zune, as that will lock media library and prevent the app from playing music (and this sample does not throw error for that).
The source for the app is on my Sky drive. Let me know if you find issues or have questions.
As my uncle used to say, “with great power comes great responsibility” .. Running under lock is pretty neat, but advanced feature, do not abuse it. Only use it for the kinds of apps that end-users would want to keep open even while locked.
Happy Windows Phone coding!