yan yankelevich
yan yankelevich

Reputation: 945

How to await OnActivityResult in Xamarin C# on Android

I'm using MvvmCross on Android via Xamarin and i got a problem. I created a core service interface called IFileExplorerService.

This interface goal is to open a open file dialog and select a file whatever the device is (Windows or android).

On android i managed easly to do this without my interface via view by just using the OnActivityResult and Intents.

But i just cannot make it work from my view, as the interface implementation is a singleton registered in the setup, when i call it from my viewmodel there is absolutely no one to handle the OnActivityResult.

I tried to make my FileExplorer implmentation to inherit from Activity or MvxActivity, for this way my FileExplorer could override OnActivityResult, but this fails.

I tried to use StartActivity(typeof(FileExplorer)) but this obviously would fails anyway as it wouldn't be the singleton registered in MvvmCross that would be started.

Does anyone has any idea/comment/question/suggestion to help me on this?

Here is my code so far :

public class FileExplorerService : IFileExplorerServiceMock
    {
        string _addedFileName;
        public static int REQUEST_FILE_CAPTURE = 2;
        private bool _hasMediaBeenAdded;
        private TaskCompletionSource<String> GetFileTask;
        public Activity activity;
        public FileExplorerService()
        {
            GetFileTask = new TaskCompletionSource<string>();
        }


        private void DispatchSelectFileIntent()
        {
            Intent Intent = new Intent();
            Intent.SetType("*/*");
            Intent.SetAction(Intent.ActionGetContent);

            activity.StartActivityForResult(Intent.CreateChooser(Intent, "Select File"), REQUEST_FILE_CAPTURE);
        }

        public  void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
        {
           //
            if (resultCode == Result.Ok)
            {
                if (data != null)
                {

                    bool isFileCopied = CopySelectedFileToAddedFilesDirectory(data.Data);
                    if (isFileCopied)
                    {
                        //everything went well
                        ShowSuccesAlertDialog();
                        GetFileTask.SetResult(_addedFileName);
                    }
                    else
                    {
                        ShowFailureAlertDialog();
                        //oops something crashed
                        //Log
                    }
                }
            }
            else
            {
                _addedFileName = String.Empty;
            }            
        }

        private void ShowFailureAlertDialog()
        {
            AlertDialog.Builder builder = new AlertDialog.Builder(activity);
            AlertDialog alertDialog = builder.Create();
            alertDialog.SetTitle("Oops... something went wrong");
            alertDialog.SetIcon(Android.Resource.Drawable.IcDialogAlert);
            alertDialog.SetMessage("Something went rong when adding a media");
            alertDialog.SetButton("Ok", (s, ev) =>
            {

            });
            alertDialog.Show();
        }

        private void ShowSuccesAlertDialog()
        {
            AlertDialog.Builder builder = new AlertDialog.Builder(activity);
            AlertDialog alertDialog = builder.Create();
            alertDialog.SetTitle("Media added with succes !");
            alertDialog.SetIcon(Android.Resource.Drawable.IcDialogAlert);
            alertDialog.SetMessage("Your media has been added  with succes");
            alertDialog.SetButton("Ok", (s, ev) =>
            {
                _hasMediaBeenAdded = true;


            });
            alertDialog.Show();
        }
        private void DeletePreviousAddedFile()
        {
            //todo delete file only if selected rex type is the same
            if (_hasMediaBeenAdded)
            {
                MsFile.Delete(_addedFileName);
                _addedFileName = string.Empty;
                _hasMediaBeenAdded = false;
            }
        }

        private static string GetRealPathFromURI(Context _context, Android.Net.Uri contentUri)
        {
            string[] projection = new string[] { MediaStore.MediaColumns.Data };
            ContentResolver cr = _context.ContentResolver;
            Android.Database.ICursor cursor = cr.Query(contentUri, projection, null, null, null);
            if (cursor != null && cursor.Count > 0)
            {
                cursor.MoveToFirst();
                int index = cursor.GetColumnIndex(Android.Provider.MediaStore.MediaColumns.Data);
                return cursor.GetString(index);
            }
            return "";
        }
        private bool CopySelectedFileToAddedFilesDirectory(Android.Net.Uri data)
        {
            var dir = new Java.IO.File(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath + "/Medias/AddedMedias/Files/");

            if (!dir.Exists())
                dir.Mkdirs();
            string path = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath + "/Medias/AddedMedias/Files/";
            JavaFile file = new Java.IO.File(path);

            string realPath = GetRealPathFromURI(activity.ApplicationContext, data);
            string FileName = realPath.Split('/').Last();
            _addedFileName = Path.Combine(dir + "/" + FileName);
            if (!string.IsNullOrEmpty(realPath))
            {
                //todo manage errors
                using (FileStream fs = new FileStream(realPath, FileMode.Open))
                {
                    byte[] datas = new byte[fs.Length];
                    int numberBytesToRead = (int)fs.Length;
                    int numBytesRead = 0;
                    while (numberBytesToRead > 0)
                    {
                        int n = fs.Read(datas, numBytesRead, numberBytesToRead);

                        if (n == 0)
                        {
                            break;
                        }
                        numBytesRead += n;
                        numberBytesToRead -= n;
                    }
                    using (FileStream fs2 = System.IO.File.OpenWrite(Path.Combine(dir + "/" + FileName)))
                    {
                        fs2.Write(datas, 0, datas.Length);
                    }
                }
                return true;
            }
            return false;
        }


        public List<FileType> FileTypes
        {
            get
            {
                return new List<FileType>();
            }
        }

        public async Task<string> Show(string fiel)
        {
            if (activity != null)
            {
                DeletePreviousAddedFile();
                DispatchSelectFileIntent();

                return GetFileTask.Task.Result;
            }
            else
            {
                return String.Empty;
            }

}

And in my view :

protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            SetContentView(Resource.Layout.RexView);
            _addMediaController = new AddMediaController(this, (RexViewModel)base.ViewModel);
            _flyoutMenuAnimator = new FlyoutMenuAnimator(this);

           var t= Mvx.Resolve<IFileExplorerServiceMock>() as FileExplorerService;
            t.activity = this;

        }
 protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            var t = Mvx.Resolve<IFileExplorerServiceMock>() as FileExplorerService;
            t.OnActivityResult(requestCode, resultCode, data);
            base.OnActivityResult(requestCode, resultCode, data);
        }

