×
Namespaces

Variants
Actions
(Difference between revisions)

Dropbox with Windows Phone

From Nokia Developer Wiki
Jump to: navigation, search
jupaavola (Talk | contribs)
(Jupaavola -)
vinayppatil (Talk | contribs)
m (Vinayppatil -)
 
(18 intermediate revisions by 4 users not shown)
Line 1: Line 1:
[[Category:Draft]]
+
[[Category:Web Services on Windows Phone]][[Category:Files/Data on Windows Phone]][[Category:Code Examples]][[Category:XAML]][[Category:Windows Phone 7.5]][[Category:Windows Phone 8]]
{{Abstract|This article explains how to connect Windows Phone to Dropbox cloud service by using Sharpbox }}
+
{{Abstract|This article explains how to connect Windows Phone to [http://www.dropbox.com/ DropBox] cloud service using [http://sharpbox.codeplex.com/ SharpBox]}}
 
+
{{SeeAlso|[http://dropboxwp7.codeplex.com/ Dropbox for Windows Phone 7] (codeplex)}}
 
{{ArticleMetaData <!-- v1.2 -->
 
{{ArticleMetaData <!-- v1.2 -->
|sourcecode= <!-- Link to example source code e.g. [[Media:The Code Example ZIP.zip]] -->
+
|sourcecode= [[Media:DropBoxImages.zip]]
 
|installfile= <!-- Link to installation file (e.g. [[Media:The Installation File.sis]]) -->
 
|installfile= <!-- Link to installation file (e.g. [[Media:The Installation File.sis]]) -->
 
|devices= <!-- Devices tested against - e.g. ''devices=Nokia 6131 NFC, Nokia C7-00'') -->
 
|devices= <!-- Devices tested against - e.g. ''devices=Nokia 6131 NFC, Nokia C7-00'') -->
 
|sdk= <!-- SDK(s) built and tested against (e.g. [http://linktosdkdownload/ Qt SDK 1.1.4]) -->
 
|sdk= <!-- SDK(s) built and tested against (e.g. [http://linktosdkdownload/ Qt SDK 1.1.4]) -->
|platform= <!-- Compatible platforms - e.g. Symbian^1 and later, Qt 4.6 and later -->
+
|dependencies= Sharpbox 1.2
|devicecompatability= <!-- Compatible devices e.g.: All* (must have internal GPS) -->
+
|dependencies= <!-- Any other/external dependencies e.g.: Google Maps Api v1.0 -->
+
 
|signing= <!-- Signing requirements - empty or one of: Self-Signed, DevCert, Manufacturer -->
 
|signing= <!-- Signing requirements - empty or one of: Self-Signed, DevCert, Manufacturer -->
 
|capabilities= <!-- Capabilities required by the article/code example (e.g. Location, NetworkServices. -->
 
|capabilities= <!-- Capabilities required by the article/code example (e.g. Location, NetworkServices. -->
Line 19: Line 17:
 
|review-by= <!-- After re-review: [[User:username]] -->
 
|review-by= <!-- After re-review: [[User:username]] -->
 
|review-timestamp= <!-- After re-review: YYYYMMDD -->
 
|review-timestamp= <!-- After re-review: YYYYMMDD -->
|update-by= <!-- After significant update: [[User:username]]-->
+
|update-by= [[User:vinayppatil]]
|update-timestamp= <!-- After significant update: YYYYMMDD -->
+
|update-timestamp= 20130124
 
|creationdate= 20120831
 
|creationdate= 20120831
 
|author= [[User:Jupaavola]]
 
|author= [[User:Jupaavola]]
Line 27: Line 25:
 
== Introduction ==
 
== Introduction ==
  
Some user may want to connect other cloud services than SkyDrive which is offered by Microsoft. By using Open Source project called Sharpbox, you can access to Dropbox clouds. At the time of writing this article, it is still not possible to access other clouds like CloudMe in Windows Phone although those others works with desktops.
+
''SharpBox'' is an open source project that provides access to ''Dropbox'' cloud services on Windows Phone. This article covers basic functionality of Sharpbox library: how to use it and how to wrap its synchronous functions to work with Windows Phone. This provides a useful complement or addition to Microsoft's SkyDrive cloud services.
 +
{{Note|SharpBox also provides access to other cloud services like ''CloudMe''. However at time of writing this article these services work on Desktop only (not Windows Phone)}}
  
== Summary ==
+
== Using SharpBox ==
 +
=== Getting SharpBox ===
  
This article covers basic functionality of Sharpbox libary. How to use it and how to wrap its synchronous functions to work with Windows Phone.
+
SharpBox is available as a [http://visualstudiogallery.msdn.microsoft.com/27077b70-9dad-4c64-adcf-c7cf6bc9970c NuGet] package or for download from codeplex [http://sharpbox.codeplex.com/ here]. If you download a Sharpbox release, extract the downloaded zip file. In Visual Studion add '''AppLimit.CloudComputing.SharpBox.dll''' and '''Newtonsoft.Json.Silverlight.dll''' files from '''sl3-wp''' folder as reference to your project.
  
Sample code also provided.
+
{{Warning|The newer release (1.2) has few bugs when logging and authenticating to Dropbox service. Mainly, token exchanging uses synchronous functions which do not work in Windows Phone. Earlier versions have functions to log in with credentials, those will work only with older Dropbox v0 API. If you have Dropbox key & secret for older API, older library works just fine. But if you create Dropbox application now, you will get Dropbox v1 key and secret and those do not work with older API.}}
  
== Required software ==
+
=== Prerequisites ===
  
Download Sharpbox from website: http://sharpbox.codeplex.com/
+
SharpBox needs a valid Dropbox application key and secret (for release, not required during testing and developing).These can be obtained in the [http://www.dropbox.com/developers developer section] of the Dropbox website.
  
Note that newer release 1.2 has few bugs when logging and authenticating to Dropbox service. Mainly, token exchanging uses synchronous functions which does not work in Windows Phone. Earlier versions have functions to log in with credentials, those will work only with older Dropbox v0 API. If you have Dropbox key & secret for older API, older library works just fine. But if create Dropbox application now, you will get Dropbox v1 key and secret and those does not work with older API.
+
=== SharpBox should not run in UI thread ===
  
If you download Sharpbox release, extract downloaded zip file to somewhere. In Visual Studion add two DLL, '''AppLimit.CloudComputing.SharpBox.dll''' and '''Newtonsoft.Json.Silverlight.dll''', files from '''sl3-wp''' folder as reference to your project.
+
The SharpBox library has both synchronous and and asynchronous functions for many operations. Both forms are useful for desktop usage, but the synchronous versions cannot be used directly in Windows Phone apps because these calls block the running UI thread and therefore the whole application.  
  
Also NuGet packages are available, get NuGet from here: http://visualstudiogallery.msdn.microsoft.com/27077b70-9dad-4c64-adcf-c7cf6bc9970c
+
The asynchronous functions can be used in the UI thread. If needed functions only exist in synchronous variants it is possible to run these in another (non UI) thread and return the results using a callback.  
  
== Sharpbox prerequisities ==
+
The following snippet shows how to use [http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher%28v=vs.95%29.aspx Dispatcher] to call {{Icode|parseFilesAndDirectories}} function - a normal private function in application thread. Example function {{Icode|CallbackFunction()}} is asynchronous callback function.
  
Sharpbox needs valid Dropbox application key and secret. These can be made in Dropbox website, developers section where you create own application. For testing and developing, you don't need to apply for production use.
+
<code csharp>
 
+
Dropbox developers: https://www.dropbox.com/developers
+
 
+
== Using Sharpbox ==
+
 
+
Sharpbox library has synchronized and asynchronized functions for many operations. For desktop usage both are fine but in Windows Phone, operating system and it's frameworks has their own limitations. All of the synchronized calls will block running UI thread and therefore the whole application. The only way to get things working is to use asynchronous functions. Some needed functions still lack of asynchronous versions but it is possible to use these by implementing own async versions to own application. Asynchronous parts are made with
+
 
+
=== Sharpbox does not run in UI thread ===
+
 
+
Note that used functions from Sharpbox are asynchronous and those are not run in UI thread where your application is running. Still it is possible to call your application functions or change member variables from callback functions by using dispatcher.
+
 
+
Example function '''CallbackFunction''' is asynchronous callback function. This snippet shows how to use dispatcher to call '''parseFilesAndDirectories''' function what is normail private function in application thread.
+
 
+
<code cpp>
+
 
void CallbackFunction(IAsyncResult result)
 
void CallbackFunction(IAsyncResult result)
 
{
 
{
            Deployment.Current.Dispatcher.BeginInvoke(() =>
+
    Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
+
    {
                if (fs != null)
+
    if (fs != null)
                {
+
        {
                    parseFilesAndDirectories(fs);
+
        parseFilesAndDirectories(fs);
                }
+
        }
            });
+
    });
 
}
 
}
 
</code>
 
</code>
Line 76: Line 62:
 
== Logging to Dropbox ==
 
== Logging to Dropbox ==
  
This is pretty easy, create credentials and configuration object. Then call '''BeginOpenRequest''' and wait for callback.
+
This is pretty easy, create credentials and configuration object. Then call {{Icode|BeginOpenRequest()}} and wait for callback.
  
 
=== Dropbox API v0, Sharpbox 1.1 and older ===
 
=== Dropbox API v0, Sharpbox 1.1 and older ===
<code cpp>
+
<code csharp>
        public void ConnectCloud(string username, string password)
+
public void ConnectCloud(string username, string password)
        {
+
    {
 
             DropBoxCredentials creds = new DropBoxCredentials();
 
             DropBoxCredentials creds = new DropBoxCredentials();
 
             creds.ConsumerSecret = APP_SECRET;
 
             creds.ConsumerSecret = APP_SECRET;
Line 89: Line 75:
  
 
             m_dropBox.BeginOpenRequest(LoginCallback, mCloudConfig, creds);           
 
             m_dropBox.BeginOpenRequest(LoginCallback, mCloudConfig, creds);           
        }
+
    }
 
</code>
 
</code>
  
 
=== Dropbox API v1, Sharpbox 1.2 ===
 
=== Dropbox API v1, Sharpbox 1.2 ===
 +
From Sharpbox 1.2, usage of '''DropBoxTokenIssuer.exe''' has been deprecated. Instead the access token is generated in code.
  
Dropbox wants application linked to your account and user must allow or deny access to it. At the same time, tokens can be exchanged. For exchanging, Sharpbox provides small utility tool which can be found from folder: '''<your project>\packages\AppLimit.CloudComputing.SharpBox.1.2.0.542\lib\net40-full'''
+
===Getting Security Token===
 +
====Generate a request token====
 +
In the first phase a request token has to be generated based on the app key and app secret issued by Dropbox in your developer account interface. The interface will be available after registration under [http://www.dropbox.com/developers developer console].
  
File is called: '''DropBoxTokenIssuer.exe''', it's a desktop application. Application will ask Dropbox application key and secret, output is plain text and you need to provide empty text file and put its path to output file section. Click authorize button, in-app browser goes to Dropbox and asks your credentials. Log in and link your application. After that you have token in TXT-file.
+
Create an application if you haven't already.
 +
<code csharp>
 +
//create a request token
 +
DropBoxRequestToken requestToken = DropBoxStorageProviderTools.GetDropBoxRequestToken(config,
 +
<<YOUR APP KEY>>,
 +
<<YOUR APP SECRET>>);
 +
</code>
  
[[File:Dropboxtokenissuer part.png]]
+
====Generate the authorization URL and visit it to allow access====
 +
In phase 2 the generated authorization URL has to be visited in a web browser control. The URL will be returned from SharpBox with the following code fragment.
  
Add token text file to your project as resource. Login function is bit different, now you don't need credentials but you will load token and authorize Sharpbox with that.
+
<code csharp>
 +
// call the authorization url via WebBrowser Plugin
 +
String AuthorizationUrl = DropBoxStorageProviderTools.GetDropBoxAuthorizationUrl(config, requestToken);
 +
</code>
  
<code cpp>
+
{{Note|For testing purpose you can put a breakpoint at {{Icode|AuthorizationUrl}} and manually browse to it to allow or deny access.}}
        public void ConnectCloud(string username, string password)
+
 
        {
+
====Exchange the request token into an access token====
            ICloudStorageAccessToken token = null;
+
The last phase converts the request token (which is only be usable during the authorization process) into an issued access token which can be stored on a local cache and has to be reused during login phase when the user comes back with your application. The following code performs the exchange process:
            StreamResourceInfo resource = Application.GetResourceStream(new Uri(@"/DropBoxImages;component/token.txt", UriKind.Relative));
+
 
            if (resource != null)
+
<code csharp>
            {
+
// create the access token
                token = m_dropBox.DeserializeSecurityToken(resource.Stream);
+
ICloudStorageAccessToken accessToken = DropBoxStorageProviderTools.ExchangeDropBoxRequestTokenIntoAccessToken(config, <<YOUR APP KEY>>, <<YOUR APP SECRET>>, requestToken);
                if (token != null)
+
</code>
                {
+
 
                    m_dropBox.BeginOpenRequest(LoginCallback, mCloudConfig, token);
+
====Saving access token for future use====
                }
+
The generated token can be stored in the ''IsolatedStorag''e for use in all future transactions to connect to Dropbox.
            }
+
<code csharp>
        }
+
private void SaveAccessTokenToIsolatedStorage(ICloudStorageAccessToken accessToken)
 +
{
 +
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
 +
{
 +
using (var stream = new IsolatedStorageFileStream(FileName, FileMode.Create, FileAccess.Write, store))
 +
{
 +
Stream accessTokenStream;
 +
accessTokenStream = cloudStorage.SerializeSecurityToken(accessToken);
 +
stream.Flush();
 +
byte[] accessTokenBytes;
 +
using (var streamReader = new MemoryStream())
 +
{
 +
accessTokenStream.CopyTo(streamReader);
 +
accessTokenBytes = streamReader.ToArray();
 +
stream.Write(accessTokenBytes, 0, accessTokenBytes.Length);
 +
}
 +
}
 +
}
 +
}
 +
</code>
 +
 
 +
{{Note|{{Icode|CloudStorage.Open(}}) must be called before attempting to serialize access token due to some issue with SharpBox library.}}
 +
 
 +
====Reading Access token from IsolatedStorage====
 +
 
 +
<code csharp>
 +
Use below snippet to read back access token from ''IsolatedStorage''.
 +
private ICloudStorageAccessToken LoadAccessTokenFromIsolatedStorage()
 +
{
 +
ICloudStorageAccessToken accessToken = null;
 +
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
 +
{
 +
using (var stream =
 +
  new IsolatedStorageFileStream(FileName, FileMode.OpenOrCreate, FileAccess.Read, store))
 +
{
 +
//using (var reader = new StreamReader(stream))
 +
{
 +
var cloudStorage = new CloudStorage();
 +
accessToken = (ICloudStorageAccessToken)cloudStorage.DeserializeSecurityToken(stream);
 +
}
 +
}
 +
}
 +
return accessToken;
 +
}
 
</code>
 
</code>
  
 
=== Login callback ===
 
=== Login callback ===
  
And the callback function for open request. Token of Dropbox connection is extracted from asynchronous functions result. This token also determines did connection succeed; if value is null, connection failed and in other values it succeeded. Without having token value, cannot continue because Sharpbox library does not know is service there.
+
This is the callback function for the open request. The Dropbox connection token is extracted from the asynchronous function's result. This token also determines whether the connection succeeded; if the value is null, connection failed and with other values it succeeded.  
  
<code cpp>
+
The token is required because without it the the SharpBox library does not know there is a service and will not continue.
        void LoginCallback(IAsyncResult result)
+
 
        {
+
<code csharp>
 +
void LoginCallback(IAsyncResult result)
 +
    {
 
             ICloudStorageAccessToken token = m_dropBox.EndOpenRequest(result);
 
             ICloudStorageAccessToken token = m_dropBox.EndOpenRequest(result);
 
             if (token != null)
 
             if (token != null)
Line 134: Line 178:
 
                 m_dropBox.BeginGetRootRequest(RootCallback);
 
                 m_dropBox.BeginGetRootRequest(RootCallback);
 
             }
 
             }
        }
+
    }
 
</code>
 
</code>
  
 
== Get Dropbox file listing ==
 
== Get Dropbox file listing ==
  
After successful connection, we can proceed to reading files from Dropbox. As seen in '''LoginCallback''', '''RootCallback''' function is callback for '''BeginGetRootRequest''' asynchronous function.Before reading any file from Dropbox, you need to know root where start. After having root, it's possible to read files from anywhere. So reading root folder is important only in the beginning. And from reading root callback, again jump to another async callback function.  
+
After successful connection, we can proceed to reading files from Dropbox. As seen in {{Icode|LoginCallback()}}, {{Icode|RootCallback()}} function is callback for {{Icode|BeginGetRootRequest()}} asynchronous function. Before reading any file from Dropbox, you need to know root where start. After having root, it's possible to read files from anywhere. So reading root folder is important only in the beginning. And from reading root callback, again jump to another async callback function.  
  
<code cpp>
+
<code csharp>
        private void RootCallback(IAsyncResult result)
+
private void RootCallback(IAsyncResult result)
        {
+
    {
            ICloudDirectoryEntry root = m_dropBox.EndGetRootRequest(result);
+
    ICloudDirectoryEntry root = m_dropBox.EndGetRootRequest(result);
 
             if (root != null)
 
             if (root != null)
 
             {
 
             {
 
                 m_dropBox.BeginGetChildsRequest(ChildCallback, root);
 
                 m_dropBox.BeginGetChildsRequest(ChildCallback, root);
 
             }
 
             }
        }
+
    }
 
</code>
 
</code>
  
After having child objects of root, any file or dir in topmost level, start parsing results.
+
After having child objects of root, any file or directory in topmost level, start parsing results.
  
<code cpp>
+
<code csharp>
        private void ChildCallback(IAsyncResult result)
+
private void ChildCallback(IAsyncResult result)
 +
    {
 +
 
 +
    List<ICloudFileSystemEntry> fs = m_dropBox.EndGetChildsRequest(result);
 +
    Deployment.Current.Dispatcher.BeginInvoke(() =>
 +
    {
 +
    if (fs != null)
 
         {
 
         {
 
+
        parseFilesAndDirectories(fs);
            List<ICloudFileSystemEntry> fs = m_dropBox.EndGetChildsRequest(result);
+
        while (m_directories.Count > 0)
            Deployment.Current.Dispatcher.BeginInvoke(() =>
+
 
             {
 
             {
                if (fs != null)
+
            ICloudDirectoryEntry e = m_directories[0];
                {
+
            m_directories.RemoveAt(0);
                    parseFilesAndDirectories(fs);
+
            m_dropBox.BeginGetChildsRequest(ChildCallback, e);
                    while (m_directories.Count > 0)
+
             }
                    {
+
                        ICloudDirectoryEntry e = m_directories[0];
+
                        m_directories.RemoveAt(0);
+
                        m_dropBox.BeginGetChildsRequest(ChildCallback, e);
+
                    }
+
                }
+
             });
+
 
         }
 
         }
 +
    });
 +
    }
 
</code>
 
</code>
  
 
And how to loop results list and populate file list. Example code looks only image files which are ending to JPG.
 
And how to loop results list and populate file list. Example code looks only image files which are ending to JPG.
  
<code cpp>
+
<code csharp>
        private void parseFilesAndDirectories(List<ICloudFileSystemEntry> direntry)
+
private void parseFilesAndDirectories(List<ICloudFileSystemEntry> direntry)
        {
+
    {
            foreach (ICloudFileSystemEntry entry in direntry)
+
    foreach (ICloudFileSystemEntry entry in direntry)
 
             {
 
             {
 
                 System.Diagnostics.Debug.WriteLine("entry: " + entry.Name);
 
                 System.Diagnostics.Debug.WriteLine("entry: " + entry.Name);
Line 192: Line 236:
 
                     Files.Add(new CloudItem() { Name = entry.Name, Entry = entry });
 
                     Files.Add(new CloudItem() { Name = entry.Name, Entry = entry });
 
                 }
 
                 }
            }
+
    }
        }
+
}
 
</code>
 
</code>
  
 
== Downloading files ==
 
== Downloading files ==
  
Although Sharpbox has synchronous function to get file system objects from Dropbox, those won't work in Windows phone because again, synchronous functions will block UI-thread and therefore whole application. To work things out, implement asynchronous wrapper to synchronous function. This wrapping code will run synchronous function in separate thread.
+
Although SharpBox has synchronous function to get file system objects from Dropbox, those won't work in Windows Phone because again, synchronous functions will block UI-thread and therefore whole application. To fix this problem, implement and asynchronous wrapper for the synchronous function. This wrapping code will run the synchronous function in separate thread.
  
Implement helper functions, '''GetFileUri''' is entry point to start work. This function constructs request and sets callback function to thread, what is sent to ThreadPool.
+
Implement helper functions, {{Icode|GetFileUri}} is entry point to start work. This function constructs request and sets callback function to thread, what is sent to {{Icode|ThreadPool}}.
  
<code cpp>
+
<code csharp>
        public void GetFileUri(AsyncCallback callback, ICloudFileSystemEntry entry)
+
public void GetFileUri(AsyncCallback callback, ICloudFileSystemEntry entry)
        {
+
    {
            BackgroundRequest request = new BackgroundRequest();
+
    BackgroundRequest request = new BackgroundRequest();
            request.callback = callback;
+
    request.callback = callback;
            request.result = new AsyncResultEx(request);
+
    request.result = new AsyncResultEx(request);
            request.fileEntry = entry;
+
    request.fileEntry = entry;
            ThreadPool.QueueUserWorkItem(GetFileUriCallback, request);
+
    ThreadPool.QueueUserWorkItem(GetFileUriCallback, request);
        }
+
    }
 
</code>
 
</code>
  
After awhile, callback function will be fired and we can try to get Dropbox file object URL. Note that this is not in your application (UI-thread) anymore. Use dispatcher if you need communicating to your application.
+
After a while, callback function will be fired and we can try to get Dropbox file object URL. Note that this is not in your application (UI-thread) anymore. Use dispatcher if you need to communicate the URL to your application.
  
<code cpp>
+
<code csharp>
        private void GetFileUriCallback(object state)
+
private void GetFileUriCallback(object state)
        {
+
    {
            BackgroundRequest req = state as BackgroundRequest;
+
    BackgroundRequest req = state as BackgroundRequest;
  
 
             try
 
             try
Line 231: Line 275:
 
             }
 
             }
  
            req.callback(req.result);
+
    req.callback(req.result);
        }
+
    }
 
</code>
 
</code>
  
Also end function is needed, this actually return result of thread.
+
Also a final function is needed to actually return result of thread:
 
+
<code csharp>
<code cpp>
+
public Uri EndGetFileUri(IAsyncResult result)
        public Uri EndGetFileUri(IAsyncResult result)
+
    {
        {
+
    BackgroundRequest req = result.AsyncState as BackgroundRequest;
            BackgroundRequest req = result.AsyncState as BackgroundRequest;
+
    return req.OperationResult as Uri;
            return req.OperationResult as Uri;
+
    }         
        }         
+
 
</code>
 
</code>
  
Now '''GetFileUri''' can be used to get file URL.
+
Now {{Icode|GetFileUri()}} can be used to get file URL.
  
<code cpp>
+
<code csharp>
        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
+
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
+
    {
 
             base.OnNavigatedTo(e);
 
             base.OnNavigatedTo(e);
 
             CloudHandler ch = Application.Current.Resources["CloudHandler"] as CloudHandler;
 
             CloudHandler ch = Application.Current.Resources["CloudHandler"] as CloudHandler;
Line 257: Line 300:
 
                 ch.GetFileUri(UrlCallback, ch.CurrentItem.Entry);
 
                 ch.GetFileUri(UrlCallback, ch.CurrentItem.Entry);
 
             }
 
             }
        }
+
    }
 
</code>
 
</code>
  
Almost there, still need one callback function. In here can be more handling to URL what you get. Example code gets URL and then downloads image file, later shows it.
+
Almost there! The last callback function can provide more handling to URL what you get. This example code gets the URL and then downloads image file, later shows it.
 
+
<code csharp>
<code cpp>
+
void UrlCallback(IAsyncResult result)
        void UrlCallback(IAsyncResult result)
+
    {
        {
+
 
             Uri fileUri = result as Uri;
 
             Uri fileUri = result as Uri;
  
Line 276: Line 318:
 
                 currentImage.Source = bi;
 
                 currentImage.Source = bi;
 
             });
 
             });
        }
+
    }
 
</code>
 
</code>
 
== Sample code ==
 
 
Sample code is using SharpBox 1.2. To try it out, create token and add it to project. Change token file name to token.txt or change code in '''Cloudhandler.cs''' to load it with different name. Build and run, click login.
 
For older Sharpbox releases, look '''ConnectCloud''' function in '''Cloudhandler.cs''', change commented code. Build and run, type your credentials and hit login.
 
  
 
=== Download sample code ===
 
=== Download sample code ===
  
[[File:DropBoxImages.zip]]
+
The attached code was create using SharpBox 1.2: [[File:DropBoxImages.zip]]
  
== Useful links ==
+
To try it out, firts build and run, then click login.
  
Dispatcher documentation: http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher%28v=vs.95%29.aspx
+
For older Sharpbox releases, look {{Icode|ConnectCloud}} function in '''Cloudhandler.cs''', change commented code. Also uncomment code to display text boxes for username and password. Send values of these fields to {{Icode|ConnectCloud}} function in '''Cloudhandler.cs'''. Build and run, type your credentials and hit login.
  
Sharpbox website: http://sharpbox.codeplex.com/
 
  
Dropbox developers: https://www.dropbox.com/developers
+
== Useful links ==
  
NuGet: http://visualstudiogallery.msdn.microsoft.com/27077b70-9dad-4c64-adcf-c7cf6bc9970c
+
* [http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher%28v=vs.95%29.aspx Dispatcher documentation] (MSDN)
 +
* [http://sharpbox.codeplex.com/ Sharpbox website]
 +
* [https://www.dropbox.com/developers Dropbox developer docs]
 +
* [http://visualstudiogallery.msdn.microsoft.com/27077b70-9dad-4c64-adcf-c7cf6bc9970c NuGet]

Latest revision as of 10:42, 29 January 2014

This article explains how to connect Windows Phone to DropBox cloud service using SharpBox

See Also
WP Metro Icon File.png
WP Metro Icon Web.png
SignpostIcon XAML 40.png
WP Metro Icon WP8.png
SignpostIcon WP7 70px.png
Article Metadata
Code ExampleCompatibility
Dependencies: Sharpbox 1.2
Article
Created: jupaavola (31 Aug 2012)
Updated: vinayppatil (24 Jan 2013)
Last edited: vinayppatil (29 Jan 2014)

Contents

[edit] Introduction

SharpBox is an open source project that provides access to Dropbox cloud services on Windows Phone. This article covers basic functionality of Sharpbox library: how to use it and how to wrap its synchronous functions to work with Windows Phone. This provides a useful complement or addition to Microsoft's SkyDrive cloud services.

Note.pngNote: SharpBox also provides access to other cloud services like CloudMe. However at time of writing this article these services work on Desktop only (not Windows Phone)

[edit] Using SharpBox

[edit] Getting SharpBox

SharpBox is available as a NuGet package or for download from codeplex here. If you download a Sharpbox release, extract the downloaded zip file. In Visual Studion add AppLimit.CloudComputing.SharpBox.dll and Newtonsoft.Json.Silverlight.dll files from sl3-wp folder as reference to your project.

Warning.pngWarning: The newer release (1.2) has few bugs when logging and authenticating to Dropbox service. Mainly, token exchanging uses synchronous functions which do not work in Windows Phone. Earlier versions have functions to log in with credentials, those will work only with older Dropbox v0 API. If you have Dropbox key & secret for older API, older library works just fine. But if you create Dropbox application now, you will get Dropbox v1 key and secret and those do not work with older API.

[edit] Prerequisites

SharpBox needs a valid Dropbox application key and secret (for release, not required during testing and developing).These can be obtained in the developer section of the Dropbox website.

[edit] SharpBox should not run in UI thread

The SharpBox library has both synchronous and and asynchronous functions for many operations. Both forms are useful for desktop usage, but the synchronous versions cannot be used directly in Windows Phone apps because these calls block the running UI thread and therefore the whole application.

The asynchronous functions can be used in the UI thread. If needed functions only exist in synchronous variants it is possible to run these in another (non UI) thread and return the results using a callback.

The following snippet shows how to use Dispatcher to call parseFilesAndDirectories function - a normal private function in application thread. Example function CallbackFunction() is asynchronous callback function.

void CallbackFunction(IAsyncResult result)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
if (fs != null)
{
parseFilesAndDirectories(fs);
}
});
}

[edit] Logging to Dropbox

This is pretty easy, create credentials and configuration object. Then call BeginOpenRequest() and wait for callback.

[edit] Dropbox API v0, Sharpbox 1.1 and older

public void ConnectCloud(string username, string password)
{
DropBoxCredentials creds = new DropBoxCredentials();
creds.ConsumerSecret = APP_SECRET;
creds.ConsumerKey = APP_KEY;
creds.UserName = username;
creds.Password = password;
 
m_dropBox.BeginOpenRequest(LoginCallback, mCloudConfig, creds);
}

[edit] Dropbox API v1, Sharpbox 1.2

From Sharpbox 1.2, usage of DropBoxTokenIssuer.exe has been deprecated. Instead the access token is generated in code.

[edit] Getting Security Token

[edit] Generate a request token

In the first phase a request token has to be generated based on the app key and app secret issued by Dropbox in your developer account interface. The interface will be available after registration under developer console.

Create an application if you haven't already.

 
//create a request token
DropBoxRequestToken requestToken = DropBoxStorageProviderTools.GetDropBoxRequestToken(config,
<<YOUR APP KEY>>,
<<YOUR APP SECRET>>);

[edit] Generate the authorization URL and visit it to allow access

In phase 2 the generated authorization URL has to be visited in a web browser control. The URL will be returned from SharpBox with the following code fragment.

// call the authorization url via WebBrowser Plugin
String AuthorizationUrl = DropBoxStorageProviderTools.GetDropBoxAuthorizationUrl(config, requestToken);

Note.pngNote: For testing purpose you can put a breakpoint at AuthorizationUrl and manually browse to it to allow or deny access.

[edit] Exchange the request token into an access token

The last phase converts the request token (which is only be usable during the authorization process) into an issued access token which can be stored on a local cache and has to be reused during login phase when the user comes back with your application. The following code performs the exchange process:

// create the access token
ICloudStorageAccessToken accessToken = DropBoxStorageProviderTools.ExchangeDropBoxRequestTokenIntoAccessToken(config, <<YOUR APP KEY>>, <<YOUR APP SECRET>>, requestToken);

[edit] Saving access token for future use

The generated token can be stored in the IsolatedStorage for use in all future transactions to connect to Dropbox.

private void SaveAccessTokenToIsolatedStorage(ICloudStorageAccessToken accessToken)
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var stream = new IsolatedStorageFileStream(FileName, FileMode.Create, FileAccess.Write, store))
{
Stream accessTokenStream;
accessTokenStream = cloudStorage.SerializeSecurityToken(accessToken);
stream.Flush();
byte[] accessTokenBytes;
using (var streamReader = new MemoryStream())
{
accessTokenStream.CopyTo(streamReader);
accessTokenBytes = streamReader.ToArray();
stream.Write(accessTokenBytes, 0, accessTokenBytes.Length);
}
}
}
}

