at.ac.tuwien.detlef.gpodder.SyncSubscriptionsAsyncTask.java Source code

Java tutorial

Introduction

Here is the source code for at.ac.tuwien.detlef.gpodder.SyncSubscriptionsAsyncTask.java

Source

/* *************************************************************************
 *  Copyright 2012 The detlef developers                                   *
 *                                                                         *
 *  This program is free software: you can redistribute it and/or modify   *
 *  it under the terms of the GNU General Public License as published by   *
 *  the Free Software Foundation, either version 2 of the License, or      *
 *  (at your option) any later version.                                    *
 *                                                                         *
 *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.  *
 ************************************************************************* */

package at.ac.tuwien.detlef.gpodder;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.apache.http.auth.AuthenticationException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpResponseException;

import android.content.Context;
import android.util.Log;
import at.ac.tuwien.detlef.Detlef;
import at.ac.tuwien.detlef.R;
import at.ac.tuwien.detlef.Singletons;
import at.ac.tuwien.detlef.db.PodcastDAO;
import at.ac.tuwien.detlef.domain.DeviceId;
import at.ac.tuwien.detlef.domain.EnhancedSubscriptionChanges;
import at.ac.tuwien.detlef.domain.Podcast;
import at.ac.tuwien.detlef.domain.PodcastPersistence;
import at.ac.tuwien.detlef.settings.GpodderSettings;

import com.dragontek.mygpoclient.api.MygPodderClient;
import com.dragontek.mygpoclient.api.SubscriptionChanges;
import com.dragontek.mygpoclient.api.UpdateResult;
import com.dragontek.mygpoclient.pub.PublicClient;
import com.dragontek.mygpoclient.simple.IPodcast;

/**
 * A Runnable to fetch subscription changes. It should be started in its own
 * Thread and sends a reply via the specified callback. The user of the Task
 * needs to implement the Callback's handle & handleFailure methods.
 */
public class SyncSubscriptionsAsyncTask implements Runnable {

    private static final String TAG = SyncSubscriptionsAsyncTask.class.getName();

    private static final int HTTP_STATUS_FORBIDDEN = 401;
    private static final int HTTP_STATUS_NOT_FOUND = 404;
    private static final int GENERIC_ERROR = -1;

    private final NoDataResultHandler<?> callback;

    public SyncSubscriptionsAsyncTask(NoDataResultHandler<?> callback) {
        this.callback = callback;
    }

    @Override
    public void run() {
        boolean success = false;

        /* Retrieve settings. */
        GpodderSettings gps = Singletons.i().getGpodderSettings();

        DeviceId id = gps.getDeviceId();
        if (id == null) {
            sendError(GENERIC_ERROR, Detlef.getAppContext().getString(R.string.no_gpodder_account_configured));
            return;
        }

        PodcastDAO pdao = Singletons.i().getPodcastDAO();

        String devId = id.toString();

        long lastUpdate = gps.getLastUpdate();

        MygPodderClient gpc = new MygPodderClient(gps.getUsername(), gps.getPassword(), gps.getApiHostname());

        try {
            EnhancedSubscriptionChanges localChanges = new EnhancedSubscriptionChanges(
                    pdao.getLocallyAddedPodcasts(), pdao.getLocallyDeletedPodcasts(), lastUpdate);

            UpdateResult result = gpc.updateSubscriptions(devId, localChanges.getAddUrls(),
                    localChanges.getRemoveUrls());

            /* Login and get subscription changes */
            SubscriptionChanges changes = gpc.pullSubscriptions(devId, lastUpdate);

            /* Get the Details for the individual URLs. */

            PodcastDetailsRetriever pdr = new PodcastDetailsRetriever();
            EnhancedSubscriptionChanges remoteChanges = pdr.getPodcastDetails(changes);

            /* Update the db here */

            applySubscriptionChanges(Detlef.getAppContext(), localChanges);
            applySubscriptionChanges(Detlef.getAppContext(), remoteChanges);

            /* apply the changed URLs */
            if (result.updateUrls != null && result.updateUrls.size() > 0) {
                for (String oldUrl : result.updateUrls.keySet()) {
                    Podcast p = pdao.getPodcastByUrl(oldUrl);
                    if (p == null) {
                        continue;
                    }

                    String newUrl = result.updateUrls.get(oldUrl);
                    if (newUrl == null) {
                        newUrl = "";
                    }

                    p.setUrl(newUrl);
                    pdao.update(p);
                }
            }

            /* Update last changed timestamp. */
            gps.setLastUpdate(remoteChanges.getTimestamp());

            Singletons.i().getGpodderSettingsDAO().writeSettings(gps);

            success = true;
        } catch (HttpResponseException e) {
            String eMsg = e.getLocalizedMessage();
            switch (e.getStatusCode()) {
            case HTTP_STATUS_FORBIDDEN:
                eMsg = Detlef.getAppContext().getString(R.string.connectiontest_unsuccessful);
                break;
            case HTTP_STATUS_NOT_FOUND:
                eMsg = String.format(Detlef.getAppContext().getString(R.string.device_doesnt_exist_fmt), devId);
                break;
            default:
                break;
            }
            sendError(e.getStatusCode(), eMsg);
        } catch (AuthenticationException e) {
            sendError(GENERIC_ERROR, e.getLocalizedMessage());
        } catch (ClientProtocolException e) {
            sendError(GENERIC_ERROR, e.getLocalizedMessage());
        } catch (IOException e) {
            sendError(GENERIC_ERROR, e.getLocalizedMessage());
        } catch (Exception e) {
            sendError(GENERIC_ERROR, e.getLocalizedMessage());
        }

        if (!success) {
            return;
        }

        /* Send the result. */
        callback.sendEvent(new NoDataResultHandler.NoDataSuccessEvent(callback));
    }

