Introduction
Cloud storage has cleverly become a good selling point for many solutions. Though the term 'Cloud' is a bit over-rated (totally personal opinion)
it eats up most of the legacy problems. At least a bunch of people are at peace as the complexity of management (be it storage or any other thing) is left to the best brains at an affordable cost. For example, the obvious drawbacks of maintaining redundant storage media are not handled anymore. And thus businesses like Google Drive and Sky Drive have rocketed many folds in such short span of times in consumer to SMBs to large business solutions.
To make things more flexible or professional these days we get free development APIs from these providers. Such APIs not only make private applications integrate-able but also brings in possibilities for seamlessly consuming cloud services with just one click of a mouse for the end-user.
This brings me to the point of end-user. Many a times such 'robots', so fascinated by cloud or rain, are in dire need for solutions that can be dumb-proof and also 'Cloud oriented' (I consciously use the term 'Cloud Oriented' in a smaller scope from what it can actually mean). I have personally spoken to a bunch of folks at different conferences (some related to Cloud Computing) who understand the need and actually need 'on-demand-cloud-storage-with-one-click' that is integrate-able with their legacy (or not so legacy) enterprise storage solutions. Some of them are good with corporate clouds, some to their private ones. But the most interesting ones are talking of already available cloud solutions. In short they need their existing storage frameworks to be seamlessly replaced by
Google drive or sky drive or box or dropbox or etc or etc. The reasons they talk of are also quite logical. They don't want to buy hard-drives every month. They also don't want to risk their data by not maintaining automatically replicating scheduled scripts. In even shorter term, they want to leave the hair-loosing thoughts to the people who have already lost them :)
Figure 0: Main Explorer UI
Background
It was one such 'daily 6 hours travel for office' that I met a sufferer of data-loss fear. And he too demanding the same thing, brought me to the page where I decided to do a POC with one of the free-public storage APIs. After trying hands on a few I found Google's API to be more 'fluid' than the others and decided to GO at it.
After a few initial tries, I got a good bite at the still maturing Google Drive 2 API. Having past experience in exposing storage data servers APIs, I wondered, how similar all of them could be and just how comfortable client applications expects to be :) The POC soon bubbled up to a semi-handsome store box for the Windows-7 desktop PC. Drive2's C# sdk had been the biggest bet with some presentation and queue implementations I could bundle this tiny app in a week. Still far from perfect and needs more improvements, I decided to write up my article for reviews and sharing some functional code for those who are still deciding to consume Google Drive2 API
SDK.
Using the application
Hey, so here is "My GDrive Explorer" or "GDrive Explorer" (incidentally these are the release and dev version names respectively).
What the user can do with the application?
- Asks for authcode by directing the user to a 'grant-access'
URL
- On getting access it shows some user info on the left panel
- On getting another access it shows the drive contents on the right panel
- Lets authorized user browse your drive similar to windows explorer (back/forward/home/refresh)
- Lets authorized user create new folders
- Lets authorized user upload/download files
- Lets authorized user delete/rename files/folders
- Presently
Word/Excel/PowerPoint/text/PDF file uploading are supported (Google drive supports files of types
https://support.google.com/drive/answer/2423485?hl=en)
- Downloading of all user files are supported (Google
doesn't let users download files created with GoogleDocs)
- Upload multiple files
in parallel (with upload progress notification)
- Download multiple files
in parallel (with download progress notification)
- Search files/folders within the current Drive folder (free text search)
- Trash files (not folders) to bin
- Trash view and able to restore/permanently-delete trashed documents
- Send documents directly from explorer to GoogleDrive by right clicking on a file-item (only single selection is supported. The context menu item works on windows 7 and should be visible till the application is running)
- Send documents directly from MS Office Applications (Word, Excel,
PowerPoint, and Outlook) by installing additional Office COM AddIn (developed using NetOffice - MS Office in .NET - http://netoffice.codeplex.com/)
Usage hints:
The application works with oauth2 authentication mechanism and would prompt the user to grant access to the Files and About mechanism. The user has to click "Accept" button for both requests. The page is displayed in a popup-webbrowser control. Post success the response message is scraped and the authtoken is used to access. If scraping fails a fallback authtoken dialog is shown for manual authentication.
There would be 2 dialogs here one for "About" and one for "Files". Both have to be allowed for further access.
My experience here is that the user has to click couple of three times on each of the two authorization dialogs to activate it. It is a known issue and work is in progress.
Figure 1: Authorization Dialog
Figure 2: Authorization Fallback Dialog
On Fail, application will not allow to continue and will shutdown.
Safety Concerns: The entire code is made available with this article and the application does not do any
mischievous things with you account. You may choose to walk through the whole code-base to guarantee your safety.
On Successful login, user should see a explorable screen as below. The features are tool-tipped and self explanatory. For a detailed usage guidelines, please download the user manual attached with this article.
Figure 3: Upload/Download Progress Indication
When the application is active, users can direct send a selected file to the application. These files will directly be queued for uploading to
Google drive in the currently selected folder.
Figure 4: Integration with Windows Explorer
Installing MS Office Addins
Download "MicrosoftOfficeAddIns.zip" attached at the top of this article. These addins are office COM AddIns for office 2007 and 2010 developed using NetOffice - MS Office in .NET (http://netoffice.codeplex.com/).
Once downloaded, you can use the standard "regasm" command from the command prompt to install or uninstall. The commands are attached in 'bat' files with the download.
To install use:
regasm GDrive.Explorer.MSOfficeAddIn.dll
To uninstall use:
regasm GDrive.Explorer.MSOfficeAddIn.dll /unregister
If everything works well here is what you should see after restarting the office applications. And once you click the 'send' button the document should directly get uploaded to your Google Drive Cloud Storage Space.
Please note, that the send to Google Drive would
only work when the GDrive Explorer application is active and running.
The source can be compiled by including necessary binaries from http://netoffice.codeplex.com/.
How does it work?
GDrive.Framework
A little delve into the code. The code GoogleDrive API is wrapped in an
Framework called as GDrive.Framework.
The framework acts as a Facade for access to the storage APIs.
There is a authentication mechanism which goes through oAuth 2 and an authCode has
to be received from a grant-access mechanism. This is routed through a webbrowser control in the
GDriveAuthentication
class. The code is same as mentioned
here [https://developers.google.com/drive/quickstart-cs]
The attached code has dependencies on these binaries and would only compile when they are referenced.
Other Links:
Drive2 API
Google APIs
public class GDriveAuthentication
{
public DriveService GetDriveService()
{
string CLIENT_ID = "CLIENT ID";
string CLIENT_SECRET = "CLIENT SECRET";
var provider = new NativeApplicationClient(
GoogleAuthenticationServer.Description, CLIENT_ID, CLIENT_SECRET);
var auth = new OAuth2Authenticator<NativeApplicationClient>(provider, GetAuthorization);
var service = new DriveService(new BaseClientService.Initializer()
{
Authenticator = auth
});
return service;
}
private IAuthorizationState GetAuthorization(NativeApplicationClient arg)
{
IAuthorizationState state =
new AuthorizationState(new[] { DriveService.Scopes.Drive.GetStringValue() });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
Uri authUri = arg.RequestUserAuthorization(state);
var authCode = AuthorizationWindow.GetToken(authUri.ToString());
if (string.IsNullOrEmpty(authCode))
authCode = Interaction.InputBox("We did not find authcode. " +
"Please enter here to continue", "Authorization Code", string.Empty);
return arg.ProcessUserAuthorization(authCode ?? string.Empty, state);
}
}
The main Facade class for exploring the drive (called as GDriveExplorer
) provides generic implementations for the methods of use.
public class GDriveExplorer
{
private DriveService service;
public GDriveExplorer()
{
service = new GDriveAuthentication().GetDriveService();
}
public GDriveFilesResponse GetFiles(string parentId = "root", string search = null,
string pageToken = null, int? perpage = null, SortOrder order = SortOrder.LastModifiedDesc)
{
...
}
public GDriveFilesResponse GetTrashedFiles(string search = null,
string pageToken = null, int? perpage = null)
{
...
}
public bool TrashFile(string id)
{
...
}
public bool UnTrashFile(string id)
{
...
}
public bool DeleteFile(string id)
{
...
}
public GDriveAbout GetInfo()
{
...
}
public object UploadFileAsync(UploadQueueItem item, ProgressChange progressCallback)
{
...
}
public bool UploadFile(UploadQueueItem item)
{
...
}
public GDriveFile CreateFolder(GDriveFile folder, string parent)
{
...
}
public GDriveFile Update(GDriveFile file)
{
...
}
public object DownloadFile(DownloadQueueItem item, ProgressChange progressCallback)
{
...
}
}
GDrive.Explorer
This is a WPF Application using MVVM. The entire code is MVVMd by
practice so there is absolutely no code in the code-behind. The strictest MVVM is hard and sometimes not practical. So many people use it as a guideline rather than a practice. My personal experience with MVVM is also rudimentary and to call it a
practice I have to toil harder. However, this is just a initial practice, so could do without code behind. This is
what the code behind contains :)
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
The main application is a singleton application. Means you cannot start more than one instance of the application. The class for that is
App
, which follows a standard singleton instance application using
Microsoft.VisualBasic
.
public class SingleInstanceManager : WindowsFormsApplicationBase
{
private App application;
private ReadOnlyCollection<string> commandLine;
public SingleInstanceManager()
{
IsSingleInstance = true;
}
protected override bool OnStartup(Microsoft.VisualBasic.ApplicationServices.StartupEventArgs eventArgs)
{
commandLine = eventArgs.CommandLine;
application = new App();
application.Run();
return false;
}
protected override void OnShutdown()
{
base.OnShutdown();
}
protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
{
base.OnStartupNextInstance(eventArgs);
application.Activate(eventArgs.CommandLine);
}
}
Upload and download queue implementations are used to implement asynchronous upload/download patterns with progress updater. Somehow the upload notification is only fired at the end of the upload. I am still working on the
AsyncUpload
function. Downloads functionally deliver in progress notifications. These classes can be found in the attached codebase.
Notification Icon in the taskbar is used to control sending the application in the background. The icon can be double-clicked to show/hide the application window without closing it.
public NotifyIconManager(Window mainwindow)
{
try
{
System.Windows.Forms.NotifyIcon ni = new System.Windows.Forms.NotifyIcon()
{
Icon = System.Drawing.Icon.ExtractAssociatedIcon(this.GetType().Assembly.Location),
ContextMenu = new System.Windows.Forms.ContextMenu(),
Text = "Doubleclick to Show/Hide Window",
Visible = true
};
ni.ContextMenu.MenuItems.Add(new System.Windows.Forms.MenuItem(
"Exit", (o, e) => App.InvokeShutDown()));
ni.DoubleClick += (s, args) =>
{
if (!mainwindow.IsVisible)
{
mainwindow.Show();
mainwindow.Activate();
}
else
{
mainwindow.Hide();
}
};
}
catch { }
}
The context menu item with icon in the Windows Explorer can be done using registry tweaks as in the
RegistryEntry
class.
public class RegistryEntry
{
public void CreateRegistry(params string[] extensions)
{
var filename = Assembly.GetEntryAssembly().Location;
foreach (var ext in extensions)
{
var regmenu = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\" + ext + "\\shell\\MyGDrive");
if (regmenu != null)
{
regmenu.SetValue("", "Send to My GDrive");
regmenu.SetValue("icon", "\"" + filename + "\",0");
regmenu.SetValue("MultiSelectModel", "Single");
regmenu.SetValue("EditFlags", new byte[] { 0x01, 0x00, 0x00, 0x00 }, RegistryValueKind.Binary);
var regcmd = regmenu.CreateSubKey("command");
if (regcmd != null)
regcmd.SetValue("", "\"" + filename + "\" \"%1\"");
}
}
}
public void RemoveRegistry(params string[] extensions)
{
foreach (var ext in extensions)
{
var key = "SOFTWARE\\Classes\\" + ext + "\\shell\\MyGDrive";
if (Registry.CurrentUser.OpenSubKey(key) != null)
{
Registry.CurrentUser.DeleteSubKeyTree(key);
}
}
}
public bool IsUserAdministrator
{
get
{
bool isAdmin = false;
try
{
var principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
}
catch (UnauthorizedAccessException)
{
}
catch (Exception)
{
}
return isAdmin;
}
}
}
The rest of the Presentation layer is divided in the following sections:
- Commands: RelayCommands used in the viewmodels
- Controls: A single control used in the application is
SearchTextBox
- Converters: A lot of value bindings use these standard converters for presentation
- ViewModels: The viewmodels used in the view
- Images: Image/Icon resources used in the view
Using the code
GDrive.Framework can be used as below.
GDriveFilesResponse filesResponse = GetFiles(parentId, searchString, pageToken, perpage, sortorder);
string nextpagetoken = filesResponse.NextPageToken;
filesResponse.Files.ToList().ForEach(f =>
{
});
GDriveFilesResponse filesresponse = GDriveFilesResponse GetTrashedFiles(searchstring, pageToken, perpage);
bool success = TrashFile(fileid);
bool success = UnTrashFile(fileid);
bool success = DeleteFile(fileid);
GDriveAbout aboutinfo = GetInfo();
object result = UploadFileAsync(uploadQueueItem, progressChangeHandler);
bool success = UploadFile(uploadQueueItem);
GDriveFile folderinfo = CreateFolder(gDriveFileInfo, parentfolderid);
GDriveFile fileinfo = Update(gDriveFileInfo);
object result = DownloadFile(downloadQueueItem, progressChangeHandler);
Known Issues
- Ordering by name etc is not supported by Google yet, there are reported change requests in these areas (presently ordered by date-modified descending is the default).
- I
haven't tested the upload async yet. The available implementation is with synchronous
upload function within a
BeginInvoke
. - Pagination is not implemented yet (presently only first 100 items are shown). I am looking for a way to do virtualized scrolling following MVVM (no code behind) -
suggestions welcome in this area
- Some other new functionality development in progress.
History
- First post dated - September 3, 2013
- Added MS Office AddIns - September 6, 2013
- Build fixes - September 16, 2013