3

I want to store a file with the Android storage access framework. My test code should store a file "hallo1" with the size of 1000000. But only a file "hallo1" with the filesize 0 will be created. I get no error message. The saving on local disc and sd-card works fine only google drive does not work. The Code:

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

        // Set our view from the "main" layout resource
        SetContentView(Resource.Layout.Main);

        // Get our button from the layout resource,
        // and attach an event to it
        Button button = FindViewById<Button>(Resource.Id.myButton);

        button.Click += delegate
        {
            Intent intentCreate = new Intent(Intent.ActionCreateDocument);
            intentCreate.AddCategory(Intent.CategoryOpenable);
            intentCreate.SetType("audio/*");
            intentCreate.PutExtra(Intent.ExtraTitle, "hallo" + count);
            StartActivityForResult(intentCreate, 1);

            count++;
        };
    }

    protected override  void OnActivityResult(int requestCode, Result resultCode, Intent data)
    {
        var stream = this.ContentResolver.OpenOutputStream(data.Data, "w");
        var b = new byte[1000000];
        stream.Write(b, 0, b.Length);
        stream.Flush();
        stream.Close();

        base.OnActivityResult(requestCode, resultCode, data);
    }

Filepicker from the android storage access framework

Enter the filename (default "hallo1" from the code)

data.Data gives an internal Android.Net.Uri back.

Somebody an idea where is the problem?

urs32
  • 61
  • 7

2 Answers2

3

I found a solution:

        protected override  void OnActivityResult(int requestCode, Result resultCode, Intent data)
    {

        // this works fine:
        using (System.IO.Stream stream = this.ContentResolver.OpenOutputStream(data.Data, "w"))
        {
            using (var javaStream = new Java.IO.BufferedOutputStream(stream))
            {
                var b = new byte[1000000];
                javaStream.Write(b, 0, b.Length);
                javaStream.Flush();
                javaStream.Close();
            }
        }

        // the direct writing on the System.IO.Stream failed:
        //using (System.IO.Stream stream = this.ContentResolver.OpenOutputStream(data.Data, "w"))
        //{
        //    var b = new byte[1000000];
        //    stream.Write(b, 0, b.Length);
        //    stream.Flush();
        //    stream.Close();
        //}

        base.OnActivityResult(requestCode, resultCode, data);
    }

Somebody a idea why this works and the direct writing with the System.IO.Stream failed?

urs32
  • 61
  • 7
1

Store file with the storage access framework on Google Drive

Please refer to: Google Drive API implementation Xamarin Android

Other useful links:

Update:

With internal storage it works and in my opinion it should also work with google drive.

You can't directly implement this feature without using Google Drive API, you should create your file first, then upload the file to Google Drive like this:

void IResultCallback.OnResult(Java.Lang.Object result)
{
    var contentResults = (result).JavaCast<IDriveApiDriveContentsResult>();
    if (!contentResults.Status.IsSuccess) // handle the error
        return;
    Task.Run(() =>
    {
        var writer = new OutputStreamWriter(contentResults.DriveContents.OutputStream);
        writer.Write("Stack Overflow");
        writer.Close();
        MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
            .SetTitle("New Text File")
            .SetMimeType("text/plain")
            .Build();
        DriveClass.DriveApi
            .GetRootFolder(_googleApiClient)
            .CreateFile(_googleApiClient, changeSet, contentResults.DriveContents);
    });
}

For more detailed information, refer to the link I post above: Google Drive API implementation Xamarin Android.


Also, you could use the Xamarin.Google.Drive.Api.Android nuget package to implement this feature easily, simple demo from the document:

CloudRail.AppKey = "{Your_License_Key}";
// Google Drive:
GoogleDrive drive = new GoogleDrive(this, "[clientIdentifier]", "", "[redirectUri]", "[state]");
drive.UseAdvancedAuthentication();
ICloudStorage cs = drive;

new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
    try
    {
        IList<CloudMetaData> filesFolders = cs.GetChildren("/");
        //IList<CloudMetaData> filesFolders = cs.GetChildrenPage("/", 1, 4);  // Path, Offet, Limit
        //cs.Upload(/image_2.jpg,stream,1024,true);   // Path and Filename, Stream (data), Size, overwrite (true/false)
    }
    catch (Exception e)
    {
        System.Console.WriteLine(e.Message);
    }
 })).Start();

Update 2:

To get the filename I found but not the folder name.

You need get Cursor from the URI first, then you can check column names etc.

For Google drive, there is a column name _display_name. This will give you the file name.

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
    var uri = data.Data;
    ICursor cursor = this.ContentResolver.Query(uri, null, null, null, null);
    cursor.MoveToFirst();

    var filenameIndex = cursor.GetColumnIndex("_display_name");
    var filename = cursor.GetString(filenameIndex);
    System.Diagnostics.Debug.WriteLine("filename == " + filename);
}
York Shen
  • 9,014
  • 1
  • 16
  • 40
  • It should also work with the storage access framework. This whould be a good way to implement easy a filepicker. The result is data.Data, a Android.Net.Uri. It is a internal path. With internal storage it works and in my opinion it should also work with google drive. – urs32 Feb 20 '18 at 11:41
  • @urs32, you still need to use the Google Drive API if you want to upload a file to Google Drive. – York Shen Feb 20 '18 at 11:51
  • @YorgShen: If this would be the only way i can not use this framework at all to save files. Because if one function does not work i can not release it. The result uri is only internal {content://com.google.android.apps.docs.storage/document/acc%3D2%3Bdoc%3D148} ond i can not get a path form this. Only to get the filename and size i found a possibility. – urs32 Feb 20 '18 at 12:12
  • @YorgShen: Thank you. But i have no idea how i can get the google drive folder from the encrypted uri (content://com.google.android.apps.docs.storage/document/acc%3D2%3Bdoc%3D148). It should give mit "/testordner/hallo1". To get the filname i found but not the foldername. – urs32 Feb 20 '18 at 16:10
  • @YorgShen: Yes, i now the code from your Update 2. But i need also the foldername, not only the filename. – urs32 Feb 22 '18 at 20:52