package org.adaptlab.chpir.android.survey;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import org.adaptlab.chpir.android.survey.Location.LocationServiceManager;
import org.adaptlab.chpir.android.survey.Models.Grid;
import org.adaptlab.chpir.android.survey.Models.Instrument;
import org.adaptlab.chpir.android.survey.Models.Option;
import org.adaptlab.chpir.android.survey.Models.Question;
import org.adaptlab.chpir.android.survey.Models.Question.QuestionType;
import org.adaptlab.chpir.android.survey.Models.Response;
import org.adaptlab.chpir.android.survey.Models.Section;
import org.adaptlab.chpir.android.survey.Models.Survey;
import org.adaptlab.chpir.android.survey.QuestionFragments.MultipleSelectGridFragment;
import org.adaptlab.chpir.android.survey.QuestionFragments.SingleSelectGridFragment;
import org.adaptlab.chpir.android.survey.Rules.InstrumentSurveyLimitPerMinuteRule;
import org.adaptlab.chpir.android.survey.Rules.InstrumentSurveyLimitRule;
import org.adaptlab.chpir.android.survey.Rules.InstrumentTimingRule;
import org.adaptlab.chpir.android.survey.Rules.RuleBuilder;
import org.adaptlab.chpir.android.survey.Tasks.SendResponsesTask;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.activeandroid.Model;
import com.crashlytics.android.Crashlytics;
public class SurveyFragment extends Fragment {
private static final String TAG = "SurveyFragment";
private static final int REVIEW_CODE = 100;
public final static String EXTRA_INSTRUMENT_ID =
"org.adaptlab.chpir.android.survey.instrument_id";
public final static String EXTRA_QUESTION_ID =
"org.adaptlab.chpir.android.survey.question_id";
public final static String EXTRA_QUESTION_NUMBER =
"org.adaptlab.chpir.android.survey.question_number";
public final static String EXTRA_SURVEY_ID =
"org.adaptlab.chpir.android.survey.survey_id";
public final static String EXTRA_PREVIOUS_QUESTION_IDS =
"org.adaptlab.chpir.android.survey.previous_questions";
public final static String EXTRA_PARTICIPANT_METADATA =
"org.adaptlab.chpir.android.survey.metadata";
public final static String EXTRA_QUESTIONS_TO_SKIP_IDS =
"org.adaptlab.chpir.android.survey.questions_to_skip_ids";
public final static String EXTRA_SKIPPED_QUESTIONS_IDS =
"org.adaptlab.chpir.android.survey.skipped_questions_ids";
private Question mQuestion;
private Instrument mInstrument;
private Survey mSurvey;
private int mQuestionNumber;
private String mMetadata;
private Question mResumeQuestion = null;
private Grid mGrid;
// mPreviousQuestions is a Stack, however Android does not allow you
// to save a Stack to the savedInstanceState, so it is represented as
// an Integer array.
private ArrayList<Integer> mPreviousQuestions;
private ArrayList<Integer> mQuestionsToSkip;
private Set<Integer> mSkippedQuestions;
private TextView mQuestionText;
private TextView mQuestionIndex;
private TextView mParticipantLabel;
private ProgressBar mProgressBar;
QuestionFragment mQuestionFragment;
private LocationServiceManager mLocationServiceManager;
//drawer vars
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
private ActionBarDrawerToggle mDrawerToggle;
private String mDrawerTitle;
private String mTitle;
private ArrayList<Section> mSections;
private String[] mSectionTitles;
private boolean mNavDrawerSet = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
if (savedInstanceState != null) {
mInstrument = Instrument.findByRemoteId(savedInstanceState.getLong(EXTRA_INSTRUMENT_ID));
if (!checkRules()) getActivity().finish();
mQuestion = Question.findByRemoteId(savedInstanceState.getLong(EXTRA_QUESTION_ID));
mSurvey = Survey.load(Survey.class, savedInstanceState.getLong(EXTRA_SURVEY_ID));
mQuestionNumber = savedInstanceState.getInt(EXTRA_QUESTION_NUMBER);
mPreviousQuestions = savedInstanceState.getIntegerArrayList(EXTRA_PREVIOUS_QUESTION_IDS);
mQuestionsToSkip = savedInstanceState.getIntegerArrayList(EXTRA_QUESTIONS_TO_SKIP_IDS);
ArrayList<Integer> skippedQuestions = savedInstanceState.getIntegerArrayList(EXTRA_SKIPPED_QUESTIONS_IDS);
mSkippedQuestions = new LinkedHashSet<Integer>(skippedQuestions);
if (mQuestion.belongsToGrid()) {
mGrid = mQuestion.getGrid();
}
} else {
Long instrumentId = getActivity().getIntent().getLongExtra(EXTRA_INSTRUMENT_ID, -1);
mMetadata = getActivity().getIntent().getStringExtra(EXTRA_PARTICIPANT_METADATA);
if (instrumentId == -1) return;
mInstrument = Instrument.findByRemoteId(instrumentId);
if (mInstrument == null) return;
if (!checkRules()) getActivity().finish();
loadOrCreateSurvey();
loadOrCreateQuestion();
}
if (AppUtil.PRODUCTION) {
Crashlytics.setString("last instrument", mInstrument.getTitle());
}
if (AppUtil.getAdminSettingsInstance().getRecordSurveyLocation()) {
startLocationServices();
}
}
private void setupNavigationDrawer() {
mSections = new ArrayList<Section>();
mSections = (ArrayList<Section>) mInstrument.sections();
mSectionTitles = new String[mSections.size()];
for (int i=0; i<mSections.size(); i++) {
mSectionTitles[i] = mSections.get(i).getTitle();
}
mTitle = mDrawerTitle = mInstrument.getTitle();
mDrawerLayout = (DrawerLayout) getActivity().findViewById(R.id.drawer_layout);
mDrawerList = (ListView) getActivity().findViewById(R.id.left_drawer);
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
mDrawerList.setAdapter(new ArrayAdapter<String>(getActivity(), R.layout.drawer_list_item, mSectionTitles));
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
mDrawerToggle = new ActionBarDrawerToggle(
getActivity(),
mDrawerLayout,
R.drawable.ic_drawer,
R.string.drawer_open,
R.string.drawer_close
) {
public void onDrawerClosed(View view) {
getActivity().getActionBar().setTitle(mTitle);
getActivity().invalidateOptionsMenu();
}
public void onDrawerOpened(View drawerView) {
getActivity().getActionBar().setTitle(mDrawerTitle);
getActivity().invalidateOptionsMenu();
}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
getActivity().getActionBar().setHomeButtonEnabled(true);
mNavDrawerSet = true;
}
private class DrawerItemClickListener implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
}
}
private void selectItem(int position) {
moveToSection(mSections.get(position).getStartQuestionIdentifier());
mDrawerList.setItemChecked(position, true);
getActivity().setTitle(mInstrument.getTitle() + " : " + mSectionTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
}
private void moveToSection(String questionIdentifier) {
mPreviousQuestions.add(mQuestionNumber);
mQuestion = Question.findByQuestionIdentifier(questionIdentifier);
mQuestionNumber = mQuestion.getNumberInInstrument() - 1;
createQuestionFragment();
updateQuestionText();
updateQuestionCountLabel();
}
private void updateQuestionText() {
setQuestionText(mQuestionText);
mQuestionText.setTypeface(mInstrument.getTypeFace(getActivity().getApplicationContext()));
}
private void startLocationServices() {
mLocationServiceManager = LocationServiceManager.get(getActivity());
mLocationServiceManager.startLocationUpdates();
}
public void loadOrCreateSurvey() {
Long surveyId = getActivity().getIntent().getLongExtra(EXTRA_SURVEY_ID, -1);
if (surveyId == -1) {
mSurvey = new Survey();
mSurvey.setInstrument(mInstrument);
mSurvey.setMetadata(mMetadata);
mSurvey.setProjectId(mInstrument.getProjectId());
mSurvey.save();
} else {
mSurvey = Model.load(Survey.class, surveyId);
}
}
public void loadOrCreateQuestion() {
mPreviousQuestions = new ArrayList<Integer>();
mQuestionsToSkip = new ArrayList<Integer>();
mSkippedQuestions = new LinkedHashSet<Integer>();
Long questionId = getActivity().getIntent().getLongExtra(EXTRA_QUESTION_ID, -1);
if (questionId == -1) {
mQuestion = mInstrument.questions().get(0);
mQuestionNumber = 0;
} else {
mQuestion = Model.load(Question.class, questionId);
mQuestionNumber = mQuestion.getNumberInInstrument() - 1;
for (int i = 0; i < mQuestionNumber; i++)
mPreviousQuestions.add(i);
}
if (mQuestion.belongsToGrid()) {
mGrid = mQuestion.getGrid();
}
}
@Override
public void onStart() {
super.onStart();
if (AppUtil.getAdminSettingsInstance().getRecordSurveyLocation()) {
getActivity().registerReceiver(mLocationServiceManager.mLocationReceiver,
new IntentFilter(LocationServiceManager.ACTION_LOCATION));
}
}
@Override
public void onStop() {
if (AppUtil.getAdminSettingsInstance().getRecordSurveyLocation()) {
getActivity().unregisterReceiver(mLocationServiceManager.mLocationReceiver);
}
super.onStop();
}
@Override
public void onResume() {
super.onResume();
if (mResumeQuestion == mQuestion) {
mQuestionNumber = mQuestion.getNumberInInstrument() - 1;
createQuestionFragment();
updateQuestionText();
updateQuestionCountLabel();
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && requestCode == REVIEW_CODE) {
Long remoteId = data.getExtras().getLong(EXTRA_QUESTION_ID);
if (remoteId == Long.MIN_VALUE) {
getActivity().finish();
} else {
Question question = Question.findByRemoteId(remoteId);
if (question != null) {
mQuestion = question;
mResumeQuestion = mQuestion;
} else {
getActivity().finish();
}
}
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong(EXTRA_INSTRUMENT_ID, mInstrument.getRemoteId());
outState.putLong(EXTRA_QUESTION_ID, mQuestion.getRemoteId());
outState.putLong(EXTRA_SURVEY_ID, mSurvey.getId());
outState.putInt(EXTRA_QUESTION_NUMBER, mQuestionNumber);
outState.putIntegerArrayList(EXTRA_PREVIOUS_QUESTION_IDS, mPreviousQuestions);
outState.putIntegerArrayList(EXTRA_QUESTIONS_TO_SKIP_IDS, mQuestionsToSkip);
outState.putIntegerArrayList(EXTRA_SKIPPED_QUESTIONS_IDS, new ArrayList<Integer>(mSkippedQuestions));
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_survey, menu);
if (mNavDrawerSet == false) {
setupNavigationDrawer();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
switch (item.getItemId()) {
case R.id.menu_item_previous:
moveToPreviousQuestion();
return true;
case R.id.menu_item_next:
if (mQuestionFragment.getSpecialResponse().equals(Response.SKIP)) {
mQuestionFragment.saveSpecialResponse("");
}
moveToNextQuestion();
return true;
case R.id.menu_item_skip:
setSpecialResponse(Response.SKIP);
if (isLastQuestion()) {
finishSurvey();
} else {
moveToNextQuestion();
}
return true;
case R.id.menu_item_rf:
setSpecialResponse(Response.RF);
return true;
case R.id.menu_item_na:
setSpecialResponse(Response.NA);
return true;
case R.id.menu_item_dk:
setSpecialResponse(Response.DK);
return true;
case R.id.menu_item_finish:
finishSurvey();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
menu.findItem(R.id.menu_item_previous)
.setEnabled(!isFirstQuestion());
menu.findItem(R.id.menu_item_next)
.setVisible(!isLastQuestion())
.setEnabled(hasValidResponse());
menu.findItem(R.id.menu_item_skip)
.setEnabled(hasValidResponse())
.setVisible(AppUtil.getAdminSettingsInstance().getShowSkip());
menu.findItem(R.id.menu_item_rf)
.setVisible(AppUtil.getAdminSettingsInstance().getShowRF());
menu.findItem(R.id.menu_item_na)
.setVisible(AppUtil.getAdminSettingsInstance().getShowNA());
menu.findItem(R.id.menu_item_dk)
.setVisible(AppUtil.getAdminSettingsInstance().getShowDK());
menu.findItem(R.id.menu_item_finish)
.setVisible(isLastQuestion())
.setEnabled(hasValidResponse());
showSpecialResponseSelection(menu);
}
/*
* Give a visual indication when a special response is selected
*/
public void showSpecialResponseSelection(Menu menu) {
if (mQuestionFragment != null && mQuestionFragment.getSpecialResponse() != null && menu != null) {
if (mQuestionFragment.getSpecialResponse().equals(Response.SKIP)) {
menu.findItem(R.id.menu_item_skip).setIcon(R.drawable.ic_menu_item_sk_selected);
} else if (mQuestionFragment.getSpecialResponse().equals(Response.RF)) {
menu.findItem(R.id.menu_item_rf).setIcon(R.drawable.ic_menu_item_rf_selected);
} else if (mQuestionFragment.getSpecialResponse().equals(Response.NA)) {
menu.findItem(R.id.menu_item_na).setIcon(R.drawable.ic_menu_item_na_selected);
} else if (mQuestionFragment.getSpecialResponse().equals(Response.DK)) {
menu.findItem(R.id.menu_item_dk).setIcon(R.drawable.ic_menu_item_dk_selected);
}
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_survey, parent, false);
mQuestionText = (TextView) v.findViewById(R.id.question_text);
mParticipantLabel = (TextView) v.findViewById(R.id.participant_label);
mQuestionIndex = (TextView) v.findViewById(R.id.question_index);
mProgressBar = (ProgressBar) v.findViewById(R.id.progress_bar);
setParticipantLabel();
updateQuestionCountLabel();
if (mQuestion.belongsToGrid()) {
setGridLabelText(mQuestionText);
} else {
setQuestionText(mQuestionText);
}
mQuestionText.setTypeface(mInstrument.getTypeFace(getActivity().getApplicationContext()));
createQuestionFragment();
ActivityCompat.invalidateOptionsMenu(getActivity());
getActivity().getActionBar().setTitle(mInstrument.getTitle());
return v;
}
/*
* Place the question fragment for the corresponding mQuestion
* on the view in the question_container.
*/
protected void createQuestionFragment() {
if (mQuestion == null)
loadOrCreateQuestion();
if (mSurvey == null)
loadOrCreateSurvey();
if (mQuestion.belongsToGrid()) {
createGridFragment();
} else {
FragmentManager fm = getChildFragmentManager();
mQuestionFragment = (QuestionFragment) QuestionFragmentFactory.createQuestionFragment(mQuestion, mSurvey);
switchOutFragments(fm);
}
}
private void createGridFragment() {
if (mQuestion.getQuestionType() == QuestionType.SELECT_ONE) {
mQuestionFragment = new SingleSelectGridFragment();
} else {
mQuestionFragment = new MultipleSelectGridFragment();
}
Bundle bundle = new Bundle();
bundle.putLong(GridFragment.EXTRA_GRID_ID, mQuestion.getGrid().getRemoteId());
bundle.putLong(GridFragment.EXTRA_SURVEY_ID, mSurvey.getId());
mQuestionFragment.setArguments(bundle);
FragmentManager fm = getChildFragmentManager();
switchOutFragments(fm);
}
private void switchOutFragments(FragmentManager fm) {
if (fm.findFragmentById(R.id.question_container) == null) {
fm.beginTransaction()
.add(R.id.question_container, mQuestionFragment)
.commit();
} else {
fm.beginTransaction()
.replace(R.id.question_container, mQuestionFragment)
.commit();
}
mSurvey.setLastQuestion(mQuestion);
mSurvey.save();
removeTextFocus();
}
private void setGridLabelText(TextView view) {
view.append(styleTextWithHtml(mGrid.getText()));
}
/*
* This will remove the focus of the input as the survey is
* traversed. If this is not called, then it will be possible
* for someone to change the answer to a question that they are
* not currently viewing.
*/
private void removeTextFocus() {
if (getActivity().getCurrentFocus() != null) {
InputMethodManager inputManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
}
/*
* If a question has a skip pattern, then read the response
* when pressing the "next" button. If the index of the response
* is able to have a skip pattern, then set the next question to
* the question indicated by the skip pattern. "Other" responses
* cannot have skip patterns, and the question is just set to the
* next question in the sequence.
*/
private Question getNextQuestion(int questionIndex) {
setSkippedForReview();
Question nextQuestion = null;
if (mQuestion.hasSkipPattern() && mSurvey.getResponseByQuestion(mQuestion) != null) {
try {
int responseIndex = Integer.parseInt(mSurvey.getResponseByQuestion(mQuestion).getText());
if (mQuestion.hasMultiSkipPattern()) {
addQuestionsToSkip(responseIndex);
}
nextQuestion = getNextQuestionForSkipPattern(questionIndex, responseIndex);
} catch (NumberFormatException nfe) {
nextQuestion = getNextQuestionWhenNumberFormatException(questionIndex);
}
} else if (mQuestion.hasMultiSkipPattern() && mSurvey.getResponseByQuestion(mQuestion) != null) {
try {
int responseIndex = Integer.parseInt(mSurvey.getResponseByQuestion(mQuestion).getText());
addQuestionsToSkip(responseIndex);
nextQuestion = nextQuestionHelper(questionIndex);
} catch (NumberFormatException nfe) {
nextQuestion = getNextQuestionWhenNumberFormatException(questionIndex);
}
} else {
nextQuestion = nextQuestionHelper(questionIndex);
}
Question question = getNextUnskippedQuestion(nextQuestion);
return question;
}
private Question getNextQuestionWhenNumberFormatException(int questionIndex) {
Question nextQuestion;
nextQuestion = nextQuestionHelper(questionIndex);
Log.wtf(TAG, "Received a non-numeric skip response index for " + mQuestion.getQuestionIdentifier());
return nextQuestion;
}
private void addQuestionsToSkip(int responseIndex) {
if (responseIndex < mQuestion.options().size()) {
Option selectedOption = mQuestion.options().get(responseIndex);
for (Question skipQuestion: selectedOption.questionsToSkip()){
mQuestionsToSkip.add(skipQuestion.getNumberInInstrument());
}
}
}
private Question getNextQuestionForSkipPattern(int questionIndex, int responseIndex) {
Question nextQuestion;
if (responseIndex < mQuestion.options().size() && mQuestion.options().get(responseIndex).getNextQuestion() != null) {
nextQuestion = mQuestion.options().get(responseIndex).getNextQuestion();
mQuestionNumber = nextQuestion.getNumberInInstrument() - 1;
} else {
nextQuestion = nextQuestionHelper(questionIndex);
}
return nextQuestion;
}
private Question getNextUnskippedQuestion(Question nextQuestion) {
if (mQuestionsToSkip.contains(nextQuestion.getNumberInInstrument())) {
if (isLastQuestion()) {
finishSurvey();
} else {
nextQuestion = nextQuestionHelper(nextQuestion.getNumberInInstrument() - 1);
nextQuestion = getNextUnskippedQuestion(nextQuestion);
}
}
return nextQuestion;
}
private Question nextQuestionHelper(int index) {
mQuestionNumber = index + 1;
return mInstrument.questions().get(mQuestionNumber);
}
private void clearSkipsForCurrentQuestion() {
if (!mQuestionsToSkip.isEmpty()) {
for (Question question : mQuestion.questionsToSkip()) {
mQuestionsToSkip.remove(Integer.valueOf(question.getNumberInInstrument()));
}
}
}
private void setSkippedForReview() {
if (nullResponse() || emptyResponse() || skippedResponse() ) {
if (pictureResponseQuestion()) {
if (mQuestionFragment.getResponsePhoto().getPicturePath() == null) {
mSkippedQuestions.add(mQuestion.getNumberInInstrument());
} else {
mSkippedQuestions.remove(Integer.valueOf(mQuestion.getNumberInInstrument()));
}
} else {
mSkippedQuestions.add(mQuestion.getNumberInInstrument());
}
} else {
mSkippedQuestions.remove(Integer.valueOf(mQuestion.getNumberInInstrument()));
}
}
private boolean emptyResponse() {
return (mSurvey.getResponseByQuestion(mQuestion).getText().length() == 0 &&
mSurvey.getResponseByQuestion(mQuestion).getSpecialResponse().length() == 0);
}
private boolean nullResponse() {
return mSurvey.getResponseByQuestion(mQuestion) == null;
}
private boolean skippedResponse() {
return mSurvey.getResponseByQuestion(mQuestion).getSpecialResponse() == Response.SKIP;
}
private boolean pictureResponseQuestion() {
return (mQuestion.getQuestionType() == QuestionType.FRONT_PICTURE || mQuestion.getQuestionType() == QuestionType.REAR_PICTURE);
}
private boolean isInstructionsQuestion(Question question) {
return (question.getQuestionType() == QuestionType.INSTRUCTIONS);
}
private void removeInstructionsQuestions() {
for (Iterator<Integer> iterator = mSkippedQuestions.iterator(); iterator.hasNext();) {
Integer next = iterator.next();
Question question = Question.findByNumberInInstrument(next, mInstrument.getId());
if (isInstructionsQuestion(question)) {
iterator.remove();
}
}
}
/*
* Switch out the next question with a fragment from the
* QuestionFragmentFactory. Increment the question to
* the next question.
*/
public void moveToNextQuestion() {
int questionsInInstrument = mInstrument.questions().size();
if (mQuestionNumber < questionsInInstrument - 1) {
if (mQuestion.firstInGrid()) {
mQuestionNumber = mGrid.questions().get(mGrid.questions().size() - 1).getNumberInInstrument() - 1;
mPreviousQuestions.add(mQuestion.getNumberInInstrument() - 1);
mQuestion = mGrid.questions().get(mGrid.questions().size() - 1);
} else {
mPreviousQuestions.add(mQuestionNumber);
}
mQuestion = getNextQuestion(mQuestionNumber);
if (mQuestion.getGrid() != null) {
mGrid = mQuestion.getGrid();
}
createQuestionFragment();
if (!setQuestionText(mQuestionText)) {
setSpecialResponse(Response.LOGICAL_SKIP);
moveToNextQuestion();
}
} else if (isLastQuestion() && !setQuestionText(mQuestionText)) {
finishSurvey();
}
updateQuestionCountLabel();
}
/*
* Move to previous question. Takes into account if
* this question is following up another question. If
* this question is not a follow up question, just move
* to the previous question in the sequence.
*/
public void moveToPreviousQuestion() {
if (mQuestionNumber > 0 && mQuestionNumber < mInstrument.questions().size()) {
mQuestionNumber = mPreviousQuestions.remove(mPreviousQuestions.size() - 1);
mQuestion = mInstrument.questions().get(mQuestionNumber);
if (mQuestion.getGrid() != null) {
mGrid = mQuestion.getGrid();
}
createQuestionFragment();
if (!setQuestionText(mQuestionText)) {
moveToPreviousQuestion();
}
if (mSurvey.getResponseByQuestion(mQuestion) != null &&
mSurvey.getResponseByQuestion(mQuestion).getText() != "" ) {
clearSkipsForCurrentQuestion();
}
}
updateQuestionCountLabel();
}
/*
* Destroy this activity, and save the survey and mark it as
* complete. Send to server if network is available.
*/
public void finishSurvey() {
setSkippedForReview(); //To check if last question is skipped
removeInstructionsQuestions();
if (AppUtil.getAdminSettingsInstance().getRecordSurveyLocation()) {
setSurveyLocation();
}
if (!mSkippedQuestions.isEmpty()) {
ArrayList<String> skippedQuestions = new ArrayList<String>();
for (Integer questionNumber : mSkippedQuestions) {
Question question = Question.findByNumberInInstrument(questionNumber, mInstrument.getId());
skippedQuestions.add(question.getQuestionIdentifier());
}
Intent i = new Intent(getActivity(), ReviewPageActivity.class);
Bundle b = new Bundle();
b.putStringArrayList(ReviewPageFragment.EXTRA_REVIEW_QUESTION_IDS, skippedQuestions);
b.putLong(ReviewPageFragment.EXTRA_REVIEW_SURVEY_ID, mSurvey.getId());
i.putExtras(b);
startActivityForResult(i, REVIEW_CODE);
}
else {
getActivity().finish();
mSurvey.setAsComplete();
mSurvey.save();
new SendResponsesTask(getActivity()).execute();
}
}
public boolean isFirstQuestion() {
return mQuestionNumber == 0;
}
public boolean isLastQuestion() {
return mInstrument.questions().size() == mQuestionNumber + 1;
}
public boolean hasValidResponse() {
if (mQuestionFragment != null && mQuestionFragment.getResponse() != null) {
return mQuestionFragment.getResponse().isValid();
} else {
return true;
}
}
private void setSurveyLocation() {
mSurvey.setLatitude(mLocationServiceManager.getLatitude());
mSurvey.setLongitude(mLocationServiceManager.getLongitude());
}
/*
* If this question is a follow up question, then attempt
* to get the response to the question that is being followed up on.
*
* If the question being followed up on was skipped by the user,
* then return false. This gives the calling function an opportunity
* to handle this accordingly. Likely this will involve skipping
* the question that is a follow up question.
*
* If this question is not a following up question, then just
* set the text as normal.
*/
private boolean setQuestionText(TextView text) {
appendInstructions(text);
if (mQuestion.isFollowUpQuestion()) {
String followUpText = mQuestion.getFollowingUpText(mSurvey, getActivity());
if (followUpText == null) {
return false;
} else {
text.append(styleTextWithHtml(followUpText));
}
} else {
text.append(styleTextWithHtml(mQuestion.getText()));
}
return true;
}
/*
* If this question has instructions, append and add new line
*/
private void appendInstructions(TextView text) {
if (mQuestion.getInstructions() != null) {
text.setText(styleTextWithHtml(mQuestion.getInstructions() + "<br /><br />"));
} else {
text.setText("");
}
}
private Spanned styleTextWithHtml(String text) {
return Html.fromHtml(text);
}
/*
* Save the special response field and clear the current
* response if there is one.
*/
private void setSpecialResponse(String response) {
mQuestionFragment.saveSpecialResponse(response);
if (isAdded()) {
ActivityCompat.invalidateOptionsMenu(getActivity());
}
}
private void setParticipantLabel() {
String surveyMetaData = mSurvey.getMetadata();
if (!TextUtils.isEmpty(surveyMetaData)) {
try {
JSONObject metadata = new JSONObject(surveyMetaData);
if (metadata.has("survey_label")) {
mParticipantLabel.setText(metadata.getString("survey_label"));
}
} catch (JSONException er) {
Log.e(TAG, er.getMessage());
}
}
}
private void updateQuestionCountLabel() {
int numberQuestions = mInstrument.questions().size();
mQuestionIndex.setText((mQuestionNumber + 1) + " " + getString(R.string.of) + " " + numberQuestions);
mProgressBar.setProgress((int) (100 * (mQuestionNumber + 1) / (float) numberQuestions));
if (isAdded()) {
ActivityCompat.invalidateOptionsMenu(getActivity());
}
}
private boolean checkRules() {
return new RuleBuilder(getActivity())
.addRule(new InstrumentSurveyLimitRule(mInstrument,
getActivity().getString(R.string.rule_failure_instrument_survey_limit)))
.addRule(new InstrumentTimingRule(mInstrument, getResources().getConfiguration().locale,
getActivity().getString(R.string.rule_failure_survey_timing)))
.addRule(new InstrumentSurveyLimitPerMinuteRule(mInstrument,
getActivity().getString(R.string.rule_instrument_survey_limit_per_minute)))
.showToastOnFailure(true)
.checkRules()
.getResult();
}
}