From 3f173a0f0a3448a5aeab4683209e8480f1968849 Mon Sep 17 00:00:00 2001 From: Vyacheslav Tretyakov Date: Wed, 30 Mar 2022 22:25:32 +0500 Subject: [PATCH] =?UTF-8?q?=D0=A2=D1=80=D0=B5=D1=82=D1=8C=D1=8F=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=92.=D0=9D.=20Homework2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iam/thevoid/epic/timeapp/MainActivity.kt | 165 +++++++++++++++++- app/src/main/res/layout/activity_main.xml | 7 +- 2 files changed, 166 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/iam/thevoid/epic/timeapp/MainActivity.kt b/app/src/main/java/iam/thevoid/epic/timeapp/MainActivity.kt index d08be6e..3fde048 100644 --- a/app/src/main/java/iam/thevoid/epic/timeapp/MainActivity.kt +++ b/app/src/main/java/iam/thevoid/epic/timeapp/MainActivity.kt @@ -1,10 +1,19 @@ package iam.thevoid.epic.timeapp -import androidx.appcompat.app.AppCompatActivity +import android.graphics.Color import android.os.Bundle import android.widget.Button import android.widget.EditText +import android.widget.FrameLayout import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.schedulers.Schedulers +import java.text.SimpleDateFormat +import java.util.* +import java.util.concurrent.TimeUnit // Дан экран с готовой разметкой // Реализовать при помощи RxJava @@ -35,26 +44,176 @@ class MainActivity : AppCompatActivity() { private lateinit var countdownText: TextView private lateinit var countdownSecondsEditText: EditText private lateinit var countdownStartButton: Button + private lateinit var timerLayout: FrameLayout // Секундомер private lateinit var stopwatchText: TextView private lateinit var stopwatchMillisText: TextView - private lateinit var stopwatchStartButton: EditText - private lateinit var stopwatchEndButton: EditText + private lateinit var stopwatchStartButton: Button + private lateinit var stopwatchEndButton: Button + private val compositeDisposable = CompositeDisposable() + + + companion object { + const val BASE_MAIN_TIME = "00:00:00" + const val BASE_MS_TIME = "000" + } + + /** Именования кнопок */ + enum class EButton(val title: String) { + PAUSE("PAUSE"), + RESET("RESET"), + START("START"), + CONTINUE("CONTINUE") + } + + /** Состояния секундомера */ + enum class EState() { + RUNNING, STOPPED, PAUSE + } + + /* Текущее состояние секундомера */ + private var stopWatchState = EState.STOPPED + + /* Последнее время сенкудомера */ + private var lastMilliseconds = 0L override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + /* Часы */ clockText = findViewById(R.id.clockText) + currentTimeObserver() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + clockText.text = it + } + + /* Обратный отсчёт */ countdownText = findViewById(R.id.countdownText) countdownSecondsEditText = findViewById(R.id.countdownEditText) countdownStartButton = findViewById(R.id.countdownStartButton) + timerLayout = findViewById(R.id.timer_layout) + countdownStartButton.setOnClickListener { + val interval = countdownSecondsEditText.text.toString() + if (interval.isNotEmpty()) { + countdownStartButton.isEnabled = false + downWatchObserver(interval.toLong()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { countdownText.text = it.toString() }, {}, + { + /* Вопрос: как можно избавиться от вложенного Observable и избежать роста цепочки? */ + handleTimerLayout() + } + ) + } + } + + // Секундомер stopwatchText = findViewById(R.id.stopwatchText) stopwatchMillisText = findViewById(R.id.stopwatchMillisText) stopwatchStartButton = findViewById(R.id.stopwatchStartButton) stopwatchEndButton = findViewById(R.id.stopwatchEndButton) + + stopwatchEndButton.setOnClickListener { + if (stopWatchState == EState.RUNNING) { + stopwatchEndButton.text = EButton.RESET.title + stopwatchStartButton.text = EButton.CONTINUE.title + + stopWatchState = EState.PAUSE + compositeDisposable.clear() + } else if (stopWatchState == EState.PAUSE) { + lastMilliseconds = 0L + + stopwatchEndButton.text = EButton.PAUSE.title + stopwatchStartButton.text = EButton.START.title + + stopwatchText.text = BASE_MAIN_TIME + stopwatchMillisText.text = BASE_MS_TIME + + stopWatchState = EState.STOPPED + } + } + + stopwatchStartButton.setOnClickListener { + if (stopWatchState == EState.STOPPED) { + stopWatchState = EState.RUNNING + } else if (stopWatchState == EState.PAUSE) { + stopWatchState = EState.RUNNING + stopwatchEndButton.text = EButton.PAUSE.title + } + + compositeDisposable.add( + stopWatchObserver(lastMilliseconds) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + stopwatchText.text = it.first + stopwatchMillisText.text = it.second + }) + } + } + + private fun handleTimerLayout() { + timerLayoutBgColorObserver() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + timerLayout.setBackgroundColor(it) + }, {}, { + countdownStartButton.isEnabled = true + timerLayout.setBackgroundColor(Color.WHITE) + }) } + + /** Возвращает время секундомера начиная с заданного времени в мс [lastMs] */ + private fun stopWatchObserver(lastMs: Long) = Observable + .interval(1, TimeUnit.MILLISECONDS, Schedulers.computation()) + .map { formatElapsedTime(it + lastMs) } + + /** Возврващет время в формате HH:mm:ss:mls для заданного кол-ва миллисекунд [elapsed] */ + private fun formatElapsedTime(elapsed: Long): Pair { + val milliseconds = elapsed % 1000 + val seconds = elapsed / 1000 + val secondsFormatted = (seconds % 60) + val minutes = seconds / 60 + val minutesFormatted = (minutes % 60) + val hoursFormatted = minutes / 60 + lastMilliseconds = elapsed + return Pair( + String.format( + "%02d:%02d:%02d", + hoursFormatted, + minutesFormatted, + secondsFormatted + ), + String.format("%03d", milliseconds) + ) + } + + /** Поочередно возвращает цвет для фона (10 раз) каждые 0.5 секунд */ + private fun timerLayoutBgColorObserver() = Observable + .intervalRange(0, 10, 0, 500, TimeUnit.MILLISECONDS, Schedulers.io()) + .map { + if (it.toInt() % 2 == 0) + Color.GREEN + else Color.WHITE + } + + /** Возвращает текущее время */ + private fun currentTimeObserver() = Observable + .interval(1, TimeUnit.SECONDS, Schedulers.io()) + .map { getCurrentTime() } + + /** Возвращает текущее время в формате HH:mm:ss */ + private fun getCurrentTime() = + SimpleDateFormat("HH:mm:ss", Locale.ROOT).format(Date()) + + /** Возвращает секунды в обратном порядке начиная с [interval] */ + private fun downWatchObserver(interval: Long) = Observable + .interval(1, TimeUnit.SECONDS, Schedulers.io()) + .takeWhile { it <= interval } + .map { interval - it } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 81ade1e..e62d44f 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -30,6 +30,7 @@ android:background="#ccc" /> @@ -59,12 +60,12 @@ tools:text="[02:37]" />