be.ac.ucl.lfsab1509.llncampus.services.AlarmService.java Source code

Java tutorial

Introduction

Here is the source code for be.ac.ucl.lfsab1509.llncampus.services.AlarmService.java

Source

package be.ac.ucl.lfsab1509.llncampus.services;

import be.ac.ucl.lfsab1509.llncampus.Coordinates;
import be.ac.ucl.lfsab1509.llncampus.Course;
import be.ac.ucl.lfsab1509.llncampus.Event;
import be.ac.ucl.lfsab1509.llncampus.LLNCampus;
import be.ac.ucl.lfsab1509.llncampus.R;
import be.ac.ucl.lfsab1509.llncampus.activity.SettingsActivity;
import be.ac.ucl.lfsab1509.llncampus.external.SecurePreferences;
import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.CursorIndexOutOfBoundsException;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.text.format.Time;
import android.util.Log;
import android.widget.Toast;

/**
 * LLNCampus. A application for students at the UCL (Belgium).
Copyright (C) 2013 Benjamin Baugnies, Quentin De Coninck, Ahn Tuan Le Pham and Damien Mercier
Copyright (C) 2014 Quentin De Coninck
    
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 3 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/>.
 */

/**
 * Class intended to create notifications for courses.
 */
public class AlarmService extends Service {

    /** Default time to notify before the event begins, in minutes. */
    private static final int DEFAULT_NOTIFY_MINUTE = 15;
    /** Default maximal distance of the event to notify, in meters. */
    private static final int DEFAULT_MAX_DISTANCE = 5000;
    /** Default speed of the user to compute time to reach the destination, in km/h. */
    private static final int DEFAULT_NOTIFY_SPEED = 5;
    /** Default time added to the computation of time to notify when user position is used. */
    private static final int DEFAULT_MORE_TIME = 5;
    /** Minimal distance of the event to notify, in meters. */
    private static final double MIN_DISTANCE = 30;
    /** Time period between potential notifications, in milliseconds. */
    private static final long TIME_TO_REPEAT = 60000;

    /** The next event that will occur. */
    private static Event nextEvent = null;

    private final IBinder mBinder = new LocalBinder();
    private AlarmManager am;

    @Override
    public void onCreate() {
        super.onCreate();
        // Define the Context here, otherwise no Context before launching the application.
        LLNCampus.setContext(getApplicationContext());
        am = (AlarmManager) LLNCampus.getContext().getSystemService(Context.ALARM_SERVICE);
        Intent i = new Intent(LLNCampus.getContext(), AlarmService.class);
        PendingIntent pi = PendingIntent.getBroadcast(LLNCampus.getContext(), 0, i, 0);
        am.set(AlarmManager.ELAPSED_REALTIME, TIME_TO_REPEAT, pi);
    }

    /**
     * Check if an event will occur and send a alert if needed.
     */
    private void checkAlarm() {
        SharedPreferences preferences = new SecurePreferences(this);
        if (!preferences.getBoolean(SettingsActivity.COURSES_NOTIFY, false)) {
            Log.d("AlarmService", "Course notifications are disabled.");
            return;
        }

        if (Course.getList().size() != 0 && nextEvent == null) {
            loadNextEvent();
        }

        if (nextEvent != null) // If there is a next event, check if an alert should be sent.
        {

            int nbMin = LLNCampus.getIntPreference(SettingsActivity.NOTIFY_MINUTE, DEFAULT_NOTIFY_MINUTE, this);

            // Added computations if notifications depend on the position of the user.
            if (preferences.getBoolean(SettingsActivity.NOTIFY_WITH_GPS, false)) {
                Coordinates eventCoord = nextEvent.getCoordinates();
                if (eventCoord != null) {
                    Coordinates currentCoord = LLNCampus.getGPS().getPosition();
                    if (currentCoord != null) {
                        double dist = eventCoord.getDistance(currentCoord);
                        if (dist > MIN_DISTANCE
                                && dist < LLNCampus.getIntPreference(SettingsActivity.NOTIFY_MAX_DISTANCE,
                                        DEFAULT_MAX_DISTANCE, this)) {
                            int speedInKmPerHour = LLNCampus.getIntPreference(SettingsActivity.NOTIFY_SPEED_MOVE,
                                    DEFAULT_NOTIFY_SPEED, this);
                            int speedMetersPerMinute = speedInKmPerHour * 1000 / 60;
                            nbMin = (int) (dist / speedMetersPerMinute) + LLNCampus
                                    .getIntPreference(SettingsActivity.NOTIFY_MORE_TIME, DEFAULT_MORE_TIME, this);
                        }
                    }
                }
            }
            Time currentTime = new Time();
            currentTime.setToNow();

            // Check if a notification should be sent.
            if (Course.getList().size() != 0 && nextEvent.getBeginTime().toMillis(false) - nbMin * 60L * 1000L
                    - currentTime.toMillis(false) < 0L) {
                sendAlert(nextEvent);
                loadNextEvent();
            }
        }
    }

