org.ohmage.reminders.notif.NotifSurveyAdaptor.java Source code

Java tutorial

Introduction

Here is the source code for org.ohmage.reminders.notif.NotifSurveyAdaptor.java

Source

/*******************************************************************************
 * 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.reminders.notif;

import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.util.Log;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.ohmage.reminders.base.TriggerActionDesc;
import org.ohmage.reminders.base.TriggerBase;
import org.ohmage.reminders.base.TriggerDB;
import org.ohmage.reminders.base.TriggerRunTimeDesc;
import org.ohmage.reminders.base.TriggerTypeMap;
import org.ohmage.reminders.utils.TrigPrefManager;

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;

/*
 * An interface to the survey list management. This class 
 * provides functions to keep track of the survey taken
 * time stamps, to get the list of active surveys and to 
 * get various trigger related JSON strings used while 
 * uploading survey responses. 
 * 
 * This class isolates the logic related to surveys from 
 * the Notifier class thus making the implementation of the
 * Notifier generic and independent of the surveys.  
 */
public class NotifSurveyAdaptor {

    private static final String TAG = "NotifSurveyAdaptor";

    /* Keys used in preparation of the JSON strings */
    private static final String KEY_ACTIVE_TRIGGERS = "active_triggers";
    private static final String KEY_TRIGGER_DESC = "trigger_description";
    private static final String KEY_NOTIF_DESC = "notification_description";
    private static final String KEY_RUNTIME_DESC = "runtime_description";
    private static final String KEY_TRIGGER_TYPE = "trigger_type";
    private static final String KEY_TRIGER_PREF = "trigger_preferences";
    private static final String KEY_SURVEY_LIST = "survey_list";
    private static final String KEY_UNTAKEN_SURVEYS = "surveys_not_taken";
    private static final String KEY_CAMPAIGN_URN = "campaign_urn";

    /*
     * Helper function to prepare the list of active surveys corresponding
     * to trigger.
     *
     * A trigger is active if it has not expired (the notification duration
     * has not been reached) after going off the last time.
     *
     * All surveys associated with an active active trigger are active
     *     - EXCEPT those which have already been taken by the user within
     *       the suppression window.
     */
    private static HashSet<String> getActiveSurveys(Context context, Cursor trig) {

        HashSet<String> actSurveys = new HashSet<String>();

        String runTime = trig.getString(trig.getColumnIndexOrThrow(TriggerDB.KEY_RUNTIME_DESCRIPT));

        String notif = trig.getString(trig.getColumnIndexOrThrow(TriggerDB.KEY_NOTIF_DESCRIPT));

        String actions = trig.getString(trig.getColumnIndexOrThrow(TriggerDB.KEY_TRIG_ACTION_DESCRIPT));

        String campaignUrn = trig.getString(trig.getColumnIndexOrThrow(TriggerDB.KEY_CAMPAIGN_URN));

        Log.v(TAG, "NotifSurveyAdaptor: Calculating active surveys for trigger");

        TriggerRunTimeDesc rtDesc = new TriggerRunTimeDesc();
        NotifDesc notifDesc = new NotifDesc();
        TriggerActionDesc actDesc = new TriggerActionDesc();

        if (!rtDesc.loadString(runTime) || !notifDesc.loadString(notif) || !actDesc.loadString(actions)) {

            Log.e(TAG, "NotifSurveyAdaptor: Descritptor(s) failed to parse");

            return actSurveys;
        }

        if (!rtDesc.hasTriggerTimeStamp()) {
            Log.v(TAG, "NotifSurveyAdaptor: Trigger has not gone off");

            return actSurveys;
        }

        long now = System.currentTimeMillis();
        long trigTS = rtDesc.getTriggerTimeStamp();

        if (trigTS > now) {
            Log.e(TAG, "NotifSurveyAdaptor: Trigger time stamp is in the future!");
            return actSurveys;
        }

        //How long it has been since the trigger went off
        long elapsedMS = now - trigTS;

        long durationMS = notifDesc.getDuration() * 60000;
        long suppressMS = notifDesc.getSuppression() * 60000;

        if (elapsedMS < durationMS) {

            //The trigger has not expired, check each survey

            String[] surveys = actDesc.getSurveys();
            for (int i = 0; i < surveys.length; i++) {

                //Has the survey been taken in within the
                //suppression window?
                if (IsSurveyTakenOrIgnored(context, surveys[i], System.currentTimeMillis() - suppressMS)) {
                    continue;
                }

                //Add the active survey to the set
                actSurveys.add(surveys[i]);
            }
        }

        return actSurveys;
    }

