org.teleportr.ConnectorService.java Source code

Java tutorial

Introduction

Here is the source code for org.teleportr.ConnectorService.java

Source

/**
 * Fahrgemeinschaft / Ridesharing App
 * Copyright (c) 2013 by it's authors.
 * Some rights reserved. See LICENSE..
 *
 */

package org.teleportr;

import java.io.FileNotFoundException;
import java.util.Date;
import java.util.HashMap;

import org.json.JSONException;
import org.teleportr.Ride.COLUMNS;

import android.app.Service;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.Cursor;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;

public class ConnectorService extends Service implements OnSharedPreferenceChangeListener {

    public static final String CLEANUP_INTERVAL = "cleanup_interval";
    public static final String LAST_CLEANUP = "last_cleanup";
    private static final String WRONG_LOGIN_OR_PASSWORD = "wrong login or password";
    private static final String VERBOSE = "verbose";
    private static final String GPLACES = "de.fahrgemeinschaft.GPlaces";
    private static final String FAHRGEMEINSCHAFT = "de.fahrgemeinschaft.FahrgemeinschaftConnector";
    private static final String WORKER = "worker";
    protected static final String TAG = "ConnectorService";
    public static final String SEARCH = "search";
    public static final String RESOLVE = "resolve";
    public static final String PUBLISH = "publish";
    public static final String MYRIDES = "myrides";
    public static final String UPGRADE = "upgrade";
    public static final String AUTH = "auth";
    private Connector fahrgemeinschaft;
    private Connector gplaces;
    private Handler worker;
    HashMap<Long, Integer> retries;
    private Handler reporter;
    protected boolean authenticating;
    public Search search;
    public Resolve resolve;
    public Publish publish;
    public Myrides myrides;

