package com.vaguehope.onosendai.payload;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import android.content.Context;
import com.vaguehope.onosendai.config.Account;
import com.vaguehope.onosendai.config.Column;
import com.vaguehope.onosendai.model.Meta;
import com.vaguehope.onosendai.model.MetaType;
import com.vaguehope.onosendai.model.Tweet;
import com.vaguehope.onosendai.provider.ProviderMgr;
import com.vaguehope.onosendai.provider.TaskUtils;
import com.vaguehope.onosendai.provider.twitter.TwitterUrls;
import com.vaguehope.onosendai.storage.DbBindingAsyncTask;
import com.vaguehope.onosendai.storage.DbInterface;
import com.vaguehope.onosendai.update.FetchLinkTitle;
import com.vaguehope.onosendai.update.FetchLinkTitle.FetchTitleListener;
import com.vaguehope.onosendai.util.LogWrapper;
import com.vaguehope.onosendai.util.exec.ExecutorEventListener;
public class TweetLinkExpanderTask extends DbBindingAsyncTask<Void, Object, Void> implements FetchTitleListener {
private static final LogWrapper LOG = new LogWrapper("LE");
public static void checkAndRun (final ExecutorEventListener eventListener, final Context context, final ProviderMgr provMgr, final Tweet tweet, final boolean hdMedia, final Account account, final PayloadListAdapter payloadListAdapter, final Executor es) {
for (final Meta meta : tweet.getMetas()) {
if (meta.getType() != MetaType.URL) continue;
new TweetLinkExpanderTask(eventListener, context, provMgr, tweet, hdMedia, account, payloadListAdapter).executeOnExecutor(es);
return;
}
}
private final ProviderMgr provMgr;
private final Tweet tweet;
private final boolean hdMedia;
private final Account account;
private final PayloadListAdapter payloadListAdapter;
public TweetLinkExpanderTask (final ExecutorEventListener eventListener, final Context context, final ProviderMgr provMgr, final Tweet tweet, final boolean hdMedia, final Account account, final PayloadListAdapter payloadListAdapter) {
super(eventListener, context);
this.provMgr = provMgr;
this.tweet = tweet;
this.hdMedia = hdMedia;
this.account = account;
this.payloadListAdapter = payloadListAdapter;
}
@Override
public String toString () {
return "tweetLinkExpander:" + this.tweet.getSid();
}
@Override
protected LogWrapper getLog () {
return LOG;
}
@Override
protected Void doInBackgroundWithDb (final DbInterface db, final Void... params) {
for (final Meta meta : this.tweet.getMetas()) {
if (meta.getType() != MetaType.URL) continue;
final String linkedTweetSid = TwitterUrls.readTweetSidFromUrl(meta.getData());
if (linkedTweetSid != null) {
fetchLinkedTweet(db, meta, linkedTweetSid);
continue;
}
if (!FetchLinkTitle.shouldFetchTitle(meta)) continue;
fetchLinkTitle(db, meta);
}
return null;
}
private enum Prg {
INIT,
URL_AND_TITLE,
TWEET,
MSG,
FAIL;
}
private final Map<Meta, Payload> metaToPayload = new HashMap<Meta, Payload>();
@Override
protected void onProgressUpdate (final Object... values) {
if (values == null || values.length < 1) return;
if (!this.payloadListAdapter.isForTweet(this.tweet)) return;
switch ((Prg) values[0]) {
case INIT: // (meta, msg).
displayPlaceholder((Meta) values[1], (String) values[2]);
break;
case URL_AND_TITLE: // (meta, url, title).
displayLinkTitle((Meta) values[1], (URL) values[2], (CharSequence) values[3]);
break;
case TWEET: // (meta, tweet).
displayTweet((Meta) values[1], (Tweet) values[2]);
break;
case MSG: // (meta, msg).
displayError((Meta) values[1], (String) values[2]);
break;
case FAIL: // (meta, exception).
displayError((Meta) values[1], (Exception) values[2]);
break;
default:
}
}
private void displayPlaceholder (final Meta meta, final String msg) {
final Payload placeHolder = new PlaceholderPayload(this.tweet, msg, true);
this.metaToPayload.put(meta, placeHolder);
final Payload linkPayload = this.payloadListAdapter.findForMeta(meta);
if (linkPayload != null) {
this.payloadListAdapter.addItemAfter(placeHolder, linkPayload);
}
else {
this.payloadListAdapter.addItem(placeHolder);
}
}
private void displayLinkTitle (final Meta meta, final URL finalUrl, final CharSequence title) {
final Payload placeHolder = this.metaToPayload.get(meta);
if (placeHolder == null) throw new IllegalStateException("No cached placeholder for " + meta);
final Payload linkPayload = this.payloadListAdapter.findForMeta(meta);
if (linkPayload != null) {
final String url = finalUrl != null ? finalUrl.toExternalForm() : meta.getData();
this.payloadListAdapter.replaceItem(linkPayload, new LinkPayload(this.tweet, meta, url, title));
this.payloadListAdapter.removeItem(placeHolder);
}
else {
this.payloadListAdapter.replaceItem(placeHolder, new PlaceholderPayload(this.tweet, title));
}
}
private void displayTweet (final Meta meta, final Tweet linkedTweet) {
final Payload placeHolder = this.metaToPayload.get(meta);
if (placeHolder == null) throw new IllegalStateException("No cached placeholder for " + meta);
if (linkedTweet != null) {
this.payloadListAdapter.replaceItem(placeHolder, new InReplyToPayload(this.tweet, linkedTweet));
}
else {
this.payloadListAdapter.replaceItem(placeHolder, new PlaceholderPayload(this.tweet, "Error: null tweet."));
}
}
private void displayError (final Meta meta, final Exception exception) {
displayError(meta, exception != null ? TaskUtils.getEmsg(exception) : "Error: null exception.");
}
private void displayError (final Meta meta, final String msg) {
final Payload placeHolder = this.metaToPayload.get(meta);
if (placeHolder == null) throw new IllegalStateException("No cached placeholder for " + meta);
this.payloadListAdapter.replaceItem(placeHolder, new PlaceholderPayload(this.tweet, msg));
}
private void fetchLinkTitle (final DbInterface db, final Meta meta) {
publishProgress(Prg.INIT, meta, "Fetching title..."); //ES
try {
FetchLinkTitle.fetchTitle(db, meta, this);
}
catch (final Exception e) {
LOG.w("Failed to retrieve title: %s", e.toString());
publishProgress(Prg.FAIL, meta, e);
}
}
@Override
public void onLinkTitle (final Meta m, final String title, final URL finalUrl) throws IOException {
publishProgress(Prg.URL_AND_TITLE, m,
finalUrl != null ? finalUrl : new URL(m.getData()),
title != null ? title : "Title not found."); //ES
}
private void fetchLinkedTweet (final DbInterface db, final Meta meta, final String linkedTweetSid) {
switch (this.account.getProvider()) {
case TWITTER:
fetchFromTwitter(db, meta, linkedTweetSid);
default:
}
}
private void fetchFromTwitter (final DbInterface db, final Meta meta, final String linkedTweetSid) {
publishProgress(Prg.INIT, meta, String.format("Fetching %s...", linkedTweetSid)); //ES
final Tweet fromCache = db.getTweetDetails(linkedTweetSid);
if (fromCache != null) {
LOG.i("From cache: %s=%s", linkedTweetSid, fromCache);
publishProgress(Prg.TWEET, meta, fromCache);
return;
}
try {
final Tweet linkedTweet = this.provMgr.getTwitterProvider().getTweet(this.account, Long.parseLong(linkedTweetSid), this.hdMedia);
LOG.i("Fetched: %s=%s", linkedTweetSid, linkedTweet);
if (linkedTweet != null) {
db.storeTweets(Column.ID_CACHED, Collections.singletonList(linkedTweet));
}
publishProgress(Prg.TWEET, meta, linkedTweet);
}
catch (final Exception e) { // NOSONAR report all errors to UI.
LOG.w("Failed to retrieve tweet %s: %s", linkedTweetSid, e.toString());
publishProgress(Prg.FAIL, meta, e);
}
}
}