diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..cec0517 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +android_2026_1 \ No newline at end of file diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml new file mode 100644 index 0000000..4a53bee --- /dev/null +++ b/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b86273d --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/deviceManager.xml b/.idea/deviceManager.xml new file mode 100644 index 0000000..91f9558 --- /dev/null +++ b/.idea/deviceManager.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..639c779 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..c902d2f --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index cc8ee59..c7f45c2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -3,6 +3,9 @@ plugins { } android { + viewBinding { + enable = true + } namespace = "com.example.android_2026_1" compileSdk = 36 @@ -37,6 +40,7 @@ dependencies { implementation(libs.material) implementation(libs.androidx.activity) implementation(libs.androidx.constraintlayout) + implementation(libs.androidx.recyclerview) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/src/main/java/com/example/android_2026_1/LapAdapter.kt b/app/src/main/java/com/example/android_2026_1/LapAdapter.kt new file mode 100644 index 0000000..b2f6bd7 --- /dev/null +++ b/app/src/main/java/com/example/android_2026_1/LapAdapter.kt @@ -0,0 +1,26 @@ +package com.example.android_2026_1 + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView + +class LapAdapter(private val lapList: List) : RecyclerView.Adapter() { + + class LapViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val textViewLap: TextView = itemView as TextView + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LapViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(android.R.layout.simple_list_item_1, parent, false) + return LapViewHolder(view) + } + + override fun onBindViewHolder(holder: LapViewHolder, position: Int) { + holder.textViewLap.text = lapList[position] + } + + override fun getItemCount(): Int = lapList.size +} \ No newline at end of file diff --git a/app/src/main/java/com/example/android_2026_1/MainActivity.kt b/app/src/main/java/com/example/android_2026_1/MainActivity.kt index ad2a6ca..9166fd5 100644 --- a/app/src/main/java/com/example/android_2026_1/MainActivity.kt +++ b/app/src/main/java/com/example/android_2026_1/MainActivity.kt @@ -1,20 +1,121 @@ package com.example.android_2026_1 +import android.annotation.SuppressLint import android.os.Bundle +import android.os.SystemClock import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import com.example.android_2026_1.databinding.ActivityMainBinding +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch class MainActivity : AppCompatActivity() { + + private lateinit var binding: ActivityMainBinding + + private var timeCentiseconds = 0 + private var timeJob: Job? = null + + private var startTime = 0L + private var pausedTime = 0L + + private lateinit var lapAdapter: LapAdapter + private val lapList = mutableListOf() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() - setContentView(R.layout.activity_main) - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> + + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + ViewCompat.setOnApplyWindowInsetsListener(binding.main) { v, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) insets } + + lapAdapter = LapAdapter(lapList) + binding.recyclerViewLaps.apply { + adapter = lapAdapter + layoutManager = LinearLayoutManager(this@MainActivity) + } + + binding.buttonStartPause.setOnClickListener { + if (timeJob?.isActive == true) pauseTimer() else startTimer() + } + + binding.buttonLap.setOnClickListener { + val currentText = getFormattedTime() + val secondsElapsed = timeCentiseconds / 100 + val lapRecord = getString(R.string.lap_record_format, secondsElapsed, currentText) + + lapList.add(0, lapRecord) + lapAdapter.notifyItemInserted(0) + binding.recyclerViewLaps.scrollToPosition(0) + } + + binding.buttonStop.setOnClickListener { + resetTimer() + clearLapRecords() + } + } + + private fun startTimer() { + startTime = SystemClock.elapsedRealtime() - pausedTime + + timeJob = lifecycleScope.launch { + binding.buttonStartPause.text = getString(R.string.btn_pause) + + while (isActive) { + val elapsedMillis = SystemClock.elapsedRealtime() - startTime + timeCentiseconds = (elapsedMillis / 10).toInt() + + updateTimerText() + delay(10L) + } + } + } + + private fun pauseTimer() { + timeJob?.cancel() + pausedTime = SystemClock.elapsedRealtime() - startTime + binding.buttonStartPause.text = getString(R.string.btn_start) + } + + private fun resetTimer() { + timeJob?.cancel() + timeCentiseconds = 0 + pausedTime = 0L + updateTimerText() + binding.buttonStartPause.text = getString(R.string.btn_start) + } + + private fun getFormattedTime(): String { + val m = (timeCentiseconds / 6000).toString().padStart(2, '0') + val s = ((timeCentiseconds % 6000) / 100).toString().padStart(2, '0') + val ms = (timeCentiseconds % 100).toString().padStart(2, '0') + + return "$m:$s:$ms" + } + + private fun updateTimerText() { + val m = (timeCentiseconds / 6000).toString().padStart(2, '0') + val s = ((timeCentiseconds % 6000) / 100).toString().padStart(2, '0') + val ms = (timeCentiseconds % 100).toString().padStart(2, '0') + + binding.textViewTime.text = "$m : $s : $ms" + } + + @SuppressLint("NotifyDataSetChanged") + private fun clearLapRecords() { + lapList.clear() + lapAdapter.notifyDataSetChanged() } } \ 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 9affce0..6b65f1f 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -7,4 +7,63 @@ android:layout_height="match_parent" tools:context=".MainActivity"> + + + + + + +