WeatherInformation WP8: MapPage improvements
authorgu.martinm@gmail.com <gu.martinm@gmail.com>
Fri, 3 Oct 2014 01:20:36 +0000 (03:20 +0200)
committergu.martinm@gmail.com <gu.martinm@gmail.com>
Fri, 3 Oct 2014 01:20:36 +0000 (03:20 +0200)
14 files changed:
WindowsPhone/WP8/WeatherInformation/WeatherInformation/MainPage.xaml.cs
WindowsPhone/WP8/WeatherInformation/WeatherInformation/MapPage.xaml
WindowsPhone/WP8/WeatherInformation/WeatherInformation/MapPage.xaml.cs
WindowsPhone/WP8/WeatherInformation/WeatherInformation/Model/ReverseGeoCode.cs [new file with mode: 0644]
WindowsPhone/WP8/WeatherInformation/WeatherInformation/Model/Services/CustomHTTPClient.cs
WindowsPhone/WP8/WeatherInformation/WeatherInformation/Properties/WMAppManifest.xml
WindowsPhone/WP8/WeatherInformation/WeatherInformation/Resources/AppResources.Designer.cs
WindowsPhone/WP8/WeatherInformation/WeatherInformation/Resources/AppResources.es.resx
WindowsPhone/WP8/WeatherInformation/WeatherInformation/Resources/AppResources.es.xlf
WindowsPhone/WP8/WeatherInformation/WeatherInformation/Resources/AppResources.qps-ploc.xlf
WindowsPhone/WP8/WeatherInformation/WeatherInformation/Resources/AppResources.resx
WindowsPhone/WP8/WeatherInformation/WeatherInformation/SettingsPage.xaml
WindowsPhone/WP8/WeatherInformation/WeatherInformation/ViewModels/SettingsViewModel.cs
WindowsPhone/WP8/WeatherInformation/WeatherInformation/WeatherInformation.csproj

index 8c2b9cf..90589a0 100644 (file)
@@ -11,6 +11,7 @@ using WeatherInformation.Resources;
 using WeatherInformation.ViewModels;
 using System.Threading.Tasks;
 using WeatherInformation.Model.JsonDataParser;
