/// <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(); } }