/*
* 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 org.odk.collect.android.activities;
import org.odk.collect.android.R;
import org.odk.collect.android.listeners.InstanceUploaderListener;
import org.odk.collect.android.preferences.PreferencesActivity;
import org.odk.collect.android.provider.InstanceProviderAPI.InstanceColumns;
import org.odk.collect.android.tasks.InstanceUploaderTask;
import org.odk.collect.android.utilities.WebUtils;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
/**
* Activity to upload completed forms.
*
* @author Carl Hartung (carlhartung@gmail.com)
*/
public class InstanceUploaderActivity extends Activity implements InstanceUploaderListener {
private final static String t = "InstanceUploaderActivity";
private final static int PROGRESS_DIALOG = 1;
private final static int AUTH_DIALOG = 2;
private ProgressDialog mProgressDialog;
private AlertDialog mAlertDialog;
private String mAlertMsg;
private String ALERT_MSG = "alertmsg";
private String ALERT_SHOWING = "alertshowing";
private static final String TO_SEND = "tosend";
private boolean mAlertShowing;
private InstanceUploaderTask mInstanceUploaderTask;
// maintain a list of what we've yet to send, in case we're interrupted by auth requests
private ArrayList<Long> mInstancesToSend;
// maintain a list of what we've sent, in case we're interrupted by auth requests
private HashMap<String, String> mUploadedInstances;
private String mUrl;
private final static String AUTH_URI = "auth";
/*
* (non-Javadoc)
* @see android.app.Activity#onCreate(android.os.Bundle)
*/
@SuppressWarnings("unchecked")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAlertMsg = getString(R.string.please_wait);
mUploadedInstances = new HashMap<String, String>();
setTitle(getString(R.string.app_name) + " > " + getString(R.string.send_data));
// get instances to upload
Intent intent = getIntent();
long[] selectedInstanceIDs = intent.getLongArrayExtra(FormEntryActivity.KEY_INSTANCES);
if (selectedInstanceIDs.length == 0) {
// If we get nothing, toast and quit
Toast.makeText(this, R.string.noselect_error, Toast.LENGTH_LONG);
finish();
return;
}
if (savedInstanceState != null) {
if (savedInstanceState.containsKey(ALERT_MSG)) {
mAlertMsg = savedInstanceState.getString(ALERT_MSG);
}
if (savedInstanceState.containsKey(ALERT_SHOWING)) {
mAlertShowing = savedInstanceState.getBoolean(ALERT_SHOWING, false);
}
}
if (savedInstanceState != null && !savedInstanceState.containsKey(TO_SEND)) {
mInstancesToSend = (ArrayList<Long>) savedInstanceState.getSerializable(TO_SEND);
} else {
mInstancesToSend = new ArrayList<Long>();
for (int i = 0; i < selectedInstanceIDs.length; i++) {
mInstancesToSend.add(new Long(selectedInstanceIDs[i]));
}
}
// get the task if we've changed orientations. If it's null it's a new upload.
mInstanceUploaderTask = (InstanceUploaderTask) getLastNonConfigurationInstance();
if (mInstanceUploaderTask == null) {
// setup dialog and upload task
showDialog(PROGRESS_DIALOG);
SharedPreferences settings =
PreferenceManager.getDefaultSharedPreferences(getBaseContext());
mInstanceUploaderTask = new InstanceUploaderTask();
// register this activity with the new uploader task
mInstanceUploaderTask.setUploaderListener(InstanceUploaderActivity.this);
Long[] toSendArray = new Long[mInstancesToSend.size()];
mInstancesToSend.toArray(toSendArray);
String auth = settings.getString(PreferencesActivity.KEY_AUTH, "");
mInstanceUploaderTask.setAuth(auth);
mInstanceUploaderTask.execute(toSendArray);
}
}
/*
* (non-Javadoc)
* @see org.odk.collect.android.listeners.InstanceUploaderListener#uploadingComplete(java.util.HashMap)
*/
@Override
public void uploadingComplete(HashMap<String, String> result) {
try {
dismissDialog(PROGRESS_DIALOG);
} catch (Exception e) {
// tried to close a dialog not open. don't care.
}
StringBuilder selection = new StringBuilder();
Set<String> keys = result.keySet();
Iterator<String> it = keys.iterator();
String[] selectionArgs = new String[keys.size()];
int i = 0;
while (it.hasNext()) {
String id = it.next();
selection.append(InstanceColumns._ID + "=?");
selectionArgs[i++] = id;
if (i != keys.size()) {
selection.append(" or ");
}
}
Cursor results =
managedQuery(InstanceColumns.CONTENT_URI, null, selection.toString(), selectionArgs,
null);
StringBuilder message = new StringBuilder();
if (results.getCount() > 0) {
results.moveToPosition(-1);
while (results.moveToNext()) {
String name =
results.getString(results.getColumnIndex(InstanceColumns.DISPLAY_NAME));
String id = results.getString(results.getColumnIndex(InstanceColumns._ID));
message.append(name + " - " + result.get(id) + "\n\n");
}
} else {
message.append(getString(R.string.no_forms_uploaded));
}
createAlertDialog(message.toString().trim());
}
/*
* (non-Javadoc)
* @see org.odk.collect.android.listeners.InstanceUploaderListener#progressUpdate(int, int)
*/
@Override
public void progressUpdate(int progress, int total) {
mAlertMsg = getString(R.string.sending_items, progress, total);
mProgressDialog.setMessage(mAlertMsg);
}
/*
* (non-Javadoc)
* @see android.app.Activity#onCreateDialog(int)
*/
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case PROGRESS_DIALOG:
mProgressDialog = new ProgressDialog(this);
DialogInterface.OnClickListener loadingButtonListener =
new DialogInterface.OnClickListener() {
/*
* (non-Javadoc)
* @see android.content.DialogInterface.OnClickListener#onClick(android.content.DialogInterface, int)
*/
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
mInstanceUploaderTask.cancel(true);
mInstanceUploaderTask.setUploaderListener(null);
finish();
}
};
mProgressDialog.setTitle(getString(R.string.uploading_data));
mProgressDialog.setMessage(mAlertMsg);
mProgressDialog.setIndeterminate(true);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setCancelable(false);
mProgressDialog.setButton(getString(R.string.cancel), loadingButtonListener);
return mProgressDialog;
case AUTH_DIALOG:
AlertDialog.Builder b = new AlertDialog.Builder(this);
LayoutInflater factory = LayoutInflater.from(this);
final View dialogView = factory.inflate(R.layout.server_auth_dialog, null);
// Get the server, username, and password from the settings
SharedPreferences settings =
PreferenceManager.getDefaultSharedPreferences(getBaseContext());
String server = mUrl;
if (server == null) {
// if the bundle is null, we're looking for a formlist
server =
settings.getString(PreferencesActivity.KEY_SERVER_URL,
getString(R.string.default_server_url))
+ settings.getString(PreferencesActivity.KEY_FORMLIST_URL,
"/formList");
}
final String url = server;
Log.i(t, "Trying connecting to: " + url);
EditText username = (EditText) dialogView.findViewById(R.id.username_edit);
String storedUsername = settings.getString(PreferencesActivity.KEY_USERNAME, null);
username.setText(storedUsername);
EditText password = (EditText) dialogView.findViewById(R.id.password_edit);
String storedPassword = settings.getString(PreferencesActivity.KEY_PASSWORD, null);
password.setText(storedPassword);
b.setTitle(getString(R.string.server_requires_auth));
b.setMessage(getString(R.string.server_auth_credentials, url));
b.setView(dialogView);
b.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
/*
* (non-Javadoc)
* @see android.content.DialogInterface.OnClickListener#onClick(android.content.DialogInterface, int)
*/
@Override
public void onClick(DialogInterface dialog, int which) {
EditText username = (EditText) dialogView.findViewById(R.id.username_edit);
EditText password = (EditText) dialogView.findViewById(R.id.password_edit);
URI u = URI.create(mUrl);
WebUtils.addCredentials(username.getText().toString(), password.getText()
.toString(), u.getHost());
mInstanceUploaderTask = new InstanceUploaderTask();
// register this activity with the new uploader task
mInstanceUploaderTask.setUploaderListener(InstanceUploaderActivity.this);
Long[] toSendArray = new Long[mInstancesToSend.size()];
mInstancesToSend.toArray(toSendArray);
mInstanceUploaderTask.execute(toSendArray);
showDialog(PROGRESS_DIALOG);
}
});
b.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
/*
* (non-Javadoc)
* @see android.content.DialogInterface.OnClickListener#onClick(android.content.DialogInterface, int)
*/
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
b.setCancelable(false);
return b.create();
}
return null;
}
/*
* (non-Javadoc)
* @see android.app.Activity#onRestoreInstanceState(android.os.Bundle)
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mUrl = savedInstanceState.getString(AUTH_URI);
}
/*
* (non-Javadoc)
* @see android.app.Activity#onSaveInstanceState(android.os.Bundle)
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(ALERT_MSG, mAlertMsg);
outState.putBoolean(ALERT_SHOWING, mAlertShowing);
outState.putSerializable(TO_SEND, mInstancesToSend);
outState.putString(AUTH_URI, mUrl);
}
/*
* (non-Javadoc)
* @see android.app.Activity#onRetainNonConfigurationInstance()
*/
@Override
public Object onRetainNonConfigurationInstance() {
return mInstanceUploaderTask;
}
/*
* (non-Javadoc)
* @see android.app.Activity#onDestroy()
*/
@Override
protected void onDestroy() {
if (mInstanceUploaderTask != null) {
mInstanceUploaderTask.setUploaderListener(null);
}
super.onDestroy();
}
/*
* (non-Javadoc)
* @see android.app.Activity#onPause()
*/
@Override
protected void onPause() {
super.onPause();
if (mAlertDialog != null && mAlertDialog.isShowing()) {
mAlertDialog.dismiss();
}
}
/*
* (non-Javadoc)
* @see android.app.Activity#onResume()
*/
@Override
protected void onResume() {
if (mInstanceUploaderTask != null) {
mInstanceUploaderTask.setUploaderListener(this);
}
if (mAlertShowing) {
createAlertDialog(mAlertMsg);
}
super.onResume();
}
/*
* (non-Javadoc)
* @see org.odk.collect.android.listeners.InstanceUploaderListener#authRequest(java.net.URI, java.util.HashMap)
*/
@Override
public void authRequest(URI url, HashMap<String, String> doneSoFar) {
if (mProgressDialog.isShowing()) {
// should always be showing here
mProgressDialog.dismiss();
}
// add our list of completed uploads to "completed"
// and remove them from our toSend list.
if (doneSoFar != null) {
Set<String> uploadedInstances = doneSoFar.keySet();
Iterator<String> itr = uploadedInstances.iterator();
while (itr.hasNext()) {
Long removeMe = new Long(itr.next());
boolean removed = mInstancesToSend.remove(removeMe);
if (removed) {
Log.i(t, removeMe
+ " was already sent, removing from queue before restarting task");
}
}
mUploadedInstances.putAll(doneSoFar);
}
// Bundle b = new Bundle();
// b.putString(AUTH_URI, url.toString());
// showDialog(AUTH_DIALOG, b);
mUrl = url.toString();
showDialog(AUTH_DIALOG);
}
private void createAlertDialog(String message) {
mAlertDialog = new AlertDialog.Builder(this).create();
mAlertDialog.setTitle(getString(R.string.upload_results));
mAlertDialog.setMessage(message);
DialogInterface.OnClickListener quitListener = new DialogInterface.OnClickListener() {
/*
* (non-Javadoc)
* @see android.content.DialogInterface.OnClickListener#onClick(android.content.DialogInterface, int)
*/
@Override
public void onClick(DialogInterface dialog, int i) {
switch (i) {
case DialogInterface.BUTTON1: // ok
// always exit this activity since it has no interface
mAlertShowing = false;
finish();
break;
}
}
};
mAlertDialog.setCancelable(false);
mAlertDialog.setButton(getString(R.string.ok), quitListener);
mAlertDialog.setIcon(android.R.drawable.ic_dialog_info);
mAlertShowing = true;
mAlertMsg = message;
mAlertDialog.show();
}
}