Java tutorial
/* * Copyright (C) 2009 University of Washington * * 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 com.odkclinic.client; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.InputStreamBody; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.client.DefaultHttpClient; import android.app.ExpandableListActivity; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.ExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.LinearLayout; import android.widget.SimpleCursorTreeAdapter; import android.widget.TextView; import android.widget.Toast; import com.odkclinic.client.db.DbAdapter; import com.odkclinic.client.db.tables.CohortMemberTable; import com.odkclinic.client.db.tables.PatientTable; import com.odkclinic.model.bundle.CohortBundle; import com.odkclinic.model.bundle.CohortMemberBundle; import com.odkclinic.model.bundle.ConceptBundle; import com.odkclinic.model.bundle.ConceptNameBundle; import com.odkclinic.model.bundle.EncounterBundle; import com.odkclinic.model.bundle.LocationBundle; import com.odkclinic.model.bundle.ObservationBundle; import com.odkclinic.model.bundle.PatientBundle; import com.odkclinic.model.bundle.PatientProgramBundle; import com.odkclinic.model.bundle.ProgramBundle; import com.odkclinic.model.bundle.ProgramWorkflowBundle; /** * * Activity that lists a set of patients to be viewed. * * @author Euzel Villanueva * */ public class PatientList extends ExpandableListActivity { private static String LOG_TAG = PatientList.class.getName(); private DbAdapter mDb; private int mGroupIdColumnIndex; private ExpandableListAdapter mAdapter; private Toast mToast; private SendDataTask mSData; private Context mContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.patientlist_list); // instantiate db instance and open mDb = new DbAdapter(this); mDb.open(); fillData(); mToast = Toast.makeText(this, "test", 2); mContext = this; } private void fillData() { // get cursor to list of patients Cursor patientCursor = mDb.getPatientList(); startManagingCursor(patientCursor); // cache group id mGroupIdColumnIndex = patientCursor.getColumnIndexOrThrow(PatientTable.ID.getName()); Log.d(LOG_TAG, "There are " + patientCursor.getCount() + " patients."); // create listadapter for cursor mAdapter = new ExpandablePatientListAdapter(patientCursor, this, R.layout.patientlist_group_item, R.layout.patientlist_child_item, new String[] { PatientTable.NAME.getName(), PatientTable.ID.getName(), CohortMemberTable.COHORT_ID.getName() }, new int[] { R.id.patient_name, R.id.patient_id, R.id.cohort_id }, new String[] { PatientTable.GENDER.getName(), PatientTable.RACE.getName(), PatientTable.BIRTHDATE.getName() }, new int[] { R.id.patientgender, R.id.patientrace, R.id.patientbirthdate }); setListAdapter(mAdapter); } // TODO check change through result code @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case R.id.patientlist_manage: break; case R.id.patientlist_update: break; default: break; } fillData(); } @Override protected void onPause() { super.onPause(); } @Override protected void onResume() { super.onResume(); } @Override protected void onDestroy() { super.onDestroy(); mDb.close(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the patientlist menu XML resource. MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.patientlist, menu); return true; } /** * Starts intent for managing patients. */ private void managePatientsIntent() { Intent i = new Intent(this, PatientManagerList.class); startActivityForResult(i, R.id.patientlist_manage); } /** * Starts intent for viewing updates. */ private void updatesIntent() { Intent i = new Intent(this, UpdateList.class); startActivityForResult(i, R.id.patientlist_sync); } /** * Starts intent for viewing patientdata */ private void patientDetailsIntent(long id) { Intent i = new Intent(this, PatientDetails.class); Bundle extras = new Bundle(); extras.putLong(PatientTable.ID.getName(), id); i.putExtras(extras); startActivityForResult(i, R.layout.patientdetails); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.patientlist_manage: managePatientsIntent(); return true; case R.id.patientlist_sync: if (mSData == null || mSData.getStatus().equals(AsyncTask.Status.FINISHED)) { Log.d(LOG_TAG, "Starting synchronization with server."); mSData = new SendDataTask(); mSData.execute((Void[]) null); mToast.setText("Starting synchronization with server."); mToast.show(); } else { mToast.setText("Already started synchronization with server."); mToast.show(); } return true; case R.id.patientlist_update: updatesIntent(); return true; default: break; } return false; } @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { patientDetailsIntent(id); return true; } private class ExpandablePatientListAdapter extends SimpleCursorTreeAdapter { public ExpandablePatientListAdapter(Cursor cursor, Context context, int groupLayout, int childLayout, String[] groupFrom, int[] groupTo, String[] childrenFrom, int[] childrenTo) { super(context, cursor, groupLayout, groupFrom, groupTo, childLayout, childrenFrom, childrenTo); } @Override protected Cursor getChildrenCursor(Cursor groupCursor) { long id = groupCursor.getLong(mGroupIdColumnIndex); Cursor patientInfo = mDb.getPatientDemographicInfo(id); startManagingCursor(patientInfo); return patientInfo; } @Override protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) { String tempGender = cursor.getString(0); String tempRace = cursor.getString(1); String tempBDay = cursor.getString(2); if (tempGender != null) { ((TextView) view.findViewById(R.id.patientgender)).setText(tempGender); } if (tempRace != null) { ((LinearLayout) view.findViewById(R.id.patientracemodule)).setVisibility(1); ((TextView) view.findViewById(R.id.patientrace)).setText(tempRace); } else { ((LinearLayout) view.findViewById(R.id.patientracemodule)).setVisibility(0); } if (tempBDay != null) { DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); DateFormat df2 = new SimpleDateFormat("MM/dd/yyyy"); try { Date date = df.parse(tempBDay); ((TextView) view.findViewById(R.id.patientbirthdate)).setText(df2.format(date)); } catch (ParseException e) { ((TextView) view.findViewById(R.id.patientbirthdate)).setText(tempBDay); e.printStackTrace(); } } } } public static final String SERVER_URL = "http://10.0.2.2:8080/openmrs/moduleServlet/odkclinic/ODKClinicServer"; private static final String USER = "admin"; private static final String PASS = "Emmzelv69"; private static final String SKEY = "null"; //dummy serializer key /** Actions for Android ODK app */ public static final byte ACTION_ANDROID_DOWNLOAD_ENCOUNTER = 1; public static final byte ACTION_ANDROID_DOWNLOAD_OBS = 2; public static final byte ACTION_ANDROID_DOWNLOAD_PATIENTS = 4; public static final byte ACTION_ANDROID_DOWNLOAD_PROGRAMS = 8; public static final byte ACTION_ANDROID_DOWNLOAD_CONCEPTS = 16; public static final byte ACTION_ANDROID_DOWNLOAD_LOCATIONS = 32; public static final byte ACTION_ANDROID_DOWNLOAD_CONCEPTNAMES = 64; public static final byte ACTION_ANDROID_DOWNLOAD_COHORTS = 127; public static final byte ACTION_ANDROID_DOWNLOAD_COHORTMEMBERS = 126; public static final byte ACTION_ANDROID_DOWNLOAD_PATIENTPROGRAMS = 125; public static final byte ACTION_ANDROID_DOWNLOAD_PROGRAMWORKFLOWS = 124; //public static final byte ACTION_ANDROID_DOWNLOADS = 15; public static final byte ACTION_ANDROID_UPLOAD_ENCOUNTER = 16; public static final byte ACTION_ANDROID_UPLOAD_OBS = 32; public static final byte ACTION_ANDROID_UPLOADS = 48; public static final byte ACTION_ANDROID_END = 123; /** Networking responses */ /** Problems occured during connection of the request. */ public static final int STATUS_ERROR = 500; /** Request communicated successfully. */ public static final int STATUS_SUCCESS = 200; /** Not permitted to carry out the requested operation. */ public static final int STATUS_ACCESS_DENIED = 403; /** * * Class for syncing data from the server. * */ private class SendDataTask extends AsyncTask<Void, Void, Void> { private boolean fail = true; @Override protected void onPreExecute() { super.onPreExecute(); mToast.setText("Starting synchronization of server"); mToast.show(); } @Override protected Void doInBackground(Void... params) { DbAdapter db = mDb; //just random reference if (db == null) { db = new DbAdapter(mContext); db.open(); } if (db.checkSync()) { return null; } db.markSync(true); EncounterBundle eb = db.getEncounterBundle(); ObservationBundle ob = db.getObservationBundle(); HttpURLConnection con = null; try { HttpClient httpclient = new DefaultHttpClient(); HttpPost httpost = new HttpPost(SERVER_URL); MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); reqEntity.addPart("USER", new StringBody(USER)); reqEntity.addPart("PASS", new StringBody(PASS)); reqEntity.addPart("SKEY", new StringBody(SKEY)); reqEntity.addPart("REVTOKEN", new StringBody(db.getRevToken() + "")); if (eb.getBundle().size() > 0) { byte[] ebBytes = eb.getBytes(); Log.d(LOG_TAG, "Sending bundle of size " + ebBytes.length); reqEntity.addPart("UPLOAD_ENCOUNTER", new InputStreamKnownSizeBody( new ByteArrayInputStream(ebBytes), ebBytes.length, "img/jpeg", "ff")); } if (ob.getBundle().size() > 0) { byte[] obBytes = ob.getBytes(); Log.d(LOG_TAG, "Sending bundle of size " + obBytes.length); reqEntity.addPart("UPLOAD_OBSERVATION", new InputStreamKnownSizeBody( new ByteArrayInputStream(obBytes), obBytes.length, "img/jpeg", "ff")); } reqEntity.addPart("DOWNLOAD_ACTIONS", new StringBody("DOWNLOAD_PROGRAM;" + "DOWNLOAD_PATIENT;" + "DOWNLOAD_ENCOUNTER;" + "DOWNLOAD_OBSERVATION;" + "DOWNLOAD_LOCATION;" + "DOWNLOAD_PATIENTPROGRAM;" + "DOWNLOAD_COHORT;" + "DOWNLOAD_COHORTMEMBER;" + "DOWNLOAD_CONCEPT;" + "DOWNLOAD_CONCEPTNAME;" + "DOWNLOAD_PROGRAMWORKFLOW")); httpost.setEntity(reqEntity); HttpResponse response = httpclient.execute(httpost); Log.d(LOG_TAG, response.getStatusLine().toString()); if (response.getStatusLine().getStatusCode() == STATUS_SUCCESS) { Log.d(LOG_TAG + "/SendDataTask", "Sending data successfull"); HttpEntity he = response.getEntity(); DataInputStream dis = new DataInputStream(he.getContent()); ProgramBundle prb = new ProgramBundle(); PatientBundle pb = new PatientBundle(); ObservationBundle secondOb = new ObservationBundle(); EncounterBundle secondEb = new EncounterBundle(); LocationBundle lb = new LocationBundle(); PatientProgramBundle ppb = new PatientProgramBundle(); CohortBundle cb = new CohortBundle(); CohortMemberBundle cmb = new CohortMemberBundle(); ConceptBundle cnb = new ConceptBundle(); ConceptNameBundle cnnb = new ConceptNameBundle(); ProgramWorkflowBundle pwb = new ProgramWorkflowBundle(); boolean success = false; try { int x = dis.read(); do { switch (x) { case ACTION_ANDROID_DOWNLOAD_ENCOUNTER: secondEb.read(dis); break; case ACTION_ANDROID_DOWNLOAD_OBS: secondOb.read(dis); break; case ACTION_ANDROID_DOWNLOAD_PROGRAMS: prb.read(dis); break; case ACTION_ANDROID_DOWNLOAD_COHORTMEMBERS: cmb.read(dis); break; case ACTION_ANDROID_DOWNLOAD_COHORTS: cb.read(dis); break; case ACTION_ANDROID_DOWNLOAD_CONCEPTS: cnb.read(dis); break; case ACTION_ANDROID_DOWNLOAD_CONCEPTNAMES: cnnb.read(dis); break; case ACTION_ANDROID_DOWNLOAD_LOCATIONS: lb.read(dis); break; case ACTION_ANDROID_DOWNLOAD_PATIENTPROGRAMS: ppb.read(dis); break; case ACTION_ANDROID_DOWNLOAD_PATIENTS: pb.read(dis); break; case ACTION_ANDROID_DOWNLOAD_PROGRAMWORKFLOWS: pwb.read(dis); break; } x = dis.read(); System.out.println(x); } while (x != ACTION_ANDROID_END && x != -1); db.setRevToken(dis.readLong()); success = true; } catch (EOFException e) { Log.d(LOG_TAG + "/SendDataTask", "Error in Stream: " + e.getMessage()); } finally { dis.close(); } if (!success) { fail = true; return (Void) null; } if (db == null) { db = new DbAdapter(mContext); db.open(); } // Delete the encounters/obs that were sent from client table db.deleteSyncedEncounters(eb); db.deleteSyncedObservations(ob); if (cb.getBundle().size() > 0) { db.insertCohortBundle(cb); Log.d(LOG_TAG, "GOT NONEMPTY CB"); } else { Log.d(LOG_TAG, "GOT EMPTY CB"); } if (cnb.getBundle().size() > 0) { db.insertConceptBundle(cnb); Log.d(LOG_TAG, "GOT NONEMPTY CNB"); } else { Log.d(LOG_TAG, "GOT EMPTY CNB"); } if (cnnb.getBundle().size() > 0) { db.insertConceptNameBundle(cnnb); Log.d(LOG_TAG, "GOT NONEMPTY CNNB"); } else { Log.d(LOG_TAG, "GOT EMPTY CNNB"); } if (pb.getBundle().size() > 0) { db.insertPatientBundle(pb); Log.d(LOG_TAG, "GOT NONEMPTY PB"); } else { Log.d(LOG_TAG, "GOT EMPTY PB"); } if (cmb.getBundle().size() > 0) { db.insertCohortMemberBundle(cmb); Log.d(LOG_TAG, "GOT NONEMPTY CMB"); } else { Log.d(LOG_TAG, "GOT EMPTY CMB"); } if (lb.getBundle().size() > 0) { db.insertLocationBundle(lb); Log.d(LOG_TAG, "GOT NONEMPTY LB"); } else { Log.d(LOG_TAG, "GOT EMPTY LB"); } if (prb.getBundle().size() > 0) { db.insertProgramBundle(prb); Log.d(LOG_TAG, "GOT NONEMPTY PRB"); } else { Log.d(LOG_TAG, "GOT EMPTY PRB"); } if (ppb.getBundle().size() > 0) { db.insertPatientProgramBundle(ppb); Log.d(LOG_TAG, "GOT NONEMPTY PPB"); } else { Log.d(LOG_TAG, "GOT EMPTY PPB"); } if (secondEb.getBundle().size() > 0) { db.insertEncounterBundle(secondEb); Log.d(LOG_TAG, "GOT NONEMPTY EB"); } else { Log.d(LOG_TAG, "GOT EMPTY EB"); } if (secondOb.getBundle().size() > 0) { db.insertObservationBundle(secondOb); Log.d(LOG_TAG, "GOT NONEMPTY OB"); } else { Log.d(LOG_TAG, "GOT EMPTY OB"); } if (pwb.getBundle().size() > 0) { db.insertProgramWorkflowBundle(pwb); Log.d(LOG_TAG, "GOT NONEMPTY PWB"); } else { Log.d(LOG_TAG, "GOT EMPTY PWB"); } // update rev token to new value //db.setRevToken(dis.readLong()); fail = false; } else { // mark the entries in the respective tables and leave them there. Log.d(LOG_TAG + "/SendDataTask", String.format("Sending data failed. Status: %s.", response.getStatusLine())); db.markEncountersFailed(eb); db.markObservationsFailed(ob); } } catch (IOException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } finally { if (con != null) con.disconnect(); db.markSync(false); } return (Void) null; } private class InputStreamKnownSizeBody extends InputStreamBody { private int length; public InputStreamKnownSizeBody(final InputStream in, final int length, final String mimeType, final String filename) { super(in, mimeType, filename); this.length = length; } @Override public long getContentLength() { return this.length; } } @Override protected void onPostExecute(Void result) { if (mToast == null) mToast = Toast.makeText(new PatientList(), "", 2); if (fail) { mToast.setText("Synchronization with server has failed."); } else mToast.setText("Synchronization with server has completed."); mToast.show(); if (mDb != null) { fillData(); } } } }