package com.kedzie.vbox.task;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.LinkedList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.ksoap2.SoapFault;
import org.kxml2.kdom.Node;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.kedzie.vbox.R;
import com.kedzie.vbox.api.IProgress;
import com.kedzie.vbox.api.IVirtualBoxErrorInfo;
import com.kedzie.vbox.app.BundleBuilder;
import com.kedzie.vbox.app.Utils;
import com.kedzie.vbox.soap.VBoxSvc;
/**
* VirtualBox® API Asynchronous task with progress & error handling.
* Don't subclass this directly, instead use {@link ActionBarTask} or {@link DialogTask}
* @param <Input> Operation input argument
* @param <Output> Operation output
*/
abstract class BaseTask<Input, Output> extends AsyncTask<Input, IProgress, Output> {
/** interval used to update progress bar for longing-running operation*/
protected final static int PROGRESS_INTERVAL = 200;
private WeakReference<SherlockFragmentActivity> _context;
protected final String TAG;
/** VirtualBox web service API */
protected VBoxSvc _vmgr;
protected boolean _indeterminate=true;
/** <code>true</code> if user pressed back button while task is executing */
protected boolean _cancelled=false;
protected boolean _failed;
private LinkedList<Future<?>> _futures = new LinkedList<Future<?>>();
private ExecutorService _executor;
/**
* Show an Alert Dialog
*/
protected Handler _alertHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
new AlertDialog.Builder(_context.get())
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(msg.getData().getString("title"))
.setMessage(msg.getData().getString("msg"))
.setPositiveButton("OK", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss();}})
.show();
}
};
/**
* @param TAG LogCat tag
* @param vmgr VirtualBox API service
*/
protected BaseTask(SherlockFragmentActivity context, VBoxSvc vmgr) {
TAG = getClass().getSimpleName();
_vmgr=vmgr;
_context = new WeakReference<SherlockFragmentActivity>(context);
init();
}
/**
* @param TAG LogCat tag
* @param vmgr VirtualBox API service
*/
protected BaseTask(String TAG, SherlockFragmentActivity context, VBoxSvc vmgr) {
this.TAG = TAG;
_vmgr=vmgr;
_context=new WeakReference<SherlockFragmentActivity>(context);
init();
}
private void init() {
_executor = _vmgr!=null ? _vmgr.getExecutor() : Executors.newCachedThreadPool();
}
protected ExecutorService getExecutor() {
return _executor;
}
/**
* Fork off parallel execution
* @param task the {@link Runnable} containing the task
*/
protected void fork(Runnable task) {
_futures.add(_executor.submit(task));
}
/**
* Wait for completion of all parallel executions
* @throws ExecutionException
* @throws InterruptedException
*/
protected void join() throws InterruptedException, ExecutionException {
for(Future<?> future : _futures)
future.get();
}
protected SherlockFragmentActivity getContext() {
return _context.get();
}
@Override
protected Output doInBackground(Input... params) {
try {
Log.d(TAG, "Performing work...");
return work(params);
} catch(SoapFault e) {
if(!_cancelled)
showAlert(e);
} catch(Exception e) {
if(!_cancelled)
showAlert(e);
}
_failed=true;
return null;
}
/**
* Implement task with default exception handling
* @param params task input parameters
* @return task return value
* @throws <code>Exception</code> any exception thrown will be displayed to user in an Alert dialog
*/
protected abstract Output work(Input...params) throws Exception;
@Override
protected void onPostExecute(Output result) {
super.onPostExecute(result);
if(!_failed && getContext()!=null && !_cancelled)
onSuccess(result);
else if(_failed)
onFailure();
}
/**
* Handle not-<code>null</code> result
* @param result the result
*/
protected void onSuccess(Output result) {}
/**
* If work function threw exception
*/
protected void onFailure() {}
@Override
protected void onCancelled() {
Log.w(TAG, "Task Cancelled");
_cancelled=true;
super.onCancelled();
}
/**
* Show an Alert dialog
* @param e <code>Throwable</code> which caused the error
*/
protected void showAlert(Throwable e) {
Log.e(TAG, "caught throwable", e);
while(Utils.isEmpty(e.getMessage()) && e.getCause()!=null)
e = e.getCause();
new BundleBuilder().putString("title", e.getClass().getSimpleName()).putString("msg", e.getMessage()).sendMessage(_alertHandler, 0);
}
/**
* Show an Alert dialog
* @param e <code>Throwable</code> which caused the error
*/
protected void showAlert(SoapFault e) {
Log.e(TAG, "SoapFault", e);
Node detail = e.detail;
while(detail.getChildCount()>0) {
Object child = detail.getChild(0);
if(child instanceof Node)
detail = (Node) detail.getChild(0);
else {
Log.i(TAG, "String detail: " + detail);
break;
}
}
new BundleBuilder().putString("title", "Soap Fault")
.putString("msg", String.format("Code: %1$s\nActor: %2$s\nString: %3$s", e.faultcode, e.faultactor, e.faultstring))
.sendMessage(_alertHandler, 0);
}
/**
* Show {@link IVirtualBoxErrorInfo}
* @param code result code
* @param msg error text
*/
protected void showAlert(int code, String msg) {
Log.e(TAG, "Alert error: " + msg);
new BundleBuilder()
.putString("title", getContext().getString(R.string.virtualbox_error_dialog_title))
.putString("msg", "Result Code: " + code + " - " + msg)
.sendMessage(_alertHandler, 0);
}
/**
* Handle VirtualBox API progress functionality
* @param p <code>IProgress</code> of the ongoing task
* @return <code>IProgress</code> of the finished task
* @throws IOException
*/
protected void handleProgress(IProgress p) throws IOException {
Log.d(TAG, "Handling progress");
while(!p.getCompleted()) {
cacheProgress(p);
publishProgress(p);
Utils.sleep(PROGRESS_INTERVAL);
}
Log.d(TAG, "Operation Completed. result code: " + p.getResultCode());
if(p.getResultCode()!=0) {
IVirtualBoxErrorInfo info = p.getErrorInfo();
showAlert(p.getResultCode(), info != null ? info.getText() : "No Message");
return;
}
return;
}
private void cacheProgress(IProgress p ) throws IOException {
p.clearCacheNamed("getDescription", "getOperation", "getOperationDescription", "getOperationWeight", "getOperationPercent", "getTimeRemaining", "getPercent", "getCompleted");
p.getDescription(); p.getOperation(); p.getOperationCount(); p.getOperationDescription();
p.getPercent(); p.getOperationPercent(); p.getOperationWeight(); p.getTimeRemaining();
p.getCompleted(); p.getCancelable();
}
}