AppNap data

Following on from last time, I thought I'd start by describing the persisted data for AppNap, which consists of an ordered list of search shortcuts, and a search shortcut is a name (which appears in the list presented to the user) and a bit of text used when actually performing the search. Because the user can change these entries on the fly, rather than a plain old C# class, I made this implement INotifyPropertyChanged:

     public class SearchShortcut : INotifyPropertyChanged
    {
        public string Name
        {
            get  { return name; }
            set
            {
                this.name = value;
                RaisePropertyChanged("Name");
            }
        }
        private string name;

        public string Extra
        {
            get { return extra; }
            set
            {
                this.extra = value;
                RaisePropertyChanged("Extra");
            }
        }
        private string extra;

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string propertyName)
        {
            var eh = this.PropertyChanged;
            if (eh != null)
                eh(this, new PropertyChangedEventArgs(propertyName));
        }
    }

And the list of search entries to provide to the user is abstracted into this Settings class (minus error checking for brevity):

     public class Settings
    {
        public ObservableCollection<SearchShortcut> Shortcuts;

        private Settings() { }

        public static Settings LoadAll()
        {
            var state = new Settings();
            state.LoadShortcuts();
            return state;
        }

        public void SaveAll()
        {
            var settings = IsolatedStorageSettings.ApplicationSettings;
            SaveShortcuts(settings);
            settings.Save();
        }

        private void SaveShortcuts(IsolatedStorageSettings settings)
        {
            var sb = new StringBuilder();
            foreach (var s in Shortcuts)
                sb.AppendFormat("{0}\t{1}\n", s.Name, s.Extra);
            settings["Shortcuts"] = sb.ToString();
         }

        private void LoadShortcuts()
        {
            var settings = IsolatedStorageSettings.ApplicationSettings;
            Shortcuts = new ObservableCollection<SearchShortcut>();
            string shortcutString;
            if (settings.TryGetValue("Shortcuts", out shortcutString))
            {
                foreach (var s in shortcutString.Split('\n'))
                {
                    var pieces = s.Split('\t');
                    var shortcut = new SearchShortcut { Name = pieces[0], Extra = pieces[1] };
                    Shortcuts.Add(shortcut);
                }
             }
            if (Shortcuts.Count == 0)
                SetDefaultShortcuts();
        }

Here, LoadAll gives back settings read from isolated storage on the phone and SaveAll writes the current settings into isolated storage. Within LoadShortcuts and SaveShortcuts I use a very simple encoding of the shortcut's name and extra text as a tab separated line for each shortcut (which would prevent both tab and newline from being used in the search shortcuts, but that's not much of a limitation). If no data items are found in isolated storage, SetDefaultShortcuts creates the default set you see when you first run the application; that routine consists of lines like:

             Shortcuts.Add(new SearchShortcut { Name = "IMDB [site]", Extra = "https://www.imdb.com/find?q=" });
            Shortcuts.Add(new SearchShortcut { Name = "IMDB [mobile]", Extra = "https://m.imdb.com/find?q=" });
            Shortcuts.Add(new SearchShortcut { Name = "IMDB [web]", Extra = "site:imdb.com" });

Having defined my settings, what do I do with them? Loading is done when the application starts, in these two routines in my App.xaml.cs:

         private void Application_Launching(object sender, LaunchingEventArgs e)
        {
            Settings = Settings.LoadAll();
        }

        private void Application_Activated(object sender, ActivatedEventArgs e)
        {
            if (Settings == null)
                Settings = Settings.LoadAll();
        }

Application_Activated is invoked when resuming from both dormant and tombstoned, as outlined in the execution overview on MSDN: in the former, there's no need to reload the settings though it make little difference here because there's so little in the way of settings here. Typically one would checkĀ e.IsApplicationInstancePreserved but Settings being non-null is a pretty good indication that they've not been preserved! Application suspension is often a good time to save settings: however, I prefer to save them as soon as they change (just in case my application crashes), so the calls to SaveAll occur in the code behind my settings page.

So... Handling user settings in Windows Phone 7 is incredibly easy, given the isolated storage classes, in particular IsolatedStorageSettings.