    private static HashSet<String> getSurveys(Cursor trig) {

        HashSet<String> actSurveys = new HashSet<String>();

        String actions = trig.getString(trig.getColumnIndexOrThrow(TriggerDB.KEY_TRIG_ACTION_DESCRIPT));

        Log.v(TAG, "NotifSurveyAdaptor: Calculating surveys for trigger");

        TriggerActionDesc actDesc = new TriggerActionDesc();

        if (!actDesc.loadString(actions)) {

            Log.e(TAG, "NotifSurveyAdaptor: Descritptor(s) failed to parse");

            return actSurveys;
        }

        actSurveys.addAll(Arrays.asList(actDesc.getSurveys()));

        return actSurveys;
    }

    /*
     * Utility function to check if a survey has been taken
     * by the user since a given time. This function checks
     * the time stamp of the survey stored in shared preferences.
     */
    private static boolean IsSurveyTakenOrIgnored(Context context, String survey, long since) {

        SharedPreferences pref = context.getSharedPreferences(NotifSurveyAdaptor.class.getName(),
                Context.MODE_PRIVATE);

        return (pref.contains(survey) && pref.getLong(survey, 0) > since)
                || (pref.contains(survey + "_ignored") && pref.getLong(survey + "_ignored", 0) > since);
    }

    /*
     * Get the list of all surveys active at the moment. This
     * function creates a set of all active surveys from all
     * active triggers.
     *
     * This function is used by the Notifier to prepare the
     * notification item.
     */
    public static Set<String> getAllActiveSurveys(Context context, String campaignUrn) {
        HashSet<String> actSurveys = new HashSet<String>();

        TriggerDB db = new TriggerDB(context);
        db.open();

        Cursor c = db.getAllTriggers(campaignUrn);
        if (c.moveToFirst()) {
            do {

                actSurveys.addAll(getActiveSurveys(context, c));

            } while (c.moveToNext());
        }
        c.close();
        db.close();

        return actSurveys;
    }

    /*
     * Get all the active surveys corresponding to a specific trigger.
     */
    public static Set<String> getActiveSurveysForTrigger(Context context, int trigId) {
        HashSet<String> actSurveys = new HashSet<String>();

        TriggerDB db = new TriggerDB(context);
        db.open();

        Cursor c = db.getTrigger(trigId);
        if (c.moveToFirst()) {
            actSurveys.addAll(getActiveSurveys(context, c));
        }

        c.close();
        db.close();

        return actSurveys;
    }

    public static Set<String> getSurveysForTrigger(Context context, int trigId) {
        HashSet<String> actSurveys = new HashSet<String>();

        TriggerDB db = new TriggerDB(context);
        db.open();

        Cursor c = db.getTrigger(trigId);
        if (c.moveToFirst()) {
            actSurveys.addAll(getSurveys(c));
        }

        c.close();
        db.close();

        return actSurveys;
    }

    /*
     * Saves the current time stamp against the given survey name.
     * This must be called whenever a survey is taken by the user.
     */
    public static void recordSurveyTaken(Context context, String survey) {

        SharedPreferences pref = context.getSharedPreferences(NotifSurveyAdaptor.class.getName(),
                Context.MODE_PRIVATE);

        SharedPreferences.Editor editor = pref.edit();
        editor.putLong(survey, System.currentTimeMillis());
        editor.commit();

        TrigPrefManager.registerPreferenceFile(context, NotifSurveyAdaptor.class.getName());
    }

    public static void clearSurveyTaken(Context context, String survey) {
        SharedPreferences pref = context.getSharedPreferences(NotifSurveyAdaptor.class.getName(),
                Context.MODE_PRIVATE);
        pref.edit().remove(survey).commit();
        TrigPrefManager.registerPreferenceFile(context, NotifSurveyAdaptor.class.getName());
    }

    /*
     * Saves the fact that a survey was ignored by a user
     */
    public static void recordSurveyIgnored(Context context, String survey) {

        SharedPreferences pref = context.getSharedPreferences(NotifSurveyAdaptor.class.getName(),
                Context.MODE_PRIVATE);

        SharedPreferences.Editor editor = pref.edit();
        editor.putLong(survey + "_ignored", System.currentTimeMillis());
        editor.commit();

        TrigPrefManager.registerPreferenceFile(context, NotifSurveyAdaptor.class.getName());
    }

    /*
     * Removes the ignored state of a survey. This should happen when a reminder for a survey goes
     * off
     */
    public static void clearSurveyIgnored(Context context, String survey) {

        SharedPreferences pref = context.getSharedPreferences(NotifSurveyAdaptor.class.getName(),
                Context.MODE_PRIVATE);

        SharedPreferences.Editor editor = pref.edit();
        editor.remove(survey + "_ignored");
        editor.commit();

        TrigPrefManager.registerPreferenceFile(context, NotifSurveyAdaptor.class.getName());
    }

