From a297460f379e9f03fa977b1a60028b3621ec1c98 Mon Sep 17 00:00:00 2001 From: Claudiu Farcas Date: Sat, 9 Apr 2011 01:47:50 +0300 Subject: [PATCH] friends caching small improvements --- main/Libs/WPImageCaching/ImageCache.cs | 111 ++++++++++++--- .../WPImageCaching/ImageCacheConverter.cs | 4 + main/Libs/WPImageCaching/ImageCacheItem.cs | 11 +- .../WPImageCaching/ImageDownloadHelper.cs | 129 ++++++++++++------ .../WPImageCaching/SerializationHelper.cs | 21 +++ .../Libs/WPImageCaching/WPImageCaching.csproj | 22 +++ main/MyFriendsAround.WP7/App.xaml.cs | 30 ++-- .../Service/ServiceAgent.cs | 9 +- .../ViewModel/MainViewModel.cs | 52 +++---- main/MyFriendsAround.WP7/Views/MainPage.xaml | 52 ++++--- main/MyFriendsAround.sln | 4 +- 11 files changed, 323 insertions(+), 122 deletions(-) create mode 100644 main/Libs/WPImageCaching/SerializationHelper.cs diff --git a/main/Libs/WPImageCaching/ImageCache.cs b/main/Libs/WPImageCaching/ImageCache.cs index a49e5f1..fa555fd 100644 --- a/main/Libs/WPImageCaching/ImageCache.cs +++ b/main/Libs/WPImageCaching/ImageCache.cs @@ -13,6 +13,7 @@ using System.Windows.Media.Imaging; using System.IO; using System.IO.IsolatedStorage; using System.Runtime.Serialization; +using Microsoft.Phone; namespace WPImageCaching { @@ -20,8 +21,66 @@ namespace WPImageCaching { private const string IMAGECACHEFILE = "imagecachefile.nfo"; internal static Dictionary imageCache; + internal static object _lock = new object(); + public static BitmapImage GetImage(string url) + { + BitmapImage image = new BitmapImage(); + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + //Wenn im Designmodus von Blend oder Visual Studio + image.UriSource = new Uri(url); + return image; + } + + if (imageCache == null) + { + LoadCachedImageInfo(); + } + + //Prüfen auf ein vorhandenes gespeichertes Bild + if (imageCache.ContainsKey(url)) + { + //Prüfen auf Gültigkeit des Bildes + if (DateTime.Compare(DateTime.Now, imageCache[url].Expiration) >= 0) + { + ImageDownloadHelper.DownloadImage(url, image, imageCache[url]); + } + else + { + if (IsolatedStorageFile.GetUserStoreForApplication().FileExists(imageCache[url].LocalFilename)) + { + //Bild ist noch gültig + using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication()) + { + lock (_lock) + { + using ( + IsolatedStorageFileStream fs = isf.OpenFile(imageCache[url].LocalFilename, + FileMode.Open)) + { + image.SetSource(fs); + return image; + } + } + } + } + else + { + ImageDownloadHelper.DownloadImage(url, image, imageCache[url]); + } + } + } + else + { + //Bild noch nicht gespeichert + ImageCacheItem item = new ImageCacheItem(); + ImageDownloadHelper.DownloadImage(url, image, item); + } + return image; + } + public static BitmapImage GetImage(BitmapImage image) { string url = image.UriSource.ToString(); @@ -65,31 +124,49 @@ namespace WPImageCaching //Laden der Bildinformationen private static void LoadCachedImageInfo() { - if (IsolatedStorageFile.GetUserStoreForApplication().FileExists(IMAGECACHEFILE)) + using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication()) { - IsolatedStorageFileStream fs = - IsolatedStorageFile.GetUserStoreForApplication().OpenFile(IMAGECACHEFILE,FileMode.Open); + lock (_lock) + { + if (isf.FileExists(IMAGECACHEFILE)) + { + using (IsolatedStorageFileStream fs = isf.OpenFile(IMAGECACHEFILE, FileMode.Open)) + { + using (StreamReader sr = new StreamReader(fs)) + { + string serObj = sr.ReadToEnd(); + imageCache = SerializationHelper.Deserialize>(serObj); + } - DataContractSerializer dcs = new DataContractSerializer(typeof(Dictionary)); - imageCache = (Dictionary)dcs.ReadObject(fs); - fs.Close(); - } - else - { - imageCache = new Dictionary(); + } + } + } + if (imageCache == null) + { + imageCache = new Dictionary(); + } } } //Speichern der Bildinformationen internal static void SaveCachedImageInfo() { - IsolatedStorageFileStream fs = - IsolatedStorageFile.GetUserStoreForApplication().CreateFile(IMAGECACHEFILE); - - DataContractSerializer dcs = new DataContractSerializer(typeof(Dictionary)); - dcs.WriteObject(fs, imageCache); - fs.Flush(); - fs.Close(); + using (IsolatedStorageFile sf = IsolatedStorageFile.GetUserStoreForApplication()) + { + lock (_lock) + { + if (sf.FileExists(IMAGECACHEFILE)) + sf.DeleteFile(IMAGECACHEFILE); + using (IsolatedStorageFileStream fs = sf.CreateFile(IMAGECACHEFILE)) + { + using (StreamWriter sw = new StreamWriter(fs)) + { + string serObj = SerializationHelper.Serialize(imageCache); + sw.Write(serObj); + } + } + } + } } } } diff --git a/main/Libs/WPImageCaching/ImageCacheConverter.cs b/main/Libs/WPImageCaching/ImageCacheConverter.cs index c680f50..c89c4d3 100644 --- a/main/Libs/WPImageCaching/ImageCacheConverter.cs +++ b/main/Libs/WPImageCaching/ImageCacheConverter.cs @@ -23,6 +23,10 @@ namespace WPImageCaching { return ImageCache.GetImage((BitmapImage)value); } + if (value is string && !string.IsNullOrEmpty(value as string)) + { + return ImageCache.GetImage((string)value); + } else { return value; diff --git a/main/Libs/WPImageCaching/ImageCacheItem.cs b/main/Libs/WPImageCaching/ImageCacheItem.cs index ec7e31d..dd708cb 100644 --- a/main/Libs/WPImageCaching/ImageCacheItem.cs +++ b/main/Libs/WPImageCaching/ImageCacheItem.cs @@ -8,14 +8,23 @@ using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; +using System.Runtime.Serialization; namespace WPImageCaching { //Beinhaltet die Informationen eines Bildes - internal class ImageCacheItem + [DataContract] + public class ImageCacheItem { + public ImageCacheItem() + { + } + + [DataMember] public string LocalFilename { get; set; } + [DataMember] public string ImageID { get; set; } + [DataMember] public DateTime Expiration { get; set; } } } diff --git a/main/Libs/WPImageCaching/ImageDownloadHelper.cs b/main/Libs/WPImageCaching/ImageDownloadHelper.cs index d37b075..f06092e 100644 --- a/main/Libs/WPImageCaching/ImageDownloadHelper.cs +++ b/main/Libs/WPImageCaching/ImageDownloadHelper.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.IO.IsolatedStorage; using System.IO; using System.Windows.Media.Imaging; +using System.Diagnostics; namespace WPImageCaching { @@ -35,7 +36,7 @@ namespace WPImageCaching //Erstellen der Abfrage var wc = (HttpWebRequest)HttpWebRequest.Create(url); - if (item.ImageID!=null) + if (item.ImageID != null) { //Prüfen, ob das Bild im Web immer noch aktuell ist wc.Headers["If-None-Match"] = item.ImageID; @@ -62,53 +63,103 @@ namespace WPImageCaching //Bild wurde nicht geändert seit dem letzten Aufruf if (response.StatusCode == HttpStatusCode.NotModified) { - Deployment.Current.Dispatcher.BeginInvoke(() => transfer.Image.SetSource(IsolatedStorageFile.GetUserStoreForApplication().OpenFile(transfer.Item.LocalFilename, FileMode.Open))); + Deployment.Current.Dispatcher.BeginInvoke(() => + { + using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication()) + { + lock (ImageCache._lock) + { + using (IsolatedStorageFileStream fs =isf.OpenFile(transfer.Item.LocalFilename,FileMode.Open)) + { + transfer.Image.SetSource(fs); + } + } + } + }); return; } - //Hat das Bild eine neue ID? - if (response.Headers["ETag"] != null) + lock (ImageCache._lock) { - transfer.Item.ImageID = response.Headers["ETag"]; - } - else - { - transfer.Item.ImageID = null; - } - //Gibt es ein Ablaufdatum? - if (response.Headers["Expires"] != null) - { - transfer.Item.Expiration = DateTime.Parse(response.Headers["Expires"]); - } - else - { - transfer.Item.Expiration = DateTime.Now.AddDays(EXPIRATIONDAYS); - } - - var responseStream = response.GetResponseStream(); - - //Schreiben der Bilddatei - using (var bw = new BinaryWriter(IsolatedStorageFile.GetUserStoreForApplication().CreateFile(transfer.Item.LocalFilename))) - { - byte[] b = new byte[4096]; - int read = 0; - while ((read = responseStream.Read(b, 0, b.Length)) > 0) + string newKey = transfer.WebRequest.RequestUri.ToString(); + if (ImageCache.imageCache.ContainsKey(newKey)) { - bw.Write(b, 0, read); + //Setzen des Bildes + Deployment.Current.Dispatcher.BeginInvoke( + () => + { + using (IsolatedStorageFile ifs = IsolatedStorageFile.GetUserStoreForApplication()) + { + using (IsolatedStorageFileStream fs = ifs.OpenFile(transfer.Item.LocalFilename, FileMode.Open)) + { + transfer.Image.SetSource(fs); + } + } + }); + return; + } + //Hat das Bild eine neue ID? + if (response.Headers["ETag"] != null) + { + transfer.Item.ImageID = response.Headers["ETag"]; + } + else + { + transfer.Item.ImageID = null; + } + //Gibt es ein Ablaufdatum? + if (response.Headers["Expires"] != null) + { + transfer.Item.Expiration = DateTime.Parse(response.Headers["Expires"]); + } + else + { + transfer.Item.Expiration = DateTime.Now.AddDays(EXPIRATIONDAYS); + } + + var responseStream = response.GetResponseStream(); + + //Schreiben der Bilddatei + using ( var bw = new BinaryWriter( IsolatedStorageFile.GetUserStoreForApplication().CreateFile(transfer.Item.LocalFilename)) ) + { + byte[] b = new byte[4096]; + int read = 0; + while ((read = responseStream.Read(b, 0, b.Length)) > 0) + { + bw.Write(b, 0, read); + } + bw.Flush(); + bw.Close(); + } + //Setzen des Bildes + Deployment.Current.Dispatcher.BeginInvoke( + () => + { + using (IsolatedStorageFile ifs = IsolatedStorageFile.GetUserStoreForApplication()) + { + using (IsolatedStorageFileStream fs = ifs.OpenFile( transfer.Item.LocalFilename,FileMode.Open)) + { + transfer.Image.SetSource(fs); + } + } + }); + //Hinzufügen der Bildinformationen + if (!ImageCache.imageCache.ContainsKey(newKey)) + { + ImageCache.imageCache.Add(newKey, transfer.Item); + //Speichern der Bildinformationen + ImageCache.SaveCachedImageInfo(); } - bw.Flush(); - bw.Close(); } - //Setzen des Bildes - Deployment.Current.Dispatcher.BeginInvoke(() => transfer.Image.SetSource(IsolatedStorageFile.GetUserStoreForApplication().OpenFile(transfer.Item.LocalFilename, FileMode.Open))); - //Hinzufügen der Bildinformationen - ImageCache.imageCache.Add(transfer.WebRequest.RequestUri.ToString(), transfer.Item); - //Speichern der Bildinformationen - ImageCache.SaveCachedImageInfo(); } - catch + catch (WebException webException) + { + } + catch (Exception ex) { //Nichts machen, da Bild nicht heruntergeladen werden konnte + Debug.WriteLine(ex.ToString()); } + } //Erstellt einen eindeutigen Namen für das zu ladende Bild @@ -118,7 +169,7 @@ namespace WPImageCaching byte[] textToHash = Encoding.UTF8.GetBytes(url); SHA1Managed sa = new SHA1Managed(); byte[] hash = sa.ComputeHash(textToHash); - return BitConverter.ToString(hash)+extension; + return BitConverter.ToString(hash) + extension; } } //Hilfsklasse für den asynchronen Aufruf diff --git a/main/Libs/WPImageCaching/SerializationHelper.cs b/main/Libs/WPImageCaching/SerializationHelper.cs new file mode 100644 index 0000000..ff500b9 --- /dev/null +++ b/main/Libs/WPImageCaching/SerializationHelper.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; + +namespace WPImageCaching +{ + public static class SerializationHelper + { + public static T Deserialize(string serialized) + { + if (string.IsNullOrEmpty(serialized)) + return default(T); + + return JsonConvert.DeserializeObject(serialized); + } + + public static string Serialize(T obj) + { + return JsonConvert.SerializeObject(obj); + } + + } +} \ No newline at end of file diff --git a/main/Libs/WPImageCaching/WPImageCaching.csproj b/main/Libs/WPImageCaching/WPImageCaching.csproj index a82c778..045f891 100644 --- a/main/Libs/WPImageCaching/WPImageCaching.csproj +++ b/main/Libs/WPImageCaching/WPImageCaching.csproj @@ -40,8 +40,29 @@ prompt 4 + + true + bin\GPS_EMULATOR\ + DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE + true + full + AnyCPU + Bin\Debug\WPImageCaching.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + MinimumRecommendedRules.ruleset + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + true + false + + + + ..\Json40r1\WindowsPhone\Newtonsoft.Json.WindowsPhone.dll + @@ -55,6 +76,7 @@ + diff --git a/main/MyFriendsAround.WP7/App.xaml.cs b/main/MyFriendsAround.WP7/App.xaml.cs index 4920e7c..8862abe 100644 --- a/main/MyFriendsAround.WP7/App.xaml.cs +++ b/main/MyFriendsAround.WP7/App.xaml.cs @@ -52,13 +52,14 @@ namespace MyFriendsAround.WP7 //register ViewModelLocator Container.Instance.RegisterInstance(typeof(ViewModelLocator), "ViewModelLocator"); - Container.Instance.RegisterInstance( new LocationService(), "LocationService"); + Container.Instance.RegisterInstance(new LocationService(), "LocationService"); } public static ILocationService LocationService { - get { + get + { return Container.Instance.Resolve("LocationService"); } } @@ -137,12 +138,7 @@ namespace MyFriendsAround.WP7 void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e) { //LittleWatson.ReportException(e.Exception, string.Empty); - DispatcherHelper.CheckBeginInvokeOnUI(() => - { - var exception = new ExceptionPrompt(); - exception.Show(e.Exception); - } - ); + ShowException(e.Exception); if (System.Diagnostics.Debugger.IsAttached) { @@ -155,13 +151,7 @@ namespace MyFriendsAround.WP7 private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) { //LittleWatson.ReportException(e.ExceptionObject, string.Empty); - - DispatcherHelper.CheckBeginInvokeOnUI(() => - { - var exception = new ExceptionPrompt(); - exception.Show(e.ExceptionObject); - } - ); + ShowException(e.ExceptionObject); if (System.Diagnostics.Debugger.IsAttached) { @@ -172,6 +162,16 @@ namespace MyFriendsAround.WP7 e.Handled = true; } + private void ShowException(Exception ex) + { + DispatcherHelper.CheckBeginInvokeOnUI(() => + { + Container.Instance.Resolve("ViewModelLocator").Main.IsBusy = false; + var exception = new ExceptionPrompt(); + exception.Show(ex); + } + ); + } #region Phone application initialization diff --git a/main/MyFriendsAround.WP7/Service/ServiceAgent.cs b/main/MyFriendsAround.WP7/Service/ServiceAgent.cs index 8731764..f4b7102 100644 --- a/main/MyFriendsAround.WP7/Service/ServiceAgent.cs +++ b/main/MyFriendsAround.WP7/Service/ServiceAgent.cs @@ -16,9 +16,14 @@ namespace MyFriendsAround.WP7.Service static ServiceAgent() { - baseUrl = "http://myfriendsaround.cloudapp.net/myfriends";//live azure - //baseUrl = "http://127.0.0.1:80/myfriends";//running in local azure emulator +#if GPS_EMULATOR + baseUrl = "http://127.0.0.1:80/myfriends";//running in local azure emulator //baseUrl = "http://localhost.:55672/myfriends";//for local asp.net mvc use +#else + baseUrl = "http://myfriendsaround.cloudapp.net/myfriends";//live azure + //baseUrl = "http://localhost:80/myfriends"; +#endif + } #region GetFriends diff --git a/main/MyFriendsAround.WP7/ViewModel/MainViewModel.cs b/main/MyFriendsAround.WP7/ViewModel/MainViewModel.cs index 257d765..56b9458 100644 --- a/main/MyFriendsAround.WP7/ViewModel/MainViewModel.cs +++ b/main/MyFriendsAround.WP7/ViewModel/MainViewModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; using System.Device.Location; using System.IO; using System.Net; @@ -57,8 +58,18 @@ namespace MyFriendsAround.WP7.ViewModel public MainViewModel() { GpsLocation = Location.Unknown; + _SelectedFriend = null; + // MainLoadCommand = new RelayCommand(() => MainLoad()); + BackKeyPressCommand = new RelayCommand((args) => + { + if (IsSelectFriend) + { + IsSelectFriend = !IsSelectFriend; + args.Cancel = true; + } + }); PublishLocationCommand = new RelayCommand(() => PublishLocationAction()); NavigateToSettingsCommand = new RelayCommand(() => NavigateToSettings()); RefreshFriendsCommand = new RelayCommand(() => RefreshFriends()); @@ -194,12 +205,8 @@ namespace MyFriendsAround.WP7.ViewModel /// public const string SelectedFriendPropertyName = "SelectedFriend"; - private PushPinModel _SelectedFriend = new PushPinModel() - { - Location = GeoCoordinate.Unknown, - PinUserName = "Guest", - PinImageUrl = string.Empty - }; + private PushPinModel _SelectedFriend; + /// /// Gets the SelectedFriend property. @@ -562,12 +569,6 @@ namespace MyFriendsAround.WP7.ViewModel set { - if (_isBusy == value) - { - return; - } - - var oldValue = _isBusy; _isBusy = value; // Update bindings, no broadcast @@ -616,6 +617,7 @@ namespace MyFriendsAround.WP7.ViewModel #region Commands public ICommand MainLoadCommand { get; set; } + public ICommand BackKeyPressCommand { get; set; } public ICommand PublishLocationCommand { get; set; } public ICommand NavigateToSettingsCommand { get; set; } public ICommand ShowMyLocationCommand { get; set; } @@ -876,7 +878,7 @@ namespace MyFriendsAround.WP7.ViewModel result.Add(new PushPinModel() { PinSource = "/ApplicationIcon.png", - Location = new GeoCoordinate(f.Latitude, f.Longitude), + Location = new GeoCoordinate(f.Latitude, f.Longitude, 0), PinUserName = f.FriendName, PinImageUrl = string.Format( @@ -923,21 +925,21 @@ namespace MyFriendsAround.WP7.ViewModel { List list = args.Friends; DispatcherHelper.CheckBeginInvokeOnUI(() => - { - PopulatePushPins(list); - IsBusy = false; - } - ); + { + IsBusy = false; + PopulatePushPins(list); + } + ); } else { DispatcherHelper.CheckBeginInvokeOnUI(() => - { - IsBusy = false; - var exception = new ExceptionPrompt(); - exception.Show(args.Error); - } - ); + { + IsBusy = false; + var exception = new ExceptionPrompt(); + exception.Show(args.Error); + } + ); } } @@ -958,6 +960,7 @@ namespace MyFriendsAround.WP7.ViewModel { DispatcherHelper.CheckBeginInvokeOnUI(() => { + IsBusy = false; var message = new DialogMessage( "Communication error!", DialogMessageCallback) { @@ -1004,6 +1007,7 @@ namespace MyFriendsAround.WP7.ViewModel { DispatcherHelper.CheckBeginInvokeOnUI(() => { + IsBusy = false; var message = new DialogMessage( "Communication error!", DialogMessageCallback) { diff --git a/main/MyFriendsAround.WP7/Views/MainPage.xaml b/main/MyFriendsAround.WP7/Views/MainPage.xaml index 8ee0254..adf7359 100644 --- a/main/MyFriendsAround.WP7/Views/MainPage.xaml +++ b/main/MyFriendsAround.WP7/Views/MainPage.xaml @@ -74,7 +74,11 @@ + + + + @@ -252,6 +256,31 @@ + + + + + + + + + + + + + - - - - - - - - - - + \ No newline at end of file diff --git a/main/MyFriendsAround.sln b/main/MyFriendsAround.sln index 380f527..cbaabc0 100644 --- a/main/MyFriendsAround.sln +++ b/main/MyFriendsAround.sln @@ -316,8 +316,8 @@ Global {17158FD9-80FD-49C1-9E3F-C5633602A4D9}.Debug|x86.ActiveCfg = Debug|Any CPU {17158FD9-80FD-49C1-9E3F-C5633602A4D9}.GPS_EMULATOR|Any CPU.ActiveCfg = Release|Any CPU {17158FD9-80FD-49C1-9E3F-C5633602A4D9}.GPS_EMULATOR|Any CPU.Build.0 = Release|Any CPU - {17158FD9-80FD-49C1-9E3F-C5633602A4D9}.GPS_EMULATOR|Mixed Platforms.ActiveCfg = Release|Any CPU - {17158FD9-80FD-49C1-9E3F-C5633602A4D9}.GPS_EMULATOR|Mixed Platforms.Build.0 = Release|Any CPU + {17158FD9-80FD-49C1-9E3F-C5633602A4D9}.GPS_EMULATOR|Mixed Platforms.ActiveCfg = GPS_EMULATOR|Any CPU + {17158FD9-80FD-49C1-9E3F-C5633602A4D9}.GPS_EMULATOR|Mixed Platforms.Build.0 = GPS_EMULATOR|Any CPU {17158FD9-80FD-49C1-9E3F-C5633602A4D9}.GPS_EMULATOR|x86.ActiveCfg = Release|Any CPU {17158FD9-80FD-49C1-9E3F-C5633602A4D9}.Release|Any CPU.ActiveCfg = Release|Any CPU {17158FD9-80FD-49C1-9E3F-C5633602A4D9}.Release|Any CPU.Build.0 = Release|Any CPU