From 386df286f50b7e16b838ca23b7ada87282977c17 Mon Sep 17 00:00:00 2001 From: LOCHOV Date: Sat, 1 Aug 2026 23:05:00 +0200 Subject: [PATCH 1/7] feature modify datetime --- .../implementation_plan.artifact.md | 54 ++++++++++ .../task.artifact.md | 9 ++ .gitignore | 1 + app/build.gradle | 2 +- .../practicetime/database/daos/SessionDao.kt | 98 ++++++++++++++----- .../practicetime/database/entities/Section.kt | 2 +- .../sessionlist/FullscreenSessionActivity.kt | 61 +++++++++++- .../layout/activity_fullscreen_session.xml | 43 ++++++++ 8 files changed, 244 insertions(+), 26 deletions(-) create mode 100644 .artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/implementation_plan.artifact.md create mode 100644 .artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/task.artifact.md diff --git a/.artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/implementation_plan.artifact.md b/.artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/implementation_plan.artifact.md new file mode 100644 index 0000000..5a10f74 --- /dev/null +++ b/.artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/implementation_plan.artifact.md @@ -0,0 +1,54 @@ +# Implementation Plan - Session Time Modification Feature + +Allow users to modify the date and time of existing practice sessions in the `FullscreenSessionActivity` (Edit Session screen). + +## User Review Required + +> [!IMPORTANT] +> The change will update the `createdAt` timestamp of the session and all its associated sections to maintain consistency. This will affect how the session appears in the history and statistics. + +## Proposed Changes + +### Database Layer + +#### [SessionDao.kt](file:///D:/mis%20documentos/Development/PracticeTime/app/src/main/java/de/practicetime/practicetime/database/daos/SessionDao.kt) + +- Update the `update` method to accept an optional `newTimestamp`. +- If `newTimestamp` is provided, update the `createdAt` field of the session and all its sections. +- Adjust goal progress calculation if necessary (though existing logic uses the section timestamps which will be updated). + +### UI Layer + +#### [activity_fullscreen_session.xml](file:///D:/mis%20documentos/Development/PracticeTime/app/src/main/res/layout/activity_fullscreen_session.xml) + +- Add a new section for "Date & Time" similar to the Rating section. +- Display the current session start time. +- Add an edit icon/button to trigger date and time selection. + +#### [FullscreenSessionActivity.kt](file:///D:/mis%20documentos/Development/PracticeTime/app/src/main/java/de/practicetime/practicetime/ui/sessionlist/FullscreenSessionActivity.kt) + +- Add logic to show `DatePickerDialog` and `TimePickerDialog` when the user wants to edit the session time. +- Store the modified timestamp in a state variable. +- Pass the modified timestamp to `sessionDao.update`. + +### Resources + +#### [strings.xml](file:///D:/mis%20documentos/Development/PracticeTime/app/src/main/res/values/strings.xml) + +- Ensure all necessary labels for the date/time editing are present. + +## Verification Plan + +### Automated Tests +- No existing unit tests for UI were found that cover this specific flow. +- I will verify the build with `gradle_build("app:assembleDebug")`. + +### Manual Verification +1. Open the app and navigate to the "Sessions" tab. +2. Long-click a session and select "Edit" (or click if it opens directly). +3. Verify the new "Date & Time" section is visible. +4. Click the edit icon for date/time. +5. Change the date and time to a past value. +6. Click "Save". +7. Verify in the session list that the session now appears under the new date/time. +8. Verify in Statistics that the time is correctly accounted for on the new date. diff --git a/.artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/task.artifact.md b/.artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/task.artifact.md new file mode 100644 index 0000000..3e8faed --- /dev/null +++ b/.artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/task.artifact.md @@ -0,0 +1,9 @@ +# Task Management + +- [x] Implement Session Time Modification Feature + - [x] Research existing session management and editing logic + - [x] Create implementation plan + - [x] Update `SessionDao.kt` to handle `createdAt` updates + - [x] Update `activity_fullscreen_session.xml` to include Date & Time edit UI + - [x] Implement Date and Time pickers in `FullscreenSessionActivity.kt` + - [x] Verify build and functionality diff --git a/.gitignore b/.gitignore index f1bd5ac..a352f1e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ local.properties /keystore /release app-release.aab +.artifacts diff --git a/app/build.gradle b/app/build.gradle index 99a24c7..3e32e77 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,7 +13,7 @@ android { minSdk 23 targetSdk 35 versionCode 15 - versionName "1.2.0" + versionName "1.2.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/de/practicetime/practicetime/database/daos/SessionDao.kt b/app/src/main/java/de/practicetime/practicetime/database/daos/SessionDao.kt index c2f542d..932c93d 100644 --- a/app/src/main/java/de/practicetime/practicetime/database/daos/SessionDao.kt +++ b/app/src/main/java/de/practicetime/practicetime/database/daos/SessionDao.kt @@ -100,37 +100,68 @@ abstract class SessionDao : BaseDao(tableName = "session") { newRating: Int, newSections: List, newComment: String, + newTimestamp: Long? = null ) { // the difference session will save the difference in the section duration // between the original session and the edited sections val sessionWithSectionsWithCategoriesWithGoals = getWithSectionsWithCategoriesWithGoals(sessionId) - sessionWithSectionsWithCategoriesWithGoals.sections.forEach { (section, _) -> - section.duration = (newSections.find { - it.section.id == section.id - }?.section?.duration ?: 0) - (section.duration ?: 0) - } + if (newTimestamp == null) { + // Existing logic for when timestamp doesn't change + sessionWithSectionsWithCategoriesWithGoals.sections.forEach { (section, _) -> + section.duration = (newSections.find { + it.section.id == section.id + }?.section?.duration ?: 0) - (section.duration ?: 0) + } - val goalProgress = PracticeTime.goalDescriptionDao.computeGoalProgressForSession( - sessionWithSectionsWithCategoriesWithGoals, - checkArchived = true - ) - - // get all active goal instances at the time of the session - PracticeTime.goalInstanceDao.apply { - get( - goalDescriptionIds = goalProgress.keys.toList(), - from = sessionWithSectionsWithCategoriesWithGoals.sections.first().section.timestamp - // add the progress - ).onEach { instance -> - goalProgress[instance.goalDescriptionId].also { progress -> - if (progress != null) { - // progress should not get lower than 0 - instance.progress = maxOf(0 , instance.progress + progress) + val goalProgress = PracticeTime.goalDescriptionDao.computeGoalProgressForSession( + sessionWithSectionsWithCategoriesWithGoals, + checkArchived = true + ) + + // get all active goal instances at the time of the session + PracticeTime.goalInstanceDao.apply { + get( + goalDescriptionIds = goalProgress.keys.toList(), + from = sessionWithSectionsWithCategoriesWithGoals.sections.first().section.timestamp + // add the progress + ).onEach { instance -> + goalProgress[instance.goalDescriptionId].also { progress -> + if (progress != null) { + // progress should not get lower than 0 + instance.progress = maxOf(0, instance.progress + progress) + } + } + update(instance) + } + } + } else { + // Logic for when timestamp changes + // 1. Remove OLD progress + val oldGoalProgress = PracticeTime.goalDescriptionDao.computeGoalProgressForSession( + sessionWithSectionsWithCategoriesWithGoals, + checkArchived = true + ) + PracticeTime.goalInstanceDao.apply { + get( + goalDescriptionIds = oldGoalProgress.keys.toList(), + from = sessionWithSectionsWithCategoriesWithGoals.sections.first().section.timestamp + ).onEach { instance -> + oldGoalProgress[instance.goalDescriptionId].also { progress -> + if (progress != null) { + instance.progress = maxOf(0, instance.progress - progress) + } } + update(instance) } - update(instance) + } + + // 2. Update timestamps in data structures + val diff = newTimestamp - sessionWithSectionsWithCategoriesWithGoals.session.createdAt + sessionWithSectionsWithCategoriesWithGoals.session.createdAt = newTimestamp + newSections.forEach { (section, _) -> + section.timestamp += diff } } @@ -145,5 +176,28 @@ abstract class SessionDao : BaseDao(tableName = "session") { } update(sessionWithSectionsWithCategoriesWithGoals.session) + + if (newTimestamp != null) { + // 3. Add NEW progress at the new timestamp + // Refetch to get the updated relations with goal descriptions at the new time + val updatedSessionWithGoals = getWithSectionsWithCategoriesWithGoals(sessionId) + val newGoalProgress = PracticeTime.goalDescriptionDao.computeGoalProgressForSession( + updatedSessionWithGoals, + checkArchived = true + ) + PracticeTime.goalInstanceDao.apply { + get( + goalDescriptionIds = newGoalProgress.keys.toList(), + from = newTimestamp + ).onEach { instance -> + newGoalProgress[instance.goalDescriptionId].also { progress -> + if (progress != null) { + instance.progress += progress + } + } + update(instance) + } + } + } } } diff --git a/app/src/main/java/de/practicetime/practicetime/database/entities/Section.kt b/app/src/main/java/de/practicetime/practicetime/database/entities/Section.kt index 016fadc..04791d6 100644 --- a/app/src/main/java/de/practicetime/practicetime/database/entities/Section.kt +++ b/app/src/main/java/de/practicetime/practicetime/database/entities/Section.kt @@ -15,5 +15,5 @@ data class Section ( @ColumnInfo(name="session_id", index = true) var sessionId: Long?, @ColumnInfo(name="category_id", index = true) val categoryId: Long, @ColumnInfo(name="duration") var duration: Int?, - @ColumnInfo(name="timestamp") val timestamp: Long, + @ColumnInfo(name="timestamp") var timestamp: Long, ) : BaseModel() diff --git a/app/src/main/java/de/practicetime/practicetime/ui/sessionlist/FullscreenSessionActivity.kt b/app/src/main/java/de/practicetime/practicetime/ui/sessionlist/FullscreenSessionActivity.kt index 9a8e4b8..3236f86 100644 --- a/app/src/main/java/de/practicetime/practicetime/ui/sessionlist/FullscreenSessionActivity.kt +++ b/app/src/main/java/de/practicetime/practicetime/ui/sessionlist/FullscreenSessionActivity.kt @@ -7,6 +7,8 @@ package de.practicetime.practicetime.ui.sessionlist import android.app.AlertDialog +import android.app.DatePickerDialog +import android.app.TimePickerDialog import android.content.Context import android.content.DialogInterface import android.content.Intent @@ -35,14 +37,18 @@ import de.practicetime.practicetime.database.entities.SessionWithSectionsWithCat import de.practicetime.practicetime.shared.EditTimeDialog import de.practicetime.practicetime.ui.MainActivity import de.practicetime.practicetime.utils.TIME_FORMAT_HUMAN_PRETTY +import de.practicetime.practicetime.utils.epochSecondsToDate import de.practicetime.practicetime.utils.getDurationString import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter class FullscreenSessionActivity : AppCompatActivity() { private lateinit var ratingBarView: RatingBar + private lateinit var dateTextView: TextView private lateinit var sectionListView: RecyclerView private lateinit var commentFieldView: TextView @@ -55,12 +61,14 @@ class FullscreenSessionActivity : AppCompatActivity() { private var sessionWithSectionsWithCategories: SessionWithSectionsWithCategories? = null private var selectedSection: Section? = null + private var sessionTimestamp: Long = 0 + private var timestampEdited = false private var showCommentPlaceholder = true private var sessionEdited = false override fun onBackPressed() { - if(!sessionEdited) return super.onBackPressed() + if(!sessionEdited && !timestampEdited) return super.onBackPressed() confirmationDialog.apply { setMessage(getString(R.string.discard_changes_dialog_message)) show() @@ -103,7 +111,7 @@ class FullscreenSessionActivity : AppCompatActivity() { if (sessionId != null) { showFullscreenSession(sessionId) findViewById(R.id.fullscreen_session_cancel).setOnClickListener { - if(!sessionEdited) return@setOnClickListener exitActivity() + if(!sessionEdited && !timestampEdited) return@setOnClickListener exitActivity() confirmationDialog.apply { setMessage(getString(R.string.discard_changes_dialog_message)) show() @@ -133,6 +141,7 @@ class FullscreenSessionActivity : AppCompatActivity() { newSections = sectionAdapterData, newComment = if(showCommentPlaceholder) "" else commentFieldView.text.toString(), + newTimestamp = if (timestampEdited) sessionTimestamp else null ) dismiss() exitActivity() @@ -148,6 +157,7 @@ class FullscreenSessionActivity : AppCompatActivity() { } private fun showFullscreenSession(sessionId: Long) { + dateTextView = findViewById(R.id.fullscreen_session_date_text) ratingBarView = findViewById(R.id.fullscreen_session_rating_bar) sectionListView = findViewById(R.id.fullscreen_session_section_list) commentFieldView = findViewById(R.id.fullscreen_session_comment_field) @@ -169,6 +179,13 @@ class FullscreenSessionActivity : AppCompatActivity() { PracticeTime.sessionDao.getWithSectionsWithCategories(sessionId) val (session, sectionsWithCategories) = sessionWithSectionsWithCategories!! + sessionTimestamp = session.createdAt + updateDateUI() + + findViewById(R.id.fullscreen_session_date_layout).setOnClickListener { + showDateTimePicker() + } + ratingBarView.progress = session.rating ratingBarView.setOnRatingBarChangeListener { _, _, _ -> sessionEdited = true @@ -189,6 +206,46 @@ class FullscreenSessionActivity : AppCompatActivity() { } + private fun updateDateUI() { + val zonedDateTime = epochSecondsToDate(sessionTimestamp) + val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy") + val timeFormatter = DateTimeFormatter.ofPattern("HH:mm") + + dateTextView.text = getString( + R.string.fullscreen_session_date, + zonedDateTime.format(dateFormatter), + zonedDateTime.format(timeFormatter) + ) + } + + private fun showDateTimePicker() { + val currentDateTime = epochSecondsToDate(sessionTimestamp) + + DatePickerDialog( + this, + { _, year, month, dayOfMonth -> + TimePickerDialog( + this, + { _, hourOfDay, minute -> + val newDateTime = ZonedDateTime.of( + year, month + 1, dayOfMonth, hourOfDay, minute, 0, 0, + currentDateTime.zone + ) + sessionTimestamp = newDateTime.toEpochSecond() + timestampEdited = true + updateDateUI() + }, + currentDateTime.hour, + currentDateTime.minute, + true + ).show() + }, + currentDateTime.year, + currentDateTime.monthValue - 1, + currentDateTime.dayOfMonth + ).show() + } + private fun editSectionDurationHandler(section: Section?, newSectionDuration: Int) { section?.duration = newSectionDuration sectionAdapterData.indexOfFirst { diff --git a/app/src/main/res/layout/activity_fullscreen_session.xml b/app/src/main/res/layout/activity_fullscreen_session.xml index 6b31c7a..4bde626 100644 --- a/app/src/main/res/layout/activity_fullscreen_session.xml +++ b/app/src/main/res/layout/activity_fullscreen_session.xml @@ -57,6 +57,49 @@ android:layout_height="wrap_content" android:layout_marginStart="24dp" android:layout_marginTop="16dp" + android:text="@string/fullscreen_session_date_label" + android:textColor="?attr/colorOnSurfaceLowerContrast" + android:textSize="16sp" + android:textStyle="bold" /> + + + + + + + + + + + Date: Sat, 1 Aug 2026 23:12:25 +0200 Subject: [PATCH 2/7] feature modify datetime --- .../implementation_plan.artifact.md | 54 ------------------- .../task.artifact.md | 9 ---- 2 files changed, 63 deletions(-) delete mode 100644 .artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/implementation_plan.artifact.md delete mode 100644 .artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/task.artifact.md diff --git a/.artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/implementation_plan.artifact.md b/.artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/implementation_plan.artifact.md deleted file mode 100644 index 5a10f74..0000000 --- a/.artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/implementation_plan.artifact.md +++ /dev/null @@ -1,54 +0,0 @@ -# Implementation Plan - Session Time Modification Feature - -Allow users to modify the date and time of existing practice sessions in the `FullscreenSessionActivity` (Edit Session screen). - -## User Review Required - -> [!IMPORTANT] -> The change will update the `createdAt` timestamp of the session and all its associated sections to maintain consistency. This will affect how the session appears in the history and statistics. - -## Proposed Changes - -### Database Layer - -#### [SessionDao.kt](file:///D:/mis%20documentos/Development/PracticeTime/app/src/main/java/de/practicetime/practicetime/database/daos/SessionDao.kt) - -- Update the `update` method to accept an optional `newTimestamp`. -- If `newTimestamp` is provided, update the `createdAt` field of the session and all its sections. -- Adjust goal progress calculation if necessary (though existing logic uses the section timestamps which will be updated). - -### UI Layer - -#### [activity_fullscreen_session.xml](file:///D:/mis%20documentos/Development/PracticeTime/app/src/main/res/layout/activity_fullscreen_session.xml) - -- Add a new section for "Date & Time" similar to the Rating section. -- Display the current session start time. -- Add an edit icon/button to trigger date and time selection. - -#### [FullscreenSessionActivity.kt](file:///D:/mis%20documentos/Development/PracticeTime/app/src/main/java/de/practicetime/practicetime/ui/sessionlist/FullscreenSessionActivity.kt) - -- Add logic to show `DatePickerDialog` and `TimePickerDialog` when the user wants to edit the session time. -- Store the modified timestamp in a state variable. -- Pass the modified timestamp to `sessionDao.update`. - -### Resources - -#### [strings.xml](file:///D:/mis%20documentos/Development/PracticeTime/app/src/main/res/values/strings.xml) - -- Ensure all necessary labels for the date/time editing are present. - -## Verification Plan - -### Automated Tests -- No existing unit tests for UI were found that cover this specific flow. -- I will verify the build with `gradle_build("app:assembleDebug")`. - -### Manual Verification -1. Open the app and navigate to the "Sessions" tab. -2. Long-click a session and select "Edit" (or click if it opens directly). -3. Verify the new "Date & Time" section is visible. -4. Click the edit icon for date/time. -5. Change the date and time to a past value. -6. Click "Save". -7. Verify in the session list that the session now appears under the new date/time. -8. Verify in Statistics that the time is correctly accounted for on the new date. diff --git a/.artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/task.artifact.md b/.artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/task.artifact.md deleted file mode 100644 index 3e8faed..0000000 --- a/.artifacts/20260517-074726-3f8d4c35-d29c-49d4-b462-d1f0ba958c12/task.artifact.md +++ /dev/null @@ -1,9 +0,0 @@ -# Task Management - -- [x] Implement Session Time Modification Feature - - [x] Research existing session management and editing logic - - [x] Create implementation plan - - [x] Update `SessionDao.kt` to handle `createdAt` updates - - [x] Update `activity_fullscreen_session.xml` to include Date & Time edit UI - - [x] Implement Date and Time pickers in `FullscreenSessionActivity.kt` - - [x] Verify build and functionality From 962c67294a235cc2a74e663dbed8a4f9ca08d5c2 Mon Sep 17 00:00:00 2001 From: LOCHOV Date: Sun, 2 Aug 2026 00:30:56 +0200 Subject: [PATCH 3/7] basic timer working --- app/build.gradle | 2 +- .../practicetime/ui/timer/TimerFragment.kt | 96 ++++++++++++++ app/src/main/res/drawable/ic_timer.xml | 10 ++ app/src/main/res/layout/fragment_timer.xml | 119 ++++++++++++++++++ .../main/res/menu/bottom_navigation_menu.xml | 5 + app/src/main/res/navigation/nav_graph.xml | 5 + app/src/main/res/values/strings.xml | 5 + 7 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/de/practicetime/practicetime/ui/timer/TimerFragment.kt create mode 100644 app/src/main/res/drawable/ic_timer.xml create mode 100644 app/src/main/res/layout/fragment_timer.xml diff --git a/app/build.gradle b/app/build.gradle index 3e32e77..47cd248 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,7 +13,7 @@ android { minSdk 23 targetSdk 35 versionCode 15 - versionName "1.2.1" + versionName "1.3.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/de/practicetime/practicetime/ui/timer/TimerFragment.kt b/app/src/main/java/de/practicetime/practicetime/ui/timer/TimerFragment.kt new file mode 100644 index 0000000..7aaf545 --- /dev/null +++ b/app/src/main/java/de/practicetime/practicetime/ui/timer/TimerFragment.kt @@ -0,0 +1,96 @@ +/* + * This software is licensed under the MIT license + * + * Copyright (c) 2022, Javier Carbone, author Matthias Emde + */ + +package de.practicetime.practicetime.ui.timer + +import android.media.AudioManager +import android.media.ToneGenerator +import android.os.Bundle +import android.os.CountDownTimer +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.EditText +import android.widget.TextView +import androidx.fragment.app.Fragment +import de.practicetime.practicetime.R +import java.util.Locale + +class TimerFragment : Fragment() { + + private lateinit var countdownText: TextView + private lateinit var inputMinutes: EditText + private lateinit var inputSeconds: EditText + private lateinit var startBtn: Button + private lateinit var stopBtn: Button + + private var timer: CountDownTimer? = null + private var isRunning = false + private val toneGenerator = ToneGenerator(AudioManager.STREAM_ALARM, 100) + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val view = inflater.inflate(R.layout.fragment_timer, container, false) + countdownText = view.findViewById(R.id.timerCountdown) + inputMinutes = view.findViewById(R.id.timerInputMinutes) + inputSeconds = view.findViewById(R.id.timerInputSeconds) + startBtn = view.findViewById(R.id.timerStartBtn) + stopBtn = view.findViewById(R.id.timerStopBtn) + + startBtn.setOnClickListener { startTimer() } + stopBtn.setOnClickListener { stopTimer() } + + return view + } + + private fun startTimer() { + val minutes = inputMinutes.text.toString().toLongOrNull() ?: 0L + val seconds = inputSeconds.text.toString().toLongOrNull() ?: 30L + val totalMillis = (minutes * 60 + seconds) * 1000 + + if (totalMillis <= 0) return + + isRunning = true + startBtn.isEnabled = false + stopBtn.isEnabled = true + inputMinutes.isEnabled = false + inputSeconds.isEnabled = false + + timer = object : CountDownTimer(totalMillis, 100) { + override fun onTick(millisUntilFinished: Long) { + val sec = (millisUntilFinished / 1000) % 60 + val min = (millisUntilFinished / 1000) / 60 + countdownText.text = String.format(Locale.getDefault(), "%02d:%02d", min, sec) + } + + override fun onFinish() { + toneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP, 500) + stopTimer() + } + }.start() + } + + private fun stopTimer() { + isRunning = false + timer?.cancel() + timer = null + startBtn.isEnabled = true + stopBtn.isEnabled = false + inputMinutes.isEnabled = true + inputSeconds.isEnabled = true + countdownText.text = "00:00" + } + + override fun onDestroy() { + super.onDestroy() + timer?.cancel() + toneGenerator.release() + } +} diff --git a/app/src/main/res/drawable/ic_timer.xml b/app/src/main/res/drawable/ic_timer.xml new file mode 100644 index 0000000..a0bba4c --- /dev/null +++ b/app/src/main/res/drawable/ic_timer.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/layout/fragment_timer.xml b/app/src/main/res/layout/fragment_timer.xml new file mode 100644 index 0000000..4133592 --- /dev/null +++ b/app/src/main/res/layout/fragment_timer.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +