From 8474e6a800c311f4350d625a22d9a61c59bddb43 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Wed, 1 Sep 2021 19:58:38 +0100 Subject: [PATCH] handle multiple timer states - feat: separate timer actions from states by adding a new class TimerEvent to hold actions Reset, Start, Pause, Finish, Refesh that could be performed on a timer. - feat: handle multiple countdown timers in the App file by creating a map of the timer id to the countdown timer. - fix: use gson instance from TypeAdapter in Room's Converters class - ref: remove scroll view parent from each timer item, a fix for the keyboard obscuring a label will be implemented in a future commit --- .../kotlin/com/simplemobiletools/clock/App.kt | 85 +++++--- .../clock/adapters/TimerAdapter.kt | 79 +++++--- .../clock/adapters/ViewPagerAdapter.kt | 3 +- .../clock/extensions/Context.kt | 12 +- .../clock/extensions/gson/TypeAdapter.kt | 3 - .../clock/fragments/TimerFragment.kt | 91 ++++----- .../clock/helpers/Constants.kt | 1 + .../clock/helpers/Converters.kt | 3 +- .../clock/helpers/TimerHelper.kt | 16 +- .../clock/interfaces/TimerDao.kt | 3 + .../clock/models/TimerEvent.kt | 9 + .../clock/models/TimerState.kt | 3 - .../clock/services/TimerService.kt | 2 +- app/src/main/res/layout/fragment_timer.xml | 2 +- app/src/main/res/layout/item_timer.xml | 186 +++++++++--------- 15 files changed, 266 insertions(+), 232 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/App.kt b/app/src/main/kotlin/com/simplemobiletools/clock/App.kt index abdd5b92..9cc7c842 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/App.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/App.kt @@ -5,16 +5,18 @@ import android.app.NotificationManager import android.content.Context import android.os.Build import android.os.CountDownTimer +import android.util.Log import androidx.annotation.RequiresApi import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.ProcessLifecycleOwner import com.facebook.stetho.Stetho -import com.simplemobiletools.clock.extensions.config import com.simplemobiletools.clock.extensions.getOpenTimerTabIntent import com.simplemobiletools.clock.extensions.getTimerNotification +import com.simplemobiletools.clock.extensions.timerHelper import com.simplemobiletools.clock.helpers.TIMER_NOTIF_ID +import com.simplemobiletools.clock.models.TimerEvent import com.simplemobiletools.clock.models.TimerState import com.simplemobiletools.clock.services.TimerStopService import com.simplemobiletools.clock.services.startTimerService @@ -25,8 +27,7 @@ import org.greenrobot.eventbus.ThreadMode class App : Application(), LifecycleObserver { - private var timer: CountDownTimer? = null - private var lastTick = 0L + private var timers = mutableMapOf() override fun onCreate() { super.onCreate() @@ -48,63 +49,83 @@ class App : Application(), LifecycleObserver { @RequiresApi(Build.VERSION_CODES.O) @OnLifecycleEvent(Lifecycle.Event.ON_STOP) private fun onAppBackgrounded() { - if (config.timerState is TimerState.Running) { - startTimerService(this) + timerHelper.getTimers { timers -> + if (timers.any { it.state is TimerState.Running }) { + startTimerService(this) + } } } @OnLifecycleEvent(Lifecycle.Event.ON_START) private fun onAppForegrounded() { EventBus.getDefault().post(TimerStopService) + timerHelper.getTimers { timers -> + val runningTimers = timers.filter { it.state is TimerState.Running } + runningTimers.forEach { timer -> + EventBus.getDefault().post(TimerEvent.Start(timer.id!!, (timer.state as TimerState.Running).tick)) + } + } } @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Idle) { - config.timerState = state - timer?.cancel() + fun onMessageEvent(event: TimerEvent.Reset) { + Log.w(TAG, "onMessageEvent: $event") + updateTimerState(event.timerId, TimerState.Idle) + timers[event.timerId]?.cancel() } @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Start) { - timer = object : CountDownTimer(state.duration, 1000) { + fun onMessageEvent(event: TimerEvent.Start) { + Log.w(TAG, "onMessageEvent: $event") + val countDownTimer = object : CountDownTimer(event.duration, 1000) { override fun onTick(tick: Long) { - lastTick = tick - - val newState = TimerState.Running(state.duration, tick) - EventBus.getDefault().post(newState) - config.timerState = newState + Log.d(TAG, "onMessageEvent--> $tick") + updateTimerState(event.timerId, TimerState.Running(event.duration, tick)) } override fun onFinish() { - EventBus.getDefault().post(TimerState.Finish(state.duration)) + EventBus.getDefault().post(TimerEvent.Finish(event.timerId, event.duration)) EventBus.getDefault().post(TimerStopService) } }.start() + timers[event.timerId] = countDownTimer } @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(event: TimerState.Finish) { - val pendingIntent = getOpenTimerTabIntent() - val notification = getTimerNotification(pendingIntent, false) - val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notificationManager.notify(TIMER_NOTIF_ID, notification) - - EventBus.getDefault().post(TimerState.Finished) + fun onMessageEvent(event: TimerEvent.Finish) { + Log.w(TAG, "onMessageEvent: $event") + timerHelper.getTimer(event.timerId) { timer -> + val pendingIntent = getOpenTimerTabIntent(event.timerId) + val notification = getTimerNotification(timer, pendingIntent, false) + val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.notify(TIMER_NOTIF_ID, notification) + updateTimerState(event.timerId, TimerState.Finished) + } } @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Finished) { - config.timerState = state + fun onMessageEvent(event: TimerEvent.Pause) { + Log.w(TAG, "onMessageEvent: $event") + timerHelper.getTimer(event.timerId) { timer -> + updateTimerState(event.timerId, TimerState.Paused(event.duration, (timer.state as TimerState.Running).tick)) + timers[event.timerId]?.cancel() + } } - @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(event: TimerState.Pause) { - EventBus.getDefault().post(TimerState.Paused(event.duration, lastTick)) + private fun updateTimerState(timerId: Long, state: TimerState) { + timerHelper.getTimer(timerId) { timer -> + val newTimer = timer.copy(state = state) + Log.w(TAG, "updateTimerState: $newTimer") + timerHelper.insertOrUpdateTimer(newTimer) { + EventBus.getDefault().post(TimerEvent.Refresh(timerId)) + timerHelper.getTimer(timerId) { + Log.e(TAG, "updated timer $it") + } + } + } } - @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Paused) { - config.timerState = state - timer?.cancel() + companion object { + private const val TAG = "App" } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt index d1ffce48..899bf347 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt @@ -2,6 +2,7 @@ package com.simplemobiletools.clock.adapters import android.media.AudioManager import android.media.RingtoneManager +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -11,7 +12,10 @@ import androidx.recyclerview.widget.RecyclerView import com.simplemobiletools.clock.R import com.simplemobiletools.clock.activities.SimpleActivity import com.simplemobiletools.clock.dialogs.MyTimePickerDialogDialog -import com.simplemobiletools.clock.extensions.* +import com.simplemobiletools.clock.extensions.checkAlarmsWithDeletedSoundUri +import com.simplemobiletools.clock.extensions.colorLeftDrawable +import com.simplemobiletools.clock.extensions.config +import com.simplemobiletools.clock.extensions.timerHelper import com.simplemobiletools.clock.helpers.PICK_AUDIO_FILE_INTENT_ID import com.simplemobiletools.clock.models.Timer import com.simplemobiletools.clock.models.TimerState @@ -20,16 +24,13 @@ import com.simplemobiletools.commons.extensions.getDefaultAlarmSound import com.simplemobiletools.commons.extensions.getFormattedDuration import com.simplemobiletools.commons.extensions.onTextChangeListener import com.simplemobiletools.commons.models.AlarmSound +import kotlin.math.roundToInt import kotlinx.android.synthetic.main.item_timer.view.* -import org.greenrobot.eventbus.EventBus class TimerAdapter( private val activity: SimpleActivity, private val onRefresh: () -> Unit, - - ) : ListAdapter(diffUtil) { - - private val config = activity.config +) : ListAdapter(diffUtil) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TimerViewHolder { return TimerViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_timer, parent, false)) @@ -45,28 +46,32 @@ class TimerAdapter( inner class TimerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + init { + itemView.timer_label.onTextChangeListener { text -> + Log.w(TAG, "timer_label") + updateTimer(getItemAt(adapterPosition).copy(label = text), false) + } + + itemView.post { + val textColor = activity.config.textColor + itemView.timer_initial_time.colorLeftDrawable(textColor) + itemView.timer_vibrate.colorLeftDrawable(textColor) + itemView.timer_sound.colorLeftDrawable(textColor) + } + } + fun bind(timer: Timer) { itemView.apply { - timer_time.text = timer.seconds.getFormattedDuration() - timer_label.setText(timer.label) - - timer_time.text = timer.seconds.getFormattedDuration() - timer_label.setText(timer.label) - - val textColor = activity.config.textColor + //only update when different to prevent flickering and unnecessary updates + if (timer_label.text.toString() != timer.label) { + timer_label.setText(timer.label) + } timer_initial_time.text = timer.seconds.getFormattedDuration() - timer_initial_time.colorLeftDrawable(textColor) timer_vibrate.isChecked = timer.vibrate - timer_vibrate.colorLeftDrawable(textColor) timer_sound.text = timer.soundTitle - timer_sound.colorLeftDrawable(textColor) - - timer_time.setOnClickListener { - stopTimer(timer) - } timer_time.setOnClickListener { changeDuration(timer) @@ -77,12 +82,13 @@ class TimerAdapter( } timer_vibrate_holder.setOnClickListener { + Log.w(TAG, "toggle") timer_vibrate.toggle() updateTimer(timer.copy(vibrate = timer_vibrate.isChecked), false) } timer_sound.setOnClickListener { - SelectAlarmSoundDialog(activity, config.timerSoundUri, AudioManager.STREAM_ALARM, PICK_AUDIO_FILE_INTENT_ID, + SelectAlarmSoundDialog(activity, timer.soundUri, AudioManager.STREAM_ALARM, PICK_AUDIO_FILE_INTENT_ID, RingtoneManager.TYPE_ALARM, true, onAlarmPicked = { sound -> if (sound != null) { @@ -99,39 +105,52 @@ class TimerAdapter( }) } - timer_label.onTextChangeListener { text -> - updateTimer(timer.copy(label = text), false) + + when (timer.state) { + is TimerState.Finished -> { + timer_time.text = 0.getFormattedDuration() + } + + is TimerState.Idle -> { + timer_time.text = timer.seconds.getFormattedDuration() + } + + is TimerState.Paused -> { + timer_time.text = timer.state.tick.div(1000F).roundToInt().getFormattedDuration() + } + + is TimerState.Running -> { + timer_time.text = timer.state.tick.div(1000F).roundToInt().getFormattedDuration() + } } } } } - private fun stopTimer(timer: Timer) { - EventBus.getDefault().post(TimerState.Idle) - activity.hideTimerNotification() - } - private fun changeDuration(timer: Timer) { MyTimePickerDialogDialog(activity, timer.seconds) { seconds -> val timerSeconds = if (seconds <= 0) 10 else seconds + Log.w(TAG, "changeDuration") updateTimer(timer.copy(seconds = timerSeconds)) } } fun updateAlarmSound(timer: Timer, alarmSound: AlarmSound) { + Log.w(TAG, "updateAlarmSound: $timer") updateTimer(timer.copy(soundTitle = alarmSound.title, soundUri = alarmSound.uri)) } private fun updateTimer(timer: Timer, refresh: Boolean = true) { + Log.w(TAG, "updateTimer: $timer") activity.timerHelper.insertOrUpdateTimer(timer) if (refresh) { onRefresh.invoke() } } - companion object { - val diffUtil = object : DiffUtil.ItemCallback() { + private const val TAG = "TimerAdapter" + private val diffUtil = object : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: Timer, newItem: Timer): Boolean { return oldItem.id == newItem.id } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt index 6969b81d..c79be53e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt @@ -11,6 +11,7 @@ import com.simplemobiletools.clock.fragments.TimerFragment import com.simplemobiletools.clock.helpers.TABS_COUNT import com.simplemobiletools.clock.helpers.TAB_ALARM import com.simplemobiletools.clock.helpers.TAB_CLOCK +import com.simplemobiletools.clock.helpers.TAB_TIMER import com.simplemobiletools.commons.models.AlarmSound class ViewPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { @@ -50,6 +51,6 @@ class ViewPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { } fun updateTimerTabAlarmSound(alarmSound: AlarmSound) { -// (fragments[TAB_TIMER] as? TimerFragment)?.updateAlarmSound(alarmSound) + (fragments[TAB_TIMER] as? TimerFragment)?.updateAlarmSound(alarmSound) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt index 5b178cec..d17da18d 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt @@ -25,6 +25,7 @@ import com.simplemobiletools.clock.helpers.* import com.simplemobiletools.clock.interfaces.TimerDao import com.simplemobiletools.clock.models.Alarm import com.simplemobiletools.clock.models.MyTimeZone +import com.simplemobiletools.clock.models.Timer import com.simplemobiletools.clock.receivers.AlarmReceiver import com.simplemobiletools.clock.receivers.DateTimeWidgetUpdateReceiver import com.simplemobiletools.clock.receivers.HideAlarmReceiver @@ -139,9 +140,10 @@ fun Context.getOpenAlarmTabIntent(): PendingIntent { return PendingIntent.getActivity(this, OPEN_ALARMS_TAB_INTENT_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT) } -fun Context.getOpenTimerTabIntent(): PendingIntent { +fun Context.getOpenTimerTabIntent(timerId: Long): PendingIntent { val intent = getLaunchIntent() ?: Intent(this, SplashActivity::class.java) intent.putExtra(OPEN_TAB, TAB_TIMER) + intent.putExtra(TIMER_ID, timerId) return PendingIntent.getActivity(this, TIMER_NOTIF_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT) } @@ -260,8 +262,8 @@ fun Context.showAlarmNotification(alarm: Alarm) { } @SuppressLint("NewApi") -fun Context.getTimerNotification(pendingIntent: PendingIntent, addDeleteIntent: Boolean): Notification { - var soundUri = config.timerSoundUri +fun Context.getTimerNotification(timer: Timer, pendingIntent: PendingIntent, addDeleteIntent: Boolean): Notification { + var soundUri = timer.soundUri if (soundUri == SILENT) { soundUri = "" } else { @@ -292,7 +294,7 @@ fun Context.getTimerNotification(pendingIntent: PendingIntent, addDeleteIntent: lightColor = getAdjustedPrimaryColor() setSound(Uri.parse(soundUri), audioAttributes) - if (!config.timerVibrate) { + if (!timer.vibrate) { vibrationPattern = longArrayOf(0L) } @@ -321,7 +323,7 @@ fun Context.getTimerNotification(pendingIntent: PendingIntent, addDeleteIntent: builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - if (config.timerVibrate) { + if (timer.vibrate) { val vibrateArray = LongArray(2) { 500 } builder.setVibrate(vibrateArray) } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/gson/TypeAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/gson/TypeAdapter.kt index 597e5861..40e948af 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/gson/TypeAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/gson/TypeAdapter.kt @@ -7,11 +7,8 @@ import com.simplemobiletools.clock.models.TimerState val timerStates = valueOf() .registerSubtype(TimerState.Idle::class.java) - .registerSubtype(TimerState.Start::class.java) .registerSubtype(TimerState.Running::class.java) - .registerSubtype(TimerState.Pause::class.java) .registerSubtype(TimerState.Paused::class.java) - .registerSubtype(TimerState.Finish::class.java) .registerSubtype(TimerState.Finished::class.java) inline fun valueOf(): RuntimeTypeAdapterFactory = RuntimeTypeAdapterFactory.of(T::class.java) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt index 49241e48..bf5d54cb 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt @@ -2,6 +2,7 @@ package com.simplemobiletools.clock.fragments import android.graphics.Color import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -10,13 +11,16 @@ import androidx.viewpager2.widget.ViewPager2 import com.simplemobiletools.clock.R import com.simplemobiletools.clock.activities.SimpleActivity import com.simplemobiletools.clock.adapters.TimerAdapter -import com.simplemobiletools.clock.dialogs.MyTimePickerDialogDialog import com.simplemobiletools.clock.extensions.config import com.simplemobiletools.clock.extensions.hideTimerNotification +import com.simplemobiletools.clock.extensions.secondsToMillis import com.simplemobiletools.clock.extensions.timerHelper import com.simplemobiletools.clock.models.Timer +import com.simplemobiletools.clock.models.TimerEvent import com.simplemobiletools.clock.models.TimerState import com.simplemobiletools.commons.extensions.* +import com.simplemobiletools.commons.models.AlarmSound +import kotlinx.android.synthetic.main.fragment_timer.timer_view_pager import kotlinx.android.synthetic.main.fragment_timer.view.* import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe @@ -24,7 +28,7 @@ import org.greenrobot.eventbus.ThreadMode class TimerFragment : Fragment() { - lateinit var view: ViewGroup + private lateinit var view: ViewGroup private lateinit var timerAdapter: TimerAdapter override fun onCreate(savedInstanceState: Bundle?) { @@ -42,7 +46,10 @@ class TimerFragment : Fragment() { timerAdapter = TimerAdapter(requireActivity() as SimpleActivity) { refreshTimers() } + timer_view_pager.adapter = timerAdapter + //set empty page transformer to disable item animations + timer_view_pager.setPageTransformer { _, _ -> } timer_view_pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { updateViews(position) @@ -64,16 +71,13 @@ class TimerFragment : Fragment() { timer_play_pause.applyColorFilter(if (activity?.getAdjustedPrimaryColor() == Color.WHITE) Color.BLACK else Color.WHITE) timer_reset.applyColorFilter(textColor) - timer_play_pause.setOnClickListener { val timer = timerAdapter.getItemAt(timer_view_pager.currentItem) when (val state = timer.state) { -// is TimerState.Idle -> EventBus.getDefault().post(TimerState.Start(timer.seconds.secondsToMillis)) -// is TimerState.Paused -> EventBus.getDefault().post(TimerState.Start(state.tick)) -// is TimerState.Running -> EventBus.getDefault().post(TimerState.Pause(state.tick)) -// is TimerState.Finished -> EventBus.getDefault().post(TimerState.Start(timer.seconds.secondsToMillis)) - else -> { - } + is TimerState.Idle -> EventBus.getDefault().post(TimerEvent.Start(timer.id!!, timer.seconds.secondsToMillis)) + is TimerState.Paused -> EventBus.getDefault().post(TimerEvent.Start(timer.id!!, state.tick)) + is TimerState.Running -> EventBus.getDefault().post(TimerEvent.Pause(timer.id!!, state.tick)) + is TimerState.Finished -> EventBus.getDefault().post(TimerEvent.Start(timer.id!!, timer.seconds.secondsToMillis)) } } @@ -95,67 +99,39 @@ class TimerFragment : Fragment() { } private fun updateViews(position: Int) { - val timer = timerAdapter.getItemAt(position) - //check if timer is running to update view + activity?.runOnUiThread { + val timer = timerAdapter.getItemAt(position) + updateViewStates(timer.state) + } } private fun refreshTimers(scrollToLast: Boolean = false) { activity?.timerHelper?.getTimers { timers -> - timerAdapter.submitList(timers) - activity?.runOnUiThread { - view.timer_delete.beVisibleIf(timers.size > 1) + Log.d(TAG, "refreshTimers: $timers") + timerAdapter.submitList(timers) { if (scrollToLast) { view.timer_view_pager.currentItem = timers.lastIndex } + updateViews(timer_view_pager.currentItem) } } } private fun stopTimer(timer: Timer) { - EventBus.getDefault().post(TimerState.Idle) + EventBus.getDefault().post(TimerEvent.Reset(timer.id!!, timer.seconds.secondsToMillis)) activity?.hideTimerNotification() -// view.timer_time.text = activity?.config?.timerSeconds?.getFormattedDuration() - } - - private fun changeDuration() { - MyTimePickerDialogDialog(activity as SimpleActivity, requireContext().config.timerSeconds) { seconds -> - val timerSeconds = if (seconds <= 0) 10 else seconds - activity?.config?.timerSeconds = timerSeconds - val duration = timerSeconds.getFormattedDuration() -// view.timer_initial_time.text = duration - -// if (view.timer_reset.isGone()) { -// stopTimer() -// } - } } @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Idle) { -// view.timer_time.text = requiredActivity.config.timerSeconds.getFormattedDuration() - updateViewStates(state) - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Running) { -// view.timer_time.text = state.tick.div(1000F).roundToInt().getFormattedDuration() - updateViewStates(state) - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Paused) { - updateViewStates(state) - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Finished) { -// view.timer_time.text = 0.getFormattedDuration() - updateViewStates(state) + fun onMessageEvent(event: TimerEvent.Refresh) { + Log.d(TAG, "onMessageEvent: $event") + refreshTimers() } private fun updateViewStates(state: TimerState) { val resetPossible = state is TimerState.Running || state is TimerState.Paused || state is TimerState.Finished view.timer_reset.beVisibleIf(resetPossible) + view.timer_delete.beVisibleIf(!resetPossible && timerAdapter.itemCount > 1) val drawableId = if (state is TimerState.Running) { R.drawable.ic_pause_vector @@ -171,10 +147,15 @@ class TimerFragment : Fragment() { view.timer_play_pause.setImageDrawable(resources.getColoredDrawableWithColor(drawableId, iconColor)) } -// -// fun updateAlarmSound(alarmSound: AlarmSound) { -// activity?.config?.timerSoundTitle = alarmSound.title -// activity?.config?.timerSoundUri = alarmSound.uri -// view.timer_sound.text = alarmSound.title -// } + + fun updateAlarmSound(alarmSound: AlarmSound) { + val timer = timerAdapter.getItemAt(timer_view_pager.currentItem) + activity?.timerHelper?.insertOrUpdateTimer(timer.copy(soundTitle = alarmSound.title, soundUri = alarmSound.uri)) { + refreshTimers() + } + } + + companion object { + private const val TAG = "TimerFragment" + } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt index 19ba8266..8a60c2e6 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt @@ -45,6 +45,7 @@ const val TAB_CLOCK = 0 const val TAB_ALARM = 1 const val TAB_STOPWATCH = 2 const val TAB_TIMER = 3 +const val TIMER_ID = "timer_position" // stopwatch sorting const val SORT_BY_LAP = 1 diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Converters.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Converters.kt index 499733ec..9b6a16f0 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Converters.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Converters.kt @@ -1,12 +1,11 @@ package com.simplemobiletools.clock.helpers import androidx.room.TypeConverter -import com.google.gson.Gson +import com.simplemobiletools.clock.extensions.gson.gson import com.simplemobiletools.clock.models.StateWrapper import com.simplemobiletools.clock.models.TimerState class Converters { - private val gson = Gson() @TypeConverter fun jsonToTimerState(value: String): TimerState { diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt index 29b84d47..d6c8ea0b 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt @@ -2,6 +2,7 @@ package com.simplemobiletools.clock.helpers import android.content.Context import android.media.RingtoneManager +import android.util.Log import com.simplemobiletools.clock.extensions.timerDb import com.simplemobiletools.clock.models.Timer import com.simplemobiletools.clock.models.TimerState @@ -12,15 +13,22 @@ import com.simplemobiletools.commons.helpers.ensureBackgroundThread class TimerHelper(val context: Context) { private val timerDao = context.timerDb - fun getTimers(callback: (notes: List) -> Unit) { + fun getTimers(callback: (timers: List) -> Unit) { ensureBackgroundThread { callback.invoke(timerDao.getTimers()) } } + fun getTimer(timerId: Long, callback: (timer: Timer) -> Unit) { + ensureBackgroundThread { + callback.invoke(timerDao.getTimer(timerId)) + } + } + fun insertOrUpdateTimer(timer: Timer, callback: () -> Unit = {}) { ensureBackgroundThread { - timerDao.insertOrUpdateTimer(timer) + val id = timerDao.insertOrUpdateTimer(timer) + Log.d(TAG, "insertOrUpdateTimer: $id") callback.invoke() } } @@ -48,4 +56,8 @@ class TimerHelper(val context: Context) { callback.invoke() } } + + companion object { + private const val TAG = "TimerHelper" + } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt b/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt index d5595aec..b29d3fc8 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt @@ -12,6 +12,9 @@ interface TimerDao { @Query("SELECT * FROM timers") fun getTimers(): List + @Query("SELECT * FROM timers WHERE id=:id") + fun getTimer(id: Long): Timer + @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertOrUpdateTimer(timer: Timer): Long diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt new file mode 100644 index 00000000..c5ae5ec5 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt @@ -0,0 +1,9 @@ +package com.simplemobiletools.clock.models + +sealed class TimerEvent(open val timerId: Long) { + data class Reset(override val timerId: Long, val duration: Long) : TimerEvent(timerId) + data class Start(override val timerId: Long, val duration: Long) : TimerEvent(timerId) + data class Pause(override val timerId: Long, val duration: Long) : TimerEvent(timerId) + data class Finish(override val timerId: Long, val duration: Long) : TimerEvent(timerId) + data class Refresh(override val timerId: Long) : TimerEvent(timerId) +} diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerState.kt b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerState.kt index e45239e3..ad8f3a6e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerState.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerState.kt @@ -2,10 +2,7 @@ package com.simplemobiletools.clock.models sealed class TimerState { object Idle : TimerState() - data class Start(val duration: Long) : TimerState() data class Running(val duration: Long, val tick: Long) : TimerState() - data class Pause(val duration: Long) : TimerState() data class Paused(val duration: Long, val tick: Long) : TimerState() - data class Finish(val duration: Long) : TimerState() object Finished : TimerState() } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt index f0f92c8a..6ea59b28 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt @@ -76,7 +76,7 @@ class TimerService : Service() { .setContentTitle(label) .setContentText(formattedDuration) .setSmallIcon(R.drawable.ic_timer) - .setContentIntent(this.getOpenTimerTabIntent()) + .setContentIntent(this.getOpenTimerTabIntent(0)) .setPriority(Notification.PRIORITY_DEFAULT) .setSound(null) .setOngoing(true) diff --git a/app/src/main/res/layout/fragment_timer.xml b/app/src/main/res/layout/fragment_timer.xml index 87297742..7e321090 100644 --- a/app/src/main/res/layout/fragment_timer.xml +++ b/app/src/main/res/layout/fragment_timer.xml @@ -24,7 +24,7 @@ android:background="?attr/selectableItemBackgroundBorderless" android:padding="@dimen/normal_margin" android:src="@drawable/ic_reset_vector" - android:visibility="gone" + android:visibility="visible" app:layout_constraintBottom_toBottomOf="@+id/timer_play_pause" app:layout_constraintEnd_toStartOf="@+id/timer_play_pause" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout/item_timer.xml b/app/src/main/res/layout/item_timer.xml index 1f0cf8ac..d4e0c7aa 100644 --- a/app/src/main/res/layout/item_timer.xml +++ b/app/src/main/res/layout/item_timer.xml @@ -1,111 +1,103 @@ - + android:layout_height="match_parent"> - + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/normal_margin" + android:background="?attr/selectableItemBackground" + android:gravity="center_horizontal" + android:padding="@dimen/small_margin" + android:textSize="@dimen/stopwatch_text_size" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="00:00" /> - + + + + - - + android:text="@string/vibrate" + android:textSize="@dimen/bigger_text_size" /> - + + + + + + + + - - - - - - - - - - - - - - - - - + android:layout_marginStart="10dp" + android:hint="@string/label" + android:maxLines="1" + android:singleLine="true" + android:textCursorDrawable="@null" + android:textSize="@dimen/normal_text_size" /> + +