Skip to content
This repository has been archived by the owner. It is now read-only.
1 change: 1 addition & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ android {

dependencies {

implementation 'com.jakewharton.rxbinding3:rxbinding:3.0.0-alpha2'
implementation 'com.jakewharton.rxrelay2:rxrelay:2.1.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0-rc01'
//implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
//implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation "io.reactivex.rxjava2:rxkotlin:2.4.0"
// implementation "io.reactivex.rxjava2:rxandroid:2.0.2"
//
implementation "io.reactivex.rxjava3:rxjava:3.1.3"
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'

Expand All @@ -43,4 +51,5 @@ dependencies {
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

}
280 changes: 272 additions & 8 deletions app/src/main/java/iam/thevoid/epic/timeapp/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,21 +1,41 @@
package iam.thevoid.epic.timeapp

import androidx.appcompat.app.AppCompatActivity
//import io.reactivex.rxjava3.core.Observable
//
//
import android.annotation.SuppressLint
import android.graphics.Color
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.jakewharton.rxbinding3.view.clicks
import io.reactivex.Observable
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.annotations.NonNull
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxkotlin.merge
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.Flow
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicLong



// Дан экран с готовой разметкой
// Реализовать при помощи RxJava
// 1) Отображение часов,начинают работать при старте, показывают время в любом удобном формате.
// Как пример можно использовать формат из разметки
// 2) Таймер
// 2) Таймер - ^?
// а) Пользователь вводит количество секунд в поле
// б) По нажатию на "Старт" начинается обратный отсчёт
// в) (не обязательно) По окончании таймер каким либо образом сигнализирует об окончании,
// например область таймера вспыхивает ярким цветом
// 3) Секундомер
// 3) Секундомер ^? - Не до конца доделал - не детализированно как время считать ? - повтор 3
// а) Пользователь нажимает на "Старт", начинается отсчёт времени. В соответствующие текстовые
// поля выводится количество прошедшего времени (отдельно время с точностью до секунд,
// отдельно миллисекунды)
Expand All @@ -25,6 +45,15 @@ import android.widget.TextView
// отсчёт
// г) (не обязательно) Можно сделать изменение состояние кнопки "Старт" на "Продолжить" для
// состояния паузы
private const val MAXIMUM_STOP_WATCH_LIMIT = 36000000L
private const val NUMBER_OF_SECONDS_IN_ONE_MINUTE = 60
private const val NUMBER_OF_MINUTES_IN_ONE_HOUR = 60
private const val NUMBER_OF_MILLSECONDS_IN_ONE_SECOND = 1000


var elapsedTime = AtomicLong();
var resumed = AtomicBoolean();
var stopped = AtomicBoolean();

class MainActivity : AppCompatActivity() {

Expand All @@ -35,26 +64,261 @@ class MainActivity : AppCompatActivity() {
private lateinit var countdownText: TextView
private lateinit var countdownSecondsEditText: EditText
private lateinit var countdownStartButton: Button
private lateinit var countDownName: TextView

// Секундомер
private lateinit var stopwatchText: TextView
private lateinit var stopwatchMillisText: TextView
private lateinit var stopwatchStartButton: EditText
private lateinit var stopwatchEndButton: EditText
private lateinit var SecName: TextView
private lateinit var stopwatchStartButton: Button /*EditText*/
private lateinit var stopwatchEndButton: Button /*EditText*/


private val disposable =
io.reactivex.disposables.CompositeDisposable()
private val disposableMS = io.reactivex.disposables.CompositeDisposable()

var flowable: Disposable? = null

//var disposable: Disposable? = null;
private val displayInitialState by lazy { resources.getString(R.string._0_0) }
private val displayInitialStateMS by lazy { resources.getString(R.string._0_0_0) }



override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

clockText = findViewById(R.id.clockText)
clock()

countdownText = findViewById(R.id.countdownText)
countdownSecondsEditText = findViewById(R.id.countdownEditText)
countdownStartButton = findViewById(R.id.countdownStartButton)

countdownStartButton.setOnClickListener {
if (!(countdownSecondsEditText.text.isNullOrBlank())) {
timer(countdownSecondsEditText.text.toString().toLong())
countDownName.setText(R.string.count_down_name)
}
else
{
countDownName.text = getResources().getString(R.string.count_down_error)
}
}


stopwatchText = findViewById(R.id.stopwatchText)

stopwatchMillisText = findViewById(R.id.stopwatchMillisText)
stopwatchStartButton = findViewById(R.id.stopwatchStartButton)
stopwatchEndButton = findViewById(R.id.stopwatchEndButton)
//
SecName = findViewById(R.id.SecName)
countDownName = findViewById(R.id.countDownName)
//

stopwatchStartButton = findViewById(R.id.stopwatchStartButton)
stopwatchEndButton = findViewById(R.id.stopwatchEndButton)


stopwatchStartButton.setOnClickListener {
resumed.set(true)
stopped.set(false)
if ( stopwatchEndButton.text == "Reset" ) {
stopwatchEndButton.text = "Pause"
}

stopwatchStartButton.isEnabled = false
stopwatchEndButton.isEnabled = true
this.flowable = startTimer()
}

stopwatchEndButton.setOnClickListener {
//1st one
var secondPress = if (stopwatchEndButton.text.equals("Pause")) { 1 } else {2}
stopwatchStartButton.isEnabled = true
stopwatchEndButton.isEnabled = true

if (stopwatchEndButton.text.equals("Reset")) {
// so that we to define that pressed button second one
stopwatchStartButton.text = "Start"
stopwatchEndButton.text = "Pause"
stopwatchEndButton.isEnabled = false // that .. get confused
secondPress = 2
elapsedTime.set(0L);
// probably don't do it. leak?
//this.flowable?.dispose()
//this.flowable = null
}

if (stopwatchEndButton.text.equals("Pause") && secondPress == 1 )
{
stopwatchEndButton.text = "Reset"
//stopwatchStartButton.text = "Start"
stopwatchStartButton.text = "Continue"
}
//stopped.set(true)
stopTimer()
}

stopwatchEndButton.isEnabled = false

/* mergeClicks().switchMap {
if (it) timerObservable()
else Observable.just(displayInitialState)
}.subscribe(
{ s -> /*println(s)*/ stopwatchText.setText(s.substring(1,7))
stopwatchMillisText.setText(s)}
)
.let(disposable::add)
*/

/* mergeClicks().switchMap {
if (it) timerObservableMS()
else Observable.just(displayInitialStateMS)
}.subscribe(/*stopwatchMillisText*/SecName::setText)
.let(disposableMS::add)
*/

}