    /**
     * Called when the task encounters an error. The given error code and string
     * are sent. The Task should exit after this has been called.
     *
     * @param errCode The error code.
     * @param errString The error string.
     */
    private void sendError(int errCode, String errString) {
        callback.sendEvent(new ResultHandler.GenericFailureEvent(callback, errCode, errString));
    }

    private void applySubscriptionChanges(Context context, EnhancedSubscriptionChanges changes) {
        Log.d(TAG, "Applying changes");

        PodcastDAO dao = Singletons.i().getPodcastDAO();
        for (Podcast p : changes.getAdd()) {
            /* The podcast may already be in the local add/delete table. */
            Podcast pod = dao.getPodcastByUrl(p.getUrl());
            if (pod != null) {
                if (pod.isLocalAdd() || pod.isLocalDel()) {
                    dao.setRemotePodcast(pod);
                    if (pod.getLogoUrl() != null && !pod.getLogoUrl().equals("")) {
                        try {
                            PodcastPersistence.download(pod);
                        } catch (IOException e) {
                            Log.e(TAG, "error downloading podcast img: " + e.getMessage());
                        }
                    }
                }

                /* Never insert a podcast twice. */
                continue;
            }

            if ((p.getTitle() != null) && (p.getUrl() != null)) {

                if (p.getLogoUrl() != null && !p.getLogoUrl().equals("")) {
                    try {
                        PodcastPersistence.download(p);
                    } catch (IOException e) {
                        Log.e(TAG, "Error downloading podcast img: " + e.getMessage());
                    }
                }
                dao.insertPodcast(p);
            } else {
                Log.w(TAG, "Cannot insert podcast without title/url");
            }
        }

        for (Podcast p : changes.getRemove()) {
            Podcast pod = dao.getPodcastByUrl(p.getUrl());
            if (pod != null) {
                dao.deletePodcast(pod);
            }
        }
    }

    /**
     * Wrapper class to turn mygpoclient-java SubscriptionChanges into our
     * EnhancedSubscriptionChanges.
     */
    private static class PodcastDetailsRetriever {
        private final PublicClient pub;

        public PodcastDetailsRetriever() {
            pub = new PublicClient();
        }

        /**
         * Convert changes into EnhancedSubscriptionChanges. This accesses the
         * Network is may be sloooooooowwwww.
         *
         * @param changes
         * @return The converted SubscriptionChagnes.
         */
        public EnhancedSubscriptionChanges getPodcastDetails(SubscriptionChanges changes) {
            return new EnhancedSubscriptionChanges(getPodcastSetDetails(changes.add),
                    getPodcastSetDetails(changes.remove), changes.timestamp);
        }

        public IPodcast getPodcastDetails(String url) {
            try {
                return pub.getPodcastData(url);
            } catch (ClientProtocolException e) {
                /* do nothing */
            } catch (IOException e) {
                /* do nothing */
            }

            return null;
        }

        private List<IPodcast> getPodcastSetDetails(Set<String> urls) {
            List<IPodcast> podcasts = new ArrayList<IPodcast>(urls.size());
            for (String url : urls) {
                IPodcast podcast = getPodcastDetails(url);
                if (podcast != null) {
                    podcasts.add(getPodcastDetails(url));
                }
            }

            return podcasts;
        }
    }
}