From c3ab1f9330ff8827248340af1eaa4afe9cc54aea Mon Sep 17 00:00:00 2001 From: Pavol Franek <> Date: Wed, 4 Mar 2020 16:27:48 +0100 Subject: [PATCH] wip 2 --- .../clock/extensions/Logs.kt | 7 + .../clock/extensions/Long.kt | 9 + .../clock/fragments/TimerFragment.kt | 194 ++++++------------ .../simplemobiletools/clock/helpers/Config.kt | 5 + .../clock/helpers/Constants.kt | 1 + .../clock/workers/timerWorker.kt | 23 ++- 6 files changed, 99 insertions(+), 140 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/clock/extensions/Logs.kt diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Logs.kt b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Logs.kt new file mode 100644 index 00000000..4a662681 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Logs.kt @@ -0,0 +1,7 @@ +package com.simplemobiletools.clock.extensions + +import android.util.Log +import com.simplemobiletools.clock.BuildConfig + +fun A.log(tag: String) = apply { if (BuildConfig.DEBUG) Log.wtf(tag, this.toString()) } +fun A.log(first: String, tag: String) = apply { if (BuildConfig.DEBUG) Log.wtf(tag, first) } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Long.kt b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Long.kt index 2b29f682..e4eac62a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Long.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Long.kt @@ -1,5 +1,7 @@ package com.simplemobiletools.clock.extensions +import android.text.format.DateFormat +import java.util.* import java.util.concurrent.TimeUnit fun Long.formatStopwatchTime(useLongerMSFormat: Boolean): String { @@ -27,3 +29,10 @@ fun Long.formatStopwatchTime(useLongerMSFormat: Boolean): String { } } } + +fun Long.timestampFormat(format: String = "dd. MM. yyyy"): String { + val calendar = Calendar.getInstance(Locale.getDefault()) + calendar.timeInMillis = this + + return DateFormat.format(format, calendar).toString() +} 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 1096b032..9447f4f8 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt @@ -4,15 +4,12 @@ import android.annotation.TargetApi import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager -import android.app.NotificationManager.IMPORTANCE_HIGH import android.content.Context -import android.content.Intent import android.graphics.Color import android.media.AudioManager import android.os.Build import android.os.Bundle -import android.os.Handler -import android.os.SystemClock +import android.os.CountDownTimer import android.util.Log import android.view.LayoutInflater import android.view.View @@ -20,16 +17,16 @@ import android.view.ViewGroup import androidx.core.app.NotificationCompat import androidx.fragment.app.Fragment import androidx.lifecycle.Observer -import androidx.lifecycle.lifecycleScope import androidx.work.WorkInfo import androidx.work.WorkManager import com.simplemobiletools.clock.R -import com.simplemobiletools.clock.activities.ReminderActivity import com.simplemobiletools.clock.activities.SimpleActivity import com.simplemobiletools.clock.dialogs.MyTimePickerDialogDialog import com.simplemobiletools.clock.extensions.* +import com.simplemobiletools.clock.helpers.Config import com.simplemobiletools.clock.helpers.PICK_AUDIO_FILE_INTENT_ID import com.simplemobiletools.clock.helpers.TIMER_NOTIF_ID +import com.simplemobiletools.clock.workers.TIMER_WORKER_KEY import com.simplemobiletools.clock.workers.cancelTimerWorker import com.simplemobiletools.clock.workers.enqueueTimerWorker import com.simplemobiletools.clock.workers.timerRequestId @@ -38,56 +35,35 @@ import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.ALARM_SOUND_TYPE_ALARM import com.simplemobiletools.commons.helpers.isOreoPlus import com.simplemobiletools.commons.models.AlarmSound -import kotlinx.android.synthetic.main.fragment_timer.* import kotlinx.android.synthetic.main.fragment_timer.view.* -import kotlinx.android.synthetic.main.fragment_timer.view.timer_time -import kotlinx.coroutines.InternalCoroutinesApi -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.asFlow -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch import java.util.concurrent.TimeUnit class TimerFragment : Fragment() { - private val UPDATE_INTERVAL = 1000L - private val WAS_RUNNING = "was_running" - private val CURRENT_TICKS = "current_ticks" - private val TOTAL_TICKS = "total_ticks" - - private var isRunning = false - private var initialSecs = 0 - private var totalTicks = 0 - private var currentTicks = 0 - lateinit var view: ViewGroup + private var timer: CountDownTimer? = null - @InternalCoroutinesApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { val config = requiredActivity.config view = (inflater.inflate(R.layout.fragment_timer, container, false) as ViewGroup).apply { timer_time.setOnClickListener { - val selectedDuration = config.timerSeconds - enqueueTimerWorker(TimeUnit.SECONDS.toMillis(selectedDuration.toLong())) - showNotification(selectedDuration.getFormattedDuration()) + startTimer(config) } timer_play_pause.setOnClickListener { - val selectedDuration = config.timerSeconds - enqueueTimerWorker(TimeUnit.SECONDS.toMillis(selectedDuration.toLong())) - showNotification(selectedDuration.getFormattedDuration()) + startTimer(config) } timer_reset.setOnClickListener { cancelTimerWorker() + requiredActivity.toast(R.string.timer_stopped) } timer_initial_time.setOnClickListener { MyTimePickerDialogDialog(activity as SimpleActivity, config.timerSeconds) { seconds -> val timerSeconds = if (seconds <= 0) 10 else seconds config.timerSeconds = timerSeconds + config.timerTimeStamp = System.currentTimeMillis() + timerSeconds timer_initial_time.text = timerSeconds.getFormattedDuration() } } @@ -99,69 +75,70 @@ class TimerFragment : Fragment() { timer_sound.setOnClickListener { SelectAlarmSoundDialog(activity as SimpleActivity, config.timerSoundUri, AudioManager.STREAM_ALARM, PICK_AUDIO_FILE_INTENT_ID, - ALARM_SOUND_TYPE_ALARM, true, - onAlarmPicked = { sound -> - if (sound != null) { - updateAlarmSound(sound) - } - }, - onAlarmSoundDeleted = { sound -> - if (config.timerSoundUri == sound.uri) { - val defaultAlarm = context.getDefaultAlarmSound(ALARM_SOUND_TYPE_ALARM) - updateAlarmSound(defaultAlarm) - } + ALARM_SOUND_TYPE_ALARM, true, + onAlarmPicked = { sound -> + if (sound != null) { + updateAlarmSound(sound) + } + }, + onAlarmSoundDeleted = { sound -> + if (config.timerSoundUri == sound.uri) { + val defaultAlarm = context.getDefaultAlarmSound(ALARM_SOUND_TYPE_ALARM) + updateAlarmSound(defaultAlarm) + } - context.checkAlarmsWithDeletedSoundUri(sound.uri) - }) + context.checkAlarmsWithDeletedSoundUri(sound.uri) + }) } - } - initialSecs = config.timerSeconds + WorkManager.getInstance(requiredActivity).getWorkInfosByTagLiveData(TIMER_WORKER_KEY).observe(requiredActivity, Observer { workInfo -> + workInfo.log("log") - WorkManager.getInstance(activity!!).getWorkInfoByIdLiveData(timerRequestId) - .observe(this, Observer { workInfo -> - Log.e("test", workInfo.toString()) + val workerState = workInfo?.firstOrNull()?.state + when (workerState) { + WorkInfo.State.ENQUEUED -> { + timer?.cancel() + timer = object : CountDownTimer(config.timerSeconds.toLong().times(1000), 1000) { + override fun onTick(millisUntilFinished: Long) { + timer_time.text = millisUntilFinished.div(1000).toInt().getFormattedDuration() + } - when (workInfo.state) { - WorkInfo.State.SUCCEEDED -> { - showNotification("-") + override fun onFinish() {} + }.start() } - WorkInfo.State.FAILED -> {} - WorkInfo.State.CANCELLED -> {} - else -> {} - } - }) - val a = lifecycleScope.launch { - (config.timerSeconds downTo 0).asFlow().onEach { delay(1000) }.collect { - Log.e("test", it.toString()) - timer_time.text = it.getFormattedDuration() - } + else -> { + timer_time.text = 0.getFormattedDuration() + } + } + + updateIcons(workerState == WorkInfo.State.ENQUEUED) + timer_reset.beVisibleIf(workerState == WorkInfo.State.ENQUEUED) + }) } return view } + private fun startTimer(config: Config) { + val selectedDuration = config.timerSeconds + val formattedTimestamp = config.timerTimeStamp.timestampFormat("HH:mm:ss") + enqueueTimerWorker(TimeUnit.SECONDS.toMillis(selectedDuration.toLong())) + showNotification("(${selectedDuration.getFormattedDuration()}) $formattedTimestamp") + } + override fun onResume() { super.onResume() setupViews() } override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState.apply { - putBoolean(WAS_RUNNING, isRunning) - putInt(TOTAL_TICKS, totalTicks) - putInt(CURRENT_TICKS, currentTicks) - }) + super.onSaveInstanceState(outState.apply {}) } override fun onViewStateRestored(savedInstanceState: Bundle?) { super.onViewStateRestored(savedInstanceState) - savedInstanceState?.apply { - isRunning = getBoolean(WAS_RUNNING, false) - totalTicks = getInt(TOTAL_TICKS, 0) - currentTicks = getInt(CURRENT_TICKS, 0) - } + savedInstanceState?.apply {} } fun updateAlarmSound(alarmSound: AlarmSound) { @@ -170,6 +147,10 @@ class TimerFragment : Fragment() { view.timer_sound.text = alarmSound.title } +// private val isRunning +// get(): Boolean = +// WorkManager.getInstance(requiredActivity).getWorkInfosByTagLiveData(TIMER_WORKER_KEY).value?.firstOrNull()?.state == WorkInfo.State.ENQUEUED + private fun setupViews() { val config = requiredActivity.config val textColor = config.textColor @@ -188,11 +169,9 @@ class TimerFragment : Fragment() { timer_sound.text = config.timerSoundTitle timer_sound.colorLeftDrawable(textColor) } - - updateIcons() } - private fun updateIcons() { + private fun updateIcons(isRunning: Boolean) { val drawableId = if (isRunning) R.drawable.ic_pause_vector else R.drawable.ic_play_vector val iconColor = if (requiredActivity.getAdjustedPrimaryColor() == Color.WHITE) Color.BLACK else requiredActivity.config.textColor view.timer_play_pause.setImageDrawable(resources.getColoredDrawableWithColor(drawableId, iconColor)) @@ -212,62 +191,17 @@ class TimerFragment : Fragment() { } val builder = NotificationCompat.Builder(context) - .setContentTitle(label) - .setContentText(formattedDuration) - .setSmallIcon(R.drawable.ic_timer) - .setContentIntent(context!!.getOpenTimerTabIntent()) - .setPriority(Notification.PRIORITY_HIGH) - .setSound(null) - .setOngoing(true) - .setAutoCancel(true) - .setChannelId(channelId) + .setContentTitle(label) + .setContentText(formattedDuration) + .setSmallIcon(R.drawable.ic_timer) + .setContentIntent(context!!.getOpenTimerTabIntent()) + .setPriority(Notification.PRIORITY_HIGH) + .setSound(null) + .setOngoing(true) + .setAutoCancel(true) + .setChannelId(channelId) builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) notificationManager.notify(TIMER_NOTIF_ID, builder.build()) } } - -// private fun resetTimer() { -// updateHandler.removeCallbacks(updateRunnable) -// isRunning = false -// currentTicks = 0 -// totalTicks = 0 -// initialSecs = context!!.config.timerSeconds -// updateDisplayedText() -// updateIcons() -// view.timer_reset.beGone() -// requiredActivity.hideTimerNotification() -// context!!.hideNotification(TIMER_NOTIF_ID) -// context?.toast(R.string.timer_stopped) -// } - -// private fun updateDisplayedText(): Boolean { -// val diff = initialSecs - totalTicks -// var formattedDuration = Math.abs(diff).getFormattedDuration() -// -// if (diff < 0) { -// formattedDuration = "-$formattedDuration" -// if (!isForegrounded) { -// resetTimer() -// return false -// } -// } -// -// view.timer_time.text = formattedDuration -// if (diff == 0) { -// if (context?.isScreenOn() == true) { -// context!!.showTimerNotification(false) -// Handler().postDelayed({ -// context?.hideTimerNotification() -// }, context?.config!!.timerMaxReminderSecs * 1000L) -// } else { -// Intent(context, ReminderActivity::class.java).apply { -// activity?.startActivity(this) -// } -// } -// } else if (diff > 0 && !isForegrounded && isRunning) { -// showNotification(formattedDuration) -// } -// -// return true -// } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Config.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Config.kt index e0ebdcda..06f9ab40 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Config.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Config.kt @@ -5,6 +5,7 @@ import com.simplemobiletools.commons.extensions.getDefaultAlarmTitle import com.simplemobiletools.commons.extensions.getDefaultAlarmUri import com.simplemobiletools.commons.helpers.ALARM_SOUND_TYPE_ALARM import com.simplemobiletools.commons.helpers.BaseConfig +import java.util.concurrent.TimeUnit class Config(context: Context) : BaseConfig(context) { companion object { @@ -27,6 +28,10 @@ class Config(context: Context) : BaseConfig(context) { get() = prefs.getInt(TIMER_SECONDS, 300) set(lastTimerSeconds) = prefs.edit().putInt(TIMER_SECONDS, lastTimerSeconds).apply() + var timerTimeStamp: Long + get() = prefs.getLong(TIMER_TIMESTAMP, System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(timerSeconds.toLong())) + set(timestamp) = prefs.edit().putLong(TIMER_TIMESTAMP, timestamp).apply() + var timerVibrate: Boolean get() = prefs.getBoolean(TIMER_VIBRATE, false) set(timerVibrate) = prefs.edit().putBoolean(TIMER_VIBRATE, timerVibrate).apply() 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 4f6ce6b8..613c4227 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt @@ -8,6 +8,7 @@ const val SHOW_SECONDS = "show_seconds" const val SELECTED_TIME_ZONES = "selected_time_zones" const val EDITED_TIME_ZONE_TITLES = "edited_time_zone_titles" const val TIMER_SECONDS = "timer_seconds" +const val TIMER_TIMESTAMP = "timer_timetamp" const val TIMER_VIBRATE = "timer_vibrate" const val TIMER_SOUND_URI = "timer_sound_uri" const val TIMER_SOUND_TITLE = "timer_sound_title" diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/workers/timerWorker.kt b/app/src/main/kotlin/com/simplemobiletools/clock/workers/timerWorker.kt index 07bf2b75..c3bd62fa 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/workers/timerWorker.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/workers/timerWorker.kt @@ -3,39 +3,42 @@ package com.simplemobiletools.clock.workers import android.content.Context import androidx.fragment.app.Fragment import androidx.work.* -import com.simplemobiletools.clock.extensions.preferences -import com.simplemobiletools.clock.extensions.requiredActivity +import com.simplemobiletools.clock.extensions.* import java.util.* import java.util.concurrent.TimeUnit private const val TIMER_REQUEST_ID = "TIMER_REQUEST_ID" -private const val TIMER_WORKER_KEY = "TIMER_WORKER_KEY" +const val TIMER_WORKER_KEY = "TIMER_WORKER_KEY" private fun Fragment.saveTimerRequestId(uuid: UUID) = preferences.edit().putString(TIMER_REQUEST_ID, uuid.toString()).apply() -val Fragment.timerRequestId: UUID get() = - UUID.fromString(preferences.getString(TIMER_REQUEST_ID, UUID.randomUUID().toString())) +val Fragment.timerRequestId: UUID? get() = + preferences.getString(TIMER_REQUEST_ID, UUID.randomUUID().toString())?.let { UUID.fromString(it) } fun Fragment.cancelTimerWorker() = - WorkManager.getInstance(requiredActivity).apply { - timerRequestId.let(::cancelWorkById) - } + WorkManager.getInstance(requiredActivity).cancelAllWorkByTag(TIMER_WORKER_KEY) fun Fragment.enqueueTimerWorker(delay: Long) = WorkManager.getInstance(requiredActivity).enqueueUniqueWork(TIMER_WORKER_KEY, ExistingWorkPolicy.REPLACE, timerRequest(delay)) private fun Fragment.timerRequest(delay: Long) = - OneTimeWorkRequestBuilder().setInitialDelay(delay, TimeUnit.MILLISECONDS).build().also { + OneTimeWorkRequestBuilder().setInitialDelay(delay, TimeUnit.MILLISECONDS).addTag(TIMER_WORKER_KEY).build().also { saveTimerRequestId(it.id) } -class TimerWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { +class TimerWorker(val context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { override fun doWork(): Result = try { + context.showTimerNotification(false) Result.success() } catch (exception: Exception) { Result.failure() } + + override fun onStopped() { + super.onStopped() + context.hideTimerNotification() + } }