/**
* Copyright (C) 2013 Johannes Schnatterer
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This file is part of nusic.
*
* nusic 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.
*
* nusic 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 nusic. If not, see <http://www.gnu.org/licenses/>.
*/
package info.schnatterer.nusic.android.service;
import info.schnatterer.nusic.Constants;
import info.schnatterer.nusic.android.activities.MainActivity;
import info.schnatterer.nusic.android.util.ImageUtil;
import info.schnatterer.nusic.android.util.Notification;
import info.schnatterer.nusic.android.util.Notification.NotificationId;
import info.schnatterer.nusic.core.PreferencesService;
import info.schnatterer.nusic.core.ReleaseService;
import info.schnatterer.nusic.core.ServiceException;
import info.schnatterer.nusic.data.DatabaseException;
import info.schnatterer.nusic.data.dao.ArtworkDao;
import info.schnatterer.nusic.data.dao.ArtworkDao.ArtworkType;
import info.schnatterer.nusic.data.model.Release;
import info.schnatterer.nusic.ui.R;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import roboguice.receiver.RoboBroadcastReceiver;
import roboguice.service.RoboService;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
/**
* Service that checks if an album is released today.
*
* @author schnatterer
*
*/
public class ReleasedTodayService extends RoboService {
/** Small icon shown in the status bar when a notification is shwown. */
private static final int NOTIFICATION_SMALL_ICON = R.drawable.ic_album_white_24dp;
private static final Logger LOG = LoggerFactory
.getLogger(ReleasedTodayService.class);
@Inject
private PreferencesService preferencesService;
@Inject
private ReleaseService releaseService;
@Inject
private ArtworkDao artworkDao;
private ReleasedTodayServiceBinder binder = new ReleasedTodayServiceBinder();
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (preferencesService.isEnabledNotifyReleasedToday()) {
try {
List<Release> releasedToday = releaseService
.findReleasedToday();
if (releasedToday.size() == 1) {
notifyReleaseToday(releasedToday.get(0));
} else if (releasedToday.size() > 1) {
// If more than one release, put less detail in notification
notifyReleaseToday(releasedToday.size());
}
} catch (ServiceException e) {
Notification
.notifyWarning(
this,
getString(R.string.ReleasedTodayService_ReleasedTodayError),
e.getLocalizedMessage());
}
} else {
// Stop schedule
stopSchedule(this);
}
// Avoid this service from being started all over again
stopSelf();
return Service.START_NOT_STICKY;
}
/**
* Puts out a notification informing about one release published today.<br/>
* <br/>
* <br/>
* Future calls overwrite any previous instances of this notification still
* on display.
*
* @param release
*
*/
private void notifyReleaseToday(Release release) {
try {
Bitmap createScaledBitmap = ImageUtil.createScaledBitmap(
artworkDao.findStreamByRelease(release, ArtworkType.SMALL),
this);
Notification.notify(
this,
NotificationId.RELEASED_TODAY,
getString(R.string.ReleasedTodayService_ReleasedToday),
release.getArtist().getArtistName() + " - "
+ release.getReleaseName(),
NOTIFICATION_SMALL_ICON, createScaledBitmap,
MainActivity.class, createExtraActiveTab());
} catch (DatabaseException e) {
LOG.warn("Unable to load artwork for notification. " + release, e);
} catch (IllegalArgumentException e) {
LOG.warn("Unable scale artwork for notification. " + release, e);
}
}
/**
* Puts out a notification informing about multiple releases published
* today.<br/>
* <br/>
* Future calls overwrite any previous instances of this notification still
* on display.
*
* @param nReleases
* the number of releases published today
*/
private void notifyReleaseToday(int nReleases) {
Notification.notify(this, NotificationId.RELEASED_TODAY, String.format(
getString(R.string.ReleasedTodayService_ReleasedTodayMultiple),
nReleases), null, NOTIFICATION_SMALL_ICON, null,
MainActivity.class, createExtraActiveTab());
}
/**
* Creates an extra bundle that contains the tab to be shown when
* {@link MainActivity} is launched.
*
* @return
*/
private Bundle createExtraActiveTab() {
Bundle extras = new Bundle();
extras.putSerializable(MainActivity.EXTRA_ACTIVE_TAB,
MainActivity.TabDefinition.AVAILABLE);
return extras;
}
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class ReleasedTodayServiceBinder extends Binder {
public ReleasedTodayService getService() {
return ReleasedTodayService.this;
}
}
/**
* Creates the pending intent that is passed to the alarm manager for
* scheduling the service.
*
* @param context
* @return
*/
private static PendingIntent createPendingIntent(Context context) {
PendingIntent pintent = PendingIntent.getBroadcast(context,
Constants.Alarms.RELEASED_TODAY.ordinal(), new Intent(context,
ReleasedTodayServiceStarterReceiver.class),
PendingIntent.FLAG_UPDATE_CURRENT);
return pintent;
}
/**
* Cancel this task so it is not scheduled anymore.
*
* @param context
*/
public void stopSchedule(Context context) {
((AlarmManager) getSystemService(ALARM_SERVICE))
.cancel(createPendingIntent(context));
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
/**
* Broadcast receiver that triggers execution of
* {@link ReleasedTodayService}.
*
* @author schnatterer
*
*/
public static class ReleasedTodayServiceStarterReceiver extends
RoboBroadcastReceiver {
@Override
public void handleReceive(final Context context, final Intent intent) {
context.startService(new Intent(context, ReleasedTodayService.class));
}
}
/**
* Broadcast receiver that schedules execution of
* {@link ReleasedTodayService}.
*
* @author schnatterer
*
*/
public static class ReleasedTodaySchedulerReceiver extends
RoboBroadcastReceiver {
@Inject
ReleasedTodayServiceScheduler releasedTodayServiceScheduler;
@Override
public void handleReceive(final Context context, final Intent intent) {
releasedTodayServiceScheduler.schedule();
}
}
public static class ReleasedTodayServiceScheduler {
@Inject
private Context context;
@Inject
private PreferencesService preferencesService;
/**
* Schedule this task to run regularly, if enabled in the preferences.
*
* @param context
*/
public void schedule() {
boolean isEnabled = preferencesService
.isEnabledNotifyReleasedToday();
if (isEnabled) {
int hourOfDay = preferencesService
.getReleasedTodayScheduleHourOfDay();
int minute = preferencesService
.getReleasedTodayScheduleMinute();
Calendar triggerAtCal = Calendar.getInstance();
triggerAtCal.set(Calendar.HOUR_OF_DAY, hourOfDay);
triggerAtCal.set(Calendar.MINUTE, minute);
triggerAtCal.set(Calendar.SECOND, 0);
if (triggerAtCal.getTimeInMillis() < System.currentTimeMillis()) {
/*
* Trigger only for today if time is in the future. If not,
* trigger same time tomorrow.
*
* If the triggering time is in the past, android will
* trigger it directly.
*/
LOG.debug("Triggering notification service for tommorrow");
triggerAtCal.add(Calendar.DAY_OF_MONTH, 1);
}
/*
* Set a repeating schedule, so there always is a next alarm
* even when one alarm should fail for some reason
*/
((AlarmManager) context.getSystemService(Context.ALARM_SERVICE))
.setRepeating(AlarmManager.RTC,
triggerAtCal.getTimeInMillis(),
AlarmManager.INTERVAL_DAY,
createPendingIntent(context));
LOG.debug("Scheduled "
+ ReleasedTodayService.class.getSimpleName()
+ " to run again every day, starting at "
+ new Date(+triggerAtCal.getTimeInMillis()));
}
}
}
}