Skip to content

SimonSimCity/Xamarin-CrossDownloadManager

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CrossDownloadManager

The CrossDownloadManager is a plugin that helps you downloading files in the background.

Build Status:

GitHub tag NuGet

Where can I use it?

Platform Supported Version
Xamarin.iOS Yes iOS 7+
Xamarin.iOS Unified Yes iOS 7+
Xamarin.Android Yes API 16+
Windows 10 UWP Yes 10.0.10240.0
Xamarin.Mac No

Getting started

Add the nuget package to your cross-platform project and to every platform specific project. Now, you have to initialize the service for every platform. You also need to write some logic, which determines where the file will be saved.

iOS

AppDelegate.cs

/**
 * Save the completion-handler we get when the app opens from the background.
 * This method informs iOS that the app has finished all internal processing and can sleep again.
 */
public override void HandleEventsForBackgroundUrl(UIApplication application, string sessionIdentifier, Action completionHandler)
{
    CrossDownloadManager.BackgroundSessionCompletionHandler = completionHandler;
}

As of iOS 9, your URL must be secured or you have to add the domain to the list of exceptions. See https://developer.apple.com/library/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS9.html#//apple_ref/doc/uid/TP40016198-SW14

Start downloading

You can now start a download by adding the following code:

var downloadManager = CrossDownloadManager.Current;
var file = downloadManager.CreateDownloadFile(url);
downloadManager.Start(file);

This will add the file to a native library, which starts the download of that file. You can watch the properties of the IDownloadFile instance and execute some code if e.g. the status changes to COMPLETED, you can also watch the IDownloadManager.Queue and execute some code if the list of files, that will be downloaded or are currently downloading changes.

After a download has been completed, the instance of IDownloadFile is then removed from IDownloadManager.Queue.

You can also disallow downloading via a cellular network by setting the second parameter of CrossDownloadManager.Current.Start().

Where are the files stored?

Default Option - Temporary Location

When you choose not to provide your own path before starting the download, the downloaded files are stored at a temporary directory and may be removed by the OS e.g. when the system runs out of space. You can move this file to a decided destination by listening on whether the status of the files changes to DownloadFileStatus.COMPLETED. You can find an implementation in the sample: #27

Recommended Option - Custom Location

Usually, you would expect to set the path to the IDownloadFile instance, you get when calling downloadManager.CreateDownloadFile(url). But, as this download manager even continues downloading when the app crashed, you have to be able to reconstruct the path in every stage of the app. The correct way is to register a method as early as possible, that, in every circumstance, can reconstruct the path that the file should be saved. This method could look like following:

CrossDownloadManager.Current.PathNameForDownloadedFile = new System.Func<IDownloadFile, string> (file => {
#if __IOS__
    string fileName = (new NSUrl(file.Url, false)).LastPathComponent;
    return Path.Combine(Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments), fileName);
#elif __ANDROID__
    string fileName = Android.Net.Uri.Parse(file.Url).Path.Split('/').Last();
    return Path.Combine (ApplicationContext.GetExternalFilesDir (Android.OS.Environment.DirectoryDownloads).AbsolutePath, fileName);
#else
    string fileName = '';
    return Path.Combine(Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments), fileName);
#endif
});

Warning: Please check if the files exist. The CrossDownloadManager will overwrite the file if it already exists.

Additional for Andriod

On Android, the destination location must be a located outside of your Apps internal directory (see #10 for details). To allow your app to write to that location, you either have to add the permission WRITE_EXTERNAL_STORAGE to the mainfest.xml file to require it when installing the app

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

or to request it at runtime (See #20).

All finished downloads are registered in a native Downloads application. If you want your finished download not to be listed there, see #17

I want to use $FAVORITE_IOC_LIBRARY

Just register the instance in CrossDownloadManager.Current in the library. Here's an example how to do it on MvvmCross:

Mvx.RegisterSingleton<IDownloadManager>(() => CrossDownloadManager.Current);

Can I have a look at a sample implementation?

I've created a quite basic implementation for UWP, iOS and Android which you can find here. Feel free to fork this repository and play around with it. It's right within the Sample folder.

Why do the files show up in the native Android Download Manager?

This is the default of the native download manager on Android. You can change this behavior by setting the property IsVisibleInDownloadsUi of the Android implementation of the download manager to false. Here's an example which you can copy into your Android application:

(CrossDownloadManager.Current as DownloadManagerImplementation).IsVisibleInDownloadsUi = false;

Contribute / Bugs / Features

Everything you tell me is contributing to this project and helps me improving it. If you found a bug or want a feature, please file an issue to let me know. For bugs, please include as much information as you know. If you can, please fork this repository, reproduce the bug in the sample projects and include a link in the issue. For features, feel free to start developing them. I'm always willing to contribute, help and give advice.

Licensing

This plugin is licensed under the MIT License

Contributors

Changes

Moved the changelog to CHANGELOG.md

Known issues

Exception java.lang.IllegalArgumentException: Unknown URL content://downloads/my_downloads on Android when calling one of IDownloadManager.Start (downloadFile), IDownloadManager.Abort (downloadFile) or IDownloadManager.AbortAll (downloadFile)

This might occur because the user has disabled the system app Download Manager. You can kindly inform him using a message box after which you delegate him to the settings where he can enable it again. You can use the code-snippet I added in the sample (credits to dashika #82).

try {
foo.StartDownloading(FindViewById<Switch>(Resource.Id.switch1).Checked);
} catch (Java.Lang.IllegalArgumentException) {
foo.File = null;
button.Text = "Download crashed.";
try {
//Open the specific App Info page:
Intent intent = new Intent(Android.Provider.Settings.ActionApplicationDetailsSettings);
intent.SetData(Android.Net.Uri.Parse("package:com.android.providers.downloads"));
StartActivity(intent);
} catch (ActivityNotFoundException) {
//Open the generic Apps page:
Intent intent = new Intent(Android.Provider.Settings.ActionManageApplicationsSettings);
StartActivity(intent);
}