From dddc4d07818577471baa38a766037083bb368be6 Mon Sep 17 00:00:00 2001 From: "gu.martinm@gmail.com" Date: Tue, 12 Aug 2014 00:49:59 +0200 Subject: [PATCH] WeatherInformation WP8: many improvements --- .../WeatherInformation/App.xaml.cs | 117 ++++++++++++++++++- .../WeatherInformation/MainPage.xaml.cs | 129 +++------------------ .../WeatherInformation/MapPage.xaml.cs | 11 +- .../WeatherInformation/Model/Location.cs | 19 --- .../Model/Services/ServiceParser.cs | 21 ++++ .../WeatherInformation/ViewModels/MainViewModel.cs | 7 -- 6 files changed, 156 insertions(+), 148 deletions(-) diff --git a/WindowsPhone/WeatherInformation/WeatherInformation/App.xaml.cs b/WindowsPhone/WeatherInformation/WeatherInformation/App.xaml.cs index 0d1fb52..dfda732 100644 --- a/WindowsPhone/WeatherInformation/WeatherInformation/App.xaml.cs +++ b/WindowsPhone/WeatherInformation/WeatherInformation/App.xaml.cs @@ -112,6 +112,79 @@ namespace WeatherInformation } } + public bool IsStoredDataFresh() + { + // Check to see if the data is fresh. + // This example uses 30 seconds as the valid time window to make it easy to test. + // Real apps will use a larger window. + + // Check the time elapsed since data was last saved to isolated storage. + TimeSpan TimeSinceLastSave = TimeSpan.FromSeconds(0); + if (IsolatedStorageSettings.ApplicationSettings.Contains("DataLastSavedTime")) + { + DateTime dataLastSaveTime = (DateTime)IsolatedStorageSettings.ApplicationSettings["DataLastSavedTime"]; + TimeSinceLastSave = DateTime.Now - dataLastSaveTime; + } + + if (TimeSinceLastSave.TotalSeconds < 30) + { + return true; + } + + return false; + } + + public WeatherData GetIsolatedStoredData() + { + string JSONRemoteCurrentWeather = null; + string JSONRemoteForecastWeather = null; + string city = null; + string country = null; + + using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (isoStore.FileExists("JSONRemoteCurrentWeatherFile.txt") && + isoStore.FileExists("JSONRemoteForecastWeatherFile.txt") && + isoStore.FileExists("CityFile.txt") && + isoStore.FileExists("CountryFile.txt")) + { + using (IsolatedStorageFileStream file = isoStore.OpenFile("JSONRemoteCurrentWeatherFile.txt", FileMode.Open)) + using (StreamReader sr = new StreamReader(file)) + { + // This method loads the data from isolated storage, if it is available. + JSONRemoteCurrentWeather = sr.ReadLine(); + } + + using (IsolatedStorageFileStream file = isoStore.OpenFile("CityFile.txt", FileMode.Open)) + using (StreamReader sr = new StreamReader(file)) + { + // This method loads the data from isolated storage, if it is available. + city = sr.ReadLine(); + } + + using (IsolatedStorageFileStream file = isoStore.OpenFile("CountryFile.txt", FileMode.Open)) + using (StreamReader sr = new StreamReader(file)) + { + // This method loads the data from isolated storage, if it is available. + country = sr.ReadLine(); + } + } + } + + if (!string.IsNullOrEmpty(JSONRemoteCurrentWeather) && !string.IsNullOrEmpty(JSONRemoteForecastWeather)) + { + // TODO: I am repeating this code 2 times. What could I do to improve it? + var parser = new ServiceParser(new JsonParser()); + var weatherData = parser.WeatherDataParser(JSONRemoteForecastWeather, JSONRemoteCurrentWeather); + weatherData.City = city ?? ""; + weatherData.Country = country ?? ""; + + return weatherData; + } + + return null; + } + // no way of moving temporary file atomically with IsolatedStorageFile. LINUX OWNS MICROSOFT. private void SaveDataToIsolatedStorage(string fileName, string value) { @@ -154,8 +227,6 @@ namespace WeatherInformation } } - - // Código para ejecutar cuando la aplicación se inicia (p.ej. a partir de Inicio) // Este código no se ejecutará cuando la aplicación se reactive private void Application_Launching(object sender, LaunchingEventArgs e) @@ -179,19 +250,33 @@ namespace WeatherInformation // Check to see if the key for the application state data is in the State dictionary. string JSONRemoteForecastWeather = null; string JSONRemoteCurrentWeather = null; + string city = null; + string country = null; if (PhoneApplicationService.Current.State.ContainsKey("JSONRemoteForecastWeather") && - PhoneApplicationService.Current.State.ContainsKey("JSONRemoteCurrentWeather")) + PhoneApplicationService.Current.State.ContainsKey("JSONRemoteCurrentWeather") && + PhoneApplicationService.Current.State.ContainsKey("City") && + PhoneApplicationService.Current.State.ContainsKey("Country")) { // If it exists, assign the data to the application member variable. JSONRemoteForecastWeather = PhoneApplicationService.Current.State["JSONRemoteForecastWeather"] as string; // If it exists, assign the data to the application member variable. JSONRemoteCurrentWeather = PhoneApplicationService.Current.State["JSONRemoteCurrentWeather"] as string; + + // If it exists, assign the data to the application member variable. + city = PhoneApplicationService.Current.State["City"] as string; + + // If it exists, assign the data to the application member variable. + country = PhoneApplicationService.Current.State["Country"] as string; } if (!string.IsNullOrEmpty(JSONRemoteCurrentWeather) && !string.IsNullOrEmpty(JSONRemoteForecastWeather)) { - weatherData = WeatherDataParser(JSONRemoteForecastWeather, JSONRemoteCurrentWeather); + // TODO: I am repeating this code 2 times. What could I do to improve it? + var parser = new ServiceParser(new JsonParser()); + weatherData = parser.WeatherDataParser(JSONRemoteForecastWeather, JSONRemoteCurrentWeather); + weatherData.City = city ?? ""; + weatherData.Country = country ?? ""; } ApplicationDataObject = weatherData; @@ -211,6 +296,8 @@ namespace WeatherInformation if (!string.IsNullOrEmpty(weatherData.JSONRemoteCurrent) && !string.IsNullOrEmpty(weatherData.JSONRemoteForecast)) { + // TODO: too many files? Remember there is a time limit for running this method!!! :/ + // Store it in the State dictionary. PhoneApplicationService.Current.State["JSONRemoteForecastWeather"] = weatherData.JSONRemoteForecast; @@ -223,6 +310,18 @@ namespace WeatherInformation // Also store it in isolated storage, in case the application is never reactivated. SaveDataToIsolatedStorage("JSONRemoteCurrentWeatherFile.txt", weatherData.JSONRemoteCurrent); + // Store it in the State dictionary. + PhoneApplicationService.Current.State["City"] = weatherData.City; + + // Also store it in isolated storage, in case the application is never reactivated. + SaveDataToIsolatedStorage("CityFile.txt", weatherData.City); + + // Store it in the State dictionary. + PhoneApplicationService.Current.State["Country"] = weatherData.Country; + + // Also store it in isolated storage, in case the application is never reactivated. + SaveDataToIsolatedStorage("CountryFile.txt", weatherData.Country); + IsolatedStorageSettings.ApplicationSettings["DataLastSavedTime"] = DateTime.Now; } } @@ -240,16 +339,24 @@ namespace WeatherInformation if (!string.IsNullOrEmpty(weatherData.JSONRemoteForecast) && !string.IsNullOrEmpty(weatherData.JSONRemoteCurrent)) { + // TODO: too many files? Remember there is a time limit for running this method!!! :/ + // Also store it in isolated storage, in case the application is never reactivated. SaveDataToIsolatedStorage("JSONRemoteForecastWeatherFile.txt", weatherData.JSONRemoteForecast); // Also store it in isolated storage, in case the application is never reactivated. SaveDataToIsolatedStorage("JSONRemoteCurrentWeatherFile.txt", weatherData.JSONRemoteCurrent); + // Also store it in isolated storage, in case the application is never reactivated. + SaveDataToIsolatedStorage("CityFile.txt", weatherData.City); + + // Also store it in isolated storage, in case the application is never reactivated. + SaveDataToIsolatedStorage("CountryFile.txt", weatherData.Country); + IsolatedStorageSettings.ApplicationSettings["DataLastSavedTime"] = DateTime.Now; } } - } + } // Código para ejecutar si hay un error de navegación private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e) diff --git a/WindowsPhone/WeatherInformation/WeatherInformation/MainPage.xaml.cs b/WindowsPhone/WeatherInformation/WeatherInformation/MainPage.xaml.cs index a7e3d63..9a9658c 100644 --- a/WindowsPhone/WeatherInformation/WeatherInformation/MainPage.xaml.cs +++ b/WindowsPhone/WeatherInformation/WeatherInformation/MainPage.xaml.cs @@ -44,7 +44,6 @@ namespace WeatherInformation // Connect to the database and instantiate data context. _locationDB = new LocationDataContext(LocationDataContext.DBConnectionString); - _locationItem = _locationDB.Locations.Where(location => location.IsSelected).FirstOrDefault(); // Código de ejemplo para traducir ApplicationBar //BuildLocalizedApplicationBar(); } @@ -70,6 +69,8 @@ namespace WeatherInformation // and it has remained in memory, this value will continue to be false. _isNewPageInstance = false; + _locationItem = _locationDB.Locations.Where(location => location.IsSelected).FirstOrDefault(); + UpdateApplicationDataUI(); } @@ -79,6 +80,7 @@ namespace WeatherInformation base.OnNavigatedFrom(e); // Save changes to the database. + // TODO: How does DataContext work? How does it know what data was modified? Is it wasting memory? :( _locationDB.SubmitChanges(); // No calling _locationDB.Dispose? :/ @@ -96,7 +98,10 @@ namespace WeatherInformation // set the page's data object from the application member variable. // TODO: I am setting and getting ApplicationDataObject from different threads!!!! What if I do not see its last value? Do I need synchronization? :/ WeatherData weatherData = (Application.Current as WeatherInformation.App).ApplicationDataObject; - if (weatherData != null && !_locationItem.IsNewLocation && IsStoredDataFresh()) + if (weatherData != null && + !_locationItem.IsNewLocation && + // TODO: NO ESTOY USANDO GetIsolatedStoredData!!!!!! :( + (Application.Current as WeatherInformation.App).IsStoredDataFresh()) { UpdateUI(); } @@ -132,11 +137,7 @@ namespace WeatherInformation _mainViewModel.LoadData(weatherData); - var locationItem = _locationDB.Locations.Where(location => location.IsSelected).FirstOrDefault(); - if (locationItem != null) - { - locationItem.IsNewLocation = false; - } + _locationItem.IsNewLocation = false; } } @@ -151,28 +152,11 @@ namespace WeatherInformation NavigationService.Navigate(new Uri(uri, UriKind.Relative)); } - public void GetDataAsync() - { - // Call the GetData method on a new thread. - // TODO: Are there too many threads? HttpClient is going to create more... (threadpools and stuff like that...) - Thread t = new Thread(new ThreadStart(GetData)); - t.Start(); - } - - async private void GetData() + async private void GetDataAsync() { - // Check to see if data exists in storage and see if the data is fresh. - WeatherData weatherData = GetIsolatedStoredData(); - - if ((weatherData != null) && IsStoredDataFresh() && !_locationItem.IsNewLocation) - { - (Application.Current as WeatherInformation.App).ApplicationDataObject = weatherData; - } - else - { - // Otherwise, it gets the data from the web. - (Application.Current as WeatherInformation.App).ApplicationDataObject = await LoadDataAsync(); - } + // Gets the data from the web. + // TODO: multiple threads writing/reading same data :( + (Application.Current as WeatherInformation.App).ApplicationDataObject = await LoadDataAsync(); } /// @@ -196,93 +180,8 @@ namespace WeatherInformation AppResources.APIVersionOpenWeatherMap, latitude, longitude, resultsNumber); string JSONRemoteCurrentWeather = await httpClient.GetWeatherDataAsync(formattedCurrentURL); - return WeatherDataParser(JSONRemoteForecastWeather, JSONRemoteCurrentWeather); - } - - // ESTE METODO ERA PARA CARGAR LOS DATOS JSON NO LOS DE LA BASE DE DATOS. HAY QUE REPENSAR ESTO :( - private bool IsStoredDataFresh() - { - // Check to see if the data is fresh. - // This example uses 30 seconds as the valid time window to make it easy to test. - // Real apps will use a larger window. - - // Check the time elapsed since data was last saved to storage. - DateTime dataLastSaveTime = _locationItem.StoredTime; - TimeSpan timeSinceLastSave = DateTime.Now - dataLastSaveTime; - - if (timeSinceLastSave.TotalSeconds < 30) - { - return true; - } - - return false; - } - - private WeatherData GetIsolatedStoredData() - { - string JSONRemoteCurrentWeather = null; - string JSONRemoteForecastWeather = null; - - using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication()) - { - if (isoStore.FileExists("JSONRemoteCurrentWeatherFile.txt") && - isoStore.FileExists("JSONRemoteForecastWeatherFile.txt")) - { - using (IsolatedStorageFileStream file = isoStore.OpenFile("JSONRemoteCurrentWeatherFile.txt", FileMode.Open)) - using (StreamReader sr = new StreamReader(file)) - { - // This method loads the data from isolated storage, if it is available. - JSONRemoteCurrentWeather = sr.ReadLine(); - } - - using (IsolatedStorageFileStream file = isoStore.OpenFile("JSONRemoteCurrentWeatherFile.txt", FileMode.Open)) - using (StreamReader sr = new StreamReader(file)) - { - // This method loads the data from isolated storage, if it is available. - JSONRemoteForecastWeather = sr.ReadLine(); - } - } - } - - if (!string.IsNullOrEmpty(JSONRemoteCurrentWeather) && !string.IsNullOrEmpty(JSONRemoteForecastWeather)) - { - return WeatherDataParser(JSONRemoteForecastWeather, JSONRemoteCurrentWeather); - } - - return null; - } - - private ForecastWeather ForecastWeatherParser(string remoteForecastWeatherData) - { - ServiceParser parser = new ServiceParser(new JsonParser()); - return parser.GetForecastWeather(remoteForecastWeatherData); - } - - private CurrentWeather CurrentWeatherParser(string remoteCurrentWeatherData) - { - ServiceParser parser = new ServiceParser(new JsonParser()); - return parser.GetCurrentWeather(remoteCurrentWeatherData); - } - - private WeatherData WeatherDataParser(string JSONRemoteForecastWeather, string JSONRemoteCurrentWeather) - { - if (string.IsNullOrEmpty(JSONRemoteForecastWeather)) - { - throw new ArgumentException("Missing argument", "JSONRemoteForecastWeather"); - } - if (string.IsNullOrEmpty(JSONRemoteCurrentWeather)) - { - throw new ArgumentException("Missing argument", "JSONRemoteCurrentWeather"); - } - - return new WeatherData - { - JSONRemoteCurrent = JSONRemoteCurrentWeather, - JSONRemoteForecast = JSONRemoteForecastWeather, - RemoteCurrent = CurrentWeatherParser(JSONRemoteCurrentWeather), - RemoteForecast = ForecastWeatherParser(JSONRemoteForecastWeather), - WasThereRemoteError = false - }; + var parser = new ServiceParser(new JsonParser()); + return parser.WeatherDataParser(JSONRemoteForecastWeather, JSONRemoteCurrentWeather); } private void Location_Click(object sender, EventArgs e) diff --git a/WindowsPhone/WeatherInformation/WeatherInformation/MapPage.xaml.cs b/WindowsPhone/WeatherInformation/WeatherInformation/MapPage.xaml.cs index b6320db..1c1575a 100644 --- a/WindowsPhone/WeatherInformation/WeatherInformation/MapPage.xaml.cs +++ b/WindowsPhone/WeatherInformation/WeatherInformation/MapPage.xaml.cs @@ -26,6 +26,9 @@ namespace WeatherInformation { // Data context for the local database private LocationDataContext _locationDB; + // TODO anything better than these two instance fields? :( + private string _city; + private string _country; public MapPage() { @@ -143,6 +146,8 @@ namespace WeatherInformation // TODO: What if there is no city or country. Is there null value or empty string? string cityCountry = String.Format(CultureInfo.InvariantCulture, "{0}, {1}", city, country); this.LocationTextCityCountry.Text = cityCountry; + _city = city; + _country = country; // Add the MapLayer to the Map. this.mapWeatherInformation.Layers.Add(myLocationLayer); } @@ -174,13 +179,14 @@ namespace WeatherInformation locationItem.Latitude = geocoordinate.Latitude; locationItem.Longitude = geocoordinate.Longitude; locationItem.Altitude = geocoordinate.Altitude; + locationItem.City = _city ?? ""; + locationItem.Country = _country ?? ""; locationItem.HorizontalAccuracy = geocoordinate.HorizontalAccuracy; locationItem.VerticalAccuracy = geocoordinate.VerticalAccuracy; locationItem.Speed = geocoordinate.Speed; locationItem.Course = geocoordinate.Course; locationItem.IsNewLocation = true; locationItem.IsSelected = true; - locationItem.StoredTime = DateTime.Now; } else { @@ -189,13 +195,14 @@ namespace WeatherInformation 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, IsNewLocation = true, IsSelected = true, - StoredTime = DateTime.Now }; // Add a location item to the local database. diff --git a/WindowsPhone/WeatherInformation/WeatherInformation/Model/Location.cs b/WindowsPhone/WeatherInformation/WeatherInformation/Model/Location.cs index 1167e66..43f93c6 100644 --- a/WindowsPhone/WeatherInformation/WeatherInformation/Model/Location.cs +++ b/WindowsPhone/WeatherInformation/WeatherInformation/Model/Location.cs @@ -244,25 +244,6 @@ namespace WeatherInformation.Model } } - private DateTime _storedTime; - [Column(CanBeNull = false)] - public DateTime StoredTime - { - get - { - return _storedTime; - } - set - { - if (_storedTime != value) - { - NotifyPropertyChanging("StoredTime"); - _storedTime = value; - NotifyPropertyChanged("StoredTime"); - } - } - } - #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; diff --git a/WindowsPhone/WeatherInformation/WeatherInformation/Model/Services/ServiceParser.cs b/WindowsPhone/WeatherInformation/WeatherInformation/Model/Services/ServiceParser.cs index d855c90..dc5753e 100644 --- a/WindowsPhone/WeatherInformation/WeatherInformation/Model/Services/ServiceParser.cs +++ b/WindowsPhone/WeatherInformation/WeatherInformation/Model/Services/ServiceParser.cs @@ -27,5 +27,26 @@ namespace WeatherInformation.Model.Services { return this._jsonParser.ParserWeatherData(jsonData); } + + public WeatherData WeatherDataParser(string JSONRemoteForecastWeather, string JSONRemoteCurrentWeather) + { + if (string.IsNullOrEmpty(JSONRemoteForecastWeather)) + { + throw new ArgumentException("Missing argument", "JSONRemoteForecastWeather"); + } + if (string.IsNullOrEmpty(JSONRemoteCurrentWeather)) + { + throw new ArgumentException("Missing argument", "JSONRemoteCurrentWeather"); + } + + return new WeatherData + { + JSONRemoteCurrent = JSONRemoteCurrentWeather, + JSONRemoteForecast = JSONRemoteForecastWeather, + RemoteCurrent = GetCurrentWeather(JSONRemoteCurrentWeather), + RemoteForecast = GetForecastWeather(JSONRemoteForecastWeather), + WasThereRemoteError = false + }; + } } } diff --git a/WindowsPhone/WeatherInformation/WeatherInformation/ViewModels/MainViewModel.cs b/WindowsPhone/WeatherInformation/WeatherInformation/ViewModels/MainViewModel.cs index a9e6aa1..1248ddf 100644 --- a/WindowsPhone/WeatherInformation/WeatherInformation/ViewModels/MainViewModel.cs +++ b/WindowsPhone/WeatherInformation/WeatherInformation/ViewModels/MainViewModel.cs @@ -3,12 +3,7 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.Globalization; using System.IO.IsolatedStorage; -using System.Threading.Tasks; -using System.Windows; using WeatherInformation.Model; -using WeatherInformation.Model.ForecastWeatherParser; -using WeatherInformation.Model.JsonDataParser; -using WeatherInformation.Model.Services; using WeatherInformation.Resources; namespace WeatherInformation.ViewModels @@ -27,13 +22,11 @@ namespace WeatherInformation.ViewModels // Settings private readonly IsolatedStorageSettings _settings; - private readonly ServiceParser _serviceParser; public MainViewModel() { this.ForecastItems = new ObservableCollection(); this.CurrentItems = new ObservableCollection(); - this._serviceParser = new ServiceParser(new JsonParser()); // Get the _settings for this application. _settings = IsolatedStorageSettings.ApplicationSettings; -- 2.1.4