Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-11995] Android: Duplicate setter in module causes cryptic packaging failure

GitHub Issuen/a
TypeBug
PriorityLow
StatusClosed
ResolutionWon't Fix
Resolution Date2017-07-19T17:45:23.000+0000
Affected Version/sRelease 2.1.3
Fix Version/sn/a
ComponentsAndroid
Labelsn/a
ReporterArthur Evans
AssigneeEric Merriman
Created2012-12-07T19:37:02.000+0000
Updated2017-07-19T17:45:23.000+0000

Description

If a proxy object defines a property using propertyAccessors and also defines a custom setter for the property, build fails with the following cryptic (and completely misleading) message:
Executing build.xml...
/Library/Application Support/Titanium/mobilesdk/osx/2.1.4.GA/module/android/build.xml:191: The following error occurred while executing this line:
/Library/Application Support/Titanium/mobilesdk/osx/2.1.4.GA/module/android/build.xml:236: The following error occurred while executing this line:
/Library/Application Support/Titanium/mobilesdk/osx/2.1.4.GA/module/android/build.xml:240: The following error occurred while executing this line:
/Library/Application Support/Titanium/mobilesdk/osx/2.1.4.GA/module/android/build.xml:221: Java returned: -1You may need to install the Command Line Tools package through XCode, in case you haven't done so yet.
Sample code follows:
/**
 * This file was auto-generated by the Titanium Module SDK helper for Android
 * Appcelerator Titanium Mobile
 * Copyright (c) 2009-2010 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the Apache Public License
 * Please see the LICENSE included with this distribution for details.
 *
 */
package com.evarino.actionbarsearch;

import java.util.HashMap;

import org.appcelerator.kroll.KrollDict;
import org.appcelerator.kroll.KrollProxy;
import org.appcelerator.kroll.annotations.Kroll;
import org.appcelerator.titanium.TiC;
import org.appcelerator.titanium.util.Log;
import org.appcelerator.titanium.util.TiConfig;
import org.appcelerator.titanium.util.TiConvert;
import org.appcelerator.titanium.proxy.TiViewProxy;
import org.appcelerator.titanium.view.TiCompositeLayout;
import org.appcelerator.titanium.view.TiCompositeLayout.LayoutArrangement;
import org.appcelerator.titanium.view.TiUIView;
import android.app.Activity;
import android.os.Build;
import android.widget.SearchView;


// This proxy can be created by calling Actionbarsearch.createExample({message: "hello world"})
@Kroll.proxy(creatableInModule = ActionbarsearchModule.class, propertyAccessors = {
		"prompt", "hintText", "showCancel", "value" })
public class ABSearchViewProxy extends TiViewProxy {
	// Standard Debugging variables
	private static final String LCAT = "SearchProxy";
	private static final boolean DBG = TiConfig.LOGD;
	
	// property names
	private static final String HINT_TEXT = "hintText";
	private static final String VALUE = "value";
	private static final String ICONIFIED = "iconified";
	private static final String ICONIFIED_BY_DEFAULT = "iconifiedByDefault";


	// Event names -- matched to SearchBar for consistency -- except "submit", which is android-specific.
	private static final String TEXT_CHANGE_EVENT = "change";
	private static final String TEXT_SUBMIT_EVENT = "submit";
	private static final String CLOSE_EVENT = "cancel";


	private class ABSearchView extends TiUIView implements SearchView.OnQueryTextListener, SearchView.OnCloseListener {
		private SearchView searchView;
		
		public ABSearchView(TiViewProxy proxy) {
			super(proxy);
			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
				searchView = new SearchView(proxy.getActivity());
				searchView.setOnQueryTextListener(this);
				searchView.setOnCloseListener(this);
				setNativeView(searchView);
			}
		}