fun startTimer(): /*@NonNull*/ Disposable { //Create and starts ticker :)
return Flowable.interval(1, java.util.concurrent.TimeUnit.MILLISECONDS)
.onBackpressureBuffer ()
.takeWhile { !stopped.get() }
.filter { resumed.get() }
.map {
elapsedTime.addAndGet(
1
)
}
.observeOn (AndroidSchedulers.mainThread ())
.subscribe({ s: Long ->
val txtSec =
"${(((s / NUMBER_OF_MILLSECONDS_IN_ONE_SECOND ) / NUMBER_OF_SECONDS_IN_ONE_MINUTE) / NUMBER_OF_MINUTES_IN_ONE_HOUR ).toString().padStart(2,'0')} : ${((s / NUMBER_OF_MILLSECONDS_IN_ONE_SECOND ) / NUMBER_OF_SECONDS_IN_ONE_MINUTE).toString().padStart(2,'0')} : ${((s / NUMBER_OF_MILLSECONDS_IN_ONE_SECOND) % NUMBER_OF_SECONDS_IN_ONE_MINUTE).toString().padStart(2,'0')}"
//"${((s / NUMBER_OF_MILLSECONDS_IN_ONE_SECOND ) / NUMBER_OF_SECONDS_IN_ONE_MINUTE).toString().padStart(2,'0')} : ${((s / NUMBER_OF_MILLSECONDS_IN_ONE_SECOND) % NUMBER_OF_SECONDS_IN_ONE_MINUTE).toString().padStart(2,'0')}"
stopwatchText.setText(txtSec)
val txtMil ="${(s % NUMBER_OF_MILLSECONDS_IN_ONE_SECOND).toString().padStart(3,'0')}"
//"${(s % NUMBER_OF_MILLSECONDS_IN_ONE_SECOND) - (s / NUMBER_OF_MILLSECONDS_IN_ONE_SECOND) }"
stopwatchMillisText.setText(txtMil)
})
}


fun pauseTimer() {
resumed.set(false);
}
fun resumeTimer() {
resumed.set(true)
}
}

fun stopTimer() {
stopped.set(true)
}

fun addToTimer(seconds: Int) {
elapsedTime.addAndGet((seconds * 1000).toLong())
}



override fun onDestroy() {
disposable.clear()
disposableMS.clear()
flowable?.dispose()
flowable = null
super.onDestroy()
}

