package me.guillaumin.android.osmtracker.util; import java.io.IOException; import java.util.HashMap; import me.guillaumin.android.osmtracker.OSMTracker; import me.guillaumin.android.osmtracker.R; import me.guillaumin.android.osmtracker.activity.TrackLogger; import me.guillaumin.android.osmtracker.layout.DisablableTableLayout; import me.guillaumin.android.osmtracker.layout.UserDefinedLayout; import me.guillaumin.android.osmtracker.listener.PageButtonOnClickListener; import me.guillaumin.android.osmtracker.listener.StillImageOnClickListener; import me.guillaumin.android.osmtracker.listener.TagButtonOnClickListener; import me.guillaumin.android.osmtracker.listener.TextNoteOnClickListener; import me.guillaumin.android.osmtracker.listener.VoiceRecOnClickListener; import me.guillaumin.android.osmtracker.service.resources.IconResolver; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.view.ViewGroup; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TableLayout; import android.widget.TableRow; /** * Reads an user defined layout, using a pull parser, * and instantiate corresponding objects (Layouts, Buttons) * * @author Nicolas Guillaumin * */ public class UserDefinedLayoutReader { @SuppressWarnings("unused") private static final String TAG = UserDefinedLayoutReader.class.getSimpleName(); /** * Map containing parsed layouts */ private HashMap<String, ViewGroup> layouts = new HashMap<String, ViewGroup>(); /** * Source parser */ private XmlPullParser parser; /** * Context for accessing resources */ private Context context; /** * The user defined Layout */ private UserDefinedLayout userDefinedLayout; /** * {@link IconResolver} to retrieve button icons. */ private IconResolver iconResolver; /** * Listener bound to text note buttons */ private TextNoteOnClickListener textNoteOnClickListener; /** * Listener bound to voice record buttons */ private VoiceRecOnClickListener voiceRecordOnClickListener; /** * Lister bound to picture buttons */ private StillImageOnClickListener stillImageOnClickListener; /** * {@link Resources} to retrieve String resources */ private Resources resources; /** * representing ScreenOrientation * see {@link Configuration.orientation} */ private int orientation; private static final int ICON_POS_AUTO = 0; private static final int ICON_POS_TOP = 1; private static final int ICON_POS_RIGHT = 2; private static final int ICON_POS_BOTTOM = 3; private static final int ICON_POS_LEFT = 4; /** * the icon position for the current layout */ private int currentLayoutIconPos = UserDefinedLayoutReader.ICON_POS_AUTO; /** * Current track id */ private long currentTrackId; /** * Constructor * * @param udl * User defined layout * @param c * Context for accessing resources * @param tl * TrackLogger activity * @param trackId * Current track id * @param input * Parser for reading layout * @param ir * Icon resolver to use to fetch icons */ public UserDefinedLayoutReader(UserDefinedLayout udl, Context c, TrackLogger tl, long trackId, XmlPullParser input, IconResolver ir) { parser = input; context = c; resources = context.getResources(); userDefinedLayout = udl; iconResolver = ir; currentTrackId = trackId; orientation = resources.getConfiguration().orientation; // Initialize listeners which will be bound to buttons textNoteOnClickListener = new TextNoteOnClickListener(tl); voiceRecordOnClickListener = new VoiceRecOnClickListener(tl); stillImageOnClickListener = new StillImageOnClickListener(tl); } /** * Parses an XML layout * * @return An HashMap of {@link ViewGroup} with layout name as key. * @throws XmlPullParserException * @throws IOException */ public HashMap<String, ViewGroup> parseLayout() throws XmlPullParserException, IOException { int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { switch (eventType) { case XmlPullParser.START_TAG: String tagName = parser.getName(); if (XmlSchema.TAG_LAYOUT.equals(tagName)) { // <layout> tag has been encountered. Inflate this layout inflateLayout(); } break; case XmlPullParser.END_TAG: break; } eventType = parser.next(); } return layouts; } /** * Inflates a <layout> into a {@link TableLayout} * * @throws IOException * @throws XmlPullParserException */ private void inflateLayout() throws IOException, XmlPullParserException { String layoutName = parser.getAttributeValue(null, XmlSchema.ATTR_NAME); String layoutIconPosValue = parser.getAttributeValue(null, XmlSchema.ATTR_ICONPOS); // find out the correct icon position for this layout if(XmlSchema.ATTR_VAL_ICONPOS_TOP.equals(layoutIconPosValue)){ // TOP position this.currentLayoutIconPos = UserDefinedLayoutReader.ICON_POS_TOP; } else if (XmlSchema.ATTR_VAL_ICONPOS_RIGHT.equals(layoutIconPosValue)){ // RIGHT position this.currentLayoutIconPos = UserDefinedLayoutReader.ICON_POS_RIGHT; } else if (XmlSchema.ATTR_VAL_ICONPOS_BOTTOM.equals(layoutIconPosValue)){ // BOTTOM position this.currentLayoutIconPos = UserDefinedLayoutReader.ICON_POS_BOTTOM; } else if (XmlSchema.ATTR_VAL_ICONPOS_LEFT.equals(layoutIconPosValue)){ // LEFT position this.currentLayoutIconPos = UserDefinedLayoutReader.ICON_POS_LEFT; } else { // if no or an undefined value is given for the current layout // AUTO position depending on screen orientation this.currentLayoutIconPos = UserDefinedLayoutReader.ICON_POS_AUTO; } // Create a new table layout and set default parameters DisablableTableLayout tblLayout = new DisablableTableLayout(context); tblLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT, 1)); String currentTagName = null; while (!XmlSchema.TAG_LAYOUT.equals(currentTagName)) { int eventType = parser.next(); switch (eventType) { case XmlPullParser.START_TAG: String name = parser.getName(); if (XmlSchema.TAG_ROW.equals(name)) { // <row> tag has been encountered, inflates it inflateRow(tblLayout); } break; case XmlPullParser.END_TAG: currentTagName = parser.getName(); break; } } // Add the new inflated layout to the list layouts.put(layoutName, tblLayout); } /** * Inflates a <row> into a {@link TableRow} * * @param layout * {@link TableLayout} to rattach the row to * @throws XmlPullParserException * @throws IOException */ private void inflateRow(TableLayout layout) throws XmlPullParserException, IOException { TableRow tblRow = new TableRow(layout.getContext()); tblRow.setLayoutParams(new TableLayout.LayoutParams(TableLayout.LayoutParams.FILL_PARENT, TableLayout.LayoutParams.FILL_PARENT, 1)); String currentTagName = null; // int eventType = parser.next(); while (!XmlSchema.TAG_ROW.equals(currentTagName)) { int eventType = parser.next(); switch (eventType) { case XmlPullParser.START_TAG: String name = parser.getName(); if (XmlSchema.TAG_BUTTON.equals(name)) { // <button> tag has been encountered, inflates it. inflateButton(tblRow); } break; case XmlPullParser.END_TAG: currentTagName = parser.getName(); break; } } // Add the inflated table row to the current layout layout.addView(tblRow); } /** * Inflates a <button> * * @param row * The table row to attach the button to */ public void inflateButton(TableRow row) { Button button = new Button(row.getContext()); button.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.FILL_PARENT, TableRow.LayoutParams.FILL_PARENT, 1)); // TODO Use kind of ButtonFactory here String buttonType = parser.getAttributeValue(null, XmlSchema.ATTR_TYPE); Drawable buttonIcon = null; if (XmlSchema.ATTR_VAL_PAGE.equals(buttonType)) { // Page button button.setText(findLabel(parser.getAttributeValue(null, XmlSchema.ATTR_LABEL), resources)); buttonIcon = iconResolver.getIcon(parser.getAttributeValue(null, XmlSchema.ATTR_ICON)); button.setOnClickListener(new PageButtonOnClickListener(userDefinedLayout, parser.getAttributeValue(null, XmlSchema.ATTR_TARGETLAYOUT))); } else if (XmlSchema.ATTR_VAL_TAG.equals(buttonType)) { // Standard tag button button.setText(findLabel(parser.getAttributeValue(null, XmlSchema.ATTR_LABEL), resources)); buttonIcon = iconResolver.getIcon(parser.getAttributeValue(null, XmlSchema.ATTR_ICON)); button.setOnClickListener(new TagButtonOnClickListener(currentTrackId)); } else if (XmlSchema.ATTR_VAL_VOICEREC.equals(buttonType)) { // Voice record button button.setText(resources.getString(R.string.gpsstatus_record_voicerec)); buttonIcon = resources.getDrawable(R.drawable.voice_32x32); button.setOnClickListener(voiceRecordOnClickListener); } else if (XmlSchema.ATTR_VAL_TEXTNOTE.equals(buttonType)) { // Text note button button.setText(resources.getString(R.string.gpsstatus_record_textnote)); buttonIcon = resources.getDrawable(R.drawable.text_32x32); button.setOnClickListener(textNoteOnClickListener); } else if (XmlSchema.ATTR_VAL_PICTURE.equals(buttonType)) { // Picture button button.setText(resources.getString(R.string.gpsstatus_record_stillimage)); buttonIcon = resources.getDrawable(R.drawable.camera_32x32); button.setOnClickListener(stillImageOnClickListener); } // Where to draw the button's icon (depending on the current layout) switch(this.currentLayoutIconPos){ case UserDefinedLayoutReader.ICON_POS_TOP: // TOP position button.setCompoundDrawablesWithIntrinsicBounds(null, buttonIcon, null, null); break; case UserDefinedLayoutReader.ICON_POS_RIGHT: // RIGHT position button.setCompoundDrawablesWithIntrinsicBounds(null, null, buttonIcon, null); break; case UserDefinedLayoutReader.ICON_POS_BOTTOM: // BOTTOM position button.setCompoundDrawablesWithIntrinsicBounds(null, null, null, buttonIcon); break; case UserDefinedLayoutReader.ICON_POS_LEFT: // LEFT position button.setCompoundDrawablesWithIntrinsicBounds(buttonIcon, null, null, null); break; case UserDefinedLayoutReader.ICON_POS_AUTO: default: // if no or an undefined value is given for the current layout // AUTO position depending on screen orientation if(orientation == Configuration.ORIENTATION_LANDSCAPE){ // in landscape mode draw icon to the LEFT button.setCompoundDrawablesWithIntrinsicBounds(buttonIcon, null, null,null); }else{ // in portrait mode draw icon to the TOP button.setCompoundDrawablesWithIntrinsicBounds(null, buttonIcon, null, null); } break; } row.addView(button); } /** * Finds a label if it's a reference to an internal resource (@string/label) * @param text Resource reference or plain label * @param r {@link Resources} to lookup from * @return Plain label, or corresponding text extracted from {@link Resources} */ private String findLabel(String text, Resources r) { if (text != null) { if (text.startsWith("@")) { // Check if it's a resource identifier int resId = resources.getIdentifier(text.replace("@", ""), null, OSMTracker.PACKAGE_NAME); if (resId != 0) { return resources.getString(resId); } } } return text; } /** * XML Schema */ private static final class XmlSchema { public static final String TAG_LAYOUT = "layout"; public static final String TAG_ROW = "row"; public static final String TAG_BUTTON = "button"; public static final String ATTR_NAME = "name"; public static final String ATTR_TYPE = "type"; public static final String ATTR_LABEL = "label"; public static final String ATTR_TARGETLAYOUT = "targetlayout"; public static final String ATTR_ICON = "icon"; public static final String ATTR_ICONPOS = "iconpos"; public static final String ATTR_VAL_TAG = "tag"; public static final String ATTR_VAL_PAGE = "page"; public static final String ATTR_VAL_VOICEREC = "voicerec"; public static final String ATTR_VAL_TEXTNOTE = "textnote"; public static final String ATTR_VAL_PICTURE = "picture"; public static final String ATTR_VAL_ICONPOS_TOP = "top"; public static final String ATTR_VAL_ICONPOS_RIGHT = "right"; public static final String ATTR_VAL_ICONPOS_BOTTOM = "bottom"; public static final String ATTR_VAL_ICONPOS_LEFT = "left"; } }