Reputation: 945
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
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