/** * ORcycle, Copyright 2014, 2015, PSU Transportation, Technology, and People Lab. * * @author Robin Murray <robin5@pdx.edu> (code) * @author Miguel Figliozzi <figliozzi@pdx.edu> and ORcycle team (general app * design and features, report questionnaires and new ORcycle features) * * For more information on the project, go to * http://www.pdx.edu/transportation-lab/orcycle and http://www.pdx.edu/transportation-lab/app-development * * Updated/modified for Oregon pilot study and app deployment. * * ORcycle is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or any later version. * ORcycle is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License along with * ORcycle. If not, see <http://www.gnu.org/licenses/>. * */ package edu.pdx.cecs.orcycle; import java.util.List; import java.util.Map; import java.util.Map.Entry; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.Spinner; public class NoteQuestionsActivity extends Activity { private static final String MODULE_TAG = "NoteQuestionsActivity"; private Spinner spnSeverity; private MultiSelectionSpinner spnIssueType; private MultiSelectionSpinner spnConflict; public static final String EXTRA_NOTE_ID = "noteId"; public static final String EXTRA_NOTE_TYPE = "noteType"; public static final String EXTRA_NOTE_SEVERITY = "noteSeverity"; public static final String EXTRA_NOTE_SOURCE = "noteSource"; public static final int EXTRA_NOTE_ID_UNDEFINED = -1; public static final int EXTRA_NOTE_TYPE_UNDEFINED = -1; public static final int EXTRA_NOTE_SEVERITY_UNDEFINED = -1; public static final int EXTRA_NOTE_SOURCE_UNDEFINED = -1; public static final int EXTRA_NOTE_SOURCE_MAIN_INPUT = 0; public static final int EXTRA_NOTE_SOURCE_TRIP_MAP = 1; public static final String EXTRA_TRIP_ID = "EXTRA_TRIP_ID"; private static final int EXTRA_TRIP_ID_UNDEFINED = -1; public static final String EXTRA_TRIP_SOURCE = "tripSource"; private static final int EXTRA_TRIP_SOURCE_UNDEFINED = -1; public static final int EXTRA_TRIP_SOURCE_MAIN_INPUT = 0; public static final int EXTRA_TRIP_SOURCE_SAVED_TRIPS = 1; private static final String PREFS_NOTE_QUESTIONS = "PREFS_NOTE_QUESTIONS"; private static final int PREF_SEVERITY = 1; private static final int PREF_CONFLICT = 2; private static final int PREF_ISSUE = 3; private int noteSeverity; private long noteId = -1; private int noteSource = EXTRA_NOTE_SOURCE_UNDEFINED; private long tripId; private int tripSource = EXTRA_TRIP_SOURCE_UNDEFINED; // ********************************************************************************* // * Activity Handlers // ********************************************************************************* /** * Handler: onCreate */ @Override public void onCreate(Bundle savedInstanceState) { try { super.onCreate(savedInstanceState); // get input values for this view Intent myIntent = getIntent(); noteId = myIntent.getLongExtra(EXTRA_NOTE_ID, EXTRA_NOTE_ID_UNDEFINED); if (EXTRA_NOTE_ID_UNDEFINED == noteId) { throw new IllegalArgumentException(MODULE_TAG + ": EXTRA_NOTE_ID undefined."); } noteSource = myIntent.getIntExtra(EXTRA_NOTE_SOURCE, EXTRA_NOTE_SOURCE_UNDEFINED); if (!((noteSource == EXTRA_NOTE_SOURCE_MAIN_INPUT) ||(noteSource == EXTRA_NOTE_SOURCE_TRIP_MAP))) { throw new IllegalArgumentException(MODULE_TAG + ": EXTRA_NOTE_SOURCE invalid argument."); } noteSeverity = myIntent.getIntExtra(EXTRA_NOTE_SEVERITY, EXTRA_NOTE_SEVERITY_UNDEFINED); // Note: these extras are used for transitioning back to the TripMapActivity if done if (EXTRA_TRIP_ID_UNDEFINED == (tripId = myIntent.getLongExtra(EXTRA_TRIP_ID, EXTRA_TRIP_ID_UNDEFINED))) { throw new IllegalArgumentException(MODULE_TAG + ": invalid extra - EXTRA_TRIP_ID"); } if (EXTRA_TRIP_SOURCE_UNDEFINED == (tripSource = myIntent.getIntExtra(EXTRA_TRIP_SOURCE, EXTRA_TRIP_SOURCE_UNDEFINED))) { throw new IllegalArgumentException(MODULE_TAG + ": invalid extra - EXTRA_TRIP_SOURCE"); } Log.v(MODULE_TAG, "Cycle: onCreate() - note_id = " + noteId); setContentView(R.layout.activity_note_questions); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); spnSeverity = (Spinner) findViewById(R.id.spnSeverityOfProblem); spnConflict = (MultiSelectionSpinner) findViewById(R.id.spnConflictType); spnConflict.setItems(getResources().getStringArray(R.array.nqaConflictType)); spnConflict.setTitle(getResources().getString(R.string.nqaConflictTypeTitle)); spnConflict.setOtherIndex(DbAnswers.findIndex(DbAnswers.noteConflict, DbAnswers.noteConflictOther)); spnIssueType = (MultiSelectionSpinner) findViewById(R.id.spnIssueType); spnIssueType.setItems(getResources().getStringArray(R.array.nqaIssueType)); spnIssueType.setTitle(getResources().getString(R.string.nqaIssueTypeTitle)); spnIssueType.setOtherIndex(DbAnswers.findIndex(DbAnswers.noteIssue, DbAnswers.noteIssueOther)); //spnIssueType_OnClick.setOtherIndex(DbAnswers.findIndex(DbAnswers.noteIssue, DbAnswers.noteIssueOther)); //spnIssueType_OnClick.setItems(getResources().getStringArray(R.array.nqaIssueType)); //spnIssueType.setOnItemSelectedListener(spnIssueType_OnClick); } catch(Exception ex) { Log.e(MODULE_TAG, ex.getMessage()); } } @Override public void onRestoreInstanceState(Bundle savedInstanceState) { try { super.onRestoreInstanceState(savedInstanceState); Log.v(MODULE_TAG, "Cycle: onRestoreInstanceState()"); recallUiSettings(); } catch(Exception ex) { Log.e(MODULE_TAG, ex.getMessage()); } } @Override public void onSaveInstanceState(Bundle savedInstanceState) { try { Log.v(MODULE_TAG, "Cycle: onSaveInstanceState()"); saveUiSettings(); } catch(Exception ex) { Log.e(MODULE_TAG, ex.getMessage()); } finally { super.onSaveInstanceState(savedInstanceState); } } /* Creates the menu items */ @Override public boolean onCreateOptionsMenu(Menu menu) { try { // Inflate the menu items for use in the action bar MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.note_questions, menu); } catch(Exception ex) { Log.e(MODULE_TAG, ex.getMessage()); } return super.onCreateOptionsMenu(menu); } /* Handles item selections */ @Override public boolean onOptionsItemSelected(MenuItem item) { try { switch (item.getItemId()) { case R.id.action_save_note_questions: if (MandatoryQuestionsAnswered()) { submitAnswers(); transitionToNoteDetailActivity(); } else { AlertUserMandatoryAnswers(); } return true; } } catch(Exception ex) { Log.e(MODULE_TAG, ex.getMessage()); } return super.onOptionsItemSelected(item); } private boolean MandatoryQuestionsAnswered() { return ((spnIssueType.getSelectedIndicies().size() > 0) && (spnSeverity.getSelectedItemPosition() > 0)); } private void AlertUserMandatoryAnswers() { final AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage("Please answer all required questions.") .setCancelable(true) .setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(final DialogInterface dialog, final int id) { dialog.cancel(); } }); final AlertDialog alert = builder.create(); alert.show(); } @Override public void onBackPressed() { try { transitionToPreviousActivity(); } catch(Exception ex) { Log.e(MODULE_TAG, ex.getMessage()); } } // ********************************************************************************* // * Button Handlers // ********************************************************************************* // ********************************************************************************* // * Saving & Recalling UI Settings // ********************************************************************************* /** * Saves UI settings to preferences file */ private void saveUiSettings() { SharedPreferences settings; SharedPreferences.Editor editor; if (null != (settings = getSharedPreferences(PREFS_NOTE_QUESTIONS, MODE_PRIVATE))) { if (null != (editor = settings.edit())) { saveSpinnerSelection(editor, spnSeverity, PREF_SEVERITY); saveSpinnerSelections(editor, spnConflict, PREF_CONFLICT); saveSpinnerSelections(editor, spnIssueType, PREF_ISSUE ); editor.commit(); } } } /** * Saves spinner selection to preferences editor * @param editor * @param spinner * @param key */ private void saveSpinnerSelection(SharedPreferences.Editor editor, Spinner spinner, int key) { editor.putInt("" + key, spinner.getSelectedItemPosition()); } /** * Saves MultiSelectionSpinner selections to preferences editor * @param editor * @param spinner * @param key */ private void saveSpinnerSelections(SharedPreferences.Editor editor, MultiSelectionSpinner spinner, int key) { editor.putString("" + key, spinner.getSelectedIndicesAsString()); } /** * Recalls UI settings from preferences file */ private void recallUiSettings() { SharedPreferences settings; Map<String, ?> prefs; try { if (null != (settings = getSharedPreferences(PREFS_NOTE_QUESTIONS, MODE_PRIVATE))) { if (null != (prefs = settings.getAll())) { for (Entry<String, ?> entry : prefs.entrySet()) { try { switch (Integer.parseInt(entry.getKey())) { case PREF_SEVERITY: setSpinnerSelection(spnSeverity, entry); break; case PREF_CONFLICT: setSpinnerSelections(spnConflict, entry); break; case PREF_ISSUE: setSpinnerSelections(spnIssueType, entry); break; } } catch(Exception ex) { Log.e(MODULE_TAG, ex.getMessage()); } } } } } catch(Exception ex) { Log.e(MODULE_TAG, ex.getMessage()); } } /** * Sets spinner setting from a map entry * @param spinner * @param p */ private void setSpinnerSelection(Spinner spinner, Entry<String, ?> p) { spinner.setSelection(((Integer) p.getValue()).intValue()); } /** * Sets MultiSelectionSpinner settings from a map entry * @param spinner * @param p */ private void setSpinnerSelections(MultiSelectionSpinner spinner, Entry<String, ?> p) { // Retireve entry value String entry = (String) p.getValue(); // Check that values exist if ((null == entry) || entry.equals("")) return; // Split values apart String[] entries; entries = entry.split(","); // Check that values exist if (entries.length < 1) return; // Setting multiple spinner selections require an array of ints int[] selections = new int[entries.length]; // Fill array of ints with settings from entry for (int i = 0; i < entries.length; ++i) { selections[i] = Integer.valueOf(entries[i]); } // Set MultiSelectionSpinner spinner control values spinner.setSelection(selections); } // ********************************************************************************* // * Submitting Answers // ********************************************************************************* /** * Saves UI settings to database */ private void submitAnswers() { DbAdapter dbAdapter = new DbAdapter(this); dbAdapter.open(); // Remove any previous answers from the local database dbAdapter.deleteNoteAnswers(noteId); // Enter the user selections into the local database try { submitSpinnerSelection(spnSeverity, dbAdapter, DbQuestions.NOTE_SEVERITY, DbAnswers.noteSeverity); submitSpinnerSelection(spnConflict, dbAdapter, DbQuestions.NOTE_CONFLICT, DbAnswers.noteConflict, DbAnswers.noteConflictOther); submitSpinnerSelection(spnIssueType, dbAdapter, DbQuestions.NOTE_ISSUE, DbAnswers.noteIssue, DbAnswers.noteIssueOther); setSeverity(spnSeverity, DbAnswers.noteSeverity); } catch(Exception ex) { Log.e(MODULE_TAG, ex.getMessage()); } finally { dbAdapter.close(); } } private void submitSpinnerSelection(Spinner spinner, DbAdapter dbAdapter, int question_id, int[] answer_ids) { submitSpinnerSelection(spinner, dbAdapter, question_id, answer_ids, -1, null); } /** * Enters the spinner selection into the database * @param spinner * @param dbAdapter * @param question_id * @param answers */ private void submitSpinnerSelection(Spinner spinner, DbAdapter dbAdapter, int question_id, int[] answer_ids, int other_id, String other_text) { // Note: The first entry is always blank, the array of answers displayed // by the UI is one greater than the number of answers in the database. int answerIndex = spinner.getSelectedItemPosition() - 1; if (answerIndex >= 0) { if (answer_ids[answerIndex] == other_id) { dbAdapter.addAnswerToNote(noteId, question_id, other_id, other_text); } else { dbAdapter.addAnswerToNote(noteId, question_id, answer_ids[answerIndex]); } } } /** * Enters the MultiSelectionSpinner selections into the database * @param spinner * @param dbAdapter * @param question_id * @param answers */ private void submitSpinnerSelection(MultiSelectionSpinner spinner, DbAdapter dbAdapter, int question_id, int[] answers, int answerOther) { List<Integer> selectedIndicies = spinner.getSelectedIndicies(); for (int index : selectedIndicies) { if ((answerOther >= 0) && (answers[index] == answerOther)) { dbAdapter.addAnswerToNote(noteId, question_id, answers[index], spinner.getOtherText()); } else { dbAdapter.addAnswerToNote(noteId, question_id, answers[index]); } } } private void setSeverity(Spinner spinner, int[] answerIds) { int answerIndex = spinner.getSelectedItemPosition() - 1; if (answerIndex >= 0) { noteSeverity = answerIds[answerIndex]; } else { noteSeverity = -1; } } // ********************************************************************************* // * Transitioning to other activities // ********************************************************************************* private void transitionToPreviousActivity() { // Cancel if (noteSource == EXTRA_NOTE_SOURCE_MAIN_INPUT) { transitionToTabsConfigActivity(); } else if (noteSource == EXTRA_NOTE_SOURCE_TRIP_MAP) { transitionToTripMapActivity(); } } private void transitionToNoteDetailActivity() { // Create intent to go to the NoteDetailActivity Intent intent = new Intent(this, NoteDetailActivity.class); intent.putExtra(NoteDetailActivity.EXTRA_NOTE_SEVERITY, noteSeverity); intent.putExtra(NoteDetailActivity.EXTRA_NOTE_ID, noteId); intent.putExtra(NoteDetailActivity.EXTRA_NOTE_SOURCE, noteSource); // the NoteType activity needs these when the back button // is pressed and we have to restart this activity intent.putExtra(NoteDetailActivity.EXTRA_TRIP_ID, tripId); intent.putExtra(NoteDetailActivity.EXTRA_TRIP_SOURCE, tripSource); // Exit this activity startActivity(intent); overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left); finish(); } private void transitionToTripMapActivity() { // Create intent to go back to the TripMapActivity Intent intent = new Intent(this, TripMapActivity.class); intent.putExtra(TripMapActivity.EXTRA_TRIP_ID, tripId); intent.putExtra(TripMapActivity.EXTRA_TRIP_SOURCE, tripSource); // Exit this activity startActivity(intent); overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right); finish(); } private void transitionToTabsConfigActivity() { // Create intent to go back to the recording screen in the Tabsconfig activity Intent intent = new Intent(this, TabsConfig.class); intent.putExtra(TabsConfig.EXTRA_SHOW_FRAGMENT, TabsConfig.FRAG_INDEX_MAIN_INPUT); // Exit this activity startActivity(intent); overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left); finish(); } }