package org.sana.android.activity;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.text.TextUtils;
import android.widget.Toast;
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.app.State.Keys;
import org.sana.android.content.DispatchResponseReceiver;
import org.sana.android.content.Intents;
import org.sana.android.content.Uris;
import org.sana.android.fragment.AuthenticationDialogFragment.AuthenticationDialogListener;
import org.sana.android.util.Logf;
import org.sana.android.util.UriUtil;
import org.sana.net.Response;
/**
* Base class that contains basic functionalities and behaviors that all
* activities should do.
* @author Sana Dev Team
*/
public abstract class BaseActivity extends FragmentActivity implements AuthenticationDialogListener{
public static final String TAG = BaseActivity.class.getSimpleName();
static
// Dialog for prompting the user that a long operation is being performed.
ProgressDialog mWaitDialog = null;
protected String mLocale = null;
protected boolean mForceLocale = false;
/**
* Finishes the calling activity and launches the activity contained in
* <code>intent</code>
* @param intent
*/
void switchActivity(Intent intent) {
startActivity(intent);
}
// Session related
public static final String INSTANCE_KEY = "instanceKey";
public static final String SESSION_KEY = "sessionKey";
private final AtomicBoolean mWaiting = new AtomicBoolean(false);
protected String mDialogString = null;
// instanceKey initialized to some random value for the instance;
private String mInstanceKey = UUID.randomUUID().toString();
// Authenticated session key default is null;
private String mSessionKey = null;
protected Uri mSubject = Uri.EMPTY;
protected Uri mEncounter = Uri.EMPTY;
protected Uri mProcedure = Uri.EMPTY;
protected Uri mObserver = Uri.EMPTY;
protected Uri mTask = Uri.EMPTY;
protected boolean mDebug = false;
protected boolean mRoot = false;
private boolean mUploadForeground = false;
// State keys
public static final String STATE_DIALOG = "__dialog";
public static final String STATE_ROLE = "__role";
protected BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
handleBroadcast(intent);
}
};
/**
* Returns the value of the instance key which is created when the object is
* instantiated.
* @return
*/
protected String getInstanceKey(){
return mInstanceKey;
}
/**
* Returns the value of the session key. Warning: any key returned must be
* authenticated with the session service.
* @return
*/
protected String getSessionKey(){
return mSessionKey;
}
/**
* Sets the value of the session key. Warning: this method does not make
* any atempt to validate whether the session is authenticated.
* @param sessionKey
*/
protected void setSessionKey(String sessionKey){
mSessionKey = sessionKey;
}
/**
* Writes the state fields for this component to a bundle.
* Currently this writes the following from the Bundle
* <ul>
* <li>instance key</li>
* <li>session key</li>
* <li>current encounter</li>
* <li>current subject</li>
* <li>current observer</li>
* <li>current procedure</li>
* </ul>
* @param outState
*/
protected void onSaveAppState(Bundle outState){
outState.putString(Keys.INSTANCE_KEY, mInstanceKey);
outState.putString(Keys.SESSION_KEY, mSessionKey);
outState.putParcelable(Intents.EXTRA_ENCOUNTER, mEncounter);
outState.putParcelable(Intents.EXTRA_SUBJECT, mSubject);
outState.putParcelable(Intents.EXTRA_PROCEDURE, mProcedure);
outState.putParcelable(Intents.EXTRA_OBSERVER, mObserver);
outState.putParcelable(Intents.EXTRA_TASK, mTask);
outState.putBoolean(STATE_ROLE, mRoot);
}
/**
* Writes the state fields for this component to an Intent as Extras.
* Currently this writes the following from the Intent.
* <ul>
* <li>instance key</li>
* <li>session key</li>
* <li>current encounter</li>
* <li>current subject</li>
* <li>current observer</li>
* <li>current procedure</li>
* </ul>
* @param outState
*/
protected void onSaveAppState(Intent outState){
outState.putExtra(Keys.INSTANCE_KEY, mInstanceKey);
outState.putExtra(Keys.SESSION_KEY, mSessionKey);
outState.putExtra(Intents.EXTRA_ENCOUNTER, mEncounter);
outState.putExtra(Intents.EXTRA_SUBJECT, mSubject);
outState.putExtra(Intents.EXTRA_PROCEDURE, mProcedure);
outState.putExtra(Intents.EXTRA_OBSERVER, mObserver);
outState.putExtra(Intents.EXTRA_TASK, mTask);
outState.putExtra(STATE_ROLE, mRoot);
}
/**
* Sets the state fields for this component from an Intent.
* Currently this attempts to read the following extras from the
* Intent.
* <ul>
* <li>instance key</li>
* <li>session key</li>
* <li>current encounter</li>
* <li>current subject</li>
* <li>current observer</li>
* <li>current procedure</li>
* </ul>
*
* @param inState
*/
protected void onUpdateAppState(Intent inState){
String k = inState.getStringExtra(Keys.INSTANCE_KEY);
if(k != null)
mInstanceKey = new String(k);
k = inState.getStringExtra(Keys.SESSION_KEY);
if(k!=null)
mSessionKey = new String(k);
Uri temp = inState.getParcelableExtra(Intents.EXTRA_ENCOUNTER);
if(temp != null)
mEncounter = UriUtil.copyInstance(temp);
temp = inState.getParcelableExtra(Intents.EXTRA_SUBJECT);
if(temp != null)
mSubject = UriUtil.copyInstance(temp);
temp = inState.getParcelableExtra(Intents.EXTRA_PROCEDURE);
if(temp != null)
mProcedure = UriUtil.copyInstance(temp);
temp = inState.getParcelableExtra(Intents.EXTRA_OBSERVER);
if(temp != null)
mObserver = UriUtil.copyInstance(temp);
temp = inState.getParcelableExtra(Intents.EXTRA_TASK);
if(temp != null)
mTask = UriUtil.copyInstance(temp);
mRoot = inState.getBooleanExtra(STATE_ROLE, false);
}
/**
* Sets the state fields for this component from a bundle.
* Currently this attempts to read the following from the Bundle
* <ul>
* <li>instance key</li>
* <li>session key</li>
* <li>current encounter</li>
* <li>current subject</li>
* <li>current observer</li>
* <li>current procedure</li>
* </ul>
*
* @param inState
*/
protected void onUpdateAppState(Bundle inState){
String k = inState.getString(Keys.INSTANCE_KEY);
if(k!=null)
mInstanceKey = new String(k);
k = inState.getString(Keys.SESSION_KEY);
if(k!=null)
mSessionKey = new String(k);
Uri temp = inState.getParcelable(Intents.EXTRA_ENCOUNTER);
if(temp != null)
mEncounter = UriUtil.copyInstance(temp);
temp = inState.getParcelable(Intents.EXTRA_SUBJECT);
if(temp != null)
mSubject = UriUtil.copyInstance(temp);
temp = inState.getParcelable(Intents.EXTRA_PROCEDURE);
if(temp != null)
mProcedure = UriUtil.copyInstance(temp);
temp = inState.getParcelable(Intents.EXTRA_OBSERVER);
if(temp != null)
mObserver = UriUtil.copyInstance(temp);
temp = inState.getParcelable(Intents.EXTRA_TASK);
if(temp != null)
mTask = UriUtil.copyInstance(temp);
mRoot = inState.getBoolean(STATE_ROLE);
}
protected void onClearAppState(){
mWaiting.set(false);
mDialogString = null;
mInstanceKey = UUID.randomUUID().toString();
mSessionKey = null;
mSubject = Uri.EMPTY;
mEncounter = Uri.EMPTY;
mProcedure = Uri.EMPTY;
mObserver = Uri.EMPTY;
mTask = Uri.EMPTY;
mRoot = false;
}
protected final void setCurrentCredentials(String username, String password){
Editor editor = PreferenceManager
.getDefaultSharedPreferences(this).edit();
editor.putString(
Constants.PREFERENCE_EMR_USERNAME, username);
editor.putString(
Constants.PREFERENCE_EMR_PASSWORD, password);
editor.commit();
}
protected final void clearCredentials(){
setCurrentCredentials("NULL","NULL");
}
/**
* Writes the app state fields as extras to an Intent. Activities
* will still need to call setResult(RESULT_OK, data) as well as
* write any other data they wish to the data Intent.
*
* @param data
*/
protected void setResultAppData(Intent data){
onSaveAppState(data);
}
public Bundle getState(){
Bundle state = new Bundle();
onSaveAppState(state);
return state;
}
/*
* (non-Javadoc)
* @see com.actionbarsherlock.app.SherlockActivity#onSaveInstanceState(android.os.Bundle)
*/
@Override
protected void onSaveInstanceState(Bundle savedInstanceState){
super.onSaveInstanceState(savedInstanceState);
onSaveAppState(savedInstanceState);
//onSaveDialog(savedInstanceState);
}
/*
* (non-Javadoc)
* @see com.actionbarsherlock.app.SherlockActivity#onRestoreInstanceState(android.os.Bundle)
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState){
super.onRestoreInstanceState(savedInstanceState);
onUpdateAppState(savedInstanceState);
//onRestoreDialog(savedInstanceState);
}
/*
* (non-Javadoc)
* @see android.app.Activity#onCreate(android.os.Bundle)
*/
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Intent intent = getIntent();
// get the fields from the launch intent extras
if(intent != null)
onUpdateAppState(intent);
// assume savedInstanceState is newer
if(savedInstanceState != null)
onUpdateAppState(savedInstanceState);
mLocale = getString(R.string.force_locale);
mForceLocale = !TextUtils.isEmpty(mLocale);
Locales.updateLocale(this, mLocale);
mUploadForeground = this.getResources().getBoolean(R.bool.cfg_upload_foreground);
}
/**
* Displays a progress dialog fragment with the provided message.
* @param message
*/
void showProgressDialogFragment(String message) {
Log.i(TAG,"showProgressDialogFragment");
if (mWaitDialog != null && mWaitDialog.isShowing()) {
hideProgressDialogFragment();
}
// No need to create dialog if this is finishing
if(isFinishing())
return;
mDialogString = message;
mWaitDialog = new ProgressDialog(this);
mWaitDialog.setMessage(mDialogString);
mWaitDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mWaitDialog.show();
mWaiting.set(true);
}
/**
* Hides the progress dialog if it is shown.
*/
void hideProgressDialogFragment() {
Log.i(TAG,"hideProgressDialogFragment");
mWaiting.set(false);
if (mWaitDialog == null) {
return;
}
// dismiss if finishing
try{
if(isFinishing()){
mWaitDialog.dismiss();
//cancelProgressDialogFragment();
} else {
mWaitDialog.dismiss();
}
} catch (Exception e){
e.printStackTrace();
}
}
final void cancelProgressDialogFragment(){
Log.i(TAG,"cancelProgressDialogFragment");
mWaiting.set(false);
mDialogString = null;
try{
if(mWaitDialog != null)// && mWaitDialog.isShowing())
mWaitDialog.dismiss();
} catch (Exception e){
e.printStackTrace();
}
}
protected final boolean showProgressForeground(){
return mUploadForeground;
}
public void setData(Uri uri){
int code = Uris.getTypeDescriptor(uri);
setData(code, uri);
}
public void setData(int code, Uri uri){
switch(code){
case Uris.ENCOUNTER_DIR:
case Uris.ENCOUNTER_ITEM:
case Uris.ENCOUNTER_UUID:
mEncounter = uri;
break;
case Uris.OBSERVER_DIR:
case Uris.OBSERVER_ITEM:
case Uris.OBSERVER_UUID:
mObserver = uri;
break;
case Uris.PROCEDURE_DIR:
case Uris.PROCEDURE_ITEM:
case Uris.PROCEDURE_UUID:
mProcedure = uri;
break;
case Uris.SUBJECT_DIR:
case Uris.SUBJECT_ITEM:
case Uris.SUBJECT_UUID:
mSubject = uri;
break;
case Uris.ENCOUNTER_TASK_DIR:
case Uris.ENCOUNTER_TASK_ITEM:
case Uris.ENCOUNTER_TASK_UUID:
case Uris.OBSERVATION_TASK_DIR:
case Uris.OBSERVATION_TASK_ITEM:
case Uris.OBSERVATION_TASK_UUID:
mTask = uri;
break;
default:
break;
}
}
protected void onPause(){
super.onPause();
if(isFinishing())
cancelProgressDialogFragment();
else
hideProgressDialogFragment();
}
@Override
protected void onResume() {
super.onResume();
if(mWaiting.get() && mDialogString != null)
showProgressDialogFragment(mDialogString);
// simple way to set the locale
// Should probably be replaced with a change listener
mLocale = Preferences.getString(this,getString(R.string.setting_locale), "en");
}
/* (non-Javadoc)
* @see org.sana.android.fragment.AuthenticationDialogFragment.AuthenticationDialogListener#onDialogPositiveClick(android.support.v4.app.DialogFragment)
*/
@Override
public void onDialogPositiveClick(DialogFragment dialog) {
throw new UnsupportedOperationException();
}
/* (non-Javadoc)
* @see org.sana.android.fragment.AuthenticationDialogFragment.AuthenticationDialogListener#onDialogNegativeClick(android.support.v4.app.DialogFragment)
*/
@Override
public void onDialogNegativeClick(DialogFragment dialog) {
throw new UnsupportedOperationException();
}
protected void dump(){
Logf.D(this.getComponentName().getShortClassName(),"dump()", String.format("{ 'encounter': '%s',"
+" 'observer': '%s', 'subject': '%s', 'procedure': '%s', 'task': '%s' }",
mEncounter, mObserver, mSubject, mProcedure, mTask));
}
protected void dump(String method){
Logf.D(this.getComponentName().getShortClassName(),method+".dump()", String.format("{ 'encounter': '%s',"
+" 'observer': '%s', 'subject': '%s', 'procedure': '%s', 'task': '%s' }",
mEncounter, mObserver, mSubject, mProcedure, mTask));
}
protected final void makeText(String text){
makeText(text, Toast.LENGTH_LONG);
}
protected final void makeText(String text, int duration){
Toast.makeText(this, text, duration).show();
}
protected final void makeText(int resId){
Locales.updateLocale(this, getString(R.string.force_locale));
makeText(resId, Toast.LENGTH_SHORT);
}
protected final void makeText(int resId, int duration){
makeText(getString(resId), duration);
}
public int getVersion() {
int v = 0;
try {
v = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;
} catch (NameNotFoundException e) {
}
return v;
}
public String getBuildString() {
String version = "";
String versionFormat = "%s-%s";
try {
PackageInfo pi = getPackageManager().getPackageInfo(
getPackageName(), 0);
ApplicationInfo ai = getPackageManager().getApplicationInfo(
getPackageName(), PackageManager.GET_META_DATA);
Bundle metadata = ai.metaData;
String local = metadata.getString("local_build");
Log.i(TAG, "Version info: name=" + pi.versionName +", code=" +
pi.versionCode);
version = (!TextUtils.isEmpty(local))?
String.format(versionFormat, pi.versionName, local): pi.versionName;
} catch (Exception e) {
}
Log.d(TAG, "...version string=" +version);
// Temporary work around.
//version = getString(R.string.display_version);
return version;
}
protected void handleBroadcast(Intent intent){
Log.i(TAG, "handleBroadcast(Intent)");
// Extract data included in the Intent
}
static final IntentFilter filter = new IntentFilter();
static{
filter.addAction(DispatchResponseReceiver.BROADCAST_RESPONSE);
filter.addDataScheme("content");
filter.addDataAuthority("org.sana.provider", null);
}
public String getStringLocalized(int resId){
if(!TextUtils.isEmpty(mLocale)) {
mLocale = Preferences.getString(this,
getString(R.string.setting_locale), "en");
}
Locales.updateLocale(this, mLocale);
return super.getString(resId);
}
public void setContentViewLocalized(int resId){
if(mForceLocale)
Locales.updateLocale(this, mLocale);
super.setContentView(resId);
}
protected void onSaveDialog(Bundle savedInstanceState){
Bundle dialog = new Bundle();
dialog.putBoolean("mWaiting",mWaiting.get());
dialog.putString("mDialogString",mDialogString);
savedInstanceState.putBundle("mDialog", dialog);
}
protected void onRestoreDialog(Bundle savedInstanceState){
Bundle dialog = savedInstanceState.getBundle("mDialog");
if(dialog != null){
mWaiting.set(dialog.getBoolean("mWaiting",false));
String msg = dialog.getString("mDialogString");
mDialogString = (!TextUtils.isEmpty(msg))? msg: null;
} else {
mWaiting.set(false);
mDialogString = null;
}
}
}