Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9042b0b5 authored by James Lemieux's avatar James Lemieux
Browse files

Introduce StopwatchListener to gracefully handle BOOT_COMPLETE

After a device reboot, there is a window of opportunity (about 15 seconds)
in which the user may open the Clock app and view the stopwatch. They
will see the stopwatch in a non-operational state. When BOOT_COMPLETE
is finally broadcast, the stopwatch is reset and the laps are cleared.

Prior to this change, the stale lap data would continue to be displayed
because no mechanism existed to notify StopwatchFragment of changes
to the stopwatch data by an external entity.

This change introduces StopwatchListener, which is notified of changes to
the stopwatch and its laps. The StopwatchFragment uses this listener to
react to stopwatch changes from external entities, such as the
BOOT_COMPLETE receiver.

Clearing laps is now a byproduct of resetting the stopwatch instead of an
independent update to the model.

Change-Id: Ie1aa3d3e5006d8a28947cb3cd6343175d1c3644c
parent 19ebe094
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -53,7 +53,6 @@ public class AlarmInitReceiver extends BroadcastReceiver {
        // Clear stopwatch data and reset timers because they rely on elapsed real-time values
        // which are meaningless after a device reboot.
        if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
            DataModel.getDataModel().clearLaps();
            DataModel.getDataModel().resetStopwatch();
            Events.sendStopwatchEvent(R.string.action_reset, R.string.label_reboot);
            DataModel.getDataModel().resetTimers(R.string.label_reboot);
+0 −1
Original line number Diff line number Diff line
@@ -145,7 +145,6 @@ public class HandleDeskClockApiCalls extends Activity {
                    break;
                }
                case ACTION_RESET_STOPWATCH: {
                    DataModel.getDataModel().clearLaps();
                    DataModel.getDataModel().resetStopwatch();
                    Events.sendStopwatchEvent(R.string.action_reset, eventLabel);
                    reason = getString(R.string.stopwatch_reset);
+16 −8
Original line number Diff line number Diff line
@@ -501,6 +501,22 @@ public final class DataModel {
    // Stopwatch
    //

    /**
     * @param stopwatchListener to be notified when stopwatch changes or laps are added
     */
    public void addStopwatchListener(StopwatchListener stopwatchListener) {
        enforceMainLooper();
        mStopwatchModel.addStopwatchListener(stopwatchListener);
    }

    /**
     * @param stopwatchListener to no longer be notified when stopwatch changes or laps are added
     */
    public void removeStopwatchListener(StopwatchListener stopwatchListener) {
        enforceMainLooper();
        mStopwatchModel.removeStopwatchListener(stopwatchListener);
    }

    /**
     * @return the current state of the stopwatch
     */
@@ -549,14 +565,6 @@ public final class DataModel {
        return mStopwatchModel.addLap();
    }

    /**
     * Clears the laps recorded for this stopwatch.
     */
    public void clearLaps() {
        enforceMainLooper();
        mStopwatchModel.clearLaps();
    }

    /**
     * @return {@code true} iff more laps can be recorded
     */
+34 −0
Original line number Diff line number Diff line
/*
 * 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;

/**
 * The interface through which interested parties are notified of changes to the stopwatch or laps.
 */
public interface StopwatchListener {

    /**
     * @param before the stopwatch state before the update
     * @param after the stopwatch state after the update
     */
    void stopwatchUpdated(Stopwatch before, Stopwatch after);

    /**
     * @param lap the lap that was added
     */
    void lapAdded(Lap lap);
}
 No newline at end of file
+37 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.content.res.Resources;
import android.os.SystemClock;
import android.support.annotation.IdRes;
import android.support.annotation.StringRes;
import android.support.annotation.VisibleForTesting;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.widget.RemoteViews;
@@ -34,6 +35,7 @@ import com.android.deskclock.HandleDeskClockApiCalls;
import com.android.deskclock.R;
import com.android.deskclock.stopwatch.StopwatchService;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@@ -58,6 +60,9 @@ final class StopwatchModel {
    /** Update stopwatch notification when locale changes. */
    private final BroadcastReceiver mLocaleChangedReceiver = new LocaleChangedReceiver();

    /** The listeners to notify when the stopwatch or its laps change. */
    private final List<StopwatchListener> mStopwatchListeners = new ArrayList<>();

    /** The current state of the stopwatch. */
    private Stopwatch mStopwatch;

@@ -74,6 +79,20 @@ final class StopwatchModel {
        mContext.registerReceiver(mLocaleChangedReceiver, localeBroadcastFilter);
    }

    /**
     * @param stopwatchListener to be notified when stopwatch changes or laps are added
     */
    void addStopwatchListener(StopwatchListener stopwatchListener) {
        mStopwatchListeners.add(stopwatchListener);
    }

    /**
     * @param stopwatchListener to no longer be notified when stopwatch changes or laps are added
     */
    void removeStopwatchListener(StopwatchListener stopwatchListener) {
        mStopwatchListeners.remove(stopwatchListener);
    }

    /**
     * @return the current state of the stopwatch
     */
@@ -89,7 +108,8 @@ final class StopwatchModel {
     * @param stopwatch the new state of the stopwatch
     */
    Stopwatch setStopwatch(Stopwatch stopwatch) {
        if (mStopwatch != stopwatch) {
        final Stopwatch before = getStopwatch();
        if (before != stopwatch) {
            StopwatchDAO.setStopwatch(mContext, stopwatch);
            mStopwatch = stopwatch;

@@ -97,6 +117,16 @@ final class StopwatchModel {
            if (!mNotificationModel.isApplicationInForeground()) {
                updateNotification();
            }

            // Resetting the stopwatch implicitly clears the recorded laps.
            if (stopwatch.isReset()) {
                clearLaps();
            }

            // Notify listeners of the stopwatch change.
            for (StopwatchListener stopwatchListener : mStopwatchListeners) {
                stopwatchListener.stopwatchUpdated(before, stopwatch);
            }
        }

        return stopwatch;
@@ -134,12 +164,18 @@ final class StopwatchModel {
            updateNotification();
        }

        // Notify listeners of the new lap.
        for (StopwatchListener stopwatchListener : mStopwatchListeners) {
            stopwatchListener.lapAdded(lap);
        }

        return lap;
    }

    /**
     * Clears the laps recorded for this stopwatch.
     */
    @VisibleForTesting
    void clearLaps() {
        StopwatchDAO.clearLaps(mContext);
        getMutableLaps().clear();
Loading