{ "id": "62754", "key": "TIMOB-2122", "fields": { "issuetype": { "id": "2", "description": "A new feature of the product, which has yet to be developed.", "name": "New Feature", "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": "16980", "description": "New V8", "name": "Release 6.0.0", "archived": false, "released": true, "releaseDate": "2016-11-15" }, { "id": "17706", "name": "Release 5.4.0", "archived": false, "released": true, "releaseDate": "2016-08-11" } ], "resolution": { "id": "1", "description": "A fix for this issue is checked into the tree and tested.", "name": "Fixed" }, "resolutiondate": "2016-04-26T04:57:53.000+0000", "created": "2011-04-15T03:11:06.000+0000", "priority": { "name": "Medium", "id": "3" }, "labels": [ "dr-list", "parity", "supportTeam", "tbs-1.8.0" ], "versions": [], "issuelinks": [ { "id": "51504", "type": { "id": "10003", "name": "Relates", "inward": "relates to", "outward": "relates to" }, "inwardIssue": { "id": "154687", "key": "TIMOB-20320", "fields": { "summary": "Android: Provide generic requestPermission(s) method", "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": "High", "id": "2" }, "issuetype": { "id": "4", "description": "An improvement or enhancement to an existing feature or task.", "name": "Improvement", "subtask": false } } } } ], "assignee": { "name": "msamah", "key": "msamah", "displayName": "Ashraf Abu", "active": false, "timeZone": "Asia/Singapore" }, "updated": "2016-07-12T21:50:53.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": "{html}{html}", "attachment": [], "flagged": false, "summary": "Android: Implement Video Recording", "creator": { "name": "dthorp", "key": "dthorp", "displayName": "Don Thorp", "active": true, "timeZone": "America/Los_Angeles" }, "subtasks": [], "reporter": { "name": "dthorp", "key": "dthorp", "displayName": "Don Thorp", "active": true, "timeZone": "America/Los_Angeles" }, "environment": null, "closedSprints": [ { "id": 624, "state": "closed", "name": "2016 Sprint 09 SDK", "startDate": "2016-04-23T00:39:45.452Z", "endDate": "2016-05-07T00:39:00.000Z", "completeDate": "2016-05-09T03:43:10.279Z", "originBoardId": 114 } ], "comment": { "comments": [ { "id": "160287", "author": { "name": "jalter", "key": "jalter", "displayName": "Jon Alter", "active": true, "timeZone": "America/Los_Angeles" }, "body": "h3. Associated Helpdesk Ticket\r\nhttp://appc.me/c/APP-632317", "updateAuthor": { "name": "jalter", "key": "jalter", "displayName": "Jon Alter", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2011-07-21T17:46:32.000+0000", "updated": "2011-07-21T17:46:32.000+0000" }, { "id": "256720", "author": { "name": "speedyankur@gmail.com", "key": "speedyankur@gmail.com", "displayName": "ankur garha", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Is this feature still implemented for android or not???", "updateAuthor": { "name": "speedyankur@gmail.com", "key": "speedyankur@gmail.com", "displayName": "ankur garha", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2013-06-07T20:03:36.000+0000", "updated": "2013-06-07T20:03:36.000+0000" }, { "id": "301723", "author": { "name": "danpe", "key": "danpe", "displayName": "Dan Peleg", "active": true, "timeZone": "Asia/Jerusalem" }, "body": "Solved here: [https://github.com/appcelerator/titanium_mobile/pull/5634]", "updateAuthor": { "name": "danpe", "key": "danpe", "displayName": "Dan Peleg", "active": true, "timeZone": "Asia/Jerusalem" }, "created": "2014-04-21T10:01:10.000+0000", "updated": "2014-04-21T10:01:10.000+0000" }, { "id": "319382", "author": { "name": "johann.chaves", "key": "johann.chaves", "displayName": "Johan Chaves", "active": true, "timeZone": "America/Guatemala" }, "body": "Hi, when is this going to be integrated?", "updateAuthor": { "name": "johann.chaves", "key": "johann.chaves", "displayName": "Johan Chaves", "active": true, "timeZone": "America/Guatemala" }, "created": "2014-08-19T23:47:26.000+0000", "updated": "2014-08-19T23:47:26.000+0000" }, { "id": "379749", "author": { "name": "michael", "key": "michael", "displayName": "Michael Gangolf", "active": true, "timeZone": "Europe/Berlin" }, "body": "@danpe iOS has startVideoCapture( ) and stopVideoCapture(). So renaming the android methods startRecording/stopRecording would help to add this to android", "updateAuthor": { "name": "michael", "key": "michael", "displayName": "Michael Gangolf", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-03-15T15:00:44.000+0000", "updated": "2016-03-15T15:00:44.000+0000" }, { "id": "382245", "author": { "name": "michael", "key": "michael", "displayName": "Michael Gangolf", "active": true, "timeZone": "Europe/Berlin" }, "body": "Started to work on a new PR: https://github.com/appcelerator/titanium_mobile/pull/7929\r\n\r\nNot ready yet but it's already working on a Nexus 4 (5.1.1) with native camera (with overlays and maxDuration) and the intent version!\r\n\r\n\r\n*Example:*\r\n{code:javascript}\r\nvar isRecording = false;\r\nvar isPhoto = false; // photo or video mode\r\n\r\nvar win = Ti.UI.createWindow({\r\n backgroundColor: \"#fff\"\r\n});\r\n\r\nvar btn_cam1 = Ti.UI.createButton({\r\n bottom: 10,\r\n title: \"CAM INTENT\"\r\n})\r\n\r\nvar btn_cam2 = Ti.UI.createButton({\r\n bottom: 50,\r\n title: \"CAM OVERLAY REAR\",\r\n id: \"cam_rear\"\r\n})\r\nvar btn_cam3 = Ti.UI.createButton({\r\n bottom: 90,\r\n title: \"CAM OVERLAY FRONT\",\r\n id: \"cam_front\"\r\n})\r\n\r\nvar btn_cam4 = Ti.UI.createButton({\r\n bottom: 130,\r\n title: \"PHOTO REAR\",\r\n id: \"photo_rear\"\r\n})\r\nvar btn_cam5 = Ti.UI.createButton({\r\n bottom: 170,\r\n title: \"PHOTO FRONT\",\r\n id: \"photo_front\"\r\n})\r\n\r\nvar btnCamera = Ti.UI.createButton({\r\n title: 'RECORD',\r\n bottom: 10\r\n});\r\n\r\nvar btnCameraClose = Ti.UI.createButton({\r\n title: 'CLOSE',\r\n top: 10\r\n});\r\n\r\nvar overlay = Ti.UI.createView({\r\n top: 0,\r\n right: 0,\r\n bottom: 0,\r\n left: 0\r\n});\r\n\r\nvar movie = Titanium.Media.createVideoPlayer({\r\n movieControlStyle: Titanium.Media.VIDEO_CONTROL_EMBEDDED,\r\n top: 0,\r\n height: 200,\r\n width: 200,\r\n backgroundColor: \"#000\",\r\n autoplay:false\r\n});\r\n\r\n\r\noverlay.add(btnCamera);\r\noverlay.add(btnCameraClose);\r\n\r\nbtnCameraClose.addEventListener('click', function(e) {\r\n Titanium.Media.hideCamera();\r\n});\r\n\r\nbtnCamera.addEventListener('click', function(e) {\r\n if (isPhoto){\r\n // photo mode\r\n Titanium.Media.takePicture();\r\n } else {\r\n // video mode\r\n if (!isRecording) {\r\n Titanium.Media.startVideoCapture();\r\n isRecording = true;\r\n btnCamera.title = \"STOP\";\r\n } else {\r\n Titanium.Media.stopVideoCapture();\r\n }\r\n }\r\n});\r\n\r\nfunction onClickCam(e) {\r\n // intent\r\n Ti.Media.showCamera({\r\n autohide: false,\r\n animated: false,\r\n allowEditing: false,\r\n saveToPhotoGallery: true,\r\n success: function(event) {\r\n movie.url = event.media.nativePath;\r\n },\r\n cancel: function(event) {\r\n console.log(\"error\");\r\n },\r\n error: function(error) {\r\n alert('error');\r\n },\r\n mediaTypes: [Titanium.Media.MEDIA_TYPE_VIDEO],\r\n videoMaximumDuration: 5000,\r\n videoQuality: Titanium.Media.QUALITY_HIGH\r\n });\r\n}\r\n\r\n\r\nfunction onClickCam2(e) {\r\n // native camera\r\n var which = Ti.Media.CAMERA_REAR;\r\n var type = Titanium.Media.MEDIA_TYPE_VIDEO;\r\n var autohide = false;\r\n isPhoto = false;\r\n \r\n if (e.source.id == \"cam_front\") {\r\n which = Ti.Media.CAMERA_FRONT;\r\n }\r\n if (e.source.id==\"photo_front\") {\r\n which = Ti.Media.CAMERA_FRONT;\r\n type = Ti.Media.MEDIA_TYPE_PHOTO;\r\n isPhoto = true;\r\n autohide = true;\r\n }\r\n if (e.source.id==\"photo_rear\") {\r\n type = Ti.Media.MEDIA_TYPE_PHOTO;\r\n isPhoto = true;\r\n autohide = true;\r\n }\r\n \r\n console.log(type + \" \" + which);\r\n \r\n Ti.Media.showCamera({\r\n overlay: overlay,\r\n autohide: autohide,\r\n animated: false,\r\n allowEditing: false,\r\n saveToPhotoGallery: true,\r\n whichCamera: which,\r\n success: function(event) {\r\n if (!isPhoto) movie.url = event.media.nativePath;\r\n isRecording = false;\r\n btnCamera.title = \"RECORD\";\r\n },\r\n cancel: function(event) {\r\n console.log(\"error\");\r\n },\r\n error: function(error) {\r\n alert('error');\r\n },\r\n mediaTypes: [type],\r\n videoMaximumDuration: 5000,\r\n videoQuality: Titanium.Media.QUALITY_HIGH\r\n });\r\n}\r\n\r\nbtn_cam1.addEventListener(\"click\", onClickCam);\r\nbtn_cam2.addEventListener(\"click\", onClickCam2);\r\nbtn_cam3.addEventListener(\"click\", onClickCam2);\r\nbtn_cam4.addEventListener(\"click\", onClickCam2);\r\nbtn_cam5.addEventListener(\"click\", onClickCam2);\r\n\r\nwin.add(btn_cam1);\r\nwin.add(btn_cam2);\r\nwin.add(btn_cam3);\r\nwin.add(btn_cam4);\r\nwin.add(btn_cam5);\r\nwin.add(movie);\r\nwin.open();\r\n{code}\r\n\r\n!http://www.migaweb.de/ti_video_example.png!\r\n\r\nAdded documentation and extended the example ", "updateAuthor": { "name": "michael", "key": "michael", "displayName": "Michael Gangolf", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-04-09T18:30:08.000+0000", "updated": "2016-04-10T12:54:22.000+0000" }, { "id": "382264", "author": { "name": "michael", "key": "michael", "displayName": "Michael Gangolf", "active": true, "timeZone": "Europe/Berlin" }, "body": "Tested on: \r\n* Nexus 4 (5.1.1)\r\n* HTC A9 (6.0.1)\r\n* Samsung Galaxy S2 (4.2.2)", "updateAuthor": { "name": "michael", "key": "michael", "displayName": "Michael Gangolf", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-04-10T19:51:36.000+0000", "updated": "2016-04-23T18:37:19.000+0000" }, { "id": "383871", "author": { "name": "msamah", "key": "msamah", "displayName": "Ashraf Abu", "active": false, "timeZone": "Asia/Singapore" }, "body": "PR by [~michael] https://github.com/appcelerator/titanium_mobile/pull/7929 Merged.\r\nAdditional PR for minor code formatting: https://github.com/appcelerator/titanium_mobile/pull/7965", "updateAuthor": { "name": "msamah", "key": "msamah", "displayName": "Ashraf Abu", "active": false, "timeZone": "Asia/Singapore" }, "created": "2016-04-26T04:58:36.000+0000", "updated": "2016-04-26T04:58:36.000+0000" }, { "id": "383887", "author": { "name": "msamah", "key": "msamah", "displayName": "Ashraf Abu", "active": false, "timeZone": "Asia/Singapore" }, "body": "Additional note: For android 6.0, for the Video to record sound as well, you would need to grant the permission `android.permission.RECORD_AUDIO`", "updateAuthor": { "name": "msamah", "key": "msamah", "displayName": "Ashraf Abu", "active": false, "timeZone": "Asia/Singapore" }, "created": "2016-04-26T07:31:12.000+0000", "updated": "2016-04-26T07:31:12.000+0000" }, { "id": "384017", "author": { "name": "msamah", "key": "msamah", "displayName": "Ashraf Abu", "active": false, "timeZone": "Asia/Singapore" }, "body": "Test app code including permission requests that is needed in Android 6.0:-\r\n{code}\r\nvar isRecording = false;\r\nvar isPhoto = false; // photo or video mode\r\n\r\nvar win = Ti.UI.createWindow({\r\n backgroundColor: \"#fff\"\r\n});\r\n\r\nvar btn_cam1 = Ti.UI.createButton({\r\n bottom: 10,\r\n title: \"CAM INTENT\"\r\n});\r\n\r\nvar btn_cam2 = Ti.UI.createButton({\r\n bottom: 50,\r\n title: \"CAM OVERLAY REAR\",\r\n id: \"cam_rear\"\r\n});\r\nvar btn_cam3 = Ti.UI.createButton({\r\n bottom: 90,\r\n title: \"CAM OVERLAY FRONT\",\r\n id: \"cam_front\"\r\n});\r\n\r\nvar btn_cam4 = Ti.UI.createButton({\r\n bottom: 130,\r\n title: \"PHOTO REAR\",\r\n id: \"photo_rear\"\r\n});\r\nvar btn_cam5 = Ti.UI.createButton({\r\n bottom: 170,\r\n title: \"PHOTO FRONT\",\r\n id: \"photo_front\"\r\n});\r\n\r\nvar btnCamera = Ti.UI.createButton({\r\n title: 'RECORD',\r\n bottom: 10\r\n});\r\n\r\nvar btnCameraClose = Ti.UI.createButton({\r\n title: 'CLOSE',\r\n top: 10\r\n});\r\n\r\nvar overlay = Ti.UI.createView({\r\n top: 0,\r\n right: 0,\r\n bottom: 0,\r\n left: 0\r\n});\r\n\r\nvar movie = Titanium.Media.createVideoPlayer({\r\n movieControlStyle: Titanium.Media.VIDEO_CONTROL_EMBEDDED,\r\n top: 0,\r\n height: 200,\r\n width: 200,\r\n backgroundColor: \"#000\",\r\n autoplay:false\r\n});\r\n\r\n\r\noverlay.add(btnCamera);\r\noverlay.add(btnCameraClose);\r\n\r\nbtnCameraClose.addEventListener('click', function(e) {\r\n Titanium.Media.hideCamera();\r\n});\r\n\r\nbtnCamera.addEventListener('click', function(e) {\r\n if (isPhoto){\r\n // photo mode\r\n Titanium.Media.takePicture();\r\n } else {\r\n // video mode\r\n if (!isRecording) {\r\n Titanium.Media.startVideoCapture();\r\n isRecording = true;\r\n btnCamera.title = \"STOP\";\r\n } else {\r\n Titanium.Media.stopVideoCapture();\r\n }\r\n }\r\n});\r\n\r\nfunction onClickCam(e) {\r\n // intent\r\n Ti.Media.showCamera({\r\n autohide: false,\r\n animated: false,\r\n allowEditing: false,\r\n saveToPhotoGallery: true,\r\n success: function(event) {\r\n movie.url = event.media.nativePath;\r\n },\r\n cancel: function(event) {\r\n console.log(\"error\");\r\n },\r\n error: function(error) {\r\n alert('error');\r\n },\r\n mediaTypes: [Titanium.Media.MEDIA_TYPE_VIDEO],\r\n videoMaximumDuration: 5000,\r\n videoQuality: Titanium.Media.QUALITY_HIGH\r\n });\r\n}\r\n\r\n\r\nfunction onClickCam2(e) {\r\n // native camera\r\n var which = Ti.Media.CAMERA_REAR;\r\n var type = Titanium.Media.MEDIA_TYPE_VIDEO;\r\n var autohide = false;\r\n isPhoto = false;\r\n\r\n if (e.source.id == \"cam_front\") {\r\n which = Ti.Media.CAMERA_FRONT;\r\n }\r\n if (e.source.id==\"photo_front\") {\r\n which = Ti.Media.CAMERA_FRONT;\r\n type = Ti.Media.MEDIA_TYPE_PHOTO;\r\n isPhoto = true;\r\n autohide = true;\r\n }\r\n if (e.source.id==\"photo_rear\") {\r\n type = Ti.Media.MEDIA_TYPE_PHOTO;\r\n isPhoto = true;\r\n autohide = true;\r\n }\r\n\r\n console.log(type + \" \" + which);\r\n\r\n Ti.Media.showCamera({\r\n overlay: overlay,\r\n autohide: autohide,\r\n animated: false,\r\n allowEditing: false,\r\n saveToPhotoGallery: true,\r\n whichCamera: which,\r\n success: function(event) {\r\n if (!isPhoto) movie.url = event.media.nativePath;\r\n isRecording = false;\r\n btnCamera.title = \"RECORD\";\r\n },\r\n cancel: function(event) {\r\n console.log(\"error\");\r\n },\r\n error: function(error) {\r\n alert('error');\r\n },\r\n mediaTypes: [type],\r\n videoMaximumDuration: 5000,\r\n videoQuality: Titanium.Media.QUALITY_HIGH\r\n });\r\n}\r\n\r\nbtn_cam1.addEventListener(\"click\", onClickCam);\r\nbtn_cam2.addEventListener(\"click\", onClickCam2);\r\nbtn_cam3.addEventListener(\"click\", onClickCam2);\r\nbtn_cam4.addEventListener(\"click\", onClickCam2);\r\nbtn_cam5.addEventListener(\"click\", onClickCam2);\r\n\r\nwin.add(btn_cam1);\r\nwin.add(btn_cam2);\r\nwin.add(btn_cam3);\r\nwin.add(btn_cam4);\r\nwin.add(btn_cam5);\r\nwin.add(movie);\r\n\r\n\r\nwin.open();\r\n\r\nvar permissionsToRequest = [];\r\n\r\nvar audioPermission = \"android.permission.RECORD_AUDIO\";\r\nvar hasAudioPerm = Ti.Android.hasPermission(audioPermission);\r\nif (!hasAudioPerm) {\r\n permissionsToRequest.push(audioPermission);\r\n} \r\n\r\nvar cameraPermission = \"android.permission.CAMERA\";\r\nvar hasCameraPerm = Ti.Android.hasPermission(cameraPermission);\r\nif (!hasCameraPerm) {\r\n permissionsToRequest.push(cameraPermission);\r\n}\r\n\r\nvar storagePermission = \"android.permission.READ_EXTERNAL_STORAGE\";\r\nvar hasStoragePerm = Ti.Android.hasPermission(storagePermission);\r\nif (!hasStoragePerm) {\r\n permissionsToRequest.push(storagePermission);\r\n}\r\n\r\nif (permissionsToRequest.length > 0) {\r\n Ti.Android.requestPermissions(permissionsToRequest, function(e) {\r\n if (e.success) {\r\n Ti.API.info(\"SUCCESS\");\r\n } else {\r\n Ti.API.info(\"ERROR: \" + e.error);\r\n }\r\n });\r\n}\r\n{code}", "updateAuthor": { "name": "msamah", "key": "msamah", "displayName": "Ashraf Abu", "active": false, "timeZone": "Asia/Singapore" }, "created": "2016-04-27T03:09:05.000+0000", "updated": "2016-04-27T03:09:05.000+0000" }, { "id": "390531", "author": { "name": "lchoudhary", "key": "lchoudhary", "displayName": "Lokesh Choudhary", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Verified the implementation.\r\nVideo recording works as expected.\r\n\r\nClosing.\r\n\r\nEnvironment:\r\nAppc Studio : 4.7.0.201607111053\r\nTi SDK : 5.4.0.v20160712013704\r\nTi CLI : 5.0.9\r\nAlloy : 1.9.1\r\nMAC El Capitan : 10.11.5\r\nAppc NPM : 4.2.8-1\r\nAppc CLI : 5.4.0-31\r\nNode: 4.4.4\r\nNexus 6 - Android 6.0.1", "updateAuthor": { "name": "lchoudhary", "key": "lchoudhary", "displayName": "Lokesh Choudhary", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-07-12T21:50:41.000+0000", "updated": "2016-07-12T21:50:41.000+0000" } ], "maxResults": 16, "total": 16, "startAt": 0 } } }