blob: bbe8928ce5f753ffc5fa7a918364b0fbd0a40ff4 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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 com.android.deskclock.data;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import android.support.annotation.DrawableRes;
import android.support.annotation.StringRes;
import android.support.annotation.VisibleForTesting;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import com.android.deskclock.HandleDeskClockApiCalls;
import com.android.deskclock.R;
import com.android.deskclock.Utils;
import com.android.deskclock.timer.ExpiredTimersActivity;
import com.android.deskclock.timer.TimerService;
import java.util.List;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
/**
* Builds KK, L, or M-style notifications to reflect the latest state of the unexpired timers.
*/
class TimerNotificationBuilderPreN implements TimerModel.NotificationBuilder {
@Override
public Notification build(Context context, NotificationModel nm, List<Timer> unexpired) {
final Timer timer = unexpired.get(0);
final long remainingTime = timer.getRemainingTime();
// Generate some descriptive text, a title, and some actions based on timer states.
final String contentText;
final String contentTitle;
@DrawableRes int firstActionIconId, secondActionIconId = 0;
@StringRes int firstActionTitleId, secondActionTitleId = 0;
Intent firstActionIntent, secondActionIntent = null;
if (unexpired.size() == 1) {
contentText = formatElapsedTimeUntilExpiry(context, remainingTime);
if (timer.isRunning()) {
// Single timer is running.
if (TextUtils.isEmpty(timer.getLabel())) {
contentTitle = context.getString(R.string.timer_notification_label);
} else {
contentTitle = timer.getLabel();
}
firstActionIconId = R.drawable.ic_pause_24dp;
firstActionTitleId = R.string.timer_pause;
firstActionIntent = new Intent(context, TimerService.class)
.setAction(HandleDeskClockApiCalls.ACTION_PAUSE_TIMER)
.putExtra(HandleDeskClockApiCalls.EXTRA_TIMER_ID, timer.getId());
secondActionIconId = R.drawable.ic_add_24dp;
secondActionTitleId = R.string.timer_plus_1_min;
secondActionIntent = new Intent(context, TimerService.class)
.setAction(HandleDeskClockApiCalls.ACTION_ADD_MINUTE_TIMER)
.putExtra(HandleDeskClockApiCalls.EXTRA_TIMER_ID, timer.getId());
} else {
// Single timer is paused.
contentTitle = context.getString(R.string.timer_paused);
firstActionIconId = R.drawable.ic_start_24dp;
firstActionTitleId = R.string.sw_resume_button;
firstActionIntent = new Intent(context, TimerService.class)
.setAction(HandleDeskClockApiCalls.ACTION_START_TIMER)
.putExtra(HandleDeskClockApiCalls.EXTRA_TIMER_ID, timer.getId());
secondActionIconId = R.drawable.ic_reset_24dp;
secondActionTitleId = R.string.sw_reset_button;
secondActionIntent = new Intent(context, TimerService.class)
.setAction(HandleDeskClockApiCalls.ACTION_RESET_TIMER)
.putExtra(HandleDeskClockApiCalls.EXTRA_TIMER_ID, timer.getId());
}
} else {
if (timer.isRunning()) {
// At least one timer is running.
final String timeRemaining = formatElapsedTimeUntilExpiry(context, remainingTime);
contentText = context.getString(R.string.next_timer_notif, timeRemaining);
contentTitle = context.getString(R.string.timers_in_use, unexpired.size());
} else {
// All timers are paused.
contentText = context.getString(R.string.all_timers_stopped_notif);
contentTitle = context.getString(R.string.timers_stopped, unexpired.size());
}
firstActionIconId = R.drawable.ic_reset_24dp;
firstActionTitleId = R.string.timer_reset_all;
firstActionIntent = TimerService.createResetUnexpiredTimersIntent(context);
}
// Intent to load the app and show the timer when the notification is tapped.
final Intent showApp = new Intent(context, HandleDeskClockApiCalls.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setAction(HandleDeskClockApiCalls.ACTION_SHOW_TIMERS)
.putExtra(HandleDeskClockApiCalls.EXTRA_TIMER_ID, timer.getId())
.putExtra(HandleDeskClockApiCalls.EXTRA_EVENT_LABEL, R.string.label_notification);
final PendingIntent pendingShowApp = PendingIntent.getActivity(context, 0, showApp,
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setOngoing(true)
.setLocalOnly(true)
.setShowWhen(false)
.setAutoCancel(false)
.setContentText(contentText)
.setContentTitle(contentTitle)
.setContentIntent(pendingShowApp)
.setSmallIcon(R.drawable.stat_notify_timer)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_ALARM)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setColor(ContextCompat.getColor(context, R.color.default_background));
final PendingIntent action1 = Utils.pendingServiceIntent(context, firstActionIntent);
final String action1Title = context.getString(firstActionTitleId);
builder.addAction(firstActionIconId, action1Title, action1);
if (secondActionIntent != null) {
final PendingIntent action2 = Utils.pendingServiceIntent(context, secondActionIntent);
final String action2Title = context.getString(secondActionTitleId);
builder.addAction(secondActionIconId, action2Title, action2);
}
final AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
final Intent updateNotification = TimerService.createUpdateNotificationIntent(context);
if (timer.isRunning() && remainingTime > MINUTE_IN_MILLIS) {
// Schedule a callback to update the time-sensitive information of the running timer.
final PendingIntent pi = PendingIntent.getService(context, 0, updateNotification,
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
final long nextMinuteChange = remainingTime % MINUTE_IN_MILLIS;
final long triggerTime = SystemClock.elapsedRealtime() + nextMinuteChange;
TimerModel.schedulePendingIntent(am, triggerTime, pi);
} else {
// Cancel the update notification callback.
final PendingIntent pi = PendingIntent.getService(context, 0, updateNotification,
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_NO_CREATE);
if (pi != null) {
am.cancel(pi);
pi.cancel();
}
}
return builder.build();
}
@Override
public Notification buildHeadsUp(Context context, List<Timer> expired) {
// Generate some descriptive text, a title, and an action name based on the timer count.
final int timerId;
final String contentText;
final String contentTitle;
final String resetActionTitle;
final int count = expired.size();
if (count == 1) {
final Timer timer = expired.get(0);
timerId = timer.getId();
resetActionTitle = context.getString(R.string.timer_stop);
contentText = context.getString(R.string.timer_times_up);
final String label = timer.getLabel();
if (TextUtils.isEmpty(label)) {
contentTitle = context.getString(R.string.timer_notification_label);
} else {
contentTitle = label;
}
} else {
timerId = -1;
contentText = context.getString(R.string.timer_multi_times_up, count);
contentTitle = context.getString(R.string.timer_notification_label);
resetActionTitle = context.getString(R.string.timer_stop_all);
}
// Content intent shows the expired timers full screen when clicked.
final Intent content = new Intent(context, ExpiredTimersActivity.class);
final PendingIntent pendingContent = Utils.pendingActivityIntent(context, content);
// Full screen intent has flags so it is different than the content intent.
final Intent fullScreen = new Intent(context, ExpiredTimersActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
final PendingIntent pendingFullScreen = Utils.pendingActivityIntent(context, fullScreen);
// Left button: Reset timer / Reset all timers
final Intent reset = TimerService.createResetExpiredTimersIntent(context);
final PendingIntent pendingReset = Utils.pendingServiceIntent(context, reset);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setOngoing(true)
.setLocalOnly(true)
.setShowWhen(false)
.setAutoCancel(false)
.setContentText(contentText)
.setContentTitle(contentTitle)
.setContentIntent(pendingContent)
.setColor(ContextCompat.getColor(context, R.color.default_background))
.setSmallIcon(R.drawable.stat_notify_timer)
.setFullScreenIntent(pendingFullScreen, true)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setDefaults(NotificationCompat.DEFAULT_LIGHTS)
.addAction(R.drawable.ic_stop_24dp, resetActionTitle, pendingReset);
// Add a second action if only a single timer is expired.
if (count == 1) {
// Right button: Add minute
final Intent addMinute = TimerService.createAddMinuteTimerIntent(context, timerId);
final PendingIntent pendingAddMinute = Utils.pendingServiceIntent(context, addMinute);
final String addMinuteTitle = context.getString(R.string.timer_plus_1_min);
builder.addAction(R.drawable.ic_add_24dp, addMinuteTitle, pendingAddMinute);
}
return builder.build();
}
/**
* Format "7 hours 52 minutes remaining"
*/
@VisibleForTesting
static String formatElapsedTimeUntilExpiry(Context context, long remainingTime) {
final int hours = (int) remainingTime / (int) HOUR_IN_MILLIS;
final int minutes = (int) remainingTime / ((int) MINUTE_IN_MILLIS) % 60;
String minSeq = Utils.getNumberFormattedQuantityString(context, R.plurals.minutes, minutes);
String hourSeq = Utils.getNumberFormattedQuantityString(context, R.plurals.hours, hours);
// The verb "remaining" may have to change tense for singular subjects in some languages.
final String verb = context.getString((minutes > 1 || hours > 1)
? R.string.timer_remaining_multiple
: R.string.timer_remaining_single);
final boolean showHours = hours > 0;
final boolean showMinutes = minutes > 0;
int formatStringId;
if (showHours) {
if (showMinutes) {
formatStringId = R.string.timer_notifications_hours_minutes;
} else {
formatStringId = R.string.timer_notifications_hours;
}
} else if (showMinutes) {
formatStringId = R.string.timer_notifications_minutes;
} else {
formatStringId = R.string.timer_notifications_less_min;
}
return String.format(context.getString(formatStringId), hourSeq, minSeq, verb);
}
}