au.org.ala.fielddata.mobile.SurveyBuilder.java Source code

Java tutorial

Introduction

Here is the source code for au.org.ala.fielddata.mobile.SurveyBuilder.java

Source

/*******************************************************************************
 * Copyright (C) 2010 Atlas of Living Australia
 * All Rights Reserved.
 *  
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 *  
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 ******************************************************************************/
package au.org.ala.fielddata.mobile;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import android.support.v4.app.FragmentActivity;
import android.text.InputType;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import au.org.ala.fielddata.mobile.nrmplus.R;
import au.org.ala.fielddata.mobile.model.Attribute;
import au.org.ala.fielddata.mobile.model.Attribute.AttributeOption;
import au.org.ala.fielddata.mobile.model.Record;
import au.org.ala.fielddata.mobile.model.Survey;
import au.org.ala.fielddata.mobile.model.SurveyViewModel;
import au.org.ala.fielddata.mobile.ui.CategorizedSpinner;
import au.org.ala.fielddata.mobile.ui.MultiSpinner;

/**
 * The SurveyBuilder is responsible for selecting and creating the appropriate
 * input widget for a particular Attribute type.
 */
public class SurveyBuilder {

    private FragmentActivity viewContext;
    private SurveyViewModel model;
    private BinderManager binder;

    public SurveyBuilder(FragmentActivity viewContext, SurveyViewModel model, BinderManager binder) {
        this.viewContext = viewContext;
        this.model = model;
        this.binder = binder;
    }

    public void buildSurveyForm(View page, int pageNum) {

        TableLayout tableLayout = (TableLayout) page.findViewById(R.id.surveyGrid);
        List<Attribute> pageAttributes = model.getPage(pageNum);

        int rowCount = pageAttributes.size();
        if (pageNum == 0) {
            TableRow row = new TableRow(viewContext);
            buildSurveyName(model.getSurvey(), row);
            addRow(tableLayout, row);
        }
        View previousView = null;
        for (int i = 0; i < rowCount; i++) {
            TableRow row = new TableRow(viewContext);

            Attribute attribute = pageAttributes.get(i);

            View inputView = buildFields(attribute, row);

            configureKeyboardBindings(previousView, inputView);
            binder.configureBindings(inputView, attribute);

            addRow(tableLayout, row);
            previousView = inputView;
        }

    }

    /**
     * The purpose of this method is to configure EditText widgets so that:
     * 1) If the next widget is an EditText, the keyboard has a "next" key.
     * 2) If the next widget is not an EditText, the keyboard has a "done" key.
     * This is to prevent the input sequence from jumping from a text field
     * to the next text field, skipping other widgets (such as spinners) in the
     * process. 
     * @param previousView the previously created widget.
     * @param inputView the widget created for the current field.
     */
    private void configureKeyboardBindings(View previousView, View inputView) {
        if ((previousView instanceof EditText) && !(inputView instanceof EditText)) {
            int imeOptions = ((EditText) previousView).getImeOptions();
            ((EditText) previousView).setImeOptions(imeOptions | EditorInfo.IME_ACTION_DONE);
        }
    }

    private void addRow(TableLayout tableLayout, TableRow row) {
        TableRow.LayoutParams params = new TableRow.LayoutParams();
        params.setMargins(5, 5, 10, 10);
        params.width = TableRow.LayoutParams.MATCH_PARENT;
        params.height = TableRow.LayoutParams.WRAP_CONTENT;
        tableLayout.addView(row, params);
    }

    public View buildFields(Attribute attribute, ViewGroup parent) {
        LinearLayout layout = (LinearLayout) viewContext.getLayoutInflater().inflate(R.layout.label_and_field,
                parent);
        ViewGroup container = (ViewGroup) layout.getChildAt(0);
        buildLabel(attribute, container);
        viewContext.getLayoutInflater().inflate(R.layout.error_label, container).findViewById(R.id.errorLabel);
        View inputField = buildInput(attribute, container);

        return inputField;
    }

    public View buildInput(Attribute attribute, ViewGroup parent) {
        Record record = model.getRecord();
        View view;

        switch (attribute.getType()) {
        case STRING:
            view = buildEditText(InputType.TYPE_CLASS_TEXT, parent);
            break;
        case INTEGER:
        case NUMBER:
            view = buildEditText(InputType.TYPE_CLASS_NUMBER, parent);
            break;
        case DECIMAL:
        case ACCURACY:
            view = buildEditText(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL, parent);
            break;
        case MULTI_SELECT:
        case POINT_SOURCE:
        case STRING_WITH_VALID_VALUES:
            view = buildSpinner(attribute, parent);
            break;
        case CATEGORIZED_MULTI_SELECT:
            view = buildCategorizedSpinner(attribute, parent);
            break;
        case NOTES:
            view = buildEditText(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE, parent);
            break;
        case WHEN:
            view = buildDatePicker(attribute, record, parent);
            break;
        case DWC_TIME:
            view = buildTimePicker(attribute, record, parent);
            break;
        case SPECIES_P:
            view = buildSpeciesPicker(attribute, parent);
            break;
        case POINT:
            view = buildLocationPicker(attribute, parent);
            break;
        case IMAGE:
            view = buildImagePicker(attribute, parent);
            break;
        case SINGLE_CHECKBOX:
            view = buildSingleCheckbox(attribute, parent);
            break;
        case MULTI_CHECKBOX:
            view = buildMultiSpinner(attribute, parent);
            break;
        default:
            view = buildEditText(InputType.TYPE_CLASS_TEXT, parent);
            break;
        }
        return view;
    }