Note.pngNote: CloudStorage.Open() must be called before attempting to serialize access token due to some issue with SharpBox library.

[edit] Reading Access token from IsolatedStorage

Use below snippet to read back access token from ''IsolatedStorage''.
private ICloudStorageAccessToken LoadAccessTokenFromIsolatedStorage()
{
ICloudStorageAccessToken accessToken = null;
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var stream =
new IsolatedStorageFileStream(FileName, FileMode.OpenOrCreate, FileAccess.Read, store))
{
//using (var reader = new StreamReader(stream))
{
var cloudStorage = new CloudStorage();
accessToken = (ICloudStorageAccessToken)cloudStorage.DeserializeSecurityToken(stream);
}
}
}
return accessToken;
}

[edit] Login callback

This is the callback function for the open request. The Dropbox connection token is extracted from the asynchronous function's result. This token also determines whether the connection succeeded; if the value is null, connection failed and with other values it succeeded.

The token is required because without it the the SharpBox library does not know there is a service and will not continue.

void LoginCallback(IAsyncResult result)
{
ICloudStorageAccessToken token = m_dropBox.EndOpenRequest(result);
if (token != null)
{
m_dropBox.BeginGetRootRequest(RootCallback);
}
else if (m_dropBox.IsOpened)
{
m_dropBox.BeginGetRootRequest(RootCallback);
}
}

