Android Open Source - rhetolog Rhetolog Content Provider






From Project

Back to project page rhetolog.

License

The source code is released under:

Copyright (c) 2012 Kirk Zurell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Soft...

If you think the Android project rhetolog listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package name.zurell.kirk.apps.android.rhetolog;
/*from   w w w .j  a  v  a  2  s .  c o m*/
/*
 * Copyright (c) 2012 Kirk Zurell
 *
 * See the file LICENSE for copying permission.
 */

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.UUID;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.UriMatcher;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteCursorDriver;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteQuery;
import android.database.sqlite.SQLiteQueryBuilder;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.util.Log;

public class RhetologContentProvider extends ContentProvider implements ContentProvider.PipeDataWriter<Cursor> {

  // Ensure content provider access methods are thread safe.
  
  
  @SuppressWarnings("unused")
  private static final String TAG = RhetologContentProvider.class.getSimpleName();
  
  private MainDatabaseHelper mOpenHelper;

  private static final class URI_TYPES {
    private static final int EVENTTABLE = 1;     // Catalogue of event records.
    private static final int EVENT = 2;          // Individual event record.
    private static final int SESSIONSTABLE = 3;    // Catalogue of session records.
    private static final int SESSION = 4;      // Individual session record.
    private static final int PARTICIPANTTABLE = 5;   // Catalogue of participant records.
    private static final int PARTICIPANT = 6;        // Individual participant record.
    
    private static final int SESSIONPARTICIPANTTABLE = 7;  // Catalogue of participant records for a specific session.
    private static final int SESSIONPARTICIPANT = 8;      
    
    private static final int SESSIONEVENTTABLE = 9; 
    private static final int SESSIONEVENT = 10; 
    
    private static final int REPORT_CSV = 11;
    
    private static final int EVENTSCOUNTSESSION = 12;

    private static final int SESSIONPARTICIPANTEVENTTABLE = 13; // Catalogue of event records for a particular session and participant.
    
    private static final int SESSIONSCURRENT = 14;
    private static final int PARTICIPANTSCURRENTSESSION = 15;
    private static final int EVENTSCURRENTSESSION = 16;
    
  }
  
  // Add sdcard and write external storage permission from manifest.
  private static final String DBNAME = "rhetolog";
  
  
  public static final String EVENTS_TABLENAME = "events";
  public static final String SESSIONS_TABLENAME = "sessions";
  public static final String PARTICIPANTS_TABLENAME = "participants";
  
  
  private SQLiteDatabase db;

  /** The types of URIs serviced by this ContentProvider */
  private static UriMatcher sUriMatcher;
  static  {
    sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    
    // Events
    // content://authority/events  --- list of all events
    sUriMatcher.addURI(RhetologContract.AUTHORITY, EVENTS_TABLENAME, URI_TYPES.EVENTTABLE);
    // content://authority/events/# --- one event: one participant received one fallacy at one time.
    sUriMatcher.addURI(RhetologContract.AUTHORITY, EVENTS_TABLENAME + "/#", URI_TYPES.EVENT);
    
    // content://authority/sessions --- all sessions
    sUriMatcher.addURI(RhetologContract.AUTHORITY, SESSIONS_TABLENAME, URI_TYPES.SESSIONSTABLE);
    // content://authority/sessions/# --- one session
    sUriMatcher.addURI(RhetologContract.AUTHORITY, SESSIONS_TABLENAME + "/#", URI_TYPES.SESSION);
    
    // content://authority/participants --- all participants 
    sUriMatcher.addURI(RhetologContract.AUTHORITY, PARTICIPANTS_TABLENAME, URI_TYPES.PARTICIPANTTABLE);
    // content://authority/participants/# --- one participant
    sUriMatcher.addURI(RhetologContract.AUTHORITY, PARTICIPANTS_TABLENAME + "/#", URI_TYPES.PARTICIPANT);
    
    // content://authority/sessions/#/participant --- several participants from one session
    sUriMatcher.addURI(RhetologContract.AUTHORITY, SESSIONS_TABLENAME + "/#/" + PARTICIPANTS_TABLENAME, URI_TYPES.SESSIONPARTICIPANTTABLE);
    // content://authority/sessions/#/participant/# --- one participant from one session
    sUriMatcher.addURI(RhetologContract.AUTHORITY, SESSIONS_TABLENAME + "/#/" + PARTICIPANTS_TABLENAME + "/#", URI_TYPES.SESSIONPARTICIPANT);
    
    // content://authority/sessions/#/events --- all events from a particular session
    sUriMatcher.addURI(RhetologContract.AUTHORITY, SESSIONS_TABLENAME + "/#/" + EVENTS_TABLENAME, URI_TYPES.SESSIONEVENTTABLE);
    // content://authority/sessions/#/events/fallacy/$ --- all events from a particular session that match a particular fallacy
    sUriMatcher.addURI(RhetologContract.AUTHORITY, SESSIONS_TABLENAME + "/#/" + EVENTS_TABLENAME + "/#", URI_TYPES.SESSIONEVENT);
    
    // content://authority/sessions/report/# --- all records from a particular session in CSV
    sUriMatcher.addURI(RhetologContract.AUTHORITY, SESSIONS_TABLENAME + "/report/#" , URI_TYPES.REPORT_CSV);
    
    // content://authority/events/count/session/# --- count of events for a particular session
    sUriMatcher.addURI(RhetologContract.AUTHORITY, EVENTS_TABLENAME + "/count/session/#", URI_TYPES.EVENTSCOUNTSESSION);
    
    // content://authority/sessions/#/participants/#/events --- all event records for a particular session and participant
    sUriMatcher.addURI(RhetologContract.AUTHORITY, SESSIONS_TABLENAME 
                            + "/" + PARTICIPANTS_TABLENAME 
                            + "/" + EVENTS_TABLENAME
                            + "/#/#" 
                            , 
                          URI_TYPES.SESSIONPARTICIPANTEVENTTABLE);
    
    
    // content://authority/sessions/current --- session record of current session
    sUriMatcher.addURI(RhetologContract.AUTHORITY, SESSIONS_TABLENAME + "/current", URI_TYPES.SESSIONSCURRENT);
    // content://authority/participants/currentsession --- participants of current session
    sUriMatcher.addURI(RhetologContract.AUTHORITY, PARTICIPANTS_TABLENAME + "/currentsession", URI_TYPES.PARTICIPANTSCURRENTSESSION);
    // content://authority/events/currentsession --- events from current session
    sUriMatcher.addURI(RhetologContract.AUTHORITY, EVENTS_TABLENAME + "/currentsession", URI_TYPES.EVENTSCURRENTSESSION);
    
  }
  
  
  /** Session management API, additional to ContentProvider 
   *  The contentprovider maintains a "current" session for the MainActivity to use.
   *  This is simpler than passing URIs and longs around.
   *  
   */
  