    public View buildLabel(Attribute attribute, ViewGroup parent) {
        ViewGroup view = (ViewGroup) viewContext.getLayoutInflater().inflate(R.layout.label_text_view, parent);
        TextView textView = (TextView) view.findViewById(R.id.fieldLabel);

        textView.setText(Utils.bold(attribute.description));
        return view;
    }

    private View buildEditText(int inputType, ViewGroup parent) {

        ViewGroup row = (ViewGroup) viewContext.getLayoutInflater().inflate(R.layout.input_text_view, parent);
        EditText editTextview = (EditText) getFirstNonLabelView(row);
        editTextview.setInputType(inputType);
        return editTextview;
    }

    public View buildDatePicker(Attribute attribute, Record record, ViewGroup parent) {

        ViewGroup row = (ViewGroup) viewContext.getLayoutInflater().inflate(R.layout.date_field, parent);
        return getFirstNonLabelView(row);
    }

    public View buildTimePicker(Attribute attribute, Record record, ViewGroup parent) {

        ViewGroup row = (ViewGroup) viewContext.getLayoutInflater().inflate(R.layout.date_field, parent);
        return getFirstNonLabelView(row);
    }

    public View buildSpeciesPicker(Attribute attribute, ViewGroup parent) {

        ViewGroup row = (ViewGroup) viewContext.getLayoutInflater().inflate(R.layout.species_row, parent);

        return getFirstNonLabelView(row);
    }

    public View buildLocationPicker(Attribute attribute, ViewGroup parent) {
        ViewGroup row = (ViewGroup) viewContext.getLayoutInflater().inflate(R.layout.read_only_location, parent);
        return row;
    }

    public View buildImagePicker(Attribute attribute, ViewGroup parent) {

        ViewGroup row = (ViewGroup) viewContext.getLayoutInflater().inflate(R.layout.image_selection, parent);
        return getFirstNonLabelView(row);
    }

    public View buildSingleCheckbox(Attribute attribute, ViewGroup parent) {
        ViewGroup row = (ViewGroup) viewContext.getLayoutInflater().inflate(R.layout.input_single_checkbox_view,
                parent);
        CheckBox checkbox = (CheckBox) getFirstNonLabelView(row);
        return checkbox;
    }

    private Spinner buildSpinner(Attribute attribute, ViewGroup parent) {

        ViewGroup row = (ViewGroup) viewContext.getLayoutInflater().inflate(R.layout.input_spinner_view, parent);

        Spinner spinner = (Spinner) row.findViewById(R.id.spinner);
        spinner.setPrompt("Select " + attribute.description);
        ArrayList<AttributeOption> options = new ArrayList<Attribute.AttributeOption>(attribute.options.length + 1);
        options.addAll(Arrays.asList(attribute.options));

        ArrayAdapter<AttributeOption> adapter = new ArrayAdapter<AttributeOption>(viewContext,
                R.layout.multiline_spinner_item, options);
        adapter.setDropDownViewResource(R.layout.multiline_spinner_dropdown_item);
        spinner.setAdapter(adapter);

        return spinner;
    }

    private Spinner buildCategorizedSpinner(Attribute attribute, ViewGroup parent) {

        ViewGroup row = (ViewGroup) viewContext.getLayoutInflater().inflate(R.layout.categorized_spinner_view,
                parent);

        CategorizedSpinner spinner = (CategorizedSpinner) row.findViewById(R.id.spinner);
        spinner.setPrompt("Select " + attribute.description);

        spinner.setItems(attribute.options);

        return spinner;
    }

    private MultiSpinner buildMultiSpinner(Attribute attribute, ViewGroup parent) {

        ViewGroup row = (ViewGroup) viewContext.getLayoutInflater().inflate(R.layout.input_multi_spinner_view,
                parent);

        // get the spinner child
        MultiSpinner multiSpinner = (MultiSpinner) getFirstNonLabelView(row);
        multiSpinner.setPrompt("Select " + attribute.description);

        return multiSpinner;
    }

    private View getFirstNonLabelView(ViewGroup row) {
        int i;
        for (i = 0; i < row.getChildCount(); i++) {
            if (!(row.getChildAt(i) instanceof TextView)) {
                break;
            } else if (row.getChildAt(i) instanceof EditText) {
                break;
            }
        }
        return row.getChildAt(i);
    }

    public void buildSurveyName(Survey survey, ViewGroup parent) {

        ViewGroup row = (ViewGroup) viewContext.getLayoutInflater().inflate(R.layout.survey_layout, parent);
        TextView name = (TextView) row.findViewById(R.id.surveyName);
        name.setText(survey.name);
        name.setFocusableInTouchMode(true);
        name.setFocusable(true);
        TextView description = (TextView) row.findViewById(R.id.surveyDescription);
        description.setText(survey.description);

    }

}