I’m having difficulties with runtime permissions check for writeExternalStorage. When I try to ask for this permission I run into jassertfalse in void RuntimePermissions::request (PermissionID permission, Callback callback) saying that I need to declare this permission also in manifest.
The problem is that it is in manifest already there:
Which version of Android are you targeting? Note that for apps targeting SDK 29+, this permission doesn’t do anything:
Instead, you can show a native ‘save’ file dialog to allow the user to select a directory where your app may write. Then, you can use juce::AndroidDocument to write into the selected location.
Hi @reuk! Thank you on your swift response.
I’m running my app on Android 11 (SDK version 30) so your problem description makes sense but how can I make it work if I want to cover Android versions that require this permission and those that don’t?
Should I check for SDK version and if it is lower than 29 ask for permission and if it is higher then don’t?
Thanks @reuk! Well if I have that boilerplate code for runtime permission check, I get that assertion I mentioned above so some sdk version check is needed I think Do you know maybe how to do that?
With this code I managed to write and read from external memory but could you check if you maybe see some problems in it:
Function for writing (here is SDK check needed I think):
void EQ::_saveEqValsToFile() {
if (_fileChooser != nullptr)
return;
// Here need to check what is current SDK version because this runtime permission
// check can be done only for Android SDK versions 28 and lower
/*
if (!RuntimePermissions::isGranted(RuntimePermissions::writeExternalStorage))
{
SafePointer<EQ> safeThis(this);
RuntimePermissions::request(RuntimePermissions::writeExternalStorage,
[safeThis](bool granted) mutable
{
if (safeThis != nullptr && granted)
safeThis->_saveEqValsToFile();
});
return;
}
*/
_fileChooser.reset(new FileChooser("Select a location or a file to save", File(), "*.mycoolformat"));
_fileChooser->launchAsync(FileBrowserComponent::saveMode | FileBrowserComponent::canSelectFiles | FileBrowserComponent::warnAboutOverwriting,
[this](const FileChooser& fc) mutable {
if (fc.getURLResults().size() > 0) {
auto my_output_file = makeOutputStream(fc.getURLResult());
std::string my_text = "Writing to files on Android makes me so happy.";
my_output_file->writeString(my_text);
}
_fileChooser = nullptr;
}, nullptr);
}
Read function:
void EQ::_loadEqValsFromFile() {
if (_fileChooser != nullptr)
return;
if (!RuntimePermissions::isGranted(RuntimePermissions::readExternalStorage))
{
SafePointer<EQ> safeThis(this);
RuntimePermissions::request(RuntimePermissions::readExternalStorage,
[safeThis](bool granted) mutable
{
if (safeThis != nullptr && granted)
safeThis->_loadEqValsFromFile();
});
return;
}
_fileChooser.reset(new FileChooser("Select a file to read...", File(), "*.mycoolformat"));
_fileChooser->launchAsync(FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles,
[this](const FileChooser& fc) mutable {
if (fc.getURLResults().size() > 0) {
auto my_input_file = makeInputSource(fc.getURLResult());
auto my_input_stream = my_input_file->createInputStream();
std::string my_text = my_input_stream->readString().toStdString();
}
_fileChooser = nullptr;
}, nullptr);
}
Mine is a simple modification to juce_android_RuntimePermissions.cpp. To the method RuntimePermissions::isRequired (PermissionID permission) I have added :
if (permission == RuntimePermissions::writeExternalStorage && getAndroidSDKVersion() > 28)
return false;
Now I can check first if (RuntimePermissions::isRequired (RuntimePermissions::writeExternalStorage)) before actually requesting permissions.
Since several people asked me regarding this issue I’m posting here an example function for writing to files (although I removed some parts of the code that were strictly coupled to my use-case, I think function should work):
And a short side-note: JUCE is a nice framework and JUCE apps run fine on desktop PCs, but there are serious issues when it comes to i.e. Android support. JUCE is not 100% cross platform as advertised, at least when it comes to Android, so some workarounds are often needed and some new and purely Android features are not directly supported like i.e. advanced notifications or controls so just keep that in mind when developing a JUCE app for Android