com.manning.androidhacks.hack023.service.TodoSyncAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.manning.androidhacks.hack023.service.TodoSyncAdapter.java

Source

/*******************************************************************************
 * Copyright (c) 2012 Manning
 * See the file license.txt for copying permission.
 ******************************************************************************/
package com.manning.androidhacks.hack023.service;

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

import org.apache.http.ParseException;
import org.apache.http.auth.AuthenticationException;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SyncResult;
import android.os.Bundle;
import android.util.Log;

import com.google.gson.JsonParseException;
import com.manning.androidhacks.hack023.api.TodoServiceImpl;
import com.manning.androidhacks.hack023.authenticator.AuthenticatorActivity;
import com.manning.androidhacks.hack023.dao.TodoDAO;
import com.manning.androidhacks.hack023.exception.AndroidHacksException;
import com.manning.androidhacks.hack023.model.Todo;
import com.manning.androidhacks.hack023.provider.StatusFlag;

public class TodoSyncAdapter extends AbstractThreadedSyncAdapter {

    private static final String TAG = TodoSyncAdapter.class.getCanonicalName();
    private final ContentResolver mContentResolver;
    private AccountManager mAccountManager;
    private final static TodoDAO mTodoDAO = TodoDAO.getInstance();

    public TodoSyncAdapter(Context context, boolean autoInitialize) {
        super(context, autoInitialize);
        mContentResolver = context.getContentResolver();
        mAccountManager = AccountManager.get(context);
    }

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

        String authtoken = null;
        try {
            authtoken = mAccountManager.blockingGetAuthToken(account, AuthenticatorActivity.PARAM_AUTHTOKEN_TYPE,
                    true);

            List<Todo> data = fetchData();
            syncRemoteDeleted(data);
            syncFromServerToLocalStorage(data);
            syncDirtyToServer(mTodoDAO.getDirtyList(mContentResolver));

        } catch (Exception e) {
            handleException(authtoken, e, syncResult);
        }

    }

    protected void syncDirtyToServer(List<Todo> dirtyList)
            throws AuthenticationException, IOException, AndroidHacksException {
        for (Todo todo : dirtyList) {
            Log.d(TAG, "Dirty list: " + todo);

            switch (todo.getStatus()) {
            case StatusFlag.ADD:
                pushNewTodo(todo);
                break;
            case StatusFlag.MOD:
                throw new AndroidHacksException("Todo title modification is not supported");
            case StatusFlag.DELETE:
                pushDeleteTodo(todo);
                break;
            default:
                throw new RuntimeException("Invalid status: " + todo.getStatus());
            }
        }
    }

    private void pushNewTodo(Todo todo) throws AuthenticationException, IOException, AndroidHacksException {
        Todo serverTodo = TodoServiceImpl.createTodo(todo.getTitle());
        mTodoDAO.clearAdd(mContentResolver, todo.getId(), serverTodo);
    }

    private void pushDeleteTodo(Todo todo) throws AuthenticationException, AndroidHacksException {
        TodoServiceImpl.deleteTodo(todo.getId());
        mTodoDAO.deleteTodoForced(mContentResolver, todo.getId());
    }

    protected void syncRemoteDeleted(List<Todo> remoteData) {
        Log.d(TAG, "Syncing remote deleted lists...");

        List<Todo> localClean = mTodoDAO.getCleanTodos(mContentResolver);
        for (Todo cleanTodo : localClean) {

            if (!remoteData.contains(cleanTodo)) {
                Log.d(TAG, "Todo with id " + cleanTodo.getId() + " has been deleted remotely.");
                mTodoDAO.forcedDeleteTodo(mContentResolver, cleanTodo.getId());
            }
        }
    }

    protected void syncFromServerToLocalStorage(List<Todo> data) {
        for (Todo todoFromServer : data) {
            Todo todoInDb = mTodoDAO.isTodoInDb(mContentResolver, todoFromServer.getId());

            if (todoInDb == null) {
                Log.d(TAG, "Adding new todo from server: " + todoFromServer);
                mTodoDAO.addNewTodo(mContentResolver, todoFromServer, StatusFlag.CLEAN);

            } else if (todoInDb.getStatus() == StatusFlag.CLEAN) {
                Log.d(TAG, "Modifying list from server: " + todoInDb);
                mTodoDAO.modifyTodoFromServer(mContentResolver, todoFromServer);
            }

        }
    }

    protected List<Todo> fetchData()
            throws AuthenticationException, AndroidHacksException, JsonParseException, IOException {
        List<Todo> list = TodoServiceImpl.fetchTodos();

        return list;
    }

    private void handleException(String authtoken, Exception e, SyncResult syncResult) {
        if (e instanceof AuthenticatorException) {
            syncResult.stats.numParseExceptions++;
            Log.e(TAG, "AuthenticatorException", e);
        } else if (e instanceof OperationCanceledException) {
            Log.e(TAG, "OperationCanceledExcepion", e);
        } else if (e instanceof IOException) {
            Log.e(TAG, "IOException", e);
            syncResult.stats.numIoExceptions++;
        } else if (e instanceof AuthenticationException) {
            mAccountManager.invalidateAuthToken(AuthenticatorActivity.PARAM_ACCOUNT_TYPE, authtoken);
            // The numAuthExceptions require user intervention and are
            // considered hard errors.
            // We automatically get a new hash, so let's make SyncManager retry
            // automatically.
            syncResult.stats.numIoExceptions++;
            Log.e(TAG, "AuthenticationException", e);
        } else if (e instanceof ParseException) {
            syncResult.stats.numParseExceptions++;
            Log.e(TAG, "ParseException", e);
        } else if (e instanceof JsonParseException) {
            syncResult.stats.numParseExceptions++;
            Log.e(TAG, "JSONException", e);
        } else if (e instanceof AndroidHacksException) {
            Log.e(TAG, "AndroidHacksException", e);
        }
    }

}