com.pindroid.syncadapter.BookmarkSyncAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.pindroid.syncadapter.BookmarkSyncAdapter.java

Source

/*
 * PinDroid - http://code.google.com/p/PinDroid/
 *
 * Copyright (C) 2010 Matt Schmidt
 *
 * PinDroid 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 3 of the License,
 * or (at your option) any later version.
 *
 * PinDroid 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 PinDroid; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

package com.pindroid.syncadapter;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.auth.AuthenticationException;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SyncResult;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;

import com.pindroid.Constants;
import com.pindroid.client.PinboardApi;
import com.pindroid.client.PinboardException;
import com.pindroid.client.TooManyRequestsException;
import com.pindroid.client.Update;
import com.pindroid.platform.BookmarkManager;
import com.pindroid.platform.NoteManager;
import com.pindroid.platform.TagManager;
import com.pindroid.providers.BookmarkContent.Bookmark;
import com.pindroid.providers.NoteContent.Note;
import com.pindroid.providers.TagContent.Tag;

/**
 * SyncAdapter implementation for syncing bookmarks.
 */
public class BookmarkSyncAdapter extends AbstractThreadedSyncAdapter {

    public static final String SYNC_FINISHED_ACTION = "sync_finished";

    private static final String TAG = "BookmarkSyncAdapter";

    private final Context mContext;
    private Account mAccount;
    private final AccountManager mAccountManager;

    public BookmarkSyncAdapter(Context context, boolean autoInitialize) {
        super(context, autoInitialize);
        mContext = context;
        mAccountManager = AccountManager.get(context);
    }

