/* Copyright (c) 2008 Bluendo S.r.L. * See about.html for details about license. * * $Id: DataFormScreen.java 1273 2009-03-13 15:57:53Z luca $ */ /** * */ package it.yup.screens; import lampiro.LampiroMidlet; import it.yup.util.ResourceIDs; import it.yup.util.ResourceManager; import it.yup.xmpp.DataFormListener; import it.yup.xmpp.packets.DataForm; import it.yup.xmpp.packets.DataForm.Field; import javax.microedition.lcdui.Choice; import javax.microedition.lcdui.ChoiceGroup; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.CommandListener; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.Form; import javax.microedition.lcdui.Item; import javax.microedition.lcdui.ItemCommandListener; import javax.microedition.lcdui.ItemStateListener; import javax.microedition.lcdui.List; import javax.microedition.lcdui.Spacer; import javax.microedition.lcdui.StringItem; import javax.microedition.lcdui.TextField; /** * <p>This class handles the data form input from the user, rendering a given Data * Form using the base controls offered by J2ME UI. * The input result is then sent to a DataFormListener that handles the form * outcome.</p> * * <p>DataForms are rendered as follows. * <ul> * <li><b>hidden</b>: are skipped.</li> * <li><b>boolean</b>: are rendered with a ChoiceGroup flagged with "MULTIPLE" * and with a single choice item that may be checked (true) or * unchecked (false).</li> * <li><b>list-multi and list-single</b>: they show a button that opens a separate * List, List is "EXCLUSIVE" (a single voice can be selected) or "MULTIPLE" * (more than one voice selected) resp. for list-single and list-multi.</li> * <li><b>jid-single</b>, <b>jid-multi</b>, <b>text-single</b>, <b>text-multi</b>, * <b>text-private</b>, <b>fixed</b>: are shown as a single TextField, * *-multi are split on '\n' chars when sending data; text-private are * flagged as PASSWORD fields, jid-single are flagged as EMAILADDRESS * fields. fixed are uneditable.</li> * </ul> * * All fields have a Label before if the DataForm field has one. Commands are * placed on the menu. Instructions, if present, make a command "instructions" * appear on the menu and that voice pops up an alert showing the instructions. * Field desc are ignored. * * At the bottom of the forms the button for the available actions are placed. * Available actions are passed via the method setActions() * * <i>TODO LIST: * <ul> * <li>list-single and list-multi should be changed: they should show a non * editable control that exposes the label and the current selected content * plus a button that spawns the pop-up screen for the selection</li> * <li>text-multi and jid-multi should open a separate TextBox item (note. on * SonyEricsson it seems that there's no difference between the two...).</li> * <li>jid-multi should be checked for emailaddress format (emailaddress is not * enforceable for multiline TextBoxes</li> * <li>check '\n' in fields that are not *-multi and pop up error.</li> * <li>add a voice "field description" on the menu (or place a button with (?)) * to honour the "desc" for each field.</li> * <li>Heuristics: when a form has a single list-* item or the list-* item has no * more than 2 or 3 options, there shouldn't be a need for a pop up screen. * </ul> * </i> * */ public class DataFormScreen extends Form implements CommandListener, ItemCommandListener, ItemStateListener { private static ResourceManager rm = ResourceManager.getManager("common", "en"); /** The handled data form */ private DataForm df; /** the listener to be notified of commands */ private DataFormListener dfl; /** the available actions */ private int actions; private Command cmd_submit = new Command(rm .getString(ResourceIDs.STR_SUBMIT), Command.ITEM, 1); private Command cmd_cancel = new Command(rm .getString(ResourceIDs.STR_CANCEL), Command.EXIT, 2); private Command cmd_prev = new Command(rm.getString(ResourceIDs.STR_PREV), Command.ITEM, 3); private Command cmd_next = new Command(rm.getString(ResourceIDs.STR_NEXT), Command.ITEM, 4); private Command cmd_delay = new Command(rm .getString(ResourceIDs.STR_FILLLATER), Command.EXIT, 3); private StringItem si_instructions = new StringItem(rm .getString(ResourceIDs.STR_INSTRUCTIONS), ""); private ChoiceGroup cg_show_instructions = new ChoiceGroup("", ChoiceGroup.MULTIPLE, new String[] { rm .getString(ResourceIDs.STR_HIDE) }, null); /** initial field offset (choice and instructions or just instructions) */ private int field_offset = 2; public DataFormScreen(DataForm df, DataFormListener dfl) { super(rm.getString(ResourceIDs.STR_FILL_FORM)); this.df = df; this.dfl = dfl; if (df.title != null) { setTitle(df.title); } addCommand(cmd_cancel); addCommand(cmd_delay); setCommandListener(this); actions = DataFormListener.CMD_SUBMIT | DataFormListener.CMD_CANCEL; cg_show_instructions.setLayout(Item.LAYOUT_SHRINK); cg_show_instructions.setLayout(Item.LAYOUT_NEWLINE_AFTER); si_instructions.setLayout(Item.LAYOUT_SHRINK); setItemStateListener(this); createControls(); } /** * Sets the available command buttons. Actions should be one of the CMD_* * flags defined in the DataFormListener interface. * @param cmds */ public void setActions(int _ac) { /* submit and cancel are always shown */ actions = _ac | DataFormListener.CMD_SUBMIT | DataFormListener.CMD_CANCEL; createControls(); } /** * Show the form, dynamically adding all the controls */ private void createControls() { deleteAll(); if (df.instructions != null) { si_instructions.setText(df.instructions); append(si_instructions); append(cg_show_instructions); field_offset = 2; } else { field_offset = 0; } for (int i = 0; i < df.fields.size(); i++) { DataForm.Field fld = (DataForm.Field) df.fields.elementAt(i); if (fld.type == DataForm.FLT_HIDDEN) { continue; } if (fld.type == DataForm.FLT_BOOLEAN) { // XXX: check how to render this ChoiceGroup cgrp = new ChoiceGroup(fld.label, Choice.MULTIPLE, new String[] { "Yes" }, null); if ("1".equals(fld.dValue) || "true".equals(fld.dValue)) { cgrp.setSelectedFlags(new boolean[] { true }); } append(cgrp); continue; } if (fld.type == DataForm.FLT_LISTMULTI || fld.type == DataForm.FLT_LISTSINGLE) { String title = (fld.label == null ? null : fld.label + "\n"); StringItem cgrp = new StringItem(title, "", Item.BUTTON); ListChoiceHandler lch = new ListChoiceHandler(cgrp, fld); cgrp.setDefaultCommand(new Command("Select", Item.BUTTON, Command.ITEM)); cgrp.setItemCommandListener(lch); cgrp.setLayout(Item.LAYOUT_EXPAND | Item.LAYOUT_NEWLINE_AFTER); append(cgrp); continue; } if (fld.type == DataForm.FLT_JIDSINGLE || fld.type == DataForm.FLT_TXTPRIV || fld.type == DataForm.FLT_TXTSINGLE || fld.type == DataForm.FLT_JIDMULTI || fld.type == DataForm.FLT_TXTMULTI || fld.type == DataForm.FLT_FIXED) { String title = (fld.label == null ? null : fld.label + "\n"); int flags = TextField.ANY; if (fld.type == DataForm.FLT_TXTPRIV) { flags |= TextField.PASSWORD; } if (fld.type == DataForm.FLT_JIDSINGLE) { flags |= TextField.EMAILADDR; } if (fld.type == DataForm.FLT_FIXED) { flags |= TextField.UNEDITABLE; } // XXX: Which the maximum allowed length? We use 1k for the moment TextField tf = new TextField(title, fld.dValue, 1024, flags); tf.setLayout(Item.LAYOUT_EXPAND | Item.LAYOUT_NEWLINE_AFTER); append(tf); continue; } } Spacer sp = new Spacer(10, 5); sp.setLayout(Item.LAYOUT_NEWLINE_AFTER | Item.LAYOUT_NEWLINE_BEFORE); append(sp); /* show actions. order is CANCEL, [PREV], [NEXT], SUBMIT */ if ((actions & DataFormListener.CMD_CANCEL) != 0) { setShowAction(cmd_cancel); } if ((actions & DataFormListener.CMD_PREV) != 0) { setShowAction(cmd_prev); } if ((actions & DataFormListener.CMD_NEXT) != 0) { setShowAction(cmd_next); } if ((actions & DataFormListener.CMD_SUBMIT) != 0) { setShowAction(cmd_submit); } } private void setShowAction(Command new_cmd) { StringItem cmd = new StringItem(null, new_cmd.getLabel(), Item.BUTTON); cmd.setDefaultCommand(new_cmd); cmd.addCommand(new_cmd); cmd.setItemCommandListener(this); cmd.setLayout(Item.LAYOUT_CENTER | Item.LAYOUT_SHRINK); append(cmd); } /** * Command handler */ public void commandAction(Command cmd, Displayable d) { int comm = -1; if (cmd == cmd_cancel) { comm = DataFormListener.CMD_CANCEL; } else if (cmd == cmd_submit) { comm = DataFormListener.CMD_SUBMIT; } else if (cmd == cmd_next) { comm = DataFormListener.CMD_NEXT; } else if (cmd == cmd_prev) { comm = DataFormListener.CMD_PREV; } else if (cmd == cmd_delay) { comm = DataFormListener.CMD_DELAY; } if (comm == -1) { /* ???? */ return; } fillForm(); dfl.execute(comm); } /** * Command handler for on-screen buttons */ public void commandAction(Command cmd, Item item) { commandAction(cmd, this); // int comm = -1; // // if(cmd == cmd_cancel) { // comm = DataFormListener.CMD_CANCEL; // } else if(cmd == cmd_submit) { // comm = DataFormListener.CMD_SUBMIT; // } else if(cmd == cmd_next) { // comm = DataFormListener.CMD_NEXT; // } else if(cmd == cmd_prev) { // comm = DataFormListener.CMD_PREV; // } else if(cmd == cmd_delay) { // comm = DataFormListener.CMD_DELAY; // } // // if(comm == -1) { // /* ???? */ // return; // } // // fillForm(); // dfl.execute(comm); } /** * Called when submit is pressed */ private void fillForm() { int fpos = -1; // XXX: here we could verify the required fields for (int i = 0; i < df.fields.size(); i++) { DataForm.Field fld = (DataForm.Field) df.fields.elementAt(i); if (fld.type == DataForm.FLT_HIDDEN) { continue; } fpos++; if (fld.type == DataForm.FLT_BOOLEAN) { ChoiceGroup cgrp = (ChoiceGroup) get(fpos + field_offset); fld.dValue = (cgrp.isSelected(0) ? "true" : "false"); continue; } if (fld.type == DataForm.FLT_LISTMULTI || fld.type == DataForm.FLT_LISTSINGLE) { /* nothing to do, dValue is already set byt the ListHandler */ continue; } if (fld.type == DataForm.FLT_JIDSINGLE || fld.type == DataForm.FLT_TXTPRIV || fld.type == DataForm.FLT_TXTSINGLE || fld.type == DataForm.FLT_JIDMULTI || fld.type == DataForm.FLT_TXTMULTI || fld.type == DataForm.FLT_FIXED) { TextField tf = (TextField) get(fpos + field_offset); fld.dValue = tf.getString(); continue; } } } public class ListChoiceHandler implements ItemCommandListener, CommandListener { /** the item referring to */ private StringItem itm; /** the field referring to */ private Field fld; /** the list to display */ private List list; public ListChoiceHandler(StringItem _itm, Field _fld) { itm = _itm; fld = _fld; String chs = rm.getString(ResourceIDs.STR_CHOOSE); StringBuffer stext = new StringBuffer(); int snum = 0; for (int j = 0; j < fld.options.size(); j++) { String[] opt = (String[]) fld.options.elementAt(j); if (fld.dValue != null && fld.dValue.indexOf(opt[0]) != -1) { snum++; if (snum > 1) { stext.append("\n"); } stext.append(opt[1]); } } itm.setText(stext.length() == 0 ? chs : stext.toString()); } /** * Handler for StringItem command. */ public void commandAction(Command _cmd, Item _itm) { int ctype = (fld.type == DataForm.FLT_LISTMULTI ? Choice.MULTIPLE : Choice.EXCLUSIVE); list = new List(fld.label, ctype); for (int j = 0; j < fld.options.size(); j++) { String[] opt = (String[]) fld.options.elementAt(j); list.append(opt[1], null); } list.addCommand(new Command(rm.getString(ResourceIDs.STR_ACCEPT), Command.ITEM, 1)); list.setCommandListener(this); LampiroMidlet.disp.setCurrent(list); } /** * Handler for List */ public void commandAction(Command _cmd, Displayable _disp) { boolean[] svals = new boolean[fld.options.size()]; list.getSelectedFlags(svals); StringBuffer stext = new StringBuffer(); StringBuffer dtext = new StringBuffer(); int scount = 0; for (int i = 0; i < svals.length; i++) { if (svals[i]) { scount++; String[] opt = (String[]) fld.options.elementAt(i); if (scount > 1) { stext.append(" / "); dtext.append("\n"); } stext.append(opt[1]); dtext.append(opt[0]); } } if (stext.length() == 0) { itm.setText(rm.getString(ResourceIDs.STR_CHOOSE)); fld.dValue = ""; } else { itm.setText(stext.toString()); fld.dValue = dtext.toString(); } LampiroMidlet.disp.setCurrent(DataFormScreen.this); } } public void itemStateChanged(Item item) { if (cg_show_instructions == item) { String choice = cg_show_instructions.getString(0); if (choice.startsWith("Show")) { cg_show_instructions.delete(0); cg_show_instructions.append(rm.getString(ResourceIDs.STR_HIDE), null); insert(0, si_instructions); field_offset++; } else { cg_show_instructions.delete(0); cg_show_instructions.append(rm .getString(ResourceIDs.STR_SHOW_INSTR), null); delete(0); field_offset--; } } } }