[TIMOB-26370] Android: "Failed to create external storage directory" when download image and save to camera roll
GitHub Issue | n/a |
---|---|
Type | Bug |
Priority | Critical |
Status | Closed |
Resolution | Fixed |
Resolution Date | 2018-11-19T23:17:29.000+0000 |
Affected Version/s | n/a |
Fix Version/s | Release 8.0.0 |
Components | n/a |
Labels | android, api, camera, roll, save |
Reporter | Ken Rucker |
Assignee | Yordan Banev |
Created | 2018-09-05T20:22:16.000+0000 |
Updated | 2018-11-26T20:10:27.000+0000 |
Description
When downloading any image, saving to temp folder and then attempting to save the image to the Android Camera Roll, results in error (saving to Camera Roll) "TiMedia: (main) [526999,526999] Failed to create external storage directory". Works as expected on Android API levels 25 and below. Fails on API levels 26, 27, 28. Tested on emulators API 25-28 and devices API 21 and API 28.
*Sample Test Code:*
var checkCameraPermissions = function(callback) {
if (Ti.Media.hasCameraPermissions()) {
Ti.UI.createAlertDialog({ cancel: 0, buttonNames: ['OK'], title:'Permissions', message:'Already had permissions. Good to go.'}).show();
callback(true);
} else {
Ti.Media.requestCameraPermissions(function(e) {
if (e.success === true) {
Ti.UI.createAlertDialog({ cancel: 0, buttonNames: ['OK'], title:'Permissions', message:'User granted permissions. Good to go.'}).show();
callback(true);
} else {
Ti.UI.createAlertDialog({ cancel: 0, buttonNames: ['OK'], title:'Permissions', message:'User did not granted permissions. Bad day.'}).show();
callback(false);
}
});
}
};
var win = Ti.UI.createWindow({
exitOnClose: true,
fullscreen: false,
title: 'Test'
});
var button = Titanium.UI.createButton({
title: 'Test DL Img & Save to Camera Roll'
});
button.addEventListener('click', function(e) {
checkCameraPermissions(function(returnedData) {
if (returnedData == true) {
var xhrImageGet = Ti.Network.createHTTPClient({
onerror : function(xhrErr) {
Ti.UI.createAlertDialog({ cancel: 0, buttonNames: ['OK'], title:'Error', message:'Unable to download the remote image. ' + xhrErr.error}).show();
},
onload : function() {
try {
var image_to_save = this.responseData;
var picture = Titanium.Filesystem.getFile(Titanium.Filesystem.tempDirectory, 'icon-arrowFeature.png');
picture.write(image_to_save);
Ti.Media.saveToPhotoGallery(picture.read(), {
success:function(){
Ti.UI.createAlertDialog({ cancel: 0, buttonNames: ['OK'], title:'Image Saved', message:'icon-arrowFeature.png has been saved to your photo gallery.'}).show();
},
error:function(err){
Ti.UI.createAlertDialog({ cancel: 0, buttonNames: ['OK'], title:'Error Saving to Gallery', message:err.error}).show();
// FAILS RIGHT HERE WITH NO ERROR MESSAGE RETURNED. HOWEVER CONSOLE SHOWS THE ERROR TiMedia: (main) [526999,526999] Failed to create external storage directory.
}
});
image_to_save = null;
}
catch(saveErr) {
Ti.UI.createAlertDialog({ cancel: 0, buttonNames: ['OK'], title:'Error', message:'Unable to save the file to your device. ' + saveErr.error}).show();
}
}
});
xhrImageGet.open("GET", 'https://s3.amazonaws.com/www.appcelerator.com.images/icon-arrowFeature.png');
xhrImageGet.send();
} else {
Ti.UI.createAlertDialog({ cancel: 0, buttonNames: ['OK'], title:'Permission Not Granted', message:'You have not granted the app the needed permissions to access your camera and/or device storage.'}).show();
}
});
});
win.add(button);
win.open();
*Test App tiapp.xml:*
<?xml version="1.0" encoding="UTF-8"?>
<ti:app xmlns:ti="http://ti.appcelerator.org">
<id>com.walkthelot.testsavetogallery</id>
<name>TestSaveToGallery</name>
<version>1.0</version>
<publisher>Ken</publisher>
<url/>
<description>undefined</description>
<copyright>2018 by Ken</copyright>
<icon>appicon.png</icon>
<fullscreen>false</fullscreen>
<navbar-hidden>false</navbar-hidden>
<analytics>true</analytics>
<guid>a17321c0-c4e2-4542-9222-e3f5731f8fb5</guid>
<property name="ti.ui.defaultunit" type="string">dp</property>
<property name="run-on-main-thread" type="bool">true</property>
<android xmlns:android="http://schemas.android.com/apk/res/android">
<manifest android:installLocation="auto">
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="26"/>
</manifest>
</android>
<modules/>
<deployment-targets>
<target device="android">true</target>
<target device="ipad">false</target>
<target device="iphone">false</target>
</deployment-targets>
<sdk-version>7.3.1.GA</sdk-version>
</ti:app>
I've dug and dug and can not find anything that I may be missing.
Attachments
File | Date | Size |
---|---|---|
Screenshot_20180906-165427.jpg | 2018-09-06T10:55:12.000+0000 | 79182 |
Hello, Tested this issue and able to reproduce on SDK 7.3.0.GA, android 8(Huawei y9 2018) device. *Test Environemnt:*
*Test code:* Sample code provided above *Test Steps:* 1. Create a classic project . 2. Paste the sample code and run. 3. Click on *Test DL Img & Save to Camera Roll* button. Checking the permissions. And receive error “*Error Saving to Gallery*" by the app. Attached the screenshot. *Console logs:*
Same problem here! Tested on LG and Huawei with android 8 and not working, on LG with android 6 no problem
Any news? This bug is HUGE!
I think this may be due to not providing WRITE_EXTERNAL_STORAGE permission at runtime. I will give the code a go and get back here when I have some results.
[~ybanev] you are right. Requesting the runtime permission:
works on Android 8. Without that I'll get the error described above
Yeah while searching I was trying and seems to work
Here is the reason why this problem occurred: https://developer.android.com/about/versions/oreo/android-8.0-changes#rmp What happens is that on Android API levels below 26 the WRITE_EXTERNAL_STORAGE was automatically granted if the user grants the READ_EXTERNAL_STORAGE permission since they are in the same permission group. The latter is requested by calling the
Ti.Media.requestCameraPermissions
method. They fixed that behavior in Android Oreo (see the link above). The workaround provided by [~michael] is the best way to go for now.I can't update the Wiki (permission denied), but the examples there should be updated to the latest permissions too: https://docs.appcelerator.com/platform/latest/#!/guide/Camera_and_Photo_Gallery_APIs
Agree with Michael The documentation should mention that for iOS you need to add these permissions in tiapp.xml:
and the showCamera() method could be adapted to something like this:
... or some cleaner code ;-)
[~michael], [~jda] Thank you for the contribution! Scheduling to have the proposed changes/improvements included in 8.0.0.
PR: https://github.com/appcelerator/titanium_mobile/pull/10443
FR Passed. PR merged
Verified the fix on SDK 8.0.0.v20181126083929 .Image get saved.Closing Test Environment: Name = Mac OS X Version = 10.13.6 Architecture = 64bit Memory = 17179869184 Node.js Node.js Version = 8.12.0 npm Version = 6.4.1 Titanium CLI CLI Version = 5.1.1 Titanium SDK SDK Version = 8.0.0.v20181120090229 Device =Pixel android 9,samsung s5 android 6 Emulator =nexsus 6p android 8