com.pacoapp.paco.ui.ExperimentExecutor.java Source code

Java tutorial

Introduction

Here is the source code for com.pacoapp.paco.ui.ExperimentExecutor.java

Source

/*
 * Copyright 2011 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (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.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 License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package com.pacoapp.paco.ui;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.NotificationManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.Settings;
import android.speech.RecognizerIntent;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.apps.paco.questioncondparser.Binding;
import com.google.android.apps.paco.questioncondparser.Environment;
import com.google.android.apps.paco.questioncondparser.ExpressionEvaluator;
import com.pacoapp.paco.PacoConstants;
import com.pacoapp.paco.R;
import com.pacoapp.paco.model.Event;
import com.pacoapp.paco.model.EventUtil;
import com.pacoapp.paco.model.Experiment;
import com.pacoapp.paco.model.ExperimentProviderUtil;
import com.pacoapp.paco.model.NotificationHolder;
import com.pacoapp.paco.model.Output;
import com.pacoapp.paco.net.SyncService;
import com.pacoapp.paco.sensors.android.BroadcastTriggerReceiver;
import com.pacoapp.paco.shared.model2.ExperimentGroup;
import com.pacoapp.paco.shared.model2.Input2;
import com.pacoapp.paco.shared.util.ExperimentHelper;
import com.pacoapp.paco.triggering.AndroidEsmSignalStore;
import com.pacoapp.paco.triggering.BeeperService;
import com.pacoapp.paco.triggering.ExperimentExpirationManagerService;
import com.pacoapp.paco.triggering.NotificationCreator;
import com.pacoapp.paco.utils.IntentExtraHelper;

import org.joda.time.DateTime;
import org.joda.time.Seconds;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class ExperimentExecutor extends ActionBarActivity
        implements ChangeListener, LocationListener, ExperimentLoadingActivity {

    public static final String FORM_DURATION_IN_SECONDS = "Form Duration";
    private Experiment experiment;
    private ExperimentGroup experimentGroup;
    private Long actionTriggerId;
    private Long actionId;
    private Long actionTriggerSpecId;

    private Long notificationHolderId;
    private boolean shouldExpireNotificationHolder;
    private Long scheduledTime = 0L;

    private ExperimentProviderUtil experimentProviderUtil;
    private List<InputLayout> inputs = new ArrayList<InputLayout>();
    private LayoutInflater inflater;
    private LinearLayout mainLayout;
    private OptionsMenu optionsMenu;

    private Object updateLock = new Object();

    private ArrayList<InputLayout> locationInputs;
    private Location location;

    private View buttonView;
    private Button doOnPhoneButton;
    private Button doOnWebButton;

    private List<SpeechRecognitionListener> speechRecognitionListeners = new ArrayList<SpeechRecognitionListener>();
    public static final int RESULT_SPEECH = 3;

    private LinearLayout inputsScrollPane;
    private DateTime formOpenTime;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActionBar actionBar = getSupportActionBar();
        actionBar.setDisplayUseLogoEnabled(true);
        actionBar.setDisplayShowHomeEnabled(true);
        // actionBar.setDisplayHomeAsUpEnabled(true);
        actionBar.setDisplayShowTitleEnabled(true);

        experimentProviderUtil = new ExperimentProviderUtil(this);
        if (experiment == null || experimentGroup == null) {
            IntentExtraHelper.loadExperimentInfoFromIntent(this, getIntent(), experimentProviderUtil);
        }
        loadNotificationData();

        if (experiment == null || experimentGroup == null) {
            displayNoExperimentMessage();
        } else {

            actionBar.setTitle(experiment.getExperimentDAO().getTitle());
            if (scheduledTime == null || scheduledTime == 0l) {
                getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            }
            inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            optionsMenu = new OptionsMenu(this, getExperiment().getExperimentDAO().getId(),
                    scheduledTime != null && scheduledTime != 0L);

            mainLayout = (LinearLayout) inflater.inflate(R.layout.experiment_executor, null);
            setContentView(mainLayout);

            inputsScrollPane = (LinearLayout) findViewById(R.id.ScrollViewChild);
            displayExperimentGroupTitle();

            ((LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE))
                    .inflate(R.layout.experiment_web_recommended_buttons, mainLayout, true);
            buttonView = findViewById(R.id.ExecutorButtonLayout);
            buttonView.setVisibility(View.GONE);

            doOnPhoneButton = (Button) findViewById(R.id.DoOnPhoneButton);
            doOnPhoneButton.setVisibility(View.GONE);
            // doOnPhoneButton.setOnClickListener(new OnClickListener() {
            // public void onClick(View v) {
            // buttonView.setVisibility(View.GONE);
            // //mainLayout.removeView(buttonView);
            // scrollView.setVisibility(View.VISIBLE);
            // showForm();
            // }
            // });

            doOnWebButton = (Button) findViewById(R.id.DoOnWebButtonButton);
            doOnWebButton.setOnClickListener(new OnClickListener() {
                public void onClick(View v) {
                    deleteNotification();
                    finish();
                }
            });

            // if (experimentGroup.getEndOfDayGroup()) {
            // renderWebRecommendedMessage();
            // } else {
            if (experimentGroup.getEndOfDayGroup()
                    || (experimentGroup.getCustomRendering() != null && experimentGroup.getCustomRendering())) {
                Intent customExecutorIntent = new Intent(this, ExperimentExecutorCustomRendering.class);

                Bundle extras = getIntent().getExtras();
                if (extras != null) {
                    customExecutorIntent.putExtras(extras);
                }

                startActivity(customExecutorIntent);
                finish();
            } else {
                showForm();
            }
            // }
        }

    }

    private void renderWebRecommendedMessage() {
        final ScrollView scrollView = (ScrollView) findViewById(R.id.ScrollView01);
        scrollView.setVisibility(View.GONE);
        buttonView.setVisibility(View.VISIBLE);

    }

    private void loadNotificationData() {
        Bundle extras = getIntent().getExtras();
        NotificationHolder notificationHolder = null;
        if (extras != null) {
            notificationHolderId = extras.getLong(NotificationCreator.NOTIFICATION_ID);
            notificationHolder = experimentProviderUtil.getNotificationById(notificationHolderId);
            Long timeoutMillis = null;
            if (notificationHolder != null) {
                experiment = experimentProviderUtil.getExperimentByServerId(notificationHolder.getExperimentId());
                experimentGroup = getExperiment().getExperimentDAO()
                        .getGroupByName(notificationHolder.getExperimentGroupName());
                actionTriggerId = notificationHolder.getActionTriggerId();
                actionId = notificationHolder.getActionId();
                actionTriggerSpecId = notificationHolder.getActionTriggerSpecId();
                scheduledTime = notificationHolder.getAlarmTime();
                Log.i(PacoConstants.TAG,
                        "Starting experimentExecutor from signal: " + getExperiment().getExperimentDAO().getTitle()
                                + ". alarmTime: " + new DateTime(scheduledTime).toString());
                timeoutMillis = notificationHolder.getTimeoutMillis();
            } else {
                scheduledTime = null;
            }

            if (isExpiredEsmPing(timeoutMillis)) {
                Toast.makeText(this, R.string.survey_expired, Toast.LENGTH_LONG).show();
                finish();
            }
        }
        if (notificationHolder == null) {
            lookForActiveNotificationForExperiment();
        }
    }

    /**
     * If the user is self-reporting there might still be an active notification for this experiment. If so, we should
     * add its scheduleTime into this response. There should only ever be one.
     */
    private void lookForActiveNotificationForExperiment() {
        NotificationHolder notificationHolder = null;
        if (getExperiment() == null) {
            return;
        }
        final long experimentServerId = getExperiment().getExperimentDAO().getId().longValue();
        List<NotificationHolder> notificationHolders = experimentProviderUtil
                .getNotificationsFor(experimentServerId, experimentGroup.getName());
        if (notificationHolders != null && !notificationHolders.isEmpty()) {
            notificationHolder = notificationHolders.get(0); // TODO can we ever have more than one for a group?
        }

        if (notificationHolder != null) {
            experiment = experimentProviderUtil.getExperimentByServerId(notificationHolder.getExperimentId());
            experimentGroup = getExperiment().getExperimentDAO()
                    .getGroupByName(notificationHolder.getExperimentGroupName());

            if (notificationHolder.isActive(new DateTime())) {
                notificationHolderId = notificationHolder.getId();
                scheduledTime = notificationHolder.getAlarmTime();
                shouldExpireNotificationHolder = true;
                Log.i(PacoConstants.TAG,
                        "ExperimentExecutor: Self report, but found signal still active : "
                                + getExperiment().getExperimentDAO().getTitle() + ". alarmTime: "
                                + new DateTime(scheduledTime).toString());
            } else {
                NotificationCreator.create(this).timeoutNotification(notificationHolder);
            }
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        registerLocationListenerIfNecessary();
        if (mainLayout != null) {
            mainLayout.clearFocus();
            if (inputs != null && inputs.size() > 0) {
                InputLayout firstInput = inputs.get(0);
                if (firstInput.getInput().getResponseType().equals(Input2.OPEN_TEXT)) {
                    firstInput.requestFocus();
                }

            }
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        for (InputLayout layout : inputs) {
            layout.onPause();
        }
        unregisterLocationListenerIfNecessary();
    }

    private void unregisterLocationListenerIfNecessary() {
        if (locationInputs.size() > 0) {
            locationInputs.clear();
            unregisterLocationListener();
        }
    }

    private void registerLocationListenerIfNecessary() {
        locationInputs = new ArrayList<InputLayout>();
        for (InputLayout input : inputs) {
            if (input.getInput().getResponseType().equals(Input2.LOCATION)) {
                locationInputs.add(input);
            }
        }
        if (locationInputs.size() > 0) {
            registerLocationListener();
        }
    }

    // Location

    private void registerLocationListener() {
        LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        if (!lm.isProviderEnabled("gps")) {
            new AlertDialog.Builder(this).setMessage(R.string.gps_message).setCancelable(true)
                    .setPositiveButton(R.string.enable_button, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            launchGpsSettings();
                            dialog.dismiss();
                        }
                    }).setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.cancel();
                        }
                    }).create().show();
        }
        if (lm != null) {
            getBestProvider(lm);
        }
    }

    public void launchGpsSettings() {
        startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
    }

    private void getBestProvider(LocationManager lm) {
        Criteria criteria = new Criteria();
        criteria.setAccuracy(Criteria.ACCURACY_COARSE);
        criteria.setAccuracy(Criteria.ACCURACY_FINE);

        String bestProvider = lm.getBestProvider(criteria, true);
        if (bestProvider != null) {
            lm.requestLocationUpdates(bestProvider, 0, 0, this);
            location = lm.getLastKnownLocation(bestProvider);
            for (InputLayout input : locationInputs) {
                input.setLocation(location);
            }

        } else {
            new AlertDialog.Builder(this).setMessage(R.string.need_location).setCancelable(true)
                    .setPositiveButton(R.string.enable_button, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            launchGpsSettings();
                            dialog.dismiss();
                        }
                    }).setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.cancel();
                        }
                    }).create().show();
        }
    }

    public void onLocationChanged(Location location) {
        this.location = location;
        for (InputLayout input : locationInputs) {
            input.setLocation(location);
        }
    }

    public void onProviderDisabled(String provider) {
        unregisterLocationListener();
        LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        getBestProvider(lm);
    }

    public void onProviderEnabled(String provider) {
        unregisterLocationListener();
        LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        getBestProvider(lm);
    }

    public void onStatusChanged(String provider, int status, Bundle extras) {
        unregisterLocationListener();
        LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        getBestProvider(lm);
    }

    private void unregisterLocationListener() {
        LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        if (lm != null) {
            lm.removeUpdates(this);
        }
    }
    //End Location

    private boolean isExpiredEsmPing(Long timeoutMillis) {
        return (scheduledTime != null && scheduledTime != 0L && timeoutMillis != null)
                && (new DateTime(scheduledTime)).plus(timeoutMillis).isBefore(new DateTime());
    }

    private void showForm() {
        renderInputs();
        renderSaveButton();
        formOpenTime = DateTime.now();
    }

    private void renderSaveButton() {
        View saveButtonView = ((LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE))
                .inflate(R.layout.experiment_save_buttons, inputsScrollPane, true);
        //    inputsScrollPane.removeView(saveButtonView);
        //LinearLayout saveButtonLayout = (LinearLayout)findViewById(R.id.ExecutorButtonLayout);
        Button saveButton = (Button) findViewById(R.id.SaveResponseButton);
        //saveButtonLayout.removeView(saveButton);
        //    inputsScrollPane.addView(saveButtonView);
        saveButton.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                save();
            }
        });

    }

    private void save() {
        try {
            if (notificationHolderId == null) {
                // workaround the bug with re-launching and stale scheduleTime.
                // How - if there isn't a notificationHolder waiting, then this is not a response
                // to a notification.
                scheduledTime = 0L;
            }
            Event event = EventUtil.createEvent(getExperiment(), experimentGroup.getName(), actionTriggerId,
                    actionId, actionTriggerSpecId, scheduledTime);
            gatherResponses(event);
            addTiming(event);
            experimentProviderUtil.insertEvent(event);

            deleteNotification();

            Bundle extras = getIntent().getExtras();
            if (extras != null) {
                extras.clear();
            }
            updateAlarms();
            notifySyncService();
            showFeedback();
            finish();
        } catch (IllegalStateException ise) {
            new AlertDialog.Builder(this).setTitle(R.string.required_answers_missing).setMessage(ise.getMessage())
                    .show();
        }
    }

    private void addTiming(Event event) {
        if (formOpenTime != null) {
            DateTime formFinishTime = DateTime.now();
            Seconds duration = Seconds.secondsBetween(formOpenTime, formFinishTime);

            Output durationResponse = new Output();
            durationResponse.setAnswer(Integer.toString(duration.getSeconds()));
            durationResponse.setName(FORM_DURATION_IN_SECONDS);
            event.addResponse(durationResponse);
        }
    }

    private void updateAlarms() {
        startService(new Intent(this, BeeperService.class));
    }

    private void deleteNotification() {
        if (notificationHolderId != null && notificationHolderId.longValue() != 0l) {
            experimentProviderUtil.deleteNotification(notificationHolderId);
        }
        if (shouldExpireNotificationHolder) {
            NotificationManager notificationManager = (NotificationManager) getSystemService(
                    Context.NOTIFICATION_SERVICE);
            notificationManager.cancel(new Long(notificationHolderId).intValue()); //This should cancel the notification even if it's non-dismissible
            shouldExpireNotificationHolder = false;
        }
    }

    private void notifySyncService() {
        startService(new Intent(this, SyncService.class));
    }

    public void showFeedback() {
        Intent intent = new Intent(this, FeedbackActivity.class);
        intent.putExtras(getIntent().getExtras());
        intent.putExtra(Experiment.EXPERIMENT_SERVER_ID_EXTRA_KEY, experiment.getExperimentDAO().getId());
        intent.putExtra(Experiment.EXPERIMENT_GROUP_NAME_EXTRA_KEY, experimentGroup.getName());
        startActivity(intent);
    }

    private void gatherResponses(Event event) throws IllegalStateException {
        Environment interpreter = updateInterpreter(null);
        ExpressionEvaluator main = new ExpressionEvaluator(interpreter);
        for (InputLayout inputView : inputs) {
            Input2 input = inputView.getInput();
            try {
                if (input.getConditional() != null && input.getConditional() == true
                        && !main.parse(input.getConditionExpression())) {
                    continue;
                }
            } catch (IllegalArgumentException iae) {
                Log.e(PacoConstants.TAG, "Parsing problem: " + iae.getMessage());
                continue;
            }
            Output responseForInput = new Output();
            String answer = inputView.getValueAsString();
            if (input.getRequired() && (answer == null || answer.length() == 0
                    || answer.equals("-1") /*||
                                           (input.getResponseType().equals(Input.LIST) && answer.equals("0"))*/)) {
                throw new IllegalStateException(getString(R.string.must_answer) + input.getText());
            }
            responseForInput.setAnswer(answer);
            responseForInput.setName(input.getName());
            event.addResponse(responseForInput);
        }
    }

    private void renderInputs() {
        for (Input2 input : experimentGroup.getInputs()) {
            final InputLayout inputView = renderInput(input);
            inputs.add(inputView);
            inputsScrollPane.addView(inputView);
            inputView.addChangeListener(this);
            if (input.getResponseType().equals(Input2.OPEN_TEXT)) {
                final TextView componentWithValue = (TextView) inputView.getComponentWithValue();
                componentWithValue.setImeOptions(EditorInfo.IME_ACTION_DONE);
                componentWithValue.setOnEditorActionListener(new TextView.OnEditorActionListener() {

                    @Override
                    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                        if (actionId == EditorInfo.IME_ACTION_DONE) {
                            // hide virtual keyboard
                            InputMethodManager imm = (InputMethodManager) getSystemService(
                                    Context.INPUT_METHOD_SERVICE);
                            imm.hideSoftInputFromWindow(inputView.getComponentWithValue().getWindowToken(),
                                    InputMethodManager.HIDE_NOT_ALWAYS);
                            return true;
                        }
                        return false;
                    }
                });
            }
        }
    }

    //  private void setNextActionOnOpenTexts() {
    //    int size = inputs.size() - 1;
    //    for (int i = 0; i < size; i++) {
    //      InputLayout inputLayout = inputs.get(i);
    //      if (inputLayout.getInput().getResponseType().equals(Input.OPEN_TEXT)) {
    //        EditText openText = ((EditText)inputLayout.getComponentWithValue());
    //        openText.setImeOptions(EditorInfo.IME_ACTION_NEXT);
    //        openText.setImeActionLabel("Next", EditorInfo.IME_ACTION_NEXT);
    //      }
    //    }
    //
    //  }

    /**
     * Note this is a simple version geared towards questions
     * with text and then some input type for the response.
     * In the future, it might make sense to create an InputView class
     * that has control of displaying the prompt (Question text),
     * and the input type. It just has an api that allows you to
     * retrieve the value that was selected. This could make it
     * easy to create interesting media inputs, or sensor inputs with
     * no display.
     *
     * @param input
     * @return
     */
    private InputLayout renderInput(Input2 input) {
        return createInputViewGroup(input);
    }

    private InputLayout createInputViewGroup(Input2 input) {
        return new InputLayout(this, input);
    }

    private void displayExperimentGroupTitle() {
        final TextView groupNameTextView = (TextView) findViewById(R.id.experiment_title);
        String name = experimentGroup.getName();
        if (name == null || experiment.getExperimentDAO().getGroups().size() == 1) {
            groupNameTextView.setVisibility(View.GONE);
        } else {
            groupNameTextView.setText(name);
        }
    }

    private void displayNoExperimentMessage() {
        inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mainLayout = (LinearLayout) inflater.inflate(R.layout.could_not_load_experiment, null);
        setContentView(mainLayout);

    }

    public void setExperimentGroup(ExperimentGroup group) {
        this.experimentGroup = group;

    }

    public Experiment getExperiment() {
        return experiment;
    }

    public void setExperiment(Experiment experiment) {
        this.experiment = experiment;

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        /*if (optionsMenu != null) {
          return optionsMenu.init(menu);
        }*/
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        return optionsMenu.onOptionsItemSelected(item);
    }

    public void stopExperiment() {
        deleteExperiment();
        finish();
    }

    public void deleteExperiment() {
        NotificationCreator nc = NotificationCreator.create(this);
        nc.timeoutNotificationsForExperiment(experiment.getExperimentDAO().getId());

        createStopEvent(experiment);

        experimentProviderUtil.deleteExperiment(experiment.getId());
        if (ExperimentHelper.shouldWatchProcesses(experiment.getExperimentDAO())) {
            BroadcastTriggerReceiver.initPollingAndLoggingPreference(this);
        }

        new AndroidEsmSignalStore(this).deleteAllSignalsForSurvey(experiment.getExperimentDAO().getId());

        startService(new Intent(this, BeeperService.class));
        startService(new Intent(this, ExperimentExpirationManagerService.class));
    }

    /**
     * Creates a pacot for stopping an experiment
     *
     * @param experiment
     */
    private void createStopEvent(Experiment experiment) {
        Event event = new Event();
        event.setExperimentId(experiment.getId());
        event.setServerExperimentId(experiment.getExperimentDAO().getId());
        event.setExperimentName(experiment.getExperimentDAO().getTitle());
        event.setExperimentVersion(experiment.getExperimentDAO().getVersion());
        event.setResponseTime(new DateTime());

        Output responseForInput = new Output();
        responseForInput.setAnswer("false");
        responseForInput.setName("joined");
        event.addResponse(responseForInput);

        experimentProviderUtil.insertEvent(event);
        startService(new Intent(this, SyncService.class));
    }

    public void onChange(InputLayout input) {
        synchronized (updateLock) {
            Environment interpreter = updateInterpreter(input);
            ExpressionEvaluator main = new ExpressionEvaluator(interpreter);
            for (InputLayout inputLayout : inputs) {
                inputLayout.checkConditionalExpression(main);
            }
        }
    }

    private Environment updateInterpreter(InputLayout input) {
        Environment interpreter = null;
        // todo make interpreter a field to optimize updates.
        if (interpreter == null) {
            interpreter = new Environment();

            for (InputLayout inputLayout : inputs) {
                interpreter.addInput(createBindingFromInputView(inputLayout));
            }
        }
        //    else {
        //      if (input != null) {
        //        interpreter.addInput(createBindingFromInputView(input));
        //      }
        //    }
        return interpreter;
    }

    private Binding createBindingFromInputView(InputLayout inputLayout) {
        String inputName = inputLayout.getInputName();
        Class responseType = inputLayout.getResponseType();
        Object value = inputLayout.getValue();
        Binding binding = new Binding(inputName, responseType, value);
        return binding;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == RESULT_SPEECH) {
            if (resultCode == RESULT_OK) {
                handleSpeechRecognitionActivityResult(resultCode, data);
            } else {
                speechRecognitionListeners.clear();
            }
        } else if (requestCode >= InputLayout.CAMERA_REQUEST_CODE && resultCode == RESULT_OK) { // camera picture
            for (InputLayout inputLayout : inputs) {
                inputLayout.cameraPictureTaken(requestCode);
            }
        } else if (resultCode == RESULT_OK) { //gallery picture
            Uri selectedImage = data.getData();
            String[] filePathColumn = { MediaStore.Images.Media.DATA };

            try {
                Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
                cursor.moveToFirst();

                int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
                String filePath = cursor.getString(columnIndex);
                cursor.close();
                for (InputLayout inputLayout : inputs) {
                    inputLayout.galleryPicturePicked(filePath, requestCode);
                }
            } catch (Exception e) {
                Log.i(PacoConstants.TAG, "Exception in gallery picking: " + e.getMessage());
                e.printStackTrace();
            }

        }
    }

    private void handleSpeechRecognitionActivityResult(int resultCode, Intent data) {
        if (resultCode == Activity.RESULT_OK && null != data) {
            ArrayList<String> guesses = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
            notifySpeechRecognitionListeners(guesses);
        }
    }

    private void notifySpeechRecognitionListeners(List<String> guesses) {
        List<SpeechRecognitionListener> copyOfSpeechListeners = new ArrayList<SpeechRecognitionListener>(
                speechRecognitionListeners);
        for (SpeechRecognitionListener listener : copyOfSpeechListeners) {
            listener.speechRetrieved(guesses);
        }
    }

    public void removeSpeechRecognitionListener(SpeechRecognitionListener listener) {
        speechRecognitionListeners.remove(listener);
    }

    public void startSpeechRecognition(SpeechRecognitionListener listener) {
        //speechRecognitionListeners.clear(); // just in case they canceled the last recognition (android gives us no feedback)
        speechRecognitionListeners.add(listener);
        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, Locale.getDefault().getDisplayName());

        try {
            startActivityForResult(intent, RESULT_SPEECH);
        } catch (ActivityNotFoundException a) {
            Toast t = Toast.makeText(getApplicationContext(),
                    R.string.oops_your_device_doesn_t_support_speech_to_text, Toast.LENGTH_SHORT);
            t.show();
        }
    }

}