[banshee] Dap/Mtp: Remove unmount, add claim (bgo#731916)



commit c0cb25366487914a556ced34117f4db173af9a5f
Author: Nicholas Little <arealityfarbetween googlemail com>
Date:   Sat Jul 5 12:01:53 2014 +0100

    Dap/Mtp: Remove unmount, add claim (bgo#731916)
    
    We need to unmount MTP devices for banshee to make use of them, this
    causes nautilus to produce error messages in some cases, as described in
    [1].
    
    This patch adds a parameter to DapSource.DeviceInitialize to signal
    whether we should attempt to force the connection to the device (at the
    user's request via the ClaimDapAction) or just make a best attempt.
    
    To facilitate this, DapSource subclasses can now throw
    InvalidDeviceStateException which will allow them to make a claim on a
    device even though they couldn't initialize it. At that point, a new
    type, PotentialSource is mapped, enabling the ClaimDapAction.
    
    [1] https://bugzilla.gnome.org/show_bug.cgi?id=731916

 .../Banshee.Dap.AppleDevice/AppleDeviceSource.cs   |    4 +-
 .../Banshee.Dap.Karma/KarmaSource.cs               |    4 +-
 .../Banshee.Dap.MassStorage/MassStorageSource.cs   |    4 +-
 .../Banshee.Dap.Mtp/Banshee.Dap.Mtp/MtpSource.cs   |   30 ++--
 src/Dap/Banshee.Dap/Banshee.Dap.Gui/DapActions.cs  |   15 ++
 src/Dap/Banshee.Dap/Banshee.Dap.Gui/DapContent.cs  |    2 +-
 .../Banshee.Dap.Gui/InactiveDapContent.cs          |  120 ++++++++++++
 src/Dap/Banshee.Dap/Banshee.Dap.csproj             |    2 +
 src/Dap/Banshee.Dap/Banshee.Dap/DapService.cs      |   76 ++++++--
 src/Dap/Banshee.Dap/Banshee.Dap/DapSource.cs       |    9 +-
 .../Banshee.Dap/InvalidDeviceException.cs          |    4 +
 src/Dap/Banshee.Dap/Banshee.Dap/PotentialSource.cs |  195 ++++++++++++++++++++
 src/Dap/Banshee.Dap/Makefile.am                    |    2 +
 src/Dap/Banshee.Dap/Resources/ActiveSourceUI.xml   |    1 +
 src/Dap/Banshee.Dap/Resources/GlobalUI.xml         |    1 +
 15 files changed, 428 insertions(+), 41 deletions(-)
---
diff --git a/src/Dap/Banshee.Dap.AppleDevice/Banshee.Dap.AppleDevice/AppleDeviceSource.cs 
b/src/Dap/Banshee.Dap.AppleDevice/Banshee.Dap.AppleDevice/AppleDeviceSource.cs
index 5803ea2..a711640 100644
--- a/src/Dap/Banshee.Dap.AppleDevice/Banshee.Dap.AppleDevice/AppleDeviceSource.cs
+++ b/src/Dap/Banshee.Dap.AppleDevice/Banshee.Dap.AppleDevice/AppleDeviceSource.cs
@@ -70,7 +70,7 @@ namespace Banshee.Dap.AppleDevice
 
 #region Device Setup/Dispose
 
-        public override void DeviceInitialize (IDevice device)
+        public override void DeviceInitialize (IDevice device, bool force)
         {
             Volume = device as IVolume;
 
@@ -94,7 +94,7 @@ namespace Banshee.Dap.AppleDevice
                 throw new InvalidDeviceException ();
             }
 
-            base.DeviceInitialize (device);
+            base.DeviceInitialize (device, force);
 
             Name = Volume.Name;
             SupportsPlaylists = true;
diff --git a/src/Dap/Banshee.Dap.Karma/Banshee.Dap.Karma/KarmaSource.cs 
b/src/Dap/Banshee.Dap.Karma/Banshee.Dap.Karma/KarmaSource.cs
index 4a0a358..d1d3f6f 100644
--- a/src/Dap/Banshee.Dap.Karma/Banshee.Dap.Karma/KarmaSource.cs
+++ b/src/Dap/Banshee.Dap.Karma/Banshee.Dap.Karma/KarmaSource.cs
@@ -50,9 +50,9 @@ namespace Banshee.Dap.Karma
         private Dictionary<long, KarmaTrackInfo> track_map =
             new Dictionary<long, KarmaTrackInfo>();
 
-        public override void DeviceInitialize(IDevice dev)
+        public override void DeviceInitialize(IDevice dev, bool force)
         {
-            base.DeviceInitialize(dev);
+            base.DeviceInitialize(dev, force);
 
             if (!IsKarma(dev))
                 throw new InvalidDeviceException();
diff --git a/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs 
b/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs
index f49a8cf..cdf6d38 100644
--- a/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs
+++ b/src/Dap/Banshee.Dap.MassStorage/Banshee.Dap.MassStorage/MassStorageSource.cs
@@ -58,9 +58,9 @@ namespace Banshee.Dap.MassStorage
         private IVolume volume;
         private IUsbDevice usb_device;
 
-        public override void DeviceInitialize (IDevice device)
+        public override void DeviceInitialize (IDevice device, bool force)
         {
-            base.DeviceInitialize (device);
+            base.DeviceInitialize (device, force);
 
             volume = device as IVolume;
 
diff --git a/src/Dap/Banshee.Dap.Mtp/Banshee.Dap.Mtp/MtpSource.cs 
b/src/Dap/Banshee.Dap.Mtp/Banshee.Dap.Mtp/MtpSource.cs
index bf3eb6a..993223a 100644
--- a/src/Dap/Banshee.Dap.Mtp/Banshee.Dap.Mtp/MtpSource.cs
+++ b/src/Dap/Banshee.Dap.Mtp/Banshee.Dap.Mtp/MtpSource.cs
@@ -64,9 +64,9 @@ namespace Banshee.Dap.Mtp
         private bool can_sync_albumart = NeverSyncAlbumArtSchema.Get () == false;
         private int thumb_width = AlbumArtWidthSchema.Get ();
 
-        public override void DeviceInitialize (IDevice device)
+        public override void DeviceInitialize (IDevice device, bool force)
         {
-            base.DeviceInitialize (device);
+            base.DeviceInitialize (device, force);
 
             var portInfo = device.ResolveUsbPortInfo ();
             if (portInfo == null || portInfo.DeviceNumber == 0) {
@@ -98,19 +98,20 @@ namespace Banshee.Dap.Mtp
                 //if (v.BusNumber == busnum && v.DeviceNumber == devnum) {
                 if (v.DeviceNumber == devnum) {
                     // If gvfs-gphoto has it mounted, unmount it
-                    if (volume != null && volume.IsMounted) {
+                    if (volume != null && volume.IsMounted && force) {
+                        Log.DebugFormat ("MtpSource: attempting to unmount {0}", volume.Name);
                         volume.Unmount ();
                     }
 
-                    for (int i = 5; i > 0 && mtp_device == null; i--) {
-                        try {
-                            mtp_device = MtpDevice.Connect (v);
-                        } catch (Exception) {}
+                    if (volume != null && volume.IsMounted) {
+                        throw new InvalidDeviceStateException ();
+                    }
 
-                        if (mtp_device == null) {
-                            Log.DebugFormat ("Failed to connect to mtp device. Trying {0} more times...", i 
- 1);
-                            Thread.Sleep (2000);
-                        }
+                    mtp_device = MtpDevice.Connect (v);
+
+                    if (mtp_device == null) {
+                        Log.DebugFormat ("Failed to connect to mtp device {0}", device.Name);
+                        throw new InvalidDeviceStateException ();
                     }
                 }
             }
@@ -465,16 +466,9 @@ namespace Banshee.Dap.Mtp
                 }
             }
 
-            ServiceManager.SourceManager.RemoveSource (this);
             mtp_device = null;
         }
 
-        protected override void Eject ()
-        {
-            base.Eject ();
-            Dispose ();
-        }
-
         private static string MakeAlbumKey (string album_artist, string album)
         {
             return String.Format ("{0}_{1}", album_artist, album);
diff --git a/src/Dap/Banshee.Dap/Banshee.Dap.Gui/DapActions.cs 
b/src/Dap/Banshee.Dap/Banshee.Dap.Gui/DapActions.cs
index 949007c..f7aaac5 100644
--- a/src/Dap/Banshee.Dap/Banshee.Dap.Gui/DapActions.cs
+++ b/src/Dap/Banshee.Dap/Banshee.Dap.Gui/DapActions.cs
@@ -49,6 +49,11 @@ namespace Banshee.Dap.Gui
         public DapActions () : base ("dap")
         {
             AddImportant (
+                new ActionEntry ("ClaimDapAction", null,
+                    Catalog.GetString ("Claim"), null,
+                    String.Empty, OnClaimDap)
+            );
+            AddImportant (
                 new ActionEntry ("SyncDapAction", null,
                     Catalog.GetString ("Sync"), null,
                     String.Empty, OnSyncDap)
@@ -57,6 +62,7 @@ namespace Banshee.Dap.Gui
             AddUiFromFile ("GlobalUI.xml");
 
             this["SyncDapAction"].IconName = Stock.Refresh;
+            this["ClaimDapAction"].IconName = Stock.Connect;
             ServiceManager.SourceManager.ActiveSourceChanged += OnActiveSourceChanged;
             Actions.SourceActions.Updated += delegate { UpdateActions (); };
             OnActiveSourceChanged (null);
@@ -71,6 +77,7 @@ namespace Banshee.Dap.Gui
             }
 
             previous_dap = ActiveSource as DapSource;
+            UpdateActions ();
 
             if (previous_dap != null) {
                 previous_dap.Sync.Updated += OnSyncUpdated;
@@ -87,6 +94,7 @@ namespace Banshee.Dap.Gui
             DapSource dap = Dap;
             if (dap != null) {
                 UpdateAction ("SyncDapAction", dap.Sync.Enabled);
+                UpdateAction ("ClaimDapAction", dap is PotentialSource);
             }
         }
 
@@ -98,5 +106,12 @@ namespace Banshee.Dap.Gui
             }
         }
 
+        private void OnClaimDap (object o, EventArgs args)
+        {
+            var dap = Dap as PotentialSource;
+            if (dap != null) {
+                dap.TryClaim ();
+            }
+        }
     }
 }
diff --git a/src/Dap/Banshee.Dap/Banshee.Dap.Gui/DapContent.cs 
b/src/Dap/Banshee.Dap/Banshee.Dap.Gui/DapContent.cs
index 81c5349..9ab9f87 100644
--- a/src/Dap/Banshee.Dap/Banshee.Dap.Gui/DapContent.cs
+++ b/src/Dap/Banshee.Dap/Banshee.Dap.Gui/DapContent.cs
@@ -139,7 +139,7 @@ namespace Banshee.Dap.Gui
             opts.Dispose ();
         }
 
-        private void BuildActions ()
+        internal static void BuildActions()
         {
             if (actions == null) {
                 actions = new DapActions ();
diff --git a/src/Dap/Banshee.Dap/Banshee.Dap.Gui/InactiveDapContent.cs 
b/src/Dap/Banshee.Dap/Banshee.Dap.Gui/InactiveDapContent.cs
new file mode 100644
index 0000000..c5c9f85
--- /dev/null
+++ b/src/Dap/Banshee.Dap/Banshee.Dap.Gui/InactiveDapContent.cs
@@ -0,0 +1,120 @@
+//
+// InactiveContent.cs
+//
+// Author:
+//   Nicholas Little <arealityfarbetween googlemail com>
+//
+// Copyright 2014 Nicholas Little
+//
+// 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.Collections.Generic;
+using System.Linq;
+
+using Gtk;
+
+using Hyena;
+using Hyena.Data;
+using Hyena.Widgets;
+
+using Mono.Unix;
+
+using Banshee.Dap;
+using Banshee.Sources.Gui;
+using Banshee.ServiceStack;
+using Banshee.Preferences;
+using Banshee.Sources;
+using Banshee.Preferences.Gui;
+using Banshee.Widgets;
+
+namespace Banshee.Dap.Gui
+{
+    public class InactiveDapContent : DapPropertiesDisplay
+    {
+        Label title;
+
+        // To avoid the GLib.MissingIntPtrCtorException seen by some; BGO #552169
+        protected InactiveDapContent (IntPtr ptr) : base (ptr)
+        {
+        }
+
+        public InactiveDapContent (DapSource dapSource) : base(dapSource)
+        {
+            dapSource.Properties.PropertyChanged += OnPropertyChanged;
+            BuildWidgets ();
+        }
+
+        private void BuildWidgets ()
+        {
+            var outer = new HBox();
+            var device = new Image (LargeIcon) { Yalign = 0.0f };
+            outer.PackStart (device, false, false, 0);
+
+            var inner = new VBox { Spacing = 5, BorderWidth = 5 };
+            title = new Label { UseMarkup = true, Xalign = 0.0f };
+            SetTitleText (Source.Name);
+            inner.PackStart (title, false, false, 0);
+
+            var box = new HBox { Spacing = 5 };
+            box.PackStart (new Image { IconName = "dialog-warning" }, false, false, 0);
+            box.PackStart (new Label { Markup = ErrorString, UseMarkup = true }, false, false, 0);
+            inner.PackStart (box, false, false, 0);
+
+            outer.PackEnd (inner, false, false, 0);
+            Add (outer);
+            ShowAll ();
+        }
+
+        private void SetTitleText (string name)
+        {
+            title.Markup = String.Format (@"<span size=""x-large"" weight=""bold"">{0}</span>", name);
+        }
+
+        private void OnPropertyChanged (object o, PropertyChangeEventArgs args)
+        {
+            if (args.PropertyName == "Name") {
+                SetTitleText (args.NewValue.ToString ());
+            }
+        }
+
+        protected virtual string ErrorString {
+            get { return DefaultErrorString; }
+        }
+
+        static InactiveDapContent ()
+        {
+            var generic = Catalog.GetString ("Your device appears to be in use by another program");
+            var claimit = String.Format (
+                @"<span weight=""bold"">{0}</span>",
+                Catalog.GetString ("Claim")
+            );
+            var pressit = String.Format (
+                Catalog.GetString ("Press the {0} button above to use it in Banshee"),
+                claimit
+            );
+            DefaultErrorString = string.Format (
+                @"<span size=""large"">{0}." + "\n" + "{1}…</span>", generic, pressit
+            );
+            DapContent.BuildActions ();
+        }
+
+        private static readonly string DefaultErrorString;
+    }
+}
diff --git a/src/Dap/Banshee.Dap/Banshee.Dap.csproj b/src/Dap/Banshee.Dap/Banshee.Dap.csproj
index 465ba8d..2fec82a 100644
--- a/src/Dap/Banshee.Dap/Banshee.Dap.csproj
+++ b/src/Dap/Banshee.Dap/Banshee.Dap.csproj
@@ -97,6 +97,8 @@
     <Compile Include="Banshee.Dap.Gui\LibrarySyncOptions.cs" />
     <Compile Include="Banshee.Dap\DapPriorityNode.cs" />
     <Compile Include="Banshee.Dap\SyncPlaylist.cs" />
+    <Compile Include="Banshee.Dap\PotentialSource.cs" />
+    <Compile Include="Banshee.Dap\Gui\InactiveDapContent.cs" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Banshee.Dap.addin.xml">
diff --git a/src/Dap/Banshee.Dap/Banshee.Dap/DapService.cs b/src/Dap/Banshee.Dap/Banshee.Dap/DapService.cs
index c00af21..c3d6c39 100644
--- a/src/Dap/Banshee.Dap/Banshee.Dap/DapService.cs
+++ b/src/Dap/Banshee.Dap/Banshee.Dap/DapService.cs
@@ -29,6 +29,7 @@
 //
 
 using System;
+using System.Linq;
 using System.Collections.Generic;
 
 using Mono.Unix;
@@ -75,7 +76,6 @@ namespace Banshee.Dap
                 ServiceManager.HardwareManager.DeviceChanged += OnHardwareDeviceChanged;
                 ServiceManager.HardwareManager.DeviceRemoved += OnHardwareDeviceRemoved;
                 ServiceManager.HardwareManager.DeviceCommand += OnDeviceCommand;
-                ServiceManager.SourceManager.SourceRemoved += OnSourceRemoved;
                 initialized = true;
 
                 // Now that we've loaded all the enabled DAP providers, load the devices
@@ -133,7 +133,6 @@ namespace Banshee.Dap
                 ServiceManager.HardwareManager.DeviceAdded -= OnHardwareDeviceAdded;
                 ServiceManager.HardwareManager.DeviceRemoved -= OnHardwareDeviceRemoved;
                 ServiceManager.HardwareManager.DeviceCommand -= OnDeviceCommand;
-                ServiceManager.SourceManager.SourceRemoved -= OnSourceRemoved;
 
                 List<DapSource> dap_sources = new List<DapSource> (sources.Values);
                 foreach (DapSource source in dap_sources) {
@@ -153,10 +152,17 @@ namespace Banshee.Dap
             foreach (TypeExtensionNode node in supported_dap_types) {
                 try {
                     DapSource source = (DapSource)node.CreateInstance ();
-                    source.DeviceInitialize (device);
+                    source.DeviceInitialize (device, false);
                     source.LoadDeviceContents ();
                     source.AddinId = node.Addin.Id;
                     return source;
+                } catch (InvalidDeviceStateException) {
+                    Log.WarningFormat (
+                        "Dap.DapService: invalid state, mapping potential source for {0}",
+                        device.Name
+                    );
+                    DapSource source = new PotentialSource (this, node, device);
+                    return source;
                 } catch (InvalidDeviceException) {
                 } catch (InvalidCastException e) {
                     Log.Warning ("Extension is not a DapSource as required", e);
@@ -173,6 +179,25 @@ namespace Banshee.Dap
             Scheduler.Schedule (new MapDeviceJob (this, device));
         }
 
+        internal void SwapSource (DapSource oldSource, DapSource newSource, bool makeActive)
+        {
+            if (oldSource.Device.Uuid != newSource.Device.Uuid) {
+                Log.ErrorFormat (
+                    "Dap.DapService: swap ignored from {0} to {1}.",
+                    oldSource.Device.Uuid, newSource.Device.Uuid
+                );
+                return;
+            }
+            Log.DebugFormat (
+                "Dap.DapService: Swapping {0} with UUID {1} for {2}",
+                oldSource.GetType ().Name, oldSource.Device.Uuid,
+                newSource.GetType ().Name
+            );
+
+            Unmap (oldSource.Device.Uuid);
+            MapSource (newSource, makeActive);
+        }
+
         private class MapDeviceJob : IJob
         {
             IDevice device;
@@ -221,18 +246,28 @@ namespace Banshee.Dap
                 }
 
                 if (source != null) {
-                    service.MapSource (source);
+                    service.MapSource (source, false);
                 }
             }
         }
 
-        private void MapSource (DapSource source)
+        private void MapSource (DapSource source, bool active)
         {
-            ThreadAssist.ProxyToMain (() => {
+            lock (sync) {
+                sources [source.Device.Uuid] = source;
+                source.RequestUnmap += OnRequestUnmap;
+            }
 
+            ThreadAssist.ProxyToMain (() => {
+            
                 ServiceManager.SourceManager.AddSource (source);
                 source.NotifyUser ();
 
+                if (active)
+                {
+                    ServiceManager.SourceManager.SetActiveSource (source);
+                }
+
                 // If there are any queued device commands, see if they are to be
                 // handled by this new DAP (e.g. --device-activate=file:///media/disk)
                 try {
@@ -254,6 +289,15 @@ namespace Banshee.Dap
             });
         }
 
+        private void OnRequestUnmap (object sender, EventArgs e)
+        {
+            DapSource source = sender as DapSource;
+            if (source != null) {
+                Log.DebugFormat ("DapService: unmap request from {0}", source.Device.Uuid);
+                UnmapDevice (source.Device.Uuid);
+            }
+        }
+
         internal void UnmapDevice (string uuid)
         {
             ThreadAssist.SpawnFromMain (() => Unmap (uuid));
@@ -276,6 +320,7 @@ namespace Banshee.Dap
             }
 
             if (source != null) {
+                source.RequestUnmap -= OnRequestUnmap;
                 source.Dispose ();
                 ThreadAssist.ProxyToMain (delegate {
                     try {
@@ -287,14 +332,6 @@ namespace Banshee.Dap
             }
         }
 
-        private void OnSourceRemoved (SourceEventArgs args)
-        {
-            DapSource dap_source = args.Source as DapSource;
-            if (dap_source != null) {
-                UnmapDevice (dap_source.Device.Uuid);
-            }
-        }
-
         private void OnHardwareDeviceAdded (object o, DeviceAddedArgs args)
         {
             MapDevice (args.Device);
@@ -302,7 +339,16 @@ namespace Banshee.Dap
 
         private void OnHardwareDeviceChanged (object o, DeviceChangedEventArgs args)
         {
-            MapDevice (args.Device);
+            DapSource source;
+            if (!sources.TryGetValue (args.Device.Uuid, out source)) {
+                MapDevice (args.Device);
+                return;
+            }
+
+            PotentialSource potential = source as PotentialSource;
+            if (potential != null) {
+                potential.TryInitialize ();
+            }
         }
 
         private void OnHardwareDeviceRemoved (object o, DeviceRemovedArgs args)
diff --git a/src/Dap/Banshee.Dap/Banshee.Dap/DapSource.cs b/src/Dap/Banshee.Dap/Banshee.Dap/DapSource.cs
index e4b7983..e4ae1a6 100644
--- a/src/Dap/Banshee.Dap/Banshee.Dap/DapSource.cs
+++ b/src/Dap/Banshee.Dap/Banshee.Dap/DapSource.cs
@@ -88,7 +88,7 @@ namespace Banshee.Dap
         {
         }
 
-        public virtual void DeviceInitialize (IDevice device)
+        public virtual void DeviceInitialize (IDevice device, bool force)
         {
             this.device = device;
             TypeUniqueId = device.Serial;
@@ -176,6 +176,8 @@ namespace Banshee.Dap
             protected set { supports_podcasts = value; }
         }
 
+        internal event EventHandler RequestUnmap;
+
 #region Source
 
         protected override void Initialize ()
@@ -343,6 +345,11 @@ namespace Banshee.Dap
         protected override void Eject ()
         {
             Flush ();
+
+            var h = RequestUnmap;
+            if (h != null) {
+                h (this, EventArgs.Empty);
+            }
         }
 
         private void Flush ()
diff --git a/src/Dap/Banshee.Dap/Banshee.Dap/InvalidDeviceException.cs 
b/src/Dap/Banshee.Dap/Banshee.Dap/InvalidDeviceException.cs
index f8dbb76..d8f7b66 100644
--- a/src/Dap/Banshee.Dap/Banshee.Dap/InvalidDeviceException.cs
+++ b/src/Dap/Banshee.Dap/Banshee.Dap/InvalidDeviceException.cs
@@ -33,4 +33,8 @@ namespace Banshee.Dap
     public class InvalidDeviceException : ApplicationException
     {
     }
+
+    public class InvalidDeviceStateException : InvalidDeviceException
+    {
+    }
 }
diff --git a/src/Dap/Banshee.Dap/Banshee.Dap/PotentialSource.cs 
b/src/Dap/Banshee.Dap/Banshee.Dap/PotentialSource.cs
new file mode 100644
index 0000000..8aab60c
--- /dev/null
+++ b/src/Dap/Banshee.Dap/Banshee.Dap/PotentialSource.cs
@@ -0,0 +1,195 @@
+//
+// PotentialSource.cs
+//
+// Author:
+//   Nicholas Little <arealityfarbetween googlemail com>
+//
+// Copyright (C) 2014 Nicholas Little
+//
+// 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 Banshee.Collection.Database;
+using Banshee.Dap.Gui;
+using Banshee.Hardware;
+using Banshee.Sources;
+using Banshee.Sources.Gui;
+using Hyena;
+using Mono.Addins;
+
+namespace Banshee.Dap
+{
+    internal class PotentialSource : DapSource
+    {
+        private readonly TypeExtensionNode Claimant;
+        private readonly DapService Service;
+
+        private object lock_object = new object();
+        private bool initialized;
+
+        internal PotentialSource (DapService service, TypeExtensionNode claimant, IDevice device)
+        {
+            Claimant = claimant;
+            Service = service;
+
+            IsTemporary = true;
+
+            SupportsPlaylists = false;
+            SupportsPodcasts = false;
+            SupportsVideo = false;
+
+            DeviceInitialize (device, false);
+            Initialize ();
+        }
+
+        #region overridden members of Source
+
+        protected override void Initialize ()
+        {
+            base.Initialize ();
+            ThreadAssist.ProxyToMain (() => {
+                ClearChildSources ();
+                Properties.Set<ISourceContents> ("Nereid.SourceContents", new InactiveDapContent (this));
+            });
+        }
+
+        #endregion
+
+        #region implemented abstract members of RemovableSource
+
+        public override void Import ()
+        {
+            throw new NotSupportedException ();
+        }
+
+        public override bool CanUnmap {
+            get { return false; }
+        }
+
+        public override bool CanImport {
+            get { return false; }
+        }
+
+        public override bool IsReadOnly {
+            get { return true; }
+        }
+
+        public override long BytesUsed {
+            get { return 0L; }
+        }
+
+        public override long BytesCapacity {
+            get { return 0L; }
+        }
+
+        #endregion
+
+        #region implemented abstract members of DapSource
+
+        public override void AddChildSource (Source child)
+        {
+        }
+
+        public override void RemoveChildSource (Source child)
+        {
+        }
+
+        protected override void AddTrackToDevice (DatabaseTrackInfo track, SafeUri fromUri)
+        {
+            throw new NotSupportedException ();
+        }
+
+        private bool TryDeviceInitialize (bool force, out DapSource source)
+        {
+            lock (lock_object) {
+                source = default (DapSource);
+
+                if (initialized) {
+                    return false;
+                }
+
+                SetStatus (
+                    AddinManager.CurrentLocalizer.GetString ("Trying to Claim Your Device\u2026"),
+                    false,
+                    true,
+                    "dialog-information"
+                );
+
+                Log.Debug ("PotentialSource: Creating Instance");
+                DapSource src = null;
+                try {
+                    src = (DapSource) Claimant.CreateInstance ();
+                    Log.Debug ("PotentialSource: Initializing Device");
+                    src.DeviceInitialize (Device, force);
+                    Log.Debug ("PotentialSource: Loading Contents");
+                    src.LoadDeviceContents ();
+
+                    Log.DebugFormat ("PotentialSource: Success, new Source {0}", src.Name);
+                    src.AddinId = Claimant.Addin.Id;
+                    source = src;
+                    initialized = true;
+                } catch (InvalidDeviceStateException e) {
+                    Log.Warning (e);
+                } catch (InvalidDeviceException e) {
+                    Log.Warning (e);
+                } catch (Exception e) {
+                    Log.Error (e);
+                }
+
+                bool success = !object.ReferenceEquals (source, default (DapSource));
+
+                SetStatus (
+                    success ? AddinManager.CurrentLocalizer.GetString ("Connection Successful. Please 
wait\u2026")
+                            : AddinManager.CurrentLocalizer.GetString ("Connection Failed\u2026"),
+                    !success,
+                    success,
+                    success ? "dialog-information"
+                            : "dialog-warning"
+                );
+
+                return success;
+            }
+        }
+        #endregion
+
+        internal void TryClaim ()
+        {
+            Log.DebugFormat ("PotentialSource: TryClaim {0} as {1}", Device.Name, Claimant.Type);
+            ThreadAssist.SpawnFromMain (() => {
+                DapSource source;
+                if (TryDeviceInitialize (true, out source)) {
+                    Service.SwapSource (this, source, true);
+                }
+            });
+        }
+
+        internal void TryInitialize ()
+        {
+            Log.DebugFormat ("PotentialSource: TryInitialize {0} as {1}", Device.Name, Claimant.Type);
+            ThreadAssist.SpawnFromMain (() => {
+                DapSource source;
+                if (TryDeviceInitialize (false, out source)) {
+                    Service.SwapSource (this, source, false);
+                }
+            });
+        }
+    }
+}
+
diff --git a/src/Dap/Banshee.Dap/Makefile.am b/src/Dap/Banshee.Dap/Makefile.am
index 11b9038..da8f34f 100644
--- a/src/Dap/Banshee.Dap/Makefile.am
+++ b/src/Dap/Banshee.Dap/Makefile.am
@@ -9,6 +9,7 @@ SOURCES =  \
        Banshee.Dap.Gui/DapInfoBar.cs \
        Banshee.Dap.Gui/DapPropertiesDialog.cs \
        Banshee.Dap.Gui/DapPropertiesDisplay.cs \
+       Banshee.Dap.Gui/InactiveDapContent.cs \
        Banshee.Dap.Gui/LibrarySyncOptions.cs \
        Banshee.Dap.Gui/PurchasedMusicActions.cs \
        Banshee.Dap/DapLibrarySync.cs \
@@ -21,6 +22,7 @@ SOURCES =  \
        Banshee.Dap/MediaGroupSource.cs \
        Banshee.Dap/MusicGroupSource.cs \
        Banshee.Dap/PodcastGroupSource.cs \
+       Banshee.Dap/PotentialSource.cs \
        Banshee.Dap/RemovableSource.cs \
        Banshee.Dap/SyncPlaylist.cs \
        Banshee.Dap/VideoGroupSource.cs
diff --git a/src/Dap/Banshee.Dap/Resources/ActiveSourceUI.xml 
b/src/Dap/Banshee.Dap/Resources/ActiveSourceUI.xml
index eb5bfac..ce56a0c 100644
--- a/src/Dap/Banshee.Dap/Resources/ActiveSourceUI.xml
+++ b/src/Dap/Banshee.Dap/Resources/ActiveSourceUI.xml
@@ -1,6 +1,7 @@
 <ui>
   <toolbar name="HeaderToolbar">
     <placeholder name="SourceActions">
+      <toolitem action="ClaimDapAction"/>
       <toolitem action="UnmapSourceAction"/>
       <toolitem action="SyncDapAction"/>
     </placeholder>
diff --git a/src/Dap/Banshee.Dap/Resources/GlobalUI.xml b/src/Dap/Banshee.Dap/Resources/GlobalUI.xml
index 9f6869c..5287014 100644
--- a/src/Dap/Banshee.Dap/Resources/GlobalUI.xml
+++ b/src/Dap/Banshee.Dap/Resources/GlobalUI.xml
@@ -1,6 +1,7 @@
 <ui>
   <popup name="RemovableSourceContextMenu" action="RemovableSourceContextMenuAction">
     <placeholder name="AboveImportSource">
+      <menuitem action="ClaimDapAction"/>
       <menuitem action="SyncDapAction"/>
     </placeholder>
   </popup>


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