mirror of
https://github.com/SimpleMobileTools/Simple-Clock.git
synced 2025-02-21 06:11:33 +01:00
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
This commit is contained in:
parent
921ca92885
commit
8474e6a800
@ -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<Long, CountDownTimer>()
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
@ -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<Timer, TimerAdapter.TimerViewHolder>(diffUtil) {
|
||||
|
||||
private val config = activity.config
|
||||
) : ListAdapter<Timer, TimerAdapter.TimerViewHolder>(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<Timer>() {
|
||||
private const val TAG = "TimerAdapter"
|
||||
private val diffUtil = object : DiffUtil.ItemCallback<Timer>() {
|
||||
override fun areItemsTheSame(oldItem: Timer, newItem: Timer): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -7,11 +7,8 @@ import com.simplemobiletools.clock.models.TimerState
|
||||
|
||||
val timerStates = valueOf<TimerState>()
|
||||
.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 <reified T : Any> valueOf(): RuntimeTypeAdapterFactory<T> = RuntimeTypeAdapterFactory.of(T::class.java)
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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<Timer>) -> Unit) {
|
||||
fun getTimers(callback: (timers: List<Timer>) -> 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"
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,9 @@ interface TimerDao {
|
||||
@Query("SELECT * FROM timers")
|
||||
fun getTimers(): List<Timer>
|
||||
|
||||
@Query("SELECT * FROM timers WHERE id=:id")
|
||||
fun getTimer(id: Long): Timer
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertOrUpdateTimer(timer: Timer): Long
|
||||
|
||||
|
@ -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)
|
||||
}
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -1,111 +1,103 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/timer_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
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" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/timer_time"
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/timer_initial_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/activity_margin"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawableStart="@drawable/ic_timer"
|
||||
android:drawablePadding="@dimen/normal_margin"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:textSize="@dimen/bigger_text_size"
|
||||
app:layout_constraintTop_toBottomOf="@+id/timer_time"
|
||||
tools:text="05:00" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/timer_vibrate_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:padding="@dimen/activity_margin"
|
||||
app:layout_constraintTop_toBottomOf="@+id/timer_initial_time">
|
||||
|
||||
<com.simplemobiletools.commons.views.MySwitchCompat
|
||||
android:id="@+id/timer_vibrate"
|
||||
android:layout_width="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" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/timer_initial_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/activity_margin"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawableStart="@drawable/ic_timer"
|
||||
android:background="@null"
|
||||
android:clickable="false"
|
||||
android:drawableStart="@drawable/ic_vibrate_vector"
|
||||
android:drawablePadding="@dimen/normal_margin"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:textSize="@dimen/bigger_text_size"
|
||||
app:layout_constraintTop_toBottomOf="@+id/timer_time"
|
||||
tools:text="05:00" />
|
||||
android:text="@string/vibrate"
|
||||
android:textSize="@dimen/bigger_text_size" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/timer_vibrate_holder"
|
||||
</RelativeLayout>
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/timer_sound"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawableStart="@drawable/ic_bell_vector"
|
||||
android:drawablePadding="@dimen/normal_margin"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:textSize="@dimen/bigger_text_size"
|
||||
app:layout_constraintTop_toBottomOf="@+id/timer_vibrate_holder"
|
||||
tools:text="Default alarm" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/timer_label_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/medium_margin"
|
||||
android:paddingEnd="@dimen/activity_margin"
|
||||
android:paddingBottom="@dimen/medium_margin"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/timer_sound"
|
||||
app:layout_constraintVertical_bias="0"
|
||||
app:layout_constraintVertical_chainStyle="spread_inside">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/timer_label_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_label_vector" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyEditText
|
||||
android:id="@+id/timer_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:padding="@dimen/activity_margin"
|
||||
app:layout_constraintTop_toBottomOf="@+id/timer_initial_time">
|
||||
|
||||
<com.simplemobiletools.commons.views.MySwitchCompat
|
||||
android:id="@+id/timer_vibrate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
android:clickable="false"
|
||||
android:drawableStart="@drawable/ic_vibrate_vector"
|
||||
android:drawablePadding="@dimen/normal_margin"
|
||||
android:text="@string/vibrate"
|
||||
android:textSize="@dimen/bigger_text_size" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/timer_sound"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawableStart="@drawable/ic_bell_vector"
|
||||
android:drawablePadding="@dimen/normal_margin"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:textSize="@dimen/bigger_text_size"
|
||||
app:layout_constraintTop_toBottomOf="@+id/timer_vibrate_holder"
|
||||
tools:text="Default alarm" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/timer_label_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/medium_margin"
|
||||
android:paddingEnd="@dimen/activity_margin"
|
||||
android:paddingBottom="@dimen/medium_margin"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/timer_sound"
|
||||
app:layout_constraintVertical_bias="0"
|
||||
app:layout_constraintVertical_chainStyle="spread_inside">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/timer_label_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_label_vector" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyEditText
|
||||
android:id="@+id/timer_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:hint="@string/label"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textCursorDrawable="@null"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
|
||||
android:layout_marginStart="10dp"
|
||||
android:hint="@string/label"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textCursorDrawable="@null"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
Loading…
x
Reference in New Issue
Block a user