You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tasks/src/main/java/com/todoroo/astrid/reminders/ShowNotificationReceiver.java

286 lines
12 KiB
Java

package com.todoroo.astrid.reminders;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.media.AudioManager;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import android.telephony.TelephonyManager;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.utility.Flags;
import com.todoroo.astrid.voice.VoiceOutputAssistant;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tasks.R;
import org.tasks.injection.InjectingBroadcastReceiver;
import org.tasks.notifications.NotificationManager;
import org.tasks.preferences.Preferences;
import org.tasks.receivers.CompleteTaskReceiver;
import org.tasks.reminders.SnoozeActivity;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.inject.Inject;
import static com.todoroo.andlib.utility.AndroidUtilities.atLeastJellybean;
import static org.tasks.date.DateTimeUtils.currentTimeMillis;
/**
* Receives requests to show an Astrid notification if they were not intercepted and handled
* by the in-app reminders in AstridActivity.
*
* @author Sam
*/
public class ShowNotificationReceiver extends InjectingBroadcastReceiver {
private static final Logger log = LoggerFactory.getLogger(ShowNotificationReceiver.class);
private static ExecutorService singleThreadVoicePool = Executors.newSingleThreadExecutor();
private static long lastNotificationSound = 0L;
@Inject NotificationManager notificationManager;
@Inject Preferences preferences;
@Inject VoiceOutputAssistant voiceOutputAssistant;
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
showNotification(
context,
intent.getIntExtra(Notifications.EXTRAS_NOTIF_ID, 0),
intent.<Intent>getParcelableExtra(Notifications.EXTRAS_CUSTOM_INTENT),
intent.getIntExtra(Notifications.EXTRAS_TYPE, 0),
intent.getStringExtra(Notifications.EXTRAS_TITLE),
intent.getStringExtra(Notifications.EXTRAS_TEXT),
intent.getIntExtra(Notifications.EXTRAS_RING_TIMES, 1));
}
/**
* @return true if notification should sound
*/
private static boolean checkLastNotificationSound() {
long now = DateUtilities.now();
if (now - lastNotificationSound > 10000) {
lastNotificationSound = now;
return true;
}
return false;
}
/**
* @return whether we're in quiet hours
*/
static boolean isQuietHours(Preferences preferences) {
boolean quietHoursEnabled = preferences.getBoolean(R.string.p_rmd_enable_quiet, false);
if (quietHoursEnabled) {
long quietHoursStart = new DateTime().withMillisOfDay(preferences.getInt(R.string.p_rmd_quietStart)).getMillis();
long quietHoursEnd = new DateTime().withMillisOfDay(preferences.getInt(R.string.p_rmd_quietEnd)).getMillis();
long now = currentTimeMillis();
if (quietHoursStart <= quietHoursEnd) {
if (now >= quietHoursStart && now < quietHoursEnd) {
return true;
}
} else { // wrap across 24/hour boundary
if (now >= quietHoursStart || now < quietHoursEnd) {
return true;
}
}
}
return false;
}
/**
* Shows an Astrid notification. Pulls in ring tone and quiet hour settings
* from preferences. You can make it say anything you like.
*
* @param ringTimes number of times to ring (-1 = nonstop)
*/
private void showNotification(Context context, int notificationId, final Intent intent, int type, String title,
String text, int ringTimes) {
// don't ring multiple times if random reminder
if (type == ReminderService.TYPE_RANDOM) {
ringTimes = 1;
}
// quiet hours? unless alarm clock
boolean quietHours = !(type == ReminderService.TYPE_ALARM || type == ReminderService.TYPE_DUE) && isQuietHours(preferences);
PendingIntent pendingIntent = PendingIntent.getActivity(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.notif_astrid)
.setTicker(text)
.setWhen(System.currentTimeMillis())
.setContentTitle(title)
.setContentText(text)
.setContentIntent(pendingIntent);
if (atLeastJellybean()) {
final long taskId = intent.getLongExtra(TaskListActivity.OPEN_TASK, 0L);
PendingIntent completeIntent = PendingIntent.getBroadcast(context, notificationId, new Intent(context, CompleteTaskReceiver.class) {{
putExtra(CompleteTaskReceiver.TASK_ID, taskId);
}}, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent snoozePendingIntent = PendingIntent.getActivity(context, notificationId, new Intent(context, SnoozeActivity.class) {{
setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
putExtra(SnoozeActivity.TASK_ID, taskId);
}}, PendingIntent.FLAG_UPDATE_CURRENT);
builder.addAction(R.drawable.ic_action_tick, context.getResources().getString(R.string.rmd_NoA_done), completeIntent)
.addAction(R.drawable.ic_action_alarm, context.getResources().getString(R.string.rmd_NoA_snooze), snoozePendingIntent);
}
Notification notification = builder.build();
notification.flags |= Notification.FLAG_AUTO_CANCEL;
if (preferences.getBoolean(R.string.p_rmd_persistent, true)) {
notification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_SHOW_LIGHTS;
notification.ledOffMS = 5000;
notification.ledOnMS = 700;
notification.ledARGB = Color.YELLOW;
} else {
notification.defaults = Notification.DEFAULT_LIGHTS;
}
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
// detect call state
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
int callState = tm.getCallState();
boolean voiceReminder = preferences.getBoolean(R.string.p_voiceRemindersEnabled, false);
// if multi-ring is activated and the setting p_rmd_maxvolume allows it, set up the flags for insistent
// notification, and increase the volume to full volume, so the user
// will actually pay attention to the alarm
boolean maxOutVolumeForMultipleRingReminders = preferences.getBoolean(R.string.p_rmd_maxvolume, true);
// remember it to set it to the old value after the alarm
int previousAlarmVolume = audioManager.getStreamVolume(AudioManager.STREAM_ALARM);
if (ringTimes != 1 && (type != ReminderService.TYPE_RANDOM)) {
notification.audioStreamType = AudioManager.STREAM_ALARM;
if (maxOutVolumeForMultipleRingReminders) {
audioManager.setStreamVolume(AudioManager.STREAM_ALARM,
audioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM), 0);
}
// insistent rings until notification is disabled
if (ringTimes < 0) {
notification.flags |= Notification.FLAG_INSISTENT;
voiceReminder = false;
}
} else {
notification.audioStreamType = AudioManager.STREAM_NOTIFICATION;
}
boolean soundIntervalOk = checkLastNotificationSound();
// quiet hours = no sound
if (quietHours || callState != TelephonyManager.CALL_STATE_IDLE) {
notification.sound = null;
voiceReminder = false;
} else {
String notificationPreference = preferences.getStringValue(R.string.p_rmd_ringtone);
if (audioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION) == 0) {
notification.sound = null;
voiceReminder = false;
} else if (notificationPreference != null) {
if (notificationPreference.length() > 0 && soundIntervalOk) {
notification.sound = Uri.parse(notificationPreference);
} else {
notification.sound = null;
}
} else if (soundIntervalOk) {
notification.defaults |= Notification.DEFAULT_SOUND;
}
}
// quiet hours && ! due date or snooze = no vibrate
if (quietHours && !(type == ReminderService.TYPE_DUE || type == ReminderService.TYPE_SNOOZE)) {
notification.vibrate = null;
} else if (callState != TelephonyManager.CALL_STATE_IDLE) {
notification.vibrate = null;
} else {
if (preferences.getBoolean(R.string.p_rmd_vibrate, true)
&& audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION) && soundIntervalOk) {
notification.vibrate = new long[]{0, 1000, 500, 1000, 500, 1000};
} else {
notification.vibrate = null;
}
}
singleThreadVoicePool.submit(new NotificationRunnable(ringTimes, notificationId, notification, voiceReminder,
maxOutVolumeForMultipleRingReminders, audioManager, previousAlarmVolume, text,
notificationManager, voiceOutputAssistant));
}
private static class NotificationRunnable implements Runnable {
private final int ringTimes;
private final int notificationId;
private final Notification notification;
private final boolean voiceReminder;
private final boolean maxOutVolumeForMultipleRingReminders;
private final AudioManager audioManager;
private final int previousAlarmVolume;
private final String text;
private NotificationManager notificationManager;
private final VoiceOutputAssistant voiceOutputAssistant;
public NotificationRunnable(int ringTimes, int notificationId, Notification notification,
boolean voiceReminder, boolean maxOutVolume,
AudioManager audioManager, int previousAlarmVolume,
String text, NotificationManager notificationManager,
VoiceOutputAssistant voiceOutputAssistant) {
this.ringTimes = ringTimes;
this.notificationId = notificationId;
this.notification = notification;
this.voiceReminder = voiceReminder;
this.maxOutVolumeForMultipleRingReminders = maxOutVolume;
this.audioManager = audioManager;
this.previousAlarmVolume = previousAlarmVolume;
this.text = text;
this.notificationManager = notificationManager;
this.voiceOutputAssistant = voiceOutputAssistant;
}
@Override
public void run() {
for (int i = 0; i < Math.max(ringTimes, 1); i++) {
notificationManager.notify(notificationId, notification);
AndroidUtilities.sleepDeep(500);
}
Flags.set(Flags.REFRESH); // Forces a reload when app launches
if (voiceReminder || maxOutVolumeForMultipleRingReminders) {
AndroidUtilities.sleepDeep(2000);
for (int i = 0; i < 50; i++) {
AndroidUtilities.sleepDeep(500);
if (audioManager.getMode() != AudioManager.MODE_RINGTONE) {
break;
}
}
try {
// first reset the Alarm-volume to the value before it was eventually maxed out
if (maxOutVolumeForMultipleRingReminders) {
audioManager.setStreamVolume(AudioManager.STREAM_ALARM, previousAlarmVolume, 0);
}
if (voiceReminder) {
voiceOutputAssistant.speak(text);
}
} catch (VerifyError e) {
// unavailable
log.error(e.getMessage(), e);
}
}
}
}
}