package org.commcare.dalvik.activities;
import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Vector;
import org.commcare.android.database.SqlStorage;
import org.commcare.android.database.user.models.FormRecord;
import org.commcare.android.database.user.models.SessionStateDescriptor;
import org.commcare.android.database.user.models.User;
import org.commcare.android.framework.BreadcrumbBarFragment;
import org.commcare.android.framework.CommCareActivity;
import org.commcare.android.javarosa.AndroidLogger;
import org.commcare.android.logic.GlobalConstants;
import org.commcare.android.models.AndroidSessionWrapper;
import org.commcare.android.models.logic.FormRecordProcessor;
import org.commcare.android.models.notifications.NotificationMessageFactory;
import org.commcare.android.models.notifications.NotificationMessageFactory.StockMessages;
import org.commcare.android.tasks.DataPullTask;
import org.commcare.android.tasks.DumpTask;
import org.commcare.android.tasks.ExceptionReportTask;
import org.commcare.android.tasks.FormRecordCleanupTask;
import org.commcare.android.tasks.ProcessAndSendTask;
import org.commcare.android.tasks.SendTask;
import org.commcare.android.tasks.WipeTask;
import org.commcare.android.util.AndroidCommCarePlatform;
import org.commcare.android.util.CommCareInstanceInitializer;
import org.commcare.android.util.FormUploadUtil;
import org.commcare.android.util.SessionUnavailableException;
import org.commcare.android.util.StorageUtils;
import org.commcare.android.view.TextImageAudioView;
import org.commcare.dalvik.R;
import org.commcare.dalvik.application.AndroidShortcuts;
import org.commcare.dalvik.application.CommCareApplication;
import org.commcare.dalvik.dialogs.CustomProgressDialog;
import org.commcare.dalvik.odk.provider.FormsProviderAPI;
import org.commcare.dalvik.odk.provider.InstanceProviderAPI;
import org.commcare.dalvik.preferences.CommCarePreferences;
import org.commcare.suite.model.Profile;
import org.commcare.suite.model.SessionDatum;
import org.commcare.suite.model.Text;
import org.commcare.util.CommCareSession;
import org.commcare.util.SessionFrame;
import org.javarosa.core.model.condition.EvaluationContext;
import org.javarosa.core.services.Logger;
import org.javarosa.core.services.locale.Localization;
import org.javarosa.core.services.storage.StorageFullException;
import org.javarosa.core.util.NoLocalizedTextException;
import org.javarosa.xpath.XPathException;
import org.javarosa.xpath.XPathParseTool;
import org.javarosa.xpath.expr.XPathExpression;
import org.javarosa.xpath.expr.XPathFuncExpr;
import org.javarosa.xpath.parser.XPathSyntaxException;
import org.odk.collect.android.tasks.FormLoaderTask;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Typeface;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.util.Base64;
import android.util.Pair;
import android.view.ContextThemeWrapper;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class CommCareHomeActivity extends CommCareActivity<CommCareHomeActivity> {
public static final int LOGIN_USER = 0;
public static final int GET_COMMAND = 1;
public static final int GET_CASE = 2;
public static final int MODEL_RESULT = 4;
public static final int INIT_APP = 8;
public static final int GET_INCOMPLETE_FORM = 16;
public static final int GET_REFERRAL = 32;
public static final int UPGRADE_APP = 64;
public static final int REPORT_PROBLEM_ACTIVITY = 128;
public static final int MISSING_MEDIA_ACTIVITY=256;
public static final int DUMP_FORMS_ACTIVITY=512;
public static final int WIFI_DIRECT_ACTIVITY=1024;
public static final int CONNECTION_DIAGNOSTIC_ACTIVITY=2048;
public static final int PREFERENCES_ACTIVITY=4096;
public static final int USE_OLD_DIALOG = 1;
public static final int DIALOG_CORRUPTED = 4;
public static final int DIALOG_NO_STORAGE = 8;
private static final int MENU_PREFERENCES = Menu.FIRST;
private static final int MENU_UPDATE = Menu.FIRST +1;
private static final int MENU_CALL_LOG = Menu.FIRST +2;
private static final int MENU_REPORT_PROBLEM = Menu.FIRST + 3;
private static final int MENU_VALIDATE_MEDIA = Menu.FIRST + 4;
private static final int MENU_DUMP_FORMS = Menu.FIRST + 5;
private static final int MENU_WIFI_DIRECT = Menu.FIRST + 6;
private static final int MENU_CONNECTION_DIAGNOSTIC = Menu.FIRST + 7;
private static final int MENU_SAVED_FORMS = Menu.FIRST + 8;
/**
* Restart is a special CommCare return code which means that the session was invalidated in the
* calling activity and that the current session should be resynced
*/
public static final int RESULT_RESTART = 3;
public static int unsentFormNumberLimit;
public static int unsentFormTimeLimit;
public final static String UNSENT_FORM_NUMBER_KEY = "unsent-number-limit";
public final static String UNSENT_FORM_TIME_KEY = "unsent-time-limit";
public static final String SESSION_REQUEST = "ccodk_session_request";
public static final String AIRPLANE_MODE_CATEGORY = "airplane-mode";
boolean wasExternal = false;
View homeScreen;
private AndroidCommCarePlatform platform;
AlertDialog mAskOldDialog;
AlertDialog mAttemptFixDialog;
Button startButton;
Button logoutButton;
Button viewIncomplete;
Button syncButton;
Button viewOldForms;
/*
* (non-Javadoc)
* @see org.commcare.android.framework.CommCareActivity#onCreate(android.os.Bundle)
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//This is a workaround required by Android Bug #2373, which is that Apps are launched from the
//Google Play store and from the App launcher with different intent flags than everywhere else
//in Android, which ruins the back stack and prevents the app from launching a high affinity
//task.
if (!isTaskRoot()) {
Intent intent = getIntent();
String action = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && action != null && action.equals(Intent.ACTION_MAIN)) {
finish();
return;
}
}
if(savedInstanceState != null) {
wasExternal = savedInstanceState.getBoolean("was_external");
}
setContentView(R.layout.mainnew);
configUi();
}
private void configUi() {
TextView version = (TextView)findViewById(R.id.str_version);
version.setText(CommCareApplication._().getCurrentVersionString());
// enter data button. expects a result.
startButton = (Button) findViewById(R.id.home_start);
startButton.setText(Localization.get("home.start"));
startButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent i = new Intent(getApplicationContext(), MenuList.class);
startActivityForResult(i, GET_COMMAND);
}
});
// enter data button. expects a result.
viewIncomplete = (Button) findViewById(R.id.home_forms_incomplete);
viewIncomplete.setText(Localization.get("home.forms.incomplete"));
viewIncomplete.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
goToFormArchive(true);
}
});
logoutButton = (Button) findViewById(R.id.home_logout);
logoutButton.setText(Localization.get("home.logout"));
logoutButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
CommCareApplication._().logout();
returnToLogin(null);
}
});
TextView formGroupLabel = (TextView) findViewById(R.id.home_formrecords_label);
formGroupLabel.setText(Localization.get("home.forms"));
viewOldForms = (Button) findViewById(R.id.home_forms_old);
viewOldForms.setText(Localization.get("home.forms.saved"));
viewOldForms.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
goToFormArchive(false);
}
});
syncButton = (Button) findViewById(R.id.home_sync);
syncButton.setText(Localization.get("home.sync"));
syncButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (!isOnline()) {
if (isAirplaneModeOn()) {
displayMessage(Localization.get("notification.sync.airplane.action"),true,true);
CommCareApplication._().reportNotificationMessage(NotificationMessageFactory.message(StockMessages.Sync_AirplaneMode, AIRPLANE_MODE_CATEGORY));
}
else {
displayMessage(Localization.get("notification.sync.connections.action"),true,true);
CommCareApplication._().reportNotificationMessage(NotificationMessageFactory.message(StockMessages.Sync_NoConnections, AIRPLANE_MODE_CATEGORY));
}
return;
}
CommCareApplication._().clearNotifications(AIRPLANE_MODE_CATEGORY);
boolean formsToSend = checkAndStartUnsentTask(true);
if(!formsToSend) {
//No unsent forms, just sync
syncData(false);
}
}
});
}
private boolean isOnline() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
if (netInfo != null && netInfo.isConnectedOrConnecting()) {
return true;
}
return false;
}
private void goToFormArchive(boolean incomplete) {
goToFormArchive(incomplete, null);
}
private void goToFormArchive(boolean incomplete, FormRecord record) {
Intent i = new Intent(getApplicationContext(), FormRecordListActivity.class);
if(incomplete) {
i.putExtra(FormRecord.META_STATUS, FormRecord.STATUS_INCOMPLETE);
}
if(record != null) {
i.putExtra(FormRecordListActivity.KEY_INITIAL_RECORD_ID, record.getID());
}
startActivityForResult(i, GET_INCOMPLETE_FORM);
}
private void syncData(boolean formsToSend) {
User u = CommCareApplication._().getSession().getLoggedInUser();
if(User.TYPE_DEMO.equals(u.getUserType())) {
//Remind the user that there's no syncing in demo mode.0
if(formsToSend){
displayMessage(Localization.get("main.sync.demo.has.forms"), true, true);
}
else{
displayMessage(Localization.get("main.sync.demo.no.forms"), true, true);
}
return;
}
SharedPreferences prefs = CommCareApplication._().getCurrentApp().getAppPreferences();
DataPullTask<CommCareHomeActivity> mDataPullTask = new DataPullTask<CommCareHomeActivity>(u.getUsername(), u.getCachedPwd(), prefs.getString("ota-restore-url",this.getString(R.string.ota_restore_url)), "", this) {
/*
* (non-Javadoc)
* @see org.commcare.android.tasks.templates.CommCareTask#deliverResult(java.lang.Object, java.lang.Object)
*/
@Override
protected void deliverResult(CommCareHomeActivity receiver, Integer result) {
try {
receiver.refreshView();
} catch(SessionUnavailableException sue) {
receiver.returnToLogin();
}
//TODO: SHARES _A LOT_ with login activity. Unify into service
switch(result) {
case DataPullTask.AUTH_FAILED:
receiver.displayMessage(Localization.get("sync.fail.auth.loggedin"), true);
break;
case DataPullTask.BAD_DATA:
receiver.displayMessage(Localization.get("sync.fail.bad.data"), true);
break;
case DataPullTask.DOWNLOAD_SUCCESS:
receiver.displayMessage(Localization.get("sync.success.synced"));
break;
case DataPullTask.SERVER_ERROR:
receiver.displayMessage(Localization.get("sync.fail.server.error"));
break;
case DataPullTask.UNREACHABLE_HOST:
receiver.displayMessage(Localization.get("sync.fail.bad.network"), true);
break;
case DataPullTask.CONNECTION_TIMEOUT:
receiver.displayMessage(Localization.get("sync.fail.timeout"), true);
break;
case DataPullTask.UNKNOWN_FAILURE:
receiver.displayMessage(Localization.get("sync.fail.unknown"), true);
break;
}
//TODO: What if the user info was updated?
}
/*
* (non-Javadoc)
* @see org.commcare.android.tasks.templates.CommCareTask#deliverUpdate(java.lang.Object, java.lang.Object[])
*/
@Override
protected void deliverUpdate(CommCareHomeActivity receiver, Integer... update) {
if(update[0] == DataPullTask.PROGRESS_STARTED) {
receiver.updateProgress(Localization.get("sync.progress.purge"), DataPullTask.DATA_PULL_TASK_ID);
} else if(update[0] == DataPullTask.PROGRESS_CLEANED) {
receiver.updateProgress(Localization.get("sync.progress.authing"), DataPullTask.DATA_PULL_TASK_ID);
} else if(update[0] == DataPullTask.PROGRESS_AUTHED) {
receiver.updateProgress(Localization.get("sync.progress.downloading"), DataPullTask.DATA_PULL_TASK_ID);
}else if(update[0] == DataPullTask.PROGRESS_RECOVERY_NEEDED) {
receiver.updateProgress(Localization.get("sync.recover.needed"), DataPullTask.DATA_PULL_TASK_ID);
} else if(update[0] == DataPullTask.PROGRESS_RECOVERY_STARTED) {
receiver.updateProgress(Localization.get("sync.recover.started"), DataPullTask.DATA_PULL_TASK_ID);
}
}
/*
* (non-Javadoc)
* @see org.commcare.android.tasks.templates.CommCareTask#deliverError(java.lang.Object, java.lang.Exception)
*/
@Override
protected void deliverError(CommCareHomeActivity receiver,
Exception e) {
receiver.displayMessage(Localization.get("sync.fail.unknown"), true);
}
};
mDataPullTask.connect(this);
mDataPullTask.execute();
}
/*
* (non-Javadoc)
*
* @see android.app.Activity#onSaveInstanceState(android.os.Bundle)
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("was_external", wasExternal);
}
/*
* (non-Javadoc)
*
* @see android.app.Activity#onSaveInstanceState(android.os.Bundle)
*/
@Override
protected void onRestoreInstanceState(Bundle inState) {
super.onRestoreInstanceState(inState);
if(inState.containsKey("was_external")) {
wasExternal = inState.getBoolean("was_external");
}
}
/*
* (non-Javadoc)
* @see android.app.Activity#onActivityResult(int, int, android.content.Intent)
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if(resultCode == RESULT_RESTART) {
startNextFetch();
return;
}
try {
// if handling new return code (want to return to home screen) but a return at the end of your statement
switch(requestCode) {
case INIT_APP:
if(resultCode == RESULT_CANCELED) {
//quit somehow.
this.finish();
return;
} else if(resultCode == RESULT_OK) {
//CTS - Removed a call to initializing resources here. The engine takes care of that.
//We do, however, need to re-init this screen to include new translations
configUi();
return;
}
break;
case UPGRADE_APP:
if(resultCode == RESULT_CANCELED) {
//This might actually be bad, but try to go about your business
//The onResume() will take us to the screen
return;
} else if(resultCode == RESULT_OK) {
if(intent.getBooleanExtra(CommCareSetupActivity.KEY_REQUIRE_REFRESH, true)) {
Toast.makeText(this, Localization.get("update.success.refresh"), Toast.LENGTH_LONG).show();
CommCareApplication._().getSession().logout();
}
//set flag that we should autoupdate on next login
SharedPreferences preferences = CommCareApplication._().getCurrentApp().getAppPreferences();
preferences.edit().putBoolean(CommCarePreferences.AUTO_TRIGGER_UPDATE,true);
//The onResume() will take us to the screen
return;
}
break;
case PREFERENCES_ACTIVITY:
configUi();
return;
case MISSING_MEDIA_ACTIVITY:
if(resultCode == RESULT_CANCELED){
this.finish();
return;
}
else if(resultCode == RESULT_OK){
Toast.makeText(this, "Media Validated!", Toast.LENGTH_LONG).show();
return;
}
case DUMP_FORMS_ACTIVITY:
if(resultCode == RESULT_CANCELED){
return;
}
else if(resultCode == DumpTask.BULK_DUMP_ID){
int dumpedCount = intent.getIntExtra(CommCareFormDumpActivity.KEY_NUMBER_DUMPED, -1);
displayMessage(Localization.get("bulk.form.dump.success",new String[] {""+dumpedCount}), false, false);
refreshView();
return;
}
else if(resultCode == SendTask.BULK_SEND_ID){
int dumpedCount = intent.getIntExtra(CommCareFormDumpActivity.KEY_NUMBER_DUMPED, -1);
displayMessage(Localization.get("bulk.form.send.success",new String[] {""+dumpedCount}),false, true);
Toast.makeText(this, Localization.get("bulk.form.send.success",new String[] {""+dumpedCount}), Toast.LENGTH_LONG).show();
refreshView();
return;
}
case CONNECTION_DIAGNOSTIC_ACTIVITY:
return;
case WIFI_DIRECT_ACTIVITY:
if(resultCode == RESULT_CANCELED){
return;
}
else if(resultCode == SendTask.BULK_SEND_ID){
int dumpedCount = intent.getIntExtra(CommCareWiFiDirectActivity.KEY_NUMBER_DUMPED, -1);
displayMessage(Localization.get("bulk.form.send.success",new String[] {""+dumpedCount}),false, true);
Toast.makeText(this, "Forms successfully submitted.", Toast.LENGTH_LONG).show();
refreshView();
return;
} else if(resultCode == WipeTask.WIPE_TASK_ID){
int dumpedCount = intent.getIntExtra(CommCareWiFiDirectActivity.KEY_NUMBER_DUMPED, -1);
displayMessage(Localization.get("bulk.form.send.success",new String[] {""+dumpedCount}),false, true);
Toast.makeText(this, "Forms successfully submitted.", Toast.LENGTH_LONG).show();
refreshView();
return;
}
case REPORT_PROBLEM_ACTIVITY:
if(resultCode == RESULT_CANCELED) {
return;
}
else if(resultCode == RESULT_OK){
CommCareApplication._().notifyLogsPending();
refreshView();
return;
}
case LOGIN_USER:
if(resultCode == RESULT_CANCELED) {
//quit somehow.
this.finish();
return;
} else if(resultCode == RESULT_OK) {
if(intent.getBooleanExtra(LoginActivity.ALREADY_LOGGED_IN, false)) {
//If we were already logged in just roll with it.
//The onResume() will take us to the screen
} else {
refreshView();
checkAndStartUnsentTask(false);
if(isDemoUser()) {
showDemoModeWarning();
}
}
return;
}
break;
case GET_INCOMPLETE_FORM:
//TODO: We might need to load this from serialized state?
AndroidSessionWrapper currentState = CommCareApplication._().getCurrentSessionWrapper();
if(resultCode == RESULT_CANCELED) {
refreshView();
return;
}
else if(resultCode == RESULT_OK) {
int record = intent.getIntExtra("FORMRECORDS", -1);
if(record == -1) {
//Hm, what to do here?
break;
}
FormRecord r = CommCareApplication._().getUserStorage(FormRecord.class).read(record);
//Retrieve and load the appropriate ssd
SqlStorage<SessionStateDescriptor> ssdStorage = CommCareApplication._().getUserStorage(SessionStateDescriptor.class);
Vector<Integer> ssds = ssdStorage.getIDsForValue(SessionStateDescriptor.META_FORM_RECORD_ID, r.getID());
if(ssds.size() == 1) {
currentState.loadFromStateDescription(ssdStorage.read(ssds.firstElement()));
} else {
currentState.setFormRecordId(r.getID());
}
formEntry(platform.getFormContentUri(r.getFormNamespace()), r);
return;
}
break;
case GET_COMMAND:
//TODO: We might need to load this from serialized state?
currentState = CommCareApplication._().getCurrentSessionWrapper();
if(resultCode == RESULT_CANCELED) {
if(currentState.getSession().getCommand() == null) {
//Needed a command, and didn't already have one. Stepping back from
//an empty state, Go home!
currentState.reset();
refreshView();
return;
} else {
currentState.getSession().stepBack();
break;
}
} else if(resultCode == RESULT_OK) {
//Get our command, set it, and continue forward
String command = intent.getStringExtra(SessionFrame.STATE_COMMAND_ID);
currentState.getSession().setCommand(command);
break;
}
break;
case GET_CASE:
//TODO: We might need to load this from serialized state?
currentState = CommCareApplication._().getCurrentSessionWrapper();
if(resultCode == RESULT_CANCELED) {
currentState.getSession().stepBack();
break;
} else if(resultCode == RESULT_OK) {
currentState.getSession().setDatum(currentState.getSession().getNeededDatum().getDataId(), intent.getStringExtra(SessionFrame.STATE_DATUM_VAL));
if(intent.hasExtra(CallOutActivity.CALL_DURATION)) {
platform.setCallDuration(intent.getLongExtra(CallOutActivity.CALL_DURATION, 0));
}
break;
}
case MODEL_RESULT:
//TODO: We might need to load this from serialized state?
currentState = CommCareApplication._().getCurrentSessionWrapper();
if(resultCode == 1) {
//Exception in form entry!
if(intent.hasExtra("odk_exception")) {
Throwable ex = (Throwable)intent.getSerializableExtra("odk_exception");
ExceptionReportTask task = new ExceptionReportTask();
task.execute(ex);
} else {
RuntimeException ex = new RuntimeException("Unspecified exception from form entry engine");
ExceptionReportTask task = new ExceptionReportTask();
task.execute(ex);
}
//TODO: Notification?
Toast.makeText(this, Localization.get("form.entry.segfault"), Toast.LENGTH_LONG);
currentState.reset();
refreshView();
break;
}
//This is the state we were in when we _Started_ form entry
FormRecord current = currentState.getFormRecord();
//See if we were viewing an old form, in which case we don't want to change the historical record
//regardless of the exit code
//TODO: This should be the default unless we're in some "Uninit" or "incomplete" state
if(FormRecord.STATUS_COMPLETE.equals(current.getStatus()) || FormRecord.STATUS_SAVED.equals(current.getStatus()) || FormRecord.STATUS_UNSENT.equals(current.getStatus())) {
currentState.reset();
if(wasExternal) {
this.finish();
} else {
//Return to where we started
goToFormArchive(false, current);
}
return;
}
if(resultCode == RESULT_OK) {
Uri resultInstanceURI = intent.getData();
//TODO: encapsulate this pattern somewhere?
if(resultInstanceURI == null) {
Logger.log(AndroidLogger.TYPE_ERROR_WORKFLOW, "Form Entry Did not Return a Form");
CommCareApplication._().reportNotificationMessage(NotificationMessageFactory.message(StockMessages.FormEntry_Unretrievable));
Toast.makeText(this, "Error while trying to read the form! See the notification", Toast.LENGTH_LONG);
currentState.reset();
if(wasExternal) {
this.finish();
}
refreshView();
return;
}
Cursor c = getContentResolver().query(resultInstanceURI, null,null,null, null);
boolean complete = false;
try {
complete = currentState.beginRecordTransaction(resultInstanceURI, c);
} catch(IllegalArgumentException iae) {
iae.printStackTrace();
CommCareApplication._().reportNotificationMessage(NotificationMessageFactory.message(StockMessages.FormEntry_Unretrievable));
Toast.makeText(this, "Error while trying to read the form! See the notification", Toast.LENGTH_LONG);
//TODO: Fail more hardcore here? Wipe the form record and its ties?
Logger.log(AndroidLogger.TYPE_ERROR_WORKFLOW, "Unrecoverable error when trying to read form|" + iae.getMessage());
currentState.reset();
if(wasExternal) {
this.finish();
}
refreshView();
return;
} finally {
c.close();
}
//TODO: Move this logic into the process task?
try {
current = currentState.commitRecordTransaction();
} catch (Exception e) {
//Something went wrong with all of the connections which should exist. Tell
//the user,
CommCareApplication._().reportNotificationMessage(NotificationMessageFactory.message(StockMessages.FormEntry_Unretrievable));
Toast.makeText(this, "An error occurred: " + e.getMessage() + " and your data could not be saved.", Toast.LENGTH_LONG);
FormRecordCleanupTask.wipeRecord(this, currentState);
//Notify the server of this problem (since we aren't going to crash)
ExceptionReportTask ert = new ExceptionReportTask();
ert.execute(e);
currentState.reset();
if(wasExternal) {
this.finish();
}
refreshView();
return;
}
Logger.log(AndroidLogger.TYPE_FORM_ENTRY, "Form Entry Completed");
//The form is either ready for processing, or not, depending on how it was saved
if(complete) {
//Form record should now be up to date now and stored correctly.
//ctsims - App stack workflows require us to have processed _this_ specific form before
//we can move on, and that needs to be synchronous. We'll go ahead and try to process just
//this form before moving on. We'll catch any errors here and just eat them (since the
//task will also try the process and fail if it does.
if(FormRecord.STATUS_COMPLETE.equals(current.getStatus())) {
try {
new FormRecordProcessor(this).process(current);
} catch(Exception e) {
Logger.log(AndroidLogger.TYPE_ERROR_WORKFLOW, "Error processing form. Should be recaptured during async processing: " + e.getMessage());
}
}
//We're honoring in order submissions, now, so trigger a full submission
//cycle
checkAndStartUnsentTask(false);
refreshView();
if(wasExternal) {
this.finish();
}
//Before we can terminate the session, we need to know that the form has been processed
//in case there is state that depends on it.
if(!currentState.terminateSession()) {
//If we didn't find somewhere to go,
//we're gonna stay here
return;
}
//Otherwise, we want to keep proceeding in order
//to keep running the workflow
} else {
//Form record is now stored.
currentState.reset();
if(wasExternal) {
this.finish();
}
refreshView();
return;
}
} else {
Logger.log(AndroidLogger.TYPE_FORM_ENTRY, "Form Entry Cancelled");
//If the form was unstarted, we want to wipe the record.
if(current.getStatus() == FormRecord.STATUS_UNSTARTED) {
//Entry was cancelled.
FormRecordCleanupTask.wipeRecord(this, currentState);
}
if(wasExternal) {
this.finish();
currentState.reset();
return;
} else {
if(current.getStatus().equals(FormRecord.STATUS_INCOMPLETE)) {
//We should head back to the incomplete forms screen
currentState.reset();
goToFormArchive(true, current);
return;
} else {
//If we cancelled form entry from a normal menu entry
//we want to go back to where were were right before we started
//entering the form.
currentState.getSession().stepBack();
currentState.setFormRecordId(-1);
}
}
}
}
startNextFetch();
}
catch (SessionUnavailableException sue) {
//TODO: Cache current return, login, and try again
returnToLogin();
}
super.onActivityResult(requestCode, resultCode, intent);
}
private void showDemoModeWarning() {
//TODO: How do we style this to "light"?
AlertDialog demoModeWarning = new AlertDialog.Builder(new ContextThemeWrapper(this, android.R.style.Theme_Light)).setInverseBackgroundForced(true).create();
demoModeWarning.setTitle(Localization.get("demo.mode.warning.title"));
DialogInterface.OnClickListener demoModeWarningListener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int i) {
switch (i) {
case DialogInterface.BUTTON1:
//Nothing, dismiss
break;
}
}
};
demoModeWarning.setCancelable(false);
demoModeWarning.setButton(Localization.get("demo.mode.warning.dismiss"), demoModeWarningListener);
String path = null;
try {
path = Localization.get("demo.warning.filepath");
} catch(NoLocalizedTextException nlte) {
}
TextImageAudioView tiav = new TextImageAudioView(this);
tiav.setAVT(Localization.get("demo.mode.warning"), path, null);
demoModeWarning.setView(tiav);
demoModeWarning.show();
}
private void createErrorDialog(String errorMsg, AlertDialog.OnClickListener errorListener) {
AlertDialog mAlertDialog = new AlertDialog.Builder(this).create();
mAlertDialog.setIcon(android.R.drawable.ic_dialog_info);
mAlertDialog.setTitle(Localization.get("app.handled.error.title"));
mAlertDialog.setMessage(errorMsg);
mAlertDialog.setCancelable(false);
mAlertDialog.setButton(Localization.get("dialog.ok"), errorListener);
mAlertDialog.show();
}
private void startNextFetch() throws SessionUnavailableException {
//TODO: feels like this logic should... not be in a big disgusting ifghetti.
//Interface out the transitions, maybe?
final CommCareSession session = CommCareApplication._().getCurrentSession();
String needed = session.getNeededData();
String[] lastPopped = session.getPoppedStep();
if(needed == null) {
EvaluationContext ec = session.getEvaluationContext(new CommCareInstanceInitializer(session));
//See if we failed any of our asseertions
Text text = session.getCurrentEntry().getAssertions().getAssertionFailure(ec);
if(text != null) {
createErrorDialog(text.evaluate(ec), new DialogInterface.OnClickListener() {
/*
* (non-Javadoc)
* @see android.content.DialogInterface.OnClickListener#onClick(android.content.DialogInterface, int)
*/
@Override
public void onClick(DialogInterface dialog, int i) {
session.stepBack();
CommCareHomeActivity.this.startNextFetch();
}
});
return;
}
startFormEntry(CommCareApplication._().getCurrentSessionWrapper());
}
else if(needed == SessionFrame.STATE_COMMAND_ID) {
Intent i = new Intent(getApplicationContext(), MenuList.class);
i.putExtra(SessionFrame.STATE_COMMAND_ID, session.getCommand());
startActivityForResult(i, GET_COMMAND);
} else if(needed == SessionFrame.STATE_DATUM_VAL) {
Intent i = new Intent(getApplicationContext(), EntitySelectActivity.class);
i.putExtra(SessionFrame.STATE_COMMAND_ID, session.getCommand());
if(lastPopped != null && SessionFrame.STATE_DATUM_VAL.equals(lastPopped[0])) {
i.putExtra(EntitySelectActivity.EXTRA_ENTITY_KEY, lastPopped[2]);
}
startActivityForResult(i, GET_CASE);
} else if(needed == SessionFrame.STATE_DATUM_COMPUTED) {
//compute
SessionDatum datum = session.getNeededDatum();
XPathExpression form;
try {
form = XPathParseTool.parseXPath(datum.getValue());
} catch (XPathSyntaxException e) {
//TODO: What.
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
EvaluationContext ec = session.getEvaluationContext(new CommCareInstanceInitializer(session));
if(datum.getType() == SessionDatum.DATUM_TYPE_FORM) {
session.setXmlns(XPathFuncExpr.toString(form.eval(ec)));
session.setDatum("", "awful");
} else {
try {
session.setDatum(datum.getDataId(), XPathFuncExpr.toString(form.eval(ec)));
}
catch (XPathException e) {
displayException(e);
return;
}
}
startNextFetch();
}
if(lastPopped != null) {
//overridePendingTransition(R.anim.push_right_in, R.anim.push_right_out);
}
}
private void startFormEntry(AndroidSessionWrapper state) throws SessionUnavailableException{
try {
//If this is a new record (never saved before), which currently all should be
if(state.getFormRecordId() == -1) {
//If form management isn't enabled we can't have these old forms around anyway
if(CommCarePreferences.isIncompleteFormsEnabled()) {
//First, see if we've already started this form before
SessionStateDescriptor existing = state.searchForDuplicates();
//I'm not proud of the second clause, here. Basically, only ask if we should continue entry if the
//saved state actually involved selecting some data.
if(existing != null && existing.getSessionDescriptor().contains(SessionFrame.STATE_DATUM_VAL)) {
createAskUseOldDialog(state, existing);
return;
}
}
//Otherwise, generate a stub record and commit it
state.commitStub();
} else {
Logger.log("form-entry", "Somehow ended up starting form entry with old state?");
}
//We should now have a valid record for our state. Time to get to form entry.
FormRecord record = state.getFormRecord();
if(platform == null) {
platform = CommCareApplication._().getCurrentApp() == null ? null : CommCareApplication._().getCurrentApp().getCommCarePlatform();
}
//TODO: May need to pass session over manually
formEntry(platform.getFormContentUri(record.getFormNamespace()), record, CommCareActivity.getTitle(this, null));
} catch (StorageFullException e) {
throw new RuntimeException(e);
}
}
/*
* (non-Javadoc)
* @see org.commcare.android.framework.CommCareActivity#getActivityTitle()
*/
@Override
public String getActivityTitle() {
String userName = null;
try {
userName = CommCareApplication._().getSession().getLoggedInUser().getUsername();
if(userName != null) {
return Localization.get("home.logged.in.message", new String[] {userName});
}
} catch(Exception e) {
//TODO: Better catch, here
}
return "";
}
/*
* (non-Javadoc)
* @see org.commcare.android.framework.CommCareActivity#isTopNavEnabled()
*/
@Override
protected boolean isTopNavEnabled() {
return false;
}
private void formEntry(Uri formUri, FormRecord r) throws SessionUnavailableException{
formEntry(formUri, r, null);
}
private void formEntry(Uri formUri, FormRecord r, String headerTitle) throws SessionUnavailableException{
Logger.log(AndroidLogger.TYPE_FORM_ENTRY, "Form Entry Starting|" + r.getFormNamespace());
//TODO: This is... just terrible. Specify where external instance data should come from
FormLoaderTask.iif = new CommCareInstanceInitializer(CommCareApplication._().getCurrentSession());
//Create our form entry activity callout
Intent i =new Intent(getApplicationContext(), org.odk.collect.android.activities.FormEntryActivity.class);
i.setAction(Intent.ACTION_EDIT);
i.putExtra("odk_title_fragment", BreadcrumbBarFragment.class.getName());
i.putExtra("instancedestination", CommCareApplication._().getCurrentApp().fsPath((GlobalConstants.FILE_CC_FORMS)));
//See if there's existing form data that we want to continue entering (note, this should be stored in the form
///record as a URI link to the instance provider in the future)
if(r.getInstanceURI() != null) {
i.setData(r.getInstanceURI());
} else {
i.setData(formUri);
}
i.putExtra("org.odk.collect.resizing.enabled", CommCarePreferences.getResizeMethod());
i.putExtra("org.odk.collect.form.management", CommCarePreferences.isIncompleteFormsEnabled());
i.putExtra("readonlyform", FormRecord.STATUS_SAVED.equals(r.getStatus()));
i.putExtra("key_aes_storage", Base64.encodeToString(r.getAesKey(), Base64.DEFAULT));
i.putExtra("form_content_uri", FormsProviderAPI.FormsColumns.CONTENT_URI.toString());
i.putExtra("instance_content_uri", InstanceProviderAPI.InstanceColumns.CONTENT_URI.toString());
if(headerTitle != null) {
i.putExtra("form_header", headerTitle);
}
startActivityForResult(i, MODEL_RESULT);
}
protected boolean checkAndStartUnsentTask(final boolean syncAfterwards) throws SessionUnavailableException {
SqlStorage<FormRecord> storage = CommCareApplication._().getUserStorage(FormRecord.class);
FormRecord[] records = StorageUtils.getUnsentRecords(storage);
if(records.length > 0) {
processAndSend(records, syncAfterwards);
return true;
} else {
//Nothing.
return false;
}
}
private String getFormPostURL() {
SharedPreferences settings = CommCareApplication._().getCurrentApp().getAppPreferences();
return settings.getString("PostURL", this.getString(R.string.PostURL));
}
@SuppressLint("NewApi")
private void processAndSend(FormRecord[] records, final boolean syncAfterwards) {
int sendTaskId = syncAfterwards ? ProcessAndSendTask.SEND_PHASE_ID : -1;
ProcessAndSendTask<CommCareHomeActivity> mProcess = new ProcessAndSendTask<CommCareHomeActivity>(this, getFormPostURL(),
sendTaskId, syncAfterwards){
/*
* (non-Javadoc)
* @see org.commcare.android.tasks.templates.CommCareTask#deliverResult(java.lang.Object, java.lang.Object)
*/
@Override
protected void deliverResult(CommCareHomeActivity receiver, Integer result) {
if(result == ProcessAndSendTask.PROGRESS_LOGGED_OUT) {
returnToLogin(Localization.get("app.workflow.login.lost"));
return;
}
try{
receiver.refreshView();
}catch(SessionUnavailableException sue) {
//might have logged out, don't really worry about it.
receiver.returnToLogin(Localization.get("home.logged.out"));
}
int successfulSends = this.getSuccesfulSends();
if(result == FormUploadUtil.FULL_SUCCESS) {
String label = Localization.get("sync.success.sent.singular", new String[] {String.valueOf(successfulSends)});
if(successfulSends > 1) {
label = Localization.get("sync.success.sent", new String[] {String.valueOf(successfulSends)});
}
receiver.displayMessage(label);
if(syncAfterwards) {
syncData(true);
}
} else if(result == FormUploadUtil.FAILURE) {
//Failures make their own notification box
} else {
receiver.displayMessage(Localization.get("sync.fail.unsent"), true);
}
}
/*
* (non-Javadoc)
* @see org.commcare.android.tasks.templates.CommCareTask#deliverUpdate(java.lang.Object, java.lang.Object[])
*/
@Override
protected void deliverUpdate(CommCareHomeActivity receiver, Long... update) {
//we don't need to deliver updates here, it happens on the notification bar
}
/*
* (non-Javadoc)
* @see org.commcare.android.tasks.templates.CommCareTask#deliverError(java.lang.Object, java.lang.Exception)
*/
@Override
protected void deliverError(CommCareHomeActivity receiver,Exception e) {
//TODO: Display somewhere useful
receiver.displayMessage(Localization.get("sync.fail.unsent"), true);
}
};
mProcess.setListeners(CommCareApplication._().getSession().startDataSubmissionListener());
mProcess.connect(this);
//Execute on a true multithreaded chain. We should probably replace all of our calls with this
//but this is the big one for now.
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ) {
mProcess.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, records);
} else {
mProcess.execute(records);
}
}
/*
* (non-Javadoc)
*
* @see android.app.Activity#onResume()
*/
@Override
protected void onResume() {
super.onResume();
platform = CommCareApplication._().getCurrentApp() == null ? null : CommCareApplication._().getCurrentApp().getCommCarePlatform();
dispatchHomeScreen();
}
private void dispatchHomeScreen() {
try {
//First make sure nothing catastrophic has happened
if(CommCareApplication._().getAppResourceState() == CommCareApplication.STATE_CORRUPTED ||
CommCareApplication._().getDatabaseState() == CommCareApplication.STATE_CORRUPTED) {
if(!CommCareApplication._().isStorageAvailable()) {
createNoStorageDialog();
} else {
//see if we're logged in. If so, prompt for recovery.
try {
CommCareApplication._().getSession();
showDialog(DIALOG_CORRUPTED);
}catch(SessionUnavailableException sue) {
//otherwise, log in first
returnToLogin();
}
}
}
//Now we need to catch any resource or database upgrade flags and make sure that the application
//is ready to go.
else if(CommCareApplication._().getAppResourceState() != CommCareApplication.STATE_READY ||
CommCareApplication._().getDatabaseState() != CommCareApplication.STATE_READY) {
Intent i = new Intent(getApplicationContext(), CommCareSetupActivity.class);
this.startActivityForResult(i, INIT_APP);
} else if(!CommCareApplication._().getCurrentApp().areResourcesValidated()){
Intent i = new Intent(this, CommCareVerificationActivity.class);
this.startActivityForResult(i, MISSING_MEDIA_ACTIVITY);
} else if(!CommCareApplication._().getSession().isLoggedIn()) {
//We got brought back to this point despite
returnToLogin();
} else if(this.getIntent().hasExtra(SESSION_REQUEST)) {
wasExternal = true;
String sessionRequest = this.getIntent().getStringExtra(SESSION_REQUEST);
SessionStateDescriptor ssd = new SessionStateDescriptor();
ssd.fromBundle(sessionRequest);
CommCareApplication._().getCurrentSessionWrapper().loadFromStateDescription(ssd);
this.startNextFetch();
return;
} else if(this.getIntent().hasExtra(AndroidShortcuts.EXTRA_KEY_SHORTCUT)) {
//We were launched in shortcut mode. Get the command and load us up.
CommCareApplication._().getCurrentSession().setCommand(this.getIntent().getStringExtra(AndroidShortcuts.EXTRA_KEY_SHORTCUT));
startNextFetch();
//Only launch shortcuts once per intent
this.getIntent().removeExtra(AndroidShortcuts.EXTRA_KEY_SHORTCUT);
}
else if(CommCareApplication._().isUpdatePending()) {
//We've got an update pending that we need to check on.
Logger.log(AndroidLogger.TYPE_MAINTENANCE, "Auto-Update Triggered");
//Create the update intent
Intent i = new Intent(getApplicationContext(), CommCareSetupActivity.class);
SharedPreferences prefs = CommCareApplication._().getCurrentApp().getAppPreferences();
String ref = prefs.getString("default_app_server", null);
i.putExtra(CommCareSetupActivity.KEY_PROFILE_REF, ref);
i.putExtra(CommCareSetupActivity.KEY_UPGRADE_MODE, true);
i.putExtra(CommCareSetupActivity.KEY_AUTO, true);
startActivityForResult(i,UPGRADE_APP);
return;
} else if(CommCareApplication._().isSyncPending(false)) {
long lastSync = CommCareApplication._().getCurrentApp().getAppPreferences().getLong("last-ota-restore", 0);
String footer = lastSync == 0 ? "never" : SimpleDateFormat.getDateTimeInstance().format(lastSync);
Logger.log(AndroidLogger.TYPE_USER, "autosync triggered. Last Sync|" + footer);
refreshView();
this.syncData(false);
}
//Normal Home Screen login time!
else {
refreshView();
}
} catch(SessionUnavailableException sue) {
//TODO: See how much context we have, and go login
returnToLogin();
}
}
private void returnToLogin() {
returnToLogin(Localization.get("app.workflow.login.lost"));
}
private void returnToLogin(String message) {
//Not yet.
if(message != null) {
//Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
Intent i = new Intent(getApplicationContext(), LoginActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivityForResult(i,LOGIN_USER);
}
public void createNoStorageDialog() {
CommCareApplication._().triggerHandledAppExit(this, Localization.get("app.storage.missing.message"), Localization.get("app.storage.missing.title"));
}
/*
* NOTE: This is probably not valid anymore
*/
private boolean testBotchedUpgrade() {
//If the install folder is empty, we know that commcare wiped out our stuff.
File install = new File(CommCareApplication._().getCurrentApp().fsPath(GlobalConstants.FILE_CC_INSTALL));
File[] installed = install.listFiles();
if(installed == null || installed.length == 0) {
return true;
}
//there's another failure mode where the files somehow end up empty.
for(File f : installed) {
if(f.length() != 0) {
return false;
}
}
return true;
}
private void createAskUseOldDialog(final AndroidSessionWrapper state, final SessionStateDescriptor existing) {
mAskOldDialog = new AlertDialog.Builder(this).create();
mAskOldDialog.setTitle(Localization.get("app.workflow.incomplete.continue.title"));
mAskOldDialog.setMessage(Localization.get("app.workflow.incomplete.continue"));
DialogInterface.OnClickListener useOldListener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int i) {
try {
switch (i) {
case DialogInterface.BUTTON1: // yes, use old
//Replace the current state from the descriptor
state.loadFromStateDescription(existing);
formEntry(platform.getFormContentUri(state.getSession().getForm()), state.getFormRecord());
break;
case DialogInterface.BUTTON2: // no, and delete the old one
FormRecordCleanupTask.wipeRecord(CommCareHomeActivity.this, existing);
//fallthrough to new now that old record is gone
case DialogInterface.BUTTON3: // no, create new
state.commitStub();
formEntry(platform.getFormContentUri(state.getSession().getForm()), state.getFormRecord());
break;
}
} catch(SessionUnavailableException sue) {
//TODO: From home activity, login again and return to form list if possible.
} catch (StorageFullException e) {
throw new RuntimeException(e);
}
}
};
mAskOldDialog.setCancelable(false);
mAskOldDialog.setButton(Localization.get("option.yes"), useOldListener);
mAskOldDialog.setButton2(Localization.get("app.workflow.incomplete.continue.option.delete"), useOldListener);
mAskOldDialog.setButton3(Localization.get("option.no"), useOldListener);
mAskOldDialog.show();
}
private void displayMessage(String message) {
displayMessage(message, false);
}
private void displayMessage(String message, boolean bad) {
displayMessage(message, bad, false);
}
private void displayMessage(String message, boolean bad, boolean suppressToast) {
if(!suppressToast) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
TextView syncMessage = (TextView)findViewById(R.id.home_sync_message);
syncMessage.setText(message);
//Need to transplant the padding due to background affecting it
int[] padding = {syncMessage.getPaddingLeft(), syncMessage.getPaddingTop(), syncMessage.getPaddingRight(),syncMessage.getPaddingBottom() };
if(bad){
syncMessage.setTextColor(getResources().getColor(R.color.red));
syncMessage.setTypeface(null, Typeface.BOLD);
syncMessage.setBackgroundDrawable(getResources().getDrawable(R.drawable.bubble_danger));
}
else{
syncMessage.setTextColor(getResources().getColor(R.color.black));
syncMessage.setTypeface(null, Typeface.NORMAL);
syncMessage.setBackgroundDrawable(getResources().getDrawable(R.drawable.bubble));
}
syncMessage.setPadding(padding[0],padding[1], padding[2], padding[3]);
}
private void refreshView() throws SessionUnavailableException{
TextView version = (TextView)findViewById(R.id.str_version);
version.setText(CommCareApplication._().getCurrentVersionString());
boolean syncOK = true;
Pair<Long, int[]> syncDetails = CommCareApplication._().getSyncDisplayParameters();
SharedPreferences prefs = CommCareApplication._().getCurrentApp().getAppPreferences();
unsentFormNumberLimit = Integer.parseInt(prefs.getString(UNSENT_FORM_NUMBER_KEY,"5"));
unsentFormTimeLimit = Integer.parseInt(prefs.getString(UNSENT_FORM_TIME_KEY,"5"));
String syncKey = "home.sync";
String lastMessageKey = "home.sync.message.last";
String homeMessageKey = "home.start";
String logoutMessageKey = "home.logout";
if(isDemoUser()) {
syncKey="home.sync.demo";
lastMessageKey="home.sync.message.last";
homeMessageKey="home.start.demo";
logoutMessageKey = "home.logout.demo";
}
//since these might have changed
startButton.setText(Localization.get(homeMessageKey));
logoutButton.setText(Localization.get(logoutMessageKey));
CharSequence syncTime = syncDetails.first == 0? Localization.get("home.sync.message.last.never") : DateUtils.formatSameDayTime(syncDetails.first, new Date().getTime(), DateFormat.DEFAULT, DateFormat.DEFAULT);
//TODO: Localize this all
String message = "";
if(syncDetails.second[0] == 1) {
message += Localization.get("home.sync.message.unsent.singular") + "\n";
} else if (syncDetails.second[0] > 1) {
message += Localization.get("home.sync.message.unsent.plural", new String[] {String.valueOf(syncDetails.second[0])}) + "\n";
}
if(syncDetails.second[0] > 0) {
syncButton.setText(Localization.get("home.sync.indicator", new String[] {String.valueOf(syncDetails.second[0]), Localization.get(syncKey)}));
} else {
syncButton.setText(Localization.get(syncKey));
}
if(syncDetails.second[1] > 0) {
viewIncomplete.setText(Localization.get("home.forms.incomplete.indicator", new String[] {String.valueOf(syncDetails.second[1]), Localization.get("home.forms.incomplete")}));
} else {
viewIncomplete.setText(Localization.get("home.forms.incomplete"));
}
if(syncDetails.second[0] > unsentFormNumberLimit){
syncOK = false;
}
long then = syncDetails.first;
long now = new Date().getTime();
int secs_ago = (int)((then-now) / 1000);
int days_ago = secs_ago / 86400;
if((-days_ago) > unsentFormTimeLimit && (prefs.getString("server-tether","push-only").equals("sync"))) {
syncOK = false;
}
message += Localization.get(lastMessageKey, new String[] { syncTime.toString() });
displayMessage(message, !syncOK, true);
//Make sure that the review button is properly enabled.
Profile p = CommCareApplication._().getCommCarePlatform().getCurrentProfile();
if(p != null && p.isFeatureActive(Profile.FEATURE_REVIEW)) {
viewOldForms.setVisibility(Button.VISIBLE);
}
View formRecordPane = this.findViewById(R.id.home_formspanel);
if((!CommCarePreferences.isIncompleteFormsEnabled() && !CommCarePreferences.isSavedFormsEnabled())) {
formRecordPane.setVisibility(View.GONE);
} else {
/*
* Not in sense mode
* Form records are visible unless specifically set to be on/off
*/
formRecordPane.setVisibility(View.VISIBLE);
if(!CommCarePreferences.isSavedFormsEnabled()){
viewOldForms.setVisibility(View.GONE);
} else {
viewOldForms.setVisibility(View.VISIBLE);
}
if(!CommCarePreferences.isIncompleteFormsEnabled()){
viewIncomplete.setVisibility(View.GONE);
} else {
viewIncomplete.setVisibility(View.VISIBLE);
}
}
}
//Process and send listeners
private boolean isDemoUser() {
try {
User u = CommCareApplication._().getSession().getLoggedInUser();
if(User.TYPE_DEMO.equals(u.getUserType())) {
return true;
}
} catch(SessionUnavailableException e) {
}
return false;
}
//END - Process and Send Listeners
/*
* (non-Javadoc)
* @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, MENU_PREFERENCES, 0, Localization.get("home.menu.settings")).setIcon(
android.R.drawable.ic_menu_preferences);
menu.add(0, MENU_UPDATE, 0, Localization.get("home.menu.update")).setIcon(
android.R.drawable.ic_menu_upload);
menu.add(0, MENU_CALL_LOG, 0, Localization.get("home.menu.call.log")).setIcon(
android.R.drawable.ic_menu_recent_history);
menu.add(0, MENU_REPORT_PROBLEM, 0, Localization.get("problem.report.menuitem")).setIcon(
android.R.drawable.ic_menu_report_image);
menu.add(0, MENU_VALIDATE_MEDIA, 0, Localization.get("home.menu.validate")).setIcon(
android.R.drawable.ic_menu_gallery);
menu.add(0, MENU_DUMP_FORMS, 0, Localization.get("home.menu.formdump")).setIcon(
android.R.drawable.ic_menu_upload);
menu.add(0, MENU_WIFI_DIRECT, 0, Localization.get("home.menu.wifi.direct")).setIcon(
android.R.drawable.ic_menu_upload);
menu.add(0, MENU_CONNECTION_DIAGNOSTIC, 0, Localization.get("home.menu.connection.diagnostic")).setIcon(
android.R.drawable.ic_menu_upload);
menu.add(0, MENU_SAVED_FORMS, 0, Localization.get("home.menu.saved.forms")).setIcon(
R.drawable.notebook_full);
return true;
}
/* (non-Javadoc)
* @see android.app.Activity#onPrepareOptionsMenu(android.view.Menu)
*/
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
//In Holo theme this gets called on startup
try {
User u = CommCareApplication._().getSession().getLoggedInUser();
boolean enableMenus = !User.TYPE_DEMO.equals(u.getUserType());
menu.findItem(MENU_PREFERENCES).setVisible(enableMenus);
menu.findItem(MENU_UPDATE).setVisible(enableMenus);
menu.findItem(MENU_VALIDATE_MEDIA).setVisible(enableMenus);
menu.findItem(MENU_DUMP_FORMS).setVisible(enableMenus);
menu.findItem(MENU_WIFI_DIRECT).setVisible(enableMenus && hasP2p());
menu.findItem(MENU_CONNECTION_DIAGNOSTIC).setVisible(enableMenus);
menu.findItem(MENU_SAVED_FORMS).setVisible(enableMenus);
} catch(SessionUnavailableException sue) {
//Nothing
}
return true;
}
/*
* (non-Javadoc)
* @see org.commcare.android.framework.CommCareActivity#onOptionsItemSelected(android.view.MenuItem)
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_PREFERENCES:
//CommCareUtil.printInstance("jr://instance/stockdb");
createPreferencesMenu();
return true;
case MENU_UPDATE:
if(!isOnline() && isAirplaneModeOn()){
CommCareApplication._().reportNotificationMessage(NotificationMessageFactory.message(StockMessages.Sync_AirplaneMode));
return true;
}
Intent i = new Intent(getApplicationContext(), CommCareSetupActivity.class);
SharedPreferences prefs = CommCareApplication._().getCurrentApp().getAppPreferences();
String ref = prefs.getString("default_app_server", null);
i.putExtra(CommCareSetupActivity.KEY_PROFILE_REF, ref);
i.putExtra(CommCareSetupActivity.KEY_UPGRADE_MODE, true);
startActivityForResult(i,UPGRADE_APP);
return true;
case MENU_CALL_LOG:
createCallLogActivity();
return true;
case MENU_REPORT_PROBLEM:
startReportActivity();
return true;
case MENU_VALIDATE_MEDIA:
startValidationActivity();
return true;
case MENU_DUMP_FORMS:
startFormDumpActivity();
return true;
case MENU_WIFI_DIRECT:
startWifiDirectActivity();
return true;
case MENU_CONNECTION_DIAGNOSTIC:
startMenuConnectionActivity();
return true;
case MENU_SAVED_FORMS:
goToFormArchive(false);
return true;
}
return super.onOptionsItemSelected(item);
}
private void createPreferencesMenu() {
Intent i = new Intent(this, CommCarePreferences.class);
CommCareHomeActivity.this.startActivityForResult(i, PREFERENCES_ACTIVITY);
}
private void createCallLogActivity() {
Intent i = new Intent(this, PhoneLogActivity.class);
startActivity(i);
}
private void startReportActivity() {
Intent i = new Intent(this, ReportProblemActivity.class);
CommCareHomeActivity.this.startActivityForResult(i, REPORT_PROBLEM_ACTIVITY);
}
private void startValidationActivity(){
Intent i = new Intent(this, CommCareVerificationActivity.class);
CommCareHomeActivity.this.startActivityForResult(i, MISSING_MEDIA_ACTIVITY);
}
private void startFormDumpActivity(){
Intent i = new Intent(this, CommCareFormDumpActivity.class);
i.putExtra(CommCareFormDumpActivity.EXTRA_FILE_DESTINATION, CommCareApplication._().getCurrentApp().storageRoot());
CommCareHomeActivity.this.startActivityForResult(i, DUMP_FORMS_ACTIVITY);
}
private void startWifiDirectActivity(){
Intent i = new Intent(this, CommCareWiFiDirectActivity.class);
CommCareHomeActivity.this.startActivityForResult(i, WIFI_DIRECT_ACTIVITY);
}
private void startMenuConnectionActivity()
{
Intent i = new Intent(this, ConnectionDiagnosticActivity.class);
CommCareHomeActivity.this.startActivityForResult(i, CONNECTION_DIAGNOSTIC_ACTIVITY);
}
// @Override
// public void onConfigurationChanged(Configuration newConfig) {
// super.onConfigurationChanged(newConfig);
// setContentView(R.layout.mainnew);
// try {
// configUi();
// refreshView();
// } catch(SessionUnavailableException sue) {
// //we'll handle this in resume?
// }
// }
private boolean isAirplaneModeOn() {
return Settings.System.getInt(getApplicationContext().getContentResolver(),
Settings.System.AIRPLANE_MODE_ON, 0) != 0;
}
private boolean hasP2p(){
return (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH && getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT));
}
protected Dialog onCreateDialog(int id) {
if (id == DIALOG_CORRUPTED) {
return createAskFixDialog();
} else return null;
}
public Dialog createAskFixDialog() {
//TODO: Localize this in theory, but really shift it to the upgrade/management state
mAttemptFixDialog = new AlertDialog.Builder(this).create();
mAttemptFixDialog.setTitle("Storage is Corrupt :/");
mAttemptFixDialog.setMessage("Sorry, something really bad has happened, and the app can't start up. With your permission CommCare can try to repair itself if you have network access.");
DialogInterface.OnClickListener attemptFixDialog = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int i) {
try {
switch (i) {
case DialogInterface.BUTTON1: // attempt repair
Intent intent = new Intent(CommCareHomeActivity.this, RecoveryActivity.class);
startActivity(intent);
break;
case DialogInterface.BUTTON2: // Shut down
CommCareHomeActivity.this.finish();
break;
}
} catch(SessionUnavailableException sue) {
//should be impossible to get here.
throw new RuntimeException("Required session unavailable. Something is seriously wrong");
}
}
};
mAttemptFixDialog.setCancelable(false);
mAttemptFixDialog.setButton("Enter Recovery Mode", attemptFixDialog);
mAttemptFixDialog.setButton2("Shut Down", attemptFixDialog);
return mAttemptFixDialog;
}
/** All methods for implementation of DialogController that are not already handled in CommCareActivity **/
/*
* (non-Javadoc)
* @see org.commcare.android.framework.CommCareActivity#generateProgressDialog(int)
*/
@Override
public CustomProgressDialog generateProgressDialog(int taskId) {
String title, message;
switch (taskId) {
case ProcessAndSendTask.SEND_PHASE_ID:
title = Localization.get("sync.progress.submitting.title");
message = Localization.get("sync.progress.submitting");
break;
case ProcessAndSendTask.PROCESSING_PHASE_ID:
title = Localization.get("form.entry.processing.title");
message = Localization.get("form.entry.processing");
break;
case DataPullTask.DATA_PULL_TASK_ID:
title = Localization.get("sync.progress.title");
message = Localization.get("sync.progress.purge");
break;
default:
System.out.println("WARNING: taskId passed to generateProgressDialog does not match "
+ "any valid possibilities in CommCareHomeActivity");
return null;
}
return CustomProgressDialog.newInstance(title, message, taskId);
}
}