package org.ohmage.activity; import android.content.Intent; import android.database.ContentObserver; import android.database.Cursor; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.text.Html; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import com.google.android.imageloader.ImageLoader; import org.ohmage.ConfigHelper; import org.ohmage.R; import org.ohmage.UserPreferencesHelper; import org.ohmage.controls.ActionBarControl; import org.ohmage.controls.ActionBarControl.ActionListener; import org.ohmage.db.DbContract.Campaigns; import org.ohmage.db.DbContract.Responses; import org.ohmage.db.DbContract.Surveys; import org.ohmage.db.Models.Campaign; import org.ohmage.db.Models.Survey; import org.ohmage.logprobe.Analytics; import org.ohmage.triggers.base.TriggerDB; import org.ohmage.ui.BaseInfoActivity; import org.ohmage.ui.OhmageFilterable.CampaignFilter; import org.ohmage.ui.OhmageFilterable.CampaignSurveyFilter; import java.text.ParseException; import java.text.SimpleDateFormat; public class SurveyInfoActivity extends BaseInfoActivity implements LoaderManager.LoaderCallbacks<Cursor> { protected static final int TRIGGER_UPDATE_FINISHED = 0; // action bar commands private static final int ACTION_VIEW_RESPHISTORY = 1; private static final int ACTION_SETUP_TRIGGERS = 2; // helpers private FragmentActivity mContext; private ConfigHelper mSharedPreferencesHelper; private ImageLoader mImageLoader; // handles to views we'll be manipulating private TextView mErrorBox; private TextView mDescView; private TextView mCampaignUrnValue; private TextView mStatusValue; private TextView mResponsesValue; private TextView mLastResponseValue; private TextView mTriggersValue; // state vars private int mCampaignStatus; // status code for campaign as of last refresh private Handler mHandler; private ContentObserver mResponsesObserver; private String mSurveyTitle; private String mCampaignUrn; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); // save the context so the action bar can use it to fire off intents mContext = this; mSharedPreferencesHelper = new ConfigHelper(this); mImageLoader = ImageLoader.get(this); // and create a handler attached to this thread for contentobserver events mHandler = new Handler(); // set the campaign-specific info page setContentView(R.layout.survey_info_details); // and inflate all the possible commands into the button tray getLayoutInflater().inflate(R.layout.survey_info_buttons, mButtonTray, true); // clear some things to their default values mNotetext.setVisibility(View.GONE); // nab references to things we'll be populating mErrorBox = (TextView)findViewById(R.id.survey_info_errorbox); mDescView = (TextView)findViewById(R.id.survey_info_desc); mCampaignUrnValue = (TextView)findViewById(R.id.survey_info_campaign_urn_value); mStatusValue = (TextView)findViewById(R.id.survey_info_status_value); mResponsesValue = (TextView)findViewById(R.id.survey_info_responses_value); mLastResponseValue = (TextView)findViewById(R.id.survey_info_last_response_value); mTriggersValue = (TextView)findViewById(R.id.survey_info_triggers_value); // and attach some handlers + populate some html data // status TextView campaignUrnDetails = (TextView)findViewById(R.id.survey_info_campaign_urn_details); campaignUrnDetails.setText(Html.fromHtml(getString(R.string.survey_info_campaign_urn_details))); setDetailsExpansionHandler( findViewById(R.id.survey_info_campaign_urn_row), campaignUrnDetails); // If we aren't in single campaign mode, show the campaign urn details findViewById(R.id.survey_info_campaign_urn_row).setVisibility((ConfigHelper.isSingleCampaignMode()) ? View.GONE : View.VISIBLE); TextView statusDetails = (TextView)findViewById(R.id.survey_info_status_details); statusDetails.setText(Html.fromHtml(getString(R.string.survey_info_status_details))); setDetailsExpansionHandler( findViewById(R.id.survey_info_status_row), statusDetails); // responses TextView responsesDetails = (TextView)findViewById(R.id.survey_info_responses_details); responsesDetails.setText(Html.fromHtml(getString(R.string.survey_info_responses_details))); setDetailsExpansionHandler( findViewById(R.id.survey_info_responses_row), responsesDetails); // last response TextView lastResponseDetails = (TextView)findViewById(R.id.survey_info_last_response_details); lastResponseDetails.setText(Html.fromHtml(getString(R.string.survey_info_last_response_details))); setDetailsExpansionHandler( findViewById(R.id.survey_info_last_response_row), lastResponseDetails); // triggers TextView triggersDetails = (TextView)findViewById(R.id.survey_info_triggers_details); triggersDetails.setText(Html.fromHtml(getString(R.string.survey_info_triggers_details))); setDetailsExpansionHandler( findViewById(R.id.survey_info_triggers_row), triggersDetails); // hide our content behind the overlay before we load setLoadingVisibility(true); // Prepare the loader. Either re-connect with an existing one, // or start a new one. getSupportLoaderManager().initLoader(1, null, this); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch(requestCode) { case TRIGGER_UPDATE_FINISHED: //Triggers might have changed so we update it here setTriggerCount(); break; } } protected void populateCommands(final String surveyID, final String campaignUrn, final String surveyTitle, final String surveySubmitText, int campaignStatus) { // first remove all the commands from the action bar... ActionBarControl actionBar = getActionBarControl(); actionBar.clearActionBarCommands(); // gather up the commands in the command tray so we can hide/show them Button takeSurveyButton = (Button)findViewById(R.id.survey_info_button_takesurvey); // now, depending on the context, we can regenerate our commands // this applies both to the action bar and to the command tray if (campaignStatus == Campaign.STATUS_READY) { // only add response history if show feedback is true if(new UserPreferencesHelper(this).showFeedback()) actionBar.addActionBarCommand(ACTION_VIEW_RESPHISTORY, getString(R.string.response_history_action_button_description), R.drawable.btn_title_resphist); actionBar.addActionBarCommand(ACTION_SETUP_TRIGGERS, getString(R.string.reminder_action_button_description), R.drawable.btn_title_trigger); // route the actions to the appropriate places actionBar.setOnActionListener(new ActionListener() { @Override public void onActionClicked(int commandID) { Intent intent; switch (commandID) { case ACTION_VIEW_RESPHISTORY: intent = new Intent(mContext, ResponseHistoryActivity.class); intent.putExtra(CampaignFilter.EXTRA_CAMPAIGN_URN, campaignUrn); intent.putExtra(CampaignSurveyFilter.EXTRA_SURVEY_ID, surveyID); startActivity(intent); break; case ACTION_SETUP_TRIGGERS: Intent triggerIntent = Survey.launchTriggerIntent(mContext, campaignUrn, new String[] { surveyTitle }); startActivityForResult(triggerIntent, TRIGGER_UPDATE_FINISHED); return; } } }); takeSurveyButton.setEnabled(true); // attach a remove handler takeSurveyButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Analytics.widget(v); // fire off the survey intent Intent intent = new Intent(mContext, SurveyActivity.class); intent.putExtra("campaign_urn", campaignUrn); intent.putExtra("survey_id", surveyID); intent.putExtra("survey_title", surveyTitle); intent.putExtra("survey_submit_text", surveySubmitText); startActivity(intent); } }); } else { takeSurveyButton.setEnabled(false); } } // ======================================================== // === view databinding below, // === describes how this info view shows its data // ======================================================== private interface QueryParams { String[] PROJECTION = { Surveys.SURVEY_ID, Surveys.CAMPAIGN_URN, Surveys.SURVEY_TITLE, Surveys.SURVEY_DESCRIPTION, Surveys.SURVEY_SUBMIT_TEXT, Campaigns.CAMPAIGN_NAME, Campaigns.CAMPAIGN_STATUS, Campaigns.CAMPAIGN_ICON, Surveys.SURVEY_TITLE }; final int SURVEY_ID = 0; final int CAMPAIGN_URN = 1; final int TITLE = 2; final int DESCRIPTION = 3; final int SUBMIT_TEXT = 4; final int CAMPAIGN_NAME = 5; final int CAMPAIGN_STATUS = 6; final int CAMPAIGN_ICON = 7; final int SURVEY_TITLE = 8; } @Override public Loader<Cursor> onCreateLoader(int id, Bundle bundle) { return new CursorLoader(this, getIntent().getData(), QueryParams.PROJECTION, null, null, null); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // ensure that we're on the first record in the cursor if (!data.moveToFirst()) return; // populate the views final String surveyID = data.getString(QueryParams.SURVEY_ID); mSurveyTitle = data.getString(QueryParams.SURVEY_TITLE); mCampaignUrn = data.getString(QueryParams.CAMPAIGN_URN); String submitText = data.getString(QueryParams.SUBMIT_TEXT); // set the header fields first mHeadertext.setText(data.getString(QueryParams.TITLE)); mSubtext.setText(data.getString(QueryParams.CAMPAIGN_NAME)); // If we aren't in single campaign mode, show the campaign name mSubtext.setVisibility((ConfigHelper.isSingleCampaignMode()) ? View.GONE : View.VISIBLE); final String iconUrl = data.getString(QueryParams.CAMPAIGN_ICON); if(iconUrl == null || mImageLoader.bind(mIconView, iconUrl, null) != ImageLoader.BindResult.OK) { mIconView.setImageResource(R.drawable.apple_logo); } // fill in the description mDescView.setText(data.getString(QueryParams.DESCRIPTION)); // hide our error box; it'll become visible below (and filled w/text) if the status is appropriate mErrorBox.setVisibility(View.GONE); mCampaignUrnValue.setText(mCampaignUrn); // set many things on the view according to the campaign status, too mStatusValue.setCompoundDrawablesWithIntrinsicBounds(R.drawable.website_status_other, 0, 0, 0); // start out a default gray sphere mCampaignStatus = data.getInt(QueryParams.CAMPAIGN_STATUS); switch (mCampaignStatus) { case Campaign.STATUS_READY: mStatusValue.setText(R.string.campaign_status_ready); mStatusValue.setCompoundDrawablesWithIntrinsicBounds(R.drawable.website_running, 0, 0, 0); break; case Campaign.STATUS_VAGUE: mStatusValue.setText(R.string.campaign_status_vague); break; case Campaign.STATUS_REMOTE: mStatusValue.setText(R.string.campaign_status_remote); break; case Campaign.STATUS_OUT_OF_DATE: mStatusValue.setText(R.string.campaign_status_out_of_date); mErrorBox.setVisibility(View.VISIBLE); mErrorBox.setText(Html.fromHtml(getString(R.string.campaign_info_errorbox_outofdate))); break; case Campaign.STATUS_NO_EXIST: mStatusValue.setText(R.string.campaign_status_no_exist); mErrorBox.setVisibility(View.VISIBLE); mErrorBox.setText(Html.fromHtml(getString(R.string.campaign_info_errorbox_no_exist))); break; case Campaign.STATUS_STOPPED: mStatusValue.setText(R.string.campaign_status_stopped); mStatusValue.setCompoundDrawablesWithIntrinsicBounds(R.drawable.website_stopped, 0, 0, 0); mErrorBox.setVisibility(View.VISIBLE); mErrorBox.setText(Html.fromHtml(getString(R.string.campaign_info_errorbox_stopped))); break; case Campaign.STATUS_INVALID_USER_ROLE: mStatusValue.setText(R.string.campaign_status_invalid_user_role); mErrorBox.setVisibility(View.VISIBLE); mErrorBox.setText(Html.fromHtml(getString(R.string.campaign_info_errorbox_invalid_role))); break; case Campaign.STATUS_DOWNLOADING: mStatusValue.setText(R.string.campaign_status_downloading); break; default: mStatusValue.setText(R.string.campaign_status_unknown); break; } // create an observer to watch the responses table for changes (hopefully) mResponsesObserver = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { // TODO Auto-generated method stub super.onChange(selfChange); // set the responses by querying the response table // and getting the number of responses submitted for this campaign Cursor responses = getContentResolver().query(Campaigns.buildResponsesUri(mCampaignUrn, surveyID), null, null, null, Responses.RESPONSE_DATE); mResponsesValue.setText(getResources().getQuantityString(R.plurals.campaign_info_response_count, responses.getCount(), responses.getCount())); // also get the date of the last submitted response and populate the last response field if (responses.moveToLast()) { String lastResponseDate = responses.getString(responses.getColumnIndex(Responses.RESPONSE_DATE)); SimpleDateFormat fromUTC = new SimpleDateFormat("yyyy-MM-dd k:mm:ss"); SimpleDateFormat toNice = new SimpleDateFormat("EEE MMM d yyyy, h:mm a"); try { mLastResponseValue.setText(toNice.format(fromUTC.parse(lastResponseDate))); } catch (ParseException e) { mLastResponseValue.setText("(not available)"); } } else mLastResponseValue.setText("(not available)"); responses.close(); } }; // register it to listen for newly submitted responses getContentResolver().registerContentObserver(Responses.CONTENT_URI, true, mResponsesObserver); // and trigger it once to refresh right now mResponsesObserver.onChange(false); // get the number of triggers for this survey setTriggerCount(); // and finally populate the action bar + command tray populateCommands(surveyID, mCampaignUrn, data.getString(QueryParams.TITLE), submitText, mCampaignStatus); // and make the entity header visible (although i assume it already was) mEntityHeader.setVisibility(View.VISIBLE); // finally, show our content setLoadingVisibility(false); } public void setTriggerCount() { TriggerDB trigDB = new TriggerDB(mContext); if (trigDB.open()) { Cursor triggers = trigDB.getSurveyTriggers(mCampaignUrn, mSurveyTitle); mTriggersValue.setText(getResources().getQuantityString(R.plurals.campaign_info_trigger_count, triggers.getCount(), triggers.getCount())); triggers.close(); trigDB.close(); } } @Override public void onLoaderReset(Loader<Cursor> loader) { // FIXME should we hide the entity header like cameron does? } }