    @Override
    public void onCreate() {
        HandlerThread thread = new HandlerThread(WORKER);
        thread.start();
        worker = new Handler(thread.getLooper());
        reporter = new Handler();
        try {
            fahrgemeinschaft = (Connector) Class.forName(FAHRGEMEINSCHAFT).newInstance();
            fahrgemeinschaft.setContext(this);
            gplaces = (Connector) Class.forName(GPLACES).newInstance();
            gplaces.setContext(this);
        } catch (Exception e) {
            e.printStackTrace();
        }
        search = new Search(getContext());
        resolve = new Resolve(getContext());
        publish = new Publish(getContext());
        myrides = new Myrides(getContext());
        retries = new HashMap<Long, Integer>();
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        prefs.registerOnSharedPreferenceChangeListener(this);
        onSharedPreferenceChanged(prefs, VERBOSE);
        cleanUp(prefs);
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent == null) {
            return START_NOT_STICKY;
        }
        String action = intent.getAction();
        if (action == null) {
            return START_NOT_STICKY;
        }
        if (action.equals(RESOLVE)) {
            worker.postAtFrontOfQueue(resolve);
        } else if (action.equals(SEARCH)) {
            worker.postAtFrontOfQueue(resolve);
            worker.postAtFrontOfQueue(search);
        } else if (action.equals(PUBLISH)) {
            worker.postAtFrontOfQueue(publish);
        } else if (action.equals(MYRIDES)) {
            worker.postAtFrontOfQueue(myrides);
        }
        return START_REDELIVER_INTENT;
    }

    public class Myrides extends Job<String> {

        private static final String LOGIN = "login";

        public Myrides(Context ctx) {
            super(ctx, worker, reporter, null);
        }

        @Override
        void work(Cursor job) throws Exception {
            try {
                progress(MYRIDES, 0);
                fahrgemeinschaft.doSearch(null, 0);
                success(MYRIDES, 0);
            } catch (FileNotFoundException e) {
                fail(MYRIDES, LOGIN);
            }
        }

        @Override
        protected void success(String what, int number) {
            worker.post(search);
            super.success(what, number);
        }
    };

    public class Resolve extends Job<Place> {

        public Resolve(Context ctx) {
            super(ctx, worker, reporter, RidesProvider.getResolveJobsUri(getContext()));
        }

        @Override
        void work(Cursor job) throws Exception {
            Place p = new Place((int) job.getLong(0), getContext());
            progress(p, 0);
            gplaces.resolvePlace(p, getContext());
            success(p, 0);
            log(p.getName() + RESOLVE);
        }
    };

    public class Search extends Job<Ride> {

        public Search(Context ctx) {
            super(ctx, worker, reporter, RidesProvider.getSearchJobsUri(getContext()));
        }

        @Override
        void work(Cursor job) throws Exception {
            Ride query = new Ride(getContext()).from(job.getInt(0)).to(job.getInt(1)).dep(new Date(job.getLong(3)));
            try {
                progress(query, 0);
                fahrgemeinschaft.doSearch(query, job.getLong(2));
                success(query, fahrgemeinschaft.getNumberOfRidesFound());
                worker.post(this);
            } catch (AuthException e) { // APIKEY invalid?
                sendBroadcast(new Intent(UPGRADE));
            } catch (FileNotFoundException e) { // APIKEY invalid?
                sendBroadcast(new Intent(UPGRADE));
            }
        }
    };

    public class Publish extends Job<String> {

        private static final String COULD_NOT_BE_DELETED = "could not be deleted";
        private static final String PLEASE_LOGIN = "please login";
        private static final String LOGIN_REQUIRED = "login required";
        private static final String AUTH = "auth";
        private static final String PASSWORD = "password";

        public Publish(Context ctx) {
            super(ctx, worker, reporter, RidesProvider.getPublishJobsUri(getContext()));
        }

        @Override
        void work(Cursor job) throws Exception {
            String ref = null;
            ContentValues values = new ContentValues();
            Ride offer = new Ride(job, getContext());
            progress(offer.toString(), job.getShort(COLUMNS.DIRTY));
            try {
                switch (job.getShort(COLUMNS.DIRTY)) {
                case Ride.FLAG_FOR_CREATE:
                    String tmpRef = offer.getRef();
                    offer.ref(null); // rm tmp ref
                    ref = fahrgemeinschaft.publish(offer);
                    values.put(Ride.REF, ref);
                    getContentResolver().update(
                            RidesProvider.getRidesUri(getContext()).buildUpon().appendPath(tmpRef).build(), values,
                            null, null);
                    offer.ref(ref);
                    flagClean(offer);
                    success(offer.toString(), Ride.FLAG_CLEAN);
                    break;
                case Ride.FLAG_FOR_UPDATE:
                    fahrgemeinschaft.publish(offer);
                    flagClean(offer);
                    success(offer.toString(), Ride.FLAG_CLEAN);
                    break;
                case Ride.FLAG_FOR_DELETE:
                    if (fahrgemeinschaft.delete(offer) != null) {
                        values.put(Ride.DIRTY, Ride.FLAG_DELETED);
                        ref = job.getString(COLUMNS.REF);
                        getContentResolver().update(
                                RidesProvider.getRidesUri(getContext()).buildUpon().appendPath(ref).build(), values,
                                null, null);
                        success(offer.toString(), Ride.FLAG_DELETED);
                    }
                    break;
                }
            } catch (FileNotFoundException e) {
                Log.e(TAG, e.getClass().getName());
                if (job.getShort(COLUMNS.DIRTY) == Ride.FLAG_FOR_DELETE) {
                    values.put(Ride.DIRTY, Ride.FLAG_DELETED);
                    ref = job.getString(COLUMNS.REF);
                    getContentResolver().update(
                            RidesProvider.getRidesUri(getContext()).buildUpon().appendPath(ref).build(), values,
                            null, null);
                    Log.e(TAG, COULD_NOT_BE_DELETED);
                    fail(offer.toString(), COULD_NOT_BE_DELETED);
                } else
                    handleAuth();
            } catch (JSONException e) {
                Log.e(TAG, e.getClass().getName());
                handleAuth();
            }
        }

        private void flagClean(Ride offer) {
            ContentValues r = new ContentValues();
            r.put(Ride.DIRTY, Ride.FLAG_CLEAN);
            getContentResolver().update(RidesProvider.getRidesUri(getContext()).buildUpon()
                    .appendPath(offer.getRef()).appendQueryParameter("id", String.valueOf(offer.getId())).build(),
                    r, null, null);
        }

        public void handleAuth() {
            if (PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean("remember_password",
                    false)) {
                log("logging in automatically..");
                authenticate(PreferenceManager.getDefaultSharedPreferences(getContext()).getString(PASSWORD, ""));
            } else {
                if (!authenticating) {
                    sendBroadcast(new Intent(AUTH));
                    Toast.makeText(getContext(), PLEASE_LOGIN, Toast.LENGTH_LONG).show();
                    fail(PUBLISH, LOGIN_REQUIRED);
                }
            }
        }

        @Override
        protected void success(String what, int number) {
            if (what != null) {
                worker.post(this);
            } else {
                worker.post(myrides);
            }
            super.success(what, number);
        }
    };

    public void authenticate(final String credential) {
        worker.postAtFrontOfQueue(new Job<String>(getContext(), worker, reporter, null) {

            @Override
            void work(Cursor job) throws Exception {
                authenticating = true;
                progress(AUTH, 0);
                try {
                    final String a = fahrgemeinschaft.authenticate(credential);
                    PreferenceManager.getDefaultSharedPreferences(getContext()).edit().putString(AUTH, a).commit();
                    success(AUTH, 0);
                    worker.post(publish);
                } catch (AuthException e) {
                    fail(AUTH, WRONG_LOGIN_OR_PASSWORD);
                } catch (FileNotFoundException e) {
                    fail(AUTH, WRONG_LOGIN_OR_PASSWORD);
                } finally {
                    authenticating = false;
                }
            }
        }.register(authCallback).setVerbose(
                PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean(VERBOSE, false)));
    }

    private Context getContext() {
        return ConnectorService.this;
    }

    public ServiceCallback<String> authCallback;

    public interface ServiceCallback<T> {
        public void onFail(T what, String reason);

        public void onSuccess(T what, int number);

        public void onProgress(T what, int how);
    }

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

    public class Bind extends Binder {
        public ConnectorService getService() {
            return ConnectorService.this;
        }
    }

    @Override
    public void unbindService(ServiceConnection conn) {
        super.unbindService(conn);
        authCallback = null;
        search.register(null);
        publish.register(null);
        myrides.register(null);
    }

    public void cleanUp(SharedPreferences prefs) {
        long older_than = System.currentTimeMillis() - prefs.getLong(CLEANUP_INTERVAL, 21 * 24 * 3600000); // 3 weeks
        if (prefs.getLong(LAST_CLEANUP, 0) < older_than) {
            getContentResolver().delete(
                    RidesProvider.getRidesUri(getContext()).buildUpon()
                            .appendQueryParameter(RidesProvider.OLDER_THAN, String.valueOf(older_than)).build(),
                    null, null);
            prefs.edit().putLong(LAST_CLEANUP, System.currentTimeMillis()).commit();
        }
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
        if (key.equals(VERBOSE)) {
            boolean verbose = prefs.getBoolean(VERBOSE, false);
            resolve.setVerbose(verbose);
            search.setVerbose(verbose);
            publish.setVerbose(verbose);
            myrides.setVerbose(verbose);
        }
    }
}