com.wizardsofm.deskclock.data.TimerNotificationBuilderPreN.java Source code

Java tutorial

Introduction

Here is the source code for com.wizardsofm.deskclock.data.TimerNotificationBuilderPreN.java

Source

/*
 * 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.wizardsofm.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.wizardsofm.deskclock.HandleDeskClockApiCalls;
import com.wizardsofm.deskclock.Utils;
import com.wizardsofm.deskclock.timer.ExpiredTimersActivity;
import com.wizardsofm.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(com.wizardsofm.deskclock.R.string.timer_notification_label);
                } else {
                    contentTitle = timer.getLabel();
                }

                firstActionIconId = com.wizardsofm.deskclock.R.drawable.ic_pause_24dp;
                firstActionTitleId = com.wizardsofm.deskclock.R.string.timer_pause;
                firstActionIntent = new Intent(context, TimerService.class)
                        .setAction(HandleDeskClockApiCalls.ACTION_PAUSE_TIMER)
                        .putExtra(HandleDeskClockApiCalls.EXTRA_TIMER_ID, timer.getId());

                secondActionIconId = com.wizardsofm.deskclock.R.drawable.ic_add_24dp;
                secondActionTitleId = com.wizardsofm.deskclock.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(com.wizardsofm.deskclock.R.string.timer_paused);

                firstActionIconId = com.wizardsofm.deskclock.R.drawable.ic_start_24dp;
                firstActionTitleId = com.wizardsofm.deskclock.R.string.sw_resume_button;
                firstActionIntent = new Intent(context, TimerService.class)
                        .setAction(HandleDeskClockApiCalls.ACTION_START_TIMER)
                        .putExtra(HandleDeskClockApiCalls.EXTRA_TIMER_ID, timer.getId());

                secondActionIconId = com.wizardsofm.deskclock.R.drawable.ic_reset_24dp;
                secondActionTitleId = com.wizardsofm.deskclock.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(com.wizardsofm.deskclock.R.string.next_timer_notif, timeRemaining);
                contentTitle = context.getString(com.wizardsofm.deskclock.R.string.timers_in_use, unexpired.size());
            } else {
                // All timers are paused.
                contentText = context.getString(com.wizardsofm.deskclock.R.string.all_timers_stopped_notif);
                contentTitle = context.getString(com.wizardsofm.deskclock.R.string.timers_stopped,
                        unexpired.size());
            }

            firstActionIconId = com.wizardsofm.deskclock.R.drawable.ic_reset_24dp;
            firstActionTitleId = com.wizardsofm.deskclock.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,
                        com.wizardsofm.deskclock.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(com.wizardsofm.deskclock.R.drawable.stat_notify_timer)
                .setPriority(NotificationCompat.PRIORITY_HIGH).setCategory(NotificationCompat.CATEGORY_ALARM)
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
                .setColor(ContextCompat.getColor(context, com.wizardsofm.deskclock.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(com.wizardsofm.deskclock.R.string.timer_stop);
            contentText = context.getString(com.wizardsofm.deskclock.R.string.timer_times_up);

            final String label = timer.getLabel();
            if (TextUtils.isEmpty(label)) {
                contentTitle = context.getString(com.wizardsofm.deskclock.R.string.timer_notification_label);
            } else {
                contentTitle = label;
            }
        } else {
            timerId = -1;
            contentText = context.getString(com.wizardsofm.deskclock.R.string.timer_multi_times_up, count);
            contentTitle = context.getString(com.wizardsofm.deskclock.R.string.timer_notification_label);
            resetActionTitle = context.getString(com.wizardsofm.deskclock.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, com.wizardsofm.deskclock.R.color.default_background))
                .setSmallIcon(com.wizardsofm.deskclock.R.drawable.stat_notify_timer)
                .setFullScreenIntent(pendingFullScreen, true).setPriority(NotificationCompat.PRIORITY_MAX)
                .setDefaults(NotificationCompat.DEFAULT_LIGHTS)
                .addAction(com.wizardsofm.deskclock.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(com.wizardsofm.deskclock.R.string.timer_plus_1_min);
            builder.addAction(com.wizardsofm.deskclock.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, com.wizardsofm.deskclock.R.plurals.minutes,
                minutes);
        String hourSeq = Utils.getNumberFormattedQuantityString(context, com.wizardsofm.deskclock.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) ? com.wizardsofm.deskclock.R.string.timer_remaining_multiple
                        : com.wizardsofm.deskclock.R.string.timer_remaining_single);

        final boolean showHours = hours > 0;
        final boolean showMinutes = minutes > 0;

        int formatStringId;
        if (showHours) {
            if (showMinutes) {
                formatStringId = com.wizardsofm.deskclock.R.string.timer_notifications_hours_minutes;
            } else {
                formatStringId = com.wizardsofm.deskclock.R.string.timer_notifications_hours;
            }
        } else if (showMinutes) {
            formatStringId = com.wizardsofm.deskclock.R.string.timer_notifications_minutes;
        } else {
            formatStringId = com.wizardsofm.deskclock.R.string.timer_notifications_less_min;
        }
        return String.format(context.getString(formatStringId), hourSeq, minSeq, verb);
    }
}