Titanium

[ALOY-834] Android only: Alloy generates addeventlistener call before createTabGroup call causing crash on Android when <Menu> is in XML...

GitHub Issuen/a
TypeBug
PriorityCritical
StatusClosed
ResolutionFixed
Resolution Date2013-09-24T18:45:59.000+0000
Affected Version/sn/a
Fix Version/sAlloy 1.3.0, 2013 Sprint 20
ComponentsRuntime, XML
Labelsalloy, android, crash, menu, qe-testadded
ReporterHenry David Spells III
AssigneeTony Lukasavage
Created2013-09-23T02:50:41.000+0000
Updated2013-10-14T20:44:26.000+0000

Description

The alloy compiler is generating an addEventListener call on a tabGroup before generating a necessary createTabGroup call. The addEventListener is generated to create the Android menus. I'm pretty sure these were calls were generated in the correct order in Alloy 1.2.0 and possibly 1.2.1. line 48 of ScheduleGroup.js says
    $.__views.ScheduleGroup.addEventListener("open", __alloyId6);
lines 56-61 of SceduleGroup.js says
    $.__views.ScheduleGroup = Ti.UI.createTabGroup({
        tabs: __alloyId7,
        id: "ScheduleGroup",
        activeTabIconTint: "yellow",
        tabsBackgroundSelectedColor: "yellow"
    });
Obviously this is going to crash since $.__views.ScheduleGroup is referenced in line 48 before it is initialized in line 56-61. This XML
<Alloy>
	<TabGroup id="ScheduleGroup" onOpen="doOnOpen" onFocus="doOnFocus" activeTabIconTint="yellow" tabsBackgroundSelectedColor="yellow">
		<Menu id="menu" platform="android">
			<MenuItem id="refreshMenuItem" title="Refresh" onClick="doRefresh" showAsAction="Ti.Android.SHOW_AS_ACTION_IF_ROOM" itemId="1" />
			<MenuItem id="settingsMenuItem" title="Settings" onClick="doSettingsMenuItem" showAsAction="Ti.Android.SHOW_AS_ACTION_IF_ROOM" itemId="0" />
		</Menu>
		<Require src="scheduleTab" />
	</TabGroup>
