0

This is a Xamarin app. I edit a photo and try to save it. With some images it saves just fine. Other images the screen goes black and the debugger quits.

I found this in the logs:

12-13 19:35:42.682 E/JavaBinder(31213): !!! FAILED BINDER TRANSACTION !!!  (parcel size = 1112628)
Thread finished: <Thread Pool> #2
The thread 0x2 has exited with code 0 (0x0).
Thread finished: <Thread Pool> #3
The thread 0x3 has exited with code 0 (0x0).
Thread started: <Thread Pool> #8
[0:] Java.Lang.RuntimeException: Failure from system ---> Android.OS.TransactionTooLargeException: data parcel size 1112628 bytes
   --- End of inner exception stack trace ---

Maybe some of the files are just over some file size limit. Here's the relevent code:

var pickerIntent = new Intent (this._context, typeof (SavePickerActivity));
pickerIntent.SetFlags (ActivityFlags.NewTask);
pickerIntent.PutExtra("fileName", fileName);
pickerIntent.PutExtra("fileBytes", fileBytes    
this._context.StartActivity (pickerIntent); // <-- CRASH!

Maybe I should feed it a stream instead of a byte array?

user875234
  • 2,399
  • 7
  • 31
  • 49

1 Answers1

0

I'm not saying this is THE answer but I'm not sure if there is one.

Anyways:

1. WRITE THE FILE TO THE CACHE DIR

private async Task<string> WriteFileToCacheDirectory(string fileName, byte[] fileBytes)
{
    File outputDir = this._context.CacheDir;
    File file = new File(outputDir, fileName);

    FileOutputStream fos = new FileOutputStream(file.AbsolutePath);
    await fos.WriteAsync(fileBytes);
    fos.Flush();
    fos.Close();
    return file.AbsolutePath;
}

2. DO THE INTENT

var pathToFile = await WriteFileToCacheDirectory(fileName, fileBytes);

var pickerIntent = new Intent(this._context, typeof(SavePickerActivity));
pickerIntent.SetFlags(ActivityFlags.NewTask);
pickerIntent.PutExtra("fileName", fileName);
pickerIntent.PutExtra("path", pathToFile);

this._context.StartActivity(pickerIntent);
...

3. THE ACTIVITY STARTS (IN MY CASE IT'S MY SavePickerActivity.cs)

[Activity (ConfigurationChanges = Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize)]
[Preserve (AllMembers = true)]
public class SavePickerActivity : Activity
{
    string fileName;
    string filePath;

    protected override void OnCreate (Bundle savedInstanceState)
    {
        base.OnCreate (savedInstanceState);

        var fileNameWithExtension = this.Intent.GetStringExtra("fileName");
        var fileExtension = Path.GetExtension(fileNameWithExtension);
        var mimeType = Android.Webkit.MimeTypeMap.Singleton.GetMimeTypeFromExtension(fileExtension.Substring(1));

        fileName = fileNameWithExtension.Replace(fileExtension, "");
        filePath = Intent.GetStringExtra("path");

        var intent = new Intent(Intent.ActionCreateDocument);
        intent.AddCategory(Intent.CategoryOpenable);
        intent.SetType(mimeType);
        intent.PutExtra(Intent.ExtraTitle, fileNameWithExtension);

        // https://stackoverflow.com/questions/9080109/android-image-picker-for-local-files-only
        // https://stackoverflow.com/questions/50980397/when-using-intent-actionopendocumenttree-can-you-allow-the-user-to-save-in-a-loc/51011346?noredirect=1#comment89016456_51011346
        // https://stackoverflow.com/questions/51012702/whats-the-correct-way-to-save-a-file-when-using-the-content-sheme/51013020?noredirect=1#comment89020123_51013020 // note that I said it worked here. i said it worked prematurely because i never got it to work completely (even after wasting too much time trying to write a MCW). 
        // when saving remotely it saves the file with 0 bytes. so only let it save locally.
        intent.PutExtra(Intent.ExtraLocalOnly, true);

        StartActivityForResult(Intent.CreateChooser(intent, "Select Save Location"), 43);
    }
...

4. OnActivityResult READ THE FILE OUT OF THE CACHE DIR AND SAVE IT

protected async override void OnActivityResult (int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
    base.OnActivityResult (requestCode, resultCode, data);

    if (resultCode == Result.Canceled && requestCode == 43) {
        // Notify user file picking was cancelled.
        OnDirectoryPickCancelled ();
        Finish ();
    } else {
        System.Diagnostics.Debug.Write (data.Data);
        try {
            // read the file out of the cache dir
            var file = new Java.IO.File(filePath);
            var fileLength = (int)file.Length();
            byte[] fileBytes = new byte[fileLength];
            using (var inputStream = new FileInputStream(filePath))
            {
                await inputStream.ReadAsync(fileBytes, 0, fileLength);
            }

            // write the file to the path chosen by the user
            using (var pFD = ContentResolver.OpenFileDescriptor(data.Data, "w"))
            using (var outputSteam = new FileOutputStream(pFD.FileDescriptor))
            {
                outputSteam.Write(fileBytes);
            }
            ...  
user875234
  • 2,399
  • 7
  • 31
  • 49