{ "id": "122777", "key": "ALOY-894", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "11113", "key": "ALOY", "name": "Alloy", "projectCategory": { "id": "10400", "description": "Tools for developing applications", "name": "Tooling" } }, "fixVersions": [], "resolution": { "id": "2", "description": "The problem described is an issue which will never be fixed.", "name": "Won't Fix" }, "resolutiondate": "2014-01-16T19:04:05.000+0000", "created": "2013-11-21T22:49:52.000+0000", "priority": null, "labels": [], "versions": [], "issuelinks": [ { "id": "34320", "type": { "id": "10003", "name": "Relates", "inward": "relates to", "outward": "relates to" }, "inwardIssue": { "id": "124876", "key": "TIMOB-16207", "fields": { "summary": "Android: Setting a property that expect a boolean to a string throws ClassCastException", "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": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false } } } } ], "assignee": { "name": "tlukasavage", "key": "tlukasavage", "displayName": "Tony Lukasavage", "active": true, "timeZone": "America/Los_Angeles" }, "updated": "2014-01-16T19:04:10.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": "12326", "name": "XML", "description": "View XML and parsing" } ], "description": "When passing custom arguments using the tag on an alloy controller\r\n\r\nmain.xml\r\n\r\n\r\ninputText.xml\r\n\r\n\t\r\n\t\t\r\n\t\r\n\r\n\r\ninputText.js\r\nvar args = arguments[0] || {};\r\n$.textField.enableReturnKey = args.enableReturnKey;\r\n\r\n\r\nOn iOS, it works. On Android, the app crashes with a ClassCastException because the string \"true\" is not boolean true.\r\n\r\n\r\njava.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Boolean\r\nE/AndroidRuntime( 3277): \tat ti.modules.titanium.ui.widget.TiUIText.onEditorAction(TiUIText.java:373)\r\nE/AndroidRuntime( 3277): \tat android.widget.TextView.onEditorAction(TextView.java:4594)\r\nE/AndroidRuntime( 3277): \tat com.android.internal.widget.EditableInputConnection.performEditorAction(EditableInputConnection.java:138)\r\nE/AndroidRuntime( 3277): \tat com.android.internal.view.IInputConnectionWrapper.executeMessage(IInputConnectionWrapper.java:297)\r\nE/AndroidRuntime( 3277): \tat com.android.internal.view.IInputConnectionWrapper$MyHandler.handleMessage(IInputConnectionWrapper.java:77)\r\nE/AndroidRuntime( 3277): \tat android.os.Handler.dispatchMessage(Handler.java:99)\r\nE/AndroidRuntime( 3277): \tat android.os.Looper.loop(Looper.java:175)\r\nE/AndroidRuntime( 3277): \tat android.app.ActivityThread.main(ActivityThread.java:5279)\r\nE/AndroidRuntime( 3277): \tat java.lang.reflect.Method.invokeNative(Native Method)\r\nE/AndroidRuntime( 3277): \tat java.lang.reflect.Method.invoke(Method.java:511)\r\nE/AndroidRuntime( 3277): \tat com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)\r\nE/AndroidRuntime( 3277): \tat com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)\r\nE/AndroidRuntime( 3277): \tat dalvik.system.NativeStart.main(Native Method)\r\n\r\n\r\nThe line being here:\r\nhttps://github.com/appcelerator/titanium_mobile/blob/master/android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIText.java\r\nBoolean enableReturnKey = (Boolean) proxy.getProperty(TiC.PROPERTY_ENABLE_RETURN_KEY);\r\n\r\n\r\n\r\nThis works fine if the attribute is set directly in the destination controller.\r\n\r\ninputText.xml\r\n\r\n\t\r\n\t\t\r\n\t\r\n\r\n\r\nAlloy is likely doing some lookahead on the proxy to change the xml string \"true\" to the object true. But this cannot be done when passing an argument through the tag.\r\n\r\n\r\nThe workaround is:\r\n\r\nvar args = arguments[0] || {};\r\n\r\n$.textField.enableReturnKey = toBoolean(args.enableReturnKey);\r\n\r\nfunction toBoolean(str) {\r\n if (!str) {\r\n return false;\r\n }\r\n if (str == \"true\") {\r\n return true;\r\n } else if (str == \"false\") {\r\n return false;\r\n }\r\n return str;\r\n}\r\n\r\n\r\nBut, this is not very elegant.\r\n\r\nThe iOS proxies likely do some graceful type conversions when looking for booleans, but the Android ones do not. Unfortunately the fix is very difficult. Modify all Android proxies that want non string values, to pass through a utility that checks the type and converts to Boolean instead of a blind cast.\r\n\r\nOr, developers should understand that they are setting a string value on a boolean attribute in their controllers. I would just accept that and fix my code, but the fact that it works in iOS and not in Android is why this may be considered a bug.\r\n", "attachment": [], "flagged": false, "summary": "Android proxies throwing ClassCastException on non string attributes", "creator": { "name": "toddlindner", "key": "toddlindner", "displayName": "Todd Lindner", "active": true, "timeZone": "America/New_York" }, "subtasks": [], "reporter": { "name": "mpmiranda", "key": "mpmiranda", "displayName": "Mauro Parra-Miranda", "active": true, "timeZone": "America/Mexico_City" }, "environment": "titanium 3.1.3 alloy 1.1.0", "comment": { "comments": [ { "id": "283031", "author": { "name": "toddlindner", "key": "toddlindner", "displayName": "Todd Lindner", "active": true, "timeZone": "America/New_York" }, "body": "When trying bleeding edge SDK builds (3.2.x or 3.3.x) I am also seeing additional ClassCastExceptions. This time on a normal . Perhaps a related bug in alloy, not converting Strings to Boolean.\r\n\r\njava.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Boolean\r\nat org.appcelerator.titanium.view.TiUIView.registerForTouch(TiUIView.java:1457)\r\n", "updateAuthor": { "name": "toddlindner", "key": "toddlindner", "displayName": "Todd Lindner", "active": true, "timeZone": "America/New_York" }, "created": "2013-12-07T06:54:47.000+0000", "updated": "2013-12-07T06:54:47.000+0000" }, { "id": "283053", "author": { "name": "tlukasavage", "key": "tlukasavage", "displayName": "Tony Lukasavage", "active": true, "timeZone": "America/Los_Angeles" }, "body": "This is more developer error than it is Alloy or Android's fault. By nature, XML handles only strings in attributes. If a developer needs to use a value other than a string, they should be using TSS, which they should be using in the first place regardless. There are hacks I can put in Alloy to do type conversion based on attribute name, but they would be \"improvements\", not \"bug fixes\" as the expectation should be that any value specified in an XML attribute should be a string. I can start to assess some of these attributes and have Alloy do the conversion, but honestly, you should be using TSS to assign properties to your UI components and avoid this entirely.", "updateAuthor": { "name": "tlukasavage", "key": "tlukasavage", "displayName": "Tony Lukasavage", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2013-12-07T14:03:42.000+0000", "updated": "2013-12-07T14:03:42.000+0000" }, { "id": "283566", "author": { "name": "toddlindner", "key": "toddlindner", "displayName": "Todd Lindner", "active": true, "timeZone": "America/New_York" }, "body": "I agree, its poor development practice. Just thought it might be a parity issue since it works on iOS but not Android. I just assumed some type juggling happens in the proxies (beyond Alloy) on iOS, but obviously not in Android as direct Java casting is used.", "updateAuthor": { "name": "toddlindner", "key": "toddlindner", "displayName": "Todd Lindner", "active": true, "timeZone": "America/New_York" }, "created": "2013-12-10T17:38:46.000+0000", "updated": "2013-12-10T17:38:46.000+0000" }, { "id": "288444", "author": { "name": "skypanther", "key": "skypanther", "displayName": "Tim Poulsen", "active": true, "timeZone": "America/New_York" }, "body": "This is probably a platform issue, not Alloy. Even in classic code, assigning a non-string value to a TextField/TextArea on Android will throw the class cast exception. iOS seems to cast values automatically.", "updateAuthor": { "name": "skypanther", "key": "skypanther", "displayName": "Tim Poulsen", "active": true, "timeZone": "America/New_York" }, "created": "2014-01-16T15:01:43.000+0000", "updated": "2014-01-16T15:01:43.000+0000" }, { "id": "288455", "author": { "name": "skypanther", "key": "skypanther", "displayName": "Tim Poulsen", "active": true, "timeZone": "America/New_York" }, "body": "I've opened TIMOB-16207 demonstrating that this behavior is present in classic projects. Recommend closing this ticket.", "updateAuthor": { "name": "skypanther", "key": "skypanther", "displayName": "Tim Poulsen", "active": true, "timeZone": "America/New_York" }, "created": "2014-01-16T16:46:38.000+0000", "updated": "2014-01-16T16:46:38.000+0000" }, { "id": "288495", "author": { "name": "skypanther", "key": "skypanther", "displayName": "Tim Poulsen", "active": true, "timeZone": "America/New_York" }, "body": "Because the cause is the underlying platform handling of data type casting, we're not going to add a workaround in Alloy. See the related TIMOB ticket opened to cover the platform issue.", "updateAuthor": { "name": "skypanther", "key": "skypanther", "displayName": "Tim Poulsen", "active": true, "timeZone": "America/New_York" }, "created": "2014-01-16T19:04:05.000+0000", "updated": "2014-01-16T19:04:05.000+0000" } ], "maxResults": 7, "total": 7, "startAt": 0 } } }