public abstract class MicroEmulatorActivity extends Activity {
		
	public static AndroidConfig config = new AndroidConfig();
	
	public boolean windowFullscreen;

	private Handler handler = new Handler();
	
	private Thread activityThread;
	
	protected View contentView;

	private Dialog dialog;
	
	private ArrayList<ActivityResultListener> activityResultListeners = new ArrayList<ActivityResultListener>();
	
	protected EmulatorContext emulatorContext;
	
	public void setConfig(AndroidConfig config) {
		MicroEmulatorActivity.config = config;
	}
    
    public EmulatorContext getEmulatorContext() {
        return emulatorContext;
    }

	public boolean post(Runnable r) {
		if (activityThread == Thread.currentThread()) {
			r.run();
			return true;
		} else {
			return handler.post(r);
		}
	}
	
	public boolean postDelayed(Runnable r, long delayMillis) {
		if (activityThread == Thread.currentThread()) {
			r.run();
			return true;
		} else {
			return handler.postDelayed(r, delayMillis);
		}
	}
	
	public boolean isActivityThread() {
		return (activityThread == Thread.currentThread());
	}

	@Override
	protected void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		
		// Query the activity property android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
		TypedArray ta = getTheme().obtainStyledAttributes(new int[] { android.R.attr.windowFullscreen });
		windowFullscreen = ta.getBoolean(0, false);
		
		Drawable phoneCallIcon = getResources().getDrawable(android.R.drawable.stat_sys_phone_call);
		int statusBarHeight = 0;
		if (!windowFullscreen) {
			statusBarHeight = phoneCallIcon.getIntrinsicHeight();
		}
		
        Display display = getWindowManager().getDefaultDisplay();
        final int width = display.getWidth();
        final int height = display.getHeight() - statusBarHeight;

        emulatorContext = new EmulatorContext() {

            private InputMethod inputMethod = new AndroidInputMethod();

            private DeviceDisplay deviceDisplay = new AndroidDeviceDisplay(MicroEmulatorActivity.this, this, width, height);
            
            private FontManager fontManager = new AndroidFontManager(getResources().getDisplayMetrics());

            public DisplayComponent getDisplayComponent() {
                // TODO consider removal of EmulatorContext.getDisplayComponent()
                System.out.println("MicroEmulator.emulatorContext::getDisplayComponent()");
                return null;
            }

            public InputMethod getDeviceInputMethod() {
                return inputMethod;
            }

            public DeviceDisplay getDeviceDisplay() {
                return deviceDisplay;
            }

            public FontManager getDeviceFontManager() {
                return fontManager;
            }

            public InputStream getResourceAsStream(Class origClass, String name) {
                try {
                    if (name.startsWith("/")) {
                        return MicroEmulatorActivity.this.getAssets().open(name.substring(1));
                    } else {
                        Package p = origClass.getPackage();
                        if (p == null) {
                            return MicroEmulatorActivity.this.getAssets().open(name);
                        } else {
                        	String folder = origClass.getPackage().getName().replace('.', '/');
                            return MicroEmulatorActivity.this.getAssets().open(folder + "/" + name);
                        }
                    }
                } catch (IOException e) {
                    Logger.debug(e);
                    return null;
                }
            }

            public boolean platformRequest(String url) throws ConnectionNotFoundException 
            {
                try {
                    startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
                } catch (ActivityNotFoundException e) {
                    throw new ConnectionNotFoundException();
                }

                return true;
            }
                    
        };
		