    /**
     * Set to null the next event.
     */
    private static void resetNextEvent() {
        nextEvent = null;
    }

    /**
     * Load the next event in the nextEvent variable.
     */
    private static void loadNextEvent() {
        Log.d("AlarmService", "nextEvent update");
        long precTime;
        if (nextEvent == null) {
            Time currentDate = new Time();
            currentDate.setToNow();
            Log.d("AlarmService", "currentDate.toMilis= " + currentDate.toMillis(false));
            precTime = currentDate.toMillis(false);
        } else {
            precTime = nextEvent.getBeginTime().toMillis(false);
        }
        Cursor c = LLNCampus.getDatabase()
                .sqlRawQuery("SELECT h.TIME_BEGIN, h.TIME_END, h.COURSE, h.ROOM, c.NAME "
                        + "FROM Horaire as h, Courses as c " + "WHERE h.COURSE = c.CODE AND TIME_BEGIN > "
                        + precTime + " " + "ORDER BY TIME_BEGIN ASC LIMIT 1");
        c.moveToFirst();
        try {
            nextEvent = new Event(c.getLong(0), c.getLong(1));
            nextEvent.addDetail(Event.COURSE, c.getString(2));
            nextEvent.addDetail(Event.ROOM, c.getString(3));
            nextEvent.addDetail(Event.TITLE, c.getString(4));
            c.close();
        } catch (CursorIndexOutOfBoundsException e) // No event yet.
        {
            resetNextEvent();
        }
    }

    /**
     * Send an alert to the user for the event e.
     * 
     * @param e
     *          Event to notify to the user.
     */
    private void sendAlert(Event e) {
        final NotificationManager notificationManager = (NotificationManager) getSystemService(
                Context.NOTIFICATION_SERVICE);
        Time currentTime = new Time();
        currentTime.setToNow();

        long nbMin = e.getBeginTime().toMillis(false) / 60L / 1000L - currentTime.toMillis(false) / 60L / 1000L;

        final NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(android.R.drawable.ic_dialog_alert)
                .setContentTitle(e.getDetail(Event.COURSE) + " " + getString(R.string.begins_in) + " " + nbMin + " "
                        + getString(R.string.minutes))
                .setContentText(e.getDetail(Event.ROOM) + " - " + e.getDetail(Event.TITLE)).setAutoCancel(true);
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
        String ringtone = preferences.getString(SettingsActivity.NOTIFY_RINGTONE, null);
        if (ringtone == null) {
            notificationBuilder.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
        } else {
            notificationBuilder.setSound(Uri.parse(ringtone));
        }
        notificationManager.notify(1, notificationBuilder.build());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        android.util.Log.i("LocalService", "Received start id " + startId + ": " + intent);
        checkAlarm();
        return START_REDELIVER_INTENT;
    }

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

    @Override
    public void onDestroy() {
        // Tell the user we stopped.
        Toast.makeText(this, getString(R.string.notification_stop), Toast.LENGTH_SHORT).show();
    }

    /**
     * A class to offer remote procedures for the AlarmService.
     */
    public class LocalBinder extends Binder {

        /**
         * Get the AlarmService.
         * 
         * @return AlarmService. 
         */
        public AlarmService getService() {
            return AlarmService.this;
        }
    }
}