+using Microsoft.Phone.Shell;
 
 namespace WeatherInformation
 {
@@ -36,6 +37,8 @@ namespace WeatherInformation
         {
             base.OnNavigatedTo(e);
 
+            CreateFlipTile();
+
             // If _isNewPageInstance is true, the page constuctor has been called, so
             // state may need to be restored.
             if (_isNewPageInstance)
@@ -171,6 +174,22 @@ namespace WeatherInformation
             NavigationService.Navigate(new Uri("/SettingsPage.xaml", UriKind.Relative));
         }
 
+        private void CreateFlipTile()
+        {
+            ShellTile tile = ShellTile.ActiveTiles.FirstOrDefault(
+                x => x.NavigationUri.ToString().Contains("flip"));
+            tile = ShellTile.ActiveTiles.First();
+            var activeTiles = ShellTile.ActiveTiles;
+
+            var tileData = new FlipTileData();
+            tileData.Title = "GUSTAVO RULES";
+            tileData.BackTitle = "Gustavo Rules Back";
+            tileData.BackContent = "Gustavo Back Content";
+            tileData.WideBackContent = "Gustavo Wid Back Content";
+            tile.Update(tileData);
+
+        }
+
         // Código de ejemplo para compilar una ApplicationBar traducida
         //private void BuildLocalizedApplicationBar()
         //{
index 5c4bd37..20eeae5 100644 (file)
         <!--TitlePanel contiene el nombre de la aplicación y el título de la página-->
         <StackPanel Grid.Row="0" Margin="12,17,0,28">
             <TextBlock Text="{Binding LocalizedResources.LocationPageTitle, Mode=OneWay, Source={StaticResource LocalizedStrings}}" Style="{StaticResource PhoneTextNormalStyle}"/>
-            <TextBlock x:Name="LocationTextCityCountry" Text="{Binding LocalizedResources.LocationPageSubTitle, Mode=OneWay, Source={StaticResource LocalizedStrings}}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle2Style}"/>
+            <Grid>
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="42*"/>
+                    <ColumnDefinition Width="41*"/>
+                    <ColumnDefinition Width="151*"/>
+                </Grid.ColumnDefinitions>
+                <TextBlock x:Name="LocationTextCity" Text="{Binding LocalizedResources.DefaultCity, Mode=OneWay, Source={StaticResource LocalizedStrings}}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle2Style}" FontSize="24" HorizontalAlignment="Left" VerticalAlignment="Center" Grid.ColumnSpan="3"/>
+                <TextBlock x:Name="LocationTextCountry" Text="{Binding LocalizedResources.DefaultCountry, Mode=OneWay, Source={StaticResource LocalizedStrings}}" Margin="0,-7,0,0" Style="{StaticResource PhoneTextTitle2Style}" FontSize="24" HorizontalAlignment="Right" VerticalAlignment="Center" Grid.Column="2"/>
+            </Grid>
+            <StackPanel Orientation="Horizontal">
+            </StackPanel>
         </StackPanel>
 
         <!--ContentPanel. Colocar aquí el contenido adicional-->
             <maps:Map x:Name="mapWeatherInformation" Grid.Row="1" Tap="mapWeatherInformation_Tap"/>
         </Grid>
         <Grid Grid.Row="2">
+            <ProgressBar x:Name="ProgressBarRemoteData" IsIndeterminate="True" LargeChange="0" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Height="80" FontSize="20" FlowDirection="LeftToRight" UseLayoutRounding="True" IsEnabled="False" />
             <Button x:Name="SaveLocationButton"  Content="{Binding LocalizedResources.MapPageSaveLocationButton, Mode=OneWay, Source={StaticResource LocalizedStrings}}" Click="SaveLocationButton_Click" HorizontalAlignment="Left" VerticalAlignment="Center"/>
             <Button x:Name="GetCurrentLocationButton" HorizontalAlignment="Right" VerticalAlignment="Center" Content="{Binding LocalizedResources.MapPageGetCurrentLocationButton, Mode=OneWay, Source={StaticResource LocalizedStrings}}" Click="GetCurrentLocationButton_Click"/>
         </Grid>
         <Grid Grid.Row="3">
+            <!-- 
             <Button x:Name="ZoomInButton" Click="ZoomInButton_Click" HorizontalAlignment="Left" VerticalAlignment="Center" Content="Zoom +"/>
-            <Button x:Name="ZoomOutButton" Click="ZoomOutButton_Click" HorizontalAlignment="Right" VerticalAlignment="Center" Content="Zoom -"/>
+            <Button x:Name="ZoomOutButton" Click="ZoomOutButton_Click" HorizontalAlignment="Right" VerticalAlignment="Center" Content="Zoom -"/> 
+        -->
         </Grid>
     </Grid>
 
index ec21ee1..97e8774 100644 (file)
@@ -1,5 +1,4 @@
 using System;
-using System.Collections.Generic;
 using System.Linq;
 using System.Windows;
 using System.Windows.Navigation;
@@ -9,107 +8,148 @@ using System.Device.Location;
 using System.Windows.Shapes;
 using System.Windows.Media;
 using Microsoft.Phone.Maps.Controls;
-using WeatherInformation.Resources;
-using System.Globalization;
-using Microsoft.Phone.Maps.Services;
 using System.Threading.Tasks;
 using WeatherInformation.Model;
 
 namespace WeatherInformation
 {
-    public partial class MapPage : PhoneApplicationPage
+    public partial class MapPage : PhoneApplicationPage, ReverseGeoCode.IReverseGeoCode
     {
-        // TODO anything better than these two instance fields? :(
-        private string _city;
-        private string _country;
+        private bool _isNewPageInstance;
+        private ReverseGeoCode _reverseGeoCodeOnProgress;
+        // TODO: how big is a MapLayer object?
+        private MapOverlay _locationOverlay;
 
         public MapPage()
         {
             InitializeComponent();
+
+            _isNewPageInstance = true;
         }
 
         protected override void OnNavigatedTo(NavigationEventArgs e)
         {
-            // TODO: I am not saving the UI state. If location changed but it was not saved
-            // user will have to pick her location again :(
-            Location locationItem = null;
-            using (var db = new LocationDataContext(LocationDataContext.DBConnectionString))
+            var geolocator = new Geolocator();
+            if (geolocator.LocationStatus != PositionStatus.Ready)
             {
-                // Define the query to gather all of the to-do items.
-                // var toDoItemsInDB = from Location location in _locationDB.Locations where location.IsSelected select location;
-                locationItem = db.Locations.Where(location => location.IsSelected).FirstOrDefault();
+                GetCurrentLocationButton.IsEnabled = false;
             }
-            
-            if (locationItem != null)
+
+            GeoCoordinate restoreReverseOnProgress = null;
+            Location restoreLocation = null;
+            if (_isNewPageInstance)
+            {
+                if (State.Count > 0)
+                {
+                    restoreReverseOnProgress = (GeoCoordinate)State["ReverseGeoCoorDinateOnProgress"];
+                    restoreLocation = (Location)State["CurrentChosenMapLocation"];
+                }
+            }
+            // Set _isNewPageInstance to false. If the user navigates back to this page
+            // and it has remained in memory, this value will continue to be false.
+            _isNewPageInstance = false;
+
+            Location location;
+            if (restoreLocation != null)
+            {
+                location = restoreLocation;
+            }
+            else if (_locationOverlay != null)
+            {
+                location = CoordinateHelper.GeoCoordinateToLocation(
+                    _locationOverlay.GeoCoordinate,
+                    LocationTextCity.Text,
+                    LocationTextCountry.Text);
+            }
+            else
+            {
+                using (var db = new LocationDataContext(LocationDataContext.DBConnectionString))
+                {
+                    // Define the query to gather all of the to-do items.
+                    // var toDoItemsInDB = from Location location in _locationDB.Locations where location.IsSelected select location;
+                    location = db.Locations.Where(locationItem => locationItem.IsSelected).FirstOrDefault();
+                }
+            }
+
+
+            if (location != null)
+            {
+                UpdateMap(CoordinateHelper.LocationToGeoCoordinate(location),
+                          location.City, location.Country);
+            }
+
+            if (restoreReverseOnProgress != null)
+            {
+                ShowProgressBar();
+                ReverseGeocodeAndUpdateMap(restoreReverseOnProgress);
+            }
+            else
             {
-                GeoCoordinate geoCoordinate = ConvertLocation(locationItem);
+                RemoveProgressBar();
+            }
+        }
+
+        protected override void OnNavigatedFrom(NavigationEventArgs e)
+        {
+            if (e.NavigationMode != System.Windows.Navigation.NavigationMode.Back)
+            {
+                // TODO: Could Center be null?
+                State["CurrentChosenMapLocation"] = CoordinateHelper.GeoCoordinateToLocation(
+                    mapWeatherInformation.Center,
+                    LocationTextCity.Text,
+                    LocationTextCountry.Text);
 
-                this.UpdateMap(geoCoordinate, locationItem.City, locationItem.Country);
+                if (_reverseGeoCodeOnProgress != null)
+                {
+                    State["ReverseGeoCoorDinateOnProgress"] = _reverseGeoCodeOnProgress.CoorDinate;
+                }  
             }
         }
 
+        public void OnCompletedReverseGeoCode(GeoCoordinate geoCoordinate, string city, string country)
+        {
+            UpdateMap(geoCoordinate, city, country);
+            RemoveProgressBar();
+            _reverseGeoCodeOnProgress = null;
+        }
+
         private async Task GetCurrentLocationAndUpdateMap()
         {
-            Geolocator geolocator = new Geolocator();
+            var geolocator = new Geolocator();
             geolocator.DesiredAccuracyInMeters = 50;
+            geolocator.ReportInterval = 1000;
 
-            Geoposition geoposition = await geolocator.GetGeopositionAsync(
-                maximumAge: TimeSpan.FromSeconds(10),
+            var geoposition = await geolocator.GetGeopositionAsync(
+                maximumAge: TimeSpan.FromSeconds(1),
                 timeout: TimeSpan.FromSeconds(10)
                 );
-            GeoCoordinate currentGeoCoordinate = CoordinateHelper.ConvertGeocoordinate(geoposition.Coordinate);
+            // TODO: check if the following is true:
+            // Without ConfigureAwait(false) await returns data on the calling thread. (AFAIK for this Context)
+            // otherwise I should call: Dispatcher.BeginInvoke(() => ReverseGeocodeAndUpdateMap(currentGeoCoordinate));
 
+            // TODO: What is going to happend when Timeout? Exception or geposition will be null value.
+            //       Should I check for null value in case of Timeout?
+            var currentGeoCoordinate = CoordinateHelper.ConvertGeocoordinate(geoposition.Coordinate);
+            ShowProgressBar();
             ReverseGeocodeAndUpdateMap(currentGeoCoordinate);
         }
 
-        // TODO: problems updating Map because this method may be called when automatically retrieving
-        //       the current user's location or when user taps on map tyring to choose by herself her location.
-        //       There could be 2 threads updating Map at the same time. Solution: remove the feature
-        //       of getting the current user's location (user must always choose her/his location instead of doing
-        //       it automatically)
-        private void ReverseGeocodeAndUpdateMap(GeoCoordinate currentGeoCoordinate)
+        private void ReverseGeocodeAndUpdateMap(GeoCoordinate geoCoordinate)
         {
-            ReverseGeocodeQuery currentReverseGeocodeQuery = new ReverseGeocodeQuery();
-            currentReverseGeocodeQuery.GeoCoordinate = currentGeoCoordinate;
-            currentReverseGeocodeQuery.QueryCompleted += QueryCompletedCallback;
-            currentReverseGeocodeQuery.QueryAsync();
-        }
-
-        private void QueryCompletedCallback(object sender, QueryCompletedEventArgs<IList<MapLocation>> eventData)
-        {
-            if (eventData.Cancelled)
+            var reverseGeoCode = new ReverseGeoCode()
             {
-                // Be careful!!! If you throw exception from this point your program will finish with "Unhandled Exception".
-                return;
-            }
+                Page = this,
+                CoorDinate = geoCoordinate
+            };
 
-            Exception errorException = eventData.Error;
-            if (errorException != null)
-            {
-                // TODO: Should I call UpdateMap even if there are not results?
-                // I could use country and city default values in that case...
-                // Problem this method: requires GeoCoordinate and I wouldn't have it here...
-                // Somehow this method should take them...
-                // TODO: if user changed the page, where is this going to appear?
-                MessageBox.Show(
-                    AppResources.NoticeErrorLocationAutodetection,
-                    AppResources.UnavailableAutomaticCurrentLocationMessageBox,
-                    MessageBoxButton.OK);
-            }
-            else
+            if (_reverseGeoCodeOnProgress != null)
             {
-                if (eventData.Result.Count > 0)
-                {
-                    // TODO: Should I call UpdateMap even if there are not results?
-                    // I could use country and city default values in that case...
-                    // Problem this method: requires GeoCoordinate and I wouldn't have it here...
-                    // Somehow this method should take them...
-                    MapAddress address = eventData.Result[0].Information.Address;
-                    GeoCoordinate currentGeoCoordinate = eventData.Result[0].GeoCoordinate;
-
-                    UpdateMap(currentGeoCoordinate, address.City, address.Country);
-                }
+                // GC may release old object.
+                _reverseGeoCodeOnProgress.Page = null;
             }
+
+            _reverseGeoCodeOnProgress = reverseGeoCode;
+            _reverseGeoCodeOnProgress.DoReverseGeocode(geoCoordinate);
         }
 
         private void UpdateMap(GeoCoordinate geoCoordinate, string city, string country)
@@ -122,36 +162,27 @@ namespace WeatherInformation
             myCircle.Opacity = 50;
 
             // Create a MapOverlay to contain the circle.
-            MapOverlay myLocationOverlay = new MapOverlay();
-            myLocationOverlay.Content = myCircle;
-            myLocationOverlay.PositionOrigin = new Point(0.5, 0.5);
-            myLocationOverlay.GeoCoordinate = geoCoordinate;
+            _locationOverlay = new MapOverlay();
+            _locationOverlay.Content = myCircle;
+            _locationOverlay.PositionOrigin = new Point(0.5, 0.5);
+            _locationOverlay.GeoCoordinate = geoCoordinate;
 
             // Create a MapLayer to contain the MapOverlay.
             MapLayer myLocationLayer = new MapLayer();
-            myLocationLayer.Add(myLocationOverlay);
+            myLocationLayer.Add(_locationOverlay);
 
-            this.mapWeatherInformation.Layers.Clear();
+            mapWeatherInformation.Layers.Clear();
 
             // TODO: problems? user could press save location and if she is fast enough she
             // could not realize the location changed.
             // But well... She will realize later... So.. Is this really a problem?
-            this.mapWeatherInformation.Center = geoCoordinate;
-            this.mapWeatherInformation.ZoomLevel = 13;
+            mapWeatherInformation.Center = geoCoordinate;
+            mapWeatherInformation.ZoomLevel = 13;
 
-            if (string.IsNullOrEmpty(city))
-            {
-                city = AppResources.DefaultCity;
-            }
-            if (string.IsNullOrEmpty(country))
-            {
-                country = AppResources.DefaultCountry;
-            }
-            this.LocationTextCityCountry.Text = String.Format(CultureInfo.InvariantCulture, "{0}, {1}", city, country);
-            _city = city;
-            _country = country;
+            LocationTextCity.Text = city;
+            LocationTextCountry.Text = country;
             // Add the MapLayer to the Map.
-            this.mapWeatherInformation.Layers.Add(myLocationLayer);
+            mapWeatherInformation.Layers.Add(myLocationLayer);
         }
 
         private static class CoordinateHelper
@@ -169,11 +200,43 @@ namespace WeatherInformation
                     geocoordinate.Heading ?? Double.NaN
                     );
             }
