{ "id": "175629", "key": "TIMOB-28182", "fields": { "issuetype": { "id": "4", "description": "An improvement or enhancement to an existing feature or task.", "name": "Improvement", "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": "21052", "description": "", "name": "Release 9.3.0", "archived": false, "released": true, "releaseDate": "2020-12-14" } ], "resolution": { "id": "1", "description": "A fix for this issue is checked into the tree and tested.", "name": "Fixed" }, "resolutiondate": "2020-11-16T21:39:44.000+0000", "created": "2020-10-09T01:06:23.000+0000", "epic": { "id": 175371, "key": "TIMOB-28045", "name": "Android 11: Implement Support", "summary": "Android 11: Implement Support", "color": { "key": "color_3" }, "done": false }, "priority": { "name": "Medium", "id": "3" }, "labels": [ "android", "externalstorage", "manifest", "permission", "storage" ], "versions": [], "issuelinks": [ { "id": "58817", "type": { "id": "10000", "name": "Blocks", "inward": "is blocked by", "outward": "blocks" }, "inwardIssue": { "id": "175376", "key": "TIMOB-28050", "fields": { "summary": "Android: Investigate \"package visibility\" handling on Android 11", "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" } }, "priority": { "name": "Medium", "id": "3" }, "issuetype": { "id": "7", "description": "gh.issue.story.desc", "name": "Story", "subtask": false } } } } ], "assignee": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "updated": "2020-11-16T21:39:44.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": [ { "id": "10202", "name": "Android", "description": "Android Platform" } ], "description": "*Synopsis:*\r\nThe \"AndroidManifest.xml\" permission {{WRITE_EXTERNAL_STORAGE}} is ignored when targeting Android 10+ and running on those Android OS versions. This is because of scoped-storage and Google only allows an app to write to very particular folders on external storage. The app's sandboxed folders on external storage no longer require permission as of Android 4.4.\r\nhttps://developer.android.com/training/data-storage/shared/media#request-permissions\r\n\r\n*To-Do:*\r\nThe Titanium build system should be changed to only inject this permission when needed based on JavaScript APIs used.\r\n\r\nIf the {{Ti.Filesystem.requestStoragePermissions()}} method is used, then the permission should be added as before like this. This is the only method that requires this permission on Android 10 and above.\r\n{code:xml}\r\n\r\n{code}\r\n\r\nIf {{Ti.Media.showCamera()}} or {{Ti.Media.saveToPhotoGallery()}} methods are used, then we should add a {{maxSdkVersion}} attribute set to Android 9 since external storage access is not required on Android 10 and higher.\r\n{code:xml}\r\n\r\n{code}\r\n\r\nIf none of the above JavaScript methods are detected, then we should *NOT* add this permission.\r\n\r\n*Note:*\r\nIf the \"tiapp.xml\" adds the {{WRITE_EXTERNAL_STORAGE}} permission, then it should take priority and the above should be ignored.\r\n", "attachment": [ { "id": "67640", "filename": "CameraPhotoExternalTest.js", "author": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2020-11-09T22:24:20.000+0000", "size": 982, "mimeType": "application/x-javascript" }, { "id": "67639", "filename": "ImageToGalleryTest.js", "author": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2020-11-09T22:23:56.000+0000", "size": 870, "mimeType": "application/x-javascript" }, { "id": "67641", "filename": "RequestStoragePermissionsTest.js", "author": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2020-11-09T23:01:26.000+0000", "size": 526, "mimeType": "application/x-javascript" } ], "flagged": false, "summary": "Android: Only add WRITE_EXTERNAL_STORAGE permission when needed", "creator": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "subtasks": [], "reporter": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "environment": null, "closedSprints": [ { "id": 1210, "state": "closed", "name": "2020 Sprint 23", "startDate": "2020-11-09T17:49:00.000Z", "endDate": "2020-11-20T17:49:00.000Z", "completeDate": "2020-11-20T17:46:00.641Z", "originBoardId": 114 } ], "comment": { "comments": [ { "id": "457471", "author": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "body": "I'm thinking we should *NOT* implement this because it would be a breaking-change that would cause a lot of tech-support issues. It should be the Titanium app developer's responsibility to do this instead.\r\n\r\nThe reason is because a lot of customer code that looks like the below will always fail to request for external storage permission on Android 10 and above if we set {{maxSdkVersion}} to {{29}}. Note that the {{requestStoragePermission()}} method call is no longer required anymore as of 9.3.0 and can be removed.\r\n{code:javascript}\r\n// You no longer need to request storage permission to access externalStorageDirectory anymore.\r\nTi.Filesystem.requestStoragePermissions((e) => {\r\n\tif (!e.success) {\r\n\t\talert(\"External storage permission not granted.\")\r\n\t} else if (!Ti.Filesystem.isExternalStoragePresent()) {\r\n\t\talert(\"External storage is not available on this device.\");\r\n\t} else {\r\n\t\tconst dataFile = Ti.Filesystem.getFile(Ti.Filesystem.externalStorageDirectory, 'MyFile.txt');\r\n\t\tdataFile.write(\"Hello World\");\r\n\t}\r\n});\r\n{code}\r\n\r\n*Note1 :*\r\nAs of Titanium 9.3.0, our {{Ti.Media.showCamera()}} and {{Ti.Media.saveToPhotoGallery()}} method no longer require this permission on Android 10 and above. And our {{Ti.Filesystem.externalStorageDirectory}} does not require this permission on Android 4.4 and above.\r\n\r\nThis means the {{Ti.Filesystem.requestStoragePermission()}} is only needed for:\r\n* Android 6 - Android 9 when using {{showCamera()}} and {{saveToPhotoGallery()}} APIs.\r\n* Sometimes by 3rd party modules or hyperloop code which writes outside of scoped storage.\r\n\r\n*Note 2:*\r\nAs of 9.3.0, the {{Ti.Media.requestCameraPermissions()}} method is smart enough to only request external storage permission for Android 6 to Android 9. It won't request this permission on Android 10 and above. This method should be used before calling {{showCamera()}}.\r\n\r\n*Note 3:*\r\nAs of 9.3.0, we've added {{Ti.Media.requestPhotoGalleryPermissions()}} support on Android. This method should be used before calling {{saveToPhotoGallery()}} like iOS. The Android implementation is smart enough to only request external storage permission for Android 6 to Android 9. This method will always return return true on Android 10 and above.\r\n", "updateAuthor": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2020-11-06T20:44:28.000+0000", "updated": "2020-11-07T04:48:53.000+0000" }, { "id": "457474", "author": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Okay. I've changed my mind.\r\n\r\nInstead of always adding the {{WRITE_EXTERNAL_STORAGE}} permission with a {{maxSdkVersion}} set to Android 9, we should instead scan the JS code for APIs that need this permission and inject it intelligently like how we handle the camera and location permissions.", "updateAuthor": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2020-11-07T05:13:36.000+0000", "updated": "2020-11-07T05:13:36.000+0000" }, { "id": "457477", "author": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "body": "PR (master): https://github.com/appcelerator/titanium_mobile/pull/12253", "updateAuthor": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2020-11-07T06:28:17.000+0000", "updated": "2020-11-07T06:28:17.000+0000" }, { "id": "457522", "author": { "name": "lchoudhary", "key": "lchoudhary", "displayName": "Lokesh Choudhary", "active": true, "timeZone": "America/Los_Angeles" }, "body": "FR Passed.\r\nPR Merged.", "updateAuthor": { "name": "lchoudhary", "key": "lchoudhary", "displayName": "Lokesh Choudhary", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2020-11-16T21:39:10.000+0000", "updated": "2020-11-16T21:39:10.000+0000" } ], "maxResults": 4, "total": 4, "startAt": 0 } } }