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.content.SharedPreferences;
20 
21 import com.android.deskclock.data.Stopwatch.State;
22 
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26 
27 import static com.android.deskclock.data.Stopwatch.State.RESET;
28 
29 /**
30  * This class encapsulates the transfer of data between {@link Stopwatch} and {@link Lap} domain
31  * objects and their permanent storage in {@link SharedPreferences}.
32  */
33 final class StopwatchDAO {
34 
35     /** Key to a preference that stores the state of the stopwatch. */
36     private static final String STATE = "sw_state";
37 
38     /** Key to a preference that stores the last start time of the stopwatch. */
39     private static final String LAST_START_TIME = "sw_start_time";
40 
41     /** Key to a preference that stores the epoch time when the stopwatch last started. */
42     private static final String LAST_WALL_CLOCK_TIME = "sw_wall_clock_time";
43 
44     /** Key to a preference that stores the accumulated elapsed time of the stopwatch. */
45     private static final String ACCUMULATED_TIME = "sw_accum_time";
46 
47     /** Prefix for a key to a preference that stores the number of recorded laps. */
48     private static final String LAP_COUNT = "sw_lap_num";
49 
50     /** Prefix for a key to a preference that stores accumulated time at the end of a lap. */
51     private static final String LAP_ACCUMULATED_TIME = "sw_lap_time_";
52 
StopwatchDAO()53     private StopwatchDAO() {}
54 
55     /**
56      * @return the stopwatch from permanent storage or a reset stopwatch if none exists
57      */
getStopwatch(SharedPreferences prefs)58     static Stopwatch getStopwatch(SharedPreferences prefs) {
59         final int stateIndex = prefs.getInt(STATE, RESET.ordinal());
60         final State state = State.values()[stateIndex];
61         final long lastStartTime = prefs.getLong(LAST_START_TIME, Stopwatch.UNUSED);
62         final long lastWallClockTime = prefs.getLong(LAST_WALL_CLOCK_TIME, Stopwatch.UNUSED);
63         final long accumulatedTime = prefs.getLong(ACCUMULATED_TIME, 0);
64         Stopwatch s = new Stopwatch(state, lastStartTime, lastWallClockTime, accumulatedTime);
65 
66         // If the stopwatch reports an illegal (negative) amount of time, remove the bad data.
67         if (s.getTotalTime() < 0) {
68             s = s.reset();
69             setStopwatch(prefs, s);
70         }
71         return s;
72     }
73 
74     /**
75      * @param stopwatch the last state of the stopwatch
76      */
setStopwatch(SharedPreferences prefs, Stopwatch stopwatch)77     static void setStopwatch(SharedPreferences prefs, Stopwatch stopwatch) {
78         final SharedPreferences.Editor editor = prefs.edit();
79 
80         if (stopwatch.isReset()) {
81             editor.remove(STATE)
82                     .remove(LAST_START_TIME)
83                     .remove(LAST_WALL_CLOCK_TIME)
84                     .remove(ACCUMULATED_TIME);
85         } else {
86             editor.putInt(STATE, stopwatch.getState().ordinal())
87                     .putLong(LAST_START_TIME, stopwatch.getLastStartTime())
88                     .putLong(LAST_WALL_CLOCK_TIME, stopwatch.getLastWallClockTime())
89                     .putLong(ACCUMULATED_TIME, stopwatch.getAccumulatedTime());
90         }
91 
92         editor.apply();
93     }
94 
95     /**
96      * @return a list of recorded laps for the stopwatch
97      */
getLaps(SharedPreferences prefs)98     static List<Lap> getLaps(SharedPreferences prefs) {
99         // Prepare the container to be filled with laps.
100         final int lapCount = prefs.getInt(LAP_COUNT, 0);
101         final List<Lap> laps = new ArrayList<>(lapCount);
102 
103         long prevAccumulatedTime = 0;
104 
105         // Lap numbers are 1-based and so the are corresponding shared preference keys.
106         for (int lapNumber = 1; lapNumber <= lapCount; lapNumber++) {
107             // Look up the accumulated time for the lap.
108             final String lapAccumulatedTimeKey = LAP_ACCUMULATED_TIME + lapNumber;
109             final long accumulatedTime = prefs.getLong(lapAccumulatedTimeKey, 0);
110 
111             // Lap time is the delta between accumulated time of this lap and prior lap.
112             final long lapTime = accumulatedTime - prevAccumulatedTime;
113 
114             // Create the lap instance from the data.
115             laps.add(new Lap(lapNumber, lapTime, accumulatedTime));
116 
117             // Update the accumulated time of the previous lap.
118             prevAccumulatedTime = accumulatedTime;
119         }
120 
121         // Laps are stored in the order they were recorded; display order is the reverse.
122         Collections.reverse(laps);
123 
124         return laps;
125     }
126 
127     /**
128      * @param newLapCount the number of laps including the new lap
129      * @param accumulatedTime the amount of time accumulate by the stopwatch at the end of the lap
130      */
addLap(SharedPreferences prefs, int newLapCount, long accumulatedTime)131     static void addLap(SharedPreferences prefs, int newLapCount, long accumulatedTime) {
132         prefs.edit()
133                 .putInt(LAP_COUNT, newLapCount)
134                 .putLong(LAP_ACCUMULATED_TIME + newLapCount, accumulatedTime)
135                 .apply();
136     }
137 
138     /**
139      * Remove the recorded laps for the stopwatch
140      */
clearLaps(SharedPreferences prefs)141     static void clearLaps(SharedPreferences prefs) {
142         final SharedPreferences.Editor editor = prefs.edit();
143 
144         final int lapCount = prefs.getInt(LAP_COUNT, 0);
145         for (int lapNumber = 1; lapNumber <= lapCount; lapNumber++) {
146             editor.remove(LAP_ACCUMULATED_TIME + lapNumber);
147         }
148         editor.remove(LAP_COUNT);
149 
150         editor.apply();
151     }
152 }