+
+            public static GeoCoordinate LocationToGeoCoordinate(Location locationItem)
+            {
+                return new GeoCoordinate
+                    (
+                    locationItem.Latitude,
+                    locationItem.Longitude,
+                    locationItem.Altitude,
+                    locationItem.HorizontalAccuracy,
+                    locationItem.VerticalAccuracy,
+                    locationItem.Speed,
+                    locationItem.Course
+                    );
+            }
+
+            public static Location GeoCoordinateToLocation(GeoCoordinate geoCoordinate, string city, string country)
+            {
+                return new Location()
+                {
+                    Latitude = geoCoordinate.Latitude,
+                    Longitude = geoCoordinate.Longitude,
+                    Altitude = geoCoordinate.Altitude,
+                    City = city,
+                    Country = country,
+                    HorizontalAccuracy = geoCoordinate.HorizontalAccuracy,
+                    VerticalAccuracy = geoCoordinate.VerticalAccuracy,
+                    Speed = geoCoordinate.Speed,
+                    Course = geoCoordinate.Course,
+                    IsSelected = true,
+                    LastRemoteDataUpdate = null,
+                };
+            }
         }
 
         // TODO: check data before storing :(
         // http://stackoverflow.com/questions/4521435/what-specific-values-can-a-c-sharp-double-represent-that-a-sql-server-float-can
