/******************************************************************************* * Copyright 2011 The Regents of the University of California * * 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 org.ohmage.triggers.types.location; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.EditTextPreference; import android.preference.ListPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.text.InputType; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; import org.ohmage.R; import org.ohmage.logprobe.Analytics; import org.ohmage.logprobe.Log; import org.ohmage.logprobe.LogProbe.Status; import org.ohmage.triggers.base.TriggerActionDesc; import org.ohmage.triggers.config.TrigUserConfig; import org.ohmage.triggers.ui.ActionSelectorView; import org.ohmage.triggers.ui.TriggerListActivity; import org.ohmage.triggers.utils.TimePickerPreference; import org.ohmage.triggers.utils.TrigListPreference; import java.util.Arrays; /* * Editor activity for location based trigger. This activity is * invoked for creating a new location trigger and also for editing * an existing trigger. */ public class LocTrigEditActivity extends PreferenceActivity implements OnPreferenceClickListener, OnPreferenceChangeListener, OnClickListener { private static final String TAG = "LocTrigEditActivity"; public static final String KEY_TRIG_DESC = LocTrigEditActivity.class.getName() + ".trigger_descriptor"; public static final String KEY_TRIG_ID = LocTrigEditActivity.class.getName() + ".trigger_id"; public static final String KEY_ADMIN_MODE = LocTrigEditActivity.class.getName() + ".admin_mode"; public static final String KEY_ACT_DESC = LocTrigEditActivity.class.getName() + ".act_desc"; private static final String PREF_KEY_LOCATION = "trigger_location"; private static final String PREF_KEY_ENABLE_RANGE = "enable_time_range"; private static final String PREF_KEY_START_TIME = "interval_start_time"; private static final String PREF_KEY_END_TIME = "interval_end_time"; private static final String PREF_KEY_TRIGGER_ALWAYS = "trigger_always"; private static final String PREF_KEY_RENETRY_INTERVAL = "minimum_reentry"; private static final String PREF_KEY_ACTIONS = "actions"; private static final int DIALOG_ID_INVALID_TIME_ALERT = 0; private static final int DIALOG_ID_DEFINE_LOC_PROMPT = 1; private static final int DIALOG_ID_ACTION_SEL = 2; private static final int DIALOG_ID_NO_SURVEYS_SELECTED = 3; private boolean mAdminMode = false; /* * Interface for the callback which is invoked when the * done button is pressed. */ public interface ExitListener { public void onDone(Context context, int trigId, String trigDesc, String actDesc); } private LocTrigDesc mTrigDesc; private TriggerActionDesc mActDesc; private static ExitListener mExitListener = null; private int mTrigId = 0; private String[] mCategories; private String[] mActions; private boolean[] mActSelected = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.trig_loc_edit_preferences); setContentView(R.layout.trigger_editor); mTrigDesc = new LocTrigDesc(); mActDesc = new TriggerActionDesc(); if(getIntent().hasExtra(TriggerListActivity.KEY_ACTIONS)) { mActions = getIntent().getStringArrayExtra(TriggerListActivity.KEY_ACTIONS); } else { Log.e(TAG, "LocTrigEditActivity: Invoked with out passing surveys"); finish(); return; } PreferenceScreen screen = getPreferenceScreen(); int prefCount = screen.getPreferenceCount(); for(int i = 0; i < prefCount; i++) { screen.getPreference(i).setOnPreferenceClickListener(this); screen.getPreference(i).setOnPreferenceChangeListener(this); } ((Button) findViewById(R.id.trig_edit_done)).setOnClickListener(this); ((Button) findViewById(R.id.trig_edit_cancel)).setOnClickListener(this); //Set global value for minimum reentry interval EditTextPreference minIntervalPref = (EditTextPreference) getPreferenceScreen() .findPreference(PREF_KEY_RENETRY_INTERVAL); minIntervalPref.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER); minIntervalPref.setSummary(mTrigDesc.getMinReentryInterval() + " minutes"); mAdminMode = getIntent().getBooleanExtra(KEY_ADMIN_MODE, false); String config = null; String action = null; if(savedInstanceState == null) { config = getIntent().getStringExtra(KEY_TRIG_DESC); action = getIntent().getStringExtra(KEY_ACT_DESC); } else { config = savedInstanceState.getString(KEY_TRIG_DESC); action = savedInstanceState.getString(KEY_ACT_DESC); } if(config != null) { mTrigId = getIntent().getIntExtra(KEY_TRIG_ID, 0); if(mTrigDesc.loadString(config) && mActDesc.loadString(action)) { initializeGUI(); } else { getPreferenceScreen().setEnabled(false); Toast.makeText(this, R.string.trigger_invalid_settings, Toast.LENGTH_SHORT).show(); } } // if there are any preselected actions specified when the activity is first created // and there's currently nothing in the action description, load the selected options // into the action description as if they were previously selected if (savedInstanceState == null && mActDesc.getCount() <= 0 && getIntent().hasExtra(TriggerListActivity.KEY_PRESELECTED_ACTIONS)) { String[] preselectedActions = getIntent().getStringArrayExtra(TriggerListActivity.KEY_PRESELECTED_ACTIONS); for (int i = 0; i < preselectedActions.length; ++i) { mActDesc.addSurvey(preselectedActions[i]); } updateActionsPrefStatus(); } } @Override public void onResume() { super.onResume(); Analytics.activity(this, Status.ON); initializeCategories(); TrigListPreference locPref = (TrigListPreference) getPreferenceScreen() .findPreference(PREF_KEY_LOCATION); locPref.setEntries(mCategories); locPref.setEntryValues(mCategories); if(mCategories.length == 0) { locPref.setEnabled(false); } else { if(locPref.getValue() == null || !Arrays.asList(mCategories).contains(locPref.getValue())) { locPref.setValue(mCategories[0]); locPref.setSummary(mCategories[0]); } } //The cancel button prompts the user to //launch the settings activity locPref.setOnCancelListener( new TrigListPreference.onCancelListener() { @Override public void onCancel() { new LocationTrigger().launchSettingsEditActivity(LocTrigEditActivity.this, mAdminMode); } }); } @Override protected void onPause() { super.onPause(); Analytics.activity(this, Status.OFF); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); updateTriggerDesc(); outState.putString(KEY_TRIG_DESC, mTrigDesc.toString()); outState.putString(KEY_ACT_DESC, mActDesc.toString()); } @Override protected Dialog onCreateDialog(int id) { switch(id) { case DIALOG_ID_ACTION_SEL: return createEditActionDialog(); case DIALOG_ID_INVALID_TIME_ALERT: final String msgErr = "Make sure that the End Time is after the Start Time"; return new AlertDialog.Builder(this) .setTitle("Invalid time settings!") .setNegativeButton("Cancel", null) .setMessage(msgErr) .create(); case DIALOG_ID_NO_SURVEYS_SELECTED: final String msgErrNoSurveys = "Make sure that at least one survey is selected"; return new AlertDialog.Builder(this) .setTitle("No survey selected!") .setNegativeButton("Cancel", null) .setMessage(msgErrNoSurveys) .create(); case DIALOG_ID_DEFINE_LOC_PROMPT: final String msgPrompt = "'" + mTrigDesc.getLocation() + "' " + "is not defined yet. Do you want to define it now?"; return new AlertDialog.Builder(this) .setTitle("Location not defined") .setNegativeButton("No", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { LocTrigEditActivity.this.finish(); } }) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { launchMapsActivity(mTrigDesc.getLocation()); LocTrigEditActivity.this.finish(); } }) .setMessage(msgPrompt) .create(); } return null; } private void initializeCategories() { LocTrigDB db = new LocTrigDB(this); db.open(); Cursor c = db.getAllCategories(); mCategories = new String[c.getCount()]; int i = 0; if(c.moveToFirst()) { do { mCategories[i++] = c.getString( c.getColumnIndexOrThrow(LocTrigDB.KEY_NAME)); } while(c.moveToNext()); } c.close(); db.close(); } private int getLocationCount(String categName) { LocTrigDB db = new LocTrigDB(this); db.open(); Cursor cCateg = db.getCategory(categName); int categId = -1; if(cCateg.moveToFirst()) { categId = cCateg.getInt( cCateg.getColumnIndexOrThrow(LocTrigDB.KEY_ID)); } cCateg.close(); int count = 0; if(categId != -1) { Cursor cLocs = db.getLocations(categId); if(cLocs.moveToFirst()) { count = cLocs.getCount(); } cLocs.close(); } db.close(); return count; } public static void setOnExitListener(ExitListener listener) { mExitListener = listener; } private void initializeGUI() { ListPreference locPref = (ListPreference) getPreferenceScreen() .findPreference(PREF_KEY_LOCATION); CheckBoxPreference rangePref = (CheckBoxPreference) getPreferenceScreen() .findPreference(PREF_KEY_ENABLE_RANGE); CheckBoxPreference alwaysPref = (CheckBoxPreference) getPreferenceScreen() .findPreference(PREF_KEY_TRIGGER_ALWAYS); TimePickerPreference startPref = (TimePickerPreference) getPreferenceScreen() .findPreference(PREF_KEY_START_TIME); TimePickerPreference endPref = (TimePickerPreference) getPreferenceScreen() .findPreference(PREF_KEY_END_TIME); EditTextPreference minIntervalPref = (EditTextPreference) getPreferenceScreen() .findPreference(PREF_KEY_RENETRY_INTERVAL); Preference actionsPref = getPreferenceScreen().findPreference(PREF_KEY_ACTIONS); if(!mAdminMode && !TrigUserConfig.editTriggerActions) { actionsPref.setEnabled(false); } updateActionsPrefStatus(); locPref.setValue(mTrigDesc.getLocation()); locPref.setSummary(mTrigDesc.getLocation()); if(mTrigDesc.isRangeEnabled()) { rangePref.setChecked(true); if(mTrigDesc.shouldTriggerAlways()) { alwaysPref.setChecked(true); } startPref.setTime(mTrigDesc.getStartTime()); endPref.setTime(mTrigDesc.getEndTime()); } locPref.setEnabled(mAdminMode || TrigUserConfig.editLocationTrigger); rangePref.setEnabled(mAdminMode || TrigUserConfig.editLocationTrigger); alwaysPref.setEnabled(mAdminMode || TrigUserConfig.editLocationTrigger); startPref.setEnabled(mAdminMode || TrigUserConfig.editLocationTrigger); endPref.setEnabled(mAdminMode || TrigUserConfig.editLocationTrigger); ((Button) findViewById(R.id.trig_edit_done)) .setEnabled(mAdminMode || TrigUserConfig.editLocationTrigger); minIntervalPref.setSummary(mTrigDesc.getMinReentryInterval() + " minutes"); } @Override public boolean onPreferenceClick(Preference pref) { if(pref.getKey().equals(PREF_KEY_ACTIONS)) { removeDialog(DIALOG_ID_ACTION_SEL); showDialog(DIALOG_ID_ACTION_SEL); } return false; } @Override public boolean onPreferenceChange(Preference pref, Object val) { if(pref.getKey().equals(PREF_KEY_LOCATION)) { if(val instanceof String) { pref.setSummary((String) val); } } else if(pref.getKey().equals(PREF_KEY_RENETRY_INTERVAL)) { if(val instanceof String) { try { int interval = Integer.parseInt((String) val); if(interval > 0) { mTrigDesc.setMinReentryInterval(interval); pref.setSummary(interval + " minutes"); } } catch (NumberFormatException e) {} } } return true; } private void updateTriggerDesc() { ListPreference locPref = (ListPreference) getPreferenceScreen() .findPreference(PREF_KEY_LOCATION); CheckBoxPreference rangePref = (CheckBoxPreference) getPreferenceScreen() .findPreference(PREF_KEY_ENABLE_RANGE); CheckBoxPreference alwaysPref = (CheckBoxPreference) getPreferenceScreen() .findPreference(PREF_KEY_TRIGGER_ALWAYS); TimePickerPreference startPref = (TimePickerPreference) getPreferenceScreen() .findPreference(PREF_KEY_START_TIME); TimePickerPreference endPref = (TimePickerPreference) getPreferenceScreen() .findPreference(PREF_KEY_END_TIME); mTrigDesc.setLocation(locPref.getValue()); if(rangePref.isChecked()) { mTrigDesc.setRangeEnabled(true); mTrigDesc.setTriggerAlways(alwaysPref.isChecked()); mTrigDesc.setStartTime(startPref.getTime()); mTrigDesc.setEndTime(endPref.getTime()); } else { mTrigDesc.setRangeEnabled(false); mTrigDesc.setTriggerAlways(false); } } private void updateActionsPrefStatus() { Preference actionsPref = getPreferenceScreen() .findPreference(PREF_KEY_ACTIONS); if (mActDesc.getSurveys().length > 0) { actionsPref.setSummary(stringArrayToString(mActDesc.getSurveys())); } else { actionsPref.setSummary("None"); } } private String stringArrayToString(String [] strings) { if (strings.length == 0) { return ""; } String string = ""; for (String s : strings) { string = string.concat(s).concat(", "); } return string.substring(0, string.length() - 2); } private void launchMapsActivity(String categName) { LocTrigDB db = new LocTrigDB(this); db.open(); Cursor c = db.getCategory(categName); if(c.moveToFirst()) { int categId = c.getInt( c.getColumnIndexOrThrow(LocTrigDB.KEY_ID)); Intent i = new Intent(this, LocTrigMapsActivity.class); i.putExtra(LocTrigDB.KEY_ID, categId); startActivity(i); } c.close(); db.close(); } @Override public void onClick(View v) { switch(v.getId()) { case R.id.trig_edit_done: if(mExitListener != null) { updateTriggerDesc(); if(!mTrigDesc.validate()) { showDialog(DIALOG_ID_INVALID_TIME_ALERT); return; } else if (mActDesc.getSurveys().length <= 0) { // if no surveys were selected, tell the user and abort showDialog(DIALOG_ID_NO_SURVEYS_SELECTED); return; } mExitListener.onDone(this, mTrigId, mTrigDesc.toString(), mActDesc.toString()); //If the location is not defined yet, prompt the //user to do so if(getLocationCount(mTrigDesc.getLocation()) == 0) { showDialog(DIALOG_ID_DEFINE_LOC_PROMPT); return; } } break; } finish(); } private Dialog createEditActionDialog() { if(mActSelected == null) { mActSelected = new boolean[mActions.length]; for(int i = 0; i < mActSelected.length; i++) { mActSelected[i] = mActDesc.hasSurvey(mActions[i]); } } AlertDialog.Builder builder = new AlertDialog.Builder(this) .setTitle(R.string.trigger_select_actions) .setNegativeButton(R.string.cancel, null) .setView(new ActionSelectorView(getBaseContext(), mActions, mActSelected)); /* AlertDialog.Builder builder = new AlertDialog.Builder(this) .setTitle("Select surveys") .setNegativeButton("Cancel", null) .setMultiChoiceItems(mActions, mActSelected, new DialogInterface.OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { mActSelected[which] = isChecked; } }); */ if(mAdminMode || TrigUserConfig.editTriggerActions) { builder.setPositiveButton("Done", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mActDesc.clearAllSurveys(); for(int i = 0; i < mActSelected.length; i++) { if(mActSelected[i]) { mActDesc.addSurvey(mActions[i]); } } dialog.dismiss(); updateActionsPrefStatus(); // handleActionSelection(mDialogTrigId, desc); } }); } return builder.create(); } }