    /*
     * Utility function to add the JSON description of a active
     * trigger to JSON array.
     */
    private static void addTriggerInfoToArray(Context context, Cursor trig, JSONArray jArray) {
        String rtDesc = trig.getString(trig.getColumnIndexOrThrow(TriggerDB.KEY_RUNTIME_DESCRIPT));
        TriggerRunTimeDesc desc = new TriggerRunTimeDesc();
        desc.loadString(rtDesc);

        String notifDesc = trig.getString(trig.getColumnIndexOrThrow(TriggerDB.KEY_NOTIF_DESCRIPT));

        String trigDesc = trig.getString(trig.getColumnIndexOrThrow(TriggerDB.KEY_TRIG_DESCRIPT));

        String trigType = trig.getString(trig.getColumnIndexOrThrow(TriggerDB.KEY_TRIG_TYPE));

        String campaignUrn = trig.getString(trig.getColumnIndexOrThrow(TriggerDB.KEY_CAMPAIGN_URN));

        JSONObject jPref = new JSONObject();
        TriggerBase trigBase = new TriggerTypeMap().getTrigger(trigType);
        if (trigBase != null) {
            jPref = trigBase.getPreferences(context);
        }

        JSONObject jTrigInfo = new JSONObject();

        try {
            jTrigInfo.put(KEY_TRIGGER_TYPE, trigType);
            jTrigInfo.put(KEY_TRIGGER_DESC, new JSONObject(trigDesc));
            jTrigInfo.put(KEY_NOTIF_DESC, new JSONObject(notifDesc));
            jTrigInfo.put(KEY_RUNTIME_DESC, new JSONObject(desc.toHumanReadableString()));
            jTrigInfo.put(KEY_TRIGER_PREF, jPref);
            jTrigInfo.put(KEY_CAMPAIGN_URN, campaignUrn);
        } catch (JSONException e) {
            return;
        }

        jArray.put(jTrigInfo);
    }

    /*
     * Get the JSON array containing the details of all the triggers
     * which have activated a given survey at the moment.
     */
    public static JSONArray getActiveTriggerInfo(Context context, String campaignUrn, String survey) {
        JSONObject jInfo = new JSONObject();
        JSONArray jTrigs = new JSONArray();

        TriggerDB db = new TriggerDB(context);
        db.open();

        Cursor c = db.getAllTriggers(campaignUrn);
        if (c.moveToFirst()) {
            do {
                if (getActiveSurveys(context, c).contains(survey)) {
                    addTriggerInfoToArray(context, c, jTrigs);
                }

            } while (c.moveToNext());
        }
        c.close();
        db.close();

        try {
            jInfo.put(KEY_ACTIVE_TRIGGERS, jTrigs);
        } catch (JSONException e) {
            return null;
        }

        return jTrigs;
    }

    /*
     * To be called when a trigger expires. This function logs using
     * LogProbe, the list of all surveys not taken by the user
     * but were activated by the given trigger.
     */
    public static void handleExpiredTrigger(Context context, int trigId) {
        TriggerDB db = new TriggerDB(context);
        db.open();

        String sActDesc = db.getActionDescription(trigId);
        String sTrigDesc = db.getTriggerDescription(trigId);
        String sTrigType = db.getTriggerType(trigId);
        String sRTDesc = db.getRunTimeDescription(trigId);
        TriggerDB.Campaign sCampaign = db.getCampaignInfo(trigId);

        db.close();

        if (sActDesc == null || sTrigDesc == null || sTrigType == null || sRTDesc == null) {

            return;
        }

        TriggerActionDesc actDesc = new TriggerActionDesc();
        if (!actDesc.loadString(sActDesc)) {
            return;
        }

        TriggerRunTimeDesc rtDesc = new TriggerRunTimeDesc();
        if (!rtDesc.loadString(sRTDesc)) {
            return;
        }

        LinkedList<String> untakenList = new LinkedList<String>();
        for (String survey : actDesc.getSurveys()) {

            if (!IsSurveyTakenOrIgnored(context, survey, rtDesc.getTriggerTimeStamp())) {

                untakenList.add(survey);
            }
        }

        if (untakenList.size() == 0) {
            return;
        }

        JSONArray jSurveyList = new JSONArray();
        for (String survey : actDesc.getSurveys()) {
            jSurveyList.put(survey);
        }

        JSONArray jUntakenSurveys = new JSONArray();
        for (String unTakenSurvey : untakenList) {
            jUntakenSurveys.put(unTakenSurvey);
        }

        JSONObject jExpired = new JSONObject();

        try {
            jExpired.put(KEY_TRIGGER_TYPE, sTrigType);
            jExpired.put(KEY_TRIGGER_DESC, new JSONObject(sTrigDesc));
            jExpired.put(KEY_SURVEY_LIST, jSurveyList);
            jExpired.put(KEY_UNTAKEN_SURVEYS, jUntakenSurveys);
            if (sCampaign != null)
                jExpired.put(KEY_CAMPAIGN_URN, sCampaign.urn);
        } catch (JSONException e) {
            return;
        }

        //Log the info
        Log.v(TAG, "Expired trigger has surveys not taken: " + jExpired.toString());
    }
}