</Alloy>
generates
function Controller() {
    function __alloyId6() {
        $.__views.ScheduleGroup.removeEventListener("open", __alloyId6);
        if ($.__views.ScheduleGroup.activity) $.__views.ScheduleGroup.activity.onCreateOptionsMenu = function(e) {
            var __alloyId4 = {
                id: "refreshMenuItem",
                title: "Refresh",
                showAsAction: Ti.Android.SHOW_AS_ACTION_IF_ROOM,
                itemId: "1"
            };
            $.__views.refreshMenuItem = e.menu.add(_.pick(__alloyId4, Alloy.Android.menuItemCreateArgs));
            $.__views.refreshMenuItem.applyProperties(_.omit(__alloyId4, Alloy.Android.menuItemCreateArgs));
            doRefresh ? $.__views.refreshMenuItem.addEventListener("click", doRefresh) : __defers["$.__views.refreshMenuItem!click!doRefresh"] = true;
            var __alloyId5 = {
                id: "settingsMenuItem",
                title: "Settings",
                showAsAction: Ti.Android.SHOW_AS_ACTION_IF_ROOM,
                itemId: "0"
            };
            $.__views.settingsMenuItem = e.menu.add(_.pick(__alloyId5, Alloy.Android.menuItemCreateArgs));
            $.__views.settingsMenuItem.applyProperties(_.omit(__alloyId5, Alloy.Android.menuItemCreateArgs));
            doSettingsMenuItem ? $.__views.settingsMenuItem.addEventListener("click", doSettingsMenuItem) : __defers["$.__views.settingsMenuItem!click!doSettingsMenuItem"] = true;
        }; else {
            Ti.API.warn("You attempted to attach an Android Menu to a lightweight Window");
            Ti.API.warn("or other UI component which does not have an Android activity.");
            Ti.API.warn("Android Menus can only be opened on TabGroups and heavyweight Windows.");
        }
    }
    function doSettingsMenuItem() {
        showHideTabGroup(tabGroup, true);
        openSettingsInContainer(tabGroup.activeTab);
    }
    function doOnFocus() {}
    function doOnOpen() {
        setupAndroidMenuIcons(tabGroup);
    }
    function doRefresh(e) {
        globalEvtMgr.fireevent("refresh", "", e);
    }
    require("alloy/controllers/BaseController").apply(this, Array.prototype.slice.call(arguments));
    this.__controllerPath = "ScheduleGroup";
    arguments[0] ? arguments[0]["__parentSymbol"] : null;
    arguments[0] ? arguments[0]["$model"] : null;
    arguments[0] ? arguments[0]["__itemTemplate"] : null;
    var $ = this;
    var exports = {};
    var __defers = {};
    $.__views.ScheduleGroup.addEventListener("open", __alloyId6);
    var __alloyId7 = [];
    $.__views.__alloyId8 = Alloy.createController("scheduleTab", {
        id: "__alloyId8"
    });
    __alloyId7.push($.__views.__alloyId8.getViewEx({
        recurse: true
    }));
    $.__views.ScheduleGroup = Ti.UI.createTabGroup({
        tabs: __alloyId7,
        id: "ScheduleGroup",
        activeTabIconTint: "yellow",
        tabsBackgroundSelectedColor: "yellow"
    });
    $.__views.ScheduleGroup && $.addTopLevelView($.__views.ScheduleGroup);
    doOnOpen ? $.__views.ScheduleGroup.addEventListener("open", doOnOpen) : __defers["$.__views.ScheduleGroup!open!doOnOpen"] = true;
    doOnFocus ? $.__views.ScheduleGroup.addEventListener("focus", doOnFocus) : __defers["$.__views.ScheduleGroup!focus!doOnFocus"] = true;
    exports.destroy = function() {};
    _.extend($, $.__views);
    var tabGroup = $.ScheduleGroup;
    Alloy.Globals.tabGroup = tabGroup;
    Alloy.Globals.tabBarVisible = true;
    __defers["$.__views.refreshMenuItem!click!doRefresh"] && $.__views.refreshMenuItem.addEventListener("click", doRefresh);
    __defers["$.__views.settingsMenuItem!click!doSettingsMenuItem"] && $.__views.settingsMenuItem.addEventListener("click", doSettingsMenuItem);
    __defers["$.__views.ScheduleGroup!open!doOnOpen"] && $.__views.ScheduleGroup.addEventListener("open", doOnOpen);
    __defers["$.__views.ScheduleGroup!focus!doOnFocus"] && $.__views.ScheduleGroup.addEventListener("focus", doOnFocus);
    _.extend($, exports);
}

var Alloy = require("alloy"), Backbone = Alloy.Backbone, _ = Alloy._;

module.exports = Controller;

Comments

  1. Tony Lukasavage 2013-09-24 PR: https://github.com/appcelerator/alloy/pull/245 test apps: * https://github.com/appcelerator/alloy/tree/master/test/apps/testing/ALOY-834 * https://github.com/appcelerator/alloy/tree/master/test/apps/ui/tabgroup Functional tests should be performed on both apps, with iOS and Android, and should be performed on one TiSDK greater than 3.1.0, and one TiSDK less than 3.1.0. Make sure all tests run without error. Additionally, the android tests should be able to launch the Android Menu with the menu button.
  2. Federico Casali 2013-10-14 Verified fixed. TiSDK 3.2.0.v20131013140318 Alloy 1.3.0 Xcode 5 and iOS7 (device and emulator) Android Google Nexus One (4.1.2) and Android Emulator 2.3.3 Closing

JSON Source