		activityThread = Thread.currentThread();
	}
	
	public View getContentView() {
		return contentView;
	}

	@Override
	public void setContentView(View view) {
Log.d("AndroidCanvasUI", "set content view: " + view);                			
		super.setContentView(view);
		
		contentView = view;
	}
		
	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
		
		Drawable phoneCallIcon = getResources().getDrawable(android.R.drawable.stat_sys_phone_call);
		int statusBarHeight = 0;
		if (!windowFullscreen) {
			statusBarHeight = phoneCallIcon.getIntrinsicHeight();
		}
		
        Display display = getWindowManager().getDefaultDisplay();
		AndroidDeviceDisplay deviceDisplay = (AndroidDeviceDisplay) DeviceFactory.getDevice().getDeviceDisplay();
		deviceDisplay.setSize(display.getWidth(), display.getHeight() - statusBarHeight);
		MIDletAccess ma = MIDletBridge.getMIDletAccess();
		if (ma == null) {
			return;
		}
		DisplayAccess da = ma.getDisplayAccess();
		if (da != null) {
			da.sizeChanged();
			deviceDisplay.repaint(0, 0, deviceDisplay.getFullWidth(), deviceDisplay.getFullHeight());
		}
	}
	
	public void addActivityResultListener(ActivityResultListener listener) {
		activityResultListeners.add(listener);
	}
	
	public void removeActivityResultListener(ActivityResultListener listener) {
		activityResultListeners.remove(listener);
	}
	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		for (Iterator<ActivityResultListener> it = activityResultListeners.iterator(); it.hasNext(); ) {
			if (it.next().onActivityResult(requestCode, resultCode, data)) {
				return;
			}
		}
		
		super.onActivityResult(requestCode, resultCode, data);
	}

	public void setDialog(Dialog dialog) {
		this.dialog = dialog;
		if (dialog != null) {
			showDialog(0);
		} else {
			removeDialog(0);
		}
	}

	@Override
	protected Dialog onCreateDialog(int id) {
		return dialog;
	}
	
} public class AndroidAlertUI extends AndroidDisplayableUI implements AlertUI {

	private AlertDialog alertDialog;

	private DialogInterface.OnClickListener onClickListener;

	private Map<Integer, CommandUI> buttons = new HashMap<Integer, CommandUI>();

	/**
	 * A quick reference to the Alert, without needing to unbox.
	 */
	protected Alert displayableUnboxed;

	/**
	 * Needed at runtime to know if we are showing a Toast instead of an Alert.
	 */
	protected boolean inShowingToast;

	/**
	 * Depending on the properties, the Alert can be rendered as a Toast or
	 * Alert.
	 * 
	 * @see #isToastable()
	 * @see #showNotifyAsToast()
	 */
	public AndroidAlertUI(final MicroEmulatorActivity activity,
			final Alert alert) {
		super(activity, alert, false);

		displayableUnboxed = alert;

		activity.post(new Runnable() {
			public void run() {
				alertDialog = new AlertDialog.Builder(activity).create();
				if (alert.getTitle() != null) {
					alertDialog.setTitle(alert.getTitle());
				}
				onClickListener = new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
						activity.setDialog(null);
						MIDletBridge.getMIDletAccess().getDisplayAccess().commandAction(
								buttons.get(which).getCommand(), displayable);
					}
				};
			}
		});
	}

	/**
	 * Decides if we should show a Toast instead of an Alert.
	 * 
	 * @return <code>true</code> if the current Alert is of type
	 *         {@link AlertType#INFO} and has one command only and the timeout
	 *         {@link Alert#getTimeout()} is different from
	 *         {@link Alert#FOREVER}.
	 */
	protected boolean isToastable() {
		boolean isToastable = 
				(displayableUnboxed.getType() == null || displayableUnboxed.getType().equals(AlertType.INFO))
				&& displayableUnboxed.getTimeout() != Alert.FOREVER
				&& getCommandsUI().size() == 1;

		return isToastable;
	}

	/**
	 * Gets the message to show in the Toast.<br>
	 * Called by {@link #showNotifyAsToast()}.
	 * 
	 * @return if both {@link Alert#getTitle()} and {@link Alert#getString()}
	 *         available then Alert.title + ": " + Alert.string. If not both
	 *         available, then Alert.title + Alert.string.
	 */
	protected String getToastMessage() {
		String title = displayableUnboxed.getTitle();
		String string = displayableUnboxed.getString();

		boolean hasBoth = title != null && !title.equals("") && string != null
				&& !string.equals("");

		String message = title + (hasBoth ? ": " : "") + string;

		return message;
	}

	/**
	 * Shows a Toast instead of the Alert dialog and calls the commandAction
	 * handler for the Ok/Default button of the Alert.
	 * 
	 * @see #isToastable()
	 */
	protected void showNotifyAsToast() {
		// show toast

		// according to the documentation the duration of the Toast
		// can be user defined, but this is not true according to the
		// implementation.
		
		//IMPLEMENTATION
		// http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=services/java/com/android/server/NotificationManagerService.java#l74
		// private static final int LONG_DELAY = 3500; // 3.5 seconds
		// private static final int SHORT_DELAY = 2000; // 2 seconds
		  
		// displayableUnboxed.getTimeout() in J2ME is in millis

		int duration = displayableUnboxed.getTimeout() <= 2000 ? Toast.LENGTH_SHORT
				: Toast.LENGTH_LONG;

		Toast toast = Toast.makeText(activity, getToastMessage(), duration);
		toast.show();

		// needed because we later need to call the commandListener
		fixLookAndFeel();

		// call commandAction
		MIDletBridge.getMIDletAccess().getDisplayAccess().commandAction(
				getCommandsUI().elementAt(0).getCommand(), displayable);
	}

	@Override
	public void showNotify() {
		boolean isToastable = isToastable();

		// set toast flag
		// needed for hideNotify
		inShowingToast = isToastable;

		if (isToastable) {
			activity.post(new Runnable() {
				public void run() {
					showNotifyAsToast();
				}
			});
		} else {
			activity.post(new Runnable() {
				public void run() {
					fixLookAndFeel();
					activity.setDialog(alertDialog);
				}
			});
		}
	}

	@Override
	public void hideNotify() {
		// we don't need to hide anything if Toasted
		if (inShowingToast) {
			inShowingToast = false;
		} else {
			activity.post(new Runnable() {
				public void run() {
					activity.setDialog(null);
				}
			});
		}
	}

	private void fixLookAndFeel() {
		for (CommandUI cmd : getCommandsUI()) {
			buttonize(cmd);
		}
	}

	private void buttonize(CommandUI cmd) {
		int which = 0;
		Command command = cmd.getCommand();
		if (command == Alert.DISMISS_COMMAND) {
			which = DialogInterface.BUTTON_NEUTRAL;
		} else {
			switch (command.getCommandType()) {
			case Command.OK:
				which = DialogInterface.BUTTON_POSITIVE;
				break;
			case Command.CANCEL:
				which = DialogInterface.BUTTON_NEGATIVE;
				break;
			}
		}
		if (which == 0) {
			alertDialog.setButton(command.getLabel(), onClickListener);
			which = DialogInterface.BUTTON_POSITIVE;
		} else {
			alertDialog.setButton(which, command.getLabel(), onClickListener);
		}
		buttons.put(which, cmd);
	}

	//
	// AlertUI
	//		

	public void setString(final String str) {
		activity.post(new Runnable() {
			public void run() {
				alertDialog.setMessage(str);
			}
		});
	}

} public class AndroidCanvasUI extends AndroidDisplayableUI implements CanvasUI {   
    
    private AndroidDisplayGraphics gameCanvasGraphics = null;   
    
    private static Bitmap gameCanvasBitmap = null;
    
    public AndroidCanvasUI(final MicroEmulatorActivity activity, Canvas canvas) {
        super(activity, canvas, false);
       
        
        activity.post(new Runnable() {
            public void run() {
                view = new CanvasView(activity, AndroidCanvasUI.this);
            }
        });
    }
        
    public View getView() {
        return view;
    }
    
    @Override
    public void hideNotify()
    {
        ((AndroidDeviceDisplay) activity.getEmulatorContext().getDeviceDisplay()).removeDisplayRepaintListener((DisplayRepaintListener) view);
        
        super.hideNotify();
    }

    @Override
    public void showNotify()
    {
        super.showNotify();
        
        activity.post(new Runnable() {
            public void run() {
		        ((AndroidDeviceDisplay) activity.getEmulatorContext().getDeviceDisplay()).addDisplayRepaintListener((DisplayRepaintListener) view);
		        ((Canvas) displayable).repaint();
            }
        });
    }
    
	public AndroidDisplayGraphics getGraphics() {
		gameCanvasGraphics = new AndroidDisplayGraphics();
        if (gameCanvasBitmap != null) {
        	gameCanvasGraphics.reset(new android.graphics.Canvas(gameCanvasBitmap));
        }
		
		return gameCanvasGraphics;
	}

    //
    // CanvasUI
    //
    
    public class CanvasView extends View implements DisplayRepaintListener {
        
        private final static int FIRST_DRAG_SENSITIVITY_X = 5;
        
        private final static int FIRST_DRAG_SENSITIVITY_Y = 5;
        
        private AndroidCanvasUI ui;
        
        private int pressedX = -FIRST_DRAG_SENSITIVITY_X;
        
        private int pressedY = -FIRST_DRAG_SENSITIVITY_Y;
        
        private Overlay overlay = null;
        
        private Matrix scale = new Matrix();

        private AndroidKeyListener keyListener = null;
        
        private int inputType = InputType.TYPE_CLASS_TEXT;

        public CanvasView(Context context, AndroidCanvasUI ui) {
            super(context);
            this.ui = ui;            
            setFocusable(true);
            setFocusableInTouchMode(true);
        }
        
        public AndroidCanvasUI getUI() {
            return ui;
        }             

        public void flushGraphics(int x, int y, int width, int height) {
            // TODO handle x, y, width and height
            if (repaintListener == null) {
                postInvalidate();
            } else {
                repaintListener.flushGraphics();
            }
        }

        public void setOverlay(Overlay overlay) {
            this.overlay = overlay;
        }
        
        public void setScale(float sx, float sy) {
            scale.reset();
            scale.postScale(sx, sy);
        }

        public void setKeyListener(AndroidKeyListener keyListener, int inputType) {
        	this.keyListener = keyListener;
        	this.inputType = inputType;
        }

        @Override
		public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        	outAttrs.imeOptions |= EditorInfo.IME_ACTION_DONE;
        	outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI;
        	outAttrs.inputType = inputType;
        	
        	return new BaseInputConnection(this, false) {

				@Override
				public boolean performEditorAction(int actionCode) {
					if (actionCode == EditorInfo.IME_ACTION_DONE) {
						InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
		                imm.hideSoftInputFromWindow(CanvasView.this.getWindowToken(), 0);		                
		                return true;
					} else {
						return super.performEditorAction(actionCode);
					}
				}

				@Override
				public boolean commitText(CharSequence text, int newCursorPosition) {
					if (keyListener != null) {
						keyListener.insert(keyListener.getCaretPosition(), text);
					}
					
					return true;
				}

				@Override
				public boolean deleteSurroundingText(int leftLength, int rightLength) {
					if (keyListener != null) {
						int caret = keyListener.getCaretPosition();
						keyListener.delete(caret - leftLength, caret + rightLength);
					}
					
					return true;
				}

				@Override
				public boolean sendKeyEvent(KeyEvent event) {
					return super.sendKeyEvent(event);
				}
        		
        	};
		}        
        
        //
        // View
        //
        
        @Override
        public void onDraw(android.graphics.Canvas androidCanvas) {
            super.onDraw(androidCanvas);
            MIDletAccess ma = MIDletBridge.getMIDletAccess();
            if (ma == null) {
                return;
            }
        	AndroidDisplayGraphics graphics = new AndroidDisplayGraphics();
        	Matrix originalMatrix = androidCanvas.getMatrix();
            graphics.reset(androidCanvas);
            if (gameCanvasGraphics != null) {
            	androidCanvas.drawBitmap(gameCanvasBitmap, scale, null);
            }
            androidCanvas.setMatrix(scale);
			AndroidDeviceDisplay deviceDisplay = (AndroidDeviceDisplay) DeviceFactory.getDevice().getDeviceDisplay();
            androidCanvas.clipRect(0, 0, deviceDisplay.getFullWidth(), deviceDisplay.getFullHeight(), Region.Op.REPLACE);
            ma.getDisplayAccess().paint(graphics);
            if (overlay != null) {
            	androidCanvas.setMatrix(originalMatrix);
            	androidCanvas.clipRect(0, 0, getWidth(), getHeight(), Region.Op.REPLACE);
                overlay.onDraw(androidCanvas);
            }
        }   
        
		@Override
		protected void onSizeChanged(int w, int h, int oldw, int oldh) {
			super.onSizeChanged(w, h, oldw, oldh);
			
			AndroidDeviceDisplay deviceDisplay = (AndroidDeviceDisplay) DeviceFactory.getDevice().getDeviceDisplay();
			deviceDisplay.setSize(w, h);

        	if (gameCanvasBitmap == null || gameCanvasBitmap.getWidth() != w || gameCanvasBitmap.getHeight() != h) {
	        	if (gameCanvasBitmap != null) {
	        		gameCanvasBitmap.recycle();
	        	}
	        	gameCanvasBitmap = Bitmap.createBitmap(deviceDisplay.getFullWidth(), deviceDisplay.getFullHeight(), Bitmap.Config.ARGB_8888);
	        }
	        if (gameCanvasGraphics != null) {
	        	gameCanvasGraphics.reset(new android.graphics.Canvas(gameCanvasBitmap));
	        }
			
			MIDletAccess ma = MIDletBridge.getMIDletAccess();
			if (ma == null) {
				return;
			}
			DisplayAccess da = ma.getDisplayAccess();
			if (da != null) {
				da.sizeChanged();
			}
		}

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (overlay != null && overlay.onTouchEvent(event)) {
                return true;
            }
            Device device = DeviceFactory.getDevice();
            AndroidInputMethod inputMethod = (AndroidInputMethod) device.getInputMethod();
            int x = (int) event.getX();
            int y = (int) event.getY();
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN :
                inputMethod.pointerPressed(x, y);
                pressedX = x;
                pressedY = y;
                break;
            case MotionEvent.ACTION_UP :
                inputMethod.pointerReleased(x, y);
                break;
            case MotionEvent.ACTION_MOVE :
                if (x > (pressedX - FIRST_DRAG_SENSITIVITY_X) &&  x < (pressedX + FIRST_DRAG_SENSITIVITY_X)
                        && y > (pressedY - FIRST_DRAG_SENSITIVITY_Y) &&  y < (pressedY + FIRST_DRAG_SENSITIVITY_Y)) {
                } else {
                    pressedX = -FIRST_DRAG_SENSITIVITY_X;
                    pressedY = -FIRST_DRAG_SENSITIVITY_Y;
                    inputMethod.pointerDragged(x, y);
                }
                break;
            default:
                return false;
            }
            
            return true;
        }

        //
        // DisplayRepaintListener
        //
      
        public void repaintInvoked(Object repaintObject)
        {
            Rect r = (Rect) repaintObject;
            flushGraphics(r.left, r.top, r.width(), r.height());
        }       
        
        private AndroidRepaintListener repaintListener;

        public void setAndroidRepaintListener(AndroidRepaintListener repaintListener)
        {
            this.repaintListener = repaintListener;
        }

    }

} public class AndroidChoiceGroupUI extends LinearLayout implements ChoiceGroupUI {

	private MicroEmulatorActivity activity;
	
	private ChoiceGroup choiceGroup;
	
	private int choiceType;
	
	private TextView labelView;
	
	private AndroidListAdapterEx listAdapter;
	
	private ViewGroup listView;
	
	public AndroidChoiceGroupUI(final MicroEmulatorActivity activity, final ChoiceGroup choiceGroup, final int choiceType) {
		super(activity);
		
		this.activity = activity;
		this.choiceGroup = choiceGroup;
		this.choiceType = choiceType;
		
		this.listAdapter = new AndroidListAdapterEx(activity);

		activity.post(new Runnable() {
			public void run() {
				setOrientation(LinearLayout.VERTICAL);
//				setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));
				
				labelView = new TextView(activity);
				labelView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
				labelView.setTextAppearance(labelView.getContext(), android.R.style.TextAppearance_Large);
				addView(labelView);
				
//				listView.setAdapter(listAdapter);
//				listView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
				
				if (choiceType == Choice.EXCLUSIVE) {
					listView = new RadioGroup(activity);
					((LinearLayout) listView).setOrientation(LinearLayout.VERTICAL);
				} else if (choiceType == Choice.POPUP) {
					listView = new AndroidPopupView(activity, AndroidChoiceGroupUI.this);
					((Spinner) listView).setAdapter(listAdapter);
				} else {
					listView = new AndroidListView(activity, AndroidChoiceGroupUI.this);
					((LinearLayout) listView).setOrientation(LinearLayout.VERTICAL);
				}
				addView(listView);
				
				setLabel(choiceGroup.getLabel());
			}
		});
	}

	public void setDefaultCommand(Command cmd) {
	}

	public void setLabel(final String label) {
		activity.post(new Runnable() {
			public void run() {
				labelView.setText(label);
			}
		});
	}
	
	private View createView(String stringPart, Image imagePart) {
		if (choiceType == Choice.EXCLUSIVE) {
			final RadioButton radioButton = new RadioButton(activity);
			radioButton.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
			radioButton.setOnCheckedChangeListener(new OnCheckedChangeListener() {

				public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
					AndroidFormUI.AndroidListView formList = (AndroidFormUI.AndroidListView) getParent();
					if (formList != null) {
						ItemStateListener listener = formList.getUI().getItemStateListener();
						if (listener != null) {
							listener.itemStateChanged(choiceGroup);
						}
					}
				}
				
			});
			radioButton.setText(stringPart);
			return radioButton;
		} else if (choiceType == Choice.MULTIPLE) {
			CheckBox checkBox = new CheckBox(activity);
			checkBox.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
			checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {

				public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
					AndroidFormUI.AndroidListView formList = (AndroidFormUI.AndroidListView) getParent();
					if (formList != null) {
						ItemStateListener listener = formList.getUI().getItemStateListener();
						if (listener != null) {
							listener.itemStateChanged(choiceGroup);
						}
					}
				}
				
			});
			checkBox.setText(stringPart);
			return checkBox;
		} else if (choiceType == Choice.IMPLICIT) {
			TextView textView = new TextView(activity);
			textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));

			textView.setText(stringPart);
			return textView;
		} else { // Choice.POPUP
			TextView textView = new TextView(activity) {

				@Override
				public String toString() {
					return (String) getText();
				}

			};
			textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));

			textView.setText(stringPart);
			return textView;
		}
	}
	
	public void delete(int elementNum) {
System.out.println("AndroidChoiceGroupUI.delete(..) not synced");
		if (listView instanceof LinearLayout) {
			listView.removeViewAt(elementNum);
		}
		listAdapter.remove(elementNum);
	}
	
	public void deleteAll() {
System.out.println("AndroidChoiceGroupUI.deleteAll() not synced");
				if (listView instanceof LinearLayout) {
					listView.removeAllViews();
				}
				listAdapter.clear();
			}

	public void setSelectedIndex(final int elementNum, final boolean selected) {
		activity.post(new Runnable() {
			public void run() {
				if (choiceType == Choice.POPUP) {
					if (selected) {
						((AdapterView) listView).setSelection(elementNum);
					}
				} else {
					Object element = listAdapter.getItem(elementNum);
					((CompoundButton) element).setChecked(selected);
				}
			}
		});
	}

	public int getSelectedIndex() {
		switch (choiceType) {
			case Choice.EXCLUSIVE:
				for (int i = 0; i < listAdapter.getCount(); ++i) {
					if (((CompoundButton) listAdapter.getItem(i)).isChecked()) {
						return i;
					}
				}
				break;
			case Choice.POPUP:
				return ((AdapterView) listView).getSelectedItemPosition();
			case Choice.IMPLICIT:
System.out.println("Choice.IMPLICIT not implemented yet");
				return -1;
		}
		return -1;
	}

	public void insert(final int elementNum, final String stringPart, final Image imagePart) {
		activity.post(new Runnable() {
			public void run() {
				synchronized (AndroidChoiceGroupUI.this) {
					View view = createView(stringPart, imagePart);
					if (listView instanceof LinearLayout) {
						listView.addView(view, elementNum);
					}
					listAdapter.insert(elementNum, view);
				}
			}
		});
	}
	
	public boolean isSelected(int elementNum) {
System.out.println("AndroidChoiceGroupUI.isSelected(..) not synced");
		if (choiceType == Choice.POPUP) {
			return elementNum == ((AdapterView) listView).getSelectedItemPosition();
		} else {
			return ((CompoundButton) listAdapter.getItem(elementNum)).isChecked();
		}
	}
	
	public void setSelectedFlags(boolean[] selectedArray) {
		for (int i = 0; i < listAdapter.getCount(); ++i) {
			setSelectedIndex(i, selectedArray[i]);
		}
	}

	public int getSelectedFlags(boolean[] selectedArray) {
System.out.println("AndroidChoiceGroupUI.getSelectedFlags(..) not synced");		
		int selectedItemsCount = 0;
		
		for (int i = 0; i < selectedArray.length; ++i) {
			selectedArray[i] = (i < listAdapter.getCount()) ? isSelected(i) : false;
			if (selectedArray[i]) {
				++selectedItemsCount;
			}
		}
		
		return selectedItemsCount;
	}
	
	public String getString(int elementNum) {
		if (choiceType == Choice.POPUP) {
			return listAdapter.getItem(elementNum).toString();
		} else {
			return (String) ((CompoundButton) listAdapter.getItem(elementNum)).getText();
		}
  }
	
	public void set(int elementNum, String stringPart, Image imagePart) {
System.out.println("AndroidChoiceGroupUI.set(..) not synced");
		View view = createView(stringPart, imagePart);
		if (listView instanceof LinearLayout) {
			listView.removeViewAt(elementNum);
			listView.addView(view, elementNum);
		}
		listAdapter.set(elementNum, view);
	}
	
	private int sizeTransfer;

	public int size() {
		sizeTransfer = Integer.MIN_VALUE;
		activity.post(new Runnable() {
			public void run() {
				synchronized (AndroidChoiceGroupUI.this) {
					sizeTransfer = listAdapter.getCount();
					AndroidChoiceGroupUI.this.notify();
				}
			}
		});

		synchronized (AndroidChoiceGroupUI.this) {
			if (sizeTransfer == Integer.MIN_VALUE) {
				try {
					wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		
		return sizeTransfer;
	}

	class AndroidListView extends LinearLayout {
		
		private AndroidChoiceGroupUI ui;

		public AndroidListView(Context context, AndroidChoiceGroupUI ui) {
			super(context);
			
			this.ui = ui;
		}
	
		@Override
		public boolean onTouchEvent(MotionEvent ev) {
			// TODO implement pointer events
			return super.onTouchEvent(ev);
		}
		
		public AndroidChoiceGroupUI getUI() {
			return ui;
		}
		
	}

	private class AndroidListAdapterEx extends ArrayAdapter<View> {

		public AndroidListAdapterEx(Context context) {
			super(context, android.R.layout.simple_spinner_item);
			setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		}

		public void insert(int position, View item) {
			super.insert(item, position);
			notifyDataSetChanged();
		}

		public void remove(int position) {
			super.remove(super.getItem(position));
			notifyDataSetChanged();
		}

		public void set(int position, View item) {
			remove(position);
			insert(position, item);
			notifyDataSetChanged();
		}

	}

	class AndroidPopupView extends Spinner {

		private AndroidChoiceGroupUI ui;

		public AndroidPopupView(Context context, AndroidChoiceGroupUI ui) {
			super(context);

			this.ui = ui;
		}

		public AndroidChoiceGroupUI getUI() {
			return ui;
		}

	}

} public class AndroidCommandUI implements CommandUI {

	private MicroEmulatorActivity activity;
	
	private Command command;
	
	private Drawable drawable;

	public AndroidCommandUI(MicroEmulatorActivity activity, Command command) {
		this.activity = activity;
		this.command = command;
	}

	public Command getCommand() {
		return command;
	}

	public void setImage(Image image) {
		drawable = new BitmapDrawable(((AndroidImmutableImage) image).getBitmap());
	}
	
	public Drawable getDrawable() {
		return drawable;
	}

} public class AndroidDateFieldUI extends LinearLayout implements DateFieldUI {

	private MicroEmulatorActivity activity;
	
	private DateField dateField;
	
	private TextView labelView;
	
	private View datetimeView;
	
	private DatePicker datePicker;
	
	private TimePicker timePicker;
	
	private int mode;

	private Command defaultCommand;
	
	public AndroidDateFieldUI(final MicroEmulatorActivity activity, final DateField dateField) {
		super(activity);
		
		this.activity = activity;
		this.dateField = dateField;
		
		this.mode = -1;
		
		activity.post(new Runnable() {
			public void run() {
				setOrientation(LinearLayout.VERTICAL);
		//		setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));
				
				labelView = new TextView(activity);
				labelView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
				labelView.setTextAppearance(labelView.getContext(), android.R.style.TextAppearance_Large);
				addView(labelView);
				
				setLabel(dateField.getLabel());
			}
		});
	}

	public void setDefaultCommand(Command cmd) {
		this.defaultCommand = cmd;
	}

	public void setLabel(String label) {
		labelView.setText(label);
	}

	public void setInputMode(final int mode) {
		activity.post(new Runnable() {
			public void run() {
				if (AndroidDateFieldUI.this.mode != mode) {
					AndroidDateFieldUI.this.mode = mode;
					if (mode == DateField.TIME) {
						timePicker = createTimePicker();
						datetimeView = timePicker;
					} else if (mode == DateField.DATE) {
						datePicker = createDatePicker();
						datetimeView = datePicker;
					} else { // DateField.DATE_TIME
						datetimeView = new LinearLayout(activity);
						((LinearLayout) datetimeView).setOrientation(LinearLayout.VERTICAL);
						timePicker = createTimePicker();
						datePicker = createDatePicker();
						((LinearLayout) datetimeView).addView(timePicker);
						((LinearLayout) datetimeView).addView(datePicker);
					}
					datetimeView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));
					addView(datetimeView);
				}
			}
		});
	}

	public void setDate(final Date date) {
		activity.post(new Runnable() {
			public void run() {
				GregorianCalendar cal = new GregorianCalendar();
				cal.setTime(date);
				if ((mode & DateField.DATE) != 0) {
					datePicker.updateDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH));
				}
				if ((mode & DateField.TIME) != 0) {
					timePicker.setCurrentHour(cal.get(Calendar.HOUR_OF_DAY));
					timePicker.setCurrentMinute(cal.get(Calendar.MINUTE));
				}
			}
		});
	}

	public Date getDate() {
		final ValueObject result = new ValueObject();
		result.value = null;
		
		activity.post(new Runnable() {
			public void run() {
				GregorianCalendar cal = new GregorianCalendar();
				if ((mode & DateField.DATE) != 0) {
					cal.set(Calendar.YEAR, datePicker.getYear());
					cal.set(Calendar.MONTH, datePicker.getMonth());
					cal.set(Calendar.DAY_OF_MONTH, datePicker.getDayOfMonth());
				}
				if ((mode & DateField.TIME) != 0) {
					cal.set(Calendar.HOUR_OF_DAY, timePicker.getCurrentHour());
					cal.set(Calendar.MINUTE, timePicker.getCurrentMinute());
				}
				
				synchronized (AndroidDateFieldUI.this) {
					result.value = cal.getTime();
					AndroidDateFieldUI.this.notify();
				}
			}
		});
		
		synchronized (AndroidDateFieldUI.this) {
			if (result.value == null) {
				try {
					wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

		return (Date) result.value;
	}
	
	private TimePicker createTimePicker() {
		TimePicker result = new TimePicker(activity);
		result.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() {

			public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
				AndroidFormUI.AndroidListView formList = (AndroidFormUI.AndroidListView) getParent();
				if (formList != null) {
					ItemStateListener listener = formList.getUI().getItemStateListener();
					if (listener != null) {
						listener.itemStateChanged(dateField);
					}
				}
			}
			
		});
		
		return result;
	}
	
	private DatePicker createDatePicker() {
		DatePicker result = new DatePicker(activity);
		result.init(1970, 1, 1, new DatePicker.OnDateChangedListener() {

			public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
				AndroidFormUI.AndroidListView formList = (AndroidFormUI.AndroidListView) getParent();
				if (formList != null) {
					ItemStateListener listener = formList.getUI().getItemStateListener();
					if (listener != null) {
						listener.itemStateChanged(dateField);
					}
				}
			}
			
		});
		
		return result;
	}	

} public class AndroidFormUI extends AndroidDisplayableUI implements FormUI {

	private ScrollView scrollView;
	
	private AndroidListView listView;
	
	private ItemStateListener itemStateListener;
	
	public AndroidFormUI(final MicroEmulatorActivity activity, Form form) {
		super(activity, form, true);
		
		activity.post(new Runnable() {
			public void run() {
				scrollView = new ScrollView(activity);
				((LinearLayout) AndroidFormUI.this.view).addView(scrollView);
				listView = new AndroidListView(activity, AndroidFormUI.this);
				listView.setOrientation(LinearLayout.VERTICAL);
				listView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));
				scrollView.addView(listView);

				invalidate();
			}
		});		
	}
	
	//
	// FormUI
	//
	
	private int appendTransfer;

	public int append(final ItemUI item) {
		if (activity.isActivityThread()) {
			appendTransfer = doAppend(item);
		} else {
			appendTransfer = Integer.MIN_VALUE;
			activity.post(new Runnable() {
				public void run() {
					synchronized (AndroidFormUI.this) {
						appendTransfer = doAppend(item);
						AndroidFormUI.this.notify();
					}
				}
			});

			synchronized (AndroidFormUI.this) {
				if (appendTransfer == Integer.MIN_VALUE) {
					try {
						wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
		
		return appendTransfer;
	}
	
	private int doAppend(ItemUI item) {
		if (item instanceof AndroidCustomItemUI) {
			// SurfaceView cannot be put inside the ScrollView
			if (((LinearLayout) AndroidFormUI.this.view).indexOfChild(scrollView) != -1) {
				scrollView.removeAllViews();
				((LinearLayout) AndroidFormUI.this.view).removeView(scrollView);
				((LinearLayout) AndroidFormUI.this.view).addView(listView);
			}		
		}
		listView.addView((View) item);
		
		return listView.getChildCount() - 1;
	}
	 
	public void delete(final int itemNum) {
		activity.post(new Runnable() {
			public void run() {
				listView.removeViewAt(itemNum);				
			}
		});
	}
	 
	public void deleteAll() {
		activity.post(new Runnable() {
			public void run() {
				listView.removeAllViews();				
			}
		});
	}
	 
	public void insert(final int itemNum, final ItemUI item) {
		activity.post(new Runnable() {
			public void run() {
				listView.addView((View) item, itemNum);
			}
		});
	}

	public void set(int itemNum, ItemUI item) {
		System.out.println("set(int itemNum, Item item)");
	}
	
	public void setItemStateListener(ItemStateListener itemStateListener) {
		this.itemStateListener = itemStateListener;
	}
	
	public ItemStateListener getItemStateListener() {
		return itemStateListener;
	}
	
	class AndroidListView extends LinearLayout {
		
		private AndroidFormUI ui;

		public AndroidListView(Context context, AndroidFormUI ui) {
			super(context);
			
			this.ui = ui;
		}
	
		public AndroidFormUI getUI() {
			return ui;
		}
		
	}

} public class AndroidGaugeUI extends LinearLayout implements GaugeUI {

	private MicroEmulatorActivity activity;
	
	private TextView labelView;

	private SeekBar seekBar;
	
	public AndroidGaugeUI(final MicroEmulatorActivity activity, final Gauge gauge) {
		super(activity);
		
		this.activity = activity;

		activity.post(new Runnable() {
			public void run() {
				setOrientation(LinearLayout.VERTICAL);
		//		setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));
				
				labelView = new TextView(activity);
				labelView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
				labelView.setTextAppearance(labelView.getContext(), android.R.style.TextAppearance_Large);
				addView(labelView);
				
				seekBar = new SeekBar(activity);
				seekBar.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));
				seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

					public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
						AndroidFormUI.AndroidListView formList = (AndroidFormUI.AndroidListView) getParent();
						if (formList != null) {
							ItemStateListener listener = formList.getUI().getItemStateListener();
							if (listener != null) {
								listener.itemStateChanged(gauge);
							}
						}
					}

					public void onStartTrackingTouch(SeekBar seekBar) {
					}

					public void onStopTrackingTouch(SeekBar seekBar) {
					}
					
				});
				addView(seekBar);
				
				setLabel(gauge.getLabel());
			}
		});
	}

	public void setDefaultCommand(Command cmd) {
	}

	public void setLabel(final String label) {
		activity.post(new Runnable() {
			public void run() {
				labelView.setText(label);
			}
		});
	}

	public void setValue(final int value) {
		activity.post(new Runnable() {
			public void run() {
				seekBar.setProgress(value);
			}
		});
	}
	
	private int getValueTransfer;

	public int getValue() {
		if (activity.isActivityThread()) {
			getValueTransfer = seekBar.getProgress();
		} else {
			getValueTransfer = Integer.MIN_VALUE;
			activity.post(new Runnable() {
				public void run() {
					synchronized (AndroidGaugeUI.this) {
						getValueTransfer = seekBar.getProgress();
						AndroidGaugeUI.this.notify();
					}
				}
			});

			synchronized (AndroidGaugeUI.this) {
				if (getValueTransfer == Integer.MIN_VALUE) {
					try {
						wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
		
		return getValueTransfer;
	}

	public void setMaxValue(final int maxValue) {
		activity.post(new Runnable() {
			public void run() {
				seekBar.setMax(maxValue);
			}
		});
	}

} public class AndroidImageStringItemUI extends LinearLayout implements ImageStringItemUI {

	private MicroEmulatorActivity activity;
	
	private TextView labelView;
	
	private ImageView imageView;

	private TextView textView;
	
	private Command defaultCommand;
	
	public AndroidImageStringItemUI(final MicroEmulatorActivity activity, final Item item) {
		super(activity);
		
		this.activity = activity;
		
		setOrientation(LinearLayout.VERTICAL);
		setFocusable(false);
		setFocusableInTouchMode(false);

		labelView = new TextView(activity);
		labelView.setFocusable(false);
		labelView.setFocusableInTouchMode(false);
		labelView.setLayoutParams(new LinearLayout.LayoutParams(
				LinearLayout.LayoutParams.FILL_PARENT,
				LinearLayout.LayoutParams.WRAP_CONTENT));
		labelView.setTextAppearance(labelView.getContext(),
				android.R.style.TextAppearance_Large);
		labelView.setVisibility(GONE);
		addView(labelView);

		textView = new TextView(activity);
		if (item instanceof StringItem && ((StringItem) item).getAppearanceMode() == Item.BUTTON) {
			textView.setClickable(true);
			textView.setOnClickListener(new View.OnClickListener() {

				public void onClick(View v) {
					if (defaultCommand != null) {
						MIDletBridge.getMIDletAccess().getDisplayAccess().commandAction(defaultCommand, item);
					}
				}

			});
		}
		textView.setLayoutParams(new LinearLayout.LayoutParams(
				LinearLayout.LayoutParams.FILL_PARENT,
				LinearLayout.LayoutParams.WRAP_CONTENT));
		addView(textView);

		if (item instanceof ImageItem && ((ImageItem) item).getAppearanceMode() == Item.BUTTON) {
			imageView = new ImageButton(activity);
			imageView.setClickable(true);
			imageView.setOnClickListener(new View.OnClickListener() {

				public void onClick(View v) {
					if (defaultCommand != null) {
						MIDletBridge.getMIDletAccess().getDisplayAccess().commandAction(defaultCommand, item);
					}
				}

			});
		} else {
			imageView = new ImageView(activity);
		}
		imageView.setVisibility(GONE);
		addView(imageView);

		setLabel(item.getLabel());
	}
	
	public void setDefaultCommand(Command cmd) {
		this.defaultCommand = cmd;
	}

	public void setLabel(final String label) {
		activity.post(new Runnable() {
			public void run() {
				if (label == null) {
					labelView.setVisibility(GONE);
				} else {
					labelView.setVisibility(VISIBLE);
					labelView.setText(label);
				}
			}
		});
	}

	public void setImage(final Image image) {
		activity.post(new Runnable() {
			public void run() {
				if (image == null) {
					imageView.setVisibility(GONE);
					imageView.setImageBitmap(null);
				} else {
					imageView.setVisibility(VISIBLE);
					if (image.isMutable()) {
						imageView.setImageBitmap(((AndroidMutableImage) image).getBitmap());
					} else {
						imageView.setImageBitmap(((AndroidImmutableImage) image).getBitmap());
					}
				}
			}
		});
	}

	public void setText(final String text) {
		activity.post(new Runnable() {
			public void run() {
				textView.setText(text);
			}
		});
	}

} public class AndroidTextBoxUI extends AndroidDisplayableUI implements TextBoxUI {
	
	private EditText editView;
	
	public AndroidTextBoxUI(final MicroEmulatorActivity activity, final TextBox textBox) {		
		super(activity, textBox, true);		
		
		activity.post(new Runnable() {
			public void run() {
				editView = new EditText(activity) {

					@Override
					public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
						Configuration conf = Resources.getSystem().getConfiguration();
						if (conf.hardKeyboardHidden != Configuration.HARDKEYBOARDHIDDEN_NO) {
							InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
                            imm.showSoftInput(this, 0);
						}
						
						return super.onCreateInputConnection(outAttrs);
					}
					
				};
				editView.setText(textBox.getString());
				editView.setGravity(Gravity.TOP);
				editView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));
				int constraints = textBox.getConstraints();
				if ((constraints & TextField.CONSTRAINT_MASK) == TextField.URL) {
					editView.setSingleLine(true);
				} else if ((constraints & TextField.CONSTRAINT_MASK) == TextField.NUMERIC) {
					editView.setSingleLine(true);
					editView.setInputType(InputType.TYPE_CLASS_NUMBER);
				} else if ((constraints & TextField.CONSTRAINT_MASK) == TextField.DECIMAL) {
					editView.setSingleLine(true);
					editView.setInputType(
							InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED | InputType.TYPE_NUMBER_FLAG_DECIMAL);
                } else if ((constraints & TextField.CONSTRAINT_MASK) == TextField.PHONENUMBER) {
                    editView.setSingleLine(true);
                    editView.setInputType(InputType.TYPE_CLASS_PHONE);
				}
				if ((constraints & TextField.PASSWORD) != 0) {
					editView.setTransformationMethod(PasswordTransformationMethod.getInstance());
					editView.setTypeface(Typeface.MONOSPACE);
				}
				editView.addTextChangedListener(new TextWatcher() {

					private String previousText;
					
					public void afterTextChanged(Editable s) {
					}

					public void beforeTextChanged(CharSequence s, int start, int count, int after) {
						previousText = s.toString();
					}

					public void onTextChanged(CharSequence s, int start, int before, int count) {
						if (s.toString().length() <= textBox.getMaxSize()
								&& InputMethod.validate(s.toString(), textBox.getConstraints())) {
						} else {
							editView.setText(previousText);
							editView.setSelection(start);
						}
					}

				});
				((LinearLayout) view).addView(editView);
				
				invalidate();
			}
		});		
	}
	
	@Override
	public void hideNotify() {
		activity.post(new Runnable() {
			public void run() {
				InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(editView.getWindowToken(), 0);
			}
		});
	}

	//
	// TextBoxUI
	//
	
	public int getCaretPosition() {
		return editView.getSelectionStart();
	}
	
	private String getStringTransfer;

	public String getString() {
		if (activity.isActivityThread()) {
			getStringTransfer = editView.getText().toString();
		} else {
			getStringTransfer = null;
			activity.post(new Runnable() {
				public void run() {
					synchronized (AndroidTextBoxUI.this) {
						getStringTransfer = editView.getText().toString();
						AndroidTextBoxUI.this.notify();
					}
				}
			});

			synchronized (AndroidTextBoxUI.this) {
				if (getStringTransfer == null) {
					try {
						wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
		
		return getStringTransfer;
	}

	public void setString(final String text) {
		activity.post(new Runnable() {
			public void run() {
				editView.setText(text);
				if (text != null) {
					editView.setSelection(text.length());
				}
			}
		});
	}

	public void insert(final String text, final int position) {
		activity.post(new Runnable() {
			public void run() {
				String newtext = getString();
				if (position > 0) {
					newtext = newtext.substring(0, position) + text + newtext.substring(position);
				}
				else {
					newtext = text + newtext;
				}
				editView.setText(newtext);
				editView.setSelection(position);
			}
		});
	}

	public void delete(final int offset, final int length) {
		activity.post(new Runnable() {
			public void run() {
				String newtext = getString();
				if (offset > 0) {
					newtext = newtext.substring(0, offset) + newtext.substring(offset + length);
				}
				else {
					newtext = newtext.substring(length);
				}
				editView.setText(newtext);
				editView.setSelection(offset);
			}
		});
	}

} public class AndroidTextFieldUI extends LinearLayout implements TextFieldUI {
	
	private MicroEmulatorActivity activity;
	
	private TextView labelView;
	
	private EditText editView;
	
	public AndroidTextFieldUI(final MicroEmulatorActivity activity, final TextField textField) {
		super(activity);
		
		this.activity = activity;
		
		activity.post(new Runnable() {
			public void run() {
				setOrientation(LinearLayout.VERTICAL);
				setFocusable(false);
				setFocusableInTouchMode(false);
		//		setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));
				
				labelView = new TextView(activity);
				labelView.setFocusable(false);
				labelView.setFocusableInTouchMode(false);
				labelView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
				labelView.setTextAppearance(labelView.getContext(), android.R.style.TextAppearance_Large);
				addView(labelView);
				
				editView = new EditText(activity);
				editView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));
				editView.addTextChangedListener(new TextWatcher() {

					private String previousText;
					
					public void afterTextChanged(Editable s) {
					}

					public void beforeTextChanged(CharSequence s, int start, int count, int after) {
						previousText = s.toString();
					}

					public void onTextChanged(CharSequence s, int start, int before, int count) {
						if (s.toString().length() <= textField.getMaxSize()
								&& InputMethod.validate(s.toString(), textField.getConstraints())) {
							if (textField != null) {
								AndroidFormUI.AndroidListView formList = (AndroidFormUI.AndroidListView) getParent();
								if (formList != null) {
									ItemStateListener listener = formList.getUI().getItemStateListener();
									if (listener != null) {
										listener.itemStateChanged(textField);
									}
								}
							}
						} else {
							editView.setText(previousText);
							editView.setSelection( start );
						}
					}

				});
				addView(editView);
				
				setLabel(textField.getLabel());
			}
		});
	}

	public void setDefaultCommand(Command cmd) {
	}

	public void setLabel(final String label) {
		activity.post(new Runnable() {
			public void run() {
				labelView.setText(label);
			}
		});
	}

	public void setConstraints(final int constraints) {
		activity.post(new Runnable() {
			public void run() {
                if ((constraints & TextField.CONSTRAINT_MASK) == TextField.URL) {
                    editView.setSingleLine(true);
                } else if ((constraints & TextField.CONSTRAINT_MASK) == TextField.NUMERIC) {
                    editView.setSingleLine(true);
                    editView.setInputType(InputType.TYPE_CLASS_NUMBER);
                } else if ((constraints & TextField.CONSTRAINT_MASK) == TextField.DECIMAL) {
                    editView.setSingleLine(true);
                    editView.setInputType(
                            InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED | InputType.TYPE_NUMBER_FLAG_DECIMAL);
                } else if ((constraints & TextField.CONSTRAINT_MASK) == TextField.PHONENUMBER) {
                    editView.setSingleLine(true);
                    editView.setInputType(InputType.TYPE_CLASS_PHONE);
                }
                if ((constraints & TextField.PASSWORD) != 0) {
                    editView.setTransformationMethod(PasswordTransformationMethod.getInstance());
                    editView.setTypeface(Typeface.MONOSPACE);
                }
			}
		});
	}

	public void setString(final String text) {
		activity.post(new Runnable() {
			public void run() {
				editView.setText(text);
			}
		});
	}

	private String getStringTransfer;

	public String getString() {
		if (activity.isActivityThread()) {
			getStringTransfer = editView.getText().toString();
		} else {
			getStringTransfer = null;
			activity.post(new Runnable() {
				public void run() {
					synchronized (AndroidTextFieldUI.this) {
						getStringTransfer = editView.getText().toString();
						AndroidTextFieldUI.this.notify();
					}
				}
			});

			synchronized (AndroidTextFieldUI.this) {
				if (getStringTransfer == null) {
					try {
						wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
		
		return getStringTransfer;
	}
	
}
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.ChoiceGroup;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.DateField;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.TextBox;
import javax.microedition.lcdui.TextField;
import org.microemu.CustomItemAccess;
import org.microemu.android.MicroEmulatorActivity;
import org.microemu.android.device.ui.AndroidAlertUI;
import org.microemu.android.device.ui.AndroidCanvasUI;
import org.microemu.android.device.ui.AndroidChoiceGroupUI;
import org.microemu.android.device.ui.AndroidCommandUI;
import org.microemu.android.device.ui.AndroidCustomItemUI;
import org.microemu.android.device.ui.AndroidDateFieldUI;
import org.microemu.android.device.ui.AndroidFormUI;
import org.microemu.android.device.ui.AndroidGaugeUI;
import org.microemu.android.device.ui.AndroidImageStringItemUI;
import org.microemu.android.device.ui.AndroidListUI;
import org.microemu.android.device.ui.AndroidTextBoxUI;
import org.microemu.android.device.ui.AndroidTextFieldUI;
import org.microemu.device.Device;
import org.microemu.device.DeviceDisplay;
import org.microemu.device.EmulatorContext;
import org.microemu.device.FontManager;
import org.microemu.device.InputMethod;
import org.microemu.device.ui.AlertUI;
import org.microemu.device.ui.CanvasUI;
import org.microemu.device.ui.ChoiceGroupUI;
import org.microemu.device.ui.CommandUI;
import org.microemu.device.ui.CustomItemUI;
import org.microemu.device.ui.DateFieldUI;
import org.microemu.device.ui.EventDispatcher;
import org.microemu.device.ui.FormUI;
import org.microemu.device.ui.GaugeUI;
import org.microemu.device.ui.ImageStringItemUI;
import org.microemu.device.ui.ListUI;
import org.microemu.device.ui.TextBoxUI;
import org.microemu.device.ui.TextFieldUI;
import org.microemu.device.ui.UIFactory;
import android.content.Context;
import android.os.Vibrator;
/**
 *  MicroEmulator
 *  Copyright (C) 2008 Bartek Teodorczyk <barteo@barteo.net>
 *
 *  It is licensed under the following two licenses as alternatives:
 *    1. GNU Lesser General Public License (the "LGPL") version 2.1 or any newer version
 *    2. Apache License (the "AL") Version 2.0
 *
 *  You may not use this file except in compliance with at least one of
 *  the above two licenses.
 *
 *  You may obtain a copy of the LGPL at
 *      http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
 *
 *  You may obtain a copy of the AL at
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the LGPL or the AL for the specific language governing permissions and
 *  limitations.
 *
 *  @version $Id: AndroidDevice.java 2236 2009-12-07 09:49:31Z barteo@gmail.com $
 */

package org.microemu.android.device;





public class AndroidDevice implements Device {

	private EmulatorContext emulatorContext;
	
	private MicroEmulatorActivity activity;
	
	private UIFactory ui = new UIFactory() {

		public EventDispatcher createEventDispatcher(Display display) {
			EventDispatcher eventDispatcher = new EventDispatcher();
			Thread thread = new Thread(eventDispatcher, EventDispatcher.EVENT_DISPATCHER_NAME);
			thread.setDaemon(true);
			thread.start();

			return eventDispatcher;
		}

		public CommandUI createCommandUI(Command command) {
			return new AndroidCommandUI(activity, command);
		}

		public AlertUI createAlertUI(Alert alert) {
			return new AndroidAlertUI(activity, alert);
		}

		public CanvasUI createCanvasUI(Canvas canvas) {
			return new AndroidCanvasUI(activity, canvas);
		}
		
		public FormUI createFormUI(Form form) {
			return new AndroidFormUI(activity, form);
		}

		public ListUI createListUI(List list) {
			return new AndroidListUI(activity, list);
		}
		
		public TextBoxUI createTextBoxUI(TextBox textBox) {
			return new AndroidTextBoxUI(activity, textBox);
		}

		public ChoiceGroupUI createChoiceGroupUI(ChoiceGroup choiceGroup, int choiceType) {
			return new AndroidChoiceGroupUI(activity, choiceGroup, choiceType);
		}

		public CustomItemUI createCustomItemUI(CustomItemAccess customItemAccess) {
			return new AndroidCustomItemUI(activity, customItemAccess);
		}

		public DateFieldUI createDateFieldUI(DateField dateField) {
			return new AndroidDateFieldUI(activity, dateField);
		}
		
		public GaugeUI createGaugeUI(Gauge gauge) {
			return new AndroidGaugeUI(activity, gauge);