ContentSharer no success with valid path

Hi,

I am using the ContentSharer class, to share .flac files. I am using the same implementation on iOS and Android. On iOS everything works as expected, on Android though every sharing option fails, once I selected the share target and Android prompts me Error - Something went wrong! Please try again later. At the same time the ContentSharer callback comes back with bool success == false.

This happens on a Google Pixel 3XL and a Samsung SM-T510 Tablet, as it does in the device simulators. The path I’m providing to ContentSharer::shareFiles() is valid as well. I use the same File object to load the .flac file into my player on Android, where it works as expected. ContentSharer also prompted me for permissions to access Storage and I allowed it.

Anything obvious I’m missing?

This is my solution for sharing a file on Android:

     Array<URL> files{ "file://" + ShareFilePath };
     ContentSharer::getInstance()->shareFiles(files, [&](bool sucess, const String& error) {});

ShareFilePath is the full path to the file to share, something like /data/data/com.yourcompany.yuorapp/content/yourfile.whatever

The trick is that the URL must contain the prefix “file://” or it won’t be found.

Hey @ZioGuido,

thanks for replying.

This is basically what I do. I’m using the juce::URL class though, which takes care of the file:// prefix.

This is my function:

        void shareFile(const File& path)
        {
            Array<URL> files;

            if (!path.existsAsFile())
            {
                DBG("ContentSharer Path invalid: " << path.getFullPathName());
                return;
            }
            else
            {
                DBG("ContentSharer Path valid: " <<path.getFullPathName());
            }

            files.add(URL(path));

            DBG("ContentSharer calling with URL " <<  files[0].toString(true));

            if (contentSharer)
                contentSharer->shareFiles(files, contentSharerCallback);
            else
                DBG("ContentSharer N/A");
        }

And this is the Debug Output that’s coming from these prints:

> 2020-02-18 09:37:03.393 20912-20912/com.myCompany.myApp I/JUCE: ContentSharer Starting share...
> 2020-02-18 09:37:03.397 20912-20912/com.myCompany.myApp I/JUCE: ContentSharer Path valid: /data/user/0/com.myCompany.myApp/My Company/My App/Recordings/My Recording - 16 Feb 2020 13:33.flac
> 2020-02-18 09:37:03.397 20912-20912/com.myCompany.myApp I/JUCE: ContentSharer calling with URL file:///data/user/0/com.myCompany.myApp/My%20Company/My%20App/Recordings/My%20Recording%20-%2016%20Feb%202020%2013%3A33.flac

Could the spaces or symbols in the file path be a problem here? Thing is, these folder names/file names are user generated, so they can definitely include spaces and all that stuff… but URL shouldn’t have a problem with that…

//Edit: just tried sharing a path without any special symbols or spaces in the pathname… no success either.

Also as mentioned in my original post, the same function successfully works on iOS… so I’m pretty sure that I’m handling it right, but it still fails on all my Android devices and simulators…

I think I’ve found a problem:

2020-02-19 12:32:32.886 30018-30047/com.myCompany.myApp E/DatabaseUtils: Writing exception to parcel
java.lang.SecurityException: Permission Denial: reading com.roli.juce.JuceSharingContentProvider uri content://com.myCompany.myApp.sharingcontentprovider/0/testrec.flac from pid=29170, uid=1000 requires the provider be exported, or grantUriPermission()
    at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:729)
    at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:602)
    at android.content.ContentProvider$Transport.query(ContentProvider.java:231)
    at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:104)
    at android.os.Binder.execTransactInternal(Binder.java:1021)
    at android.os.Binder.execTransact(Binder.java:994)

So, this:
java.lang.SecurityException: Permission Denial: reading com.roli.juce.JuceSharingContentProvider
looks like a problem with Juce, doesn’t it? Or is this grantUriPermission() something I have to do? Maybe @ed95 knows something here? (I hope it’s ok to tag you when suspecting a problem with juce, if not let me know and apologies)

Have you looked at the content sharing code in the DialogsDemo project? Does that exhibit the same problem?

Just had a look at it.

            Array<URL> urls;
            urls.add ({ fileToSave.getFullPathName() });

            ContentSharer::getInstance()->shareFiles (urls,
                [] (bool success, const String& error)
                {
                    auto resultString = success ? String ("success") : ("failure\n (error: " + error + ")");

                    AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
                                                      "Sharing Files Result",
                                                      "Sharing files finished\nwith " + resultString);
                });

This is exactly what I’m doing in my code, see the code example in the post 3 posts above this one. The only difference is that I use a static callback function instead of a lambda, which works fine on iOS and also triggers on Android, just comes back with success == false

Should I still test the demo on my devices? I would have to generate a keystore for it first and probably create a new Projucer project including the keystore and the DialogsDemo, before I can build it to my devices, seems a little unneccessary given that I do the exact same thing in my code and my code is working on iOS, right? I mean say the word and I’ll jump and make it happen :smiley:

Have you got permission to read external storage? You’ll need to set the “Read from External Storage” setting in the Projucer’s Android exporter and then add the RuntimePermissions call from the demo:

RuntimePermissions::request (RuntimePermissions::readExternalStorage,
                             [] (bool granted)
                             {
                                 if (! granted)
                                 {
                                     AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
                                                                       "Permissions warning",
                                                                       "External storage access permission not granted, some files"
                                                                       " may be inaccessible.");
                                 }
                             });

Yes, I have both enabled it in Projucer and I get the RuntimePermissions, which I obviously granted when it asked.

What about this error I’m getting in Logcat though, couldn’t that be related? Reads like it to me:
java.lang.SecurityException: Permission Denial: reading com.roli.juce.JuceSharingContentProvider
(The whole thing is 4 posts above)

I’ve tested this on a Google Pixel 3a device and needed this change to get things working, but it looks like you’re already doing that in your code example. I would recommend trying out the demo project on your device to see if that’s working. You don’t need to generate a keystore or create a new project, you can just build the DemoRunner Android project from the JUCE repo and debug it on your devices.

It’s also worth noting that the docs for ContentSharer::shareFiles() state that:

Upon completion you will receive a callback with a sharing result. Note:
Sadly on Android the returned success flag may be wrong as there is no
standard way the sharing targets report if the sharing operation
succeeded. Also, the optional error message is always empty on Android.

Could this be related to your issue? Have you checked that the files are actually being shared correctly but the flag is wrong?

Oh, I’m actually using the URL() constructor instead of just passing in the fullPathName into some curly brackets. (I was under the impression that those both basically do the same thing, don’t they?)

I wasn’t aware of the whole success flag situation on Android, but that’s fine, nothing was depending on this. The thing is that this is happening before the callback itself happens:

This happens on a facebook messenger share, same message on a Gmail/FacebookApp/WhatsApp share…

I’ll try your suggestions passing in the fullPathName in curly brackets and if that doesn’t work I’ll try the demo a bit later today. Thanks for your help!!

No, the change above is doing the opposite of that. Previously it was constructing a URL from fileToSave.getFullPathName() which was calling the String constructor of URL and not correctly adding the file:// scheme. Using the explicit URL constructor (as you are doing in your example) is the correct thing to do.

dammit, confused red and green (again) :smiley:

so yeah, I’m doing that. I’ll test the demo later then. Thank you