package org.sana.android.activity;
import org.sana.R;
import org.sana.android.Constants;
import org.sana.android.activity.settings.Settings;
import org.sana.android.provider.Encounters;
import org.sana.android.provider.Notifications;
import org.sana.android.provider.Patients;
import org.sana.android.provider.Procedures;
import org.sana.android.media.EducationResource;
import org.sana.android.procedure.Procedure;
import org.sana.android.service.BackgroundUploader;
import org.sana.android.service.ServiceConnector;
import org.sana.android.service.ServiceListener;
import org.sana.android.task.MDSSyncTask;
import org.sana.android.task.ResetDatabaseTask;
import org.sana.android.util.SanaUtil;
import org.sana.android.util.UriUtil;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.AsyncTask.Status;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
/**
* Main Sana activity. When Sana is launched, this activity runs, allowing the
* user to either run a procedure, view notifications, or view pending
* transfers.
*
* @author Sana Dev Team
*/
public class Sana extends BaseActivity implements View.OnClickListener {
public static final String TAG = Sana.class.getSimpleName();
// Option menu codes
private static final int OPTION_RELOAD_DATABASE = 0;
private static final int OPTION_SETTINGS = 1;
private static final int OPTION_SYNC = 2;
// Activity request codes
/** Intent request code for picking a procedure */
public static final int PICK_PROCEDURE = 0;
/** Intent request code for picking a saved procedure */
public static final int PICK_SAVEDPROCEDURE = 1;
/** Intent request code for picking a notification */
public static final int PICK_NOTIFICATION = 2;
/** Intent request code to start running a procedure */
public static final int RUN_PROCEDURE = 3;
/** Intent request code to resume running a saved procedure*/
public static final int RESUME_PROCEDURE = 4;
/** INtent request code to view settings */
public static final int SETTINGS = 6;
/** Intent request code for creating a new patient. */
public static final int NEW_PATIENT = 7;
/** Intent request code for viewing all patients. */
public static final int PICK_PATIENT = 8;
//Alert dialog codes
private static final int DIALOG_INCORRECT_PASSWORD = 0;
private static final int DIALOG_NO_CONNECTIVITY = 1;
private static final int DIALOG_NO_PHONE_NAME = 2;
private ServiceConnector mConnector = new ServiceConnector();
private BackgroundUploader mUploadService = null;
private ResetDatabaseTask mResetDatabaseTask;
private MDSSyncTask mSyncTask;
// State
private Bundle mSavedState;
static final String STATE_CHECK_CREDENTIALS = "_credentials";
static final String STATE_MDS_SYNC = "_mdssync";
static final String STATE_RESET_DB = "_resetdb";
/**
* Background listener for taking action when network service is available
*
* @author Sana Development Team
*
*/
private class BackgroundUploaderConnectionListener implements
ServiceListener<BackgroundUploader>
{
public void onConnect(BackgroundUploader uploadService) {
Log.i(TAG, "onServiceConnected");
mUploadService = uploadService;
}
public void onDisconnect(BackgroundUploader uploadService) {
Log.i(TAG, "onServiceDisconnected");
mUploadService = null;
}
}
/** {@inheritDoc} */
@Override
public void onDestroy() {
super.onDestroy();
try {
mConnector.disconnect(this);
mUploadService = null;
} catch (IllegalArgumentException e) {
Log.e(TAG, "While disconnecting service got exception: "
+ e.getMessage());
e.printStackTrace();
}
}
/** {@inheritDoc} */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.main);
View viewPatients = findViewById(R.id.moca_main_view_patients);
viewPatients.setOnClickListener(this);
View openProcedure = findViewById(R.id.moca_main_procedure);
openProcedure.setOnClickListener(this);
View viewTransfers = findViewById(R.id.moca_main_transfers);
viewTransfers.setOnClickListener(this);
View viewNotifications = findViewById(R.id.moca_main_notifications);
viewNotifications.setOnClickListener(this);
// Create a connection to the background upload service.
// This starts the service when the app starts.
try {
mConnector.setServiceListener(
new BackgroundUploaderConnectionListener());
mConnector.connect(this);
}
catch (Exception e) {
Log.e(TAG, "Exception starting background upload service: "
+ e.getMessage());
e.printStackTrace();
}
}
void init(){
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(this);
if(!preferences.getBoolean(Constants.DB_INIT,false)){
doClearDatabase();
// Make sure directory structure is in place on external drive
EducationResource.intializeDevice();
Procedure.intializeDevice();
preferences.edit().putBoolean(Constants.DB_INIT, true).commit();
}
}
/**
* Starts Activity for viewing all patients
*
* @param intentWithProcedureInfo Intent to use for selecting a patient
* (optional). This Intent should contain information about the procedure
* to start for the selected patient (via Bundle extra).
*/
private void pickPatient(Intent intentWithProcedureInfo) {
if (intentWithProcedureInfo == null) {
intentWithProcedureInfo = new Intent();
}
intentWithProcedureInfo.setAction(Intent.ACTION_PICK);
intentWithProcedureInfo.setType(Patients.CONTENT_TYPE);
intentWithProcedureInfo.setData(Patients.CONTENT_URI);
onSaveAppState(intentWithProcedureInfo);
startActivityForResult(intentWithProcedureInfo, PICK_PATIENT);
}
/**
* Activates selecting a procedure and to start a new encounter
*
* @param intentWithPatientId Optional Intent for selecting a Procedure.
* This Intent should contain the patient (patient ID) to run the
* Procedure for.
*/
private void pickProcedure(Intent intentWithPatientId) {
if (intentWithPatientId == null) {
intentWithPatientId = new Intent();
}
intentWithPatientId.setAction(Intent.ACTION_PICK);
intentWithPatientId.setType(Procedures.CONTENT_TYPE);
intentWithPatientId.setData(Procedures.CONTENT_URI);
onSaveAppState(intentWithPatientId);
startActivityForResult(intentWithPatientId, PICK_PROCEDURE);
}
/** Starts Activity for selecting and then viewing a previous encounter */
private void pickSavedProcedure() {
Intent i = new Intent(Intent.ACTION_PICK);
i.setType(Encounters.CONTENT_TYPE);
i.setData(Encounters.CONTENT_URI);
onSaveAppState(i);
startActivityForResult(i, PICK_SAVEDPROCEDURE);
}
/** Starts Activity for selecting and then viewing notifications */
private void pickNotification() {
Intent i = new Intent(Intent.ACTION_PICK);
i.setType(Notifications.CONTENT_TYPE);
i.setData(Notifications.CONTENT_URI);
onSaveAppState(i);
startActivityForResult(i, PICK_NOTIFICATION);
}
/** {@inheritDoc} */
@Override
public void onClick(View arg0) {
Log.d(TAG, "Button: " +arg0.getId());
switch (arg0.getId()) {
// buttons on the main screen
case R.id.moca_main_view_patients:
pickPatient(null);
break;
case R.id.moca_main_procedure:
pickProcedure(null);
break;
case R.id.moca_main_transfers:
pickSavedProcedure();
break;
case R.id.moca_main_notifications:
pickNotification();
break;
}
}
/** {@inheritDoc} */
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data)
{
SanaUtil.logActivityResult(TAG, requestCode, resultCode);
switch (resultCode) {
case RESULT_CANCELED:
if(requestCode == RUN_PROCEDURE) {
pickProcedure(null);
} else if(requestCode == RESUME_PROCEDURE) {
pickSavedProcedure();
} else if(requestCode == SETTINGS) {
//Check to make sure there is a phone number entered,
// otherwise will not connect to MDS
String phoneNum = PreferenceManager
.getDefaultSharedPreferences(this)
.getString(Constants.PREFERENCE_PHONE_NAME,
null);
Log.d(TAG, "phoneNum from preferences is: " + phoneNum);
if (TextUtils.isEmpty(phoneNum)) {
Log.d(TAG, "No phone number entered - showing dialog now");
if(!isFinishing())
showDialog(DIALOG_NO_PHONE_NAME);
}
}
break;
case RESULT_OK:
// update selected subject
onUpdateAppState(data);
Uri uri = null;
if(data != null) {
uri = data.getData();
}
if(requestCode == PICK_PROCEDURE) {
//assert(uri != null);
//TODO use the patient UUID from the subject extra
long patientId = data.getLongExtra(PatientsList.EXTRA_PATIENT_ID, PatientsList.INVALID_PATIENT_ID);
mProcedure = uri;
if(UriUtil.isEmpty(mSubject)){
//if (patientId == PatientsList.INVALID_PATIENT_ID) {
pickPatient(data);
} else {
doPerformProcedureForPatient(uri, patientId);
}
} else if(requestCode == PICK_SAVEDPROCEDURE) {
assert(uri != null);
mEncounter = uri;
doResumeProcedure(uri);
} else if(requestCode == PICK_NOTIFICATION) {
assert(uri != null);
doShowNotification(uri);
} else if (requestCode == RUN_PROCEDURE ||
requestCode == RESUME_PROCEDURE) {
pickSavedProcedure();
} else if (requestCode == PICK_PATIENT) {
long patientId = data.getLongExtra(PatientsList.EXTRA_PATIENT_ID, PatientsList.INVALID_PATIENT_ID);
//assert(patientId != PatientsList.INVALID_PATIENT_ID);
//Uri procedureUri = data.getParcelableExtra(ProceduresList.EXTRA_PROCEDURE_URI);
mSubject = uri;
if (UriUtil.isEmpty(mProcedure)) {
pickProcedure(data);
} else {
doPerformProcedureForPatient(mProcedure, patientId);
}
}
break;
}
}
/** {@inheritDoc} */
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_INCORRECT_PASSWORD:
return new AlertDialog.Builder(this)
.setTitle("Error!")
.setMessage(getString(R.string.dialog_incorrect_credentials))
.setPositiveButton(getString(R.string.general_change_settings),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Dismiss dialog and return to settings
Intent i = new Intent(Intent.ACTION_PICK);
i.setClass(Sana.this, Settings.class);
onSaveAppState(i);
startActivityForResult(i, SETTINGS);
setResult(RESULT_OK, null);
dialog.dismiss();
}
})
.setCancelable(true)
.setNegativeButton(getString(R.string.general_cancel),
new OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton) {
setResult(RESULT_CANCELED, null);
dialog.dismiss();
}
})
.create();
case DIALOG_NO_CONNECTIVITY:
return new AlertDialog.Builder(this)
.setTitle(getString(R.string.general_error))
.setMessage(getString(R.string.dialog_no_network))
.setPositiveButton(getString(R.string.general_ok),
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton) {
// Dismiss dialog and return to settings
setResult(RESULT_OK, null);
dialog.dismiss();
}
})
.create();
case DIALOG_NO_PHONE_NAME:
return new AlertDialog.Builder(this)
.setTitle(getString(R.string.general_error))
.setMessage(getString(R.string.dialog_no_phone_name))
.setPositiveButton(getString(R.string.general_change_settings),
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton) {
// Dismiss dialog and return to settings
Intent i = new Intent(Intent.ACTION_PICK);
i.setClass(Sana.this, Settings.class);
onSaveAppState(i);
startActivityForResult(i, SETTINGS);
setResult(RESULT_OK, null);
dialog.dismiss();
}
})
.setCancelable(true)
.setNegativeButton(getString(R.string.general_cancel),
new OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton) {
setResult(RESULT_CANCELED, null);
dialog.dismiss();
}
})
.create();
}
return null;
}
/**
* Starts Activity for viewing a Notification.
*
* @param uri The notification to view.
*/
private void doShowNotification(Uri uri) {
try {
Intent i = new Intent(Intent.ACTION_VIEW, uri);
startActivity(i);
} catch(Exception e) {
Log.e(TAG, "While showing notification " + uri
+ " an exception occured: " + e.toString());
}
}
/**
* Starts Activity for resuming a saved procedure
*
* @param uri The saved procedure to restart
*/
private void doResumeProcedure(Uri uri) {
try {
Intent i = new Intent(Intent.ACTION_VIEW, uri);
i.putExtra("savedProcedureUri", uri.toString());
onSaveAppState(i);
startActivityForResult(i, RESUME_PROCEDURE);
} catch(Exception e) {
Log.e(TAG, "While resuming procedure "
+ uri + " an exception occured: " + e.toString());
}
}
/**
* Starts an Activity for running a new Procedure
*
* @param uri The Procedure to run
*/
private void doPerformProcedureForPatient(final Uri uri, long patientId) {
Log.i(TAG, "doPerformProcedure uri=" + uri.toString());
try {
Intent i = new Intent(Intent.ACTION_VIEW, uri);
i.putExtra(PatientsList.EXTRA_PATIENT_ID, patientId);
onSaveAppState(i);
startActivityForResult(i, RUN_PROCEDURE);
} catch (Exception e) {
SanaUtil.errorAlert(this, e.toString());
Log.e(TAG, "While running procedure " + uri
+ " an exception occured: " + e.toString());
}
}
/** {@inheritDoc} */
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, OPTION_RELOAD_DATABASE, 0,
getString(R.string.menu_reload_db));
menu.add(0, OPTION_SETTINGS, 1, getString(R.string.menu_settings));
menu.add(0, OPTION_SYNC, 2, getString(R.string.menu_sync));
return true;
}
/** Executes a task to clear out the database */
private void doClearDatabase() {
// TODO: context leak
if(mResetDatabaseTask!= null && mResetDatabaseTask.getStatus() != Status.FINISHED)
return;
mResetDatabaseTask =
(ResetDatabaseTask) new ResetDatabaseTask(this).execute(this);
}
/** Syncs the Patient database with MDS */
private void doUpdatePatientDatabase() {
if(mSyncTask != null && mSyncTask.getStatus() != Status.FINISHED)
return;
mSyncTask = (MDSSyncTask) new MDSSyncTask(this).execute(this);
}
/** {@inheritDoc} */
@Override
public boolean onOptionsItemSelected(MenuItem item){
switch (item.getItemId()) {
case OPTION_RELOAD_DATABASE:
// TODO: Dialog leak
AlertDialog.Builder bldr = new AlertDialog.Builder(this);
AlertDialog dialog = bldr.create();
dialog.setMessage(getString(R.string.dialog_no_reload_db_warn));
dialog.setCancelable(true);
dialog.setButton("Yes", new OnClickListener() {
public void onClick(DialogInterface i, int v) {
doClearDatabase();
}
});
dialog.setButton2(getString(R.string.general_no),
(OnClickListener)null);
if(!isFinishing())
dialog.show();
return true;
case OPTION_SETTINGS:
Intent i = new Intent(Intent.ACTION_PICK);
i.setClass(this, Settings.class);
startActivityForResult(i, SETTINGS);
return true;
case OPTION_SYNC:
doUpdatePatientDatabase();
return true;
}
return false;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
saveLocalTaskState(outState);
mSavedState = outState;
}
private void saveLocalTaskState(Bundle outState){
final MDSSyncTask mTask = mSyncTask;
if (mTask != null && mTask.getStatus() != Status.FINISHED) {
mTask.cancel(true);
outState.putBoolean(STATE_MDS_SYNC, true);
}
final ResetDatabaseTask rTask = mResetDatabaseTask;
if (rTask != null && rTask.getStatus() != Status.FINISHED) {
rTask.cancel(true);
outState.putBoolean(STATE_RESET_DB, true);
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
restoreLocalTaskState(savedInstanceState);
mSavedState = null;
}
/** Restores any tasks running on this thread */
private void restoreLocalTaskState(Bundle savedInstanceState){
if (savedInstanceState.getBoolean(STATE_MDS_SYNC))
mSyncTask = (MDSSyncTask) new MDSSyncTask(this).execute(this);
if (savedInstanceState.getBoolean(STATE_RESET_DB))
mResetDatabaseTask =
(ResetDatabaseTask) new ResetDatabaseTask(this).execute(this);
}
@Override
protected void onResume() {
super.onResume();
if (mSavedState != null) restoreLocalTaskState(mSavedState);
init();
}
}