it.gulch.linuxday.android.services.AlarmIntentService.java Source code

Java tutorial

Introduction

Here is the source code for it.gulch.linuxday.android.services.AlarmIntentService.java

Source

/*
 * Copyright 2014 Christophe Beyls
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package it.gulch.linuxday.android.services;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.Typeface;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.app.TaskStackBuilder;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.util.Log;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.sql.SQLException;
import java.util.Date;
import java.util.List;

import it.gulch.linuxday.android.R;
import it.gulch.linuxday.android.activities.EventDetailsActivity;
import it.gulch.linuxday.android.activities.MainActivity;
import it.gulch.linuxday.android.constants.ActionConstants;
import it.gulch.linuxday.android.constants.ExtraConstants;
import it.gulch.linuxday.android.db.manager.BookmarkManager;
import it.gulch.linuxday.android.db.manager.EventManager;
import it.gulch.linuxday.android.db.manager.impl.BookmarkManagerImpl;
import it.gulch.linuxday.android.db.manager.impl.DatabaseManagerFactory;
import it.gulch.linuxday.android.db.manager.impl.EventManagerImpl;
import it.gulch.linuxday.android.fragments.SettingsFragment;
import it.gulch.linuxday.android.model.db.Bookmark;
import it.gulch.linuxday.android.model.db.Event;
import it.gulch.linuxday.android.receivers.AlarmReceiver;

/**
 * A service to schedule or unschedule alarms in the background, keeping the app responsive.
 *
 * @author Christophe Beyls
 */
public class AlarmIntentService extends IntentService {
    private static final String TAG = AlarmIntentService.class.getSimpleName();

    public static final String ACTION_UPDATE_ALARMS = "it.gulch.linuxday.android.action.UPDATE_ALARMS";

    public static final String ACTION_DISABLE_ALARMS = "it.gulch.linuxday.android.action.DISABLE_ALARMS";

    public static final String EXTRA_WITH_WAKE_LOCK = "with_wake_lock";

    private AlarmManager alarmManager;

    private BookmarkManager bookmarkManager;

    private EventManager eventManager;

    public AlarmIntentService() {
        super("AlarmIntentService");
    }

    @Override
    public void onCreate() {
        super.onCreate();

        setupServices();

        // Ask for the last unhandled intents to be redelivered if the service dies early.
        // This ensures we handle all events, in order.
        setIntentRedelivery(true);

        alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    }

    private void setupServices() {
        try {
            eventManager = DatabaseManagerFactory.getEventManager(this);
            bookmarkManager = DatabaseManagerFactory.getBookmarkManager(this);
        } catch (SQLException e) {
            Log.e(TAG, e.getMessage(), e);
        }
    }

    private PendingIntent getAlarmPendingIntent(long eventId) {
        Intent intent = new Intent(this, AlarmReceiver.class);
        intent.setAction(AlarmReceiver.ACTION_NOTIFY_EVENT);
        intent.setData(Uri.parse(String.valueOf(eventId)));

        return PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        String action = intent.getAction();

        switch (action) {
        case ACTION_UPDATE_ALARMS:
            updateAlarms(intent);
            break;
        case ACTION_DISABLE_ALARMS:
            disableAlarms();
            break;
        case ActionConstants.ACTION_ADD_BOOKMARK:
            addBookmark(intent);
            break;
        case ActionConstants.ACTION_REMOVE_BOOKMARKS:
            removeBookmarks(intent);
            break;
        case AlarmReceiver.ACTION_NOTIFY_EVENT:
            notifyEvent(intent);
            AlarmReceiver.completeWakefulIntent(intent);
            break;
        }

        // TODO: ELSE?
    }

    private void updateAlarms(Intent intent) {
        // Create/update all alarms
        try {
            long delay = getDelay();
            Date nowDate = new Date();
            long now = nowDate.getTime();

            List<Bookmark> bookmarks = bookmarkManager.getBookmarks(nowDate);
            for (Bookmark bookmark : bookmarks) {
                Event event = bookmark.getEvent();
                long notificationTime = event.getStartDate().getTime() - delay;

                PendingIntent pendingIntent = getAlarmPendingIntent(event.getId());
                if (notificationTime < now) {
                    // Cancel pending alarms that where scheduled between now and delay, if any
                    alarmManager.cancel(pendingIntent);
                } else {
                    alarmManager.set(AlarmManager.RTC_WAKEUP, notificationTime, pendingIntent);
                }
            }

            // Release the wake lock setup by AlarmReceiver, if any
            if (intent.getBooleanExtra(EXTRA_WITH_WAKE_LOCK, false)) {
                AlarmReceiver.completeWakefulIntent(intent);
            }
        } catch (SQLException e) {
            Log.e(TAG, e.getMessage(), e);
        }
    }

    private void disableAlarms() {
        // Cancel alarms of every bookmark in the future
        try {
            List<Bookmark> bookmarks = bookmarkManager.getBookmarks(new Date());
            for (Bookmark bookmark : bookmarks) {
                long eventId = bookmark.getEvent().getId();
                alarmManager.cancel(getAlarmPendingIntent(eventId));
            }
        } catch (SQLException e) {
            Log.e(TAG, e.getMessage(), e);
        }
    }