-        private void StoreLocation(GeoCoordinate geocoordinate)
+        private void StoreLocation(GeoCoordinate geocoordinate, string city, string country)
         {
             Location locationItem = null;
             using (var db = new LocationDataContext(LocationDataContext.DBConnectionString))
@@ -187,8 +250,8 @@ namespace WeatherInformation
                     locationItem.Latitude = geocoordinate.Latitude;
                     locationItem.Longitude = geocoordinate.Longitude;
                     locationItem.Altitude = geocoordinate.Altitude;
-                    locationItem.City = _city ?? "";
-                    locationItem.Country = _country ?? "";
+                    locationItem.City = city;
+                    locationItem.Country = country;
                     locationItem.HorizontalAccuracy = geocoordinate.HorizontalAccuracy;
                     locationItem.VerticalAccuracy = geocoordinate.VerticalAccuracy;
                     locationItem.Speed = geocoordinate.Speed;
@@ -203,8 +266,8 @@ namespace WeatherInformation
                         Latitude = geocoordinate.Latitude,
                         Longitude = geocoordinate.Longitude,
                         Altitude = geocoordinate.Altitude,
-                        City = _city ?? "",
-                        Country = _country ?? "",
+                        City = city,
+                        Country = country,
                         HorizontalAccuracy = geocoordinate.HorizontalAccuracy,
                         VerticalAccuracy = geocoordinate.VerticalAccuracy,
                         Speed = geocoordinate.Speed,
@@ -221,52 +284,57 @@ namespace WeatherInformation
             }
         }
 