[edit] Get Dropbox file listing

After successful connection, we can proceed to reading files from Dropbox. As seen in LoginCallback(), RootCallback() function is callback for BeginGetRootRequest() asynchronous function. Before reading any file from Dropbox, you need to know root where start. After having root, it's possible to read files from anywhere. So reading root folder is important only in the beginning. And from reading root callback, again jump to another async callback function.

private void RootCallback(IAsyncResult result)
{
ICloudDirectoryEntry root = m_dropBox.EndGetRootRequest(result);
if (root != null)
{
m_dropBox.BeginGetChildsRequest(ChildCallback, root);
}
}

After having child objects of root, any file or directory in topmost level, start parsing results.

private void ChildCallback(IAsyncResult result)
{
 
List<ICloudFileSystemEntry> fs = m_dropBox.EndGetChildsRequest(result);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
if (fs != null)
{
parseFilesAndDirectories(fs);
while (m_directories.Count > 0)
{
ICloudDirectoryEntry e = m_directories[0];
m_directories.RemoveAt(0);
m_dropBox.BeginGetChildsRequest(ChildCallback, e);
}
}
});
}

And how to loop results list and populate file list. Example code looks only image files which are ending to JPG.

private void parseFilesAndDirectories(List<ICloudFileSystemEntry> direntry)
{
foreach (ICloudFileSystemEntry entry in direntry)
{
System.Diagnostics.Debug.WriteLine("entry: " + entry.Name);
if (entry is ICloudDirectoryEntry)
{
m_directories.Add((ICloudDirectoryEntry)entry);
}
 
if (entry is ICloudFileSystemEntry && entry.Name.EndsWith(".jpg", StringComparison.CurrentCultureIgnoreCase))
{
Files.Add(new CloudItem() { Name = entry.Name, Entry = entry });
}
}
}