  private long currentSession = 0;
  
  public static final String CURRENTSESSIONEXTRA = "CURRENTSESSIONEXTRA";
  private static final String RHETOLOGCONTENTPREFS = "RHETOLOGCONTENTPREFS";
  private static final String CURRENTSESSIONPREFERENCE = "CURRENTSESSIONPREFERENCE";
  
  public Bundle /* unused */ setCurrentSession(String arg, Bundle extraArgs) {
    long toset = Long.valueOf(arg);
    doSetCurrentSession(toset);
    return null;
  }
  
  private void doSetCurrentSession(long newSession) {
    currentSession = newSession;
    
    // Store in shared preference
    SharedPreferences sp  = getContext().getSharedPreferences(RHETOLOGCONTENTPREFS, Context.MODE_PRIVATE);
    SharedPreferences.Editor ed = sp.edit();
    ed.putLong(CURRENTSESSIONPREFERENCE, currentSession);
    ed.commit();
    
    // Notify onlookers of new (current) session record.
    getContext().getContentResolver().notifyChange(RhetologContract.SESSIONSCURRENT_URI, null);
    
    // Notify onlookers watching count of events
    getContext().getContentResolver().notifyChange(RhetologContract.EVENTSCOUNTSESSION_URI, null);
    
    // Notify onlookers of new (current) list of participants.
    getContext().getContentResolver().notifyChange(RhetologContract.PARTICIPANTSCURRENTSESSION_URI, null);
  }
  
  
  public Bundle getCurrentSession(String unusedArg, Bundle unusedExtraArgs) {
    
    Bundle returnSessionLong = new Bundle();
    returnSessionLong.putLong(CURRENTSESSIONEXTRA, currentSession);
    
    return returnSessionLong;
  }
  
  
  /** Helper method
   * 
   */
  
  private void createFirstSession() {
    // Create initial session record.
    ContentValues cv = new ContentValues();
    cv.put(RhetologContract.SessionsColumns.TITLE, "First Session");
    cv.put(RhetologContract.SessionsColumns.UUID, UUID.randomUUID().toString());
    long newCurrentSession = db.insert(SESSIONS_TABLENAME, null, cv);
    
    doSetCurrentSession(newCurrentSession);
  }
  
  
  /** ContentProvider methods. */
  
  @Override
  public boolean onCreate() {

    mOpenHelper = new MainDatabaseHelper(getContext());

    db = mOpenHelper.getWritableDatabase();
    
    if (db == null) {
      return false;
    }
      
    // Retrieve current session long from preferences, or create new session record and use.
    SharedPreferences sp = getContext().getSharedPreferences(RHETOLOGCONTENTPREFS, Context.MODE_PRIVATE);
    long possibleSession = sp.getLong(CURRENTSESSIONPREFERENCE, 0);
    if(possibleSession == 0) {
      createFirstSession();
    } else {
      doSetCurrentSession(possibleSession);
    }
    
    return true;
  }

  
  /* (non-Javadoc)
   * @see android.content.ContentProvider#call(java.lang.String, java.lang.String, android.os.Bundle)
   */
  @Override
  public Bundle call(String method, String arg, Bundle extras) {
    
    if (method.contentEquals("getCurrentSession")) {
      return getCurrentSession(arg, extras);
    } else if (method.contentEquals("setCurrentSession")) {
      return setCurrentSession(arg, extras);
    }
    
    return super.call(method, arg, extras);
  }
  
  
  

