/*
* Copyright (C) 2014 Alex Korovyansky.
*/
package com.alexkorovyansky.wearpomodoro.helpers;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.PowerManager;
import android.util.Log;
import com.alexkorovyansky.wearpomodoro.BuildConfig;
import com.alexkorovyansky.wearpomodoro.R;
import com.alexkorovyansky.wearpomodoro.app.receivers.PomodoroAlarmReceiver;
import com.alexkorovyansky.wearpomodoro.app.receivers.PomodoroAlarmTickReceiver;
import com.alexkorovyansky.wearpomodoro.app.receivers.PomodoroControlReceiver;
import com.alexkorovyansky.wearpomodoro.app.services.PomodoroNotificationService;
import com.alexkorovyansky.wearpomodoro.model.ActivityType;
import java.util.Calendar;
import java.util.Date;
public class PomodoroMaster {
private static final int NOTIFICATION_ID = 1;
private final Context context;
private NotificationManager notificationManager;
private AlarmManager alarmManager;
private PowerManager powerManager;
private final PersistentStorage persistentStorage;
public PomodoroMaster(Context context, PersistentStorage persistentStorage) {
this.context = context;
this.notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
this.alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
this.powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
this.persistentStorage = persistentStorage;
}
public void check() {
long now = System.currentTimeMillis();
long last = persistentStorage.readLastEatenPomodoroTimestampMs();
if (!isTheSamePomodoroDay(last, now)) {
persistentStorage.writeEatenPomodoros(0);
}
}
public void start(ActivityType activityType) {
long now = System.currentTimeMillis();
long when = now + activityType.getLengthMs();
persistentStorage.writeWhenMs(when);
persistentStorage.writeActivityType(activityType);
scheduleAlarms(when);
syncNotification(activityType, when, isScreenOn());
startDisplayService();
}
public int getEatenPomodoros() {
return persistentStorage.readEatenPomodoros();
}
public boolean isActive() {
return persistentStorage.readActivityType() != ActivityType.NONE;
}
public void syncNotification() {
syncNotification(isScreenOn());
}
public void syncNotification(boolean isOn) {
syncNotification(persistentStorage.readActivityType(), persistentStorage.readWhenMs(), isOn);
}
public void cancelNotification() {
notificationManager.cancel(NOTIFICATION_ID);
}
public ActivityType stop() {
ActivityType stoppingForType = persistentStorage.readActivityType();
if (stoppingForType.isBreak()) {
persistentStorage.writeEatenPomodoros(persistentStorage.readEatenPomodoros() + 1);
persistentStorage.writeLastEatenPomodoroTimestampMs(System.currentTimeMillis());
}
persistentStorage.writeActivityType(ActivityType.NONE);
unscheduleAlarms();
cancelNotification();
stopDisplayService();
return stoppingForType;
}
private void scheduleAlarms(long whenMs) {
PendingIntent pendingAlarmIntent = createPendingIntentAlarmBroadcast(context);
alarmManager.setExact(AlarmManager.RTC_WAKEUP, whenMs, pendingAlarmIntent);
PendingIntent pendingAlarmTickIntent = createPendingIntentTickAlarmBroadcast(context);
long now = System.currentTimeMillis();
int oneMinuteMs = 20 * 1000;
int fiveSecondsMs = 20 * 1000;
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, now + fiveSecondsMs, oneMinuteMs, pendingAlarmTickIntent);
}
private void unscheduleAlarms() {
PendingIntent pendingAlarmIntent = createPendingIntentAlarmBroadcast(context);
alarmManager.cancel(pendingAlarmIntent);
PendingIntent pendingAlarmTickIntent = createPendingIntentTickAlarmBroadcast(context);
alarmManager.cancel(pendingAlarmTickIntent);
}
private void syncNotification(ActivityType activityType, long whenMs, boolean screenOn) {
if (activityType != ActivityType.NONE) {
notificationManager.notify(NOTIFICATION_ID,
createNotificationBuilderForActivityType(context, activityType, getEatenPomodoros(), whenMs, screenOn));
} else {
Log.e(BuildConfig.APPLICATION_ID, "ignore notify for activityType " + ActivityType.NONE);
}
}
private void startDisplayService() {
context.startService(new Intent(context, PomodoroNotificationService.class));
}
private void stopDisplayService() {
context.stopService(new Intent(context, PomodoroNotificationService.class));
}
public static String convertDiffToPrettyMinutesLeft(long diffMs) {
diffMs = Math.max(0, diffMs);
int secondsTotal = (int) diffMs / 1000;
int seconds = secondsTotal % 60;
int minutes = (secondsTotal - seconds) / 60;
if (minutes == 0) {
return "< 1 minute";
} else {
return String.format("%d minute%s", minutes, minutes > 1 ? "s" : "");
}
}
private static Notification createNotificationBuilderForActivityType(Context context, ActivityType activityType,
int eatenPomodors, long whenMs, boolean isScreenOn) {
Notification.Action stopAction = createStopAction(context);
Notification.WearableExtender wearableExtender = new Notification.WearableExtender()
.addAction(stopAction)
.setBackground(BitmapFactory.decodeResource(context.getResources(), backgroundResourceForActivityType(activityType)));
Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(R.drawable.ic_launcher)
.setDefaults(Notification.DEFAULT_ALL)
.setOnlyAlertOnce(true)
.setPriority(Notification.PRIORITY_MAX)
.setOngoing(true)
.setLocalOnly(true)
.setStyle(new Notification.BigPictureStyle())
.setContentTitle(titleForActivityType(activityType, eatenPomodors))
.extend(wearableExtender);
if (isScreenOn) {
builder.setUsesChronometer(true);
builder.setWhen(whenMs);
} else {
builder.setUsesChronometer(false);
builder.setContentText(convertDiffToPrettyMinutesLeft(whenMs - System.currentTimeMillis()));
}
return builder.build();
}
private static Notification.Action createStopAction(Context context) {
PendingIntent stopActionPendingIntent =
createPendingIntentControlBroadcast(context,PomodoroControlReceiver.COMMAND_STOP);
return new Notification.Action.Builder(
R.drawable.ic_stop,
context.getString(R.string.action_stop),
stopActionPendingIntent)
.build();
}
private static PendingIntent createPendingIntentControlBroadcast(Context context, int command) {
Intent stopActionIntent = new Intent(PomodoroControlReceiver.ACTION);
stopActionIntent.putExtra(PomodoroControlReceiver.EXTRA_COMMAND, command);
return PendingIntent.getBroadcast(context, 1, stopActionIntent, PendingIntent.FLAG_CANCEL_CURRENT);
}
private static PendingIntent createPendingIntentAlarmBroadcast(Context context) {
Intent intent = new Intent(PomodoroAlarmReceiver.ACTION);
return PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_CANCEL_CURRENT);
}
private static PendingIntent createPendingIntentTickAlarmBroadcast(Context context) {
Intent intent = new Intent(PomodoroAlarmTickReceiver.ACTION);
return PendingIntent.getBroadcast(context, 2, intent, PendingIntent.FLAG_CANCEL_CURRENT);
}
private static int backgroundResourceForActivityType(ActivityType activityType) {
switch (activityType) {
case LONG_BREAK:
return R.drawable.bg_long_break;
case POMODORO:
return R.drawable.bg_pomodoro;
case SHORT_BREAK:
return R.drawable.bg_short_break;
}
throw new IllegalStateException("unsupported activityType " + activityType);
}
private static String titleForActivityType(ActivityType activityType, int eatenPomodoros) {
switch (activityType) {
case LONG_BREAK:
return "Long Break";
case POMODORO:
return "Pomodoro #" + (eatenPomodoros + 1);
case SHORT_BREAK:
return "Short Break";
}
throw new IllegalStateException("unsupported activityType " + activityType);
}
private boolean isScreenOn() {
return powerManager.isInteractive();
}
private static boolean isTheSamePomodoroDay(long first, long second) {
Calendar cal1 = Calendar.getInstance();
cal1.setTime(new Date(first));
Calendar cal2 = Calendar.getInstance();
cal2.setTime(new Date(second));
boolean sameDay = cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR);
boolean isBothAfter6am = cal1.get(Calendar.HOUR_OF_DAY) > 6 &&
cal2.get(Calendar.HOUR_OF_DAY) > 6;
return sameDay && isBothAfter6am;
}
}