[banshee] Windows: Auto-update functionality (bgo#641960)



commit b0c18050a1e4e8c72d1af6eb790b1575146168c5
Author: Ján Sokoly <cruster gmail com>
Date:   Mon Mar 14 23:34:43 2011 +0100

    Windows: Auto-update functionality (bgo#641960)
    
    Checks for update on startup, looking for a newer unstable release,
    downloads it in background and launches the installer.
    
    Signed-off-by: Gabriel Burt <gabriel burt gmail com>

 .../Banshee.Windows/Banshee.Windows.csproj         |    7 +-
 .../Banshee.Windows/VersionUpdater.cs              |  194 ++++++++++++++++++++
 .../Banshee.Windows/WindowsService.cs              |   28 +++
 .../Resources/core-ui-actions-layout.xml           |    1 +
 4 files changed, 229 insertions(+), 1 deletions(-)
---
diff --git a/src/Backends/Banshee.Windows/Banshee.Windows.csproj b/src/Backends/Banshee.Windows/Banshee.Windows.csproj
index 2c6f9f1..5f3fd92 100644
--- a/src/Backends/Banshee.Windows/Banshee.Windows.csproj
+++ b/src/Backends/Banshee.Windows/Banshee.Windows.csproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"; ToolsVersion="3.5">
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -82,8 +82,13 @@
     <Reference Include="System.Core">
     </Reference>
     <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Xml" />
+    <Reference Include="System.Xml.Linq">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Banshee.Windows\VersionUpdater.cs" />
     <Compile Include="Banshee.Windows\WindowsService.cs" />
     <Compile Include="Banshee.Windows\ExtensionsForGtkAction.cs" />
     <Compile Include="Banshee.Windows\GtkWindowThumbnailToolbarManager.cs" />
diff --git a/src/Backends/Banshee.Windows/Banshee.Windows/VersionUpdater.cs b/src/Backends/Banshee.Windows/Banshee.Windows/VersionUpdater.cs
new file mode 100644
index 0000000..2380335
--- /dev/null
+++ b/src/Backends/Banshee.Windows/Banshee.Windows/VersionUpdater.cs
@@ -0,0 +1,194 @@
+//
+// VersionUpdater.cs
+//
+// Authors:
+//   Ján Sokoly <cruster gmail com>
+//
+// Copyright 2011 Ján Sokoly
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Xml.Linq;
+
+using Banshee.Gui;
+using Banshee.ServiceStack;
+
+using Gtk;
+using Hyena.Downloader;
+using Mono.Unix;
+
+namespace Banshee.Windows
+{
+    public class VersionUpdater : DownloadManager
+    {
+        private const string download_url = "http://download.banshee.fm/banshee/";;
+        private const string doap_filename = "banshee.doap";
+        private string temp_doap_path = Path.GetTempPath () + doap_filename;
+        private string unstable_version;
+        private string temp_installer_path;
+        private bool verbose;
+        private DownloadManagerJob job;
+
+        public void CheckForUpdates (bool verbose)
+        {
+            this.verbose = verbose;
+
+            HttpFileDownloader downloader = new HttpFileDownloader ();
+            downloader.Uri = new Uri (download_url + doap_filename);
+            downloader.TempPathRoot = Path.GetTempPath ();
+            downloader.Finished += new Action<HttpDownloader> (DoapDownloader_Finished);
+            downloader.Start ();
+
+            temp_doap_path = downloader.LocalPath;
+        }
+
+        void DoapDownloader_Finished (HttpDownloader obj)
+        {
+            if (obj.State.FailureException != null) {
+                if (verbose) {
+                    DisplayMessage (Catalog.GetString ("Can't check for updates"), Catalog.GetString ("We're currently not able to check if there's a new version available. Please try again later."), MessageType.Error);
+                }
+            }
+            else {
+                XDocument doap = XDocument.Load (temp_doap_path);
+                XNamespace nsDoap = XNamespace.Get ("http://usefulinc.com/ns/doap#";);
+                unstable_version = (from p in doap.Descendants (nsDoap + "Version")
+                                    where p.Element (nsDoap + "branch").Value.StartsWith ("master")
+                                    select new { Revision = p.Element (nsDoap + "revision").Value }).First ().Revision;
+
+                // once we have the version information, the temporary local copy of doap may be deleted
+                File.Delete (temp_doap_path);
+
+                if (!String.IsNullOrEmpty (unstable_version) &&
+                    Banshee.ServiceStack.Application.Version != "Unknown" &&
+                    new Version (Banshee.ServiceStack.Application.Version) < new Version (unstable_version)) {
+                    Gtk.Application.Invoke (delegate {
+                        DisplayUpdateAvailableDialog ();
+                    });
+                }
+                else {
+                    if (verbose) {
+                        DisplayMessage (Catalog.GetString ("No update available"), Catalog.GetString ("You already have the latest version of Banshee installed."), MessageType.Info);
+                    }
+                }
+            }
+        }
+
+        public void DisplayUpdateAvailableDialog ()
+        {
+            bool update;
+            using (var message_dialog = new MessageDialog (ServiceManager.Get<GtkElementsService> ().PrimaryWindow, 0, MessageType.Question, ButtonsType.YesNo, String.Format (Catalog.GetString ("A new version of Banshee ({0}) is available.{1}Do you want to update?"), unstable_version, Environment.NewLine))) {
+                message_dialog.WindowPosition = WindowPosition.CenterOnParent;
+                message_dialog.Title = Catalog.GetString ("Banshee update available");
+                update = (message_dialog.Run () == (int)ResponseType.Yes);
+                message_dialog.Destroy ();
+            }
+
+            if (update) {
+                string downloadUrl = String.Format ("{0}unstable/{1}/Banshee-{1}.msi", download_url, unstable_version);
+
+                HttpFileDownloader downloader = new HttpFileDownloader ();
+                downloader.Uri = new Uri (downloadUrl);
+                downloader.TempPathRoot = Path.GetTempPath ();
+                downloader.FileExtension = "msi";
+                downloader.Progress += new Action<HttpDownloader> (InstallerDownloader_Progress);
+                downloader.Finished += new Action<HttpDownloader> (InstallerDownloader_Finished);
+                downloader.Start ();
+
+                temp_installer_path = downloader.LocalPath;
+
+                job = new DownloadManagerJob (this)
+                {
+                    Title = Catalog.GetString (String.Format ("Downloading Banshee-{0}.msi", unstable_version)),
+                    CanCancel = false
+                };
+
+                ServiceManager.Get<JobScheduler> ().Add (job);
+            }
+        }
+
+        void InstallerDownloader_Progress (HttpDownloader obj)
+        {
+            string downloaded = Math.Round (obj.State.TotalBytesRead / 1024d / 1024d, 1).ToString ("F1");
+            string todownload = Math.Round (obj.State.TotalBytesExpected / 1024d / 1024d, 1).ToString ();
+            string rate = Math.Round (obj.State.TransferRate / 1024d, 1).ToString ("F1");
+
+            job.Progress = obj.State.PercentComplete;
+            job.Status = String.Format ("{0} MB / {1} MB ({2} KB/s)", downloaded, todownload, rate);
+        }
+
+        void InstallerDownloader_Finished (HttpDownloader obj)
+        {
+            base.OnDownloaderFinished (obj);
+
+            if (obj.State.FailureException != null) {
+                DisplayMessage (Catalog.GetString ("Update download failed"), Catalog.GetString ("The download failed. Please try again later."), MessageType.Error);
+            }
+            else {
+                Gtk.Application.Invoke (delegate {
+                    DisplayUpdateFinishedDownloadingDialog ();
+                });
+            }
+        }
+
+        private void DisplayUpdateFinishedDownloadingDialog ()
+        {
+            bool update;
+            using (var message_dialog = new MessageDialog (ServiceManager.Get<GtkElementsService> ().PrimaryWindow, 0, MessageType.Question, ButtonsType.YesNo, String.Format (Catalog.GetString ("The update finished downloading.{0}Do you want to shutdown Banshee and run the installer?"), Environment.NewLine))) {
+                message_dialog.WindowPosition = WindowPosition.CenterOnParent;
+                message_dialog.Title = Catalog.GetString ("Update finished downloading");
+                update = (message_dialog.Run () == (int)ResponseType.Yes);
+                message_dialog.Destroy ();
+            }
+
+            if (update) {
+                // run the downloaded installer and shutdown the running instance of Banshee
+                Process msiProcess = new Process ();
+                msiProcess.StartInfo.FileName = "msiexec";
+                msiProcess.StartInfo.Arguments = "/i " + temp_installer_path;
+                msiProcess.Start ();
+
+                Banshee.ServiceStack.Application.Shutdown ();
+            }
+            else {
+                // delete the downloaded installer as the user does not want to update now
+                File.Delete (temp_installer_path);
+            }
+        }
+
+        // helper which makes it possible to show Gtk's MessageDialog from a thread
+        private void DisplayMessage (string title, string message, MessageType type)
+        {
+            Gtk.Application.Invoke (delegate {
+                using (var message_dialog = new MessageDialog (ServiceManager.Get<GtkElementsService> ().PrimaryWindow, 0, type, ButtonsType.Ok, message)) {
+                    message_dialog.WindowPosition = WindowPosition.CenterOnParent;
+                    message_dialog.Title = title;
+                    message_dialog.Run ();
+                    message_dialog.Destroy ();
+                }
+            });
+        }
+    }
+}
diff --git a/src/Backends/Banshee.Windows/Banshee.Windows/WindowsService.cs b/src/Backends/Banshee.Windows/Banshee.Windows/WindowsService.cs
index ecc9684..f7604ff 100644
--- a/src/Backends/Banshee.Windows/Banshee.Windows/WindowsService.cs
+++ b/src/Backends/Banshee.Windows/Banshee.Windows/WindowsService.cs
@@ -36,6 +36,7 @@ using System.Text;
 using Banshee.ServiceStack;
 using Banshee.Gui;
 
+using Mono.Unix;
 using Windows7Support;
 
 namespace Banshee.Windows
@@ -45,6 +46,7 @@ namespace Banshee.Windows
         bool disposed;
         GtkElementsService elements_service;
         InterfaceActionService interface_action_service;
+        VersionUpdater version_updater = new VersionUpdater ();
 
         private IList<ThumbnailToolbarButton> buttons;
 
@@ -92,6 +94,7 @@ namespace Banshee.Windows
         public void Initialize ()
         {
             // TODO check for updates and other cool stuff
+            version_updater.CheckForUpdates (false);
 
             elements_service = ServiceManager.Get<GtkElementsService> ();
             interface_action_service = ServiceManager.Get<InterfaceActionService> ();
@@ -99,6 +102,31 @@ namespace Banshee.Windows
             if (!ServiceStart ()) {
                 ServiceManager.ServiceStarted += OnServiceStarted;
             }
+
+            // add check for updates action
+            interface_action_service.GlobalActions.Add (new Gtk.ActionEntry[] {
+                new Gtk.ActionEntry ("CheckForUpdatesAction", null,
+                    Catalog.GetString("Check for Updates"), null,
+                    null, CheckForUpdatesEvent)
+            });
+
+            // merge check for updates menu item
+            interface_action_service.UIManager.AddUiFromString (@"
+              <ui>
+                <menubar name=""MainMenu"">
+                  <menu name=""HelpMenu"" action=""HelpMenuAction"">
+                    <placeholder name=""CheckForUpdatesPlaceholder"">
+                    <menuitem name=""CheckForUpdates"" action=""CheckForUpdatesAction""/>
+                    </placeholder>
+                  </menu>
+                </menubar>
+              </ui>
+            ");
+        }
+
+        private void CheckForUpdatesEvent (object o, EventArgs args)
+        {
+            version_updater.CheckForUpdates (true);
         }
 
         #endregion
diff --git a/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml b/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
index 547fe75..7908e13 100644
--- a/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
+++ b/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
@@ -121,6 +121,7 @@
       <menuitem name="Wiki" action="WikiAction"/>
       <menuitem name="WikiDeveloper" action="WikiDeveloperAction"/>
       <separator/>
+      <placeholder name="CheckForUpdatesPlaceholder"/>
       <menuitem name="VersionInformation" action="VersionInformationAction"/>
       <menuitem name="About" action="AboutAction"/>
     </menu>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]