-        private GeoCoordinate ConvertLocation(Location locationItem)
-        {
-            return new GeoCoordinate
-                (
-                locationItem.Latitude,
-                locationItem.Longitude,
-                locationItem.Altitude,
-                locationItem.HorizontalAccuracy,
-                locationItem.VerticalAccuracy,
-                locationItem.Speed,
-                locationItem.Course
-                );
-        }
-
         private void mapWeatherInformation_Tap(object sender, System.Windows.Input.GestureEventArgs e)
         {
+            // TODO: if exception from here application will crash (I guess)
             var point = e.GetPosition(this.mapWeatherInformation);
             GeoCoordinate geocoordinate = this.mapWeatherInformation.ConvertViewportPointToGeoCoordinate(point);
+            ShowProgressBar();
             ReverseGeocodeAndUpdateMap(geocoordinate);
         }
 
         private async void GetCurrentLocationButton_Click(object sender, RoutedEventArgs e)
         {
-            try
-            {
-                await this.GetCurrentLocationAndUpdateMap();
-            }
-            catch (Exception ex)
-            {
-                // TODO: make sure when exception in GetCurrentLocationAndUpdateMap we catch it here.
-                // TODO: if user changed the page, where is this going to appear?
-                MessageBox.Show(
-                    AppResources.NoticeErrorLocationAutodetection,
-                    AppResources.UnavailableAutomaticCurrentLocationMessageBox,
-                    MessageBoxButton.OK);
-            }
+            //Geolocator geolocator = new Geolocator();
+            //geolocator.DesiredAccuracyInMeters = 50;
+            //if (geolocator.LocationStatus != PositionStatus.Ready)
+            //{
+            //    // TODO: to use ToastPrompt from the Coding4Fun Toolkit (using NuGet)
+            //    return;
+            //}
+            
+            // TODO: if exception from here application will crash (I guess)
+            await this.GetCurrentLocationAndUpdateMap();
         }
 
         private void SaveLocationButton_Click(object sender, RoutedEventArgs e)
         {
-            // TODO: Could there some problem if user clicks button and thread is in this very moment updating map?
-            var geoCoordinate = this.mapWeatherInformation.Center;
+            // TODO: Could Center be null?
+            StoreLocation(this.mapWeatherInformation.Center, this.LocationTextCity.Text, this.LocationTextCountry.Text);
+        }
 
-            StoreLocation(geoCoordinate);
+        private void ShowProgressBar()
+        {
+            GetCurrentLocationButton.IsEnabled = false;
+            GetCurrentLocationButton.Visibility = Visibility.Collapsed;
+            SaveLocationButton.IsEnabled = false;
+            SaveLocationButton.Visibility = Visibility.Collapsed;
+            ProgressBarRemoteData.IsEnabled = true;
+            ProgressBarRemoteData.Visibility = Visibility.Visible;
         }
 
+        private void RemoveProgressBar()
+        {
+            GetCurrentLocationButton.IsEnabled = true;
+            GetCurrentLocationButton.Visibility = Visibility.Visible;
+            SaveLocationButton.IsEnabled = true;
+            SaveLocationButton.Visibility = Visibility.Visible;
+            ProgressBarRemoteData.IsEnabled = false;
+            ProgressBarRemoteData.Visibility = Visibility.Collapsed;
+        }
+
+
+
         private void ZoomOutButton_Click(object sender, RoutedEventArgs e)
         {
             this.mapWeatherInformation.ZoomLevel = this.mapWeatherInformation.ZoomLevel - 1;
diff --git a/WindowsPhone/WP8/WeatherInformation/WeatherInformation/Model/ReverseGeoCode.cs b/WindowsPhone/WP8/WeatherInformation/WeatherInformation/Model/ReverseGeoCode.cs
new file mode 100644 (file)
index 0000000..9b85676
--- /dev/null
@@ -0,0 +1,63 @@
+using Microsoft.Phone.Maps.Services;
+using System;
+using System.Collections.Generic;
+using System.Device.Location;
+using WeatherInformation.Resources;
+
+namespace WeatherInformation.Model
+{
+    class ReverseGeoCode
+    {
+        public interface IReverseGeoCode
+        {
+            void OnCompletedReverseGeoCode(GeoCoordinate geoCoordinate, string city, string country);
+        }
+
+        public IReverseGeoCode Page { get; set; }
+        public GeoCoordinate CoorDinate { get; set; }
+
+        public void DoReverseGeocode(GeoCoordinate geoCoordinate)
+        {
+            var currentReverseGeocodeQuery = new ReverseGeocodeQuery();
+            currentReverseGeocodeQuery.GeoCoordinate = geoCoordinate;
+            currentReverseGeocodeQuery.QueryCompleted += QueryCompletedCallback;
+            currentReverseGeocodeQuery.QueryAsync();
+        }
+
+        private void QueryCompletedCallback(object sender, QueryCompletedEventArgs<IList<MapLocation>> eventData)
+        {
+            // Commenting out because I have to disable progress dialog even if the async task was cancelled.
+            //if (eventData.Cancelled)
+            //{
+            //    // Be careful!!! If you throw exception from this point your program will finish with "Unhandled Exception".  
+            //    return;
+            //}
+
+            ReverseGeocodeQuery reverseGeocodeQuery = sender as ReverseGeocodeQuery;
+
+            // Default values
+            var city = AppResources.DefaultCity;
+            var country = AppResources.DefaultCountry;
+
+            Exception errorException = eventData.Error;
+            if (errorException == null)
+            {
+                if (eventData.Result.Count > 0)
+                {
+                    if (eventData.Result[0].Information != null
+                        && eventData.Result[0].Information.Address != null)
+                    {
+                        var address = eventData.Result[0].Information.Address;
+                        city = string.IsNullOrEmpty(address.City) ? AppResources.DefaultCity : address.City;
+                        country = string.IsNullOrEmpty(address.Country) ? AppResources.DefaultCountry : address.Country;
+                    }
+                }
+            }
+
+            if (Page != null)
+            {
+                Page.OnCompletedReverseGeoCode(reverseGeocodeQuery.GeoCoordinate, city,country);
+            }
+        }
+    }
+}
index f93fc75..af04f11 100644 (file)
@@ -42,8 +42,7 @@ namespace WeatherInformation.Model.Services
                 headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));
 
                 // Bypassing Windows cache
