/// <summary> /// File picking implementation /// </summary> /// <param name="allowedTypes">list of allowed types; may be null</param> /// <param name="action">Android intent action to use; unused</param> /// <returns>picked file data, or null when picking was cancelled</returns> private Task <FileData> PickFileAsync(string[] allowedTypes, string action) { var id = this.GetRequestId(); var ntcs = new TaskCompletionSource <FileData>(id); if (Interlocked.CompareExchange(ref this.completionSource, ntcs, null) != null) { throw new InvalidOperationException("Only one operation can be active at a time"); } try { var pickerIntent = new Intent(this.context, typeof(FilePickerActivity)); pickerIntent.SetFlags(ActivityFlags.NewTask); pickerIntent.PutExtra(FilePickerActivity.ExtraAllowedTypes, allowedTypes); this.context.StartActivity(pickerIntent); EventHandler <FilePickerEventArgs> handler = null; EventHandler <FilePickerCancelledEventArgs> cancelledHandler = null; handler = (s, e) => { var tcs = Interlocked.Exchange(ref this.completionSource, null); FilePickerActivity.FilePickCancelled -= cancelledHandler; FilePickerActivity.FilePicked -= handler; tcs?.SetResult(new FileData( e.FilePath, e.FileName, () => { if (IOUtil.IsMediaStore(e.FilePath)) { var contentUri = Android.Net.Uri.Parse(e.FilePath); return(Application.Context.ContentResolver.OpenInputStream(contentUri)); } else { return(System.IO.File.OpenRead(e.FilePath)); } })); }; cancelledHandler = (s, e) => { var tcs = Interlocked.Exchange(ref this.completionSource, null); FilePickerActivity.FilePickCancelled -= cancelledHandler; FilePickerActivity.FilePicked -= handler; if (e?.Exception != null) { tcs?.SetException(e.Exception); } else { tcs?.SetResult(null); } }; FilePickerActivity.FilePickCancelled += cancelledHandler; FilePickerActivity.FilePicked += handler; } catch (Exception ex) { Debug.Write(ex); this.completionSource.SetException(ex); } return(this.completionSource.Task); }
/// <summary> /// Called when activity started with StartActivityForResult() returns. /// </summary> /// <param name="requestCode">request code used in StartActivityForResult()</param> /// <param name="resultCode">result code</param> /// <param name="data">intent data from file picking</param> protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data) { base.OnActivityResult(requestCode, resultCode, data); if (resultCode == Result.Canceled) { // Notify user file picking was cancelled. OnFilePickCancelled(); this.Finish(); } else { try { if (data?.Data == null) { throw new Exception("File picking returned no valid data"); } System.Diagnostics.Debug.Write(data.Data); var uri = data.Data; if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat) { context.ContentResolver.TakePersistableUriPermission( uri, ActivityFlags.GrantReadUriPermission); } // The scoped storage feature was introduced on Android Q and it prevents direct file access on external storage. // Thus, using the IOUtil.GetPath should be avoided because a System.UnauthorizedAccessException // will be thrown when calling File.OpenRead() on the local path. // For more information, see: https://developer.android.com/training/data-storage/#scoped-storage var filePath = (int)Build.VERSION.SdkInt >= 29 ? uri.ToString() : IOUtil.GetPath(this.context, uri); if (string.IsNullOrEmpty(filePath)) { filePath = IOUtil.IsMediaStore(uri.Scheme) ? uri.ToString() : uri.Path; } var fileName = this.GetFileName(this.context, uri); OnFilePicked(new FilePickerEventArgs(fileName, filePath)); } catch (Exception readEx) { System.Diagnostics.Debug.Write(readEx); // Notify user file picking failed. FilePickCancelled?.Invoke( this, new FilePickerCancelledEventArgs { Exception = readEx }); } finally { this.Finish(); } } }
//private Task<FileData> TakeMediaAsync (string type, string action) private Task <FileData> TakeMediaAsync(string[] allowedTypes, string action) { if (this._context.PackageManager.CheckPermission( Android.Manifest.Permission.ReadExternalStorage, this._context.PackageName) == Android.Content.PM.Permission.Denied) { throw new InvalidOperationException("Android permission READ_EXTERNAL_STORAGE is missing or was denied by user"); } var id = GetRequestId(); var ntcs = new TaskCompletionSource <FileData> (id); if (Interlocked.CompareExchange(ref _completionSource, ntcs, null) != null) { throw new InvalidOperationException("Only one operation can be active at a time"); } try { var pickerIntent = new Intent(this._context, typeof(FilePickerActivity)); pickerIntent.SetFlags(ActivityFlags.NewTask); pickerIntent.PutExtra(nameof(allowedTypes), allowedTypes); this._context.StartActivity(pickerIntent); EventHandler <FilePickerEventArgs> handler = null; EventHandler <FilePickerCancelledEventArgs> cancelledHandler = null; handler = (s, e) => { var tcs = Interlocked.Exchange(ref _completionSource, null); FilePickerActivity.FilePickCancelled -= cancelledHandler; FilePickerActivity.FilePicked -= handler; tcs?.SetResult(new FileData(e.FilePath, e.FileName, () => { if (IOUtil.isMediaStore(e.FilePath)) { var contentUri = Android.Net.Uri.Parse(e.FilePath); return(Application.Context.ContentResolver.OpenInputStream(contentUri)); } else { return(System.IO.File.OpenRead(e.FilePath)); } })); }; cancelledHandler = (s, e) => { var tcs = Interlocked.Exchange(ref _completionSource, null); FilePickerActivity.FilePickCancelled -= cancelledHandler; FilePickerActivity.FilePicked -= handler; if (e?.Exception != null) { tcs?.SetException(e.Exception); } else { tcs?.SetResult(null); } }; FilePickerActivity.FilePickCancelled += cancelledHandler; FilePickerActivity.FilePicked += handler; } catch (Exception exAct) { Debug.Write(exAct); _completionSource.SetException(exAct); } return(_completionSource.Task); }