package org.sana.android.fragment;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import org.joda.time.DateTime;
import org.sana.R;
import org.sana.android.Constants;
import org.sana.android.app.Locales;
import org.sana.android.app.Preferences;
import org.sana.android.content.DispatchResponseReceiver;
import org.sana.android.content.Intents;
import org.sana.android.content.Uris;
import org.sana.android.content.core.PatientWrapper;
import org.sana.android.db.ModelWrapper;
import org.sana.android.provider.EncounterTasks;
import org.sana.android.provider.Encounters;
import org.sana.android.provider.Patients;
import org.sana.android.provider.Procedures;
import org.sana.android.provider.EncounterTasks.Contract;
import org.sana.android.provider.Subjects;
import org.sana.android.service.impl.DispatchService;
import org.sana.android.util.Bitmaps;
import org.sana.android.util.Dates;
import org.sana.android.util.Logf;
import org.sana.api.IModel;
import org.sana.api.task.EncounterTask;
import org.sana.api.task.Status;
import org.sana.util.StringUtil;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.util.LongSparseArray;
import android.support.v4.util.SparseArrayCompat;
import android.support.v4.widget.CursorAdapter;
import android.support.v4.widget.SimpleCursorAdapter;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
/**
* Fragment displaying all patients.
*
* @author Sana Development Team
*/
public class EncounterTaskListFragment extends ListFragment implements LoaderCallbacks<Cursor> {
public static final String TAG = EncounterTaskListFragment.class.getSimpleName();
public static SimpleDateFormat sdf = new SimpleDateFormat(IModel.DATE_FORMAT,
Locale.US);
public static SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm",
Locale.US);
//TODO start using a proper inner join for the query instead of the
//repetitive queries for the patient and procedure info.
protected static final String SELECT = "SELECT" +
"encountertask.uuid AS uuid, " +
"encountertask.due_date AS due_on, " +
"encountertask.subject AS subject_uuid, " +
"subject.given_name AS subject_given_name, " +
"subject.family_name AS subject_family_name, " +
"subject.system_id AS subject_system_id, " +
"encountertask.procedure AS procedure_uuid, " +
"procedure.title AS procedure_title " +
"FROM encountertask " +
"INNER JOIN procedure ON encountertask.procedure = procedure._id " +
"INNER JOIN subject ON encountertask.subject = subject.uuid;";
static final String SIMPLE_SELECT = EncounterTasks.Contract.OBSERVER + " = ?"
+ " AND " + EncounterTasks.Contract.STATUS + " = ? ";
static final String[] mProjection = new String[] {
Contract._ID,
Contract.SUBJECT,
Contract.PROCEDURE,
Contract.DUE_DATE,
Contract.UUID,
Contract.STATUS,
Contract.COMPLETED,
Contract.ENCOUNTER
};
// Once a day 86400000
long delta = 1000;
protected Uri mUri;
protected EncounterTaskCursorAdapter mAdapter;
protected OnModelItemSelectedListener mListener;
protected Handler mHandler;
protected boolean doSync = false;
String[] months;
protected LongSparseArray<Bundle> mData = new LongSparseArray<Bundle>();
//
// Activity Methods
//
/** {@inheritDoc} */
@Override
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate()");
super.onCreate(savedInstanceState);
//setRetainInstance(true);
Locales.updateLocale(this.getActivity(), getString(R.string.force_locale));
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
Log.d(TAG, "onActivityCreated()");
super.onActivityCreated(savedInstanceState);
Locale locale = new Locale(Preferences.getString(getActivity(),
Constants.PREFERENCE_LOCALE, "en"));
df = new SimpleDateFormat(
getActivity().getString(R.string.display_date_time_format),
locale);
Locales.updateLocale(getActivity(), locale);
months = getActivity().getResources().getStringArray(R.array.months_long_format);
// signal the dispatcher to sync
mUri = getActivity().getIntent().getData();
if (mUri == null) {
mUri = EncounterTasks.CONTENT_URI;
}
mAdapter = new EncounterTaskCursorAdapter(getActivity(), null, 0);
setListAdapter(mAdapter);
Uri syncUri = EncounterTasks.CONTENT_URI;
Uri observer = getActivity().getIntent().getParcelableExtra(
Intents.EXTRA_OBSERVER);
delta = getActivity().getResources().getInteger(
R.integer.sync_delta_encountertasks);
// Always sync subjects first
syncSubjects(getActivity(),Subjects.CONTENT_URI);
// sync for specific observer or all tasks
if(!Uris.isEmpty(observer)){
Log.i(TAG, "sync: observer = " + observer);
// sync subjects first
sync(getActivity(), Subjects.CONTENT_URI);
String observerUuid = ModelWrapper.getUuid(
observer, getActivity().getContentResolver());
Uri u = EncounterTasks.CONTENT_URI.buildUpon().appendQueryParameter(
"assigned_to__uuid",observerUuid).build();
sync(getActivity(), u);
} else {
Log.i(TAG, "sync: all ");
// sync subjects first
sync(getActivity(), Subjects.CONTENT_URI);
sync(getActivity(), EncounterTasks.CONTENT_URI);
}
LoaderManager.enableDebugLogging(true);
getActivity().getSupportLoaderManager().initLoader(Uris.ENCOUNTER_TASK_DIR, null, this);
}
/** {@inheritDoc} */
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
if (mListener != null) {
mListener.onModelItemSelected(position);
}
}
//
// Loader Callbacks
//
/** {@inheritDoc} */
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
Log.d(TAG, "onCreateLoader() ");
CursorLoader loader = new CursorLoader(getActivity(),
mUri,
mProjection,
getSelection(),
new String[]{ getObserver() , getSelectedStatus() },
Contract.DUE_DATE + " ASC");
return loader;
}
/** {@inheritDoc} */
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
Log.d(TAG, "onLoadFinished() ");
if (cursor == null || (cursor !=null && cursor.getCount() == 0)) {
setEmptyText(getString(R.string.msg_no_patients));
}
if(cursor != null)
((EncounterTaskCursorAdapter) this.getListAdapter()).swapCursor(cursor);
}
/*
* (non-Javadoc)
* @see android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset(android.support.v4.content.Loader)
*/
@Override
public void onLoaderReset(Loader<Cursor> loader) {
Log.d(TAG, "onLoaderReset() ");
((EncounterTaskCursorAdapter) this.getListAdapter()).swapCursor(null);
}
/**
* Events specific to this PatientListFragment
*
* @author Sana Development Team
*/
public interface OnModelItemSelectedListener {
/**
* Callback when a patient is selected in the list.
*
* @param id The selected patient's ID.
*/
public void onModelItemSelected(long id);
}
/**
* Sets a listener to this fragment.
*
* @param listener
*/
public void setOnModelItemSelectedListener(OnModelItemSelectedListener listener) {
mListener = listener;
}
public static class ViewHolder{
ImageView image;
TextView name;
TextView systemId;
TextView location;
TextView label;
int position = 1;
EncounterTask task = null;
}
public String getObserver(){
Uri obsUri = getActivity().getIntent().getParcelableExtra(Intents.EXTRA_OBSERVER);
String observer = ModelWrapper.getUuid(obsUri, getActivity().getContentResolver());
return (TextUtils.isEmpty(observer))? "": observer;
}
//TODO try reading from intent
public String getSelectedStatus(){
return "Assigned";
}
public String getSelection(){
return SIMPLE_SELECT;
}
/**
* @author Sana Development Team
*/
public class EncounterTaskCursorAdapter extends CursorAdapter{
private final LayoutInflater mInflater;
// Holders for the cursor column index values
protected int idIndex = -1;
protected int subjectIndex = -1;
protected int procedureIndex = -1;
protected int dueOnIndex = -1;
protected int statusIndex = -1;
protected int completedIndex = -1;
protected int uuidIndex = -1;
protected int encounterIndex = -1;
protected String[] months;
public EncounterTaskCursorAdapter(Context context, Cursor c) {
super(context.getApplicationContext(),c,false);
mInflater = LayoutInflater.from(context);
}
public EncounterTaskCursorAdapter(Context context) {
this(context, null, 0);
}
public EncounterTaskCursorAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
setColumnIndexes(c);
}
@Override
public void changeCursor (Cursor cursor){
setColumnIndexes(cursor);
super.changeCursor(cursor);
}
@Override
public Cursor swapCursor(Cursor newCursor) {
setColumnIndexes(newCursor);
return super.swapCursor(newCursor);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
Log.d(TAG+".mAdapter", "bindView(): cursor position: "
+ ((cursor != null)? cursor.getPosition(): 0));
int position = this.getCursor().getPosition();
String uuid = ((Cursor) getItem(position)).getString(uuidIndex);
String due_on = ((Cursor) this.getItem(position)).getString(dueOnIndex);
String status = ((Cursor) this.getItem(position)).getString(statusIndex);
String completed = ((Cursor) getItem(position)).getString(completedIndex);
boolean complete = false;
boolean reviewed = false;
Status stat = Status.fromString(status);
switch(stat){
case COMPLETED:
case REVIEWED:
complete = true;
break;
default:
complete = false;
}
if(complete)
setDate(view,true,completed,position);
else
setDate(view,due_on, position);
String patientUUid = ((Cursor) getItem(position)).getString(subjectIndex);
setPatient(context,view,patientUUid);
String procedureUuid = ((Cursor) getItem(position)).getString(procedureIndex);
setProcedure(context,view,procedureUuid);
String encounter = ((Cursor) getItem(position)).getString(encounterIndex);
Bundle data = new Bundle();
data.putString(Contract.STATUS, status);
data.putParcelable(Intents.EXTRA_TASK,
Uris.withAppendedUuid(EncounterTasks.CONTENT_URI, uuid));
data.putParcelable(Intents.EXTRA_SUBJECT,
Uris.withAppendedUuid(Subjects.CONTENT_URI, patientUUid));
data.putParcelable(Intents.EXTRA_PROCEDURE,
Uris.withAppendedUuid(Procedures.CONTENT_URI, procedureUuid));
data.putParcelable(Intents.EXTRA_TASK_ENCOUNTER,
Uris.withAppendedUuid(EncounterTasks.CONTENT_URI, uuid));
if(!TextUtils.isEmpty(encounter))
data.putParcelable(Intents.EXTRA_ENCOUNTER,
Uris.withAppendedUuid(Encounters.CONTENT_URI, encounter));
view.setTag(data);
mData.put(position, data);
Log.i(TAG, "Finished setting data for: " + position);
}
public View getView(int position, View convertView, ViewGroup parent) {
Log.d(TAG+".mAdapter", "get view ");
return super.getView(position,convertView, parent);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
Log.d(TAG+".mAdapter", "new view cursor position: "
+ ((cursor != null)? cursor.getPosition(): 0));
View view = mInflater.inflate(R.layout.encountertask_list_item, null);
bindView(view, context, cursor);
return view;
}
protected void setColumnIndexes(Cursor cursor){
// Do a null check just in case
if(cursor == null) {
Log.w(TAG, "setColumnIndexes(null)");
return;
}
idIndex = cursor.getColumnIndex(Contract._ID);
subjectIndex = cursor.getColumnIndex(Contract.SUBJECT);
procedureIndex = cursor.getColumnIndex(Contract.PROCEDURE);
dueOnIndex = cursor.getColumnIndex(Contract.DUE_DATE);
statusIndex = cursor.getColumnIndex(Contract.STATUS);
completedIndex = cursor.getColumnIndex(Contract.COMPLETED);
uuidIndex = cursor.getColumnIndex(Contract.UUID);
encounterIndex = cursor.getColumnIndex(Contract.ENCOUNTER);
}
}
public final void sync(Context context, Uri uri){
Logf.D(TAG, "sync() --> " + uri);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
long lastSync = 0;
int descriptor = Uris.getContentDescriptor(uri);
switch(descriptor){
case Uris.SUBJECT:
lastSync = prefs.getLong("patient_sync", 0);
break;
case Uris.ENCOUNTER_TASK:
lastSync = prefs.getLong("encountertask_sync", 0);
break;
default:
return;
}
long now = System.currentTimeMillis();
Log.d(TAG, "last: " + lastSync +", now: " + now+ ", delta: " + (now-lastSync) + ", doSync: " + ((now - lastSync) > delta));
// TODO
if((now - lastSync) > delta){
Logf.I(TAG, "sync(): synchronizing list: " + uri);
switch(descriptor){
case Uris.SUBJECT:
prefs.edit().putLong("patient_sync", now).commit();
break;
case Uris.ENCOUNTER_TASK:
prefs.edit().putLong("encountertask_sync", now).commit();
break;
default:
}
Intent intent = new Intent(Intents.ACTION_READ, uri);
context.startService(intent);
}
}
public final void syncSubjects(Context context, Uri uri){
Logf.D(TAG, "sync() --> " + uri);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
long lastSync = prefs.getLong("patient_sync", 0);
long now = System.currentTimeMillis();
Log.d(TAG, "last: " + lastSync +", now: " + now+ ", delta: " + (now-lastSync) + ", doSync: " + ((now - lastSync) > delta));
// TODO
if((now - lastSync) > delta){
Logf.I(TAG, "sync(): synchronizing list: " + uri);
prefs.edit().putLong("patient_sync", now).commit();
Intent intent;
intent = new Intent(Intents.ACTION_READ, uri);
context.startService(intent);
} else {
/*
Intent broadcast = new Intent(DispatchResponseReceiver.BROADCAST_RESPONSE);
broadcast.setData(EncounterTasks.CONTENT_URI);
Locales.updateLocale(getActivity(), getString(R.string.force_locale));
broadcast.putExtra(DispatchResponseReceiver.KEY_RESPONSE_MESSAGE, "");
broadcast.putExtra(DispatchResponseReceiver.KEY_RESPONSE_CODE, 200);
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(broadcast);
*/
}
}
public final void setProcedure(Context context, View view, String uuid){
Log.d(TAG+".setProcedure", "procedure uuid: " + uuid);
Cursor c = null;
String title = null;
TextView procedureTitle = (TextView)view.findViewById(R.id.procedure_title);
try{
Uri uri = Uris.withAppendedUuid(Procedures.CONTENT_URI, uuid);
c = context.getContentResolver().query(uri, new String[]{ Procedures.Contract.TITLE } , null,null,null);
if(c != null && c.moveToFirst()){
title = c.getString(0);
}
} finally {
if(c != null) c.close();
procedureTitle.setText((TextUtils.isEmpty(title)? "null": title));
}
Log.d(TAG+".setProcedure", "finished setting view: " + title);
}
public void setDate(View view, String due_on, long id){
setDate(view,false,due_on,id);
}
public void setDate(View view, boolean completed, String due_on, long id){
TextView dueOn = (TextView)view.findViewById(R.id.due_on);
Log.i(TAG, "due_on:" + due_on);
Date date = new Date();
Date now = new Date();
try {
date = sdf.parse(due_on);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
DateTime dt = new DateTime(date);
int month = dt.getMonthOfYear();
int dayOfMonth = dt.getDayOfMonth();
int year = dt.getYear();
String localizedMonth = months[month - 1];
due_on = String.format("%02d %s %04d", dayOfMonth, localizedMonth, year);
Log.i(TAG, "due_on(formatted):" + due_on);
dueOn.setText(due_on);
if(completed){
dueOn.setTextColor(getResources().getColor(R.color.complete));
} else if(now.getDay() == date.getDay()
&& now.getMonth() == date.getMonth()
&& now.getYear() == date.getYear()){
if(now.after(date)){
dueOn.setTextColor(getResources().getColor(R.color.past_due));
Log.w(TAG, "DUE TODAY-PAST DUE:" + id);
} else {
Log.w(TAG, "DUE TODAY:" + id);
dueOn.setTextColor(getResources().getColor(R.color.due_today));
}
} else if(now.after(date)){
dueOn.setTextColor(getResources().getColor(R.color.past_due));
Log.w(TAG, "PAST DUE DATE:" + id);
} else {
Log.i(TAG, "due date ok:" + id);
dueOn.setTextColor(getResources().getColor(android.R.color.white));
}
}
public void setPatient(Context context, View view, String uuid){
Cursor c = null;
String familyName = null;
String givenName = null;
String displayName = null;
String id = null;
String imagePath = null;
TextView name = (TextView) view.findViewById(R.id.name);
TextView systemId = (TextView)view.findViewById(R.id.system_id);
ImageView image = (ImageView)view.findViewById(R.id.image);
try{
c = ModelWrapper.getOneByUuid(Subjects.CONTENT_URI, context.getContentResolver(), uuid);
if(c != null && c.moveToFirst()){
familyName = c.getString(c.getColumnIndex(Patients.Contract.FAMILY_NAME));
givenName = c.getString(c.getColumnIndex(Patients.Contract.GIVEN_NAME));
id = c.getString(c.getColumnIndex(Patients.Contract.PATIENT_ID));
displayName = StringUtil.formatPatientDisplayName(givenName, familyName);
imagePath = c.getString(c.getColumnIndex(Patients.Contract.IMAGE));
}
} finally {
if(c != null) c.close();
name.setText((TextUtils.isEmpty(displayName)? "null": displayName));
systemId.setText((TextUtils.isEmpty(id)? "null id":id));
setImage(image,imagePath);
}
}
public void setImage(ImageView view, String imagePath){
// Set patient name and image
if(imagePath != null){
try{
view.setImageBitmap(Bitmaps.decodeSampledBitmapFromFile(
Uri.parse(imagePath).getPath(), 128,128));
} catch (Exception e){
Log.e(TAG+".mAdapter", "bindView(): " + e.getMessage());
}
}
}
public Bundle getSelectedData(long id){
Bundle data = mData.get(id);
return data;
}
}