/// <summary>
        /// Tries the move file asynchronous.
        /// </summary>
        /// <param name="context">The execution context.</param>
        /// <param name="url">The original file to move.</param>
        /// <param name="path">The file destination.</param>
        /// <param name="isPhoto">A value indicating whenter the operation is performed on a photo.</param>
        /// <returns>A <see cref="Task"/> represening the asyncroneous operation.</returns>
        private static Task <bool> TryMoveFileAsync(Context context, Uri url, Uri path, bool isPhoto)
        {
            var moveTo = MediaPickerActivity.GetLocalPath(path);

            return(MediaPickerActivity.GetFileForUriAsync(context, url, isPhoto).ContinueWith(
                       t =>
            {
                // If nothing to move
                if (t.Result.Item1 == null)
                {
                    return false;
                }

                // Move the file to required destination
                File.Delete(moveTo);
                File.Move(t.Result.Item1, moveTo);

                // If the schema is content
                if (url.Scheme == "content")
                {
                    // Delete content file
                    context.ContentResolver.Delete(url, null, null);
                }

                return true;
            },
                       TaskScheduler.Default));
        }
        /// <summary>
        /// Touches this instance.
        /// </summary>
        private void Touch()
        {
            if (this.path.Scheme != "file")
            {
                return;
            }

            File.Create(MediaPickerActivity.GetLocalPath(this.path)).Close();
        }
        /// <summary>
        /// Called when an activity you launched exits, giving you the requestCode you started it with, the
        /// resultCode it returned, and any additional data from it.
        /// </summary>
        /// <param name="requestCode">
        ///   The integer request code originally supplied to startActivityForResult(), allowing you to identify who
        ///   this result came from.
        /// </param>
        /// <param name="resultCode">
        ///   The integer result code returned by the child activity through its setResult().
        /// </param>
        /// <param name="data">
        ///   An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
        /// </param>
        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            base.OnActivityResult(requestCode, resultCode, data);

            // If the operation was tasked
            if (this.tasked)
            {
                // Get the media from task result
                var future = resultCode == Result.Canceled
                                 ? MediaPickerActivity.TaskFromResult(new MediaPickedEventArgs(requestCode, true))
                                 : MediaPickerActivity.GetMediaFileAsync(
                    this,
                    requestCode,
                    this.action,
                    this.isPhoto,
                    ref this.path,
                    data?.Data);

                // Finish the current activity
                this.Finish();
                future.ContinueWith(t => MediaPickerActivity.RaiseOnMediaPicked(t.Result));
            }
            else
            {
                // If the task was canceled
                if (resultCode == Result.Canceled)
                {
                    this.SetResult(Result.Canceled);
                }
                else
                {
                    // Return the media resule
                    var resultData = new Intent();
                    resultData.PutExtra(MediaPickerActivity.MediaFileExtraName, data?.Data);
                    resultData.PutExtra(MediaPickerActivity.ExtraPath, this.path);
                    resultData.PutExtra("isPhoto", this.isPhoto);
                    resultData.PutExtra(MediaPickerActivity.ExtraAction, this.action);

                    this.SetResult(Result.Ok, resultData);
                }

                // Finish the current activity
                this.Finish();
            }
        }
        /// <summary>
        /// Gets the output media file.
        /// </summary>
        /// <param name="context">The execution context.</param>
        /// <param name="subdir">The sub-directory name to place file.</param>
        /// <param name="name">The file name.</param>
        /// <param name="isPhoto">A value indicating whenter the operation is performed on a photo.</param>
        /// <returns>The Uri of the output media file.</returns>
        private static Uri GetOutputMediaFile(Context context, string subdir, string name, bool isPhoto)
        {
            // Get the media file name
            subdir = subdir ?? string.Empty;
            if (string.IsNullOrWhiteSpace(name))
            {
                name = MediaPickerActivity.GetMediaFileWithPath(isPhoto, subdir, string.Empty, name);
            }

            var mediaType = isPhoto ? Environment.DirectoryPictures : Environment.DirectoryMovies;

            using (var mediaStorageDir = new Java.IO.File(context.GetExternalFilesDir(mediaType), subdir))
            {
                // If directory does not exists
                if (!mediaStorageDir.Exists())
                {
                    // If create directory failed
                    if (!mediaStorageDir.Mkdirs())
                    {
                        throw new IOException(
                                  "Couldn't create directory, have you added the WRITE_EXTERNAL_STORAGE permission?");
                    }

                    // Ensure this media doesn't show up in gallery apps
                    using (var nomedia = new Java.IO.File(mediaStorageDir, ".nomedia"))
                    {
                        nomedia.CreateNewFile();
                    }
                }

                // Return the media file URI
                return
                    (Uri.FromFile(
                         new Java.IO.File(
                             MediaPickerActivity.GetUniqueMediaFileWithPath(
                                 isPhoto,
                                 mediaStorageDir.Path,
                                 name,
                                 File.Exists))));
            }
        }
        /// <summary>
        /// Gets the file for URI asynchronous.
        /// </summary>
        /// <param name="context">The execution context.</param>
        /// <param name="uri">The file URI.</param>
        /// <param name="isPhoto">A value indicating whenter the operation is performed on a photo.</param>
        /// <returns>A <see cref="Task"/> represening the asyncroneous operation.</returns>
        // ReSharper disable once UnusedParameter.Local
        private static Task <Tuple <string, bool> > GetFileForUriAsync(Context context, Uri uri, bool isPhoto)
        {
            var tcs = new TaskCompletionSource <Tuple <string, bool> >();

            // Fix the content URI
            var fixedUri = MediaPickerActivity.FixUri(uri.Path);

            if (fixedUri != null)
            {
                uri = fixedUri;
            }

            if (uri.Scheme == "file")
            {
                // Return the media file
                tcs.SetResult(new Tuple <string, bool>(new System.Uri(uri.ToString()).LocalPath, false));
            }
            else if (uri.Scheme == "content")
            {
                // Return the content file
                Task.Factory.StartNew(
                    () =>
                {
                    ICursor cursor = null;
                    try
                    {
                        // Get the content from resolver
                        cursor = context.ContentResolver.Query(uri, null, null, null, null);
                        if ((cursor == null) || !cursor.MoveToNext())
                        {
                            // No data returned
                            tcs.SetResult(new Tuple <string, bool>(null, false));
                        }
                        else
                        {
                            // Parse the content data
                            int column         = cursor.GetColumnIndex(MediaStore.MediaColumns.Data);
                            string contentPath = null;
                            if (column != -1)
                            {
                                contentPath = cursor.GetString(column);
                            }

                            tcs.SetResult(new Tuple <string, bool>(contentPath, false));
                        }
                    }
                    finally
                    {
                        if (cursor != null)
                        {
                            cursor.Close();
                            cursor.Dispose();
                        }
                    }
                },
                    CancellationToken.None,
                    TaskCreationOptions.None,
                    TaskScheduler.Default);
            }
            else
            {
                // Not a file not content
                tcs.SetResult(new Tuple <string, bool>(null, false));
            }

            return(tcs.Task);
        }
        /// <summary>
        /// Gets the media file asynchronous.
        /// </summary>
        /// <param name="context">The execution context.</param>
        /// <param name="requestCode">The request operation code.</param>
        /// <param name="action">The action to perform.</param>
        /// <param name="isPhoto">A value indicating whenter the operation is performed on a photo.</param>
        /// <param name="path">The media path.</param>
        /// <param name="data">The media data URI.</param>
        /// <returns>A <see cref="Task"/> represening the asyncroneous operation.</returns>
        private static Task <MediaPickedEventArgs> GetMediaFileAsync(
            Context context,
            int requestCode,
            string action,
            bool isPhoto,
            ref Uri path,
            Uri data)
        {
            // If requested to take a photo
            Task <Tuple <string, bool> > pathFuture;
            Action <bool> dispose      = null;
            string        originalPath = null;

            if (action != Intent.ActionPick)
            {
                originalPath = path.Path;

                // Not all camera apps respect EXTRA_OUTPUT, some will instead return a content or file uri from data.
                if ((data != null) && (data.Path != originalPath))
                {
                    // Move the camera output
                    originalPath = data.ToString();
                    var currentPath = path.Path;
                    pathFuture =
                        MediaPickerActivity.TryMoveFileAsync(context, data, path, isPhoto)
                        .ContinueWith(t => new Tuple <string, bool>(t.Result ? currentPath : null, false));
                }
                else
                {
                    pathFuture = MediaPickerActivity.TaskFromResult(new Tuple <string, bool>(path.Path, false));
                }
            }
            else if (data != null)
            {
                // Pick the file from  media galery
                originalPath = data.ToString();
                path         = data;
                pathFuture   = MediaPickerActivity.GetFileForUriAsync(context, path, isPhoto);
            }
            else
            {
                // Nothing to perform
                pathFuture = MediaPickerActivity.TaskFromResult <Tuple <string, bool> >(null);
            }

            return(pathFuture.ContinueWith(
                       t =>
            {
                // If selected file exists
                var resultPath = t.Result.Item1;
                if ((resultPath != null) && File.Exists(t.Result.Item1))
                {
                    // Return the selected media
                    if (t.Result.Item2)
                    {
                        dispose = d => File.Delete(resultPath);
                    }

                    var mf = new MediaFile(resultPath, () => File.OpenRead(t.Result.Item1), dispose);
                    return new MediaPickedEventArgs(requestCode, false, mf);
                }

                // Return an error
                return new MediaPickedEventArgs(
                    requestCode,
                    new FileNotFoundException("Media file not found", originalPath));
            }));
        }
        /// <summary>
        /// Called when the activity is starting.
        /// </summary>
        /// <param name="savedInstanceState">
        ///   If the activity is being re-initialized after previously being shut down then this Bundle contains
        ///   the data it most recently supplied in <see cref="OnSaveInstanceState"/>. Otherwise it is <c>null</c>.
        /// </param>
        protected override void OnCreate(Bundle savedInstanceState)
        {
            // Call the base member
            base.OnCreate(savedInstanceState);

            // Take the state from the saved instance or an intent
            var b   = savedInstanceState ?? this.Intent.Extras;
            var ran = b.GetBoolean("ran", false);

            this.title       = b.GetString(MediaStore.MediaColumns.Title);
            this.description = b.GetString(MediaStore.Images.ImageColumns.Description);
            this.tasked      = b.GetBoolean(MediaPickerActivity.ExtraTasked);
            this.id          = b.GetInt(MediaPickerActivity.ExtraId, 0);
            this.type        = b.GetString(MediaPickerActivity.ExtraType);

            // Parse media type
            if (this.type == "image/*")
            {
                this.isPhoto = true;
            }

            // Get the requested action
            this.action = b.GetString(MediaPickerActivity.ExtraAction);
            Intent pickIntent = null;

            try
            {
                // Create the media pick intent
                pickIntent = new Intent(this.action);
                if (this.action == Intent.ActionPick)
                {
                    pickIntent.SetType(this.type);
                }
                else
                {
                    // Setup the video properties for video
                    pickIntent.PutExtra(MediaStore.ExtraVideoQuality, 1);
                    if (!this.isPhoto)
                    {
                        this.durationLimit = b.GetInt(MediaStore.ExtraDurationLimit, 0);
                        if (this.durationLimit != 0)
                        {
                            pickIntent.PutExtra(MediaStore.ExtraDurationLimit, this.durationLimit);
                        }
                    }

                    // If the activity was not run
                    if (!ran)
                    {
                        this.path = MediaPickerActivity.GetOutputMediaFile(
                            this,
                            b.GetString(MediaPickerActivity.ExtraPath),
                            this.title,
                            this.isPhoto);
                        this.Touch();
                        pickIntent.PutExtra(MediaStore.ExtraOutput, this.path);
                    }
                    else
                    {
                        // Get the media location
                        this.path = Uri.Parse(b.GetString(MediaPickerActivity.ExtraPath));
                    }
                }

                // If the activity was not run before for the current operation
                if (!ran)
                {
                    // Check camera permission
                    if (global::Android.OS.Build.VERSION.Release == "6.0")
                    {
                        if (this.CheckSelfPermission(Manifest.Permission.Camera)
                            != Android.Content.PM.Permission.Granted)
                        {
                            this.RequestPermissions(new[] { Manifest.Permission.Camera }, 1);
                        }
                    }

                    // Start the media operation
                    this.StartActivityForResult(pickIntent, this.id);
                }
            }
            catch (Exception ex)
            {
                // Handle exception
                MediaPickerActivity.RaiseOnMediaPicked(new MediaPickedEventArgs(this.id, ex));
            }
            finally
            {
                // Cleanup
                pickIntent?.Dispose();
            }
        }