package org.sana.android.fragment;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
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.SanaDB.ImageSQLFormat;
import org.sana.android.db.ModelWrapper;
import org.sana.android.provider.Encounters;
import org.sana.android.provider.Observations;
import org.sana.android.provider.Patients;
import org.sana.android.provider.Procedures;
import org.sana.android.provider.Subjects;
import org.sana.android.service.impl.DispatchService;
import org.sana.android.service.QueueManager;
import org.sana.android.util.Dates;
import org.sana.android.util.Logf;
import org.sana.android.util.SanaUtil;
import org.sana.api.IModel;
import org.sana.core.Encounter;
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.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
/**
* Fragment displaying all patients.
*
* @author Sana Development Team
*/
public class EncounterListFragment extends ListFragment implements LoaderCallbacks<Cursor> {
public static final String TAG = EncounterListFragment.class.getSimpleName();
static final SimpleDateFormat sdf = new SimpleDateFormat(IModel.DATE_FORMAT,
Locale.US);
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd",
Locale.US);
static final String[] mProjection = new String[] {
Encounters.Contract._ID,
Encounters.Contract.UUID,
Encounters.Contract.PROCEDURE,
Encounters.Contract.SUBJECT,
Encounters.Contract.STATE,
Encounters.Contract.UPLOAD_STATUS,
Encounters.Contract.UPLOAD_QUEUE,
Encounters.Contract.CREATED,
Encounters.Contract.FINISHED };
// Once a day 86400000
long delta = 1000;
private Uri mUri;
private EncounterCursorAdapter mAdapter;
private OnModelItemSelectedListener mListener;
Handler mHandler;
private boolean doSync = false;
String[] months;
LongSparseArray<Bundle> mData = new LongSparseArray<Bundle>();
//
// Activity Methods
//
/** {@inheritDoc} */
@Override
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate()");
super.onCreate(savedInstanceState);
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(
getString(R.string.display_date_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 = Encounters.CONTENT_URI;
}
mAdapter = new EncounterCursorAdapter(getActivity(), null, 0);
setListAdapter(mAdapter);
LoaderManager.enableDebugLogging(true);
getActivity().getSupportLoaderManager().initLoader(Uris.ENCOUNTER_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,
null,
null,
Encounters.Contract.CREATED + " DESC");
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_encounters));
}
if(cursor != null)
((EncounterCursorAdapter) 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() ");
((EncounterCursorAdapter) 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;
}
static class ViewHolder{
ImageView image;
TextView name;
TextView systemId;
TextView location;
TextView label;
int position = 1;
Encounter 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 null;
}
/**
* @author Sana Development Team
*/
public class EncounterCursorAdapter extends CursorAdapter{
private final LayoutInflater mInflater;
private ArrayList<Boolean> itemChecked = new ArrayList<Boolean>();
String[] months;
public EncounterCursorAdapter(Context context, Cursor c) {
super(context.getApplicationContext(),c,false);
mInflater = LayoutInflater.from(context);
for (int i = 0; i < this.getCount(); i++) {
itemChecked.add(i, false); // initializes all items value with false
}
}
public EncounterCursorAdapter(Context context) {
this(context, null, 0);
}
public EncounterCursorAdapter(Context context, Cursor c, int flags) {
super(context,c, flags);
mInflater = LayoutInflater.from(context);
}
@Override
public void changeCursor (Cursor cursor){
// need to update to checked records
updateChecked(cursor);
super.changeCursor(cursor);
}
@Override
public Cursor swapCursor(Cursor newCursor) {
// need to update to checked records
updateChecked(newCursor);
return super.swapCursor(newCursor);
}
private void updateChecked(Cursor newCursor){
Log.i(TAG, "updateChecked()");
int sizeOf = (newCursor != null)? newCursor.getCount(): 0;
int sizeOld = itemChecked.size();
ArrayList<Boolean> checked = new ArrayList<Boolean>(sizeOf);
for(int i = 0; i< sizeOf; i++){
checked.add(false);
}
int index = 0;
Log.d(TAG, "....sizeOld: " + sizeOld);
Log.d(TAG, "....sizeOf: " + sizeOf);
while(index < sizeOf){
if(sizeOld > 0 && index < sizeOld){
checked.set(index, itemChecked.get(index));
} else {
checked.set(index, false);
}
index++;
}
itemChecked = checked;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
Log.d(TAG+".mAdapter", "bindView(): cursor position: " + ((cursor != null)? cursor.getPosition(): 0));
final int position = this.getCursor().getPosition();
Bundle data = new Bundle();
// Get the encounter UUID
String uuid = cursor.getString(1);
String state = cursor.getString(4);
Boolean finished = Boolean.valueOf(cursor.getString(8));
data.putParcelable(Intents.EXTRA_ENCOUNTER, Uris.withAppendedUuid(Encounters.CONTENT_URI, uuid));
data.putBoolean(Encounters.Contract.FINISHED,finished);
final String procedureUuid = cursor.getString(2);
final String date = cursor.getString(7);
final int status = cursor.getInt(5);
final int queuePosition = cursor.getInt(6);
final String patientUUid = cursor.getString(3);
// MAke sure we bind the text views to something before
// anything else happens
((TextView) view.findViewById(R.id.procedure)).setText(procedureUuid);
((TextView) view.findViewById(R.id.subject)).setText(patientUUid);
((TextView) view.findViewById(R.id.procedure_date)).setText(date);
((TextView) view.findViewById(R.id.queue_status)).setText("" + status + " - " + queuePosition);
//((CheckBox) view.findViewById(R.id.checkbox));
//view.setTag(0, uuid);
//view.setTag(1,state);
// TODO move this to a background thread?
// Sets the procedure title string
setProcedure(context,view,procedureUuid);
// Sets the date string
setDate(view, date);
// Sets the status string
setUploadStatus(view,status,queuePosition,finished);
// Sets the patient name and id string
setPatient(context,view,patientUUid);
Log.d(TAG, "Putting data into position: " + position);
mData.put(position,data);
}
public View getView(final int pos, View inView, ViewGroup parent) {
Log.i(TAG+".mAdapter", "getView()");
if (inView == null) {
//LayoutInflater inflater = (LayoutInflater) context
// .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inView = mInflater.inflate(R.layout.encounterlist_item, parent,false);
}
final CheckBox cBox = (CheckBox) inView.findViewById(R.id.checkbox);
// CheckBox
cBox.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Need the size check
CheckBox cb = (CheckBox) v.findViewById(R.id.checkbox);
if (cb.isChecked()) {
if(itemChecked.size() > 0)
itemChecked.set(pos, true);
} else if (!cb.isChecked()) {
if(itemChecked.size() > 0)
itemChecked.set(pos, false);
}
}
});
if(itemChecked.size() > 0)
cBox.setChecked(itemChecked.get(pos));
return super.getView(pos, inView, 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.encounterlist_item, viewGroup, false);
bindView(view, context, cursor);
return view;
}
}
public void setProcedure(Context context, View view, String uuid){
Log.i(TAG, "setProcedure() " + uuid);
TextView name = (TextView) view.findViewById(R.id.procedure);
String title = "null";
Cursor cur2 = null;
Uri procedure = Uri.parse(Procedures.CONTENT_URI.toString() + "/" + uuid);
try {
cur2 = getActivity().getContentResolver().query(procedure,
new String[]{
Procedures.Contract._ID,
Procedures.Contract.TITLE},
null, null, null);
if (cur2.moveToFirst()) {
title = cur2.getString(1);
}
}catch(Exception e){
e.printStackTrace();
} finally {
if(cur2 != null) cur2.close();
}
name.setText((TextUtils.isEmpty(title)? "null": title));
}
public void setPatient(Context context, View view, String uuid){
Log.i(TAG, "setPatient() " + uuid);
TextView name = (TextView) view.findViewById(R.id.subject);
Cursor c = null;
String familyName = null;
String givenName = null;
String displayName = null;
String id = null;
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);
}
} finally {
if(c != null) c.close();
name.setText((TextUtils.isEmpty(displayName)? "null": displayName) + " "+ id);
}
}
public void setDate(View view, String date){
Log.i(TAG, "setDate() " + date);
/*
TextView dateView = (TextView)view.findViewById(R.id.procedure_date);
Date dateObj = null;
try {
dateObj = sdf.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
String display = (dateObj == null)? "null date": df.format(dateObj);
dateView.setText(display);
*/
TextView dateView = (TextView)view.findViewById(R.id.procedure_date);
try {
Date d = Dates.fromSQL(date);
DateTime dt = new DateTime(d);
int month = dt.getMonthOfYear();
int dayOfMonth = dt.getDayOfMonth();
int year = dt.getYear();
String localizedMonth = months[month - 1];
dateView.setText(String.format("%02d %s %04d", dayOfMonth, localizedMonth, year));
} catch (ParseException e){
e.printStackTrace();
}
}
public void setUploadStatus(View view, int queueStatus, int queuePosition,boolean complete){
Log.i(TAG, "setUploadStatus() " + queueStatus + ":" + queuePosition);
TextView statusView = (TextView)view.findViewById(R.id.queue_status);
statusView.setText("" + queueStatus + " - " + queuePosition);
String message = "";
if (queueStatus == 0)
message = getString(R.string.not_uploaded);
else if (queueStatus == QueueManager.UPLOAD_STATUS_WAITING) {
message = "Waiting in the queue to be uploaded, " + queuePosition;
if (queuePosition == -1)
message = "Waiting in the queue to be uploaded";
else if (queuePosition == 1)
message += "st in line";
else if (queuePosition == 2)
message += "nd in line";
else if (queuePosition == 3)
message += "rd in line";
else
message += "th in line";
} else if (queueStatus == QueueManager.UPLOAD_STATUS_SUCCESS)
message = getString(R.string.upload_success);
else if (queueStatus == QueueManager.UPLOAD_STATUS_IN_PROGRESS)
message = getString(R.string.general_upload_in_progress);
else if (queueStatus == QueueManager.UPLOAD_NO_CONNECTIVITY)
message = "Upload stalled - Waiting for connectivity";
else if (queueStatus == QueueManager.UPLOAD_STATUS_FAILURE)
message = getString(R.string.upload_fail);
else if (queueStatus == QueueManager.UPLOAD_STATUS_CREDENTIALS_INVALID)
message = "Upload stalled - username/password incorrect";
else Log.i(TAG, "Not a valid number stored in database.");
//TODO Fix this so that it shows whether finished
/*
if(complete)
message = message + "-" + StringUtils.getLocalizedString(getActivity(),R.string.general_complete);
else
message = message + "-" + StringUtils.getLocalizedString(getActivity(),R.string.general_incomplete);
*/
statusView.setText(message);
}
/** All checkboxes will be checked */
public void selectAllProcedures() {
for (int x = 0; x < getListAdapter().getCount(); x++) {
try {
CheckBox checkbox = (CheckBox) getListView().getChildAt(x)
.findViewById(R.id.checkbox);
checkbox.setChecked(true);
Log.i(TAG, "....Is checkbox checked? (Should be true): "
+ checkbox.isChecked());
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Exception in selectAll(): pos: " + x+" ," + e.getMessage());
}
}
}
/*
* Unselect all checked items in the list.
*/
public void unselectAllProcedures() {
try {
for (int x = 0; x < getListAdapter().getCount(); x++) {
View v = getListView().getChildAt(x);
//getListView().setItemChecked(x, false);
CheckBox checkbox = (CheckBox) v.findViewById(R.id.checkbox);
checkbox.setChecked(false);
}
}
catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Exception in unselectAll(): " + e.toString());
}
}
public List<Uri> getSelected(){
Log.i(TAG, "getSelected()");
List<Uri> uris = new ArrayList<Uri>();
long[] ids = getChecked();
//if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.FROYO)
int count = ids.length;
Log.d(TAG, "....count=" + count);
// iterate over list and resend checked
for (int x = 0; x < count; x++) {
//View child = view.getChildAt(x);
Bundle data = mData.get(x);
if(data != null){
Uri encounter = data.getParcelable(Intents.EXTRA_ENCOUNTER);
uris.add(encounter);
} else {
Log.e(TAG,"....NULL data bundle");
}
}
return uris;
}
public List<Uri> getSelectedFinished(){
Log.i(TAG, "getSelectedFinished()");
List<Uri> uris = new ArrayList<Uri>();
long[] ids = getChecked();
int count = ids.length;
Log.d(TAG, "....count=" + count);
// iterate over list and resend checked
for (int x = 0; x < count; x++) {
//View child = view.getChildAt(x);
boolean finished = isItemFinished(x);
if(finished){
Uri encounter = getItemUri(x);
uris.add(encounter);
} else {
Log.e(TAG,"....NULL data bundle");
}
}
return uris;
}
public long[] getChecked(){
Log.i(TAG,"getChecked()");
List<Long> checked = new ArrayList<Long>();
for(int x = 0;x < getListAdapter().getCount(); x++){
if(getItemChecked(x)){
checked.add(new Long(x));
}
}
long[] ids = new long[checked.size()];
for(int y=0; y < checked.size();y++){
ids[y] = checked.get(y);
}
return ids;
}
public boolean getItemChecked(int id){
Log.i(TAG,"getItemChecked() " + id);
ListView view = getListView();
final View child = view.getChildAt(id);
if(child == null)
return false;
CheckBox checkbox = (CheckBox) child.findViewById(R.id.checkbox);
return (checkbox != null)? checkbox.isChecked(): false;
}
public boolean isItemFinished(long id){
Log.i(TAG,"isItemFinished() " + id);
Bundle data = mData.get(id);
boolean finished = data.getBoolean(Encounters.Contract.FINISHED, false);
return finished;
}
public Uri getItemUri(long id){
Bundle data = mData.get(id);
if(data != null){
Uri encounter = data.getParcelable(Intents.EXTRA_ENCOUNTER);
return encounter;
}
return Uri.EMPTY;
}
public int deleteSelected(){
Log.i(TAG, "deleteSelected()");
int count = 0;
int obsCount = 0;
int imageCount = 0;
long[] checked = getChecked();
List<Long> ids = new ArrayList<Long>();
for(long id:checked){
ids.add(id);
}
for(Long id:ids){
try{
String uuid = ((Cursor) getListAdapter().getItem(id.intValue())).getString(1);
Log.d(TAG,"....uuid = " + uuid);
obsCount += getActivity().getContentResolver().delete(Observations.CONTENT_URI,
Observations.Contract.ENCOUNTER + " = ?",
new String[]{ uuid });
imageCount += getActivity().getContentResolver().delete(ImageSQLFormat.CONTENT_URI,
ImageSQLFormat.ENCOUNTER_ID+ " = ?",
new String[]{ String.valueOf(id) });
} catch(Exception e){
e.printStackTrace();
}
}
Log.d(TAG, "....deleted:");
Log.d(TAG, "........images=" + imageCount);
Log.d(TAG, "........observations" + obsCount);
try{
if(ids.size() > 1) {
String idList = SanaUtil.formatPrimaryKeyList(ids);
count = getActivity().getContentResolver().delete(Encounters.CONTENT_URI,
Encounters.Contract._ID + " IN " + idList, null);
} else if(ids.size() == 1){
count = getActivity().getContentResolver().delete(
Encounters.CONTENT_URI,
Encounters.Contract._ID + " = ?",
new String[]{ String.valueOf(ids.get(0)) });
} else {
Log.d(TAG, "Delete with None selected");
}
} catch(Exception e){
e.printStackTrace();
}
Log.d(TAG,"....deleted encounters = " + count);
return count;
}
public Bundle getSelectedData(long id){
Bundle data = mData.get(id);
return data;
}
}