private fun mergeClicks(): Observable<Boolean> =
listOf( stopwatchStartButton.clicks().map { true }, stopwatchEndButton.clicks().map { false })
.merge()
.doOnNext(::buttonStateManager)

private fun buttonStateManager(boolean: Boolean) {
stopwatchStartButton.isEnabled = !boolean
stopwatchEndButton.isEnabled = boolean

}
private fun timerObservable(): Observable<String> =
Observable.interval(0, 1, /*java.util.concurrent.TimeUnit.SECONDS*/java.util.concurrent.TimeUnit.MILLISECONDS)
.takeWhile { it <= MAXIMUM_STOP_WATCH_LIMIT }
.map(timeFormatter)
.observeOn(io.reactivex.android.schedulers.AndroidSchedulers.mainThread())
.doOnComplete { buttonStateManager(false) }

private fun timerObservableMS(): Observable<String> =
Observable.interval(0, 1, java.util.concurrent.TimeUnit.MILLISECONDS)
.takeWhile { it <= MAXIMUM_STOP_WATCH_LIMIT }
.map(timeFormatterMS)
.observeOn(io.reactivex.android.schedulers.AndroidSchedulers.mainThread())
.doOnComplete { buttonStateManager(false) }

private val timeFormatter: (Long) -> String =
{ secs ->
if (secs == MAXIMUM_STOP_WATCH_LIMIT) displayInitialState
/// else "${secs / NUMBER_OF_SECONDS_IN_ONE_MINUTE} : ${secs % NUMBER_OF_SECONDS_IN_ONE_MINUTE}"
else "${(secs / NUMBER_OF_MILLSECONDS_IN_ONE_SECOND ) / NUMBER_OF_SECONDS_IN_ONE_MINUTE} : ${(secs / NUMBER_OF_MILLSECONDS_IN_ONE_SECOND) % NUMBER_OF_SECONDS_IN_ONE_MINUTE} : ${(secs / NUMBER_OF_MILLSECONDS_IN_ONE_SECOND) - (secs % NUMBER_OF_MILLSECONDS_IN_ONE_SECOND)}"

}


private val timeFormatterMS: (Long) -> String =
{ secs ->
if (secs == MAXIMUM_STOP_WATCH_LIMIT) displayInitialStateMS
else "${secs} "
}

override fun onStop() {
super.onStop()
disposable?.dispose()
// disposable = null
}

override fun onStart() {
super.onStart()
// disposable = null; // subscribeInput()
}

val mSubscription: Flow.Subscription? = null
@SuppressLint("ResourceAsColor", "ResourceType")
private fun timer(count: Long) {
countdownStartButton.isEnabled=false //how make kill emit and start again
Flowable.interval (0, 1, java.util.concurrent.TimeUnit.SECONDS)
.onBackpressureBuffer ()
.take (count + 1)
.map{ aLong ->
count - aLong
}
.observeOn (AndroidSchedulers.mainThread ())
.subscribe({
countdownText.text = it.toString()
if (it == 0L) {
//val redClr = getResources().getString(R.color.red);
countdownText.setTextColor(/*R.color.red*/ Color.RED)
countdownStartButton.isEnabled=true
}
})

}


private fun clock() {
val count: Long = 10000000000000000L
Flowable.interval (0, 1, java.util.concurrent.TimeUnit.SECONDS)
.onBackpressureBuffer ()
.take (count + 1)
.map{ aLong ->
count - aLong //
}
.observeOn (AndroidSchedulers.mainThread ())
.subscribe({
clockText.text = SimpleDateFormat(/*"hh:mm:ss.S"*//*"HH:mm:ss.SSS"*/"HH:mm:ss").format(Date(System.currentTimeMillis())) //time
})



}

}

8 changes: 5 additions & 3 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
android:layout_weight="1">

<TextView
android:id="@+id/countDownName"
style="@style/TitleText"
android:text="Обратный отсчёт:" />

Expand All @@ -59,12 +60,12 @@
tools:text="[02:37]" />

<Button
android:layout_marginStart="16dp"
android:layout_marginBottom="4dp"
android:layout_gravity="bottom"
android:id="@+id/countdownStartButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="16dp"
android:layout_marginBottom="4dp"
android:text="Start" />
</FrameLayout>

Expand All @@ -79,6 +80,7 @@
android:layout_weight="1">

<TextView
android:id="@+id/SecName"
style="@style/TitleText"
android:text="Секундомер:" />

Expand Down
Loading