{ "id": "172640", "key": "TIMOB-26581", "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": [], "resolution": null, "resolutiondate": null, "created": "2018-11-17T02:05:45.000+0000", "priority": { "name": "Medium", "id": "3" }, "labels": [ "android", "click", "engSchedule", "parity", "touch", "touchEnabled" ], "versions": [], "issuelinks": [ { "id": "58451", "type": { "id": "10003", "name": "Relates", "inward": "relates to", "outward": "relates to" }, "outwardIssue": { "id": "110733", "key": "TIMOB-12989", "fields": { "summary": "Android: Events still fire if touchEnabled on view is false", "status": { "description": "The issue is open and ready for the assignee to start work on it.", "name": "Open", "id": "1", "statusCategory": { "id": 2, "key": "new", "colorName": "blue-gray", "name": "To Do" } }, "priority": { "name": "Low", "id": "4" }, "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false } } } } ], "assignee": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "updated": "2021-02-22T18:39:07.000+0000", "status": { "description": "The issue is open and ready for the assignee to start work on it.", "name": "Open", "id": "1", "statusCategory": { "id": 2, "key": "new", "colorName": "blue-gray", "name": "To Do" } }, "components": [ { "id": "10202", "name": "Android", "description": "Android Platform" } ], "description": "*Summary:*\r\nIf a parent view's \"touchEnabled\" property is set {{true}}, its child view \"touchEnabled\" is set {{false}}, and then you tap on the child view... that child view will wrongly fire a touch/click event. The problem with this is that event will \"bubble\" to the parent view and its coordinates will be relative to the child, not the parent.\r\n\r\n*Steps to reproduce:*\r\n# Build and run the below code on Android.\r\n# Touch and drag the blue square. (The square's \"touchEnabled\" property is set {{false}}.)\r\n# If the square turns red, then it has wrongly fired a touch event.\r\n\r\n{code:javascript}\r\nfunction onTouch(event) {\r\n\tif (event.source != parentView) {\r\n\t\tTi.API.error(\"@@@ Oh-no! Child view wrongly fired a touch event.\");\r\n\t\tchildView.backgroundColor = \"red\";\r\n\t}\r\n\tif ((event.type === \"pinch\") || (event.type === \"swipe\")) {\r\n\t\treturn;\r\n\t}\r\n\tchildView.center = { x: event.x, y: event.y };\r\n}\r\n\r\nvar window = Ti.UI.createWindow();\r\nvar parentView = Ti.UI.createView({\r\n\ttouchEnabled: true,\r\n\tbackgroundColor: \"gray\",\r\n\twidth: Ti.UI.FILL,\r\n\theight: Ti.UI.FILL,\r\n});\r\nvar childView = Ti.UI.createView({\r\n\ttouchEnabled: false,\r\n\tbackgroundColor: \"blue\",\r\n\twidth: \"100dp\",\r\n\theight: \"100dp\",\r\n});\r\nparentView.add(childView);\r\nparentView.addEventListener(\"touchstart\", onTouch);\r\nparentView.addEventListener(\"touchmove\", onTouch);\r\nparentView.addEventListener(\"touchend\", onTouch);\r\nparentView.addEventListener(\"touchcancel\", onTouch);\r\nparentView.addEventListener(\"click\", onTouch);\r\nparentView.addEventListener(\"dblclick\", onTouch);\r\nparentView.addEventListener(\"doubletap\", onTouch);\r\nparentView.addEventListener(\"longpress\", onTouch);\r\nparentView.addEventListener(\"pinch\", onTouch);\r\nparentView.addEventListener(\"swipe\", onTouch);\r\nwindow.add(parentView);\r\nwindow.open();\r\n{code}\r\n\r\n*Cause:*\r\nThe Java touch/gesture event listeners in our {{TiUIView.java}} class are always listening. They fire an event if a JavaScript listener is set up on the view or any of its parent views by calling {{KrollProxy.hierarchyHasListener()}}. This is partly the issue.\r\nhttps://github.com/appcelerator/titanium_mobile/blob/master/android/titanium/src/java/org/appcelerator/titanium/view/TiUIView.java\r\n\r\n*Recommended Solution:*\r\nReplace the {{KrollProxy.hierarchyHasListener()}} calls with {{KrollProxy.hasListener()}}. Also check if touch events are enabled by calling [View.isClickable()|https://developer.android.com/reference/android/view/View.html#isClickable()] which I +think+ will return {{false}} (we need to double check) when we set the \"touchEnabled\" property to false because changing that property ends up calling {{TiUIView.doSetClickable()}}.\r\n\r\n*Work-around:*\r\nCheck the event's \"source\" property to see which view has fired this event. The above test code does this and you can simply return out of the listener if it doesn't reference the expected view.\r\n", "attachment": [], "flagged": false, "summary": "Android: Child view with \"touchEnabled\" false wrongly receives touch events if parent is touch enabled", "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, "comment": { "comments": [ { "id": "445680", "author": { "name": "michael", "key": "michael", "displayName": "Michael Gangolf", "active": true, "timeZone": "Europe/Berlin" }, "updateAuthor": { "name": "michael", "key": "michael", "displayName": "Michael Gangolf", "active": true, "timeZone": "Europe/Berlin" }, "created": "2019-01-27T18:08:35.000+0000", "updated": "2019-01-27T18:08:35.000+0000" }, { "id": "445711", "author": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Thanks [~michael]. That PR might have introduced the issue, but the \"cause\" I've written up above is still true. I think the real issue here is that the code is walking up the parent hierarchy and is regarding the child as touch enabled if at least 1 of its parents is touch enabled. iOS does not work this way. This is the root cause.", "updateAuthor": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2019-01-28T19:42:25.000+0000", "updated": "2019-01-28T19:42:25.000+0000" }, { "id": "450935", "author": { "name": "dbankier", "key": "dbankier", "displayName": "David Bankier", "active": true, "timeZone": "America/Los_Angeles" }, "body": "The same scenario will trigger a longpress on the parent, even for a short click/tap.\r\nThe above work around can be used to determine a true long press.\r\n\r\n", "updateAuthor": { "name": "dbankier", "key": "dbankier", "displayName": "David Bankier", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2019-08-29T10:59:57.000+0000", "updated": "2019-08-29T10:59:57.000+0000" }, { "id": "452881", "author": { "name": "Andrea.Vitale", "key": "andrea.vitale", "displayName": "Andrea Vitale", "active": true, "timeZone": "Europe/Berlin" }, "body": "Same issue here with latest SDK available (8.2.0.GA). touchEnabled: false seems to be ignored.", "updateAuthor": { "name": "Andrea.Vitale", "key": "andrea.vitale", "displayName": "Andrea Vitale", "active": true, "timeZone": "Europe/Berlin" }, "created": "2019-11-22T09:41:06.000+0000", "updated": "2019-11-22T09:41:06.000+0000" } ], "maxResults": 4, "total": 4, "startAt": 0 } } }