Skip to content

Commit

Permalink
Merge pull request #57 from ahudson20/55-make-stopwatch-persist-when-…
Browse files Browse the repository at this point in the history
…app-closed

Persist stopwatch when app closed/backgrounded
  • Loading branch information
ahudson20 authored Apr 22, 2024
2 parents 4ee1617 + 9f8cf6c commit c941835
Show file tree
Hide file tree
Showing 12 changed files with 321 additions and 77 deletions.
14 changes: 12 additions & 2 deletions app/src/main/java/com/app/whakaara/activities/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,21 @@ class MainActivity : ComponentActivity() {

override fun onResume() {
super.onResume()
viewModel.recreateTimer()
with(viewModel) {
recreateTimer()
recreateStopwatch()
cancelStopwatchNotification()
cancelTimerNotification()
}
}

override fun onPause() {
super.onPause()
viewModel.saveTimerStateForRecreation()
with(viewModel) {
saveTimerStateForRecreation()
saveStopwatchStateForRecreation()
startStopwatchNotification()
startTimerNotification()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.app.whakaara.state.Lap
import com.app.whakaara.state.StopwatchState
import com.app.whakaara.state.TimerStateDataStore
import com.app.whakaara.ui.theme.darkGreen
import com.app.whakaara.utils.GeneralUtils
import com.app.whakaara.utils.constants.DateUtilsConstants.STOPWATCH_STARTING_TIME
import com.app.whakaara.utils.constants.GeneralConstants.ZERO_MILLIS
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.map
import javax.inject.Inject
Expand All @@ -29,10 +35,20 @@ class PreferencesDataStore @Inject constructor(
private object PreferencesKeys {
val COLOUR_BACKGROUND_KEY = stringPreferencesKey("colour_background")
val COLOUR_TEXT_KEY = stringPreferencesKey("colour_text")

val TIMER_FINISH_KEY = longPreferencesKey("timer_finish")
val TIMER_ACTIVE_KEY = booleanPreferencesKey("timer_active")
val TIMER_PAUSED_KEY = booleanPreferencesKey("timer_paused")
val TIMER_TIME_STAMP = longPreferencesKey("timer_time_stamp")

// TODO: Shift to Room
val STOPWATCH_TIME_MILLIS = longPreferencesKey("stopwatch_time")
val STOPWATCH_LAST_TIMESTAMP = longPreferencesKey("stopwatch_timestamp")
val STOPWATCH_FORMATTED_TIME = stringPreferencesKey("stopwatch_formattedTime")
val STOPWATCH_IS_ACTIVE = booleanPreferencesKey("stopwatch_is_active")
val STOPWATCH_IS_START = booleanPreferencesKey("stopwatch_is_start")
val STOPWATCH_IS_PAUSED = booleanPreferencesKey("stopwatch_is_paused")
val STOPWATCH_LAP_LIST = stringPreferencesKey("stopwatch_lap_list")
}

//region colour
Expand All @@ -55,10 +71,10 @@ class PreferencesDataStore @Inject constructor(
//region timer
val readTimerStatus = preferencesDataStore.data.map { preferences ->
TimerStateDataStore(
remainingTimeInMillis = preferences[PreferencesKeys.TIMER_FINISH_KEY] ?: 0L,
remainingTimeInMillis = preferences[PreferencesKeys.TIMER_FINISH_KEY] ?: ZERO_MILLIS,
isActive = preferences[PreferencesKeys.TIMER_ACTIVE_KEY] ?: false,
isPaused = preferences[PreferencesKeys.TIMER_PAUSED_KEY] ?: false,
timeStamp = preferences[PreferencesKeys.TIMER_TIME_STAMP] ?: 0L
timeStamp = preferences[PreferencesKeys.TIMER_TIME_STAMP] ?: ZERO_MILLIS
)
}

Expand All @@ -71,4 +87,45 @@ class PreferencesDataStore @Inject constructor(
}
}
//endregion

//region stopwatch
val readStopwatchState = preferencesDataStore.data.map { preferences ->
val lapString = preferences[PreferencesKeys.STOPWATCH_LAP_LIST] ?: "[]"
val list: ArrayList<Lap> = Gson().fromJson(lapString, object : TypeToken<ArrayList<Lap>>() {}.type)
StopwatchState(
timeMillis = preferences[PreferencesKeys.STOPWATCH_TIME_MILLIS] ?: ZERO_MILLIS,
lastTimeStamp = preferences[PreferencesKeys.STOPWATCH_LAST_TIMESTAMP] ?: ZERO_MILLIS,
formattedTime = preferences[PreferencesKeys.STOPWATCH_FORMATTED_TIME] ?: STOPWATCH_STARTING_TIME,
isActive = preferences[PreferencesKeys.STOPWATCH_IS_ACTIVE] ?: false,
isStart = preferences[PreferencesKeys.STOPWATCH_IS_START] ?: true,
isPaused = preferences[PreferencesKeys.STOPWATCH_IS_PAUSED] ?: false,
lapList = list
)
}

suspend fun saveStopwatchState(state: StopwatchState) {
val list = Gson().toJson(state.lapList)
preferencesDataStore.edit { preferences ->
preferences[PreferencesKeys.STOPWATCH_TIME_MILLIS] = state.timeMillis
preferences[PreferencesKeys.STOPWATCH_LAST_TIMESTAMP] = state.lastTimeStamp
preferences[PreferencesKeys.STOPWATCH_FORMATTED_TIME] = state.formattedTime
preferences[PreferencesKeys.STOPWATCH_IS_ACTIVE] = state.isActive
preferences[PreferencesKeys.STOPWATCH_IS_START] = state.isStart
preferences[PreferencesKeys.STOPWATCH_IS_PAUSED] = state.isPaused
preferences[PreferencesKeys.STOPWATCH_LAP_LIST] = list
}
}

suspend fun clearStopwatchState() {
preferencesDataStore.edit { preferences ->
preferences.remove(PreferencesKeys.STOPWATCH_TIME_MILLIS)
preferences.remove(PreferencesKeys.STOPWATCH_LAST_TIMESTAMP)
preferences.remove(PreferencesKeys.STOPWATCH_FORMATTED_TIME)
preferences.remove(PreferencesKeys.STOPWATCH_IS_ACTIVE)
preferences.remove(PreferencesKeys.STOPWATCH_IS_START)
preferences.remove(PreferencesKeys.STOPWATCH_IS_PAUSED)
preferences.remove(PreferencesKeys.STOPWATCH_LAP_LIST)
}
}
//endregion
}
82 changes: 66 additions & 16 deletions app/src/main/java/com/app/whakaara/logic/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -204,13 +204,47 @@ class MainViewModel @Inject constructor(
stopwatchManagerWrapper.pauseStopwatch()
}

fun resetStopwatch() {
fun resetStopwatch() = viewModelScope.launch(Dispatchers.IO) {
stopwatchManagerWrapper.resetStopwatch()
preferencesDatastore.clearStopwatchState()
}

fun lapStopwatch() {
stopwatchManagerWrapper.lapStopwatch()
}

fun saveStopwatchStateForRecreation() = viewModelScope.launch(Dispatchers.IO) {
if (stopwatchState.value.isActive || stopwatchState.value.isPaused) {
preferencesDatastore.saveStopwatchState(
state = stopwatchState.value
)
}
}

fun recreateStopwatch() = viewModelScope.launch(Dispatchers.Main) {
if (stopwatchState.value == StopwatchState()) {
val state = preferencesDatastore.readStopwatchState.first()
if (state.isActive) {
stopwatchManagerWrapper.recreateStopwatchActive(state = state)
preferencesDatastore.clearStopwatchState()
} else if (state.isPaused) {
stopwatchManagerWrapper.recreateStopwatchPaused(state = state)
preferencesDatastore.clearStopwatchState()
}
}
}

fun startStopwatchNotification() {
if (stopwatchState.value.isActive) {
stopwatchManagerWrapper.createStopwatchNotification()
} else if (stopwatchState.value.isPaused) {
stopwatchManagerWrapper.pauseStopwatchNotification()
}
}

fun cancelStopwatchNotification() {
stopwatchManagerWrapper.cancelNotification()
}
//endregion

//region timer
Expand Down Expand Up @@ -250,22 +284,26 @@ class MainViewModel @Inject constructor(
}

fun recreateTimer() = viewModelScope.launch(Dispatchers.Main) {
val status = preferencesDatastore.readTimerStatus.first()
val difference = System.currentTimeMillis() - status.timeStamp
if (status.remainingTimeInMillis > 0 && timerState.value.isStart && (status.remainingTimeInMillis > difference)) {
if (status.isActive) {
timerManagerWrapper.recreateActiveTimer(
milliseconds = status.remainingTimeInMillis - difference
)
} else if (status.isPaused) {
timerManagerWrapper.recreatePausedTimer(
milliseconds = status.remainingTimeInMillis - difference
)
}
if (!timerState.value.isStart) {
val status = preferencesDatastore.readTimerStatus.first()
with(status) {
val difference = System.currentTimeMillis() - timeStamp
if (remainingTimeInMillis > 0 && timerState.value.isStart && (remainingTimeInMillis > difference)) {
if (isActive) {
timerManagerWrapper.recreateActiveTimer(
milliseconds = remainingTimeInMillis - difference
)
} else if (isPaused) {
timerManagerWrapper.recreatePausedTimer(
milliseconds = remainingTimeInMillis - difference
)
}

preferencesDatastore.saveTimerData(
state = TimerStateDataStore()
)
preferencesDatastore.saveTimerData(
state = TimerStateDataStore()
)
}
}
}
}

Expand All @@ -281,5 +319,17 @@ class MainViewModel @Inject constructor(
)
}
}

fun startTimerNotification() {
if (timerState.value.isTimerPaused) {
timerManagerWrapper.pauseTimerNotificationCountdown()
} else if (timerState.value.isTimerActive) {
timerManagerWrapper.startTimerNotificationCountdown(milliseconds = timerState.value.currentTime + Calendar.getInstance().timeInMillis)
}
}

fun cancelTimerNotification() {
timerManagerWrapper.cancelNotification()
}
// endregion
}
Loading

0 comments on commit c941835

Please sign in to comment.