From 0d3652c1b876d22961bb99a400b1325518490a55 Mon Sep 17 00:00:00 2001 From: anhtuanm Date: Sat, 7 Feb 2026 13:28:47 +0100 Subject: [PATCH 1/4] Optimize Wheel.kt --- .../src/main/java/com/anhaki/picktime/components/Wheel.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/picktime/src/main/java/com/anhaki/picktime/components/Wheel.kt b/picktime/src/main/java/com/anhaki/picktime/components/Wheel.kt index df45cb4..2035fbe 100644 --- a/picktime/src/main/java/com/anhaki/picktime/components/Wheel.kt +++ b/picktime/src/main/java/com/anhaki/picktime/components/Wheel.kt @@ -158,12 +158,14 @@ internal fun Wheel( } else { (firstIndex + if (firstVisibleOffset > maxOffset / 2) 1 else 0) % items.size } - onItemSelected(selected) - localSelectedItem = selected + if (localSelectedItem != selected) { + localSelectedItem = selected + onItemSelected(selected) + } } LaunchedEffect(isScrolling) { - if (!isScrolling) { + if (!isScrolling && firstVisibleOffset != 0) { coroutineScope.launch { listState.animateScrollToItem(firstIndex + if (firstVisibleOffset > maxOffset / 2) 1 else 0) } From 29637ff899c1fda679ad2f61f477c28c59554250 Mon Sep 17 00:00:00 2001 From: anhtuanm Date: Sat, 7 Feb 2026 13:30:07 +0100 Subject: [PATCH 2/4] Stabilize NumberWheel.kt text width --- .../src/main/java/com/anhaki/picktime/components/NumberWheel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picktime/src/main/java/com/anhaki/picktime/components/NumberWheel.kt b/picktime/src/main/java/com/anhaki/picktime/components/NumberWheel.kt index 71ed982..e78d0cb 100644 --- a/picktime/src/main/java/com/anhaki/picktime/components/NumberWheel.kt +++ b/picktime/src/main/java/com/anhaki/picktime/components/NumberWheel.kt @@ -46,6 +46,6 @@ internal fun NumberWheel( isLooping = isLooping, overlayColor = overlayColor, itemToString = { it.toString().padStart(2, '0') }, - longestText = items.maxBy { it }.toString() + longestText = "00" ) } From 72550fa3caf057cec09947e870596b5acfec5161 Mon Sep 17 00:00:00 2001 From: anhtuanm Date: Sat, 7 Feb 2026 14:29:15 +0100 Subject: [PATCH 3/4] Add PickHourMinuteDuration --- .../anhaki/picktime/PickHourMinuteDuration.kt | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 picktime/src/main/java/com/anhaki/picktime/PickHourMinuteDuration.kt diff --git a/picktime/src/main/java/com/anhaki/picktime/PickHourMinuteDuration.kt b/picktime/src/main/java/com/anhaki/picktime/PickHourMinuteDuration.kt new file mode 100644 index 0000000..67e2583 --- /dev/null +++ b/picktime/src/main/java/com/anhaki/picktime/PickHourMinuteDuration.kt @@ -0,0 +1,134 @@ +package com.anhaki.picktime + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.key +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.anhaki.picktime.components.GenericPickTime +import com.anhaki.picktime.components.NumberWheel +import com.anhaki.picktime.utils.PickTimeFocusIndicator +import com.anhaki.picktime.utils.PickTimeTextStyle + +/** + * A composable function that creates a customizable duration picker consisting of hour and minute wheel pickers, + * with an enforced maximum total duration. The minute range dynamically adjusts based on the selected hour + * to ensure the combined value never exceeds [maxValue]. + * + * @param initialHour The initial value of the hour wheel picker. + * @param onHourChange The callback function invoked when the hour value changes. + * @param initialMinute The initial value of the minute wheel picker. + * @param onMinuteChange The callback function invoked when the minute value changes. Also called automatically + * when the minute range shrinks due to an hour change, with the clamped minute value. + * @param maxValue The maximum total duration in minutes (e.g., `4 * 60 + 30` for 4h30m). Defaults to `23 * 60 + 59` (23h59m). + * The hour range is derived as `0..(maxValue / 60)` and the minute range adjusts per selected hour. + * @param selectedTextStyle The style of the selected text, using [PickTimeTextStyle] including `color`, `fontSize`, `fontFamily`, and `fontWeight`. + * @param unselectedTextStyle The style of the unselected text, using [PickTimeTextStyle] including `color`, `fontSize`, `fontFamily`, and `fontWeight`. + * @param verticalSpace The vertical spacing between each item of the wheel picker. + * @param horizontalSpace The horizontal spacing between the wheel pickers and the colon. + * @param containerColor The color of the container (background and gradient overlay). + * @param isLooping Whether the wheel pickers loop infinitely. + * @param extraRow The number of extra rows shown on each side (top and bottom). + * @param focusIndicator The focus indicator displayed at the center of the wheel pickers, using [PickTimeFocusIndicator] including `enabled`, `widthFull`, `background`, `shape`, and `border`. + * + * Note: The function internally clamps `initialHour` between 0 and `maxValue / 60` (max 23), + * and `initialMinute` between 0 and the remaining minutes for the selected hour. + */ + +@Composable +fun PickHourMinuteDuration( + initialHour: Int, + onHourChange: (Int) -> Unit, + initialMinute: Int, + onMinuteChange: (Int) -> Unit, + maxValue: Int = 23*60 + 59, + selectedTextStyle: PickTimeTextStyle = PickTimeTextStyle( + color = Color(0xFF404040), + fontSize = 24.sp, + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Bold, + ), + unselectedTextStyle: PickTimeTextStyle = PickTimeTextStyle( + color = Color(0xFF9F9F9F), + fontSize = 18.sp, + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + ), + verticalSpace: Dp = 10.dp, + horizontalSpace: Dp = 10.dp, + containerColor: Color = Color(0xFFFFFFFF), + isLooping: Boolean = false, + extraRow: Int = 2, + focusIndicator: PickTimeFocusIndicator = PickTimeFocusIndicator( + enabled = true, + widthFull = false, + background = Color(0xFFFFFFFF), + shape = RoundedCornerShape(20.dp), + border = BorderStroke(4.dp, Color(0xFFEE4720)), + ) +) { + val maxHour = (maxValue / 60).coerceIn(0, 23) + val hourRange = 0..maxHour + val displayedHour = initialHour.coerceIn(0, maxHour) + val restMinutes = maxValue - displayedHour * 60 + val minuteRange = if (restMinutes > 0) 0..restMinutes.coerceAtMost(59) else 0..0 + val displayedMinute = initialMinute.coerceIn(minuteRange.first, minuteRange.last) + + LaunchedEffect(minuteRange) { + onMinuteChange(displayedMinute) + } + + val row = extraRow.coerceIn(1, 5) + + val adjustedSelectedTextStyle = if (selectedTextStyle.fontSize < unselectedTextStyle.fontSize) { + selectedTextStyle.copy(fontSize = unselectedTextStyle.fontSize) + } else selectedTextStyle + + GenericPickTime( + selectedTextStyle = adjustedSelectedTextStyle, + verticalSpace = verticalSpace, + containerColor = containerColor, + focusIndicator = focusIndicator + ){ + NumberWheel( + items = hourRange.toList(), + selectedItem = displayedHour, + onItemSelected = onHourChange, + space = verticalSpace, + selectedTextStyle = adjustedSelectedTextStyle, + unselectedTextStyle = unselectedTextStyle, + extraRow = row, + isLooping = isLooping, + overlayColor = containerColor, + ) + Spacer(modifier = Modifier.width(horizontalSpace)) + Text( + text = ":", + style = adjustedSelectedTextStyle.toTextStyle() + ) + Spacer(modifier = Modifier.width(horizontalSpace)) + key(minuteRange) { + NumberWheel( + items = minuteRange.toList(), + selectedItem = displayedMinute, + onItemSelected = onMinuteChange, + space = verticalSpace, + selectedTextStyle = adjustedSelectedTextStyle, + unselectedTextStyle = unselectedTextStyle, + extraRow = row, + isLooping = isLooping, + overlayColor = containerColor, + ) + } + } +} From d1fac9941b825d15b5946d9183c6c234d7f1e48a Mon Sep 17 00:00:00 2001 From: anhtuanm Date: Tue, 14 Apr 2026 10:30:04 +0200 Subject: [PATCH 4/4] feat : publish fork version on JitPack.io --- app/build.gradle.kts | 4 +++- picktime/build.gradle.kts | 4 ++-- settings.gradle.kts | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a0eccd2..d1c6ca5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -57,5 +57,7 @@ dependencies { debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) - implementation(project(":picktime")) + //noinspection UseTomlInstead + implementation("com.github.anhtuanmai:PickTime-Compose:1.1.5.1") +// implementation(project(":picktime")) } \ No newline at end of file diff --git a/picktime/build.gradle.kts b/picktime/build.gradle.kts index 6a23f50..d6e61a1 100644 --- a/picktime/build.gradle.kts +++ b/picktime/build.gradle.kts @@ -40,9 +40,9 @@ android { publishing { publications { create("release", MavenPublication::class) { - groupId = "com.github.anhaki" + groupId = "com.github.anhtuanmai" artifactId = "picktime-compose" - version = "1.1.5" + version = "1.1.5.1" afterEvaluate { from(components["release"]) diff --git a/settings.gradle.kts b/settings.gradle.kts index 78a7080..5305bfc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,14 +9,14 @@ pluginManagement { } mavenCentral() gradlePluginPortal() - } + maven { url = uri("https://jitpack.io") } } } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() - } + maven { url = uri("https://jitpack.io") } } } rootProject.name = "PickTime"