  @Override
  synchronized public String getType(Uri uri) {
    
    String result = null;
    
    switch(sUriMatcher.match(uri)) {
    
    case URI_TYPES.EVENTTABLE:
    case URI_TYPES.SESSIONEVENTTABLE:
    case URI_TYPES.SESSIONPARTICIPANTEVENTTABLE:
    case URI_TYPES.EVENTSCURRENTSESSION:
      result = RhetologContract.RHETOLOG_TYPE_EVENTTABLE;
      break;
      
    case URI_TYPES.EVENT:
    case URI_TYPES.SESSIONEVENT:
      result = RhetologContract.RHETOLOG_TYPE_EVENT;
      break;
      
    case URI_TYPES.SESSIONSTABLE:
      result = RhetologContract.RHETOLOG_TYPE_SESSIONTABLE;
      break;
      
    case URI_TYPES.SESSION:
    case URI_TYPES.SESSIONSCURRENT:
      result = RhetologContract.RHETOLOG_TYPE_SESSION;
      break;
      
    case URI_TYPES.PARTICIPANTTABLE:
    case URI_TYPES.SESSIONPARTICIPANTTABLE:
    case URI_TYPES.PARTICIPANTSCURRENTSESSION:
      result = RhetologContract.RHETOLOG_TYPE_PARTICIPANTTABLE;
      break;
      
    case URI_TYPES.PARTICIPANT:
    case URI_TYPES.SESSIONPARTICIPANT:
      result = RhetologContract.RHETOLOG_TYPE_PARTICIPANT;
      break;
      
    case URI_TYPES.REPORT_CSV:
      result = RhetologContract.RHETOLOG_TYPE_SESSION_REPORT;
      break;
      
    case URI_TYPES.EVENTSCOUNTSESSION:
      result = RhetologContract.RHETOLOG_TYPE_EVENTSCOUNTSESSION;
      break;
      
    default:
      throw new IllegalArgumentException("Unknown URI: " + uri);
    }
    
    return result;
  }
  
  
  
  @Override
  public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
  
    String[] result = null; 
    
    switch (sUriMatcher.match(uri)) {
    
    case URI_TYPES.REPORT_CSV:  
      String[] csvResult = {RhetologContract.RHETOLOG_TYPE_SESSION_REPORT};
      result = csvResult;
      break;

    default:
      return super.getStreamTypes(uri, mimeTypeFilter);
      
    }
    