[edit] Downloading files

Although SharpBox has synchronous function to get file system objects from Dropbox, those won't work in Windows Phone because again, synchronous functions will block UI-thread and therefore whole application. To fix this problem, implement and asynchronous wrapper for the synchronous function. This wrapping code will run the synchronous function in separate thread.

Implement helper functions, GetFileUri is entry point to start work. This function constructs request and sets callback function to thread, what is sent to ThreadPool.

public void GetFileUri(AsyncCallback callback, ICloudFileSystemEntry entry)
{
BackgroundRequest request = new BackgroundRequest();
request.callback = callback;
request.result = new AsyncResultEx(request);
request.fileEntry = entry;
ThreadPool.QueueUserWorkItem(GetFileUriCallback, request);
}

After a while, callback function will be fired and we can try to get Dropbox file object URL. Note that this is not in your application (UI-thread) anymore. Use dispatcher if you need to communicate the URL to your application.

private void GetFileUriCallback(object state)
{
BackgroundRequest req = state as BackgroundRequest;
 
try
{
req.OperationResult = m_dropBox.GetFileSystemObjectUrl(req.fileEntry.Name, req.fileEntry.Parent);
}
catch (Exception e)
{
var openRequest = req.result.AsyncState as BackgroundRequest;
openRequest.OperationResult = null;
openRequest.errorReason = e;
}
 
req.callback(req.result);
}