    private void addBookmark(Intent intent) {
        long delay = getDelay();
        long eventId = intent.getLongExtra(ExtraConstants.EXTRA_EVENT_ID, -1L);
        long startTime = intent.getLongExtra(ExtraConstants.EXTRA_EVENT_START_TIME, -1L);

        // Only schedule future events. If they start before the delay, the alarm will go off immediately
        if ((startTime == -1L) || (startTime < System.currentTimeMillis())) {
            return;
        }

        alarmManager.set(AlarmManager.RTC_WAKEUP, startTime - delay, getAlarmPendingIntent(eventId));
    }

    private void removeBookmarks(Intent intent) {
        // Cancel matching alarms, might they exist or not
        long[] eventIds = intent.getLongArrayExtra(ExtraConstants.EXTRA_EVENT_IDS);
        for (long eventId : eventIds) {
            alarmManager.cancel(getAlarmPendingIntent(eventId));
        }
    }

    private void notifyEvent(Intent intent) {
        long eventId = Long.parseLong(intent.getDataString());
        Event event = eventManager.get(eventId);
        if (event == null) {
            return;
        }

        //      NotificationManager notificationManager = (NotificationManager) getSystemService(Context
        // .NOTIFICATION_SERVICE);

        //      PendingIntent eventPendingIntent =
        //            TaskStackBuilder.create(this).addNextIntent(new Intent(this,
        // MainActivity.class)).addNextIntent(
        //                  new Intent(this, EventDetailsActivity.class).setData(Uri.parse(String.valueOf(event
        // .getId()))))
        //                  .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
        PendingIntent eventPendingIntent = TaskStackBuilder.create(this)
                .addNextIntent(new Intent(this, MainActivity.class))
                .addNextIntent(new Intent(this, EventDetailsActivity.class)
                        .setData(Uri.parse(String.valueOf(event.getId()))))
                .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

        int defaultFlags = Notification.DEFAULT_SOUND;
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        if (sharedPreferences.getBoolean(SettingsFragment.KEY_PREF_NOTIFICATIONS_VIBRATE, false)) {
            defaultFlags |= Notification.DEFAULT_VIBRATE;
        }

        String trackName = event.getTrack().getTitle();
        CharSequence bigText;
        String contentText;
        if (CollectionUtils.isEmpty(event.getPeople())) {
            contentText = trackName;
            bigText = event.getSubtitle();
        } else {
            String personsSummary = StringUtils.join(event.getPeople(), ", ");
            contentText = String.format("%1$s - %2$s", trackName, personsSummary);
            String subTitle = event.getSubtitle();

            SpannableString spannableBigText;
            if (TextUtils.isEmpty(subTitle)) {
                spannableBigText = new SpannableString(personsSummary);
            } else {
                spannableBigText = new SpannableString(String.format("%1$s\n%2$s", subTitle, personsSummary));
            }

            // Set the persons summary in italic
            spannableBigText.setSpan(new StyleSpan(Typeface.ITALIC),
                    +spannableBigText.length() - personsSummary.length(), spannableBigText.length(),
                    +Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            bigText = spannableBigText;
        }

        String roomName = event.getTrack().getRoom().getName();
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.ic_launcher).setWhen(event.getStartDate().getTime())
                .setContentTitle(event.getTitle()).setContentText(contentText)
                .setStyle(new NotificationCompat.BigTextStyle().bigText(bigText).setSummaryText(trackName))
                .setContentInfo(roomName).setContentIntent(eventPendingIntent).setAutoCancel(true)
                .setDefaults(defaultFlags).setPriority(NotificationCompat.PRIORITY_HIGH);

        // Blink the LED with FOSDEM color if enabled in the options
        if (sharedPreferences.getBoolean(SettingsFragment.KEY_PREF_NOTIFICATIONS_LED, false)) {
            notificationBuilder.setLights(getResources().getColor(R.color.maincolor), 1000, 5000);
        }

        /*// Android Wear extensions
        NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender();
            
        // Add an optional action button to show the room map image
        int roomImageResId = getResources()
        .getIdentifier(StringUtils.roomNameToResourceName(roomName), "drawable", getPackageName());
        if(roomImageResId != 0) {
           // The room name is the unique Id of a RoomImageDialogActivity
           Intent mapIntent = new Intent(this, RoomImageDialogActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
           .setData(Uri.parse(roomName));
           mapIntent.putExtra(RoomImageDialogActivity.EXTRA_ROOM_NAME, roomName);
           mapIntent.putExtra(RoomImageDialogActivity.EXTRA_ROOM_IMAGE_RESOURCE_ID, roomImageResId);
           PendingIntent mapPendingIntent =
           PendingIntent.getActivity(this, 0, mapIntent, PendingIntent.FLAG_UPDATE_CURRENT);
           CharSequence mapTitle = getString(R.string.room_map);
           notificationBuilder
           .addAction(new NotificationCompat.Action(R.drawable.ic_action_place, mapTitle, mapPendingIntent));
           // Use bigger action icon for wearable notification
           wearableExtender.addAction(
           new NotificationCompat.Action(R.drawable.ic_place_white_wear, mapTitle, mapPendingIntent));
        }
            
        notificationBuilder.extend(wearableExtender);*/

        NotificationManagerCompat.from(this).notify((int) eventId, notificationBuilder.build());
    }

    private long getDelay() {
        String delayString = PreferenceManager.getDefaultSharedPreferences(this)
                .getString(SettingsFragment.KEY_PREF_NOTIFICATIONS_DELAY, "0");
        // Convert from minutes to milliseconds
        return Long.parseLong(delayString) * 1000L * 60L;
    }
}