-                DateTime currentDate = DateTime.UtcNow;
-                string uriWindowsCacheSucks = String.Concat(uri, currentDate);
+                string uriWindowsCacheSucks = String.Concat(uri, "&time=", DateTime.UtcNow.Ticks);
 
                 // TODO: HttpCompletionOption, without it, by default, I am buffering the received data.
                 //       in this case it is not a problem but when receiving loads of bytes I do not
index 46b8cd5..ef0dd40 100644 (file)
@@ -5,7 +5,7 @@
     <Language code="es" />
     <Language code="qps-ploc" />
   </Languages>
-  <App xmlns="" ProductID="{71d9693c-af50-4d7f-ac37-811ba1f1a0d7}" Title="WeatherInformation" RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal" Author="Gustavo Martin Morcuende" Description="Sample description" Publisher="gumartinm.name" PublisherID="{78440e1b-f001-4e24-a0ea-dce58b830f07}">
+  <App xmlns="" ProductID="{71d9693c-af50-4d7f-ac37-811ba1f1a0d7}" Title="Weather Information" RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal" Author="Gustavo Martin Morcuende" Description="Sample description" Publisher="gumartinm.name" PublisherID="{78440e1b-f001-4e24-a0ea-dce58b830f07}">
     <IconPath IsRelative="true" IsResource="false">Assets\ApplicationIcon.png</IconPath>
     <Capabilities>
       <Capability Name="ID_CAP_NETWORKING" />
           <SmallImageURI IsRelative="true" IsResource="false">Assets\Tiles\FlipCycleTileSmall.png</SmallImageURI>
           <Count>0</Count>
           <BackgroundImageURI IsRelative="true" IsResource="false">Assets\Tiles\FlipCycleTileMedium.png</BackgroundImageURI>
-          <Title>WeatherInformation</Title>
-          <BackContent></BackContent>
-          <BackBackgroundImageURI></BackBackgroundImageURI>
-          <BackTitle></BackTitle>
-          <DeviceLockImageURI></DeviceLockImageURI>
-          <HasLarge></HasLarge>
+          <Title>Weather Information</Title>
+          <BackContent>
+          </BackContent>
+          <BackBackgroundImageURI IsRelative="true" IsResource="false">
+          </BackBackgroundImageURI>
+          <BackTitle>
+          </BackTitle>
+          <LargeBackgroundImageURI IsRelative="true" IsResource="false">Assets\Tiles\FlipCycleTileLarge.png</LargeBackgroundImageURI>
+          <LargeBackContent />
+          <LargeBackBackgroundImageURI IsRelative="true" IsResource="false">
+          </LargeBackBackgroundImageURI>
+          <DeviceLockImageURI IsRelative="true" IsResource="false">
+          </DeviceLockImageURI>
+          <HasLarge>True</HasLarge>
         </TemplateFlip>
       </PrimaryToken>
     </Tokens>
index e87e4bb..1326219 100644 (file)
@@ -124,15 +124,6 @@ namespace WeatherInformation.Resources {
         }
         
         /// <summary>