Also a final function is needed to actually return result of thread:

public Uri EndGetFileUri(IAsyncResult result)
{
BackgroundRequest req = result.AsyncState as BackgroundRequest;
return req.OperationResult as Uri;
}

Now GetFileUri() can be used to get file URL.

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
CloudHandler ch = Application.Current.Resources["CloudHandler"] as CloudHandler;
if (ch != null)
{
imageProgress.IsIndeterminate = true;
ch.GetFileUri(UrlCallback, ch.CurrentItem.Entry);
}
}

Almost there! The last callback function can provide more handling to URL what you get. This example code gets the URL and then downloads image file, later shows it.

void UrlCallback(IAsyncResult result)
{
Uri fileUri = result as Uri;
 
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
CloudHandler ch = Application.Current.Resources["CloudHandler"] as CloudHandler;
fileUri = ch.EndGetFileUri(result);
BitmapImage bi = new BitmapImage();
bi.DownloadProgress += new EventHandler<DownloadProgressEventArgs>(bi_DownloadProgress);
bi.UriSource = fileUri;
currentImage.Source = bi;
});
}

[edit] Download sample code

The attached code was create using SharpBox 1.2: File:DropBoxImages.zip

To try it out, firts build and run, then click login.

For older Sharpbox releases, look ConnectCloud function in Cloudhandler.cs, change commented code. Also uncomment code to display text boxes for username and password. Send values of these fields to ConnectCloud function in Cloudhandler.cs. Build and run, type your credentials and hit login.


[edit] Useful links

This page was last modified on 29 January 2014, at 10:42.
942 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×