edu.mit.mobile.android.locast.sync.LocastSimpleSyncService.java Source code

Java tutorial

Introduction

Here is the source code for edu.mit.mobile.android.locast.sync.LocastSimpleSyncService.java

Source

package edu.mit.mobile.android.locast.sync;

/*
 * Copyright (C) 2011-2013  MIT Mobile Experience Lab
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
import java.io.IOException;
import java.util.concurrent.PriorityBlockingQueue;

import org.json.JSONException;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.OperationApplicationException;
import android.content.SyncResult;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import edu.mit.mobile.android.locast.Constants;
import edu.mit.mobile.android.locast.R;
import edu.mit.mobile.android.locast.data.NoPublicPath;
import edu.mit.mobile.android.locast.data.SyncException;
import edu.mit.mobile.android.locast.net.LocastApplicationCallbacks;
import edu.mit.mobile.android.locast.net.NetworkClient;
import edu.mit.mobile.android.locast.net.NetworkProtocolException;

/**
 * <p>
 * A sync manager for {@link SyncEngine} that uses a simplified interface to let activities sync
 * content. Requests are put in a {@link PriorityBlockingQueue} and are handled in reverse
 * chronological order (most recent requests are first). This will be used when
 * {@link AbsLocastAccountSyncService} and the accounts / sync framework isn't being used.
 * </p>
 *
 * <p>
 * Requests should be made using {@link LocastSyncService#startSync(Context, Uri, boolean, Bundle)}
 * and its various permutations.
 * </p>
 *
 */
public abstract class LocastSimpleSyncService extends LocastSyncService {
    private static final String TAG = LocastSimpleSyncService.class.getSimpleName();

    public static final boolean DEBUG = Constants.DEBUG;

    private SyncEngine mSyncEngine;

    private NetworkClient mNetworkClient;

    private SyncQueueProcessor mSyncProcessor;
    private Thread mSyncThread;

    private final String mAuthority;

    private SyncableProvider mProvider;

    private final PriorityBlockingQueue<SyncItem> mPriorityQueue = new PriorityBlockingQueue<LocastSimpleSyncService.SyncItem>();

    public ContentProviderClient mContentProviderClient;

    public LocastSimpleSyncService() {

        mAuthority = getAuthority();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mNetworkClient = ((LocastApplicationCallbacks) getApplication()).getNetworkClientForAccount(this, null);

        mContentProviderClient = getContentResolver().acquireContentProviderClient(mAuthority);

        mProvider = (SyncableProvider) mContentProviderClient.getLocalContentProvider();

        mSyncEngine = new SyncEngine(this, mNetworkClient, mProvider);
        mSyncProcessor = new SyncQueueProcessor();
        mSyncThread = new Thread(mSyncProcessor);
        mSyncThread.start();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        final Uri uri = intent.getData();

        final Bundle extras = intent.getExtras();

        enqueueItem(uri, extras);

        return START_REDELIVER_INTENT;
    }

    public void enqueueItem(Uri uri, Bundle extras) {

        final boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
        final boolean manual = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);

        final SyncItem i = new SyncItem(uri, extras, (expedited ? 10 : 0) + (manual ? 5 : 0));

        if (!expedited && mPriorityQueue.contains(i)) {
            Log.d(TAG, "not adding " + i + " as it's already in the sync queue");
            return;
        }

        mPriorityQueue.add(i);
        Log.d(TAG, "enqueued " + i);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mSyncProcessor.stop();
        mContentProviderClient.release();
        mProvider = null;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new LocalBinder();
    }

    public class LocalBinder extends Binder {
        public LocastSimpleSyncService getBinder() {
            return LocastSimpleSyncService.this;
        }
    }

    private void showNotification() {
        final Notification n = new NotificationCompat.Builder(this)
                .setContentTitle(getString(R.string.sync_notification))
                .setTicker(getString(R.string.sync_notification)).setSmallIcon(android.R.drawable.stat_notify_sync)
                .setOngoing(true)
                .setContentIntent(PendingIntent.getActivity(this, 0, getPackageManager()
                        .getLaunchIntentForPackage(getPackageName()).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0))
                .build();
        final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        nm.notify(R.id.locast_core__sync, n);

    }

    private void clearNotification() {
        final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        nm.cancel(R.id.locast_core__sync);
    }

    private class SyncQueueProcessor implements Runnable {

        private boolean mKeepOn = true;

        public void stop() {
            mKeepOn = false;
            Thread.currentThread().interrupt();
        }

        @Override
        public void run() {
            while (mKeepOn) {

                try {
                    showNotification();
                    final SyncResult sr = new SyncResult();
                    final SyncItem item = mPriorityQueue.take();
                    Log.d(TAG, "took " + item + " from sync queue. Syncing...");
                    mSyncEngine.sync(item.uri, null, item.extras, mContentProviderClient, sr);
                    Log.d(TAG, "finished syncing " + item);
                    Log.d(TAG, mPriorityQueue.size() + " item(s) in queue");

                } catch (final RemoteException e) {
                    Log.e(TAG, "sync error", e);
                } catch (final SyncException e) {
                    Log.e(TAG, "sync error", e);
                } catch (final JSONException e) {
                    Log.e(TAG, "sync error", e);
                } catch (final IOException e) {
                    Log.e(TAG, "sync error", e);
                } catch (final NetworkProtocolException e) {
                    Log.e(TAG, "sync error", e);
                } catch (final NoPublicPath e) {
                    Log.e(TAG, "sync error", e);
                } catch (final OperationApplicationException e) {
                    Log.e(TAG, "sync error", e);
                } catch (final InterruptedException e) {
                    Log.w(TAG, "interrupted", e);
                } finally {
                    clearNotification();
                }
            }
        }
    }

    private static class SyncItem implements Comparable<SyncItem> {
        final Uri uri;
        final Bundle extras;
        final int priority; // higher priority is more likely to occur
        final long creationTime = System.currentTimeMillis();

        public SyncItem(Uri uri, Bundle extras, int priority) {
            this.uri = uri;
            this.extras = extras;
            this.priority = priority;
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof SyncItem) {
                final SyncItem osi = (SyncItem) o;
                return this.priority == osi.priority && (uri != null ? uri.equals(osi.uri) : uri != osi.uri)
                        && (extras != null ? extras.equals(osi.extras) : extras != osi.extras);
            } else {
                return false;
            }
        }

        @Override
        public int compareTo(SyncItem another) {
            if (priority == another.priority) {
                return -/* inverted */Long.valueOf(creationTime).compareTo(another.creationTime);
            } else {
                return -/* inverted */Integer.valueOf(priority).compareTo(another.priority);
            }
        }

        @Override
        public String toString() {

            return "SyncItem: uri=" + uri + ", extras=" + extras + ", priority=" + priority + ", creationTime="
                    + creationTime;
        }
    }
}