Jan 16 2012

SwimEventTimes W7P Development -- Live Tile

Category: Administrator @ 12:18

If you have an app then you owe it to yourself and users of your app to get something relevant onto the "pinned" Application Tile.  For a user who has "pinned" your application they will quickly expect something to appear on the back portion of the application tile.  This is what people consider the "cool" factor of owning a Windows Phone.  Having my phone open in the elevator with lots of flipping tiles makes the Windows 7 Phone look impressive to those other phone owners.  If your application tile does not flip and reveal any tidbits of information then your app is doomed to live unpinned and probably unused.

What I decided to do for my app was to have the user of the app select which swimmer(s) stat would appear on the Live Tile.  Then when the agent code (usually every 30 minutes) runs it will select a ("Best") random stat from the selected swimmer(s).  This way every 30 minutes I get a new stat for a swimmer on the back of the Live Tile.  You only have about 45 characters to play with so be selective about what you will show on the back of the Live Tile.

 

Now for the code:

From your main page include the start of the agent in the constructor:

// Constructor
        public RequestedSwimmersPage()
        {
            InitializeComponent();
           
            AgentMgr.StartAgent();      
            
        }

I've added a separate class to handle the use of the agent.  Keeps the code cleaner for future changes:

public static class AgentMgr
    {
        private const String AgentName = "SwimmerAgent";
        private const String AgentDescription = "Custom background agent for Swim Event Times pinned Tile!";


        public static void StartAgent()
        {
            StopAgentIfStarted();

            PeriodicTask task = new PeriodicTask(AgentName);
            task.Description = AgentDescription;
            ScheduledActionService.Add(task);
#if DEBUG 
        // If we're debugging, attempt to start the task immediately
            ScheduledActionService.LaunchForTest(AgentName, new TimeSpan(0, 0, 1)); 
#endif
        }


        public  static void StopAgentIfStarted()
        {
            if (ScheduledActionService.Find(AgentName) != null)
            {
                ScheduledActionService.Remove(AgentName);
            }
        } 
         
    }
}

 

So to get things moving you'll need a separate project (for the Agent worker) added to your solution to have the agent run.  Select the "Scheduled Task Agent" project type when adding the project to your solution.  Once you have a separate (Agent) project then add a reference to that from your main application.

It runs on a 30 minute interval so the data that I'm pulling will change on the back of the pinned Application Tile on that interval.

Now the Agent project will have one class and one method (OnInvoke) that you need to be concerned with.  Place your code to get new information onto the back tile in this method:

/// <summary>
        /// Agent that runs a scheduled task
        /// </summary>
        /// <param name="task">
        /// The invoked task
        /// </param>
        /// <remarks>
        /// This method is called when a periodic or resource intensive task is invoked
        /// </remarks>
        protected override void OnInvoke(ScheduledTask task)
        {
        
            //Here is where you put the meat of the work to be done by the agent

            //get the data from ISO 
            ObservableCollection<Swimmer> Swimmers = BackgroundAgentRESTCall.GetSwimmers();
            ObservableCollection<RequestedSwimmer> RequestedSwimmers = BackgroundAgentRESTCall.GetRequestedSwimmers();

            //pull out those swimmers who have the Live Tile turned on
            //and only those that match the 'Course' selection
            IEnumerable<Swimmer> tileSwimmers = from swimmer in Swimmers
                                            join reqswimmer in RequestedSwimmers on swimmer.SwimmerID equals reqswimmer.SwimmerID
                                            where reqswimmer.UseOnLiveTile == true
                                            && reqswimmer.BestCourseSelection == swimmer.Course
                                            select swimmer;


            string tileText = String.Empty;
            string titleText = string.Empty;
            
            //make sure that we have at least one stroke for a swimmer.
            if (tileSwimmers.Count() != 0)
            {

                //pull out the best swims for all marked (Live Tile) swimmers
                var bestswims = from p in tileSwimmers
                                //where conditions or joins with other tables to be included here                           
                                group p by p.SwimmerID + p.Stroke + p.Course + p.Distance into grp
                                let MinTime = grp.Min(g => g.TimeSecs)
                                from p in grp
                                where p.TimeSecs == MinTime
                                orderby p.Course descending, p.StrokeOrder, p.Distance
                                select p;

                int count = bestswims.Count(); // 1st round-trip 
                int index = new Random().Next(count);

                var tileSwimmer = bestswims.Skip(index).FirstOrDefault(); // 2nd round-trip 
              

                //foreach (Swimmer tileSwimmer in tileSwim)
                //{
                //Debug.WriteLine("WWW-data.LastProcessToTouchFile=" + tileSwimmer.AltAdjTime);
                titleText = tileSwimmer.Distance + tileSwimmer.Stroke + ": " + tileSwimmer.Time;

                Guid NavGUID;
                NavGUID = (Guid)tileSwimmer.SwimmerID;

                var tileReqSwimmer = (from reqswimmer in RequestedSwimmers
                                      where reqswimmer.SwimmerID == NavGUID
                                      select reqswimmer).FirstOrDefault();

                int LastnamLen = tileReqSwimmer.LastName.Length;
                if (LastnamLen > 9)
                {
                    LastnamLen = 9;
                }


                int StandardLen = tileSwimmer.Standard.Length;
                if (StandardLen > 6)
                {
                    StandardLen = 6;
                }

                tileText = tileReqSwimmer.FirstName.Substring(0, 1) + tileReqSwimmer.LastName.Substring(0, LastnamLen)
                + " Age: " + tileSwimmer.Age + "   "
                + String.Format("{0:MMM yyyy}", tileSwimmer.MeetDate) + "  "
                + tileSwimmer.Course + "  " + tileSwimmer.Standard.Substring(0, StandardLen);
            }
            else
            {
                tileText = "Select a Swimmer to be shown.";
                titleText = "Event & Time";
            }

            UpdateAppTile(tileText, titleText);

           
        }

The above routine gets the data from Isolated Storage. 

Note: I used a mutex in the call for the ISO data since we could be writing to the same storage at the time we are trying to read from it.

  I made heavy use of the sample mutex code listed from the link below:
 http://www.31a2ba2a-b718-11dc-8314-0800200c9a66.com/2011/11/this-is-continuation-of-this-post.html

Then I used LINQ to pull out the data needed, format it and call the Update method for the Tile:

private void UpdateAppTile(string message, string backTitleText)
        {
            ShellTile appTile = ShellTile.ActiveTiles.First();
            if (appTile != null)
            {
                StandardTileData tileData = new StandardTileData
                {
                    BackContent = message
                    ,BackBackgroundImage = new Uri("/Images/LiveTileC173x173.png", UriKind.Relative)
                    ,BackTitle = backTitleText
                };

                appTile.Update(tileData);
            }
        }

That gives us a pinned Application Tile with swimmer(s) changing info. 

Swimmers like it since it randomly rotates through their "Best" swim times and it gives the app that cool flipping tile that other phone owners love to hate.

 

 

 

 

Tags: , , , , ,