{
"id": "172180",
"key": "TIMOB-26370",
"fields": {
"issuetype": {
"id": "1",
"description": "A problem which impairs or prevents the functions of the product.",
"name": "Bug",
"subtask": false
},
"project": {
"id": "10153",
"key": "TIMOB",
"name": "Titanium SDK/CLI",
"projectCategory": {
"id": "10100",
"description": "Titanium and related SDKs used in application development",
"name": "Client"
}
},
"fixVersions": [
{
"id": "19882",
"name": "Release 8.0.0",
"archived": false,
"released": true,
"releaseDate": "2019-03-14"
}
],
"resolution": {
"id": "1",
"description": "A fix for this issue is checked into the tree and tested.",
"name": "Fixed"
},
"resolutiondate": "2018-11-19T23:17:29.000+0000",
"created": "2018-09-05T20:22:16.000+0000",
"priority": {
"name": "Critical",
"id": "1"
},
"labels": [
"android",
"api",
"camera",
"roll",
"save"
],
"versions": [],
"issuelinks": [],
"assignee": {
"name": "ybanev",
"key": "ybanev",
"displayName": "Yordan Banev",
"active": true,
"timeZone": "Europe/Athens"
},
"updated": "2018-11-26T20:10:27.000+0000",
"status": {
"description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.",
"name": "Closed",
"id": "6",
"statusCategory": {
"id": 3,
"key": "done",
"colorName": "green",
"name": "Done"
}
},
"components": [],
"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.\r\n\r\n*Sample Test Code:*\r\n\r\n{code:js}\r\nvar checkCameraPermissions = function(callback) {\r\n if (Ti.Media.hasCameraPermissions()) {\r\n Ti.UI.createAlertDialog({ cancel: 0, buttonNames: ['OK'], title:'Permissions', message:'Already had permissions. Good to go.'}).show();\r\n callback(true);\r\n } else { \r\n Ti.Media.requestCameraPermissions(function(e) {\r\n if (e.success === true) {\r\n Ti.UI.createAlertDialog({ cancel: 0, buttonNames: ['OK'], title:'Permissions', message:'User granted permissions. Good to go.'}).show();\r\n callback(true);\r\n } else {\r\n Ti.UI.createAlertDialog({ cancel: 0, buttonNames: ['OK'], title:'Permissions', message:'User did not granted permissions. Bad day.'}).show();\r\n callback(false);\r\n }\r\n });\r\n }\r\n};\r\n \r\nvar win = Ti.UI.createWindow({\r\n exitOnClose: true,\r\n fullscreen: false,\r\n title: 'Test'\r\n});\r\n\r\nvar button = Titanium.UI.createButton({\r\n title: 'Test DL Img & Save to Camera Roll'\r\n}); \r\nbutton.addEventListener('click', function(e) {\r\n checkCameraPermissions(function(returnedData) { \r\n if (returnedData == true) {\r\n var xhrImageGet = Ti.Network.createHTTPClient({\r\n onerror : function(xhrErr) {\r\n Ti.UI.createAlertDialog({ cancel: 0, buttonNames: ['OK'], title:'Error', message:'Unable to download the remote image. ' + xhrErr.error}).show();\r\n },\r\n onload : function() {\r\n try {\r\n var image_to_save = this.responseData; \r\n var picture = Titanium.Filesystem.getFile(Titanium.Filesystem.tempDirectory, 'icon-arrowFeature.png');\r\n picture.write(image_to_save); \r\n Ti.Media.saveToPhotoGallery(picture.read(), {\r\n success:function(){\r\n Ti.UI.createAlertDialog({ cancel: 0, buttonNames: ['OK'], title:'Image Saved', message:'icon-arrowFeature.png has been saved to your photo gallery.'}).show();\r\n },\r\n error:function(err){\r\n Ti.UI.createAlertDialog({ cancel: 0, buttonNames: ['OK'], title:'Error Saving to Gallery', message:err.error}).show();\r\n// FAILS RIGHT HERE WITH NO ERROR MESSAGE RETURNED. HOWEVER CONSOLE SHOWS THE ERROR TiMedia: (main) [526999,526999] Failed to create external storage directory.\r\n }\r\n }); \r\n image_to_save = null;\r\n }\r\n catch(saveErr) {\r\n Ti.UI.createAlertDialog({ cancel: 0, buttonNames: ['OK'], title:'Error', message:'Unable to save the file to your device. ' + saveErr.error}).show();\r\n }\r\n }\r\n }); \r\n xhrImageGet.open(\"GET\", 'https://s3.amazonaws.com/www.appcelerator.com.images/icon-arrowFeature.png');\r\n xhrImageGet.send(); \r\n } else {\r\n 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();\r\n } \r\n }); \r\n});\r\n\r\nwin.add(button); \r\nwin.open();\r\n{code}\r\n\r\n*Test App tiapp.xml:*\r\n{code:xml}\r\n\r\n\r\n com.walkthelot.testsavetogallery\r\n TestSaveToGallery\r\n 1.0\r\n Ken\r\n \r\n undefined\r\n 2018 by Ken\r\n appicon.png\r\n false\r\n false\r\n true\r\n a17321c0-c4e2-4542-9222-e3f5731f8fb5\r\n dp\r\n true\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n true\r\n false\r\n false\r\n \r\n 7.3.1.GA\r\n\r\n{code}\r\nI've dug and dug and can not find anything that I may be missing.",
"attachment": [
{
"id": "65498",
"filename": "Screenshot_20180906-165427.jpg",
"author": {
"name": "rmitro",
"key": "rmitro",
"displayName": "Rakhi Mitro",
"active": false,
"timeZone": "America/Los_Angeles"
},
"created": "2018-09-06T10:55:12.000+0000",
"size": 79182,
"mimeType": "image/jpeg"
}
],
"flagged": false,
"summary": "Android: \"Failed to create external storage directory\" when download image and save to camera roll",
"creator": {
"name": "ken@walkthelot.com",
"key": "ken@walkthelot.com",
"displayName": "Ken Rucker",
"active": true,
"timeZone": "America/New_York"
},
"subtasks": [],
"reporter": {
"name": "ken@walkthelot.com",
"key": "ken@walkthelot.com",
"displayName": "Ken Rucker",
"active": true,
"timeZone": "America/New_York"
},
"environment": "TI SDK 7.3.1 (fails below as well). MacOS. Android API 26, 27 & 28",
"comment": {
"comments": [
{
"id": "441376",
"author": {
"name": "rmitro",
"key": "rmitro",
"displayName": "Rakhi Mitro",
"active": false,
"timeZone": "America/Los_Angeles"
},
"body": "Hello,\r\n\r\nTested this issue and able to reproduce on SDK 7.3.0.GA, android 8(Huawei y9 2018) device.\r\n\r\n*Test Environemnt:*\r\n{code}\r\n\r\nAppcelerator Command-Line Interface, version 7.0.5\r\nOperating System\r\n Name = Mac OS X\r\n Version = 10.13.6\r\n Architecture = 64bit\r\n # CPUs = 4\r\n Memory = 8589934592\r\nNode.js\r\n Node.js Version = 8.9.1\r\n npm Version = 5.5.1\r\nTitanium CLI\r\n CLI Version = 5.1.1\r\nTitanium SDK\r\n SDK Version = 7.3.0.GA\r\n\r\nDevice: Android 8(Huawei y9 2018) device, Android 5 device\r\n {code}\r\n\r\n*Test code:* Sample code provided above\r\n\r\n*Test Steps:*\r\n1. Create a classic project .\r\n2. Paste the sample code and run. \r\n3. 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.\r\n\r\n\r\n*Console logs:*\r\n\r\n{code}\r\n[WARN] : libEGL: EGLNativeWindowType 0x7330b73010 disconnect failed\r\n[ERROR] : TiMedia: (main) [14437,14437] Failed to create external storage directory.\r\n[INFO] : zygote64: Do partial code cache collection, code=30KB, data=29KB\r\n[INFO] : zygote64: After code cache collection, code=30KB, data=29KB\r\n[INFO] : zygote64: Increasing code cache capacity to 128KB\r\n[INFO] : PressGestureDetector: HiTouch restricted: AboardArea.\r\n[INFO] : APSAnalyticsRunnable: Analytics Started\r\n[INFO] : APSAnalyticsRunnable: Stopping Service\r\n{code}",
"updateAuthor": {
"name": "sdarda",
"key": "sdarda",
"displayName": "Sharif AbuDarda",
"active": false,
"timeZone": "Asia/Dhaka"
},
"created": "2018-09-06T10:53:42.000+0000",
"updated": "2018-09-07T13:02:17.000+0000"
},
{
"id": "442281",
"author": {
"name": "Pietro",
"key": "pietro",
"displayName": "Pietro Granati",
"active": true,
"timeZone": "Europe/Rome"
},
"body": "Same problem here! \r\nTested on LG and Huawei with android 8 and not working, on LG with android 6 no problem",
"updateAuthor": {
"name": "Pietro",
"key": "pietro",
"displayName": "Pietro Granati",
"active": true,
"timeZone": "Europe/Rome"
},
"created": "2018-10-04T10:40:36.000+0000",
"updated": "2018-10-04T10:40:36.000+0000"
},
{
"id": "442327",
"author": {
"name": "Pietro",
"key": "pietro",
"displayName": "Pietro Granati",
"active": true,
"timeZone": "Europe/Rome"
},
"body": "Any news? This bug is HUGE!",
"updateAuthor": {
"name": "Pietro",
"key": "pietro",
"displayName": "Pietro Granati",
"active": true,
"timeZone": "Europe/Rome"
},
"created": "2018-10-05T08:32:40.000+0000",
"updated": "2018-10-05T08:32:40.000+0000"
},
{
"id": "442329",
"author": {
"name": "ybanev",
"key": "ybanev",
"displayName": "Yordan Banev",
"active": true,
"timeZone": "Europe/Athens"
},
"body": "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.",
"updateAuthor": {
"name": "ybanev",
"key": "ybanev",
"displayName": "Yordan Banev",
"active": true,
"timeZone": "Europe/Athens"
},
"created": "2018-10-05T09:05:20.000+0000",
"updated": "2018-10-05T09:05:20.000+0000"
},
{
"id": "442330",
"author": {
"name": "michael",
"key": "michael",
"displayName": "Michael Gangolf",
"active": true,
"timeZone": "Europe/Berlin"
},
"body": "[~ybanev]\r\nyou are right. Requesting the runtime permission:\r\n{code}\r\nvar permissions = ['android.permission.WRITE_EXTERNAL_STORAGE'...];\r\nTi.Android.requestPermissions(permissions, function(e) {...})\r\n{code}\r\n\r\nworks on Android 8. Without that I'll get the error described above",
"updateAuthor": {
"name": "michael",
"key": "michael",
"displayName": "Michael Gangolf",
"active": true,
"timeZone": "Europe/Berlin"
},
"created": "2018-10-05T09:23:59.000+0000",
"updated": "2018-10-05T09:23:59.000+0000"
},
{
"id": "442331",
"author": {
"name": "Pietro",
"key": "pietro",
"displayName": "Pietro Granati",
"active": true,
"timeZone": "Europe/Rome"
},
"body": "Yeah while searching I was trying and seems to work",
"updateAuthor": {
"name": "Pietro",
"key": "pietro",
"displayName": "Pietro Granati",
"active": true,
"timeZone": "Europe/Rome"
},
"created": "2018-10-05T09:26:03.000+0000",
"updated": "2018-10-05T09:26:03.000+0000"
},
{
"id": "442332",
"author": {
"name": "ybanev",
"key": "ybanev",
"displayName": "Yordan Banev",
"active": true,
"timeZone": "Europe/Athens"
},
"body": "Here is the reason why this problem occurred:\r\nhttps://developer.android.com/about/versions/oreo/android-8.0-changes#rmp\r\nWhat 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).\r\nThe workaround provided by [~michael] is the best way to go for now. ",
"updateAuthor": {
"name": "ybanev",
"key": "ybanev",
"displayName": "Yordan Banev",
"active": true,
"timeZone": "Europe/Athens"
},
"created": "2018-10-05T10:00:21.000+0000",
"updated": "2018-10-05T10:00:21.000+0000"
},
{
"id": "442529",
"author": {
"name": "michael",
"key": "michael",
"displayName": "Michael Gangolf",
"active": true,
"timeZone": "Europe/Berlin"
},
"body": "I can't update the Wiki (permission denied), but the examples there should be updated to the latest permissions too:\r\nhttps://docs.appcelerator.com/platform/latest/#!/guide/Camera_and_Photo_Gallery_APIs\r\n",
"updateAuthor": {
"name": "michael",
"key": "michael",
"displayName": "Michael Gangolf",
"active": true,
"timeZone": "Europe/Berlin"
},
"created": "2018-10-11T14:31:27.000+0000",
"updated": "2018-10-11T14:31:27.000+0000"
},
{
"id": "442530",
"author": {
"name": "jda",
"key": "jda",
"displayName": "John Dalsgaard",
"active": true,
"timeZone": "Europe/Berlin"
},
"body": "Agree with Michael\r\n\r\nThe documentation should mention that for iOS you need to add these permissions in tiapp.xml:\r\n\r\n{code:java}\r\n NSCameraUsageDescription\r\n We need access to take a photo\r\n NSPhotoLibraryUsageDescription\r\n We need access to save a photo\r\n NSPhotoLibraryAddUsageDescription\r\n We need access to save a photo\r\n{code}\r\n\r\nand the showCamera() method could be adapted to something like this:\r\n\r\n{code:java}\r\n // check if we already have permissions to capture media\r\n var permissionsToRequest = [\"android.permission.CAMERA\",\"android.permission.WRITE_EXTERNAL_STORAGE\"];\r\n if ((OS_ANDROID && !Ti.Android.hasPermission(permissionsToRequest)) || (OS_IOS && !Ti.Media.hasCameraPermissions())) {\r\n // request permissions to capture media\r\n if(OS_ANDROID) {\r\n\t Ti.Android.requestPermissions(permissionsToRequest,function (e) {\r\n\t // success! display the camera\r\n\t if (e.success) {\r\n\t camera();\r\n\t } else {\r\n\t callback(new Error('could not obtain camera permissions!'), null);\r\n\t }\r\n\t });\r\n } else {\r\n\t Ti.Media.requestCameraPermissions(function (e) {\r\n\t // success! display the camera\r\n\t if (e.success) {\r\n\t camera();\r\n\t } else {\r\n\t callback(new Error('could not obtain camera permissions!'), null);\r\n\t }\r\n\t });\r\n }\r\n } else {\r\n camera();\r\n }\r\n{code}\r\n\r\n... or some cleaner code ;-) ",
"updateAuthor": {
"name": "jda",
"key": "jda",
"displayName": "John Dalsgaard",
"active": true,
"timeZone": "Europe/Berlin"
},
"created": "2018-10-11T15:00:54.000+0000",
"updated": "2018-10-11T15:00:54.000+0000"
},
{
"id": "442702",
"author": {
"name": "ybanev",
"key": "ybanev",
"displayName": "Yordan Banev",
"active": true,
"timeZone": "Europe/Athens"
},
"body": "[~michael], [~jda] Thank you for the contribution! Scheduling to have the proposed changes/improvements included in 8.0.0.",
"updateAuthor": {
"name": "ybanev",
"key": "ybanev",
"displayName": "Yordan Banev",
"active": true,
"timeZone": "Europe/Athens"
},
"created": "2018-10-17T15:14:47.000+0000",
"updated": "2018-10-17T15:14:47.000+0000"
},
{
"id": "443421",
"author": {
"name": "ybanev",
"key": "ybanev",
"displayName": "Yordan Banev",
"active": true,
"timeZone": "Europe/Athens"
},
"body": "PR: https://github.com/appcelerator/titanium_mobile/pull/10443",
"updateAuthor": {
"name": "ybanev",
"key": "ybanev",
"displayName": "Yordan Banev",
"active": true,
"timeZone": "Europe/Athens"
},
"created": "2018-11-08T16:28:46.000+0000",
"updated": "2018-11-08T16:28:46.000+0000"
},
{
"id": "443760",
"author": {
"name": "kmahalingam",
"key": "kmahalingam",
"displayName": "Keerthi Mahalingam",
"active": false,
"timeZone": "America/Los_Angeles"
},
"body": "FR Passed. PR merged",
"updateAuthor": {
"name": "kmahalingam",
"key": "kmahalingam",
"displayName": "Keerthi Mahalingam",
"active": false,
"timeZone": "America/Los_Angeles"
},
"created": "2018-11-19T23:14:36.000+0000",
"updated": "2018-11-19T23:14:36.000+0000"
},
{
"id": "443894",
"author": {
"name": "kmahalingam",
"key": "kmahalingam",
"displayName": "Keerthi Mahalingam",
"active": false,
"timeZone": "America/Los_Angeles"
},
"body": "Verified the fix on SDK 8.0.0.v20181126083929 .Image get saved.Closing\r\nTest Environment:\r\n Name = Mac OS X\r\n Version = 10.13.6\r\n Architecture = 64bit\r\n Memory = 17179869184\r\nNode.js\r\n Node.js Version = 8.12.0\r\n npm Version = 6.4.1\r\nTitanium CLI\r\n CLI Version = 5.1.1\r\nTitanium SDK\r\n SDK Version = 8.0.0.v20181120090229\r\nDevice =Pixel android 9,samsung s5 android 6\r\nEmulator =nexsus 6p android 8\r\n",
"updateAuthor": {
"name": "kmahalingam",
"key": "kmahalingam",
"displayName": "Keerthi Mahalingam",
"active": false,
"timeZone": "America/Los_Angeles"
},
"created": "2018-11-26T20:10:22.000+0000",
"updated": "2018-11-26T20:10:22.000+0000"
}
],
"maxResults": 16,
"total": 16,
"startAt": 0
}
}
}