1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.deskclock.data; 18 19 import android.os.SystemClock; 20 21 import static com.android.deskclock.data.Stopwatch.State.PAUSED; 22 import static com.android.deskclock.data.Stopwatch.State.RESET; 23 import static com.android.deskclock.data.Stopwatch.State.RUNNING; 24 25 /** 26 * A read-only domain object representing a stopwatch. 27 */ 28 public final class Stopwatch { 29 30 public enum State { RESET, RUNNING, PAUSED } 31 32 /** The single, immutable instance of a reset stopwatch. */ 33 private static final Stopwatch RESET_STOPWATCH = new Stopwatch(RESET, Long.MIN_VALUE, 0); 34 35 /** Current state of this stopwatch. */ 36 private final State mState; 37 38 /** Elapsed time in ms the stopwatch was last started; {@link Long#MIN_VALUE} if not running. */ 39 private final long mLastStartTime; 40 41 /** Elapsed time in ms this stopwatch has accumulated while running. */ 42 private final long mAccumulatedTime; 43 Stopwatch(State state, long lastStartTime, long accumulatedTime)44 Stopwatch(State state, long lastStartTime, long accumulatedTime) { 45 mState = state; 46 mLastStartTime = lastStartTime; 47 mAccumulatedTime = accumulatedTime; 48 } 49 getState()50 public State getState() { return mState; } getLastStartTime()51 public long getLastStartTime() { return mLastStartTime; } isReset()52 public boolean isReset() { return mState == RESET; } isPaused()53 public boolean isPaused() { return mState == PAUSED; } isRunning()54 public boolean isRunning() { return mState == RUNNING; } 55 56 /** 57 * @return the total amount of time accumulated up to this moment 58 */ getTotalTime()59 public long getTotalTime() { 60 if (mState != RUNNING) { 61 return mAccumulatedTime; 62 } 63 64 // In practice, "now" can be any value due to device reboots. When the real-time clock 65 // is reset, there is no more guarantee that "now" falls after the last start time. To 66 // ensure the stopwatch is monotonically increasing, normalize negative time segments to 0, 67 final long timeSinceStart = now() - mLastStartTime; 68 return mAccumulatedTime + Math.max(0, timeSinceStart); 69 } 70 71 /** 72 * @return the amount of time accumulated up to the last time the stopwatch was started 73 */ getAccumulatedTime()74 long getAccumulatedTime() { 75 return mAccumulatedTime; 76 } 77 78 /** 79 * @return a copy of this stopwatch that is running 80 */ start()81 Stopwatch start() { 82 if (mState == RUNNING) { 83 return this; 84 } 85 86 return new Stopwatch(RUNNING, now(), getTotalTime()); 87 } 88 89 /** 90 * @return a copy of this stopwatch that is paused 91 */ pause()92 Stopwatch pause() { 93 if (mState != RUNNING) { 94 return this; 95 } 96 97 return new Stopwatch(PAUSED, Long.MIN_VALUE, getTotalTime()); 98 } 99 100 /** 101 * @return a copy of this stopwatch that is reset 102 */ reset()103 Stopwatch reset() { 104 return RESET_STOPWATCH; 105 } 106 now()107 private static long now() { 108 return SystemClock.elapsedRealtime(); 109 } 110 }