Merge pull request 'Fat square interesting buttons' (#3) from fat_square_interesting_buttons into main

Reviewed-on: http://192.168.1.74:30008/kfj001/bong_hit_timer_android/pulls/3
This commit was merged in pull request #3.
This commit is contained in:
2026-05-26 13:24:59 -07:00
30 changed files with 412 additions and 57 deletions
@@ -24,12 +24,12 @@ class CountdownCanvasView @JvmOverloads constructor(
isFakeBoldText = false isFakeBoldText = false
} }
private val ringPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { private val ringPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.parseColor("#6750A4") color = ContextCompat.getColor(context, R.color.md_color_ring_progress)
style = Paint.Style.STROKE style = Paint.Style.STROKE
strokeWidth = 16f strokeWidth = 16f
} }
private val ringBgPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { private val ringBgPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.parseColor("#D0BCFF") color = ContextCompat.getColor(context, R.color.md_color_ring_track)
style = Paint.Style.STROKE style = Paint.Style.STROKE
strokeWidth = 16f strokeWidth = 16f
} }
@@ -18,8 +18,8 @@ class CountdownDialog(
private val prefs: SharedPreferences private val prefs: SharedPreferences
) : Dialog(context) { ) : Dialog(context) {
private lateinit var cvCountdown: CountdownCanvasView private lateinit var cvCountdown: CountdownCanvasView
private lateinit var btnStop: Button private lateinit var btnStop: View
private lateinit var btnRestart: Button private lateinit var btnRestart: View
private lateinit var btnDismiss: Button private lateinit var btnDismiss: Button
private var timer: CountDownTimer? = null private var timer: CountDownTimer? = null
private var toneGenerator: ToneGenerator? = null private var toneGenerator: ToneGenerator? = null
@@ -61,14 +61,10 @@ class CountdownDialog(
val progress = millisUntilFinished.toFloat() / durationMillis val progress = millisUntilFinished.toFloat() / durationMillis
cvCountdown.setProgress(progress) cvCountdown.setProgress(progress)
if (millisUntilFinished >= durationMillis) {
cvCountdown.setText(context.getString(R.string.countdown_text))
} else {
val secondsLeft = (millisUntilFinished / 1000).toInt() + 1 val secondsLeft = (millisUntilFinished / 1000).toInt() + 1
cvCountdown.setText(secondsLeft.toString()) cvCountdown.setText(secondsLeft.toString())
}
if (millisUntilFinished <= 3000 && millisUntilFinished > 1000) { if (secondsLeft >= 1 && secondsLeft <= 3) {
playTone(ToneGenerator.TONE_PROP_BEEP) playTone(ToneGenerator.TONE_PROP_BEEP)
if (prefs.getBoolean("vibration", true)) { if (prefs.getBoolean("vibration", true)) {
vibrator?.vibrate(50) vibrator?.vibrate(50)
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:color="#81C784" />
<item android:color="#E8E8E8" />
</selector>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="40dp" />
<gradient
android:angle="270"
android:startColor="#81C784"
android:endColor="#4CAF50"
android:type="linear" />
<stroke
android:width="3dp"
android:color="#A5D6A7" />
</shape>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="40dp" />
<gradient
android:angle="270"
android:startColor="#C8E6C9"
android:endColor="#A5D6A7"
android:type="linear" />
<stroke
android:width="3dp"
android:color="#E8F5E9" />
</shape>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="40dp" />
<gradient
android:angle="270"
android:startColor="#66BB6A"
android:endColor="#388E3C"
android:type="linear" />
<stroke
android:width="3dp"
android:color="#81C784" />
</shape>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/btn_duration_disabled" android:state_enabled="false" />
<item android:drawable="@drawable/btn_duration_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/btn_duration_pressed" android:state_activated="true" />
<item android:drawable="@drawable/btn_duration_default" />
</selector>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size android:width="120dp" android:height="120dp" />
<gradient
android:angle="270"
android:startColor="#42A5F5"
android:endColor="#1565C0"
android:type="linear" />
<stroke
android:width="3dp"
android:color="#90CAF9" />
</shape>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size android:width="120dp" android:height="120dp" />
<gradient
android:angle="270"
android:startColor="#90CAF9"
android:endColor="#64B5F6"
android:type="linear" />
<stroke
android:width="3dp"
android:color="#BBDEFB" />
</shape>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size android:width="120dp" android:height="120dp" />
<gradient
android:angle="270"
android:startColor="#1E88E5"
android:endColor="#0D47A1"
android:type="linear" />
<stroke
android:width="3dp"
android:color="#42A5F5" />
</shape>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/btn_restart_disabled" android:state_enabled="false" />
<item android:drawable="@drawable/btn_restart_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/btn_restart_pressed" android:state_activated="true" />
<item android:drawable="@drawable/btn_restart_default" />
</selector>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size android:width="120dp" android:height="120dp" />
<gradient
android:angle="270"
android:startColor="#4CAF50"
android:endColor="#2E7D32"
android:type="linear" />
<stroke
android:width="3dp"
android:color="#66BB6A" />
</shape>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size android:width="120dp" android:height="120dp" />
<gradient
android:angle="270"
android:startColor="#A5D6A7"
android:endColor="#81C784"
android:type="linear" />
<stroke
android:width="3dp"
android:color="#C8E6C9" />
</shape>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size android:width="120dp" android:height="120dp" />
<gradient
android:angle="270"
android:startColor="#388E3C"
android:endColor="#1B5E20"
android:type="linear" />
<stroke
android:width="3dp"
android:color="#4CAF50" />
</shape>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/btn_start_disabled" android:state_enabled="false" />
<item android:drawable="@drawable/btn_start_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/btn_start_pressed" android:state_activated="true" />
<item android:drawable="@drawable/btn_start_default" />
</selector>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size android:width="120dp" android:height="120dp" />
<gradient
android:angle="270"
android:startColor="#EF5350"
android:endColor="#C62828"
android:type="linear" />
<stroke
android:width="3dp"
android:color="#EF9A9A" />
</shape>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size android:width="120dp" android:height="120dp" />
<gradient
android:angle="270"
android:startColor="#EF9A9A"
android:endColor="#E57373"
android:type="linear" />
<stroke
android:width="3dp"
android:color="#FFCDD2" />
</shape>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size android:width="120dp" android:height="120dp" />
<gradient
android:angle="270"
android:startColor="#E53935"
android:endColor="#B71C1C"
android:type="linear" />
<stroke
android:width="3dp"
android:color="#EF5350" />
</shape>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/btn_stop_disabled" android:state_enabled="false" />
<item android:drawable="@drawable/btn_stop_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/btn_stop_pressed" android:state_activated="true" />
<item android:drawable="@drawable/btn_stop_default" />
</selector>
@@ -4,6 +4,6 @@
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#6750A4" android:fillColor="#FFFFFF"
android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6z"/> android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6z"/>
</vector> </vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4L17.65,6.35z"
android:strokeWidth="0" />
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M6,6h12v12H6z"
android:strokeWidth="0" />
</vector>
+30 -11
View File
@@ -110,15 +110,22 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<com.google.android.material.button.MaterialButton <Button
android:id="@+id/btnDurationLabel" android:id="@+id/btnDurationLabel"
style="@style/Widget.Material3.Button.OutlinedButton" android:layout_width="180dp"
android:layout_width="wrap_content" android:layout_height="80dp"
android:layout_height="wrap_content"
android:layout_marginTop="32dp" android:layout_marginTop="32dp"
android:background="@drawable/btn_duration_selector"
android:text="@string/start_timer" android:text="@string/start_timer"
android:drawableEnd="@drawable/ic_keyboard_arrow_down" android:drawableEnd="@drawable/ic_keyboard_arrow_down"
app:cornerRadius="20dp" /> android:drawablePadding="8dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:textColor="#FFFFFF"
android:textSize="18sp"
android:textStyle="bold"
android:gravity="center"
android:includeFontPadding="false" />
<HorizontalScrollView <HorizontalScrollView
android:id="@+id/hsvDurationOptions" android:id="@+id/hsvDurationOptions"
@@ -139,6 +146,8 @@
style="@style/Widget.Material3.Chip.Suggestion.Elevated" style="@style/Widget.Material3.Chip.Suggestion.Elevated"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minWidth="56dp"
app:chipBackgroundColor="@color/chip_duration_bg"
android:text="5s" /> android:text="5s" />
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
@@ -146,7 +155,9 @@
style="@style/Widget.Material3.Chip.Suggestion.Elevated" style="@style/Widget.Material3.Chip.Suggestion.Elevated"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minWidth="56dp"
android:checked="true" android:checked="true"
app:chipBackgroundColor="@color/chip_duration_bg"
android:text="10s" /> android:text="10s" />
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
@@ -154,6 +165,8 @@
style="@style/Widget.Material3.Chip.Suggestion.Elevated" style="@style/Widget.Material3.Chip.Suggestion.Elevated"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minWidth="56dp"
app:chipBackgroundColor="@color/chip_duration_bg"
android:text="15s" /> android:text="15s" />
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
@@ -161,19 +174,25 @@
style="@style/Widget.Material3.Chip.Suggestion.Elevated" style="@style/Widget.Material3.Chip.Suggestion.Elevated"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minWidth="56dp"
app:chipBackgroundColor="@color/chip_duration_bg"
android:text="30s" /> android:text="30s" />
</com.google.android.material.chip.ChipGroup> </com.google.android.material.chip.ChipGroup>
</HorizontalScrollView> </HorizontalScrollView>
<com.google.android.material.button.MaterialButton <Button
android:id="@+id/btnStart" android:id="@+id/btnStart"
style="@style/Widget.Material3.Button" android:layout_width="120dp"
android:layout_width="wrap_content" android:layout_height="120dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:background="@drawable/btn_start_selector"
android:drawableTop="@drawable/ic_play_arrow"
android:gravity="center"
android:padding="16dp"
android:text="@string/start_timer" android:text="@string/start_timer"
app:cornerRadius="20dp" android:textColor="#FFFFFF"
app:icon="@drawable/ic_play_arrow" /> android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout> </LinearLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
+66 -34
View File
@@ -5,47 +5,79 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#80000000"> android:background="#80000000">
<com.example.countdowntimer.CountdownCanvasView
android:id="@+id/cvCountdown"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/buttonRow"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/buttonRow"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="48dp"
android:gravity="center"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<FrameLayout
android:id="@+id/btnStop"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginEnd="16dp"
android:background="@drawable/btn_stop_selector"
android:clickable="true"
android:focusable="true">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:contentDescription="@string/stop_timer"
android:src="@drawable/ic_stop_square" />
</FrameLayout>
<FrameLayout
android:id="@+id/btnRestart"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginStart="16dp"
android:background="@drawable/btn_restart_selector"
android:clickable="true"
android:focusable="true">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:contentDescription="@string/restart_timer"
android:src="@drawable/ic_refresh_square" />
</FrameLayout>
</LinearLayout>
<Button <Button
android:id="@+id/btnDismiss" android:id="@+id/btnDismiss"
android:layout_width="wrap_content" android:layout_width="100dp"
android:layout_height="wrap_content" android:layout_height="100dp"
android:background="@drawable/btn_duration_selector"
android:gravity="center"
android:padding="16dp"
android:text="Dismiss" android:text="Dismiss"
android:textColor="#FFFFFF"
android:textSize="14sp"
android:textStyle="bold"
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent" />
<com.example.countdowntimer.CountdownCanvasView
android:id="@+id/cvCountdown"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/btnStop"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_margin="16dp" />
<Button
android:id="@+id/btnStop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stop"
android:layout_marginTop="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/btnRestart"
app:layout_constraintHorizontal_chainStyle="spread" />
<Button
android:id="@+id/btnRestart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Restart"
android:layout_marginTop="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/btnStop"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
+2
View File
@@ -2,4 +2,6 @@
<resources> <resources>
<color name="color_gradient_start">#1B3A1B</color> <color name="color_gradient_start">#1B3A1B</color>
<color name="color_gradient_end">#0D1F0D</color> <color name="color_gradient_end">#0D1F0D</color>
<color name="md_color_ring_progress">#A5D6A7</color>
<color name="md_color_ring_track">#2E7D32</color>
</resources> </resources>
+1
View File
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="md_theme_dark_primary">#A5D6A7</color> <color name="md_theme_dark_primary">#A5D6A7</color>
<color name="md_theme_dark_primaryContainer">#2E7D32</color>
<color name="md_theme_dark_secondary">#869F7C</color> <color name="md_theme_dark_secondary">#869F7C</color>
<style name="Theme.CountdownTimer" parent="Theme.Material3.DayNight.NoActionBar"> <style name="Theme.CountdownTimer" parent="Theme.Material3.DayNight.NoActionBar">
+2
View File
@@ -3,4 +3,6 @@
<color name="color_gradient_start">#E8F5E9</color> <color name="color_gradient_start">#E8F5E9</color>
<color name="color_gradient_end">#C8E6C9</color> <color name="color_gradient_end">#C8E6C9</color>
<color name="color_icon_tint">#2E7D32</color> <color name="color_icon_tint">#2E7D32</color>
<color name="md_color_ring_progress">#2E7D32</color>
<color name="md_color_ring_track">#81C784</color>
</resources> </resources>
+2
View File
@@ -11,4 +11,6 @@
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="vibration">Vibration</string> <string name="vibration">Vibration</string>
<string name="sounds">Sounds</string> <string name="sounds">Sounds</string>
<string name="stop_timer">Stop Timer</string>
<string name="restart_timer">Restart Timer</string>
</resources> </resources>
+2
View File
@@ -1,12 +1,14 @@
<resources> <resources>
<style name="Theme.CountdownTimer" parent="Theme.Material3.DayNight.NoActionBar"> <style name="Theme.CountdownTimer" parent="Theme.Material3.DayNight.NoActionBar">
<item name="colorPrimary">@color/md_theme_light_primary</item> <item name="colorPrimary">@color/md_theme_light_primary</item>
<item name="colorPrimaryContainer">@color/md_theme_light_primaryContainer</item>
<item name="colorSecondary">@color/md_theme_light_secondary</item> <item name="colorSecondary">@color/md_theme_light_secondary</item>
<item name="android:statusBarColor">@color/color_gradient_start</item> <item name="android:statusBarColor">@color/color_gradient_start</item>
<item name="android:windowLightStatusBar">true</item> <item name="android:windowLightStatusBar">true</item>
</style> </style>
<color name="md_theme_light_primary">#2E7D32</color> <color name="md_theme_light_primary">#2E7D32</color>
<color name="md_theme_light_primaryContainer">#81C784</color>
<color name="md_theme_light_secondary">#4C6842</color> <color name="md_theme_light_secondary">#4C6842</color>
<color name="md_theme_light_surface">#F1F8E9</color> <color name="md_theme_light_surface">#F1F8E9</color>
</resources> </resources>
+88
View File
@@ -0,0 +1,88 @@
# Goal: Redesign all buttons into bold, square, visually striking interactive elements
## Objective
Replace every basic/rectangular button in the app with larger, square-aspect-ratio buttons that have eye-catching visual design. The goal is to transform the UI from a generic Material 3 look into a distinctive, personality-rich interface that matches the playful "BONG HIT TIMER" brand.
## Current State
The app uses 4 visible buttons across 2 screens:
**MainActivity (home screen):**
- `btnDurationLabel` — outlined button showing selected duration (e.g. "10s") with a dropdown arrow. Uses `Widget.Material3.Button.OutlinedButton` with 20dp corners.
- `btnStart` — filled button labeled "Start Timer" with a play icon. Uses `Widget.Material3.Button` with 20dp corners.
**CountdownDialog (timer screen):**
- `btnStop` — plain Android `Button` labeled "Stop" (default styling, small)
- `btnRestart` — plain Android `Button` labeled "Restart" (default styling, small)
- `btnDismiss` — plain Android `Button` labeled "Dismiss" (currently gone, shows after timer completes)
## Design Requirements
### 1. Square Aspect Ratio
- Change all buttons from `wrap_content` rectangles to a fixed square size (e.g., `120dp` x `120dp` or `100dp` x `100dp`)
- Use `app:layout_constraintDimensionRatio="1:1"` where dynamic sizing is needed
- Buttons should feel substantial and tappable, not compact
### 2. Visual Interest — Each button should have its own personality
Create custom drawable backgrounds for each button type. Design ideas (pick the best approach):
- **Circular/spherical buttons** — Buttons that look like physical 3D balls or pucks. Use gradient fills with highlights/shadows to create a convex "pressable" look.
- **Neon/glow effect** — Since the app is a bong hit timer, a subtle neon glow that pulses or intensifies during countdown would be thematically appropriate.
- **Retro/gaming arcade style** — Bold colors, thick borders, pressed/active states that simulate physical button depression.
- **Gradient + icon centering** — For the timer screen, center the stop/restart icons prominently in the square buttons with bold gradient backgrounds.
### 3. Interactive States
Every button must have rich feedback:
- **Pressed/active state** — noticeable visual change (shrink, darker shade, or inner glow)
- **Default state** — visually engaging with gradients, shadows, or subtle animations
- **Disabled state** — reduced opacity but still recognizable
### 4. Icon Integration
Square buttons are perfect for icon-first design:
- **Stop button** — large centered pause/stop icon
- **Restart button** — large centered refresh/replay icon
- **Start button** — center the play icon, consider removing the text label since the icon is universal
- **Duration label** — consider a pill-shaped or circular chip that pulses when tapped
### 5. Color Palette
Stay within the existing green theme (`#2E7D32` primary, `#4C6842` secondary, `#E8F5E9`/`#C8E6C9` gradient) but push it further:
- Use the primary green for the start/positive actions
- A red/coral accent for stop
- A neutral or blue for restart/duration selection
- Consider gradient transitions within each button (e.g., green to darker green top-to-bottom for a 3D effect)
### 6. Shadow & Elevation
- Add drop shadows to all buttons for depth (use `app:strokeWidth="0dp"` and custom `rippleColor` for Material buttons)
- Create a `btn_square_default.xml` and `btn_square_pressed.xml` in `res/drawable/` for each button type
- The pressed state should visually simulate "pressing in" (reduced shadow, lighter/darker fill)
## Technical Implementation
### New drawable files to create (in `res/drawable/`):
- `btn_start_square.xml` — gradient + shadow drawable for start button
- `btn_start_square_pressed.xml` — pressed state variant
- `btn_stop_square.xml` — gradient + shadow drawable for stop button
- `btn_stop_square_pressed.xml` — pressed state variant
- `btn_restart_square.xml` — gradient + shadow drawable for restart button
- `btn_restart_square_pressed.xml` — pressed state variant
- `btn_duration_chip.xml` — drawable for duration selector
### Layout changes needed:
- `activity_main.xml`:
- `btnDurationLabel`: change to square dimensions, wrap text/icon in center, apply new drawable
- `btnStart`: change to square, center play icon, apply new drawable
- `dialog_countdown.xml`:
- `btnStop`: change from `Button` to `com.google.android.material.button.MaterialButton`, set square dimensions, apply drawable
- `btnRestart`: same treatment
- `btnDismiss`: same treatment, keep as gone by default
- Adjust the ConstraintLayout chain to accommodate square buttons (may need a horizontal chain with proper spacing)
### Code changes needed:
- `CountdownDialog.kt`: Set button backgrounds programmatically if needed, ensure clickable/pressable states work
- No changes to button click logic — only visual overhaul
## Constraints
- Use only Material 3 components already in the dependency (`com.google.android.material:material:1.11.0`)
- Traditional XML layouts — no Compose
- Minimum SDK 24 — no API 30+ features
- Keep the app's existing green aesthetic but make it bolder
- Maintain accessibility (minimum 48dp touch target, sufficient contrast)