    return result;
    
  }
  
  

    
  // Could make this recursive?
  @Override
  synchronized public int delete(Uri uri, String selection, String[] selectionArgs) {
    
    // To add to selectionArgs.
    ArrayList<String> completeArgs = new ArrayList<String>();
    if (selectionArgs != null) {
      completeArgs.addAll(Arrays.asList(selectionArgs));
    }
    
    boolean session_stats_changed = false;
    
    Uri notifyUri = null;
    int count = 0;
    
    switch(sUriMatcher.match(uri)) {
    
    case URI_TYPES.EVENTTABLE:
      // All events of particular session
      notifyUri = RhetologContract.EVENTS_URI;
      session_stats_changed = true;
      
      String[] finalETArgs = new String[completeArgs.size()];
      finalETArgs = completeArgs.toArray(finalETArgs);
      
      count = db.delete(EVENTS_TABLENAME, selection, finalETArgs);
      break;

    case URI_TYPES.EVENT:
      String eventId = uri.getPathSegments().get(1);
      if (!TextUtils.isEmpty(selection)) {
        selection = " ( " + selection + " ) AND ";
      } else {
        selection = "";
      }
      selection = selection + " ( " + RhetologContract.EventsColumns._ID + " = ? ) ";
      completeArgs.add(eventId);
      String[] finalEArgs = new String[completeArgs.size()];
      finalEArgs = completeArgs.toArray(finalEArgs);
      count = db.delete(EVENTS_TABLENAME, selection, finalEArgs);
      if (count > 0) {
        session_stats_changed = true;
        notifyUri = RhetologContract.EVENTS_URI;
      }
      break;

//    case URI_TYPES.SESSIONSTABLE:
//      break;

    case URI_TYPES.SESSION: // Checked
      String sessionId = uri.getPathSegments().get(1);
      boolean amDeletingCurrentSession = (Long.valueOf(sessionId) == currentSession);
      
      if (!TextUtils.isEmpty(selection)) {
        selection = " ( " + selection + " ) AND ";
      } else
        selection = "";
      selection += " ( " + RhetologContract.SessionsColumns._ID + " = ? ) ";
      completeArgs.add(sessionId);
      String[] finalSArgs = new String[completeArgs.size()];
      finalSArgs = completeArgs.toArray(finalSArgs);
      
      // Delete session.
      count = db.delete(SESSIONS_TABLENAME, selection, finalSArgs);  
      if (count > 0) {
        notifyUri = RhetologContract.SESSIONS_URI;
      
        // Delete session events.
        String eventSelection = " ( " + RhetologContract.EventsColumns.SESSION + " = ? ) ";
        String[] eventSelectionArgs = {sessionId};
        delete(RhetologContract.EVENTS_URI, eventSelection, eventSelectionArgs);
        
        // Delete session participants.
        String participantSelection = " ( " + RhetologContract.ParticipantsColumns.SESSION + " = ? ) ";
        String[] participantSelectionArgs = {sessionId};
        delete(RhetologContract.PARTICIPANTS_URI, participantSelection, participantSelectionArgs);
      }
      
            
      // If no sessions left, create new one. Must always be a session.
      String[] countColumns = {RhetologContract.SessionsColumns._ID};
      Cursor countQuery = db.query(SESSIONS_TABLENAME, countColumns, null, null, null, null, null);
      if(countQuery != null) {
        if(countQuery.getCount() == 0) {
          createFirstSession();
        } else if(amDeletingCurrentSession) {
          countQuery.moveToFirst();
          int idCol = countQuery.getColumnIndex(RhetologContract.SessionsColumns._ID);
          long firstId = countQuery.getLong(idCol);
          doSetCurrentSession(firstId);
        }
        countQuery.close();
      }
      
      break;

    case URI_TYPES.PARTICIPANTTABLE:
      notifyUri = RhetologContract.PARTICIPANTS_URI;
      String[] finalPTArgs = new String[completeArgs.size()];
      finalPTArgs = completeArgs.toArray(finalPTArgs);
      count = db.delete(PARTICIPANTS_TABLENAME, selection, finalPTArgs);
      break;
      
    case URI_TYPES.PARTICIPANT:
      String participantId = uri.getPathSegments().get(1);
      if (!TextUtils.isEmpty(selection)) {
        selection = " ( " + selection + " ) AND ";
      } else {
        selection = "";
      }
      selection = selection + " ( " + RhetologContract.ParticipantsColumns._ID + " = ? ) ";
      completeArgs.add(participantId);
      String[] finalPArgs = new String[completeArgs.size()];
      finalPArgs = completeArgs.toArray(finalPArgs);
      
      count = db.delete(PARTICIPANTS_TABLENAME, selection, selectionArgs);
      
      if (count > 0) {
        notifyUri = RhetologContract.PARTICIPANTS_URI;
      }
      
      //delete matching events and participants
      String participantEventSelection = " ( " + RhetologContract.EventsColumns.PARTICIPANT + " = ? ) ";
      String[] participantEventSelectionArgs = {participantId};
      delete(RhetologContract.EVENTS_URI, participantEventSelection, participantEventSelectionArgs);
      
      break;
    
    
      
    default:
      throw new IllegalArgumentException("Unknown URI: " + uri);
    }
    
    
    // Notify all interested watchers that a delete has occurred in the original Uri.
    //getContext().getContentResolver().notifyChange(uri, null);
    
    // Notify all interested observers that a delete has occurred as noted
    if (notifyUri != null) {
      getContext().getContentResolver().notifyChange(notifyUri, null);
    }
    
    // Other notifications
    if (session_stats_changed) {
      getContext().getContentResolver().notifyChange(RhetologContract.EVENTSCOUNTSESSION_URI, null);
    }
    
    return count;
  }

  
  @Override
  synchronized public Uri insert(Uri uri, ContentValues values) {
    
    String table_to_insert = null;
    Uri resultUriPrefix = null;
    
    boolean notify_original_uri = false;
    boolean session_stats_changed = false;
    
    
    long sessionToNotify = 0;
    
    
    switch(sUriMatcher.match(uri)) {
    
      // Insert an event to any session
    case URI_TYPES.EVENTTABLE:
      table_to_insert = EVENTS_TABLENAME;
      resultUriPrefix = RhetologContract.EVENTS_URI;
      sessionToNotify = values.getAsLong(RhetologContract.EventsColumns.SESSION);
      session_stats_changed = true;
      break;
      // Insert a session
    case URI_TYPES.SESSIONSTABLE:
      table_to_insert = SESSIONS_TABLENAME;
      resultUriPrefix = RhetologContract.SESSIONS_URI;
      break;
      // Insert a participant to any session, specified in values
    case URI_TYPES.PARTICIPANTTABLE:
      table_to_insert = PARTICIPANTS_TABLENAME;
      resultUriPrefix = RhetologContract.PARTICIPANTS_URI;
      sessionToNotify = values.getAsLong(RhetologContract.ParticipantsColumns.SESSION);
      break;
      
      // Insert an event in the current session
    case URI_TYPES.EVENTSCURRENTSESSION:
      table_to_insert = EVENTS_TABLENAME;
      values.put(RhetologContract.EventsColumns.SESSION, currentSession);
      resultUriPrefix = RhetologContract.EVENTS_URI;
      notify_original_uri = true;
      session_stats_changed = true;
      break;
      // Insert a participant in the current session
    case URI_TYPES.PARTICIPANTSCURRENTSESSION:
      table_to_insert = PARTICIPANTS_TABLENAME;
      values.put(RhetologContract.ParticipantsColumns.SESSION, currentSession);
      resultUriPrefix = RhetologContract.PARTICIPANTS_URI;
      notify_original_uri = true;
      break;
      
    default:
      throw new IllegalArgumentException("Unknown URI: " + uri);
    }
    
    // Do insert
    long rowID = db.insert(table_to_insert, null, values);
    if (rowID <= 0) {
      return null;
    }
    
    // Set result URI and notify
    Uri resultUri = ContentUris.withAppendedId(resultUriPrefix, rowID);
    getContext().getContentResolver().notifyChange(resultUri, null);
    
    // Notify original uri if desired
    if (notify_original_uri) {
      getContext().getContentResolver().notifyChange(uri, null);
    }
    
    
    // Notify associated session
    
    if (sessionToNotify > 0) {
      getContext().getContentResolver().notifyChange(Uri.withAppendedPath(RhetologContract.SESSIONS_URI, Long.toString(sessionToNotify)), null);
    }
    
    // Notify that statistics have changed.
    if (session_stats_changed) {
      getContext().getContentResolver().notifyChange(RhetologContract.EVENTSCOUNTSESSION_URI, null);
    }
    
    // Notify anyone watching the session and participant for new events
    if (sUriMatcher.match(uri) == URI_TYPES.EVENTTABLE) {
      String sessionStr = values.getAsString(RhetologContract.EventsColumns.SESSION);
      String partStr = values.getAsString(RhetologContract.EventsColumns.PARTICIPANT);
      Uri precise = Uri.withAppendedPath(RhetologContract.SESSIONSPARTICIPANTSEVENTS_URI, sessionStr + "/" + partStr);
      getContext().getContentResolver().notifyChange(precise, null);
    }
    
    
    return resultUri;

  }

  @Override
  synchronized public Cursor query(Uri uri, String[] projection, String selection,
                    String[] selectionArgs, String sortOrder) {

    Uri notificationUri = null;
    
    ArrayList<String> completeProjection = new ArrayList<String>();
    if (projection != null) {
      completeProjection.addAll(Arrays.asList(projection));
    }
    
    // Pre-treat empty selection?
    
    ArrayList<String> completeSelectionArgs = new ArrayList<String>();
    if (selectionArgs != null) {
      completeSelectionArgs.addAll(Arrays.asList(selectionArgs));
    }
    
    Cursor c = null;
    
    SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
    
    
    int uriToMatch = sUriMatcher.match(uri);
    
    switch(uriToMatch) {
    
      // Query for a particular event.
    case URI_TYPES.EVENT:
      builder.setTables(EVENTS_TABLENAME); 
      builder.appendWhere(RhetologContract.EventsColumns._ID + " = " + uri.getPathSegments().get(1));
      if(sortOrder == null || sortOrder.isEmpty()) {
        sortOrder = RhetologContract.EventsColumns.TIMESTAMP;
      }
      notificationUri = RhetologContract.EVENTS_URI;
      break;
      // Query for all events.
    case URI_TYPES.EVENTTABLE: 
      builder.setTables(EVENTS_TABLENAME);
      if(sortOrder == null || sortOrder.isEmpty()) {
        sortOrder = RhetologContract.EventsColumns.TIMESTAMP;
      }
      notificationUri = RhetologContract.EVENTS_URI;
      break;
      // Query for a particular session by ID.
    case URI_TYPES.SESSION: 
      builder.setTables(SESSIONS_TABLENAME);
      String sessionFromUri = uri.getLastPathSegment();
      builder.appendWhere(RhetologContract.SessionsColumns._ID + " = " + sessionFromUri);
      notificationUri = RhetologContract.SESSIONS_URI;
      break;
      // Query for all sessions.
    case URI_TYPES.SESSIONSTABLE: 
      builder.setTables(SESSIONS_TABLENAME);
      notificationUri = RhetologContract.SESSIONS_URI;
      break;
      // Query for a particular participant by ID.
    case URI_TYPES.PARTICIPANT:
      builder.setTables(PARTICIPANTS_TABLENAME);
      String participantFromUri = uri.getLastPathSegment();
      builder.appendWhere(RhetologContract.ParticipantsColumns._ID + " = " + participantFromUri);
      notificationUri = RhetologContract.PARTICIPANTS_URI;
      break;
      // Query for all participants.
    case URI_TYPES.PARTICIPANTTABLE:
      builder.setTables(PARTICIPANTS_TABLENAME);
      notificationUri = RhetologContract.PARTICIPANTS_URI;
      break;
      
    
      
    case URI_TYPES.REPORT_CSV:
      builder.setTables(EVENTS_TABLENAME);
      //completeProjection.add("'Session Report' AS _display_name");
      //completeProjection.add("23 as _size");
      String reportSessionId = uri.getPathSegments().get(2);
      builder.appendWhere(RhetologContract.EventsColumns.SESSION + " = " + reportSessionId);
      break;
      
      // Query for a count of all events of a particular session
    case URI_TYPES.EVENTSCOUNTSESSION: 
      builder.setTables(EVENTS_TABLENAME);
      
      // Find virtual column, replace with expression that generates it.
      int virt = -1;
      for (int i = 0; i < completeProjection.size(); i++) {
        String eachProjColumn = completeProjection.get(i);
        if (eachProjColumn.contentEquals(RhetologContract.EventsCountSessionColumns.EVENTCOUNT)) {
          virt = i;
          break;
        }
      }
      if (virt >= 0) {
        completeProjection.remove(virt);
        completeProjection.add(virt, " COUNT( " + RhetologContract.EventsColumns._ID + ") AS " + RhetologContract.EventsCountSessionColumns.EVENTCOUNT);
      }
        
      builder.appendWhere(RhetologContract.EventsColumns.SESSION + " = ?");
      completeSelectionArgs.add(uri.getPathSegments().get(3));
      
      notificationUri = RhetologContract.EVENTSCOUNTSESSION_URI;
      break;
      // Query for all events from a particular session and participant
    case URI_TYPES.SESSIONPARTICIPANTEVENTTABLE:
      builder.setTables(EVENTS_TABLENAME);
      // An excellent example of why to read the GD docs.
      builder.appendWhere(RhetologContract.EventsColumns.SESSION + " = ? AND " + RhetologContract.EventsColumns.PARTICIPANT + " = ?");
      completeSelectionArgs.add(uri.getPathSegments().get(3));
      completeSelectionArgs.add(uri.getPathSegments().get(4));
      notificationUri = RhetologContract.SESSIONSPARTICIPANTSEVENTS_URI;
      break;
      // Query for a single record of the current session
    case URI_TYPES.SESSIONSCURRENT:
      builder.setTables(SESSIONS_TABLENAME);
      builder.appendWhere(RhetologContract.SessionsColumns._ID + " = ?");
      completeSelectionArgs.add(Long.toString(currentSession));
      notificationUri = RhetologContract.SESSIONSCURRENT_URI;
      break;
      // Query for all participants of the current session
    case URI_TYPES.PARTICIPANTSCURRENTSESSION:
      builder.setTables(PARTICIPANTS_TABLENAME);
      builder.appendWhere(RhetologContract.ParticipantsColumns.SESSION + " = ?");
      completeSelectionArgs.add(Long.toString(currentSession));
      notificationUri = RhetologContract.PARTICIPANTSCURRENTSESSION_URI;
      break;
      // Query for all events of the current session
    case URI_TYPES.EVENTSCURRENTSESSION:
      builder.setTables(EVENTS_TABLENAME);
      builder.appendWhere(RhetologContract.EventsColumns.SESSION + " = ?");
      completeSelectionArgs.add(Long.toString(currentSession));
      notificationUri = RhetologContract.EVENTSCURRENTSESSION_URI;
      break;
      
    default:
      throw new IllegalArgumentException("Unknown URI: " + uri);
    }
    
    String[] finalProjection = completeProjection.toArray(new String[completeProjection.size()]);
    String[] finalArgs = completeSelectionArgs.toArray(new String[completeSelectionArgs.size()]);
    
    try {
      c = builder.query(db, finalProjection, selection, finalArgs, null, null, sortOrder);  
    } catch (SQLiteException e) {
      return null;
    }
    
    Log.d(TAG, "builder.query == " + builder.buildQuery(null, null, null, null, null, null));
    
    if (c == null) {
       return null;
    }
    
    Log.d(TAG, "cursor.count == " + c.getCount());
    
    if(notificationUri != null) {
      c.setNotificationUri(getContext().getContentResolver(), notificationUri);  
    }
    
    
    return c;
  }

  
  
  @Override
  synchronized public int update(Uri uri, ContentValues values, String selection,
      String[] selectionArgs) {
  
    Uri notifyUri = null;
    
    String updateTable = null;
    
    if (selection == null) selection = "";
    if (!selection.isEmpty())
      selection = " ( " + selection + " ) AND ";
  
    ArrayList<String> completeSelectionArgs = new ArrayList<String>();
    if (selectionArgs != null) {
      completeSelectionArgs.addAll(Arrays.asList(selectionArgs));
    }
  
    int count = 0;
    
    switch(sUriMatcher.match(uri)) {
    
    case URI_TYPES.EVENT:
      updateTable = EVENTS_TABLENAME;
      String whereClause = " ( " + RhetologContract.EventsColumns._ID + " = ? ) ";
      selection += whereClause;
      completeSelectionArgs.add(uri.getLastPathSegment());
      notifyUri = uri;
      break;
      
    case URI_TYPES.SESSION:
      updateTable = SESSIONS_TABLENAME;
      String sessionWhereClause = " ( " + RhetologContract.SessionsColumns._ID + " = ? ) ";
      selection += sessionWhereClause;
      completeSelectionArgs.add(uri.getLastPathSegment());
      notifyUri = uri;
      break;
      
    case URI_TYPES.SESSIONSCURRENT:
      updateTable = SESSIONS_TABLENAME;
      String sessionCWhereClause = " ( " + RhetologContract.SessionsColumns._ID + " = ? ) ";
      selection += sessionCWhereClause;
      completeSelectionArgs.add(Long.toString(currentSession));
      notifyUri = uri;
      break;
      
    default:
      throw new IllegalArgumentException("Unknown URI: " + uri);  
      //break;    
    }
    
    // Finalize selection args
    String[] finalSelectionArgs = new String[completeSelectionArgs.size()];
    completeSelectionArgs.toArray(finalSelectionArgs);
    
    // Commit update
    count = db.update(updateTable, values, selection, finalSelectionArgs);
    
    // Notify all observers of change
    if (notifyUri != null) {
      getContext().getContentResolver().notifyChange(notifyUri, null);
    }
    
    // Report to caller
    return count;
  }
  
  
  
  /** Support for CSV reports */
  
  
  
  /* (non-Javadoc)
   * @see android.content.ContentProvider#openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle)
   */
  @Override
  public AssetFileDescriptor openTypedAssetFile(Uri uri,
      String mimeTypeFilter, Bundle opts) throws FileNotFoundException {
    
    
    switch (sUriMatcher.match(uri)) {
    case URI_TYPES.REPORT_CSV:
      
      String session = uri.getLastPathSegment();
      
      String[] columns = {
          PARTICIPANTS_TABLENAME + "." + RhetologContract.ParticipantsColumns.NAME,
          EVENTS_TABLENAME + "." + RhetologContract.EventsColumns.SESSION,
          EVENTS_TABLENAME + "." + RhetologContract.EventsColumns.PARTICIPANT,
          EVENTS_TABLENAME + "." + RhetologContract.EventsColumns.FALLACY,
          EVENTS_TABLENAME + "." + RhetologContract.EventsColumns.TIMESTAMP
      };
      String selection = EVENTS_TABLENAME + "." + RhetologContract.EventsColumns.SESSION + " = ? ";
      String[] selectionArgs = {session};
      String groupBy = null;
      String having = null;
      String orderBy = null;
      
      String UNIONTABLE = EVENTS_TABLENAME + " INNER JOIN " + PARTICIPANTS_TABLENAME 
          + " ON " 
          + EVENTS_TABLENAME + "." + RhetologContract.EventsColumns.PARTICIPANT
          + " = "
          + PARTICIPANTS_TABLENAME + "." + RhetologContract.ParticipantsColumns._ID;
      
      Cursor c = db.query(UNIONTABLE, columns, selection, selectionArgs, groupBy, having, orderBy);
      if (c == null || !c.moveToFirst()) {
        if (c != null) {
          c.close();
        }
        throw new FileNotFoundException("Unable to query " + uri.toString());
      }
      
      String mimeType = RhetologContract.RHETOLOG_TYPE_SESSION_REPORT; 
      
      return new AssetFileDescriptor(openPipeHelper(uri, mimeType, opts, c, this), 
          0 /* startOffset */, 
          AssetFileDescriptor.UNKNOWN_LENGTH);
      
      
      //break;

    default:
      return super.openTypedAssetFile(uri, mimeTypeFilter, opts);
    }
    
  }
  
  
  
  /** Support for CSV */
  
  @Override
  public void writeDataToPipe(ParcelFileDescriptor output, Uri uri,
      String mimeType, Bundle opts, Cursor c) {
    FileOutputStream fout = new FileOutputStream(output.getFileDescriptor());
    
    PrintWriter pw = null;
    
    try {
      pw = new PrintWriter(new OutputStreamWriter(fout, "UTF-8"));
      
      //write csv lines of report
    
      int name = c.getColumnIndex(RhetologContract.ParticipantsColumns.NAME);
      int fallacy = c.getColumnIndex(RhetologContract.EventsColumns.FALLACY);
      int participant = c.getColumnIndex(RhetologContract.EventsColumns.PARTICIPANT);
      int timestamp = c.getColumnIndex(RhetologContract.EventsColumns.TIMESTAMP);
      String line;
      
      while(!c.isAfterLast()) {
        line = c.getString(fallacy) 
            + "," 
            + c.getString(participant) 
            + "," 
            + c.getString(name) 
            + "," 
            + Integer.toString(c.getInt(timestamp))
            + "\n";
        pw.print(line);
        c.moveToNext();
      }
      
      
    } catch (UnsupportedEncodingException e) {
      // No idea what to do with this
      e.printStackTrace();
    } finally {
      c.close();
      if (pw != null) {
        pw.flush();
      }
      try {
        fout.close();
      } catch (Exception e2) {
        //do nothing
      }
    }
    
  }

  
  

  /* (non-Javadoc)
   * @see android.content.ContentProvider#openFile(android.net.Uri, java.lang.String)
   */
  @Override
  public ParcelFileDescriptor openFile(Uri uri, String mode)
      throws FileNotFoundException {
    
    switch (sUriMatcher.match(uri)) {
    case URI_TYPES.REPORT_CSV:
      break;

    default:
      break;
    }
    return super.openFile(uri, mode);
  }




  private static final String SQL_CREATE_EVENTS_TABLE = "CREATE TABLE "
      + EVENTS_TABLENAME
      + " ( "
      + RhetologContract.EventsColumns._ID + " INTEGER PRIMARY KEY, " 
      + RhetologContract.EventsColumns.PARTICIPANT + " TEXT, "
      + RhetologContract.EventsColumns.FALLACY + " TEXT, " 
      + RhetologContract.EventsColumns.TIMESTAMP + " INTEGER, "
      + RhetologContract.EventsColumns.SESSION + " INTEGER " // Link to owning session
      + " ) ";
  
  private static final String SQL_CREATE_SESSIONS_TABLE = "CREATE TABLE "
      + SESSIONS_TABLENAME
      + " ( "
      + RhetologContract.SessionsColumns._ID + " INTEGER PRIMARY KEY, "
      + RhetologContract.SessionsColumns.TITLE + " TEXT, "
      + RhetologContract.SessionsColumns.UUID + " TEXT, "
      + RhetologContract.SessionsColumns.STARTTIME + " TEXT, "
      + RhetologContract.SessionsColumns.ENDTIME + " TEXT "
      + " ) ";

  private static final String SQL_CREATE_PARTICIPANTS_TABLE = "CREATE TABLE "
      + PARTICIPANTS_TABLENAME
      + " ( "
      + RhetologContract.ParticipantsColumns._ID + " INTEGER PRIMARY KEY, "
      + RhetologContract.ParticipantsColumns.NAME + " TEXT, "
      + RhetologContract.ParticipantsColumns.PHOTO + " TEXT, "
      + RhetologContract.ParticipantsColumns.LOOKUP + " TEXT, "
      + RhetologContract.ParticipantsColumns.SESSION + " INTEGER "
      + " ) ";
  
  
  protected static final class MainDatabaseHelper extends SQLiteOpenHelper {

    private static SQLiteDatabase.CursorFactory cursorFactory = null;
    static {
      cursorFactory = new SQLiteDatabase.CursorFactory() {      
        @Override
        public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery, String editTable, SQLiteQuery query) {
          Log.d(TAG, "Query: "+query);

          return new SQLiteCursor(db, masterQuery, editTable, query);
        }
      };  
    }
     
    
    public MainDatabaseHelper(Context context) {
      super(context, DBNAME, cursorFactory, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
      db.execSQL(SQL_CREATE_EVENTS_TABLE);
      db.execSQL(SQL_CREATE_SESSIONS_TABLE);
      db.execSQL(SQL_CREATE_PARTICIPANTS_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
      
    }

  }

  
  

//  /* Support routines */
//  
//  private static String[] joinStringArrays(String[]...arrays) {
//
//        final List<String> output = new ArrayList<String>();
//
//        for(String[] array : arrays) {
//          if(array == null) continue;
//          output.addAll(Arrays.asList(array));
//        }
//
//        return output.toArray(new String[output.size()]);
//
//}
  
}




