[TimePicker] Default State to system time setting
Relnote: TimePickers is24Hour uses system setting
Test: timePickerState_format_*
Bug: 267174857, 268199094
Change-Id: I18856a395db9ce7e4dbd099299ded52407fd2873
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index 5ec853d..0e225db 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -846,6 +846,9 @@
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
}
+ public final class TimeFormat_androidKt {
+ }
+
public final class TimePickerKt {
}
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index 8eec45e..0df8104 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -1175,6 +1175,9 @@
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
}
+ public final class TimeFormat_androidKt {
+ }
+
@androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class TimePickerColors {
}
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index 5ec853d..0e225db 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -846,6 +846,9 @@
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
}
+ public final class TimeFormat_androidKt {
+ }
+
public final class TimePickerKt {
}
diff --git a/compose/material3/material3/build.gradle b/compose/material3/material3/build.gradle
index 279836b..4b043b1 100644
--- a/compose/material3/material3/build.gradle
+++ b/compose/material3/material3/build.gradle
@@ -67,8 +67,7 @@
androidTestImplementation(libs.testRunner)
androidTestImplementation(libs.junit)
androidTestImplementation(libs.truth)
- androidTestImplementation(libs.dexmakerMockito)
- androidTestImplementation(libs.mockitoCore)
+ androidTestImplementation(libs.dexmakerMockitoInlineExtended)
androidTestImplementation(libs.mockitoKotlin)
androidTestImplementation(libs.testUiautomator)
@@ -134,13 +133,11 @@
implementation(project(':compose:foundation:foundation-layout'))
implementation(project(":test:screenshot:screenshot"))
implementation(project(":core:core"))
-
implementation(libs.testRules)
implementation(libs.testRunner)
implementation(libs.junit)
implementation(libs.truth)
- implementation(libs.dexmakerMockito)
- implementation(libs.mockitoCore)
+ implementation(libs.dexmakerMockitoInlineExtended)
implementation(libs.mockitoKotlin)
implementation(libs.testUiautomator)
}
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TimePickerSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TimePickerSamples.kt
index ca6b7b4..f455324 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TimePickerSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TimePickerSamples.kt
@@ -16,7 +16,6 @@
package androidx.compose.material3.samples
-import android.text.format.DateFormat
import androidx.annotation.Sampled
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
@@ -42,11 +41,9 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
@@ -62,9 +59,7 @@
@Preview
fun TimePickerSample() {
var showTimePicker by remember { mutableStateOf(false) }
- val context = LocalContext.current
- val is24HourFormat by rememberUpdatedState(DateFormat.is24HourFormat(context))
- val state = rememberTimePickerState(is24Hour = is24HourFormat)
+ val state = rememberTimePickerState()
val formatter = remember { SimpleDateFormat("hh:mm a", Locale.getDefault()) }
val snackState = remember { SnackbarHostState() }
val snackScope = rememberCoroutineScope()
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt
index 6dbc6fc..04d31bf 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt
@@ -1225,6 +1225,7 @@
}
@Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
fun testOutlinedTextField_imeActionAndKeyboardTypePropagatedDownstream() {
val platformTextInputService = mock<PlatformTextInputService>()
val textInputService = TextInputService(platformTextInputService)
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SnackbarHostTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SnackbarHostTest.kt
index 81b8b4e..8201b0a 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SnackbarHostTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SnackbarHostTest.kt
@@ -16,6 +16,7 @@
package androidx.compose.material3
+import android.os.Build
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
@@ -33,6 +34,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
import com.google.common.truth.Truth
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.doReturn
@@ -192,6 +194,7 @@
}
@Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
fun snackbarDuration_toMillis_nonNullAccessibilityManager() {
val mockDurationControl = 10000L
val mockDurationNonControl = 5000L
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TextFieldTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TextFieldTest.kt
index c3818c3e..447e330 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TextFieldTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TextFieldTest.kt
@@ -1248,6 +1248,7 @@
}
@Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
fun testTextField_imeActionAndKeyboardTypePropagatedDownstream() {
val platformTextInputService = mock<PlatformTextInputService>()
val textInputService = TextInputService(platformTextInputService)
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TimePickerTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TimePickerTest.kt
index d9a9edd..3888776 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TimePickerTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TimePickerTest.kt
@@ -16,6 +16,10 @@
package androidx.compose.material3
+import android.content.Context
+import android.os.Build
+import android.text.format.DateFormat
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.semantics.SemanticsProperties.SelectableGroup
@@ -45,14 +49,20 @@
import androidx.compose.ui.test.performTouchInput
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.MockedMethod
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.quality.Strictness
@OptIn(ExperimentalMaterial3Api::class)
@MediumTest
@RunWith(AndroidJUnit4::class)
+
class TimePickerTest {
@get:Rule
@@ -141,6 +151,60 @@
}
@Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+ fun timePickerState_format_12h() {
+ lateinit var context: Context
+ lateinit var state: TimePickerState
+ val session = mockitoSession()
+ .spyStatic(DateFormat::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
+ try {
+ rule.setMaterialContent(lightColorScheme()) {
+ context = LocalContext.current
+ doReturn(false).`when`(object : MockedMethod<Boolean> {
+ override fun get(): Boolean {
+ return DateFormat.is24HourFormat(context)
+ }
+ })
+
+ state = rememberTimePickerState()
+ }
+ } finally {
+ session.finishMocking()
+ }
+
+ assertThat(state.is24hour).isFalse()
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+ fun timePickerState_format_24h() {
+ lateinit var context: Context
+ lateinit var state: TimePickerState
+ val session = mockitoSession()
+ .spyStatic(DateFormat::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
+ try {
+ rule.setMaterialContent(lightColorScheme()) {
+ context = LocalContext.current
+ doReturn(true).`when`(object : MockedMethod<Boolean> {
+ override fun get(): Boolean {
+ return DateFormat.is24HourFormat(context)
+ }
+ })
+
+ state = rememberTimePickerState()
+ }
+ } finally {
+ session.finishMocking()
+ }
+
+ assertThat(state.is24hour).isTrue()
+ }
+
+ @Test
fun timePicker_toggle_semantics() {
val state = TimePickerState(initialHour = 14, initialMinute = 23, is24Hour = false)
lateinit var contentDescription: String
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/TimeFormat.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/TimeFormat.android.kt
new file mode 100644
index 0000000..ea45cd6
--- /dev/null
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/TimeFormat.android.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import android.text.format.DateFormat.is24HourFormat
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.ui.platform.LocalContext
+
+internal actual val is24HourFormat: Boolean
+ @Composable
+ @ReadOnlyComposable get() = is24HourFormat(LocalContext.current)
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimeFormat.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimeFormat.kt
new file mode 100644
index 0000000..01f635a
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimeFormat.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+
+internal expect val is24HourFormat: Boolean
+ @Composable
+ @ReadOnlyComposable get
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt
index ef9cb5a..f4c7f87 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt
@@ -337,15 +337,15 @@
* Ranges from 0 to 23
* @param initialMinute starting minute for this state, will be displayed in the time picker when
* launched. Ranges from 0 to 59
- * @param is24Hour The format for this time picker `false` for 12 hour format with an AM/PM toggle
- * or `true` for 24 hour format without toggle.
+ * @param is24Hour The format for this time picker. `false` for 12 hour format with an AM/PM toggle
+ * or `true` for 24 hour format without toggle. Defaults to follow system setting.
*/
@Composable
@ExperimentalMaterial3Api
fun rememberTimePickerState(
initialHour: Int = 0,
initialMinute: Int = 0,
- is24Hour: Boolean = false,
+ is24Hour: Boolean = is24HourFormat,
): TimePickerState = rememberSaveable(
saver = TimePickerState.Saver()
) {
diff --git a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/TimeFormat.desktop.kt b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/TimeFormat.desktop.kt
new file mode 100644
index 0000000..6f365ff
--- /dev/null
+++ b/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material/TimeFormat.desktop.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+
+internal actual val is24HourFormat: Boolean
+ @Composable
+ @ReadOnlyComposable get() = false
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 68d0f3e..fb082dc 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -100,6 +100,7 @@
daggerCompiler = { module = "com.google.dagger:dagger-compiler", version.ref = "dagger" }
dexmakerMockito = { module = "com.linkedin.dexmaker:dexmaker-mockito", version.ref = "dexmaker" }
dexmakerMockitoInline = { module = "com.linkedin.dexmaker:dexmaker-mockito-inline", version.ref = "dexmaker" }
+dexmakerMockitoInlineExtended = { module = "com.linkedin.dexmaker:dexmaker-mockito-inline-extended", version.ref = "dexmaker" }
dexMemberList = { module = "com.jakewharton.dex:dex-member-list", version = "4.1.1" }
dokkaCli = { module = "org.jetbrains.dokka:dokka-cli", version.ref = "dokka" }
dokkaAnalysis = { module = "org.jetbrains.dokka:dokka-analysis", version.ref = "dokka" }