visual enhancements and documentation changes.

This commit is contained in:
2026-05-27 18:32:37 -07:00
parent 075311d298
commit 451569de17
6 changed files with 83 additions and 34 deletions
+3 -2
View File
@@ -7,13 +7,13 @@
- **Run Unit Tests**: `./gradlew test` (if applicable)
## Code Style Guidelines
- **Language**: Kotlin 1.9+
- **Language**: Kotlin 1.9.0
- **UI Framework**: Traditional Android View system (XML layouts), **not** Jetpack Compose.
- **Naming Conventions**:
- Classes: `PascalCase` (e.g., `CountdownDialog`)
- Functions/Variables: `camelCase` (e.g., `startTimer()`)
- Layout Files: `snake_case` (e.g., `activity_main.xml`)
- Resource IDs: `snake_case` (e.g., `btn_start`)
- Resource IDs: `camelCase` (e.g., `btnStart`, `cvCountdown`)
- **Android Patterns**: Use `findViewById` for view binding in this project to maintain consistency with existing simple architecture.
## Architecture
@@ -26,6 +26,7 @@
## Critical Files Reference
- `app/src/main/java/com/example/countdowntimer/MainActivity.kt`: Entry point and trigger for the timer.
- `app/src/main/java/com/example/countdowntimer/CountdownDialog.kt`: Implementation of the countdown logic and `ToneGenerator` trigger.
- `app/src/main/java/com/example/countdowntimer/CountdownCanvasView.kt`: Custom view for the circular progress ring and countdown text.
- `app/src/main/res/layout/activity_main.xml`: Main screen layout.
- `app/src/main/res/layout/dialog_countdown.xml`: Timer dialog layout.
- `app/build.gradle`: Build configuration, SDK versions (minSdk 24, targetSdk 34), and dependencies.
+34 -7
View File
@@ -3,12 +3,12 @@
A minimal Android application that demonstrates a simple countdown functionality with an audible alert.
## Description
The Countdown Timer app is a lightweight utility that launches a modal dialog to count down from 10 seconds. Once the timer reaches zero, the app plays a system error tone and closes the dialog. This project serves as a basic example of using `CountDownTimer`, `ToneGenerator`, and custom `Dialog` implementation in Android.
The Countdown Timer app is a lightweight utility that launches a modal dialog to count down from a user-selected duration (5s, 10s, 15s, or 30s). Once the timer reaches zero, the app plays a confirmation tone and vibration, then shows a dismiss button. This project serves as a basic example of using `CountDownTimer`, `ToneGenerator`, and custom `Dialog` implementation in Android.
## Key Features
- **Single-Action Interface**: A simple main screen with a start button.
- **Modal Countdown**: A non-cancelable dialog that prevents user interference during the countdown.
- **Audible Alert**: Integration with Android's `ToneGenerator` to play `TONE_SUP_ERROR` upon completion.
- **Selectable Duration**: A main screen with a duration picker (chip group) and a start button.
- **Modal Countdown**: A full-screen dialog with circular progress ring, stop/restart buttons, and a confirmation tone during the final 3 seconds.
- **Haptic Feedback**: Vibration on the last 3 seconds and on completion (configurable via shared preferences).
- **Real-time Updates**: UI updates every second to reflect the remaining time.
## Getting Started
@@ -25,19 +25,46 @@ To build the debug APK from the command line:
```
### Running the App
#### Via Android Studio
1. Open the project in Android Studio.
2. Select your target device/emulator.
3. Click the **Run** button or press `Shift + F10`.
#### Via ADB
First, ensure your device or emulator is connected (`adb devices`). Then install the debug APK and launch the app:
```bash
# Install the debug APK
adb install -r app/build/outputs/apk/debug/app-debug.apk
# Launch the app
adb shell am start -n com.example.countdowntimer.debug/com.example.countdowntimer.MainActivity
```
#### Taking a Screenshot via ADB
To capture a screenshot of the app from your host machine:
```bash
# Take the screenshot on the device
adb shell screencap -p /sdcard/screenshot.png
# Pull it to your host machine
adb pull /sdcard/screenshot.png screenshots/
```
## Project Structure
```text
app/
├── src/
│ └── main/
│ ├── java/com/example/countdowntimer/
│ │ ├── MainActivity.kt # Main entry point; launches the timer
│ │ ── CountdownDialog.kt # Logic for the 10s countdown and tone
├── res/ # UI layouts and resources
│ │ ├── MainActivity.kt # Main entry point; duration picker and timer trigger
│ │ ── CountdownDialog.kt # Countdown logic, tone, and vibration
│ └── CountdownCanvasView.kt # Custom circular progress ring view
│ ├── res/layout/ # UI layouts
│ │ ├── activity_main.xml
│ │ └── dialog_countdown.xml
│ └── AndroidManifest.xml # Application manifest
└── build.gradle # App-level build configuration
```
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="120dp"
android:height="120dp"
android:viewportWidth="120"
android:viewportHeight="120"
android:tint="@android:color/white">
<path
android:fillColor="@android:color/white"
android:pathData="M20,20v80l60,-40z" />
</vector>
@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="64dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="64dp">
<path android:fillColor="@android:color/white" android:pathData="M655,880L513,738L569,682L654,767L824,597L880,654L655,880ZM480,490L480,490L480,490Q480,490 480,490Q480,490 480,490Q480,490 480,490Q480,490 480,490Q480,490 480,490Q480,490 480,490Q480,490 480,490Q480,490 480,490Q480,490 480,490Q480,490 480,490Q480,490 480,490Q480,490 480,490Q480,490 480,490Q480,490 480,490ZM412,750L501,839Q496,839 490.5,839.5Q485,840 480,840Q346,840 253,747Q160,654 160,520Q160,391 246.5,275Q333,159 480,80L480,212Q480,246 503.5,269Q527,292 561,292Q579,292 594.5,284.5Q610,277 622,262L640,240Q714,282 757,357Q800,432 800,520L720,520Q720,495 715,471.5Q710,448 701,425.5Q692,403 678.5,383Q665,363 648,346Q628,359 606,365.5Q584,372 561,372Q499,372 453.5,331Q408,290 401,230L401,230Q323,296 281.5,370.5Q240,445 240,520Q240,602 288.5,665Q337,728 412,750Z"/>
</vector>
+26 -20
View File
@@ -22,7 +22,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.7">
app:layout_constraintWidth_percent=".85">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
@@ -54,7 +54,7 @@
<LinearLayout
android:id="@+id/instructionsLayout"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:orientation="vertical"
@@ -68,11 +68,13 @@
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/gas_gauge_full"
android:src="@drawable/outline_fire_check_24"
app:tint="@color/color_icon_tint" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -87,11 +89,13 @@
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/chair"
android:src="@drawable/outline_air_24"
app:tint="@color/color_icon_tint" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -106,11 +110,13 @@
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/outline_air_24"
android:src="@drawable/ic_play_arrow"
app:tint="@color/color_icon_tint" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -163,18 +169,18 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="56dp"
app:chipBackgroundColor="@color/chip_duration_bg"
android:text="5s" />
android:text="5s"
app:chipBackgroundColor="@color/chip_duration_bg" />
<com.google.android.material.chip.Chip
android:id="@+id/chip10s"
style="@style/Widget.Material3.Chip.Suggestion.Elevated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="56dp"
android:checked="true"
app:chipBackgroundColor="@color/chip_duration_bg"
android:text="10s" />
android:minWidth="56dp"
android:text="10s"
app:chipBackgroundColor="@color/chip_duration_bg" />
<com.google.android.material.chip.Chip
android:id="@+id/chip15s"
@@ -182,8 +188,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="56dp"
app:chipBackgroundColor="@color/chip_duration_bg"
android:text="15s" />
android:text="15s"
app:chipBackgroundColor="@color/chip_duration_bg" />
<com.google.android.material.chip.Chip
android:id="@+id/chip30s"
@@ -191,24 +197,24 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="56dp"
app:chipBackgroundColor="@color/chip_duration_bg"
android:text="30s" />
android:text="30s"
app:chipBackgroundColor="@color/chip_duration_bg" />
</com.google.android.material.chip.ChipGroup>
</HorizontalScrollView>
<Button
android:id="@+id/btnStart"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@drawable/btn_start_selector"
android:drawableTop="@drawable/ic_play_arrow"
android:contentDescription="Start"
android:gravity="center"
android:padding="16dp"
android:text="@string/start_timer"
android:padding="24dp"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:textStyle="bold"
app:icon="@drawable/ic_play_arrow_large"
app:iconSize="56dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/hsvDurationOptions" />
+3 -3
View File
@@ -4,9 +4,9 @@
<string name="countdown_text">!!! HOLD !!!</string>
<string name="timer_zero">EXHALE!</string>
<string name="instruction_title">BONG HIT TIMER!</string>
<string name="instruction_volume">Ensure your bong is loaded</string>
<string name="instruction_position">Find a comfortable position</string>
<string name="instruction_distractions">Light your bowl.\nTake your hit.\nPress Start.</string>
<string name="instruction_volume">Light your bowl</string>
<string name="instruction_position">Take your hit</string>
<string name="instruction_distractions">Press Start to time how long to hold it!</string>
<string name="pick_duration">Pick Duration</string>
<string name="settings">Settings</string>
<string name="vibration">Vibration</string>