With this everything work except that OnActivityResult is never called in my view

Upvotes: 2

Views: 4670

Answers (1)

yan yankelevich
yan yankelevich

Reputation: 945

Okay i solved it ! Thanks to this link https://forums.xamarin.com/discussion/45691/pass-data-from-android-project-to-pcl

I had to rewrite start activity to give it a callback as an argument, the callback being my OnActivityResult in my file Explorer.

Here is my the code for anyone needing it : in MyView:

private Action<int, Result, Intent> _resultCallback;

        public void StartActivity(Intent intent,int resultCode, Action<int, Result, Intent> resultCallback)
        {
            _resultCallback = resultCallback;

            StartActivityForResult(intent, resultCode);
        }

        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            base.OnActivityResult(requestCode, resultCode, data);
            if (_resultCallback != null)
            {
                _resultCallback(requestCode, resultCode, data);
                _resultCallback = null;
            }
        }

and in my file Explorer :

 string _addedFileName;
        public static int REQUEST_FILE_CAPTURE = 2;
        private bool _hasMediaBeenAdded;
        private TaskCompletionSource<String> GetFileTask;
        public RexView activity;
        public FileExplorerService()
        {
        }


        private void DispatchSelectFileIntent()
        {
            Intent intent = new Intent();
            intent.SetType("*/*");
            intent.SetAction(Intent.ActionGetContent);

           GetFileTask = new TaskCompletionSource<string>();
            activity.StartActivity(Intent.CreateChooser(intent, "Select File"), REQUEST_FILE_CAPTURE, OnActivityResult);
        //  activity.StartActivityForResult(Intent.CreateChooser(intent, "Select File"), REQUEST_FILE_CAPTURE);
        }

        public  void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
        {

            //
            if (resultCode == Result.Ok)
            {
                if (data != null)
                {

                    bool isFileCopied = CopySelectedFileToAddedFilesDirectory(data.Data);
                    if (isFileCopied)
                    {
                        //everything went well
                        ShowSuccesAlertDialog();
                        GetFileTask.SetResult(_addedFileName);
                    }
                    else
                    {
                        ShowFailureAlertDialog();
                        //oops something crashed
                        //Log
                    }
                }
            }
            else
            {
                _addedFileName = String.Empty;
            }            
        }

        private void ShowFailureAlertDialog()
        {
            AlertDialog.Builder builder = new AlertDialog.Builder(activity);
            AlertDialog alertDialog = builder.Create();
            alertDialog.SetTitle("Oops... something went wrong");
            alertDialog.SetIcon(Android.Resource.Drawable.IcDialogAlert);
            alertDialog.SetMessage("Something went rong when adding a media");
            alertDialog.SetButton("Ok", (s, ev) =>
            {

            });
            alertDialog.Show();
        }

        private void ShowSuccesAlertDialog()
        {
            AlertDialog.Builder builder = new AlertDialog.Builder(activity);
            AlertDialog alertDialog = builder.Create();
            alertDialog.SetTitle("Media added with succes !");
            alertDialog.SetIcon(Android.Resource.Drawable.IcDialogAlert);
            alertDialog.SetMessage("Your media has been added with succes");
            alertDialog.SetButton("Ok", (s, ev) =>
            {
                _hasMediaBeenAdded = true;


            });
            alertDialog.Show();
        }
        private void DeletePreviousAddedFile()
        {
            //todo delete file only if selected rex type is the same
            if (_hasMediaBeenAdded)
            {
                MsFile.Delete(_addedFileName);
                _addedFileName = string.Empty;
                _hasMediaBeenAdded = false;
            }
        }

        private static string GetRealPathFromURI(Context _context, Android.Net.Uri contentUri)
        {
            string[] projection = new string[] { MediaStore.MediaColumns.Data };
            ContentResolver cr = _context.ContentResolver;
            Android.Database.ICursor cursor = cr.Query(contentUri, projection, null, null, null);
            if (cursor != null && cursor.Count > 0)
            {
                cursor.MoveToFirst();
                int index = cursor.GetColumnIndex(Android.Provider.MediaStore.MediaColumns.Data);
                return cursor.GetString(index);
            }
            return "";
        }
        private bool CopySelectedFileToAddedFilesDirectory(Android.Net.Uri data)
        {
            var dir = new Java.IO.File(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath + "/Medias/AddedMedias/Files/");

            if (!dir.Exists())
                dir.Mkdirs();
            string path = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath + "/Medias/AddedMedias/Files/";
            JavaFile file = new Java.IO.File(path);

            string realPath = GetRealPathFromURI(activity.ApplicationContext, data);
            string FileName = realPath.Split('/').Last();
            _addedFileName = Path.Combine(dir + "/" + FileName);
            if (!string.IsNullOrEmpty(realPath))
            {
                //todo manage errors
                using (FileStream fs = new FileStream(realPath, FileMode.Open))
                {
                    byte[] datas = new byte[fs.Length];
                    int numberBytesToRead = (int)fs.Length;
                    int numBytesRead = 0;
                    while (numberBytesToRead > 0)
                    {
                        int n = fs.Read(datas, numBytesRead, numberBytesToRead);

                        if (n == 0)
                        {
                            break;
                        }
                        numBytesRead += n;
                        numberBytesToRead -= n;
                    }
                    using (FileStream fs2 = System.IO.File.OpenWrite(Path.Combine(dir + "/" + FileName)))
                    {
                        fs2.Write(datas, 0, datas.Length);
                    }
                }
                return true;
            }
            return false;
        }


        public List<FileType> FileTypes
        {
            get
            {
                return new List<FileType>();
            }
        }

        public async Task<string> Show(string fiel)
        {
            if (activity != null)
            {
                DeletePreviousAddedFile();
                DispatchSelectFileIntent();

                return await GetFileTask.Task;
            }
            else
            {
                return String.Empty;
            }
        }
    }

And in my view model

private async void BrowseMedias()
        {
            var fileExplorerService = Mvx.Resolve<IFileExplorerServiceMock>();
            fileExplorerService.FileTypes.Add(FileType.Picture);
            string fileSavedPath = await fileExplorerService.Show(DatabaseContentPath);
           /* file managment*/
        }

Upvotes: 5

Related Questions