-        ///   Busca una cadena traducida similar a City, country.
-        /// </summary>
-        public static string LocationPageSubTitle {
-            get {
-                return ResourceManager.GetString("LocationPageSubTitle", resourceCulture);
-            }
-        }
-        
-        /// <summary>
         ///   Busca una cadena traducida similar a Pick your location.
         /// </summary>
         public static string LocationPageTitle {
@@ -511,6 +502,33 @@ namespace WeatherInformation.Resources {
         }
         
         /// <summary>
+        ///   Busca una cadena traducida similar a Notifications.
+        /// </summary>
+        public static string SettingsTileNotificationSwitchHeader {
+            get {
+                return ResourceManager.GetString("SettingsTileNotificationSwitchHeader", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Busca una cadena traducida similar a Off.
+        /// </summary>
+        public static string SettingsTileNotificationSwitchOff {
+            get {
+                return ResourceManager.GetString("SettingsTileNotificationSwitchOff", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Busca una cadena traducida similar a On.
+        /// </summary>
+        public static string SettingsTileNotificationSwitchOn {
+            get {
+                return ResourceManager.GetString("SettingsTileNotificationSwitchOn", resourceCulture);
+            }
+        }
+        
+        /// <summary>
         ///   Busca una cadena traducida similar a ºC.
         /// </summary>
         public static string TemperatureUnitsCentigradeSymbol {
index 4691c53..369b96c 100644 (file)
     <value>Previsión 5 días</value>
     <comment>Settings page, select forecast day numbers</comment>
   </data>
-  <data name="LocationPageSubTitle" xml:space="preserve">
-    <value>Ciudad, país</value>
-    <comment>Subtitle in location page</comment>
-  </data>
   <data name="LocationPageTitle" xml:space="preserve">
     <value>Selecciona tu localización</value>
     <comment>Title in location page</comment>
   <data name="SelectedDatePageDefaultDescription" xml:space="preserve">
     <value>descripción no disponible</value>
   </data>
+  <data name="SettingsTileNotificationSwitchOn" xml:space="preserve">
+    <value>Activado</value>
+    <comment>Settings page, switch notifications on</comment>
+  </data>
+  <data name="SettingsTileNotificationSwitchOff" xml:space="preserve">
+    <value>Desactivado</value>
+    <comment>Settings page, switch notifications off</comment>
+  </data>
+  <data name="SettingsTileNotificationSwitchHeader" xml:space="preserve">
+    <value>Notificaciones</value>
+    <comment>Settings page, switch notifications header</comment>
+  </data>
 </root>
\ No newline at end of file
index 73d1ca4..b4c6c13 100644 (file)
           <source>ºF</source>
           <target state="needs-review-translation" state-qualifier="mt-suggestion">º F</target>
         </trans-unit>
-        <trans-unit id="Resx/LocationPageSubTitle" translate="yes" xml:space="preserve">
-          <source>City, country</source>
-          <target state="translated" state-qualifier="mt-suggestion">Ciudad, país</target>
-          <note from="MultilingualBuild" annotates="source" priority="2">Subtitle in location page</note>
-          </trans-unit>
         <trans-unit id="Resx/LocationPageTitle" translate="yes" xml:space="preserve">
           <source>Pick your location</source>
           <target state="translated">Selecciona tu localización</target>
           <source>no description available</source>
           <target state="translated">descripción no disponible</target>
         </trans-unit>
+        <trans-unit id="Resx/SettingsTileNotificationSwitchHeader" translate="yes" xml:space="preserve">
+          <source>Notifications</source>
+          <target state="translated">Notificaciones</target>
+          <note from="MultilingualBuild" annotates="source" priority="2">Settings page, switch notifications header</note>
+          </trans-unit>
+        <trans-unit id="Resx/SettingsTileNotificationSwitchOn" translate="yes" xml:space="preserve">
+          <source>On</source>
+          <target state="translated">Activado</target>
+          <note from="MultilingualBuild" annotates="source" priority="2">Settings page, switch notifications on</note>
+          </trans-unit>
+        <trans-unit id="Resx/SettingsTileNotificationSwitchOff" translate="yes" xml:space="preserve">
+          <source>Off</source>
+          <target state="translated">Desactivado</target>
+          <note from="MultilingualBuild" annotates="source" priority="2">Settings page, switch notifications off</note>
+          </trans-unit>
         <trans-unit id="Resx/DefaultCity" translate="yes" xml:space="preserve">
           <source>City not found</source>
           <target state="translated">Ciudad no encontrada</target>
index 235ed58..7b12b47 100644 (file)
           <source>ºF</source>
           <target state="new">ºF</target>
         </trans-unit>
-        <trans-unit id="Resx/LocationPageSubTitle" translate="yes" xml:space="preserve">
-          <source>City, country</source>
-          <target state="new">City, country</target>
-          <note from="MultilingualBuild" annotates="source" priority="2">Subtitle in location page</note>
-        </trans-unit>
         <trans-unit id="Resx/LocationPageTitle" translate="yes" xml:space="preserve">
           <source>Pick your location</source>
           <target state="new">Pick your location</target>
           <source>no description available</source>
           <target state="new">no description available</target>
         </trans-unit>
+        <trans-unit id="Resx/SettingsTileNotificationSwitchHeader" translate="yes" xml:space="preserve">
+          <source>Notifications</source>
+          <target state="new">Notifications</target>
+          <note from="MultilingualBuild" annotates="source" priority="2">Settings page, switch notifications header</note>
+        </trans-unit>
+        <trans-unit id="Resx/SettingsTileNotificationSwitchOn" translate="yes" xml:space="preserve">
+          <source>On</source>
+          <target state="new">On</target>
+          <note from="MultilingualBuild" annotates="source" priority="2">Settings page, switch notifications on</note>
+        </trans-unit>
+        <trans-unit id="Resx/SettingsTileNotificationSwitchOff" translate="yes" xml:space="preserve">
+          <source>Off</source>
+          <target state="new">Off</target>
+          <note from="MultilingualBuild" annotates="source" priority="2">Settings page, switch notifications off</note>
+        </trans-unit>
         <trans-unit id="Resx/DefaultCity" translate="yes" xml:space="preserve">
           <source>City not found</source>
           <target state="new">City not found</target>
index f5959fe..ed45386 100644 (file)
   <data name="TemperatureUnitsFahrenheitSymbol" xml:space="preserve">
     <value>ºF</value>
   </data>
-  <data name="LocationPageSubTitle" xml:space="preserve">
-    <value>City, country</value>
-    <comment>Subtitle in location page</comment>
-  </data>
   <data name="LocationPageTitle" xml:space="preserve">
     <value>Pick your location</value>
     <comment>Title in location page</comment>
   <data name="SelectedDatePageDefaultDescription" xml:space="preserve">
     <value>no description available</value>
   </data>
+  <data name="SettingsTileNotificationSwitchHeader" xml:space="preserve">
+    <value>Notifications</value>
+    <comment>Settings page, switch notifications header</comment>
+  </data>
+  <data name="SettingsTileNotificationSwitchOff" xml:space="preserve">
+    <value>Off</value>
+    <comment>Settings page, switch notifications off</comment>
+  </data>
+  <data name="SettingsTileNotificationSwitchOn" xml:space="preserve">
+    <value>On</value>
+    <comment>Settings page, switch notifications on</comment>
+  </data>
 </root>
\ No newline at end of file
index b0555a7..d411ff4 100644 (file)
@@ -36,6 +36,7 @@
                             <RowDefinition Height="Auto" />
                             <RowDefinition Height="Auto" />
                             <RowDefinition Height="Auto" />
+                            <RowDefinition Height="Auto" />
                         </Grid.RowDefinitions>
                         <Grid.ColumnDefinitions>
                             <ColumnDefinition Width="*" />
                                 <toolkit:ListPickerItem Content="{Binding LocalizedResources.SettingsForecastDayNumbersSelectionFourteen, Mode=OneWay, Source={StaticResource LocalizedStrings}}"/>
                             </toolkit:ListPicker>
                         </StackPanel>
+                        <StackPanel Grid.Column="0" Grid.Row="3" Orientation="Vertical" VerticalAlignment="Center" Margin="0,30,0,0">
+                            <toolkit:ToggleSwitch x:Name="TileNotificationSwitch"
+                                                  Content="{Binding TileNotificationSwitchContentSetting, Mode=TwoWay, Source={StaticResource SettingsViewModelDataSource}}"
+                                                  Header="{Binding LocalizedResources.SettingsTileNotificationSwitchHeader, Mode=OneWay, Source={StaticResource LocalizedStrings}}"
+                                                  SwitchForeground="#FF0049E5"
+                                                  IsChecked="{Binding TileNotificationSwitchSetting, Mode=TwoWay}"/>
+                        </StackPanel>
                     </Grid>
                 </ScrollViewer>
             </phone:PivotItem>
index 3babef1..f002adf 100644 (file)
@@ -1,6 +1,7 @@
 using System;
 using System.ComponentModel;
 using System.IO.IsolatedStorage;
+using WeatherInformation.Resources;
 
 namespace WeatherInformation.ViewModels
 {
@@ -14,11 +15,15 @@ namespace WeatherInformation.ViewModels
         private const string _languageSelectionSettingKeyName = "LanguageSelection";
         private const string _temperatureUnitsSelectionSettingKeyName = "TemperatureUnitsSelection";
         private const string _forecastDayNumbersSelectionSelectionSettingKeyName = "ForecastDayNumbersSelection";
+        private const string _tileNotificationSwitchSettingKeyName = "TileNotificationSwitch";
+        private const string _tileNotificationSwitchContentSettingKeyName = "TileNotificationSwitchContentSetting";
 
-        // The default value of ListPicker _settings
+        // The default values
         private const int _languageSelectionSettingDefault = 0;
         private const int _temperatureUnitsSelectionSettingDefault = 0;
         private const int _forecastDayNumbersSelectionSettingDefault = 0;
+        // Notifications off.
+        private const bool _tileNotificationSwitchSettingDefault = false;
 
 
         public SettingsViewModel()
@@ -91,6 +96,50 @@ namespace WeatherInformation.ViewModels
         }
 
         /// <summary>
+        /// Turns on/off tile notification.
+        /// </summary>
+        public bool TileNotificationSwitchSetting
+        {
+            get
+            {
+                bool value = GetValueOrDefault<bool>(_tileNotificationSwitchSettingKeyName, _tileNotificationSwitchSettingDefault);
+                if (value)
+                {
+                    TileNotificationSwitchContentSetting = AppResources.SettingsTileNotificationSwitchOn;
+                }
+                else
+                {
+                    TileNotificationSwitchContentSetting = AppResources.SettingsTileNotificationSwitchOff;       
+                }
+                NotifyPropertyChanged(_tileNotificationSwitchContentSettingKeyName);
+                return value;
+            }
+            set
+            {
+                if (AddOrUpdateValue(_tileNotificationSwitchSettingKeyName, value))
+                {
+                    Save();
+                }
+                if (value)
+                {
+                    TileNotificationSwitchContentSetting = AppResources.SettingsTileNotificationSwitchOn;
+                }
+                else
+                {
+                    TileNotificationSwitchContentSetting = AppResources.SettingsTileNotificationSwitchOff;
+                }
+                NotifyPropertyChanged(_tileNotificationSwitchSettingKeyName);
+                NotifyPropertyChanged(_tileNotificationSwitchContentSettingKeyName);
+            }
+        }
+
+        /// <summary>
+        /// Changes content switch notification setting.
+        /// </summary>
+        public string TileNotificationSwitchContentSetting { get; private set; }
+
+
+        /// <summary>
         /// Get the current value of the setting, or if it is not found, set the 
         /// setting to the default value.
         /// </summary>
index 820d1fe..0ae2007 100644 (file)
     <Compile Include="Model\JsonDataParser\JsonParser.cs" />
     <Compile Include="Model\Location.cs" />
     <Compile Include="Model\LocationDataContext.cs" />
+    <Compile Include="Model\ReverseGeoCode.cs" />
     <Compile Include="Model\WeatherData.cs" />
     <Compile Include="Model\Services\CustomHTTPClient.cs" />
     <Compile Include="Model\Services\ServiceParser.cs" />
       <LastGenOutput>AppResources.qps-ploc.resx</LastGenOutput>
     </XliffResource>
   </ItemGroup>
+  <ItemGroup />
   <Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).$(TargetFrameworkVersion).Overrides.targets" />
   <Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).CSharp.targets" />
   <Import Project="$(MSBuildExtensionsPath)\Microsoft\Multilingual App Toolkit\Microsoft.Multilingual.ResxResources.targets" Label="MultilingualAppToolkit" />