package org.sana.android.util; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Iterator; import java.util.List; import java.util.Random; import javax.xml.parsers.ParserConfigurationException; import org.sana.R; import org.sana.android.app.Locales; import org.sana.android.db.SanaDB; import org.sana.android.db.SanaDB.BinarySQLFormat; import org.sana.android.db.SanaDB.ImageSQLFormat; import org.sana.android.db.SanaDB.SoundSQLFormat; import org.sana.android.procedure.Procedure; import org.sana.android.procedure.ProcedureParseException; import org.sana.android.provider.Encounters; import org.sana.android.provider.Notifications; import org.sana.android.provider.Observations; import org.sana.android.provider.Patients; import org.sana.android.provider.Procedures; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.xml.sax.SAXException; import android.app.Activity; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.database.Cursor; import android.net.Uri; import android.net.wifi.WifiManager; import android.os.Environment; import android.telephony.TelephonyManager; import android.util.Log; //TODO /** Application utilities * * @author Sana Development Team */ public class SanaUtil { public static final String TAG = SanaUtil.class.getSimpleName(); private static final String[] PROJECTION = new String[] { Procedures.Contract._ID, Procedures.Contract.TITLE, Procedures.Contract.AUTHOR, Procedures.Contract.VERSION }; private static final String alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; /** Generates a random string. * * @param prefix a set string to prepend * @param length the total length of the * @return a new randomized string. */ public static String randomString(String prefix, int length) { return randomString(prefix, length, alphabet); } /** Generates a random string in a specified alphabet. * * @param prefix a set string to prepend * @param length the total length of the * @param alphabet the set of valid characters * @return a new random string */ public static String randomString(String prefix, int length, String alphabet) { StringBuilder sb = new StringBuilder(prefix); Random r = new Random(); int alphabetlength = alphabet.length(); for (int i = 0; i < length; i++) { sb.append(alphabet.charAt(r.nextInt(alphabetlength - 1))); } return sb.toString(); } /** Creates an error message as a dialog. * * @param context the current Context * @param message the error message */ public static void errorAlert(Context context, String message) { if (context instanceof Activity) { if (!((Activity) context).isFinishing()) createDialog(context, "Error", message).show(); } } /** Creates a message dialog. * * @param context the current Context * @param title the dialog title * @param message the dialog message * @return a new dialogf for alerting the user. */ public static AlertDialog createDialog(Context context, String title, String message) { Builder dialogBuilder = new Builder(context); dialogBuilder.setPositiveButton(context.getResources().getString( R.string.general_ok), null); dialogBuilder.setTitle(title); dialogBuilder.setMessage(message); return dialogBuilder.create(); } public static AlertDialog okCancelDialog(Context context, String title, String message, DialogInterface.OnClickListener okCancel) { Builder dialogBuilder = new Builder(context); dialogBuilder.setPositiveButton(context.getResources().getString( R.string.general_ok), okCancel); dialogBuilder.setTitle(title); dialogBuilder.setMessage(message); dialogBuilder.setNegativeButton(context.getResources().getString( R.string.general_cancel), okCancel); return dialogBuilder.create(); } // TODO /** Retrieves the value for a xml Node attribute or a default if not found. * * @param node The Node to fetch the value from. * @param name The attribute name. * @param defaultValue The default value to return if not found. * @return and attribute value or a default if not found. */ public static String getNodeAttributeOrDefault(Node node, String name, String defaultValue) { NamedNodeMap attributes = node.getAttributes(); Node valueNode = attributes.getNamedItem(name); String value = defaultValue; if (valueNode != null) value = valueNode.getNodeValue(); return value; } // TODO /** Retrieves the value for a xml Node attribute or fails if not found. * * @param <T> the exception type to throw * @param node The Node to fetch the value from. * @param name The attribute name. * @param e an Exception instance * @return the attribute value * @throws T */ public static <T extends Exception> String getNodeAttributeOrFail(Node node, String name, T e) throws T { NamedNodeMap attributes = node.getAttributes(); Node valueNode = attributes.getNamedItem(name); if (valueNode == null) throw e; return valueNode.getNodeValue(); } /** Utility method for deleting all the elements from a given content URI. * You have to provide the name of the primary key column. * * @param ctx the context whose content resolver to use to lookup the URI * @param contentUri the content URI to delete all the items from * @param idColumn the column of the primary key for the URI */ private static void deleteContentUri(Context ctx, Uri contentUri, String idColumn) { ctx.getContentResolver().delete(contentUri, null, null); } /** Deletes all stored user content from the database including: * <ul> * <li>Procedures</li> * <li>SavedProcedures</li> * <li>Images</li> * <li>Sounds</li> * <li>Notifications</li> * </ul> * * @param ctx the Context where the data is stored */ public static void clearDatabase(Context ctx) { /* deleteContentUri(ctx, Procedures.CONTENT_URI, Procedures.Contract._ID); deleteContentUri(ctx, Encounters.CONTENT_URI, Encounters.Contract._ID); deleteContentUri(ctx, ImageSQLFormat.CONTENT_URI, ImageSQLFormat._ID); deleteContentUri(ctx, SoundSQLFormat.CONTENT_URI, SoundSQLFormat._ID); deleteContentUri(ctx, Notifications.CONTENT_URI, Notifications.Contract._ID); deleteContentUri(ctx, BinarySQLFormat.CONTENT_URI, BinarySQLFormat._ID); if (SanaDB.DATABASE_VERSION > 4) deleteContentUri(ctx, Observations.CONTENT_URI, Observations.Contract._ID); */ } /** Removes all stored patient information * * @param ctx the Context where the data is stored */ public static void clearPatientData(Context ctx) { deleteContentUri(ctx, Patients.CONTENT_URI, Patients.Contract._ID); } /** Inserts a new procedure into the data store * * @param ctx the Context where the data is stored * @param id the raw resource id */ private static void insertProcedure(Context ctx, int id) { String title = SanaUtil.randomString("Procedure ", 10); String author = ""; String guid = ""; String version = "1.0"; String xmlFullProcedure; try { InputStream rs = ctx.getResources().openRawResource(id); byte[] data = new byte[rs.available()]; rs.read(data); xmlFullProcedure = new String(data); Procedure p = Procedure.fromXMLString(xmlFullProcedure); title = p.getTitle(); author = p.getAuthor(); guid = p.getGuid(); version = p.getVersion(); ContentValues cv = new ContentValues(); cv.put(Procedures.Contract.TITLE, title); cv.put(Procedures.Contract.AUTHOR, author); cv.put(Procedures.Contract.UUID, guid); cv.put(Procedures.Contract.VERSION, version); cv.put(Procedures.Contract.PROCEDURE, xmlFullProcedure); if (searchDuplicateTitleAuthor(ctx, title, author)){ Log.d(TAG, "Duplicate found!"); ctx.getContentResolver().update(Procedures.CONTENT_URI, cv, "(title LIKE\"" + title + "\")", null); }else ctx.getContentResolver().insert(Procedures.CONTENT_URI, cv); } catch (Exception e) { Log.e(TAG, "Couldn't add procedure id=" + id + ", title = " + title + ", to db. Exception : " + e.toString()); e.printStackTrace(); } } /** Code to insert procedure into database is a duplicate with * insertProcedure this just takes the location from the sd card instead of * an id from the resources. * * @throws IOException * @throws ProcedureParseException * @throws SAXException * @throws ParserConfigurationException */ public static Integer insertProcedureFromSd(final Context ctx, String location) throws IOException, ParserConfigurationException, SAXException, ProcedureParseException { String title = SanaUtil.randomString("Procedure ", 10); String author = ""; String guid = ""; String version = "1.0"; String xmlFullProcedure; Log.v(TAG, location); FileInputStream rs = new FileInputStream(location); byte[] data = new byte[rs.available()]; rs.read(data); xmlFullProcedure = new String(data); Procedure p = Procedure.fromXMLString(xmlFullProcedure); title = p.getTitle(); author = p.getAuthor(); guid = p.getGuid(); version = p.getVersion(); final ContentValues cv = new ContentValues(); cv.put(Procedures.Contract.TITLE, title); cv.put(Procedures.Contract.AUTHOR, author); cv.put(Procedures.Contract.UUID, guid); cv.put(Procedures.Contract.VERSION, version); cv.put(Procedures.Contract.PROCEDURE, xmlFullProcedure); if (searchDuplicateTitleAuthor(ctx, title, author)) { Log.i(TAG, "Duplicate found! Updating..."); // TODO Versioning ctx.getContentResolver().update(p.getInstanceUri(), cv, "(title LIKE\"" + title + "\")", null); Log.i(TAG, "Updated"); return 0; } else { Log.i(TAG, "Inserting record."); ctx.getContentResolver().insert( Procedures.CONTENT_URI, cv); } Log.i(TAG, "Acquired procedure record from local cache."); return 0; } private static boolean searchDuplicateTitleAuthor(Context ctx, String title, String author) { Cursor cursor = null; try { cursor = ctx.getContentResolver().query( Procedures.CONTENT_URI, PROJECTION, "(title LIKE\"" + title + "\")", null, null); if (cursor.getCount() > 0) { return true; } } catch (Exception e) { } finally { if (cursor != null) cursor.close(); } return false; } /** Loading Sana with XML-described procedures is currently hard-coded. New * files can be added or removed here. */ public static void loadDefaultDatabase(Context ctx) { /* * insertProcedure(ctx, R.raw.bronchitis); insertProcedure(ctx, * R.raw.cervicalcancer); insertProcedure(ctx, R.raw.surgery_demo); * insertProcedure(ctx, R.raw.tbcontact); insertProcedure(ctx, * R.raw.multiupload_test); * insertProcedure(ctx, R.raw.upload_test); insertProcedure(ctx, * R.raw.hiv); insertProcedure(ctx, R.raw.cervicalcancer); * insertProcedure(ctx, R.raw.prenatal); insertProcedure(ctx, * R.raw.surgery); insertProcedure(ctx, R.raw.derma); * insertProcedure(ctx, R.raw.teleradiology); insertProcedure(ctx, * R.raw.ophthalmology); insertProcedure(ctx, R.raw.tbcontact2); * insertProcedure(ctx, R.raw.tbpatient); insertProcedure(ctx, * R.raw.oral_cancer); insertProcedure(ctx, R.raw.cvd_protocol); insertProcedure(ctx, R.raw.api_test); insertProcedure(ctx, R.raw.ssi_two_site); insertProcedure(ctx, R.raw.audio_upload_test); */ insertProcedure(ctx, R.raw.demonstration); /* insertProcedure(ctx, R.raw.chain_test1); insertProcedure(ctx, R.raw.chain_test2); insertProcedure(ctx, R.raw.api_test_entry); insertProcedure(ctx, R.raw.api_test_select); */ /* Haiti procedures */ //insertProcedure(ctx, R.raw.ssi); } /** Returns true if the phone has telphony or wifi service * * @param c - The current context * @return true if Android has either a wifi or cellular connection active */ public static boolean checkConnection(Context c) { try { TelephonyManager telMan = (TelephonyManager) c.getSystemService( Context.TELEPHONY_SERVICE); WifiManager wifiMan = (WifiManager) c.getSystemService( Context.WIFI_SERVICE); if (telMan != null && wifiMan != null) { int dataState = telMan.getDataState(); if (dataState == TelephonyManager.DATA_CONNECTED || (wifiMan.isWifiEnabled() && wifiMan.pingSupplicant())) return true; } return false; } catch (Exception e) { Log.e(TAG, "Exception in checkConnection(): " + e.toString()); return false; } } /** Utility for creating an returning a dialog with no click listener * * @param c The Context the dialog will be created in * @param alertMessage The dialog text * @return a new AlertDialog with no listener */ public static AlertDialog createAlertMessage(Context c, String alertMessage) { return createAlertMessage(c, alertMessage, null); } /** Utility for creating an returning a dialog with a listener for receiving * click value. * * @param c The Context the dialog will be created in * @param alertMessage The dialog text * @param listener A listener for receiving click events, may be <b>null</b> * @return a new AlertDialog with a specified listener */ public static AlertDialog createAlertMessage(Context c, String alertMessage, DialogInterface.OnClickListener listener) { Locales.updateLocale(c, c.getString(R.string.force_locale)); AlertDialog.Builder builder = new AlertDialog.Builder(c); builder.setMessage(alertMessage).setCancelable(false) .setPositiveButton( c.getResources().getString(R.string.general_ok), listener); AlertDialog alert = builder.create(); alert.show(); return alert; } /** Format a list of primary keys into a SQLite-formatted list of ids. Ex * 1,2,3 is formatted as (1,2,3) */ public static String formatPrimaryKeyList(List<?> idList) { StringBuilder sb = new StringBuilder("("); Iterator<?> it = idList.iterator(); while (it.hasNext()) { sb.append(String.valueOf(it.next())); if (it.hasNext()) { sb.append(","); } } sb.append(")"); return sb.toString(); } /** Convenience wrapper around Log.d to print a debug string as: * <code>onActivityResult: requestCode = <b>value</b>, resultCode = <b>value</b></code> * * @param tag THe calling classes tag * @param requestCode the request code used when launching the Activity * @param resultCode the result code returned by the Activity */ public static void logActivityResult(String tag, int requestCode, int resultCode) { Log.d(tag, "onActivityResult: requestCode = " + requestCode + ", resultCode = " + resultCode); } public static final boolean exportDatabase(Context ctx, String dbName) throws IOException{ boolean result = false; File db = ctx.getDatabasePath(dbName); File out = new File(Environment.getExternalStorageDirectory(), dbName); InputStream is = null; OutputStream os = null; try { is = new BufferedInputStream(new FileInputStream(db)); os = new BufferedOutputStream(new FileOutputStream(out)); byte[] buffer = new byte[1024]; while(is.read(buffer) > 0){ os.write(buffer); } result = true; } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if(is != null) is.close(); if(os != null) os.close(); } return result; } }