package org.commcare.android.tasks; import java.util.ArrayList; import java.util.List; import org.commcare.android.models.Entity; import org.commcare.android.models.NodeEntityFactory; import org.commcare.android.models.notifications.NotificationMessageFactory; import org.commcare.android.models.notifications.NotificationMessageFactory.StockMessages; import org.commcare.dalvik.application.CommCareApplication; import org.commcare.suite.model.Detail; import org.javarosa.core.model.condition.EvaluationContext; import org.javarosa.core.model.instance.TreeReference; import org.javarosa.xpath.XPathException; import org.javarosa.xpath.XPathMissingInstanceException; import android.os.AsyncTask; import android.util.Pair; /** * @author ctsims * */ public class EntityLoaderTask extends AsyncTask<TreeReference, Integer, Pair<List<Entity<TreeReference>>, List<TreeReference>>> { private static EntityLoaderTask pending[] = {null}; NodeEntityFactory factory; EvaluationContext ec; EntityLoaderListener listener; Exception mException = null; private long waitingTime; public EntityLoaderTask(Detail d, EvaluationContext ec) { this.factory = new NodeEntityFactory(d, ec); this.ec = ec; } public void attachListener(EntityLoaderListener listener){ this.listener = listener; listener.attach(this); } /* (non-Javadoc) * @see android.os.AsyncTask#onProgressUpdate(Progress[]) */ @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); } /* (non-Javadoc) * @see android.os.AsyncTask#onPostExecute(java.lang.Object) */ @Override protected void onPostExecute(Pair<List<Entity<TreeReference>>, List<TreeReference>> result) { super.onPostExecute(result); waitingTime = System.currentTimeMillis(); //Ok. So. time to try to deliver the result while(true) { //grab the lock synchronized(pending) { //If our listener is still live, we can deliver our result if(listener != null) { //zero this out to free up reference. this is used as an indicator below to determine if work still needs to be done this.pending[0] = null; // if we have encountered an exception, deliver it and return if(mException != null){ listener.deliverError(mException); return; } //pass those params listener.deliverResult(result.first, result.second); return; } //If our listener is _not_ alive } //Wait try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //If this is pending for more than about a second, drop it, we never know if it's going to get reattached if(System.currentTimeMillis() - waitingTime > 1000) { pending[0] = null; return; } } } /* * (non-Javadoc) * @see android.os.AsyncTask#doInBackground(java.lang.Object[]) */ @Override protected Pair<List<Entity<TreeReference>>, List<TreeReference>> doInBackground(TreeReference... nodeset) { try{ List<TreeReference> references = ec.expandReference(nodeset[0]); List<Entity<TreeReference>> full = new ArrayList<Entity<TreeReference>>(); for(TreeReference ref : references) { if(this.isCancelled()) { return null; } Entity<TreeReference> e = factory.getEntity(ref); if(e != null) { full.add(e); } } return new Pair<List<Entity<TreeReference>>, List<TreeReference>>(full, references); } catch (XPathException xe){ XPathException me = new XPathException("Encountered an xpath error while trying to load and filter the list."); me.setSource(xe.getSource()); mException = me; return null; } } /** * detach the activity and */ public void detachActivity() { synchronized(pending) { pending[0] = this; } } public static boolean attachToActivity(EntityLoaderListener listener) { synchronized(pending) { if(pending[0] == null) { return false; } EntityLoaderTask task = pending[0]; task.attachListener(listener); pending[0] = null; return true; } } }