AmalgaDrive sample

Source repository: https://github.com/aelyo-softworks/AmalgaDrive

A WPF application shell folder. Demonstrates the File On-Demand feature. The Visual Studio solution has 3 projects:

AmalgaDrive.DavServer: A zero-dependency custom WebDAV Server library that serves a local directory as a WebDAV directory resource. Exposes a custom REST / JSON API. Written for .NET core.

AmalgaDrive.DavServerSite: A sample WebDAV site. It’s merely a host for the AmalgaDrive.DavServer library. Written with ASP.NET core.

AmalgaDrive: a WPF Shell Namespace Extension that amalgamates various Cloud drives.

AmalgaDrive has been written to connect to implementations of the ShellBoost’s IRemoteFileSystem interface (described in the Files On-Demand support chapter). The sample currently contains only one implementation of IRemoteFileSystem which is an implementation that relies on a WebDAV server for the “remote” file system. The implementation is located in the DavFileSystem.cs and DavResource.cs files. This DavFileSystem implementation has been written to connect to any WebDAV service, local, or on the internet. It has been successfully tested to work with AmalgaDrive.DavServerSite, www.box.com and www.cloudme.com WebDAV implementations.

Other implementation of IRemoteFileSystem could be added to enable AmalgaDrive to use other cloud “drive” systems (Google, Amazon, etc.).

AmalgaDrive.DavServerSite sample

AmalgaDrive.DavServerSite is not mandatory to run to test AmalgaDrive since AmalgaDrive supports by default other WebDAV services such as www.box.com or www.cloudme.com. if you want to test it, it has no UI, it’s an API-only site.

What it does is serve an existing folder on your hard disk as a WebDAV directory resource. The served folder can be configured in the appsettings.json file in the project. It looks like this:

{
  "Logging": {
    "IncludeScopes": false,
    "Debug": {
      "LogLevel": {
        "Default": "Warning"
      }
    },
    "Console": {
      "LogLevel": {
        "Default": "Warning"
      }
    }
  },
  "DavServer": {
    "FileSystem": {
      "TypeName": "AmalgaDrive.DavServer.FileSystem.Local.LocalFileSystem",
      "Properties": {
        "RootPath": "c:\\temp"
      }
    }
  }
}

Just change the RootPath value and restart the server to serve another folder on your disk.

By default, it runs on http://localhost:61786/

Using AmalgaDrive

When you start AmalgaDrive, open the “File / Add a Cloud Drive…” menu item, then enter login information and press OK.

This is an example with the www.box.com service:

Using AmalgaDrive - Picture 57

This is an example with the AmaldaDrive.DavServerSite sample (there is no authentication for demonstration purposes)

Using AmalgaDrive - Picture 71

Once you’ve added at least one Cloud Drive, you should see something like this. You can add any number of drives. Right now, remember that the program only supports WebDav-type cloud drives.

Using AmalgaDrive - Picture 72

Synchronization should happen automatically in 300 seconds (by default). If you want to force synchronization, just press the green refresh button. Note there is a menu item in the Tools menu that allows you to see sync logs.

Let’s suppose you’re using a www.box.com account with the following files in your web personal storage:

Using AmalgaDrive - Picture 66

If you want to see the drive like a standard Windows directory path, click on the AmalgaDrive UI first orange button (tooltip says “Open Path”). You should see something like this in Windows Explorer:Using AmalgaDrive - Picture 67

As you see it looks like OneDrive, with a Status column that represents the offline availability of each shell item. Nothing is stored locally yet, as the little blue cloud icon means “Available when online”. If you double-click on one file for example the AboutWindow.xaml.cs sample file, Windows will ask AmalgaDrive to download the file, once downloaded (in the background, the process is completely transparent to the end-user), the file will open (with your configured editor for .cs files in this case). At the same time, the shell will automatically update the icon to a little green circle check that means “Available on this device”.

Using AmalgaDrive - Picture 68

Note that for this to work, we don’t use any Shell Namespace Extension. But you can also view the same directory hierarchy using a ShellBoost Namespace Extension embedded into AmalgaDrive. Click on the AmalgaDrive UI second orange button (tooltip says “Open Extension”). You should see something like this in Windows Explorer:

Using AmalgaDrive - Picture 3

As you see we have the same Status icon column in our namespace extension. The column is added to a folder with this code:

public OnDemandRootFolder(OnDemandShellFolderServer server, ShellItemIdList idList, DirectoryInfo info)
    : base(idList, info)
{
    if (server == null)
        throw new ArgumentNullException(nameof(server));
 
    if (info == null)
        throw new ArgumentNullException(nameof(info));
 
    Server = server;
 
    // since this is a FileSystem oriented NSE, we let base properties pass through
    ReadPropertiesFromShell = true;
 
    /// We just add the sync status column.
    AddColumn(Props.System.StorageProviderUIStatus, SHCOLSTATE.SHCOLSTATE_ONBYDEFAULT);
}

And each item declares its state like this:

public override bool TryGetPropertyValue(PropertyKey key, out object value)
{
    // this property is asked by the Shell to display the sync status icon
    if (key == Props.System.StorageProviderUIStatus)
    {
        value = GetSyncState(this);
        return true;
    }
    return base.TryGetPropertyValue(key, out value);
}
 
private static MemoryPropertyStore GetSyncState(ShellItem item)
{
    var ms = new MemoryPropertyStore();
    ms.SetValue(Props.System.PropList.StatusIcons, "prop:" + Props.System.StorageProviderState.CanonicalName);
    ms.SetValue(Props.System.PropList.StatusIconsDisplayFlag, (uint)2);
 
    // read the sync state from the shell
    // it works because we have set ReadPropertiesFromShell = true as a passthrough
    var state = item.GetPropertyValue(Props.System.StorageProviderState, 0);
    ms.SetValue(Props.System.StorageProviderState, state);
 
    return ms;
}