    @Override
    public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider,
            SyncResult syncResult) {

        boolean upload = extras.containsKey(ContentResolver.SYNC_EXTRAS_UPLOAD);
        boolean manual = extras.containsKey(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF)
                && extras.containsKey(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS);

        try {
            if (upload) {
                Log.d(TAG, "Beginning Upload Sync");
                DeleteBookmarks(account, syncResult);
                UploadBookmarks(account, syncResult);
            } else {
                if (manual)
                    Log.d(TAG, "Beginning Manual Download Sync");
                else
                    Log.d(TAG, "Beginning Download Sync");

                DeleteBookmarks(account, syncResult);
                UploadBookmarks(account, syncResult);
                InsertBookmarks(account, syncResult);
            }

            checkSecretToken(account);
        } catch (final ParseException e) {
            syncResult.stats.numParseExceptions++;
            Log.e(TAG, "ParseException", e);
        } catch (final AuthenticationException e) {
            syncResult.stats.numAuthExceptions++;
            Log.e(TAG, "AuthException", e);
        } catch (final IOException e) {
            syncResult.stats.numIoExceptions++;
            Log.e(TAG, "IOException", e);
        } catch (final TooManyRequestsException e) {
            syncResult.delayUntil = e.getBackoff();
            Log.d(TAG, "Too Many Requests.  Backing off for " + e.getBackoff() + " seconds.");
        } catch (PinboardException e) {
            syncResult.stats.numSkippedEntries++;
            Log.e(TAG, "PinboardException", e);
        } finally {
            Log.d(TAG, "Finished Sync");
            LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent(SYNC_FINISHED_ACTION));
        }
    }

    private void InsertBookmarks(Account account, SyncResult syncResult) throws AuthenticationException,
            IOException, TooManyRequestsException, ParseException, PinboardException {

        long lastUpdate = getServerSyncMarker(account);
        final String username = account.name;
        mAccount = account;

        final Update update = PinboardApi.lastUpdate(account, mContext);

        if (update.getLastUpdate() > lastUpdate) {

            Log.d(TAG, "In Bookmark Load");
            final ArrayList<String> accounts = new ArrayList<String>();
            accounts.add(account.name);

            final ArrayList<Bookmark> addBookmarkList = getBookmarkList();
            BookmarkManager.TruncateBookmarks(accounts, mContext, false);
            if (!addBookmarkList.isEmpty()) {
                List<Bookmark> unsyncedBookmarks = BookmarkManager.GetLocalBookmarks(username, mContext);
                addBookmarkList.removeAll(unsyncedBookmarks);

                BookmarkManager.BulkInsert(addBookmarkList, username, mContext);
            }

            final ArrayList<Tag> tagList = PinboardApi.getTags(account, mContext);
            TagManager.TruncateTags(username, mContext);
            if (!tagList.isEmpty()) {
                TagManager.BulkInsert(tagList, username, mContext);
            }

            SyncNotes();

            setServerSyncMarker(account, update.getLastUpdate());

            syncResult.stats.numEntries += addBookmarkList.size();
        } else {
            Log.d(TAG, "No update needed.  Last update time before last sync.");
        }
    }

    private void SyncNotes()
            throws AuthenticationException, IOException, TooManyRequestsException, PinboardException {

        final ArrayList<Note> noteList = PinboardApi.getNoteList(mAccount, mContext);
        NoteManager.TruncateNotes(mAccount.name, mContext);

        for (Note n : noteList) {
            //NoteManager.UpsertNote(n, mAccount.name, mContext);

            Note t = PinboardApi.getNote(n.getPid(), mAccount, mContext);
            n.setText(t.getText());
        }

        if (!noteList.isEmpty()) {
            NoteManager.BulkInsert(noteList, mAccount.name, mContext);
        }
    }

    private void UploadBookmarks(Account account, SyncResult syncResult)
            throws AuthenticationException, IOException, TooManyRequestsException, ParseException {

        final ArrayList<Bookmark> bookmarks = BookmarkManager.GetLocalBookmarks(account.name, mContext);

        for (Bookmark b : bookmarks) {
            try {
                PinboardApi.addBookmark(b, account, mContext);

                Log.d(TAG, "Bookmark edited: " + (b.getHash() == null ? "" : b.getHash()));
                b.setSynced(1);
                BookmarkManager.SetSynced(b, 1, account.name, mContext);

                syncResult.stats.numEntries++;
            } catch (PinboardException e) {
                Log.d(TAG, "Error editing bookmark: " + (b.getHash() == null ? "" : b.getHash()));
                b.setSynced(-1);
                BookmarkManager.SetSynced(b, -1, account.name, mContext);
            }
        }
    }

    private void DeleteBookmarks(Account account, SyncResult syncResult) throws AuthenticationException,
            IOException, TooManyRequestsException, ParseException, PinboardException {

        final ArrayList<Bookmark> bookmarks = BookmarkManager.GetDeletedBookmarks(account.name, mContext);

        for (Bookmark b : bookmarks) {
            PinboardApi.deleteBookmark(b, account, mContext);

            Log.d(TAG, "Bookmark deleted: " + (b.getHash() == null ? "" : b.getHash()));
            BookmarkManager.DeleteBookmark(b, mContext);
        }

        syncResult.stats.numEntries += bookmarks.size();
    }

    private ArrayList<Bookmark> getBookmarkList()
            throws AuthenticationException, IOException, TooManyRequestsException, PinboardException {
        int pageSize = Constants.BOOKMARK_PAGE_SIZE;
        ArrayList<Bookmark> results = new ArrayList<Bookmark>();

        int page = 0;
        boolean morePages = true;

        do {
            morePages = results
                    .addAll(PinboardApi.getAllBookmarks(null, page++ * pageSize, pageSize, mAccount, mContext));
        } while (morePages);

        return results;
    }

    private void checkSecretToken(Account account) throws AuthenticationException, IOException,
            TooManyRequestsException, ParseException, PinboardException {

        String token = mAccountManager.getUserData(account, Constants.PREFS_SECRET_TOKEN);

        if (token == null) {
            token = PinboardApi.getSecretToken(account, mContext);
            mAccountManager.setUserData(account, Constants.PREFS_SECRET_TOKEN, token);
        }
    }

    /**
     * This helper function fetches the last known high-water-mark
     * we received from the server - or 0 if we've never synced.
     * @param account the account we're syncing
     * @return the change high-water-mark
     */
    private long getServerSyncMarker(Account account) {
        String markerString = mAccountManager.getUserData(account, Constants.SYNC_MARKER_KEY);
        if (!TextUtils.isEmpty(markerString)) {
            return Long.parseLong(markerString);
        }
        return 0;
    }

    /**
     * Save off the high-water-mark we receive back from the server.
     * @param account The account we're syncing
     * @param marker The high-water-mark we want to save.
     */
    private void setServerSyncMarker(Account account, long marker) {
        mAccountManager.setUserData(account, Constants.SYNC_MARKER_KEY, Long.toString(marker));
    }
}