/*
* Zandy
* Based in part on Mendroid, Copyright 2011 Martin Paul Eve <martin@martineve.com>
*
* This file is part of Zandy.
*
* Zandy is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Zandy is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Zandy. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.gimranov.zandy.app.task;
import java.util.ArrayList;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import com.gimranov.zandy.app.ServerCredentials;
import com.gimranov.zandy.app.XMLResponseParser;
import com.gimranov.zandy.app.data.Attachment;
import com.gimranov.zandy.app.data.Database;
import com.gimranov.zandy.app.data.Item;
/**
* Executes one or more API requests asynchronously.
*
* Steps in migration:
* 1. Move the logic on what kind of request is handled how into the APIRequest itself
* 2. Throw exceptions when we have errors
* 3. Call the handlers when provided
* 4. Move aggressive syncing logic out of ZoteroAPITask itself; it should be elsewhere.
*
* @author ajlyon
*
*/
public class ZoteroAPITask extends AsyncTask<APIRequest, Message, Message> {
private static final String TAG = "com.gimranov.zandy.app.task.ZoteroAPITask";
public ArrayList<APIRequest> deletions;
public ArrayList<APIRequest> queue;
public int syncMode = -1;
public static final int AUTO_SYNC_STALE_COLLECTIONS = 1;
public boolean autoMode = false;
private Database db;
private ServerCredentials cred;
private Handler handler;
public ZoteroAPITask(Context c)
{
queue = new ArrayList<APIRequest>();
cred = new ServerCredentials(c);
/* TODO reenable in a working way
if (settings.getBoolean("sync_aggressively", false))
syncMode = AUTO_SYNC_STALE_COLLECTIONS;
*/
deletions = APIRequest.delete(c);
db = new Database(c);
}
public void setHandler(Handler h) {
handler = h;
}
private Handler getHandler() {
if (handler == null) {
handler = new Handler() {};
}
return handler;
}
@Override
protected Message doInBackground(APIRequest... params) {
return doFetch(params);
}
@SuppressWarnings("unused")
public Message doFetch(APIRequest... reqs)
{
int count = reqs.length;
for (int i = 0; i < count; i++) {
if (reqs[i] == null) {
Log.d(TAG, "Skipping null request");
continue;
}
// Just in case we missed something, we fix the user ID right here too,
// and we set the key as well.
reqs[i] = cred.prep(reqs[i]);
try {
Log.i(TAG, "Executing API call: " + reqs[i].query);
reqs[i].issue(db, cred);
Log.i(TAG, "Succesfully retrieved API call: " + reqs[i].query);
reqs[i].succeeded(db);
} catch (APIException e) {
Log.e(TAG, "Failed to execute API call: " + e.request.query, e);
e.request.status = APIRequest.REQ_FAILING + e.request.getHttpStatus();
e.request.save(db);
Message msg = Message.obtain();
msg.arg1 = APIRequest.ERROR_UNKNOWN + e.request.getHttpStatus();
return msg;
}
// The XML parser's queue is simply from following continuations in the paged
// feed. We shouldn't split out its requests...
if (XMLResponseParser.queue != null && !XMLResponseParser.queue.isEmpty()) {
Log.i(TAG, "Finished call, but adding " +
XMLResponseParser.queue.size() +
" items to queue.");
queue.addAll(XMLResponseParser.queue);
XMLResponseParser.queue.clear();
} else {
Log.i(TAG, "Finished call, and parser's request queue is empty");
}
}
//
if (queue.size() > 0) {
// If the last batch saw unchanged items, don't follow the Atom
// continuations; just run the child requests
// XXX This is disabled for now
if (false && !XMLResponseParser.followNext) {
ArrayList<APIRequest> toRemove = new ArrayList<APIRequest>();
for (APIRequest r : queue) {
if (r.type != APIRequest.ITEMS_CHILDREN) {
Log.d(TAG, "Removing request from queue since last page had old items: "+r.query);
toRemove.add(r);
}
}
queue.removeAll(toRemove);
}
Log.i(TAG, "Starting queued requests: " + queue.size() + " requests");
APIRequest[] templ = { };
APIRequest[] requests = queue.toArray(templ);
queue.clear();
Log.i(TAG, "Queue size now: "+queue.size());
// XXX I suspect that this calling of doFetch from doFetch might be the cause of our
// out-of-memory situations. We may be able to accomplish the same thing by expecting
// the code listening to our handler to fetch again if QUEUED_MORE is received. In that
// case, we could just save our queue here and really return.
// XXX Test: Here, we try to use doInBackground instead
doInBackground(requests);
// Return a message with the number of requests added to the queue
Message msg = Message.obtain();
msg.arg1 = APIRequest.QUEUED_MORE;
msg.arg2 = requests.length;
return msg;
}
// Here's where we tie in to periodic housekeeping syncs
// If we're already in auto mode (that is, here), just move on
if (autoMode) {
Message msg = Message.obtain();
msg.arg1 = APIRequest.UPDATED_DATA;
return msg;
}
Log.d(TAG, "Sending local changes");
Item.queue(db);
Attachment.queue(db);
APIRequest[] templ = {};
ArrayList<APIRequest> list = new ArrayList<APIRequest>();
for (Item i : Item.queue) {
list.add(cred.prep(APIRequest.update(i)));
}
for (Attachment a : Attachment.queue) {
list.add(cred.prep(APIRequest.update(a, db)));
}
// This queue has deletions, collection memberships, and failing requests
// We may want to filter it in the future
list.addAll(APIRequest.queue(db));
// We're in auto mode...
autoMode = true;
doInBackground(list.toArray(templ));
// Return a message noting that we've queued more requests
Message msg = Message.obtain();
msg.arg1 = APIRequest.QUEUED_MORE;
msg.arg2 = list.size();
return msg;
}
@Override
protected void onPostExecute(Message result) {
getHandler().sendMessage(result);
}
}