Java Source Code List

name.zurell.kirk.apps.android.rhetolog.DigitalSessionClock.java
name.zurell.kirk.apps.android.rhetolog.FallacyAdapter.java
name.zurell.kirk.apps.android.rhetolog.FallacyView.java
name.zurell.kirk.apps.android.rhetolog.Fallacy.java
name.zurell.kirk.apps.android.rhetolog.MRUAdapter.java
name.zurell.kirk.apps.android.rhetolog.MRUView.java
name.zurell.kirk.apps.android.rhetolog.MainActivity.java
name.zurell.kirk.apps.android.rhetolog.ParticipantAdapter.java
name.zurell.kirk.apps.android.rhetolog.ParticipantView.java
name.zurell.kirk.apps.android.rhetolog.PreferencesActivity.java
name.zurell.kirk.apps.android.rhetolog.PreferencesGeneralFragment.java
name.zurell.kirk.apps.android.rhetolog.RhetologApplication.java
name.zurell.kirk.apps.android.rhetolog.RhetologContentProvider.java
name.zurell.kirk.apps.android.rhetolog.RhetologContract.java
name.zurell.kirk.apps.android.rhetolog.SessionActor.java
name.zurell.kirk.apps.android.rhetolog.SessionDateTimeDialogFragment.java
name.zurell.kirk.apps.android.rhetolog.SessionDetailActivity.java
name.zurell.kirk.apps.android.rhetolog.SessionDetailFragment.java
name.zurell.kirk.apps.android.rhetolog.SessionListActivity.java
name.zurell.kirk.apps.android.rhetolog.SessionListFragment.java
name.zurell.kirk.apps.android.rhetolog.SimpleGraphView.java