		@Override
		public void processProperties(KrollDict props) {
			super.processProperties(props);
			
			Log.d(LCAT,"[VIEW LIFECYCLE EVENT] processProperties " + props);

			// Check if the hint text is specified when the view is created.
			if (props.containsKey(HINT_TEXT)) {
				searchView.setQueryHint(props.getString(HINT_TEXT));			
			} 
			if (props.containsKey(VALUE)) {
				searchView.setQuery(props.getString(VALUE), false);			
			} 
			if (props.containsKey(ICONIFIED)) {
				searchView.setIconified(props.getBoolean(ICONIFIED));
			} 
			if (props.containsKey(ICONIFIED_BY_DEFAULT)) {
				searchView.setIconified(props.getBoolean(ICONIFIED_BY_DEFAULT));
			}
		}
		
		@Override
		public void propertyChanged(String key, Object oldValue, Object newValue, KrollProxy proxy) {
			Log.d(LCAT, "Got change event for: " + key);
			if (key.equals(HINT_TEXT)) {
				searchView.setQueryHint((String) newValue);
			}  else if (key.equals(VALUE)) {
				searchView.setQuery((String) newValue, false);			
			}
			else if (key.equals(ICONIFIED)) {
				searchView.setIconified(TiConvert.toBoolean(newValue));
			} else if (key.equals(ICONIFIED_BY_DEFAULT)) {
				searchView.setIconified(TiConvert.toBoolean(newValue));
			}
		}

		@Override
		public boolean onClose() {
			if (proxy.hasListeners(CLOSE_EVENT)) {
				proxy.fireEvent(CLOSE_EVENT, null);
				return true;
			} else {
				return false;
			}		}

		@Override
		public boolean onQueryTextChange(String query) {
				// It is a good idea to check if there are listeners for the event that
				// is about to be fired. There could be zero or multiple listeners for the
				// specified event.
				
				proxy.setProperty(VALUE, query);
				
				if (proxy.hasListeners(TEXT_CHANGE_EVENT)) {
					proxy.fireEvent(TEXT_CHANGE_EVENT, null);
					return true;
				} else {
					return false;
				}
			}

		@Override
		public boolean onQueryTextSubmit(String query) {
			
			if (proxy.hasListeners(TEXT_SUBMIT_EVENT)) {
				proxy.fireEvent(TEXT_SUBMIT_EVENT, null);
				return true;
			} else {
				return false;
			}
		}
		
	}

	// Constructor
	public ABSearchViewProxy() {
		super();
	}

	@Override
	public TiUIView createView(Activity activity) {
		TiUIView view = new ABSearchView(this);
		view.getLayoutParams().autoFillsHeight = true;
		view.getLayoutParams().autoFillsWidth = true;
		return view;
	}

	// Handle creation options
	@Override
	public void handleCreationDict(KrollDict options) {
		super.handleCreationDict(options);

		if (options.containsKey("message")) {
			Log.d(LCAT,
					"example created with message: " + options.get("message"));
		}
	}

	// Methods
	@Kroll.method
	public void printMessage(String message) {
		Log.d(LCAT, "printing message: " + message);
	}

	@Kroll.getProperty
	@Kroll.method
	public String getMessage() {
		return (String) getProperty("message");
	}

	@Kroll.setProperty(retain=true)
	@Kroll.method
	public void setMessage(String message) {
		Log.d(LCAT, "Tried setting module message to: " + message);
		//setPropertyAndFire("message", message);
	}
	
	@Kroll.setProperty
	@Kroll.method
	public void setHintText(String text) {
		Log.d(LCAT, "Tried setting module message to: " + text);
		//setProperty("hintText", text);
	}
}
After commenting out the setHintText method and removing the build directory, the module builds fine. Since a similar pattern works on iOS, people are likely to expect it will work on Android. I will document it, but it would be good if we could provide a better error message, as well.

Comments

  1. Arthur Evans 2012-12-07

    It appears that the failure is in the rhino.idswitch code, which isn't ours. Perhaps we can just fix the Ant buildfile to give a better message when this script fails, indicating that it could be because of a duplicate identifier OR missing Xcode tools.
  2. Lee Morris 2017-07-19

    Closing ticket due to time passed and lack of progress for a number of years. Please open a new ticket if there are any problems.

JSON Source