Merge "Only show AnimatedText when API level >= S" into androidx-main
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/binarycompatibilityvalidator/BinaryCompatibilityValidation.kt b/buildSrc/private/src/main/kotlin/androidx/build/binarycompatibilityvalidator/BinaryCompatibilityValidation.kt
index cc9782a..dff7821 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/binarycompatibilityvalidator/BinaryCompatibilityValidation.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/binarycompatibilityvalidator/BinaryCompatibilityValidation.kt
@@ -20,11 +20,14 @@
import androidx.build.AndroidXMultiplatformExtension
import androidx.build.Version
+import androidx.build.addToBuildOnServer
+import androidx.build.addToCheckTask
import androidx.build.checkapi.ApiType
import androidx.build.checkapi.getBcvFileDirectory
import androidx.build.checkapi.getBuiltBcvFileDirectory
import androidx.build.checkapi.getRequiredCompatibilityApiFileFromDir
import androidx.build.checkapi.shouldWriteVersionedApiFile
+import androidx.build.uptodatedness.cacheEvenIfNoOutputs
import androidx.build.version
import com.android.utils.appendCapitalized
import java.io.File
@@ -79,6 +82,8 @@
val updateAll: TaskProvider<Task> = project.tasks.register(UPDATE_NAME)
configureKlibTasks(project, checkAll, updateAll)
project.tasks.named("check").configure { it.dependsOn(checkAll) }
+ project.addToCheckTask(checkAll)
+ project.addToBuildOnServer(checkAll)
}
private fun configureKlibTasks(
@@ -138,14 +143,16 @@
/* Check that the current ABI definition is up to date. */
private fun Project.checkKlibAbiTask(projectApiFile: File, generatedApiFile: File) =
- project.tasks.register(
- CHECK_NAME.appendCapitalized(NATIVE_SUFFIX),
- KotlinApiCompareTask::class.java
- ) {
- it.projectApiFile = projectApiFile
- it.generatedApiFile = generatedApiFile
- it.group = ABI_GROUP_NAME
- }
+ project.tasks
+ .register(
+ CHECK_NAME.appendCapitalized(NATIVE_SUFFIX),
+ KotlinApiCompareTask::class.java
+ ) {
+ it.projectApiFile = projectApiFile
+ it.generatedApiFile = generatedApiFile
+ it.group = ABI_GROUP_NAME
+ }
+ .also { task -> task.configure { it.cacheEvenIfNoOutputs() } }
/* Check that the current ABI definition is compatible with most recently released version */
private fun Project.checkKlibAbiReleaseTask(
@@ -205,7 +212,7 @@
*/
private fun Project.extractKlibAbiTask(klibApiDir: File, extractDir: File) =
project.tasks.register(EXTRACT_NAME, KotlinKlibExtractSupportedTargetsAbiTask::class.java) {
- it.strictValidation = true
+ it.strictValidation = HostManager.hostIsMac
it.supportedTargets = project.provider { supportedNativeTargetNames() }
it.inputAbiFile = klibApiDir.resolve(CURRENT_API_FILE_NAME)
it.outputAbiFile = extractDir.resolve(CURRENT_API_FILE_NAME)
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/concurrent/DualSurfaceProcessor.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/concurrent/DualSurfaceProcessor.java
index 74981b2..6e85928 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/concurrent/DualSurfaceProcessor.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/concurrent/DualSurfaceProcessor.java
@@ -127,7 +127,7 @@
mPrimarySurfaceTexture = surfaceTexture;
} else {
mSecondarySurfaceTexture = surfaceTexture;
- // Only render when primary camera frames come in
+ // Only render when secondary camera frames come in
surfaceTexture.setOnFrameAvailableListener(this, mGlHandler);
}
}, surfaceRequest::willNotProvideSurface);
diff --git a/compose/foundation/foundation/build.gradle b/compose/foundation/foundation/build.gradle
index 95118fd..f969a10 100644
--- a/compose/foundation/foundation/build.gradle
+++ b/compose/foundation/foundation/build.gradle
@@ -30,10 +30,10 @@
id("AndroidXComposePlugin")
}
-
androidXMultiplatform {
android()
jvmStubs()
+ linuxX64Stubs()
defaultPlatform(PlatformIdentifier.ANDROID)
@@ -41,13 +41,13 @@
commonMain {
dependencies {
implementation(libs.kotlinStdlib)
- api("androidx.collection:collection:1.4.0")
+ api(project(":collection:collection"))
api(project(":compose:animation:animation"))
api(project(":compose:runtime:runtime"))
api(project(":compose:ui:ui"))
- implementation("androidx.compose.ui:ui-text:1.6.0")
- implementation("androidx.compose.ui:ui-util:1.6.0")
- implementation(project(':compose:foundation:foundation-layout'))
+ implementation(project(":compose:ui:ui-text"))
+ implementation(project(":compose:ui:ui-util"))
+ implementation(project(":compose:foundation:foundation-layout"))
}
}
@@ -74,10 +74,16 @@
}
}
+ commonStubsMain {
+ dependsOn(commonMain)
+ }
+
jvmStubsMain {
- dependsOn(jvmMain)
- dependencies {
- }
+ dependsOn(commonStubsMain)
+ }
+
+ linuxx64StubsMain {
+ dependsOn(commonStubsMain)
}
androidInstrumentedTest {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldFocusTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldFocusTest.kt
index 6718e56..64c33f0 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldFocusTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/TextFieldFocusTest.kt
@@ -71,6 +71,7 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.FlakyTest
import androidx.test.filters.LargeTest
import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry
@@ -382,6 +383,7 @@
rule.onNodeWithTag("test-text-field-1").assertSelection(TextRange(2))
}
+ @FlakyTest(bugId = 348380475)
@SdkSuppress(minSdkVersion = 22) // b/266742195
@Test
fun basicTextField_checkFocusNavigation_onDPadRight_hardwareKeyboard() {
@@ -424,6 +426,7 @@
rule.onNodeWithTag("test-text-field-1").assertSelection(TextRange(3))
}
+ @FlakyTest(bugId = 348380475)
@SdkSuppress(minSdkVersion = 22) // b/266742195
@Test
fun basicTextField_checkFocusNavigation_onDPadDown_hardwareKeyboard() {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt
index aea1f51..a3b1549 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/BasicMarquee.kt
@@ -63,6 +63,7 @@
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.constrainWidth
import androidx.compose.ui.unit.dp
+import kotlin.jvm.JvmInline
import kotlin.math.absoluteValue
import kotlin.math.ceil
import kotlin.math.roundToInt
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/MutatorMutex.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/MutatorMutex.kt
index 8b177f3..066cb2a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/MutatorMutex.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/MutatorMutex.kt
@@ -57,13 +57,7 @@
* lookups to build the exception message and stack trace collection. Remove if these are changed in
* kotlinx.coroutines.
*/
-private class MutationInterruptedException : CancellationException("Mutation interrupted") {
- override fun fillInStackTrace(): Throwable {
- // Avoid null.clone() on Android <= 6.0 when accessing stackTrace
- stackTrace = emptyArray()
- return this
- }
-}
+internal expect class MutationInterruptedException() : CancellationException
/**
* Mutual exclusion for UI state mutation over time.
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/content/TransferableContent.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/content/TransferableContent.kt
index 5c12db2..29f7850 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/content/TransferableContent.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/content/TransferableContent.kt
@@ -19,6 +19,7 @@
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.ui.platform.ClipEntry
import androidx.compose.ui.platform.ClipMetadata
+import kotlin.jvm.JvmInline
/**
* Represents content that can be transferred between applications or processes.
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt
index 6e2afec..40708d7 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt
@@ -1489,12 +1489,7 @@
return if (target > 0) coerceAtMost(target) else coerceAtLeast(target)
}
-private class AnchoredDragFinishedSignal : CancellationException() {
- override fun fillInStackTrace(): Throwable {
- stackTrace = emptyArray()
- return this
- }
-}
+internal expect class AnchoredDragFinishedSignal() : CancellationException
private suspend fun <I> restartable(inputs: () -> I, block: suspend (I) -> Unit) {
try {
@@ -1527,7 +1522,7 @@
) : DraggableAnchors<T> {
init {
- assert(keys.size == anchors.size) {
+ assertOnJvm(keys.size == anchors.size) {
"DraggableAnchors were constructed with " +
"inconsistent key-value sizes. Keys: $keys | Anchors: ${anchors.toList()}"
}
@@ -1608,6 +1603,8 @@
}
}
+internal expect inline fun assertOnJvm(statement: Boolean, message: () -> String): Unit
+
internal val AnchoredDraggableMinFlingVelocity = 125.dp
private const val ConfigurationMovedToModifier =
diff --git a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.kt
similarity index 63%
copy from compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
copy to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.kt
index a5cf43e..0ca25ee 100644
--- a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.kt
@@ -14,14 +14,6 @@
* limitations under the License.
*/
-package androidx.compose.material.ripple
+package androidx.compose.foundation.internal
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun implementedInJetBrainsFork(): Nothing =
- throw NotImplementedError(
- """
- Implemented only in JetBrains fork.
- Please use `org.jetbrains.compose.material:material` package instead.
- """
- .trimIndent()
- )
+internal expect annotation class JvmSynchronized()
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt
index 0565551..b05993a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutItemContentFactory.kt
@@ -22,6 +22,7 @@
import androidx.compose.runtime.ReusableContentHost
import androidx.compose.runtime.Stable
import androidx.compose.runtime.saveable.SaveableStateHolder
+import kotlin.jvm.JvmInline
/**
* This class:
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.kt
index 1518e8e..42d7668 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.kt
@@ -28,7 +28,6 @@
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.util.trace
-import kotlin.system.measureNanoTime
/**
* State for lazy items prefetching, used by lazy layouts to instruct the prefetcher.
@@ -211,6 +210,8 @@
}
}
+internal expect inline fun measureNanoTime(doMeasure: () -> Unit): Long
+
@ExperimentalFoundationApi
private object DummyHandle : PrefetchHandle {
override fun cancel() {}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/ObservableScopeInvalidator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/ObservableScopeInvalidator.kt
index dc2b9cf..d56a7ba 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/ObservableScopeInvalidator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/ObservableScopeInvalidator.kt
@@ -19,6 +19,7 @@
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.neverEqualPolicy
+import kotlin.jvm.JvmInline
/**
* Simple wrapper over a mutable state which allows to invalidate an observable scope. We might
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
index 66a7fed..1c54460 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
@@ -40,6 +40,7 @@
import androidx.compose.ui.util.packInts
import androidx.compose.ui.util.unpackInt1
import androidx.compose.ui.util.unpackInt2
+import kotlin.jvm.JvmInline
import kotlin.math.abs
import kotlin.math.min
import kotlin.math.sign
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
index bec29c5..7cfd681 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
@@ -432,7 +432,7 @@
source: NestedScrollSource
): Offset {
if (source == NestedScrollSource.SideEffect && available.mainAxis() != 0f) {
- throw CancellationException()
+ throw CancellationException("Scroll cancelled")
}
return Offset.Zero
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoView.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoView.kt
index fffad28..ac2df12 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoView.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoView.kt
@@ -22,6 +22,8 @@
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.node.DelegatableNode
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
/**
* Platform-specific "root" of the [BringIntoViewParent] chain to call into when there are no
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt
index 2edc81a..7821f49 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewRequester.kt
@@ -24,6 +24,8 @@
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.InspectorInfo
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
/**
* Can be used to send [bringIntoView] requests. Pass it as a parameter to
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt
index 3896f76..dd7d949 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.kt
@@ -29,6 +29,8 @@
import androidx.compose.ui.node.findNearestAncestor
import androidx.compose.ui.node.requireLayoutCoordinates
import androidx.compose.ui.platform.InspectorInfo
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/ScrollIntoViewRequester.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/ScrollIntoViewRequester.kt
index dd43101..189bb33 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/ScrollIntoViewRequester.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/relocation/ScrollIntoViewRequester.kt
@@ -25,6 +25,8 @@
import androidx.compose.ui.node.DelegatableNode
import androidx.compose.ui.node.requireLayoutCoordinates
import androidx.compose.ui.unit.toSize
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
/**
* Bring this node into bounds by making all the scrollable parents scroll appropriately.
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/ValidatingOffsetMapping.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/ValidatingOffsetMapping.kt
index 39de64f..3bcd0d1 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/ValidatingOffsetMapping.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/ValidatingOffsetMapping.kt
@@ -16,12 +16,12 @@
package androidx.compose.foundation.text
+import androidx.annotation.VisibleForTesting
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.input.OffsetMapping
import androidx.compose.ui.text.input.TransformedText
import androidx.compose.ui.text.input.VisualTransformation
import kotlin.math.min
-import org.jetbrains.annotations.VisibleForTesting
internal val ValidatingEmptyOffsetMappingIdentity: OffsetMapping =
ValidatingOffsetMapping(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer.kt
index 43420ee..9867e6d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldBuffer.kt
@@ -22,6 +22,7 @@
import androidx.compose.foundation.text.input.internal.OffsetMappingCalculator
import androidx.compose.foundation.text.input.internal.PartialGapBuffer
import androidx.compose.ui.text.TextRange
+import kotlin.jvm.JvmName
/**
* A text buffer that can be edited, similar to [StringBuilder].
@@ -179,6 +180,7 @@
// Doc inherited from Appendable.
// This append overload should be first so it ends up being the target of links to this method.
+ @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
override fun append(text: CharSequence?): Appendable = apply {
if (text != null) {
onTextWillChange(length, length, text.length)
@@ -187,6 +189,7 @@
}
// Doc inherited from Appendable.
+ @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
override fun append(text: CharSequence?, start: Int, end: Int): Appendable = apply {
if (text != null) {
onTextWillChange(length, length, end - start)
@@ -195,6 +198,7 @@
}
// Doc inherited from Appendable.
+ @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
override fun append(char: Char): Appendable = apply {
onTextWillChange(length, length, 1)
buffer.replace(buffer.length, buffer.length, char.toString())
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldCharSequence.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldCharSequence.kt
index 350b5eca..5db1e22 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldCharSequence.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldCharSequence.kt
@@ -19,6 +19,7 @@
import androidx.compose.foundation.text.input.internal.toCharArray
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.coerceIn
+import kotlin.jvm.JvmInline
/**
* An immutable snapshot of the contents of a [TextFieldState].
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextObfuscationMode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextObfuscationMode.kt
index 2be9695..2c0c102 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextObfuscationMode.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextObfuscationMode.kt
@@ -16,6 +16,8 @@
package androidx.compose.foundation.text.input
+import kotlin.jvm.JvmInline
+
/**
* Defines how the text will be obscured in secure text fields.
*
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/GapBuffer.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/GapBuffer.kt
index 0c7fcc1..98e60df 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/GapBuffer.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/GapBuffer.kt
@@ -167,8 +167,8 @@
* @param builder The output string builder
*/
fun append(builder: StringBuilder) {
- builder.append(buffer, 0, gapStart)
- builder.append(buffer, gapEnd, capacity - gapEnd)
+ builder.appendRange(buffer, 0, gapStart)
+ builder.appendRange(buffer, gapEnd, capacity)
}
/**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.kt
index ac8290f..242a2c4 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.kt
@@ -16,6 +16,7 @@
package androidx.compose.foundation.text.input.internal
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.content.MediaType
import androidx.compose.ui.draganddrop.DragAndDropEvent
import androidx.compose.ui.draganddrop.DragAndDropModifierNode
@@ -23,6 +24,7 @@
import androidx.compose.ui.platform.ClipEntry
import androidx.compose.ui.platform.ClipMetadata
+@ExperimentalFoundationApi
internal expect fun textFieldDragAndDropNode(
hintMediaTypes: () -> Set<MediaType>,
onDrop: (clipEntry: ClipEntry, clipMetadata: ClipMetadata) -> Boolean,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/InlineDensity.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/InlineDensity.kt
index ebe89ee..f41cb97 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/InlineDensity.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/modifiers/InlineDensity.kt
@@ -20,6 +20,7 @@
import androidx.compose.ui.util.packFloats
import androidx.compose.ui.util.unpackFloat1
import androidx.compose.ui.util.unpackFloat2
+import kotlin.jvm.JvmInline
/**
* [Density] is an interface, not a final class. When you want to have a snapshot of Density values
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
index e8adbcb..c4bfef6 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/MultiWidgetSelectionDelegate.kt
@@ -16,6 +16,7 @@
package androidx.compose.foundation.text.selection
+import androidx.compose.foundation.internal.JvmSynchronized
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.isUnspecified
@@ -44,7 +45,7 @@
* instance check is enough to accomplish whether a text layout has changed in a meaningful way.
*/
private val TextLayoutResult.lastVisibleOffset: Int
- @Synchronized
+ @JvmSynchronized
get() {
if (_previousTextLayoutResult !== this) {
val lastVisibleLine =
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/BasicTooltip.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/BasicTooltip.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/BasicTooltip.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/BasicTooltip.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/Clickable.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/Clickable.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/Clickable.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/Clickable.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/DarkTheme.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/DarkTheme.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/DarkTheme.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/DarkTheme.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/DesktopOverscroll.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/DesktopOverscroll.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/DesktopOverscroll.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/DesktopOverscroll.commonStubs.kt
diff --git a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/Expect.commonStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/Expect.commonStubs.kt
new file mode 100644
index 0000000..b425466
--- /dev/null
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/Expect.commonStubs.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021 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.foundation
+
+internal actual class AtomicReference<V> actual constructor(value: V) {
+ actual fun get(): V = implementedInJetBrainsFork()
+
+ actual fun set(value: V) {
+ implementedInJetBrainsFork()
+ }
+
+ actual fun getAndSet(value: V): V = implementedInJetBrainsFork()
+
+ actual fun compareAndSet(expect: V, newValue: V): Boolean = implementedInJetBrainsFork()
+}
+
+internal actual class AtomicLong actual constructor(value: Long) {
+ actual fun get(): Long = implementedInJetBrainsFork()
+
+ actual fun set(value: Long) {
+ implementedInJetBrainsFork()
+ }
+
+ actual fun getAndIncrement(): Long = implementedInJetBrainsFork()
+}
diff --git a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/MutatorMutex.commonStubs.kt
similarity index 63%
copy from compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
copy to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/MutatorMutex.commonStubs.kt
index a5cf43e..8272b93 100644
--- a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/MutatorMutex.commonStubs.kt
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-package androidx.compose.material.ripple
+package androidx.compose.foundation
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun implementedInJetBrainsFork(): Nothing =
- throw NotImplementedError(
- """
- Implemented only in JetBrains fork.
- Please use `org.jetbrains.compose.material:material` package instead.
- """
- .trimIndent()
- )
+import kotlinx.coroutines.CancellationException
+
+internal actual class MutationInterruptedException : CancellationException("Mutation interrupted") {
+ init {
+ implementedInJetBrainsFork()
+ }
+}
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/NotImplemented.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/NotImplemented.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/NotImplemented.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/NotImplemented.commonStubs.kt
diff --git a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/MediaType.commonStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/MediaType.commonStubs.kt
new file mode 100644
index 0000000..015092f
--- /dev/null
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/MediaType.commonStubs.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.foundation.content
+
+import androidx.compose.foundation.implementedInJetBrainsFork
+
+actual class MediaType internal constructor() {
+ actual constructor(representation: String) : this()
+
+ actual val representation: String = implementedInJetBrainsFork()
+
+ actual companion object {
+ actual val Text: MediaType = implementedInJetBrainsFork()
+ actual val PlainText: MediaType = implementedInJetBrainsFork()
+ actual val HtmlText: MediaType = implementedInJetBrainsFork()
+ actual val Image: MediaType = implementedInJetBrainsFork()
+ actual val All: MediaType = implementedInJetBrainsFork()
+ }
+}
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/TransferableContent.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/TransferableContent.commonStubs.kt
similarity index 88%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/TransferableContent.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/TransferableContent.commonStubs.kt
index 0f012d2..3c35fdf 100644
--- a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/TransferableContent.jvmStubs.kt
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/TransferableContent.commonStubs.kt
@@ -19,10 +19,13 @@
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.implementedInJetBrainsFork
import androidx.compose.ui.platform.ClipEntry
-import java.awt.datatransfer.Transferable
@ExperimentalFoundationApi
-actual class PlatformTransferableContent internal constructor(val transferable: Transferable)
+actual class PlatformTransferableContent internal constructor() {
+ init {
+ implementedInJetBrainsFork()
+ }
+}
@ExperimentalFoundationApi
actual fun TransferableContent.hasMediaType(mediaType: MediaType): Boolean =
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/internal/DragAndDropRequestPermission.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/internal/DragAndDropRequestPermission.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/internal/DragAndDropRequestPermission.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/internal/DragAndDropRequestPermission.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/internal/ReceiveContentDragAndDropNode.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/internal/ReceiveContentDragAndDropNode.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/internal/ReceiveContentDragAndDropNode.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/content/internal/ReceiveContentDragAndDropNode.commonStubs.kt
diff --git a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.commonStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.commonStubs.kt
new file mode 100644
index 0000000..d9cb99e
--- /dev/null
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.commonStubs.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2024 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.foundation.gestures
+
+import androidx.compose.foundation.implementedInJetBrainsFork
+import kotlinx.coroutines.CancellationException
+
+internal actual class AnchoredDragFinishedSignal actual constructor() :
+ CancellationException("Anchored drag finished") {
+ init {
+ implementedInJetBrainsFork()
+ }
+}
+
+internal actual inline fun assertOnJvm(statement: Boolean, message: () -> String): Unit =
+ implementedInJetBrainsFork()
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/gestures/BringIntoViewSpec.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/gestures/BringIntoViewSpec.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/gestures/BringIntoViewSpec.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/gestures/BringIntoViewSpec.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/gestures/DesktopScrollable.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/gestures/DesktopScrollable.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/gestures/DesktopScrollable.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/gestures/DesktopScrollable.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/internal/JvmDefaultWithCompatibility.commonStubs.kt
similarity index 61%
copy from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt
copy to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/internal/JvmDefaultWithCompatibility.commonStubs.kt
index 18b1f9a..4b50cb8 100644
--- a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/internal/JvmDefaultWithCompatibility.commonStubs.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2022 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.
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-package androidx.compose.foundation.text
+package androidx.compose.foundation.internal
-import androidx.compose.foundation.implementedInJetBrainsFork
-
-internal actual fun String.findPrecedingBreak(index: Int): Int = implementedInJetBrainsFork()
-
-internal actual fun String.findFollowingBreak(index: Int): Int = implementedInJetBrainsFork()
+internal actual annotation class JvmDefaultWithCompatibility
diff --git a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.commonStubs.kt
similarity index 63%
copy from compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
copy to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.commonStubs.kt
index a5cf43e..503ce20 100644
--- a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.commonStubs.kt
@@ -14,14 +14,6 @@
* limitations under the License.
*/
-package androidx.compose.material.ripple
+package androidx.compose.foundation.internal
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun implementedInJetBrainsFork(): Nothing =
- throw NotImplementedError(
- """
- Implemented only in JetBrains fork.
- Please use `org.jetbrains.compose.material:material` package instead.
- """
- .trimIndent()
- )
+internal actual annotation class JvmSynchronized()
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/lazy/layout/Lazy.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/lazy/layout/Lazy.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/lazy/layout/Lazy.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/lazy/layout/Lazy.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.commonStubs.kt
similarity index 68%
copy from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt
copy to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.commonStubs.kt
index 18b1f9a..23a886a 100644
--- a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.commonStubs.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2024 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.
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package androidx.compose.foundation.text
+package androidx.compose.foundation.lazy.layout
import androidx.compose.foundation.implementedInJetBrainsFork
-internal actual fun String.findPrecedingBreak(index: Int): Int = implementedInJetBrainsFork()
-
-internal actual fun String.findFollowingBreak(index: Int): Int = implementedInJetBrainsFork()
+internal actual inline fun measureNanoTime(doMeasure: () -> Unit): Long =
+ implementedInJetBrainsFork()
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/lazy/layout/PrefetchScheduler.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/relocation/BringIntoViewResponder.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/ContextMenu.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/ContextMenu.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/ContextMenu.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/ContextMenu.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/DesktopCursorHandle.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/CursorHandle.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/DesktopCursorHandle.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/CursorHandle.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/DeadKeyCombiner.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/DeadKeyCombiner.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/DeadKeyCombiner.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/DeadKeyCombiner.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/KeyEventHelpers.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/KeyEventHelpers.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/KeyEventHelpers.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/KeyEventHelpers.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/KeyMapping.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/KeyMapping.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/KeyMapping.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/KeyMapping.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.commonStubs.kt
similarity index 88%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.commonStubs.kt
index 18b1f9a..dd19eab 100644
--- a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.commonStubs.kt
@@ -21,3 +21,6 @@
internal actual fun String.findPrecedingBreak(index: Int): Int = implementedInJetBrainsFork()
internal actual fun String.findFollowingBreak(index: Int): Int = implementedInJetBrainsFork()
+
+internal actual fun StringBuilder.appendCodePointX(codePoint: Int): StringBuilder =
+ implementedInJetBrainsFork()
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TextFieldFocusModifier.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TextFieldFocusModifier.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TextFieldFocusModifier.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TextFieldFocusModifier.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TextFieldKeyInput.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TextFieldKeyInput.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TextFieldKeyInput.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TextFieldKeyInput.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TextPointerIcon.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TextPointerIcon.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TextPointerIcon.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TextPointerIcon.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TouchMode.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TouchMode.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/TouchMode.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/TouchMode.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/UndoManager.commonStubs.kt
similarity index 78%
copy from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt
copy to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/UndoManager.commonStubs.kt
index 18b1f9a..8065392 100644
--- a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/UndoManager.commonStubs.kt
@@ -18,6 +18,4 @@
import androidx.compose.foundation.implementedInJetBrainsFork
-internal actual fun String.findPrecedingBreak(index: Int): Int = implementedInJetBrainsFork()
-
-internal actual fun String.findFollowingBreak(index: Int): Int = implementedInJetBrainsFork()
+internal actual fun timeNowMillis(): Long = implementedInJetBrainsFork()
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/handwriting/StylusHandwriting.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/handwriting/StylusHandwriting.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/handwriting/StylusHandwriting.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/handwriting/StylusHandwriting.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/CodepointHelpers.commonStubs.kt
similarity index 62%
copy from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt
copy to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/CodepointHelpers.commonStubs.kt
index 18b1f9a..ef0a75e 100644
--- a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/StringHelpers.jvmStubs.kt
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/CodepointHelpers.commonStubs.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package androidx.compose.foundation.text
+package androidx.compose.foundation.text.input.internal
import androidx.compose.foundation.implementedInJetBrainsFork
-internal actual fun String.findPrecedingBreak(index: Int): Int = implementedInJetBrainsFork()
+internal actual fun CharSequence.codePointAt(index: Int): Int = implementedInJetBrainsFork()
-internal actual fun String.findFollowingBreak(index: Int): Int = implementedInJetBrainsFork()
+internal actual fun charCount(codePoint: Int): Int = implementedInJetBrainsFork()
+
+internal actual fun CharSequence.codePointBefore(index: Int): Int = implementedInJetBrainsFork()
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyPlatformTextInputServiceAdapter.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldDragAndDropNode.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldKeyEventHandler.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextFieldLayoutStateCache.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/DesktopTextInputSession.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextInputSession.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/DesktopTextInputSession.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/TextInputSession.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/ToCharArray.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/ToCharArray.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/ToCharArray.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/ToCharArray.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/selection/DesktopTextFieldMagnifier.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifier.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/input/internal/selection/DesktopTextFieldMagnifier.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifier.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/selection/DesktopSelectionHandles.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/selection/SelectionHandles.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/selection/DesktopSelectionHandles.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/selection/SelectionHandles.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.commonStubs.kt
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.jvmStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.commonStubs.kt
similarity index 100%
rename from compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.jvmStubs.kt
rename to compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.commonStubs.kt
diff --git a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/MutatorMutex.jvm.kt
similarity index 60%
copy from compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
copy to compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/MutatorMutex.jvm.kt
index a5cf43e..d451c90 100644
--- a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
+++ b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/MutatorMutex.jvm.kt
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-package androidx.compose.material.ripple
+package androidx.compose.foundation
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun implementedInJetBrainsFork(): Nothing =
- throw NotImplementedError(
- """
- Implemented only in JetBrains fork.
- Please use `org.jetbrains.compose.material:material` package instead.
- """
- .trimIndent()
- )
+import kotlinx.coroutines.CancellationException
+
+internal actual class MutationInterruptedException actual constructor() :
+ CancellationException("Mutation interrupted") {
+ override fun fillInStackTrace(): Throwable {
+ // Avoid null.clone() on Android <= 6.0 when accessing stackTrace
+ stackTrace = emptyArray()
+ return this
+ }
+}
diff --git a/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.jvm.kt b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.jvm.kt
new file mode 100644
index 0000000..807aa9a
--- /dev/null
+++ b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.jvm.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2024 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.foundation.gestures
+
+import kotlinx.coroutines.CancellationException
+
+internal actual class AnchoredDragFinishedSignal actual constructor() :
+ CancellationException("Anchored drag finished") {
+ override fun fillInStackTrace(): Throwable {
+ stackTrace = emptyArray()
+ return this
+ }
+}
+
+@Suppress("NOTHING_TO_INLINE")
+internal actual inline fun assertOnJvm(statement: Boolean, message: () -> String): Unit {
+ assert(statement, message)
+}
diff --git a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.jvm.kt
similarity index 63%
copy from compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
copy to compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.jvm.kt
index a5cf43e..6037e0e 100644
--- a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
+++ b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/internal/JvmSynchronized.jvm.kt
@@ -14,14 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.material.ripple
+package androidx.compose.foundation.internal
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun implementedInJetBrainsFork(): Nothing =
- throw NotImplementedError(
- """
- Implemented only in JetBrains fork.
- Please use `org.jetbrains.compose.material:material` package instead.
- """
- .trimIndent()
- )
+@Suppress("ACTUAL_WITHOUT_EXPECT") // https://youtrack.jetbrains.com/issue/KT-37316
+internal actual typealias JvmSynchronized = kotlin.jvm.Synchronized
diff --git a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.jvm.kt
similarity index 63%
copy from compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
copy to compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.jvm.kt
index a5cf43e..9ed6841 100644
--- a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
+++ b/compose/foundation/foundation/src/jvmMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutPrefetchState.jvm.kt
@@ -14,14 +14,8 @@
* limitations under the License.
*/
-package androidx.compose.material.ripple
+package androidx.compose.foundation.lazy.layout
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun implementedInJetBrainsFork(): Nothing =
- throw NotImplementedError(
- """
- Implemented only in JetBrains fork.
- Please use `org.jetbrains.compose.material:material` package instead.
- """
- .trimIndent()
- )
+internal actual inline fun measureNanoTime(doMeasure: () -> Unit): Long {
+ return kotlin.system.measureNanoTime(doMeasure)
+}
diff --git a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/MediaType.jvmStubs.kt b/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/MediaType.jvmStubs.kt
deleted file mode 100644
index 9bb0b64..0000000
--- a/compose/foundation/foundation/src/jvmStubsMain/kotlin/androidx/compose/foundation/content/MediaType.jvmStubs.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.foundation.content
-
-import java.awt.datatransfer.DataFlavor
-
-actual class MediaType internal constructor(val dataFlavor: DataFlavor) {
-
- actual constructor(representation: String) : this(DataFlavor(representation))
-
- actual val representation: String = dataFlavor.mimeType
-
- actual companion object {
- actual val Text: MediaType = MediaType(DataFlavor.stringFlavor)
-
- actual val PlainText: MediaType = MediaType(DataFlavor.getTextPlainUnicodeFlavor())
-
- actual val HtmlText: MediaType = MediaType(DataFlavor.allHtmlFlavor)
-
- actual val Image: MediaType = MediaType(DataFlavor.imageFlavor)
-
- actual val All: MediaType = MediaType(DataFlavor("*/*"))
- }
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (other !is MediaType) return false
-
- return dataFlavor == other.dataFlavor
- }
-
- override fun hashCode(): Int {
- return dataFlavor.hashCode()
- }
-
- override fun toString(): String {
- return "MediaType(" + "dataFlavor=$dataFlavor, " + "representation='$representation')"
- }
-}
diff --git a/compose/material/material-ripple/build.gradle b/compose/material/material-ripple/build.gradle
index 7189ea2..77af4eb 100644
--- a/compose/material/material-ripple/build.gradle
+++ b/compose/material/material-ripple/build.gradle
@@ -33,19 +33,20 @@
androidXMultiplatform {
android()
jvmStubs()
+ linuxX64Stubs()
defaultPlatform(PlatformIdentifier.ANDROID)
sourceSets {
commonMain {
dependencies {
- implementation(libs.kotlinStdlibCommon)
- api("androidx.compose.foundation:foundation:1.6.0")
+ implementation(libs.kotlinStdlib)
+ api(project(":compose:foundation:foundation"))
api(project(":compose:runtime:runtime"))
- implementation("androidx.collection:collection:1.4.0")
- implementation("androidx.compose.animation:animation:1.6.0")
- implementation("androidx.compose.ui:ui-util:1.6.0")
+ implementation(project(":collection:collection"))
+ implementation(project(":compose:animation:animation"))
+ implementation(project(":compose:ui:ui-util"))
}
}
@@ -66,11 +67,16 @@
}
}
+ commonStubsMain {
+ dependsOn(commonMain)
+ }
+
jvmStubsMain {
- dependsOn(jvmMain)
- dependencies {
- implementation(libs.kotlinStdlib)
- }
+ dependsOn(commonStubsMain)
+ }
+
+ linuxx64StubsMain {
+ dependsOn(commonStubsMain)
}
androidInstrumentedTest {
diff --git a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt b/compose/material/material-ripple/src/commonStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.commonStubs.kt
similarity index 90%
rename from compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
rename to compose/material/material-ripple/src/commonStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.commonStubs.kt
index a5cf43e..75e04663 100644
--- a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
+++ b/compose/material/material-ripple/src/commonStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.commonStubs.kt
@@ -21,7 +21,7 @@
throw NotImplementedError(
"""
Implemented only in JetBrains fork.
- Please use `org.jetbrains.compose.material:material` package instead.
+ Please use `org.jetbrains.compose.material:material-ripple` package instead.
"""
.trimIndent()
)
diff --git a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/Ripple.jvmStubs.kt b/compose/material/material-ripple/src/commonStubsMain/kotlin/androidx/compose/material/ripple/Ripple.commonStubs.kt
similarity index 100%
rename from compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/Ripple.jvmStubs.kt
rename to compose/material/material-ripple/src/commonStubsMain/kotlin/androidx/compose/material/ripple/Ripple.commonStubs.kt
diff --git a/compose/material/material/build.gradle b/compose/material/material/build.gradle
index a550d824..2a7e5d6 100644
--- a/compose/material/material/build.gradle
+++ b/compose/material/material/build.gradle
@@ -33,25 +33,25 @@
androidXMultiplatform {
android()
jvmStubs()
+ linuxX64Stubs()
defaultPlatform(PlatformIdentifier.ANDROID)
sourceSets {
commonMain {
dependencies {
- implementation(libs.kotlinStdlibCommon)
- api("androidx.compose.animation:animation-core:1.6.0")
+ implementation(libs.kotlinStdlib)
+ api(project(":compose:animation:animation-core"))
api(project(":compose:foundation:foundation"))
api(project(":compose:ui:ui-text"))
- api("androidx.compose.material:material-icons-core:1.6.7")
api(project(":compose:material:material-ripple"))
api(project(":compose:runtime:runtime"))
- api("androidx.compose.ui:ui:1.6.0")
+ api(project(":compose:ui:ui"))
implementation(project(":compose:animation:animation-core"))
- implementation("androidx.compose.animation:animation:1.6.0")
- implementation("androidx.compose.foundation:foundation-layout:1.6.0")
- implementation("androidx.compose.ui:ui-util:1.6.0")
+ implementation(project(":compose:animation:animation"))
+ implementation(project(":compose:foundation:foundation-layout"))
+ implementation(project(":compose:ui:ui-util"))
}
}
@@ -69,6 +69,8 @@
androidMain {
dependsOn(jvmMain)
dependencies {
+ api("androidx.compose.material:material-icons-core:1.6.7")
+
api("androidx.annotation:annotation:1.1.0")
api("androidx.annotation:annotation-experimental:1.4.1")
@@ -79,11 +81,16 @@
}
}
+ commonStubsMain {
+ dependsOn(commonMain)
+ }
+
jvmStubsMain {
- dependsOn(jvmMain)
- dependencies {
- implementation(libs.kotlinStdlib)
- }
+ dependsOn(commonStubsMain)
+ }
+
+ linuxx64StubsMain {
+ dependsOn(commonStubsMain)
}
androidInstrumentedTest {
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt
index 1764b73..e444fbf 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt
@@ -713,12 +713,7 @@
val AnimationSpec = SpringSpec<Float>()
}
-private class AnchoredDragFinishedSignal : CancellationException() {
- override fun fillInStackTrace(): Throwable {
- stackTrace = emptyArray()
- return this
- }
-}
+internal expect class AnchoredDragFinishedSignal() : CancellationException
private suspend fun <I> restartable(inputs: () -> I, block: suspend (I) -> Unit) {
try {
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt
index ae9557c..8e9bc84 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BackdropScaffold.kt
@@ -66,6 +66,7 @@
import androidx.compose.ui.util.fastCoerceIn
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastMap
+import kotlin.jvm.JvmName
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
index f9134e1..76fa17e6 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
@@ -53,6 +53,7 @@
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastMap
import androidx.compose.ui.util.fastMaxBy
+import kotlin.jvm.JvmName
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Chip.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Chip.kt
index f92ff6f..0fa7967 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Chip.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Chip.kt
@@ -32,7 +32,6 @@
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CornerSize
-import androidx.compose.material.icons.Icons
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable
@@ -174,8 +173,8 @@
* color for this chip in different states. See [ChipDefaults.filterChipColors].
* @param leadingIcon Optional icon at the start of the chip, preceding the content text.
* @param selectedIcon Icon used to indicate a chip's selected state, it is commonly a
- * [Icons.Filled.Done]. By default, if a leading icon is also provided, the leading icon will be
- * obscured by a circle overlay and then the selected icon.
+ * [androidx.compose.material.icons.Icons.Filled.Done]. By default, if a leading icon is also
+ * provided, the leading icon will be obscured by a circle overlay and then the selected icon.
* @param trailingIcon Optional icon at the end of the chip, following the content text. Filter
* chips commonly do not display any trailing icon.
* @param content the content of this chip
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
index ffb693f..97b3d16 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
@@ -66,6 +66,7 @@
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastCoerceIn
+import kotlin.jvm.JvmName
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/InteractiveComponentSize.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/InteractiveComponentSize.kt
index d2497c9..3a13c018 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/InteractiveComponentSize.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/InteractiveComponentSize.kt
@@ -64,11 +64,13 @@
"interactions if the element would measure smaller"
}
- override fun hashCode(): Int = System.identityHashCode(this)
+ override fun hashCode(): Int = identityHashCode(this)
override fun equals(other: Any?) = (other === this)
}
+internal expect inline fun identityHashCode(value: Any): Int
+
internal class MinimumInteractiveModifierNode :
Modifier.Node(), CompositionLocalConsumerModifierNode, LayoutModifierNode {
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
index e421ecf..64341fe 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
@@ -63,6 +63,7 @@
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
+import kotlin.jvm.JvmName
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
index 3db57d2..b7b30dc 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt
@@ -43,6 +43,7 @@
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastMap
import androidx.compose.ui.util.fastMaxBy
+import kotlin.jvm.JvmInline
/**
* State for [Scaffold] composable component.
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeToDismiss.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeToDismiss.kt
index 3b8c5a1..c038442 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeToDismiss.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SwipeToDismiss.kt
@@ -153,7 +153,7 @@
*/
@Composable
@ExperimentalMaterialApi
-@SuppressWarnings("ReferencesDeprecated")
+@Suppress("ReferencesDeprecated")
fun SwipeToDismiss(
state: DismissState,
modifier: Modifier = Modifier,
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/AlertDialog.jvmStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/AlertDialog.jvmStubs.kt
similarity index 100%
rename from compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/AlertDialog.jvmStubs.kt
rename to compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/AlertDialog.jvmStubs.kt
diff --git a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/AnchoredDraggable.commonStubs.kt
similarity index 63%
copy from compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
copy to compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/AnchoredDraggable.commonStubs.kt
index a5cf43e..f96d54d 100644
--- a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
+++ b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/AnchoredDraggable.commonStubs.kt
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-package androidx.compose.material.ripple
+package androidx.compose.material
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun implementedInJetBrainsFork(): Nothing =
- throw NotImplementedError(
- """
- Implemented only in JetBrains fork.
- Please use `org.jetbrains.compose.material:material` package instead.
- """
- .trimIndent()
- )
+import kotlinx.coroutines.CancellationException
+
+internal actual class AnchoredDragFinishedSignal : CancellationException("Anchored drag finished") {
+ init {
+ implementedInJetBrainsFork()
+ }
+}
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/DefaultPlatformTextStyle.jvmStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/DefaultPlatformTextStyle.jvmStubs.kt
similarity index 100%
rename from compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/DefaultPlatformTextStyle.jvmStubs.kt
rename to compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/DefaultPlatformTextStyle.jvmStubs.kt
diff --git a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/InteractiveComponentSize.commonStubs.kt
similarity index 67%
copy from compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
copy to compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/InteractiveComponentSize.commonStubs.kt
index a5cf43e..360c617 100644
--- a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
+++ b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/InteractiveComponentSize.commonStubs.kt
@@ -14,14 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.material.ripple
+package androidx.compose.material
@Suppress("NOTHING_TO_INLINE")
-internal inline fun implementedInJetBrainsFork(): Nothing =
- throw NotImplementedError(
- """
- Implemented only in JetBrains fork.
- Please use `org.jetbrains.compose.material:material` package instead.
- """
- .trimIndent()
- )
+internal actual inline fun identityHashCode(value: Any): Int = implementedInJetBrainsFork()
diff --git a/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/InternalMutatorMutex.commonStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/InternalMutatorMutex.commonStubs.kt
new file mode 100644
index 0000000..9d385de
--- /dev/null
+++ b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/InternalMutatorMutex.commonStubs.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2024 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.material
+
+internal actual class InternalAtomicReference<V> actual constructor(value: V) {
+ actual fun get(): V = implementedInJetBrainsFork()
+
+ actual fun set(value: V) {
+ implementedInJetBrainsFork()
+ }
+
+ actual fun getAndSet(value: V): V = implementedInJetBrainsFork()
+
+ actual fun compareAndSet(expect: V, newValue: V): Boolean = implementedInJetBrainsFork()
+}
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/MaterialTheme.jvmStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/MaterialTheme.jvmStubs.kt
similarity index 100%
rename from compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/MaterialTheme.jvmStubs.kt
rename to compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/MaterialTheme.jvmStubs.kt
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/Menu.jvmStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/Menu.commonStubs.kt
similarity index 70%
rename from compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/Menu.jvmStubs.kt
rename to compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/Menu.commonStubs.kt
index 4164d13..191389b 100644
--- a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/Menu.jvmStubs.kt
+++ b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/Menu.commonStubs.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2024 The Android Open Source Project
+ * Copyright 2020 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.
@@ -17,13 +17,26 @@
package androidx.compose.material
import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.RowScope
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.window.PopupProperties
@Composable
+actual fun DropdownMenuItem(
+ onClick: () -> Unit,
+ modifier: Modifier,
+ enabled: Boolean,
+ contentPadding: PaddingValues,
+ interactionSource: MutableInteractionSource?,
+ content: @Composable RowScope.() -> Unit
+): Unit = implementedInJetBrainsFork()
+
+@Composable
actual fun DropdownMenu(
expanded: Boolean,
onDismissRequest: () -> Unit,
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/NotImplemented.jvmStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/NotImplemented.jvmStubs.kt
similarity index 100%
rename from compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/NotImplemented.jvmStubs.kt
rename to compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/NotImplemented.jvmStubs.kt
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/Strings.jvmStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/Strings.jvmStubs.kt
similarity index 100%
rename from compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/Strings.jvmStubs.kt
rename to compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/Strings.jvmStubs.kt
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/SystemBarsDefaultInsets.jvmStubs.kt b/compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/SystemBarsDefaultInsets.jvmStubs.kt
similarity index 100%
rename from compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/SystemBarsDefaultInsets.jvmStubs.kt
rename to compose/material/material/src/commonStubsMain/kotlin/androidx/compose/material/SystemBarsDefaultInsets.jvmStubs.kt
diff --git a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt b/compose/material/material/src/jvmMain/kotlin/androidx/compose/material/AnchoredDraggable.jvm.kt
similarity index 63%
copy from compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
copy to compose/material/material/src/jvmMain/kotlin/androidx/compose/material/AnchoredDraggable.jvm.kt
index a5cf43e..7f07ef3 100644
--- a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
+++ b/compose/material/material/src/jvmMain/kotlin/androidx/compose/material/AnchoredDraggable.jvm.kt
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-package androidx.compose.material.ripple
+package androidx.compose.material
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun implementedInJetBrainsFork(): Nothing =
- throw NotImplementedError(
- """
- Implemented only in JetBrains fork.
- Please use `org.jetbrains.compose.material:material` package instead.
- """
- .trimIndent()
- )
+import kotlinx.coroutines.CancellationException
+
+internal actual class AnchoredDragFinishedSignal : CancellationException("Anchored drag finished") {
+ override fun fillInStackTrace(): Throwable {
+ stackTrace = emptyArray()
+ return this
+ }
+}
diff --git a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt b/compose/material/material/src/jvmMain/kotlin/androidx/compose/material/InteractiveComponentSize.jvm.kt
similarity index 67%
copy from compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
copy to compose/material/material/src/jvmMain/kotlin/androidx/compose/material/InteractiveComponentSize.jvm.kt
index a5cf43e..7597077 100644
--- a/compose/material/material-ripple/src/jvmStubsMain/kotlin/androidx/compose/material/ripple/NotImplemented.jvmStubs.kt
+++ b/compose/material/material/src/jvmMain/kotlin/androidx/compose/material/InteractiveComponentSize.jvm.kt
@@ -14,14 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.material.ripple
+package androidx.compose.material
@Suppress("NOTHING_TO_INLINE")
-internal inline fun implementedInJetBrainsFork(): Nothing =
- throw NotImplementedError(
- """
- Implemented only in JetBrains fork.
- Please use `org.jetbrains.compose.material:material` package instead.
- """
- .trimIndent()
- )
+internal actual inline fun identityHashCode(value: Any): Int = System.identityHashCode(value)
diff --git a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/DesktopMenu.jvmStubs.kt b/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/DesktopMenu.jvmStubs.kt
deleted file mode 100644
index 2e0c188..0000000
--- a/compose/material/material/src/jvmStubsMain/kotlin/androidx/compose/material/DesktopMenu.jvmStubs.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2020 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.material
-
-import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.RowScope
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-
-@Composable
-actual fun DropdownMenuItem(
- onClick: () -> Unit,
- modifier: Modifier,
- enabled: Boolean,
- contentPadding: PaddingValues,
- interactionSource: MutableInteractionSource?,
- content: @Composable RowScope.() -> Unit
-): Unit = implementedInJetBrainsFork()
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index 74b1bf2..8be8424 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -782,13 +782,17 @@
method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getExtendedFabShape();
method public float getLargeIconSize();
method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getLargeShape();
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public float getMediumIconSize();
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getMediumShape();
method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getSmallShape();
method @androidx.compose.runtime.Composable public androidx.compose.material3.FloatingActionButtonElevation loweredElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation);
property public final float LargeIconSize;
+ property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final float MediumIconSize;
property @androidx.compose.runtime.Composable public final long containerColor;
property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape extendedFabShape;
property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape largeShape;
+ property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape mediumShape;
property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape smallShape;
field public static final androidx.compose.material3.FloatingActionButtonDefaults INSTANCE;
@@ -802,6 +806,7 @@
method @androidx.compose.runtime.Composable public static void ExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean expanded, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
method @androidx.compose.runtime.Composable public static void FloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void LargeFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void MediumFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void SmallFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
@@ -1382,6 +1387,7 @@
method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors colors();
method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors colors(optional long focusedTextColor, optional long unfocusedTextColor, optional long disabledTextColor, optional long errorTextColor, optional long focusedContainerColor, optional long unfocusedContainerColor, optional long disabledContainerColor, optional long errorContainerColor, optional long cursorColor, optional long errorCursorColor, optional androidx.compose.foundation.text.selection.TextSelectionColors? selectionColors, optional long focusedBorderColor, optional long unfocusedBorderColor, optional long disabledBorderColor, optional long errorBorderColor, optional long focusedLeadingIconColor, optional long unfocusedLeadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long focusedTrailingIconColor, optional long unfocusedTrailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long focusedPlaceholderColor, optional long unfocusedPlaceholderColor, optional long disabledPlaceholderColor, optional long errorPlaceholderColor, optional long focusedSupportingTextColor, optional long unfocusedSupportingTextColor, optional long disabledSupportingTextColor, optional long errorSupportingTextColor, optional long focusedPrefixColor, optional long unfocusedPrefixColor, optional long disabledPrefixColor, optional long errorPrefixColor, optional long focusedSuffixColor, optional long unfocusedSuffixColor, optional long disabledSuffixColor, optional long errorSuffixColor);
method public androidx.compose.foundation.layout.PaddingValues contentPadding(optional float start, optional float top, optional float end, optional float bottom);
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.foundation.text.input.TextFieldDecorator decorator(androidx.compose.foundation.text.input.TextFieldState state, boolean enabled, androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> container);
method public float getFocusedBorderThickness();
method public float getMinHeight();
method public float getMinWidth();
@@ -1396,6 +1402,7 @@
}
public final class OutlinedTextFieldKt {
+ method @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super kotlin.jvm.functions.Function0<androidx.compose.ui.text.TextLayoutResult?>,kotlin.Unit>? onTextLayout, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
method @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
method @androidx.compose.runtime.Composable public static void OutlinedTextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
}
@@ -2177,6 +2184,7 @@
method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors colors(optional long focusedTextColor, optional long unfocusedTextColor, optional long disabledTextColor, optional long errorTextColor, optional long focusedContainerColor, optional long unfocusedContainerColor, optional long disabledContainerColor, optional long errorContainerColor, optional long cursorColor, optional long errorCursorColor, optional androidx.compose.foundation.text.selection.TextSelectionColors? selectionColors, optional long focusedIndicatorColor, optional long unfocusedIndicatorColor, optional long disabledIndicatorColor, optional long errorIndicatorColor, optional long focusedLeadingIconColor, optional long unfocusedLeadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long focusedTrailingIconColor, optional long unfocusedTrailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long focusedPlaceholderColor, optional long unfocusedPlaceholderColor, optional long disabledPlaceholderColor, optional long errorPlaceholderColor, optional long focusedSupportingTextColor, optional long unfocusedSupportingTextColor, optional long disabledSupportingTextColor, optional long errorSupportingTextColor, optional long focusedPrefixColor, optional long unfocusedPrefixColor, optional long disabledPrefixColor, optional long errorPrefixColor, optional long focusedSuffixColor, optional long unfocusedSuffixColor, optional long disabledSuffixColor, optional long errorSuffixColor);
method public androidx.compose.foundation.layout.PaddingValues contentPaddingWithLabel(optional float start, optional float end, optional float top, optional float bottom);
method public androidx.compose.foundation.layout.PaddingValues contentPaddingWithoutLabel(optional float start, optional float top, optional float end, optional float bottom);
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.foundation.text.input.TextFieldDecorator decorator(androidx.compose.foundation.text.input.TextFieldState state, boolean enabled, androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> container);
method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFilledShape();
method @Deprecated public float getFocusedBorderThickness();
method public float getFocusedIndicatorThickness();
@@ -2203,6 +2211,7 @@
}
public final class TextFieldKt {
+ method @androidx.compose.runtime.Composable public static void TextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super kotlin.jvm.functions.Function0<androidx.compose.ui.text.TextLayoutResult?>,kotlin.Unit>? onTextLayout, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
method @androidx.compose.runtime.Composable public static void TextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
method @androidx.compose.runtime.Composable public static void TextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
}
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index 74b1bf2..8be8424 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -782,13 +782,17 @@
method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getExtendedFabShape();
method public float getLargeIconSize();
method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getLargeShape();
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public float getMediumIconSize();
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getMediumShape();
method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getSmallShape();
method @androidx.compose.runtime.Composable public androidx.compose.material3.FloatingActionButtonElevation loweredElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation);
property public final float LargeIconSize;
+ property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final float MediumIconSize;
property @androidx.compose.runtime.Composable public final long containerColor;
property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape extendedFabShape;
property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape largeShape;
+ property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape mediumShape;
property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape smallShape;
field public static final androidx.compose.material3.FloatingActionButtonDefaults INSTANCE;
@@ -802,6 +806,7 @@
method @androidx.compose.runtime.Composable public static void ExtendedFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean expanded, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
method @androidx.compose.runtime.Composable public static void FloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void LargeFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void MediumFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void SmallFloatingActionButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long containerColor, optional long contentColor, optional androidx.compose.material3.FloatingActionButtonElevation elevation, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
}
@@ -1382,6 +1387,7 @@
method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors colors();
method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors colors(optional long focusedTextColor, optional long unfocusedTextColor, optional long disabledTextColor, optional long errorTextColor, optional long focusedContainerColor, optional long unfocusedContainerColor, optional long disabledContainerColor, optional long errorContainerColor, optional long cursorColor, optional long errorCursorColor, optional androidx.compose.foundation.text.selection.TextSelectionColors? selectionColors, optional long focusedBorderColor, optional long unfocusedBorderColor, optional long disabledBorderColor, optional long errorBorderColor, optional long focusedLeadingIconColor, optional long unfocusedLeadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long focusedTrailingIconColor, optional long unfocusedTrailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long focusedPlaceholderColor, optional long unfocusedPlaceholderColor, optional long disabledPlaceholderColor, optional long errorPlaceholderColor, optional long focusedSupportingTextColor, optional long unfocusedSupportingTextColor, optional long disabledSupportingTextColor, optional long errorSupportingTextColor, optional long focusedPrefixColor, optional long unfocusedPrefixColor, optional long disabledPrefixColor, optional long errorPrefixColor, optional long focusedSuffixColor, optional long unfocusedSuffixColor, optional long disabledSuffixColor, optional long errorSuffixColor);
method public androidx.compose.foundation.layout.PaddingValues contentPadding(optional float start, optional float top, optional float end, optional float bottom);
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.foundation.text.input.TextFieldDecorator decorator(androidx.compose.foundation.text.input.TextFieldState state, boolean enabled, androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> container);
method public float getFocusedBorderThickness();
method public float getMinHeight();
method public float getMinWidth();
@@ -1396,6 +1402,7 @@
}
public final class OutlinedTextFieldKt {
+ method @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super kotlin.jvm.functions.Function0<androidx.compose.ui.text.TextLayoutResult?>,kotlin.Unit>? onTextLayout, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
method @androidx.compose.runtime.Composable public static void OutlinedTextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
method @androidx.compose.runtime.Composable public static void OutlinedTextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
}
@@ -2177,6 +2184,7 @@
method @androidx.compose.runtime.Composable public androidx.compose.material3.TextFieldColors colors(optional long focusedTextColor, optional long unfocusedTextColor, optional long disabledTextColor, optional long errorTextColor, optional long focusedContainerColor, optional long unfocusedContainerColor, optional long disabledContainerColor, optional long errorContainerColor, optional long cursorColor, optional long errorCursorColor, optional androidx.compose.foundation.text.selection.TextSelectionColors? selectionColors, optional long focusedIndicatorColor, optional long unfocusedIndicatorColor, optional long disabledIndicatorColor, optional long errorIndicatorColor, optional long focusedLeadingIconColor, optional long unfocusedLeadingIconColor, optional long disabledLeadingIconColor, optional long errorLeadingIconColor, optional long focusedTrailingIconColor, optional long unfocusedTrailingIconColor, optional long disabledTrailingIconColor, optional long errorTrailingIconColor, optional long focusedLabelColor, optional long unfocusedLabelColor, optional long disabledLabelColor, optional long errorLabelColor, optional long focusedPlaceholderColor, optional long unfocusedPlaceholderColor, optional long disabledPlaceholderColor, optional long errorPlaceholderColor, optional long focusedSupportingTextColor, optional long unfocusedSupportingTextColor, optional long disabledSupportingTextColor, optional long errorSupportingTextColor, optional long focusedPrefixColor, optional long unfocusedPrefixColor, optional long disabledPrefixColor, optional long errorPrefixColor, optional long focusedSuffixColor, optional long unfocusedSuffixColor, optional long disabledSuffixColor, optional long errorSuffixColor);
method public androidx.compose.foundation.layout.PaddingValues contentPaddingWithLabel(optional float start, optional float end, optional float top, optional float bottom);
method public androidx.compose.foundation.layout.PaddingValues contentPaddingWithoutLabel(optional float start, optional float top, optional float end, optional float bottom);
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public androidx.compose.foundation.text.input.TextFieldDecorator decorator(androidx.compose.foundation.text.input.TextFieldState state, boolean enabled, androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, androidx.compose.foundation.interaction.InteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function0<kotlin.Unit> container);
method @Deprecated @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getFilledShape();
method @Deprecated public float getFocusedBorderThickness();
method public float getFocusedIndicatorThickness();
@@ -2203,6 +2211,7 @@
}
public final class TextFieldKt {
+ method @androidx.compose.runtime.Composable public static void TextField(androidx.compose.foundation.text.input.TextFieldState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional androidx.compose.foundation.text.input.OutputTransformation? outputTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.foundation.text.input.TextFieldLineLimits lineLimits, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super kotlin.jvm.functions.Function0<androidx.compose.ui.text.TextLayoutResult?>,kotlin.Unit>? onTextLayout, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
method @androidx.compose.runtime.Composable public static void TextField(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
method @androidx.compose.runtime.Composable public static void TextField(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? prefix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? suffix, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingText, optional boolean isError, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.TextFieldColors colors);
}
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
index 8f4e168..b356f1a 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
@@ -53,6 +53,7 @@
import androidx.compose.material3.samples.DatePickerSample
import androidx.compose.material3.samples.DatePickerWithDateSelectableDatesSample
import androidx.compose.material3.samples.DateRangePickerSample
+import androidx.compose.material3.samples.DenseTextFieldContentPadding
import androidx.compose.material3.samples.DeterminateContainedLoadingIndicatorSample
import androidx.compose.material3.samples.DeterminateLoadingIndicatorSample
import androidx.compose.material3.samples.DismissibleNavigationDrawerSample
@@ -107,6 +108,7 @@
import androidx.compose.material3.samples.LinearWavyProgressIndicatorSample
import androidx.compose.material3.samples.LoadingIndicatorPullToRefreshSample
import androidx.compose.material3.samples.LoadingIndicatorSample
+import androidx.compose.material3.samples.MediumFloatingActionButtonSample
import androidx.compose.material3.samples.MenuSample
import androidx.compose.material3.samples.MenuWithScrollStateSample
import androidx.compose.material3.samples.ModalBottomSheetSample
@@ -123,7 +125,7 @@
import androidx.compose.material3.samples.OutlinedIconButtonSample
import androidx.compose.material3.samples.OutlinedIconToggleButtonSample
import androidx.compose.material3.samples.OutlinedSplitButtonSample
-import androidx.compose.material3.samples.OutlinedTextFieldSample
+import androidx.compose.material3.samples.OutlinedTextFieldWithInitialValueAndSelection
import androidx.compose.material3.samples.PasswordTextField
import androidx.compose.material3.samples.PermanentNavigationDrawerSample
import androidx.compose.material3.samples.PinnedTopAppBar
@@ -185,13 +187,14 @@
import androidx.compose.material3.samples.TextAndIconTabs
import androidx.compose.material3.samples.TextArea
import androidx.compose.material3.samples.TextButtonSample
-import androidx.compose.material3.samples.TextFieldSample
import androidx.compose.material3.samples.TextFieldWithErrorState
import androidx.compose.material3.samples.TextFieldWithHideKeyboardOnImeAction
import androidx.compose.material3.samples.TextFieldWithIcons
+import androidx.compose.material3.samples.TextFieldWithInitialValueAndSelection
import androidx.compose.material3.samples.TextFieldWithPlaceholder
import androidx.compose.material3.samples.TextFieldWithPrefixAndSuffix
import androidx.compose.material3.samples.TextFieldWithSupportingText
+import androidx.compose.material3.samples.TextFieldWithTransformations
import androidx.compose.material3.samples.ThreeLineListItemWithExtendedSupporting
import androidx.compose.material3.samples.ThreeLineListItemWithOverlineAndSupporting
import androidx.compose.material3.samples.TimeInputSample
@@ -780,6 +783,13 @@
LargeFloatingActionButtonSample()
},
Example(
+ name = "MediumFloatingActionButtonSample",
+ description = FloatingActionButtonsExampleDescription,
+ sourceUrl = FloatingActionButtonsExampleSourceUrl,
+ ) {
+ MediumFloatingActionButtonSample()
+ },
+ Example(
name = "SmallFloatingActionButtonSample",
description = FloatingActionButtonsExampleDescription,
sourceUrl = FloatingActionButtonsExampleSourceUrl,
@@ -1554,11 +1564,11 @@
SimpleTextFieldSample()
},
Example(
- name = "TextFieldSample",
+ name = "TextFieldWithInitialValueAndSelection",
description = TextFieldsExampleDescription,
sourceUrl = TextFieldsExampleSourceUrl
) {
- TextFieldSample()
+ TextFieldWithInitialValueAndSelection()
},
Example(
name = "SimpleOutlinedTextFieldSample",
@@ -1568,11 +1578,18 @@
SimpleOutlinedTextFieldSample()
},
Example(
- name = "OutlinedTextFieldSample",
+ name = "OutlinedTextFieldWithInitialValueAndSelection",
description = TextFieldsExampleDescription,
sourceUrl = TextFieldsExampleSourceUrl
) {
- OutlinedTextFieldSample()
+ OutlinedTextFieldWithInitialValueAndSelection()
+ },
+ Example(
+ name = "TextFieldWithTransformations",
+ description = TextFieldsExampleDescription,
+ sourceUrl = TextFieldsExampleSourceUrl
+ ) {
+ TextFieldWithTransformations()
},
Example(
name = "TextFieldWithIcons",
@@ -1610,6 +1627,13 @@
TextFieldWithSupportingText()
},
Example(
+ name = "DenseTextFieldContentPadding",
+ description = TextFieldsExampleDescription,
+ sourceUrl = TextFieldsExampleSourceUrl
+ ) {
+ DenseTextFieldContentPadding()
+ },
+ Example(
name = "PasswordTextField",
description = TextFieldsExampleDescription,
sourceUrl = TextFieldsExampleSourceUrl
@@ -1633,10 +1657,9 @@
)
.map {
// By default text field samples are minimal and don't have a `width` modifier to
- // restrict the
- // width. As a result, they grow horizontally if enough text is typed. To prevent this
- // behavior
- // in Catalog app the code below restricts the width of every text field sample
+ // restrict the width. As a result, they grow horizontally if enough text is typed. To
+ // prevent this behavior in Catalog app, the code below restricts the width of every
+ // text field sample
it.copy(content = { Box(Modifier.wrapContentWidth().width(280.dp)) { it.content() } })
}
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Themes.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Themes.kt
index d497651..4d2970d 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Themes.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Themes.kt
@@ -16,9 +16,13 @@
package androidx.compose.material3.catalog.library.model
+import androidx.compose.material3.MaterialExpressiveTheme
+import androidx.compose.material3.MaterialTheme
+
data class Theme(
- val themeMode: ThemeMode = ThemeMode.System,
+ val themeColorMode: ThemeColorMode = ThemeColorMode.System,
val colorMode: ColorMode = ColorMode.Baseline,
+ val expressiveThemeMode: ExpressiveThemeMode = ExpressiveThemeMode.NonExpressive,
val fontScale: Float = 1.0f,
val fontScaleMode: FontScaleMode = FontScaleMode.System,
val textDirection: TextDirection = TextDirection.System,
@@ -26,8 +30,10 @@
constructor(
map: Map<String, Float>
) : this(
- themeMode = ThemeMode.values()[map.getValue(ThemeModeKey).toInt()],
+ themeColorMode = ThemeColorMode.values()[map.getValue(ThemeModeKey).toInt()],
colorMode = ColorMode.values()[map.getValue(ColorModeKey).toInt()],
+ expressiveThemeMode =
+ ExpressiveThemeMode.values()[map.getValue(ExpressiveThemeModeKey).toInt()],
fontScale = map.getValue(FontScaleKey).toFloat(),
fontScaleMode = FontScaleMode.values()[map.getValue(FontScaleModeKey).toInt()],
textDirection = TextDirection.values()[map.getValue(TextDirectionKey).toInt()],
@@ -35,8 +41,9 @@
fun toMap() =
mapOf(
- ThemeModeKey to themeMode.ordinal.toFloat(),
+ ThemeModeKey to themeColorMode.ordinal.toFloat(),
ColorModeKey to colorMode.ordinal.toFloat(),
+ ExpressiveThemeModeKey to expressiveThemeMode.ordinal.toFloat(),
FontScaleKey to fontScale,
FontScaleModeKey to fontScaleMode.ordinal.toFloat(),
TextDirectionKey to textDirection.ordinal.toFloat(),
@@ -95,17 +102,32 @@
override fun toString(): String = label
}
-enum class ThemeMode {
+/**
+ * Determines whether the current [ColorMode] should be in light theme, dark theme, or determined by
+ * the system.
+ */
+enum class ThemeColorMode {
System,
Light,
Dark,
}
+/**
+ * A class for identifying legacy and expressive Material3 themes.
+ *
+ * See [MaterialTheme] and [MaterialExpressiveTheme] for more information.
+ */
+enum class ExpressiveThemeMode {
+ Expressive,
+ NonExpressive,
+}
+
const val MinFontScale = 0.4f
const val MaxFontScale = 2f
private const val ThemeModeKey = "themeMode"
private const val ColorModeKey = "colorMode"
+private const val ExpressiveThemeModeKey = "expressiveThemeMode"
private const val FontScaleKey = "fontScale"
private const val FontScaleModeKey = "fontScaleMode"
private const val TextDirectionKey = "textDirection"
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/Theme.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/Theme.kt
index 4aec483..e1c92f3 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/Theme.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/Theme.kt
@@ -22,15 +22,19 @@
import android.content.ContextWrapper
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.MaterialExpressiveTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.catalog.library.model.ColorMode
+import androidx.compose.material3.catalog.library.model.ExpressiveThemeMode
import androidx.compose.material3.catalog.library.model.FontScaleMode
import androidx.compose.material3.catalog.library.model.TextDirection
import androidx.compose.material3.catalog.library.model.Theme
-import androidx.compose.material3.catalog.library.model.ThemeMode
+import androidx.compose.material3.catalog.library.model.ThemeColorMode
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.expressiveLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
@@ -45,6 +49,7 @@
import androidx.core.view.WindowCompat
@SuppressLint("NewApi")
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun CatalogTheme(theme: Theme, content: @Composable () -> Unit) {
val context = LocalContext.current
@@ -52,7 +57,11 @@
when (theme.colorMode) {
ColorMode.Dynamic -> dynamicLightColorScheme(context)
ColorMode.Custom -> LightCustomColorScheme
- ColorMode.Baseline -> lightColorScheme()
+ ColorMode.Baseline -> {
+ if (theme.expressiveThemeMode == ExpressiveThemeMode.Expressive) {
+ expressiveLightColorScheme()
+ } else lightColorScheme()
+ }
}
val darkColorScheme =
when (theme.colorMode) {
@@ -62,7 +71,7 @@
}
val colorScheme =
colorSchemeFromThemeMode(
- themeMode = theme.themeMode,
+ themeColorMode = theme.themeColorMode,
lightColorScheme = lightColorScheme,
darkColorScheme = darkColorScheme
)
@@ -94,23 +103,30 @@
}
)
) {
- MaterialTheme(
- colorScheme = colorScheme,
- content = content,
- )
+ if (theme.expressiveThemeMode == ExpressiveThemeMode.Expressive) {
+ MaterialExpressiveTheme(
+ colorScheme = colorScheme,
+ content = content,
+ )
+ } else {
+ MaterialTheme(
+ colorScheme = colorScheme,
+ content = content,
+ )
+ }
}
}
@Composable
fun colorSchemeFromThemeMode(
- themeMode: ThemeMode,
+ themeColorMode: ThemeColorMode,
lightColorScheme: ColorScheme,
darkColorScheme: ColorScheme
): ColorScheme {
- return when (themeMode) {
- ThemeMode.Light -> lightColorScheme
- ThemeMode.Dark -> darkColorScheme
- ThemeMode.System ->
+ return when (themeColorMode) {
+ ThemeColorMode.Light -> lightColorScheme
+ ThemeColorMode.Dark -> darkColorScheme
+ ThemeColorMode.System ->
if (!isSystemInDarkTheme()) {
lightColorScheme
} else {
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/ThemePicker.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/ThemePicker.kt
index cb21b3d..c5a5228 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/ThemePicker.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/ui/theme/ThemePicker.kt
@@ -30,24 +30,31 @@
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.selection.selectable
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Warning
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Badge
import androidx.compose.material3.Button
import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.material3.catalog.library.R
import androidx.compose.material3.catalog.library.model.ColorMode
+import androidx.compose.material3.catalog.library.model.ExpressiveThemeMode
import androidx.compose.material3.catalog.library.model.FontScaleMode
import androidx.compose.material3.catalog.library.model.MaxFontScale
import androidx.compose.material3.catalog.library.model.MinFontScale
import androidx.compose.material3.catalog.library.model.TextDirection
import androidx.compose.material3.catalog.library.model.Theme
-import androidx.compose.material3.catalog.library.model.ThemeMode
+import androidx.compose.material3.catalog.library.model.ThemeColorMode
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
@@ -58,6 +65,9 @@
@Composable
fun ThemePicker(theme: Theme, onThemeChange: (theme: Theme) -> Unit) {
+ val openExpressiveDialog = remember { mutableStateOf(false) }
+ val expressiveThemeValue = remember { mutableStateOf(ExpressiveThemeMode.NonExpressive) }
+
LazyColumn(
contentPadding =
WindowInsets.safeDrawing
@@ -73,30 +83,30 @@
modifier = Modifier.padding(horizontal = ThemePickerPadding)
)
// LazyVerticalGrid can't be used within LazyColumn due to nested scrolling
- val themeModes = ThemeMode.values()
+ val themeColorModes = ThemeColorMode.values()
Column(
modifier = Modifier.padding(ThemePickerPadding),
) {
Row(horizontalArrangement = Arrangement.spacedBy(ThemePickerPadding)) {
RadioButtonOption(
modifier = Modifier.weight(1f),
- option = themeModes[0],
- selected = themeModes[0] == theme.themeMode,
- onClick = { onThemeChange(theme.copy(themeMode = it)) }
+ option = themeColorModes[0],
+ selected = themeColorModes[0] == theme.themeColorMode,
+ onClick = { onThemeChange(theme.copy(themeColorMode = it)) }
)
RadioButtonOption(
modifier = Modifier.weight(1f),
- option = themeModes[1],
- selected = themeModes[1] == theme.themeMode,
- onClick = { onThemeChange(theme.copy(themeMode = it)) }
+ option = themeColorModes[1],
+ selected = themeColorModes[1] == theme.themeColorMode,
+ onClick = { onThemeChange(theme.copy(themeColorMode = it)) }
)
}
Row {
RadioButtonOption(
modifier = Modifier.weight(1f),
- option = themeModes[2],
- selected = themeModes[2] == theme.themeMode,
- onClick = { onThemeChange(theme.copy(themeMode = it)) }
+ option = themeColorModes[2],
+ selected = themeColorModes[2] == theme.themeColorMode,
+ onClick = { onThemeChange(theme.copy(themeColorMode = it)) }
)
}
}
@@ -204,6 +214,53 @@
onValueChangeFinished = { onThemeChange(theme.copy(fontScale = fontScale)) }
)
}
+ HorizontalDivider(Modifier.padding(horizontal = ThemePickerPadding))
+ }
+ item {
+ Row {
+ Text(
+ text = stringResource(id = R.string.expressive_theme_mode),
+ style = MaterialTheme.typography.bodyMedium,
+ modifier =
+ Modifier.padding(
+ start = ThemePickerPadding,
+ // Align Badge closer to text
+ end = ThemePickerPadding / 2
+ )
+ )
+ Badge { Text(stringResource(R.string.experimental)) }
+ }
+ // LazyVerticalGrid can't be used within LazyColumn due to nested scrolling
+ val expressiveThemeModes = ExpressiveThemeMode.values()
+ Column(
+ modifier = Modifier.padding(ThemePickerPadding),
+ ) {
+ Row(horizontalArrangement = Arrangement.spacedBy(ThemePickerPadding)) {
+ RadioButtonOption(
+ modifier = Modifier.weight(1f),
+ option = expressiveThemeModes[0],
+ selected = expressiveThemeModes[0] == theme.expressiveThemeMode,
+ onClick = {
+ if (theme.expressiveThemeMode != it) {
+ expressiveThemeValue.value = it
+ openExpressiveDialog.value = true
+ }
+ }
+ )
+ RadioButtonOption(
+ modifier = Modifier.weight(1f),
+ option = expressiveThemeModes[1],
+ selected = expressiveThemeModes[1] == theme.expressiveThemeMode,
+ onClick = {
+ if (theme.expressiveThemeMode != it) {
+ expressiveThemeValue.value = it
+ openExpressiveDialog.value = true
+ }
+ }
+ )
+ }
+ }
+ HorizontalDivider(Modifier.padding(horizontal = ThemePickerPadding))
}
item {
Column(
@@ -216,6 +273,31 @@
}
}
}
+ if (openExpressiveDialog.value) {
+ ExpressiveAlertDialog(
+ onDismissRequest = {
+ // Return to previous data type.
+ if (expressiveThemeValue.value == ExpressiveThemeMode.NonExpressive) {
+ expressiveThemeValue.value = ExpressiveThemeMode.Expressive
+ } else {
+ expressiveThemeValue.value = ExpressiveThemeMode.NonExpressive
+ }
+ openExpressiveDialog.value = false
+ },
+ onDismissButtonClick = {
+ // Return to previous data type.
+ if (expressiveThemeValue.value == ExpressiveThemeMode.NonExpressive) {
+ expressiveThemeValue.value = ExpressiveThemeMode.Expressive
+ } else {
+ expressiveThemeValue.value = ExpressiveThemeMode.NonExpressive
+ }
+ openExpressiveDialog.value = false
+ },
+ onConfirmButtonClick = {
+ onThemeChange(theme.copy(expressiveThemeMode = expressiveThemeValue.value))
+ }
+ )
+ }
}
@Composable
@@ -273,4 +355,25 @@
}
}
+@Composable
+private fun ExpressiveAlertDialog(
+ onDismissRequest: () -> Unit,
+ onConfirmButtonClick: () -> Unit,
+ onDismissButtonClick: () -> Unit,
+) {
+ AlertDialog(
+ icon = { Icon(imageVector = Icons.Filled.Warning, contentDescription = null) },
+ title = { Text("Warning") },
+ text = {
+ Text(
+ "Setting a new Material theme will reset the catalog and progress will be " +
+ "lost. Please confirm before proceeding."
+ )
+ },
+ onDismissRequest = onDismissRequest,
+ confirmButton = { Button(onClick = onConfirmButtonClick) { Text("Confirm") } },
+ dismissButton = { Button(onClick = onDismissButtonClick) { Text("Cancel") } },
+ )
+}
+
private val ThemePickerPadding = 16.dp
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/res/values/donottranslate-strings.xml b/compose/material3/material3/integration-tests/material3-catalog/src/main/res/values/donottranslate-strings.xml
index efb89e9..89c8d8e 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/res/values/donottranslate-strings.xml
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/res/values/donottranslate-strings.xml
@@ -25,6 +25,7 @@
<string name="more_menu_button">External links</string>
<string name="description">Description</string>
+ <string name="experimental">Experimental</string>
<string name="examples">Examples</string>
<string name="no_examples">No examples</string>
@@ -37,6 +38,7 @@
<string name="open_source_licenses">Open source licenses</string>
<string name="theme" description="Theme. [CHAR_LIMIT=NONE]">Theme</string>
+ <string name="expressive_theme_mode" description="Theme. [CHAR_LIMIT=NONE]">Expressive theme mode</string>
<string name="color_mode" description=" Material 3 Color Mode. [CHAR_LIMIT=NONE]">Color mode</string>
<string name="font_scale" description="Font scale. [CHAR_LIMIT=NONE]">Font scale</string>
<string name="text_direction" description="Text direction. [CHAR_LIMIT=NONE]">Text direction</string>
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingActionButtonSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingActionButtonSamples.kt
index 2850b62..2d6d354 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingActionButtonSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/FloatingActionButtonSamples.kt
@@ -24,12 +24,14 @@
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.FabPosition
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.FloatingActionButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.LargeFloatingActionButton
+import androidx.compose.material3.MediumFloatingActionButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SmallFloatingActionButton
import androidx.compose.material3.Text
@@ -63,6 +65,22 @@
}
}
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Preview
+@Sampled
+@Composable
+fun MediumFloatingActionButtonSample() {
+ MediumFloatingActionButton(
+ onClick = { /* do something */ },
+ ) {
+ Icon(
+ Icons.Filled.Add,
+ contentDescription = "Localized description",
+ modifier = Modifier.size(FloatingActionButtonDefaults.MediumIconSize),
+ )
+ }
+}
+
@Preview
@Sampled
@Composable
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TextFieldSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TextFieldSamples.kt
index b78589e..61abf46 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TextFieldSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TextFieldSamples.kt
@@ -20,59 +20,66 @@
import androidx.annotation.Sampled
import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.text.BasicTextField
-import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.InputTransformation
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.clearText
+import androidx.compose.foundation.text.input.insert
+import androidx.compose.foundation.text.input.maxLength
+import androidx.compose.foundation.text.input.rememberTextFieldState
+import androidx.compose.foundation.text.input.then
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.Favorite
-import androidx.compose.material.icons.filled.Info
-import androidx.compose.material.icons.materialIcon
-import androidx.compose.material.icons.materialPath
+import androidx.compose.material.icons.filled.Visibility
+import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
+import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.error
+import androidx.compose.ui.semantics.maxTextLength
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
-import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import androidx.core.text.isDigitsOnly
@Preview
@Sampled
@Composable
fun SimpleTextFieldSample() {
- var text by rememberSaveable { mutableStateOf("") }
-
TextField(
- value = text,
- onValueChange = { text = it },
+ state = rememberTextFieldState(),
+ lineLimits = TextFieldLineLimits.SingleLine,
label = { Text("Label") },
- singleLine = true
)
}
@@ -80,23 +87,54 @@
@Sampled
@Composable
fun SimpleOutlinedTextFieldSample() {
- var text by rememberSaveable { mutableStateOf("") }
+ OutlinedTextField(
+ state = rememberTextFieldState(),
+ lineLimits = TextFieldLineLimits.SingleLine,
+ label = { Text("Label") },
+ )
+}
- OutlinedTextField(value = text, onValueChange = { text = it }, label = { Text("Label") })
+@Preview
+@Sampled
+@Composable
+fun TextFieldWithTransformations() {
+ TextField(
+ state = rememberTextFieldState(),
+ lineLimits = TextFieldLineLimits.SingleLine,
+ label = { Text("Phone number") },
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
+ // Input transformation to limit user input to 10 digits
+ inputTransformation =
+ InputTransformation.maxLength(10).then {
+ if (!this.asCharSequence().isDigitsOnly()) {
+ revertAllChanges()
+ }
+ },
+ outputTransformation = {
+ // Output transformation to format as a phone number: (XXX) XXX-XXXX
+ if (length > 0) insert(0, "(")
+ if (length > 4) insert(4, ") ")
+ if (length > 9) insert(9, "-")
+ },
+ )
}
@Preview
@Sampled
@Composable
fun TextFieldWithIcons() {
- var text by rememberSaveable { mutableStateOf("") }
+ val state = rememberTextFieldState()
TextField(
- value = text,
- onValueChange = { text = it },
+ state = state,
+ lineLimits = TextFieldLineLimits.SingleLine,
label = { Text("Label") },
- leadingIcon = { Icon(Icons.Filled.Favorite, contentDescription = "Localized description") },
- trailingIcon = { Icon(Icons.Filled.Info, contentDescription = "Localized description") }
+ leadingIcon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
+ trailingIcon = {
+ IconButton(onClick = { state.clearText() }) {
+ Icon(Icons.Filled.Clear, contentDescription = "Clear text")
+ }
+ }
)
}
@@ -104,11 +142,9 @@
@Sampled
@Composable
fun TextFieldWithPlaceholder() {
- var text by rememberSaveable { mutableStateOf("") }
-
TextField(
- value = text,
- onValueChange = { text = it },
+ state = rememberTextFieldState(),
+ lineLimits = TextFieldLineLimits.SingleLine,
label = { Text("Email") },
placeholder = { Text("example@gmail.com") }
)
@@ -118,12 +154,9 @@
@Sampled
@Composable
fun TextFieldWithPrefixAndSuffix() {
- var text by rememberSaveable { mutableStateOf("") }
-
TextField(
- value = text,
- onValueChange = { text = it },
- singleLine = true,
+ state = rememberTextFieldState(),
+ lineLimits = TextFieldLineLimits.SingleLine,
label = { Text("Label") },
prefix = { Text("www.") },
suffix = { Text(".com") },
@@ -136,33 +169,34 @@
@Composable
fun TextFieldWithErrorState() {
val errorMessage = "Text input too long"
- var text by rememberSaveable { mutableStateOf("") }
+ val state = rememberTextFieldState()
var isError by rememberSaveable { mutableStateOf(false) }
val charLimit = 10
- fun validate(text: String) {
+ fun validate(text: CharSequence) {
isError = text.length > charLimit
}
+ LaunchedEffect(Unit) {
+ // Run validation whenever text value changes
+ snapshotFlow { state.text }.collect { validate(it) }
+ }
TextField(
- value = text,
- onValueChange = {
- text = it
- validate(text)
- },
- singleLine = true,
+ state = state,
+ lineLimits = TextFieldLineLimits.SingleLine,
label = { Text(if (isError) "Username*" else "Username") },
supportingText = {
Row {
Text(if (isError) errorMessage else "", Modifier.clearAndSetSemantics {})
Spacer(Modifier.weight(1f))
- Text("Limit: ${text.length}/$charLimit")
+ Text("Limit: ${state.text.length}/$charLimit")
}
},
isError = isError,
- keyboardActions = KeyboardActions { validate(text) },
+ onKeyboardAction = { validate(state.text) },
modifier =
Modifier.semantics {
+ maxTextLength = charLimit
// Provide localized description of the error
if (isError) error(errorMessage)
}
@@ -173,11 +207,9 @@
@Sampled
@Composable
fun TextFieldWithSupportingText() {
- var text by rememberSaveable { mutableStateOf("") }
-
TextField(
- value = text,
- onValueChange = { text = it },
+ state = rememberTextFieldState(),
+ lineLimits = TextFieldLineLimits.SingleLine,
label = { Text("Label") },
supportingText = {
Text("Supporting text that is long and perhaps goes onto another line.")
@@ -185,8 +217,8 @@
)
}
+// TODO: update sample with TextFieldState once we have wrappers for BasicSecureTextField
@Preview
-@Sampled
@Composable
fun PasswordTextField() {
var password by rememberSaveable { mutableStateOf("") }
@@ -211,117 +243,42 @@
)
}
-/**
- * We copy the implementation of Visibility and VisibilityOff icons to showcase them in the password
- * text field sample but to avoid adding material-icons-extended library as a dependency to the
- * samples not to increase the build time
- */
-private val Icons.Filled.Visibility: ImageVector
- get() {
- if (_visibility != null) {
- return _visibility!!
- }
- _visibility =
- materialIcon(name = "Filled.Visibility") {
- materialPath {
- moveTo(12.0f, 4.5f)
- curveTo(7.0f, 4.5f, 2.73f, 7.61f, 1.0f, 12.0f)
- curveToRelative(1.73f, 4.39f, 6.0f, 7.5f, 11.0f, 7.5f)
- reflectiveCurveToRelative(9.27f, -3.11f, 11.0f, -7.5f)
- curveToRelative(-1.73f, -4.39f, -6.0f, -7.5f, -11.0f, -7.5f)
- close()
- moveTo(12.0f, 17.0f)
- curveToRelative(-2.76f, 0.0f, -5.0f, -2.24f, -5.0f, -5.0f)
- reflectiveCurveToRelative(2.24f, -5.0f, 5.0f, -5.0f)
- reflectiveCurveToRelative(5.0f, 2.24f, 5.0f, 5.0f)
- reflectiveCurveToRelative(-2.24f, 5.0f, -5.0f, 5.0f)
- close()
- moveTo(12.0f, 9.0f)
- curveToRelative(-1.66f, 0.0f, -3.0f, 1.34f, -3.0f, 3.0f)
- reflectiveCurveToRelative(1.34f, 3.0f, 3.0f, 3.0f)
- reflectiveCurveToRelative(3.0f, -1.34f, 3.0f, -3.0f)
- reflectiveCurveToRelative(-1.34f, -3.0f, -3.0f, -3.0f)
- close()
- }
- }
- return _visibility!!
- }
-private var _visibility: ImageVector? = null
-
-private val Icons.Filled.VisibilityOff: ImageVector
- get() {
- if (_visibilityOff != null) {
- return _visibilityOff!!
- }
- _visibilityOff =
- materialIcon(name = "Filled.VisibilityOff") {
- materialPath {
- moveTo(12.0f, 7.0f)
- curveToRelative(2.76f, 0.0f, 5.0f, 2.24f, 5.0f, 5.0f)
- curveToRelative(0.0f, 0.65f, -0.13f, 1.26f, -0.36f, 1.83f)
- lineToRelative(2.92f, 2.92f)
- curveToRelative(1.51f, -1.26f, 2.7f, -2.89f, 3.43f, -4.75f)
- curveToRelative(-1.73f, -4.39f, -6.0f, -7.5f, -11.0f, -7.5f)
- curveToRelative(-1.4f, 0.0f, -2.74f, 0.25f, -3.98f, 0.7f)
- lineToRelative(2.16f, 2.16f)
- curveTo(10.74f, 7.13f, 11.35f, 7.0f, 12.0f, 7.0f)
- close()
- moveTo(2.0f, 4.27f)
- lineToRelative(2.28f, 2.28f)
- lineToRelative(0.46f, 0.46f)
- curveTo(3.08f, 8.3f, 1.78f, 10.02f, 1.0f, 12.0f)
- curveToRelative(1.73f, 4.39f, 6.0f, 7.5f, 11.0f, 7.5f)
- curveToRelative(1.55f, 0.0f, 3.03f, -0.3f, 4.38f, -0.84f)
- lineToRelative(0.42f, 0.42f)
- lineTo(19.73f, 22.0f)
- lineTo(21.0f, 20.73f)
- lineTo(3.27f, 3.0f)
- lineTo(2.0f, 4.27f)
- close()
- moveTo(7.53f, 9.8f)
- lineToRelative(1.55f, 1.55f)
- curveToRelative(-0.05f, 0.21f, -0.08f, 0.43f, -0.08f, 0.65f)
- curveToRelative(0.0f, 1.66f, 1.34f, 3.0f, 3.0f, 3.0f)
- curveToRelative(0.22f, 0.0f, 0.44f, -0.03f, 0.65f, -0.08f)
- lineToRelative(1.55f, 1.55f)
- curveToRelative(-0.67f, 0.33f, -1.41f, 0.53f, -2.2f, 0.53f)
- curveToRelative(-2.76f, 0.0f, -5.0f, -2.24f, -5.0f, -5.0f)
- curveToRelative(0.0f, -0.79f, 0.2f, -1.53f, 0.53f, -2.2f)
- close()
- moveTo(11.84f, 9.02f)
- lineToRelative(3.15f, 3.15f)
- lineToRelative(0.02f, -0.16f)
- curveToRelative(0.0f, -1.66f, -1.34f, -3.0f, -3.0f, -3.0f)
- lineToRelative(-0.17f, 0.01f)
- close()
- }
- }
- return _visibilityOff!!
- }
-private var _visibilityOff: ImageVector? = null
-
@Preview
@Sampled
@Composable
-fun TextFieldSample() {
- var text by
- rememberSaveable(stateSaver = TextFieldValue.Saver) {
- mutableStateOf(TextFieldValue("example", TextRange(0, 7)))
- }
-
- TextField(value = text, onValueChange = { text = it }, label = { Text("Label") })
+fun TextFieldWithInitialValueAndSelection() {
+ val state = rememberTextFieldState("Initial text", TextRange(0, 12))
+ TextField(
+ state = state,
+ lineLimits = TextFieldLineLimits.SingleLine,
+ label = { Text("Label") },
+ )
}
@Preview
@Sampled
@Composable
-fun OutlinedTextFieldSample() {
- var text by
- rememberSaveable(stateSaver = TextFieldValue.Saver) {
- mutableStateOf(TextFieldValue("example", TextRange(0, 7)))
- }
+fun OutlinedTextFieldWithInitialValueAndSelection() {
+ val state = rememberTextFieldState("Initial text", TextRange(0, 12))
+ OutlinedTextField(
+ state = state,
+ lineLimits = TextFieldLineLimits.SingleLine,
+ label = { Text("Label") },
+ )
+}
- OutlinedTextField(value = text, onValueChange = { text = it }, label = { Text("Label") })
+@Preview
+@Sampled
+@Composable
+fun DenseTextFieldContentPadding() {
+ TextField(
+ state = rememberTextFieldState(),
+ lineLimits = TextFieldLineLimits.SingleLine,
+ label = { Text("Label") },
+ // Need to set a min height using `heightIn` to override the default
+ modifier = Modifier.heightIn(min = 48.dp),
+ contentPadding = PaddingValues(top = 4.dp, bottom = 4.dp, start = 12.dp, end = 12.dp),
+ )
}
@Preview
@@ -329,34 +286,104 @@
@Composable
fun TextFieldWithHideKeyboardOnImeAction() {
val keyboardController = LocalSoftwareKeyboardController.current
- var text by rememberSaveable { mutableStateOf("") }
TextField(
- value = text,
- onValueChange = { text = it },
+ state = rememberTextFieldState(),
label = { Text("Label") },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
- keyboardActions =
- KeyboardActions(
- onDone = {
- keyboardController?.hide()
- // do something here
- }
- )
+ onKeyboardAction = { keyboardController?.hide() }
)
}
@Composable
fun TextArea() {
- var text by rememberSaveable {
- mutableStateOf(
- "This is a very long input that extends beyond " + "the height of the text area."
+ val state =
+ rememberTextFieldState(
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor " +
+ "incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quisque " +
+ "nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
+ "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
+ "fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in " +
+ "culpa qui officia deserunt mollit anim id est laborum."
)
- }
- TextField(
- value = text,
- onValueChange = { text = it },
- modifier = Modifier.height(100.dp),
- label = { Text("Label") }
+ TextField(state = state, modifier = Modifier.height(120.dp), label = { Text("Label") })
+}
+
+@Preview
+@Sampled
+@Composable
+fun CustomTextFieldUsingDecorator() {
+ val state = rememberTextFieldState()
+ val interactionSource = remember { MutableInteractionSource() }
+ val enabled = true
+ val isError = false
+ val lineLimits = TextFieldLineLimits.SingleLine
+
+ BasicTextField(
+ state = state,
+ modifier = Modifier,
+ interactionSource = interactionSource,
+ enabled = enabled,
+ lineLimits = lineLimits,
+ textStyle = LocalTextStyle.current,
+ decorator =
+ TextFieldDefaults.decorator(
+ state = state,
+ outputTransformation = null,
+ lineLimits = lineLimits,
+ enabled = enabled,
+ isError = isError,
+ interactionSource = interactionSource,
+ container = {
+ TextFieldDefaults.Container(
+ enabled = enabled,
+ isError = isError,
+ interactionSource = interactionSource,
+ // Update indicator line thickness
+ unfocusedIndicatorLineThickness = 2.dp,
+ focusedIndicatorLineThickness = 4.dp,
+ )
+ }
+ )
+ )
+}
+
+@Preview
+@Sampled
+@Composable
+fun CustomOutlinedTextFieldUsingDecorator() {
+ val state = rememberTextFieldState()
+ val interactionSource = remember { MutableInteractionSource() }
+ val enabled = true
+ val isError = false
+ val lineLimits = TextFieldLineLimits.SingleLine
+
+ BasicTextField(
+ state = state,
+ modifier = Modifier,
+ interactionSource = interactionSource,
+ enabled = enabled,
+ lineLimits = lineLimits,
+ textStyle = LocalTextStyle.current,
+ decorator =
+ OutlinedTextFieldDefaults.decorator(
+ state = state,
+ outputTransformation = null,
+ lineLimits = lineLimits,
+ enabled = enabled,
+ isError = isError,
+ interactionSource = interactionSource,
+ container = {
+ OutlinedTextFieldDefaults.Container(
+ enabled = enabled,
+ isError = isError,
+ interactionSource = interactionSource,
+ // Update border thickness and shape
+ shape = RectangleShape,
+ unfocusedBorderThickness = 2.dp,
+ focusedBorderThickness = 4.dp
+ )
+ },
+ )
)
}
@@ -364,101 +391,77 @@
@Sampled
@Composable
fun CustomTextFieldBasedOnDecorationBox() {
- @OptIn(ExperimentalMaterial3Api::class)
- @Composable
- fun CustomTextField(
- value: String,
- onValueChange: (String) -> Unit,
- modifier: Modifier = Modifier
- ) {
- val interactionSource = remember { MutableInteractionSource() }
- // parameters below will be passed to BasicTextField for correct behavior of the text field,
- // and to the decoration box for proper styling and sizing
- val enabled = true
- val singleLine = true
- val passwordTransformation = PasswordVisualTransformation()
+ var text by remember { mutableStateOf("") }
+ val interactionSource = remember { MutableInteractionSource() }
+ val enabled = true
+ val isError = false
+ val singleLine = true
- BasicTextField(
- value = value,
- onValueChange = onValueChange,
- modifier = modifier,
- visualTransformation = passwordTransformation,
- // internal implementation of the BasicTextField will dispatch focus events
- interactionSource = interactionSource,
- enabled = enabled,
- singleLine = singleLine
- ) {
+ BasicTextField(
+ value = text,
+ onValueChange = { text = it },
+ modifier = Modifier,
+ interactionSource = interactionSource,
+ enabled = enabled,
+ singleLine = singleLine,
+ textStyle = LocalTextStyle.current,
+ decorationBox = { innerTextField ->
TextFieldDefaults.DecorationBox(
- value = value,
- visualTransformation = passwordTransformation,
- innerTextField = it,
+ value = text,
+ innerTextField = innerTextField,
+ visualTransformation = VisualTransformation.None,
singleLine = singleLine,
enabled = enabled,
- // same interaction source as the one passed to BasicTextField to read focus state
- // for text field styling
+ isError = isError,
interactionSource = interactionSource,
- supportingText = { Text("Supporting text") },
- // keep horizontal paddings but change the vertical
- contentPadding =
- TextFieldDefaults.contentPaddingWithoutLabel(top = 8.dp, bottom = 8.dp),
+ container = {
+ TextFieldDefaults.Container(
+ enabled = enabled,
+ isError = isError,
+ interactionSource = interactionSource,
+ // Update indicator line thickness
+ unfocusedIndicatorLineThickness = 2.dp,
+ focusedIndicatorLineThickness = 4.dp,
+ )
+ }
)
}
- }
+ )
}
@Preview
@Sampled
@Composable
fun CustomOutlinedTextFieldBasedOnDecorationBox() {
- @OptIn(ExperimentalMaterial3Api::class)
- @Composable
- fun CustomTextField(
- value: String,
- onValueChange: (String) -> Unit,
- modifier: Modifier = Modifier
- ) {
- val interactionSource = remember { MutableInteractionSource() }
- // parameters below will be passed to BasicTextField for correct behavior of the text field,
- // and to the decoration box for proper styling and sizing
- val enabled = true
- val singleLine = true
+ var text by remember { mutableStateOf("") }
+ val interactionSource = remember { MutableInteractionSource() }
+ val enabled = true
+ val isError = false
+ val singleLine = true
- val colors =
- OutlinedTextFieldDefaults.colors(
- unfocusedBorderColor = Color.LightGray,
- focusedBorderColor = Color.DarkGray
- )
- BasicTextField(
- value = value,
- onValueChange = onValueChange,
- modifier = modifier,
- // internal implementation of the BasicTextField will dispatch focus events
- interactionSource = interactionSource,
- enabled = enabled,
- singleLine = singleLine
- ) {
+ BasicTextField(
+ value = text,
+ onValueChange = { text = it },
+ modifier = Modifier,
+ interactionSource = interactionSource,
+ enabled = enabled,
+ singleLine = singleLine,
+ textStyle = LocalTextStyle.current,
+ decorationBox = { innerTextField ->
OutlinedTextFieldDefaults.DecorationBox(
- value = value,
+ value = text,
+ innerTextField = innerTextField,
visualTransformation = VisualTransformation.None,
- innerTextField = it,
singleLine = singleLine,
enabled = enabled,
- // same interaction source as the one passed to BasicTextField to read focus state
- // for text field styling
+ isError = isError,
interactionSource = interactionSource,
- supportingText = { Text("Supporting text") },
- // keep horizontal paddings but change the vertical
- contentPadding =
- OutlinedTextFieldDefaults.contentPadding(top = 8.dp, bottom = 8.dp),
- // update border colors
- colors = colors,
- // update border thickness and shape
container = {
OutlinedTextFieldDefaults.Container(
enabled = enabled,
- isError = false,
- colors = colors,
+ isError = isError,
interactionSource = interactionSource,
+ // Update border thickness and shape
shape = RectangleShape,
unfocusedBorderThickness = 2.dp,
focusedBorderThickness = 4.dp
@@ -466,5 +469,5 @@
},
)
}
- }
+ )
}
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingActionButtonScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingActionButtonScreenshotTest.kt
index a5fa9ea..6b35b4a 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingActionButtonScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/FloatingActionButtonScreenshotTest.kt
@@ -19,6 +19,7 @@
import android.os.Build.VERSION.SDK_INT
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
@@ -224,6 +225,48 @@
}
@Test
+ fun smallFab() {
+ rule.setMaterialContent(lightColorScheme()) {
+ SmallFloatingActionButton(onClick = {}) {
+ Icon(Icons.Filled.Favorite, contentDescription = null)
+ }
+ }
+
+ assertClickableAgainstGolden("fab_small_size")
+ }
+
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+ @Test
+ fun mediumFab() {
+ rule.setMaterialContent(lightColorScheme()) {
+ MediumFloatingActionButton(onClick = {}) {
+ Icon(
+ Icons.Filled.Add,
+ contentDescription = "Localized description",
+ modifier = Modifier.size(FloatingActionButtonDefaults.MediumIconSize),
+ )
+ }
+ }
+
+ assertClickableAgainstGolden("fab_medium_size")
+ }
+
+ @Test
+ fun largeFab() {
+ rule.setMaterialContent(lightColorScheme()) {
+ LargeFloatingActionButton(onClick = {}) {
+ Icon(
+ Icons.Filled.Add,
+ contentDescription = "Localized description",
+ modifier = Modifier.size(FloatingActionButtonDefaults.LargeIconSize),
+ )
+ }
+ }
+
+ assertClickableAgainstGolden("fab_large_size")
+ }
+
+ @Test
fun text() {
rule.setMaterialContent(lightColorScheme()) {
ExtendedFloatingActionButton(
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldScreenshotTest.kt
index 734f398..6f20f08 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldScreenshotTest.kt
@@ -22,6 +22,8 @@
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Call
@@ -44,7 +46,6 @@
import androidx.compose.ui.test.swipeLeft
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
@@ -56,7 +57,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalMaterial3Api::class)
@LargeTest
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
@@ -64,13 +64,11 @@
private val TextFieldTag = "OutlinedTextField"
private val longText =
- TextFieldValue(
- "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " +
- "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam," +
- " quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
- "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
- "fugiat nulla pariatur."
- )
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " +
+ "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam," +
+ " quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
+ "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
+ "fugiat nulla pariatur."
private val platformTextStyle = defaultPlatformTextStyle()
@@ -83,8 +81,7 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Text"
OutlinedTextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState(text),
label = { Text("Label") },
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
)
@@ -97,8 +94,7 @@
fun outlinedTextField_notFocused() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
)
@@ -111,8 +107,7 @@
fun outlinedTextField_focused() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
)
@@ -128,8 +123,7 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
)
@@ -146,8 +140,7 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Input"
OutlinedTextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState(text),
label = { Text("Label") },
isError = true,
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
@@ -163,8 +156,7 @@
fun outlinedTextField_error_notFocused() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
isError = true,
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
@@ -179,8 +171,7 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Hello, world!"
OutlinedTextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState(text),
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp),
colors = OutlinedTextFieldDefaults.colors(unfocusedTextColor = Color.Magenta),
)
@@ -194,8 +185,11 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Hello, world!"
OutlinedTextField(
- value = TextFieldValue(text = text, selection = TextRange(0, text.length)),
- onValueChange = {},
+ state =
+ rememberTextFieldState(
+ initialText = text,
+ initialSelection = TextRange(0, text.length),
+ ),
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
colors =
OutlinedTextFieldDefaults.colors(
@@ -218,8 +212,7 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Text"
OutlinedTextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState(text),
label = { Text("Label") },
modifier =
Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -234,8 +227,7 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Text"
OutlinedTextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState(text),
modifier =
Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
)
@@ -248,8 +240,7 @@
fun outlinedTextField_multiLine_withLabel_placeholderAlignedToTop() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
placeholder = { Text("placeholder") },
modifier =
@@ -266,8 +257,7 @@
fun outlinedTextField_multiLine_withoutLabel_placeholderAlignedToTop() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
placeholder = { Text("placeholder") },
modifier =
Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -283,8 +273,7 @@
fun outlinedTextField_multiLine_labelAlignedToTop() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier =
Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -299,9 +288,8 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Text"
OutlinedTextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
- singleLine = true,
+ state = rememberTextFieldState(text),
+ lineLimits = TextFieldLineLimits.SingleLine,
label = { Text("Label") },
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
)
@@ -315,9 +303,8 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Text"
OutlinedTextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
- singleLine = true,
+ state = rememberTextFieldState(text),
+ lineLimits = TextFieldLineLimits.SingleLine,
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
)
}
@@ -329,11 +316,10 @@
fun outlinedTextField_singleLine_withLabel_placeholderAlignedToTop() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
placeholder = { Text("placeholder") },
label = { Text("Label") },
- singleLine = true,
+ lineLimits = TextFieldLineLimits.SingleLine,
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
)
}
@@ -347,10 +333,9 @@
fun outlinedTextField_singleLine_withoutLabel_placeholderCenteredVertically() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
placeholder = { Text("placeholder") },
- singleLine = true,
+ lineLimits = TextFieldLineLimits.SingleLine,
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
)
}
@@ -366,8 +351,7 @@
fun outlinedTextField_singleLine_labelCenteredVertically() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
)
@@ -380,9 +364,8 @@
fun outlinedTextField_disabled() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = TextFieldValue("Text"),
- onValueChange = {},
- singleLine = true,
+ state = rememberTextFieldState("Text"),
+ lineLimits = TextFieldLineLimits.SingleLine,
enabled = false,
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
)
@@ -395,9 +378,8 @@
fun outlinedTextField_disabled_notFocusable() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = TextFieldValue("Text"),
- onValueChange = {},
- singleLine = true,
+ state = rememberTextFieldState("Text"),
+ lineLimits = TextFieldLineLimits.SingleLine,
enabled = false,
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
)
@@ -412,9 +394,8 @@
fun outlinedTextField_disabled_notScrolled() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = longText,
- onValueChange = {},
- singleLine = true,
+ state = rememberTextFieldState(longText),
+ lineLimits = TextFieldLineLimits.SingleLine,
modifier = Modifier.testTag(TextFieldTag).requiredWidth(300.dp),
enabled = false
)
@@ -435,8 +416,7 @@
fun outlinedTextField_readOnly() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = TextFieldValue("Text"),
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp),
enabled = true,
readOnly = true
@@ -450,8 +430,7 @@
fun outlinedTextField_readOnly_focused() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = TextFieldValue("Text"),
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp),
enabled = true,
readOnly = true
@@ -467,10 +446,9 @@
fun outlinedTextField_readOnly_scrolled() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = longText,
- onValueChange = {},
+ state = rememberTextFieldState(longText),
modifier = Modifier.testTag(TextFieldTag).requiredWidth(300.dp),
- singleLine = true,
+ lineLimits = TextFieldLineLimits.SingleLine,
enabled = true,
readOnly = true
)
@@ -490,14 +468,12 @@
@Test
fun outlinedTextField_textCenterAligned() {
rule.setMaterialContent(lightColorScheme()) {
- val text = "Hello world"
OutlinedTextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState("Hello world"),
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
textStyle =
TextStyle(textAlign = TextAlign.Center, platformStyle = platformTextStyle),
- singleLine = true
+ lineLimits = TextFieldLineLimits.SingleLine
)
}
@@ -507,13 +483,11 @@
@Test
fun outlinedTextField_textAlignedToEnd() {
rule.setMaterialContent(lightColorScheme()) {
- val text = "Hello world"
OutlinedTextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState("Hello world"),
modifier = Modifier.fillMaxWidth().testTag(TextFieldTag),
textStyle = TextStyle(textAlign = TextAlign.End, platformStyle = platformTextStyle),
- singleLine = true
+ lineLimits = TextFieldLineLimits.SingleLine
)
}
@@ -524,11 +498,10 @@
fun outlinedTextField_customShape() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
- singleLine = true,
+ lineLimits = TextFieldLineLimits.SingleLine,
shape = CutCornerShape(10.dp)
)
}
@@ -540,10 +513,9 @@
fun outlinedTextField_supportingText() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
modifier = Modifier.testTag(TextFieldTag).fillMaxWidth(),
- singleLine = true,
+ lineLimits = TextFieldLineLimits.SingleLine,
supportingText = { Text("Supporting text") }
)
}
@@ -555,11 +527,10 @@
fun outlinedTextField_errorSupportingText() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
isError = true,
modifier = Modifier.testTag(TextFieldTag).fillMaxWidth(),
- singleLine = true,
+ lineLimits = TextFieldLineLimits.SingleLine,
supportingText = { Text("Error supporting text") }
)
}
@@ -573,8 +544,7 @@
rule.setMaterialContent(lightColorScheme()) {
makeLabelNull = remember { mutableStateOf(false) }
OutlinedTextField(
- value = "Text",
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
label =
if (makeLabelNull.value) {
@@ -595,8 +565,7 @@
fun outlinedTextField_leadingTrailingIcons() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
leadingIcon = { Icon(Icons.Default.Call, null) },
@@ -611,8 +580,7 @@
fun outlinedTextField_leadingTrailingIcons_error() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
leadingIcon = { Icon(Icons.Default.Call, null) },
@@ -628,8 +596,7 @@
fun outlinedTextField_prefixSuffix_withLabelAndInput() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "Text",
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
label = { Text("Label") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
prefix = { Text("P:") },
@@ -644,8 +611,7 @@
fun outlinedTextField_prefixSuffix_withLabelAndInput_darkTheme() {
rule.setMaterialContent(darkColorScheme()) {
OutlinedTextField(
- value = "Text",
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
label = { Text("Label") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
prefix = { Text("P:") },
@@ -660,8 +626,7 @@
fun outlinedTextField_prefixSuffix_withLabelAndInput_focused() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "Text",
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
label = { Text("Label") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
prefix = { Text("P:") },
@@ -678,8 +643,7 @@
fun outlinedTextField_prefixSuffix_withPlaceholder() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
placeholder = { Text("Placeholder") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
prefix = { Text("P:") },
@@ -694,8 +658,7 @@
fun outlinedTextField_prefixSuffix_withLeadingTrailingIcons() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "Text",
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
label = { Text("Label") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
prefix = { Text("P:") },
@@ -713,8 +676,7 @@
rule.setMaterialContent(darkColorScheme()) {
val text = "Text"
OutlinedTextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState(text),
label = { Text("Label") },
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
)
@@ -727,8 +689,7 @@
fun outlinedTextField_focused_darkTheme() {
rule.setMaterialContent(darkColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
)
@@ -744,8 +705,7 @@
rule.setMaterialContent(darkColorScheme()) {
val text = "Input"
OutlinedTextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState(text),
label = { Text("Label") },
isError = true,
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
@@ -761,9 +721,8 @@
fun outlinedTextField_disabled_darkTheme() {
rule.setMaterialContent(darkColorScheme()) {
OutlinedTextField(
- value = TextFieldValue("Text"),
- onValueChange = {},
- singleLine = true,
+ state = rememberTextFieldState("Text"),
+ lineLimits = TextFieldLineLimits.SingleLine,
enabled = false,
modifier = Modifier.testTag(TextFieldTag).requiredWidth(280.dp)
)
@@ -776,8 +735,7 @@
fun outlinedTextField_leadingTrailingIcons_darkTheme() {
rule.setMaterialContent(darkColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
leadingIcon = { Icon(Icons.Default.Call, null) },
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt
index 8d66554..b5d1385 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/OutlinedTextFieldTest.kt
@@ -33,6 +33,11 @@
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.delete
+import androidx.compose.foundation.text.input.insert
+import androidx.compose.foundation.text.input.placeCursorAtEnd
+import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material3.internal.HorizontalIconPadding
@@ -86,7 +91,6 @@
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
-import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
@@ -118,8 +122,7 @@
rule
.setMaterialContentForSizeAssertions {
OutlinedTextField(
- value = "input",
- onValueChange = {},
+ state = rememberTextFieldState("input"),
modifier = Modifier.requiredWidth(40.dp)
)
}
@@ -130,7 +133,7 @@
fun testOutlinedTextField_defaultWidth() {
rule
.setMaterialContentForSizeAssertions {
- OutlinedTextField(value = "input", onValueChange = {})
+ OutlinedTextField(rememberTextFieldState("input"))
}
.assertWidthIsEqualTo(ExpectedDefaultTextFieldWidth)
}
@@ -150,16 +153,14 @@
Modifier.testTag(textField1Tag).onFocusChanged {
textField1Focused = it.isFocused
},
- value = "input1",
- onValueChange = {}
+ state = rememberTextFieldState("input1"),
)
OutlinedTextField(
modifier =
Modifier.testTag(textField2Tag).onFocusChanged {
textField2Focused = it.isFocused
},
- value = "input2",
- onValueChange = {}
+ state = rememberTextFieldState("input2"),
)
}
}
@@ -187,8 +188,7 @@
OutlinedTextField(
modifier =
Modifier.testTag(TextFieldTag).onFocusChanged { focused = it.isFocused },
- value = "input",
- onValueChange = {}
+ state = rememberTextFieldState("input"),
)
}
}
@@ -207,8 +207,7 @@
CompositionLocalProvider(LocalDensity provides density) {
Box(Modifier.testTag("box").background(Color.Red)) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
colors =
OutlinedTextFieldDefaults.colors(
unfocusedTextColor = Color.White,
@@ -238,9 +237,8 @@
val labelPosition = Ref<Offset>()
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
- singleLine = true,
+ state = rememberTextFieldState(),
+ lineLimits = TextFieldLineLimits.SingleLine,
label = {
Box(
Modifier.size(MinTextLineHeight).onGloballyPositioned {
@@ -270,8 +268,7 @@
val labelPosition = Ref<Offset>()
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
Box(
Modifier.size(MinTextLineHeight).onGloballyPositioned {
@@ -299,8 +296,7 @@
val labelPosition = Ref<Offset>()
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
modifier = Modifier.requiredWidth(textFieldWidth),
label = {
Text(
@@ -339,8 +335,7 @@
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
Box(
Modifier.size(MinFocusedLabelLineHeight).onGloballyPositioned {
@@ -369,8 +364,7 @@
val labelPosition = Ref<Offset>()
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
modifier = Modifier.testTag(TextFieldTag).requiredWidth(textFieldWidth),
label = {
Text(
@@ -410,8 +404,7 @@
val labelHeight = 200.dp
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
modifier =
Modifier.testTag(TextFieldTag).onGloballyPositioned { tfSize.value = it.size },
label = { Box(Modifier.size(width = 50.dp, height = labelHeight)) },
@@ -431,8 +424,7 @@
val trailingSize = Ref<IntSize>()
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
modifier = Modifier.testTag(TextFieldTag).requiredWidth(textFieldWidth),
label = {
Text(
@@ -477,8 +469,7 @@
val labelPosition = Ref<Offset>()
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "input",
- onValueChange = {},
+ state = rememberTextFieldState("input"),
label = {
Box(
Modifier.size(labelSize).onGloballyPositioned {
@@ -503,8 +494,7 @@
// Regression test for b/251162419
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text(text = "Label") },
placeholder = {
Text(text = "Placeholder", modifier = Modifier.testTag("Placeholder"))
@@ -527,8 +517,7 @@
Box {
OutlinedTextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Box(Modifier.size(MinFocusedLabelLineHeight)) },
placeholder = {
Box(
@@ -558,8 +547,7 @@
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
placeholder = {
Box(
Modifier.size(placeholderSize).onGloballyPositioned {
@@ -585,8 +573,7 @@
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "input",
- onValueChange = {},
+ state = rememberTextFieldState("input"),
placeholder = {
Text(
text = "placeholder",
@@ -614,8 +601,7 @@
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
placeholder = {
Text("placeholder")
assertThat(LocalTextStyle.current).isEqualTo(MaterialTheme.typography.bodyLarge)
@@ -631,11 +617,9 @@
fun testOutlinedTextField_placeholderColor_whenInputEmptyAndFocused() {
var focused = false
rule.setMaterialContent(lightColorScheme()) {
- val text = remember { mutableStateOf("") }
OutlinedTextField(
modifier = Modifier.testTag(TextFieldTag),
- value = text.value,
- onValueChange = { text.value = it },
+ state = rememberTextFieldState(),
colors =
OutlinedTextFieldDefaults.colors(
focusedPlaceholderColor = Color.Red,
@@ -671,8 +655,7 @@
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
Box(
Modifier.size(labelSize).onGloballyPositioned {
@@ -730,8 +713,7 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalDensity provides density) {
OutlinedTextField(
- value = "text",
- onValueChange = {},
+ state = rememberTextFieldState("text"),
modifier = Modifier.width(textFieldWidth),
label = { Text("label") },
leadingIcon = {
@@ -803,8 +785,7 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalDensity provides density) {
OutlinedTextField(
- value = "text",
- onValueChange = {},
+ state = rememberTextFieldState("text"),
modifier = Modifier.width(textFieldWidth).height(textFieldHeight),
leadingIcon = {
IconButton(
@@ -872,8 +853,7 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalDensity provides density) {
OutlinedTextField(
- value = "text",
- onValueChange = {},
+ state = rememberTextFieldState("text"),
modifier = Modifier.width(textFieldWidth).height(textFieldHeight),
leadingIcon = {
Box(
@@ -928,8 +908,7 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalDensity provides density) {
OutlinedTextField(
- value = "text",
- onValueChange = {},
+ state = rememberTextFieldState("text"),
modifier = Modifier.width(textFieldWidth),
label = { Box(Modifier.size(MinFocusedLabelLineHeight)) },
prefix = {
@@ -981,8 +960,7 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalDensity provides density) {
OutlinedTextField(
- value = "text",
- onValueChange = {},
+ state = rememberTextFieldState("text"),
modifier = Modifier.width(textFieldWidth),
prefix = {
Box(
@@ -1029,8 +1007,7 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalDensity provides density) {
OutlinedTextField(
- value = "text",
- onValueChange = {},
+ state = rememberTextFieldState("text"),
modifier = Modifier.width(textFieldWidth),
prefix = {
Box(
@@ -1079,8 +1056,7 @@
val labelPosition = Ref<Offset>()
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
Text(
text = "label",
@@ -1108,8 +1084,7 @@
val labelPosition = Ref<Offset>()
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
Text(
text = "label",
@@ -1133,8 +1108,7 @@
fun testOutlinedTextField_colorInLeadingTrailing_whenValidInput() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
isError = false,
leadingIcon = {
assertThat(LocalContentColor.current)
@@ -1152,8 +1126,7 @@
fun testOutlinedTextField_colorInLeadingTrailing_whenInvalidInput() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
isError = true,
leadingIcon = {
assertThat(LocalContentColor.current)
@@ -1171,8 +1144,7 @@
val supportingPosition = Ref<Offset>()
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
textStyle = TextStyle(fontSize = 1.sp), // ensure text size is minimum
supportingText = {
Box(
@@ -1198,8 +1170,7 @@
val supportingSize = Ref<IntSize>()
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
modifier = Modifier.onGloballyPositioned { tfSize.value = it.size },
supportingText = {
Text(
@@ -1222,8 +1193,7 @@
val tfSize = Ref<IntSize>()
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
modifier = Modifier.onGloballyPositioned { tfSize.value = it.size },
supportingText = { Text("Supporting") }
)
@@ -1239,8 +1209,8 @@
fun testOutlinedTextField_supportingText_remainsVisibleWithTallInput() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = buildString { repeat(200) { append("line $it\n") } },
- onValueChange = {},
+ state =
+ rememberTextFieldState(buildString { repeat(200) { append("line $it\n") } }),
modifier = Modifier.size(width = ExpectedDefaultTextFieldWidth, height = 150.dp),
supportingText = { Text("Supporting", modifier = Modifier.testTag("Supporting")) }
)
@@ -1255,8 +1225,7 @@
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
modifier = Modifier.onFocusChanged { focused = it.isFocused },
- value = "input",
- onValueChange = {},
+ state = rememberTextFieldState("input"),
supportingText = { Text("Supporting") }
)
}
@@ -1269,8 +1238,7 @@
fun testOutlinedTextField_supportingText_colorAndStyle() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
supportingText = {
assertThat(LocalTextStyle.current).isEqualTo(MaterialTheme.typography.bodySmall)
assertThat(LocalContentColor.current)
@@ -1284,8 +1252,7 @@
fun testOutlinedTextField_supportingText_error_colorAndStyle() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
isError = true,
supportingText = {
assertThat(LocalTextStyle.current).isEqualTo(MaterialTheme.typography.bodySmall)
@@ -1308,12 +1275,10 @@
awaitCancellation()
}
rule.setContent {
- val text = remember { mutableStateOf("") }
InterceptPlatformTextInput(interceptor) {
OutlinedTextField(
modifier = Modifier.testTag(TextFieldTag),
- value = text.value,
- onValueChange = { text.value = it },
+ state = rememberTextFieldState(),
keyboardOptions =
KeyboardOptions(imeAction = ImeAction.Go, keyboardType = KeyboardType.Email)
)
@@ -1340,9 +1305,14 @@
Box(Modifier.background(color = Color.White)) {
OutlinedTextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "qwerty",
- onValueChange = {},
- visualTransformation = PasswordVisualTransformation('\u0020')
+ state = rememberTextFieldState("qwerty"),
+ outputTransformation = {
+ // transform all chars to blank spaces
+ val size = length
+ delete(0, length)
+ insert(0, " ".repeat(size))
+ placeCursorAtEnd()
+ }
)
}
}
@@ -1364,7 +1334,7 @@
fun testOutlinedTextField_errorSemantics_defaultMessage() {
lateinit var errorMessage: String
rule.setMaterialContent(lightColorScheme()) {
- OutlinedTextField(value = "test", onValueChange = {}, isError = true)
+ OutlinedTextField(state = rememberTextFieldState("test"), isError = true)
errorMessage = getString(Strings.DefaultErrorMessage)
}
@@ -1381,8 +1351,7 @@
rule.setMaterialContent(lightColorScheme()) {
val isError = remember { mutableStateOf(true) }
OutlinedTextField(
- value = "test",
- onValueChange = {},
+ state = rememberTextFieldState("test"),
modifier =
Modifier.testTag(TextFieldTag).semantics {
if (isError.value) error(errorMessage)
@@ -1413,7 +1382,6 @@
var size: IntSize? = null
var dividerSize: IntSize? = null
rule.setMaterialContent(lightColorScheme()) {
- val text = remember { mutableStateOf("") }
Box(Modifier.onGloballyPositioned { size = it.size }) {
Row(Modifier.height(IntrinsicSize.Min)) {
VerticalDivider(
@@ -1421,9 +1389,8 @@
modifier = Modifier.onGloballyPositioned { dividerSize = it.size }
)
OutlinedTextField(
- value = text.value,
+ state = rememberTextFieldState(),
label = { Text(text = "Label") },
- onValueChange = { text.value = it }
)
}
}
@@ -1441,8 +1408,7 @@
fun testOutlinedTextField_appliesContainerColor() {
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
modifier = Modifier.testTag(TextFieldTag),
colors =
OutlinedTextFieldDefaults.colors(
@@ -1461,7 +1427,6 @@
var textFieldSize: IntSize? = null
var dividerSize: IntSize? = null
rule.setMaterialContent(lightColorScheme()) {
- val text = remember { mutableStateOf("") }
Box {
Column(Modifier.width(IntrinsicSize.Min)) {
HorizontalDivider(
@@ -1469,9 +1434,8 @@
modifier = Modifier.onGloballyPositioned { dividerSize = it.size }
)
OutlinedTextField(
- value = text.value,
+ state = rememberTextFieldState(),
label = { Text(text = "Label") },
- onValueChange = { text.value = it },
modifier = Modifier.onGloballyPositioned { textFieldSize = it.size }
)
}
@@ -1496,8 +1460,7 @@
rule.setMaterialContent(lightColorScheme()) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
textStyle = LocalTextStyle.current
contentColor = LocalContentColor.current
@@ -1538,8 +1501,7 @@
val bodySmall = MaterialTheme.typography.bodySmall.copy(color = bodySmallColor)
MaterialTheme(typography = Typography(bodySmall = bodySmall)) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
textStyle = LocalTextStyle.current
contentColor = LocalContentColor.current
@@ -1580,8 +1542,7 @@
val bodySmall = MaterialTheme.typography.bodySmall.copy(color = expectedLabelColor)
MaterialTheme(typography = Typography(bodySmall = bodySmall)) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
textStyle = LocalTextStyle.current
contentColor = LocalContentColor.current
@@ -1625,8 +1586,7 @@
val bodyLarge = MaterialTheme.typography.bodyLarge.copy(color = bodyLargeColor)
MaterialTheme(typography = Typography(bodySmall = bodySmall, bodyLarge = bodyLarge)) {
OutlinedTextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
textStyle = LocalTextStyle.current
contentColor = LocalContentColor.current
@@ -1657,13 +1617,8 @@
@Test
fun testOutlinedTextField_withIntrinsicsMeasurement_getsIdle() {
rule.setMaterialContent(lightColorScheme()) {
- val text = remember { mutableStateOf("") }
Row(Modifier.height(IntrinsicSize.Min)) {
- OutlinedTextField(
- value = text.value,
- onValueChange = { text.value = it },
- label = { Text("Label") }
- )
+ OutlinedTextField(state = rememberTextFieldState(), label = { Text("Label") })
VerticalDivider()
}
}
@@ -1677,13 +1632,9 @@
fun testOutlinedTextField_intrinsicHeight_withOnlyEmptyInput() {
var height = 0
rule.setMaterialContent(lightColorScheme()) {
- val text = remember { mutableStateOf("") }
Box(Modifier.onGloballyPositioned { height = it.size.height }) {
Row(Modifier.height(IntrinsicSize.Min)) {
- OutlinedTextField(
- value = text.value,
- onValueChange = { text.value = it },
- )
+ OutlinedTextField(rememberTextFieldState())
VerticalDivider()
}
}
@@ -1698,12 +1649,10 @@
fun testOutlinedTextField_intrinsicHeight_withEmptyInput_andDecorations() {
var height = 0
rule.setMaterialContent(lightColorScheme()) {
- val text = remember { mutableStateOf("") }
Box(Modifier.onGloballyPositioned { height = it.size.height }) {
Row(Modifier.height(IntrinsicSize.Min)) {
OutlinedTextField(
- value = text.value,
- onValueChange = { text.value = it },
+ state = rememberTextFieldState(),
leadingIcon = { Icon(Icons.Default.Favorite, null) },
trailingIcon = { Icon(Icons.Default.Favorite, null) },
prefix = { Text("P") },
@@ -1728,8 +1677,7 @@
Row {
Box(Modifier.width(150.dp).height(IntrinsicSize.Min)) {
OutlinedTextField(
- value = text,
- onValueChange = {},
+ state = rememberTextFieldState(text),
modifier =
Modifier.onGloballyPositioned { tfHeightIntrinsic = it.size.height },
leadingIcon = { Icon(Icons.Default.Favorite, null) },
@@ -1741,8 +1689,7 @@
Box(Modifier.width(150.dp)) {
OutlinedTextField(
- value = text,
- onValueChange = {},
+ state = rememberTextFieldState(text),
modifier =
Modifier.onGloballyPositioned { tfHeightNoIntrinsic = it.size.height },
leadingIcon = { Icon(Icons.Default.Favorite, null) },
@@ -1797,7 +1744,7 @@
modifier =
Modifier.height(IntrinsicSize.Min).horizontalScroll(rememberScrollState())
) {
- OutlinedTextField(value = "Cat", onValueChange = {}, leadingIcon = { Text("Icon") })
+ OutlinedTextField(state = rememberTextFieldState(), leadingIcon = { Text("Icon") })
}
}
}
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldDecorationBoxTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldDecoratorTest.kt
similarity index 79%
rename from compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldDecorationBoxTest.kt
rename to compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldDecoratorTest.kt
index a130983..0ceb337 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldDecorationBoxTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldDecoratorTest.kt
@@ -28,6 +28,8 @@
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material3.TextFieldDefaults.indicatorLine
import androidx.compose.material3.internal.TextFieldPadding
import androidx.compose.runtime.Composable
@@ -48,7 +50,6 @@
import androidx.compose.ui.test.captureToImage
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
-import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntSize
@@ -66,7 +67,7 @@
@MediumTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalMaterial3Api::class)
-class TextFieldDecorationBoxTest {
+class TextFieldDecoratorTest {
@get:Rule val rule = createComposeRule()
private val Density = Density(1f)
@@ -345,40 +346,39 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalDensity provides Density) {
val interactionSource = remember { MutableInteractionSource() }
- val singleLine = true
+ val lineLimits = TextFieldLineLimits.SingleLine
val colors = OutlinedTextFieldDefaults.colors(unfocusedBorderColor = Color.Red)
+ val state = rememberTextFieldState(value)
BasicTextField(
- value = value,
- onValueChange = {},
+ state = state,
modifier =
Modifier.size(
with(Density) { textFieldWidth.toDp() },
with(Density) { textFieldHeight.toDp() }
),
- singleLine = singleLine,
- interactionSource = interactionSource
- ) {
- OutlinedTextFieldDefaults.DecorationBox(
- value = value,
- innerTextField = it,
- enabled = true,
- visualTransformation = VisualTransformation.None,
- interactionSource = interactionSource,
- singleLine = singleLine,
- container = {
- OutlinedTextFieldDefaults.Container(
- enabled = true,
- isError = false,
- colors = colors,
- interactionSource = interactionSource,
- shape = RectangleShape,
- unfocusedBorderThickness = with(Density) { borderWidth.toDp() }
- )
- },
- colors = colors,
- contentPadding = PaddingValues(0.dp)
- )
- }
+ lineLimits = lineLimits,
+ interactionSource = interactionSource,
+ decorator =
+ OutlinedTextFieldDefaults.decorator(
+ state = state,
+ enabled = true,
+ outputTransformation = null,
+ interactionSource = interactionSource,
+ lineLimits = lineLimits,
+ container = {
+ OutlinedTextFieldDefaults.Container(
+ enabled = true,
+ isError = false,
+ colors = colors,
+ interactionSource = interactionSource,
+ shape = RectangleShape,
+ unfocusedBorderThickness = with(Density) { borderWidth.toDp() }
+ )
+ },
+ colors = colors,
+ contentPadding = PaddingValues(0.dp),
+ )
+ )
}
}
@@ -405,11 +405,11 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalDensity provides Density) {
val interactionSource = remember { MutableInteractionSource() }
- val singleLine = true
+ val lineLimits = TextFieldLineLimits.SingleLine
val colors = TextFieldDefaults.colors(unfocusedIndicatorColor = Color.Red)
+ val state = rememberTextFieldState(value)
BasicTextField(
- value = value,
- onValueChange = {},
+ state = state,
modifier =
Modifier.indicatorLine(
enabled = true,
@@ -423,20 +423,19 @@
with(Density) { textFieldWidth.toDp() },
with(Density) { textFieldHeight.toDp() }
),
- singleLine = singleLine,
- interactionSource = interactionSource
- ) {
- TextFieldDefaults.DecorationBox(
- value = value,
- innerTextField = it,
- enabled = true,
- visualTransformation = VisualTransformation.None,
- interactionSource = interactionSource,
- singleLine = singleLine,
- colors = colors,
- contentPadding = PaddingValues(0.dp)
- )
- }
+ lineLimits = lineLimits,
+ interactionSource = interactionSource,
+ decorator =
+ TextFieldDefaults.decorator(
+ state = state,
+ enabled = true,
+ outputTransformation = null,
+ interactionSource = interactionSource,
+ lineLimits = lineLimits,
+ colors = colors,
+ contentPadding = PaddingValues(0.dp)
+ )
+ )
}
}
@@ -465,25 +464,24 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalDensity provides Density) {
val interactionSource = remember { MutableInteractionSource() }
- val singleLine = false
+ val lineLimits = TextFieldLineLimits.Default
+ val state = rememberTextFieldState(value)
BasicTextField(
- value = value,
- onValueChange = {},
+ state = state,
modifier = Modifier.onSizeChanged { size = it },
- singleLine = singleLine,
- interactionSource = interactionSource
- ) {
- TextFieldDefaults.DecorationBox(
- value = value,
- innerTextField = it,
- enabled = true,
- visualTransformation = VisualTransformation.None,
- interactionSource = interactionSource,
- singleLine = singleLine,
- placeholder = { Spacer(Modifier.size(placeholderDimension)) },
- contentPadding = PaddingValues(vertical = verticalPadding)
- )
- }
+ lineLimits = lineLimits,
+ interactionSource = interactionSource,
+ decorator =
+ TextFieldDefaults.decorator(
+ state = state,
+ enabled = true,
+ outputTransformation = null,
+ interactionSource = interactionSource,
+ lineLimits = lineLimits,
+ placeholder = { Spacer(Modifier.size(placeholderDimension)) },
+ contentPadding = PaddingValues(vertical = verticalPadding)
+ )
+ )
}
}
@@ -629,45 +627,49 @@
Box(Modifier.onSizeChanged { size = it }) {
val value = "Text"
val interactionSource = remember { MutableInteractionSource() }
+ val state = rememberTextFieldState(value)
+ val lineLimits =
+ if (singleLine) TextFieldLineLimits.SingleLine
+ else TextFieldLineLimits.Default
BasicTextField(
- value = value,
- onValueChange = {},
+ state = state,
modifier = Modifier.focusRequester(focusRequester),
- singleLine = singleLine,
- interactionSource = interactionSource
- ) {
- val innerTextField: @Composable () -> Unit = {
- Box(
- Modifier.size(InnerTextFieldWidth, InnerTextFieldHeight)
- .onGloballyPositioned { position = it.positionInRoot() }
- ) {
- it()
+ lineLimits = lineLimits,
+ interactionSource = interactionSource,
+ decorator = {
+ val innerTextField: @Composable () -> Unit = {
+ Box(
+ Modifier.size(InnerTextFieldWidth, InnerTextFieldHeight)
+ .onGloballyPositioned { position = it.positionInRoot() }
+ ) {
+ it()
+ }
+ }
+ if (isOutlined) {
+ OutlinedTextFieldDefaults.decorator(
+ state = state,
+ enabled = true,
+ lineLimits = lineLimits,
+ outputTransformation = null,
+ interactionSource = interactionSource,
+ contentPadding = padding,
+ label = label
+ )
+ .Decoration(innerTextField)
+ } else {
+ TextFieldDefaults.decorator(
+ state = state,
+ enabled = true,
+ lineLimits = lineLimits,
+ outputTransformation = null,
+ interactionSource = interactionSource,
+ contentPadding = padding,
+ label = label
+ )
+ .Decoration(innerTextField)
}
}
- if (isOutlined) {
- OutlinedTextFieldDefaults.DecorationBox(
- value = value,
- innerTextField = innerTextField,
- enabled = true,
- singleLine = singleLine,
- visualTransformation = VisualTransformation.None,
- interactionSource = interactionSource,
- contentPadding = padding,
- label = label
- )
- } else {
- TextFieldDefaults.DecorationBox(
- value = value,
- innerTextField = innerTextField,
- enabled = true,
- singleLine = singleLine,
- visualTransformation = VisualTransformation.None,
- interactionSource = interactionSource,
- contentPadding = padding,
- label = label
- )
- }
- }
+ )
}
}
}
@@ -694,22 +696,21 @@
@Test
fun testTextFields_TextDecoration_noCrashConstraintsInfinity() {
-
rule.setMaterialContent(lightColorScheme()) {
Column(
modifier =
Modifier.height(IntrinsicSize.Min).horizontalScroll(rememberScrollState())
) {
- TextFieldDefaults.DecorationBox(
- value = "Hats",
- innerTextField = { Text("Cats") },
- enabled = true,
- singleLine = true,
- visualTransformation = VisualTransformation.None,
- interactionSource = remember { MutableInteractionSource() },
- suffix = { Text("Rats") },
- colors = TextFieldDefaults.colors(),
- )
+ TextFieldDefaults.decorator(
+ state = rememberTextFieldState("Hats"),
+ enabled = true,
+ lineLimits = TextFieldLineLimits.SingleLine,
+ outputTransformation = null,
+ interactionSource = remember { MutableInteractionSource() },
+ suffix = { Text("Rats") },
+ colors = TextFieldDefaults.colors(),
+ )
+ .Decoration { Text("Cats") }
}
}
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldScreenshotTest.kt
index 50beab3..5e44ee9 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldScreenshotTest.kt
@@ -22,6 +22,8 @@
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Call
@@ -42,7 +44,6 @@
import androidx.compose.ui.test.swipeLeft
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
@@ -54,20 +55,17 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalMaterial3Api::class)
@LargeTest
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
class TextFieldScreenshotTest {
private val TextFieldTag = "TextField"
private val longText =
- TextFieldValue(
- "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " +
- "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam," +
- " quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
- "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
- "fugiat nulla pariatur."
- )
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " +
+ "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam," +
+ " quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " +
+ "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
+ "fugiat nulla pariatur."
private val platformTextStyle = defaultPlatformTextStyle()
@@ -78,11 +76,9 @@
@Test
fun textField_withInput() {
rule.setMaterialContent(lightColorScheme()) {
- Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
- val text = "Text"
+ Box(Modifier.testTag(TextFieldTag)) {
TextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
label = { Text("Label") },
modifier = Modifier.requiredWidth(280.dp)
)
@@ -95,10 +91,9 @@
@Test
fun textField_notFocused() {
rule.setMaterialContent(lightColorScheme()) {
- Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
+ Box(Modifier.testTag(TextFieldTag)) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.requiredWidth(280.dp)
)
@@ -111,10 +106,9 @@
@Test
fun textField_focused() {
rule.setMaterialContent(lightColorScheme()) {
- Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
+ Box(Modifier.testTag(TextFieldTag)) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.requiredWidth(280.dp)
)
@@ -130,10 +124,9 @@
fun textField_focused_rtl() {
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
- Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
+ Box(Modifier.testTag(TextFieldTag)) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.requiredWidth(280.dp)
)
@@ -149,10 +142,8 @@
@Test
fun textField_error_focused() {
rule.setMaterialContent(lightColorScheme()) {
- val text = "Input"
TextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState("Input"),
label = { Text("Label") },
isError = true,
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
@@ -168,8 +159,7 @@
fun textField_error_notFocused() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
isError = true,
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
@@ -184,8 +174,7 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Hello, world!"
TextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState(text),
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
colors = TextFieldDefaults.colors(unfocusedTextColor = Color.Green)
)
@@ -199,8 +188,11 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Hello, world!"
TextField(
- value = TextFieldValue(text = text, selection = TextRange(0, text.length)),
- onValueChange = {},
+ state =
+ rememberTextFieldState(
+ initialText = text,
+ initialSelection = TextRange(0, text.length),
+ ),
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
colors =
TextFieldDefaults.colors(
@@ -223,8 +215,7 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Text"
TextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState(text),
label = { Text("Label") },
modifier =
Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -239,8 +230,7 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Text"
TextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState(text),
modifier =
Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
)
@@ -253,8 +243,7 @@
fun textField_multiLine_withLabel_placeholderAlignedToTop() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
placeholder = { Text("placeholder") },
modifier =
@@ -271,8 +260,7 @@
fun textField_multiLine_withoutLabel_placeholderAlignedToTop() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
placeholder = { Text("placeholder") },
modifier =
Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -288,8 +276,7 @@
fun textField_multiLine_labelAlignedToTop() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier =
Modifier.requiredHeight(300.dp).requiredWidth(280.dp).testTag(TextFieldTag)
@@ -304,9 +291,8 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Text"
TextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
- singleLine = true,
+ state = rememberTextFieldState(text),
+ lineLimits = TextFieldLineLimits.SingleLine,
label = { Text("Label") },
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
)
@@ -320,9 +306,8 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Text"
TextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
- singleLine = true,
+ state = rememberTextFieldState(text),
+ lineLimits = TextFieldLineLimits.SingleLine,
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
)
}
@@ -334,11 +319,10 @@
fun textField_singleLine_withLabel_placeholderAlignedToTop() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
placeholder = { Text("placeholder") },
label = { Text("Label") },
- singleLine = true,
+ lineLimits = TextFieldLineLimits.SingleLine,
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
)
}
@@ -352,10 +336,9 @@
fun textField_singleLine_withoutLabel_placeholderCenteredVertically() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
placeholder = { Text("placeholder") },
- singleLine = true,
+ lineLimits = TextFieldLineLimits.SingleLine,
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
)
}
@@ -371,8 +354,7 @@
fun textField_singleLine_labelCenteredVertically() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
)
@@ -385,10 +367,9 @@
fun textField_disabled() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = TextFieldValue("Text"),
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
- singleLine = true,
+ lineLimits = TextFieldLineLimits.SingleLine,
enabled = false
)
}
@@ -400,9 +381,8 @@
fun textField_disabled_notFocusable() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = TextFieldValue("Text"),
- onValueChange = {},
- singleLine = true,
+ state = rememberTextFieldState("Text"),
+ lineLimits = TextFieldLineLimits.SingleLine,
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
enabled = false
)
@@ -417,9 +397,8 @@
fun textField_disabled_notScrolled() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = longText,
- onValueChange = {},
- singleLine = true,
+ state = rememberTextFieldState(longText),
+ lineLimits = TextFieldLineLimits.SingleLine,
modifier = Modifier.testTag(TextFieldTag).requiredWidth(300.dp),
enabled = false
)
@@ -440,8 +419,7 @@
fun textField_readOnly() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = TextFieldValue("Text"),
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
enabled = true,
readOnly = true
@@ -455,8 +433,7 @@
fun textField_readOnly_focused() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = TextFieldValue("Text"),
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
enabled = true,
readOnly = true
@@ -472,10 +449,9 @@
fun textField_readOnly_scrolled() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = longText,
- onValueChange = {},
+ state = rememberTextFieldState(longText),
modifier = Modifier.testTag(TextFieldTag).requiredWidth(300.dp),
- singleLine = true,
+ lineLimits = TextFieldLineLimits.SingleLine,
enabled = true,
readOnly = true
)
@@ -496,12 +472,11 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Hello world"
TextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState(text),
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
textStyle =
TextStyle(textAlign = TextAlign.Center, platformStyle = platformTextStyle),
- singleLine = true
+ lineLimits = TextFieldLineLimits.SingleLine
)
}
@@ -513,11 +488,10 @@
rule.setMaterialContent(lightColorScheme()) {
val text = "Hello world"
TextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState(text),
modifier = Modifier.fillMaxWidth().testTag(TextFieldTag),
textStyle = TextStyle(textAlign = TextAlign.End, platformStyle = platformTextStyle),
- singleLine = true
+ lineLimits = TextFieldLineLimits.SingleLine
)
}
@@ -528,10 +502,9 @@
fun textField_supportingText() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
modifier = Modifier.testTag(TextFieldTag).fillMaxWidth(),
- singleLine = true,
+ lineLimits = TextFieldLineLimits.SingleLine,
supportingText = { Text("Supporting text") }
)
}
@@ -543,11 +516,10 @@
fun textField_errorSupportingText() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
isError = true,
modifier = Modifier.testTag(TextFieldTag).fillMaxWidth(),
- singleLine = true,
+ lineLimits = TextFieldLineLimits.SingleLine,
supportingText = { Text("Error supporting text") }
)
}
@@ -559,8 +531,7 @@
fun textField_leadingTrailingIcons() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
leadingIcon = { Icon(Icons.Default.Call, null) },
@@ -575,8 +546,7 @@
fun textField_leadingTrailingIcons_error() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
leadingIcon = { Icon(Icons.Default.Call, null) },
@@ -592,8 +562,7 @@
fun textField_prefixSuffix_withLabelAndInput() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "Text",
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
label = { Text("Label") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
prefix = { Text("P:") },
@@ -608,8 +577,7 @@
fun textField_prefixSuffix_withLabelAndInput_darkTheme() {
rule.setMaterialContent(darkColorScheme()) {
TextField(
- value = "Text",
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
label = { Text("Label") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
prefix = { Text("P:") },
@@ -624,8 +592,7 @@
fun textField_prefixSuffix_withLabelAndInput_focused() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "Text",
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
label = { Text("Label") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
prefix = { Text("P:") },
@@ -642,8 +609,7 @@
fun textField_prefixSuffix_withPlaceholder() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
placeholder = { Text("Placeholder") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
prefix = { Text("P:") },
@@ -658,8 +624,7 @@
fun textField_prefixSuffix_withLeadingTrailingIcons() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "Text",
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
label = { Text("Label") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
prefix = { Text("P:") },
@@ -678,8 +643,7 @@
Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
val text = "Text"
TextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState(text),
label = { Text("Label") },
modifier = Modifier.requiredWidth(280.dp)
)
@@ -694,8 +658,7 @@
rule.setMaterialContent(darkColorScheme()) {
Box(Modifier.semantics(mergeDescendants = true) {}.testTag(TextFieldTag)) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.requiredWidth(280.dp)
)
@@ -712,8 +675,7 @@
rule.setMaterialContent(darkColorScheme()) {
val text = "Input"
TextField(
- value = TextFieldValue(text = text, selection = TextRange(text.length)),
- onValueChange = {},
+ state = rememberTextFieldState(text),
label = { Text("Label") },
isError = true,
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag)
@@ -729,10 +691,9 @@
fun textField_disabled_darkTheme() {
rule.setMaterialContent(darkColorScheme()) {
TextField(
- value = TextFieldValue("Text"),
- onValueChange = {},
+ state = rememberTextFieldState("Text"),
modifier = Modifier.requiredWidth(280.dp).testTag(TextFieldTag),
- singleLine = true,
+ lineLimits = TextFieldLineLimits.SingleLine,
enabled = false
)
}
@@ -744,8 +705,7 @@
fun textField_leadingTrailingIcons_darkTheme() {
rule.setMaterialContent(darkColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
modifier = Modifier.width(300.dp).testTag(TextFieldTag),
leadingIcon = { Icon(Icons.Default.Call, null) },
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldTest.kt
index 193887e..a5069cf 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/TextFieldTest.kt
@@ -38,6 +38,11 @@
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.delete
+import androidx.compose.foundation.text.input.insert
+import androidx.compose.foundation.text.input.placeCursorAtEnd
+import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material3.internal.HorizontalIconPadding
@@ -97,13 +102,8 @@
import androidx.compose.ui.test.performTextInput
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
-import androidx.compose.ui.text.input.OffsetMapping
-import androidx.compose.ui.text.input.PasswordVisualTransformation
-import androidx.compose.ui.text.input.TransformedText
-import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
@@ -138,7 +138,10 @@
fun testTextField_setSmallHeight() {
rule
.setMaterialContentForSizeAssertions {
- TextField(value = "input", onValueChange = {}, modifier = Modifier.height(20.dp))
+ TextField(
+ state = rememberTextFieldState("input"),
+ modifier = Modifier.height(20.dp)
+ )
}
.assertHeightIsEqualTo(20.dp)
}
@@ -148,8 +151,7 @@
rule
.setMaterialContentForSizeAssertions {
TextField(
- value = "input",
- onValueChange = {},
+ state = rememberTextFieldState("input"),
modifier = Modifier.requiredWidth(40.dp)
)
}
@@ -161,8 +163,7 @@
rule
.setMaterialContentForSizeAssertions {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
textStyle = TextStyle(fontSize = 1.sp), // ensure text size is minimum
)
}
@@ -172,7 +173,7 @@
@Test
fun testTextField_defaultWidth() {
rule
- .setMaterialContentForSizeAssertions { TextField(value = "input", onValueChange = {}) }
+ .setMaterialContentForSizeAssertions { TextField(rememberTextFieldState("input")) }
.assertWidthIsEqualTo(ExpectedDefaultTextFieldWidth)
}
@@ -185,8 +186,7 @@
rule.setMaterialContent(lightColorScheme()) {
TextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
)
}
@@ -214,8 +214,7 @@
rule.setMaterialContent(lightColorScheme()) {
TextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Text("Label") },
supportingText = { Text("Supporting") }
)
@@ -253,14 +252,12 @@
Column {
TextField(
modifier = Modifier.testTag(textField1Tag),
- value = "input1",
- onValueChange = {},
- interactionSource = interactionSource1
+ state = rememberTextFieldState("input1"),
+ interactionSource = interactionSource1,
)
TextField(
modifier = Modifier.testTag(textField2Tag),
- value = "input2",
- onValueChange = {},
+ state = rememberTextFieldState("input2"),
interactionSource = interactionSource2
)
}
@@ -305,8 +302,7 @@
scope = rememberCoroutineScope()
TextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "input",
- onValueChange = {},
+ state = rememberTextFieldState("input"),
interactionSource = interactionSource
)
}
@@ -339,8 +335,7 @@
.focusTarget()
.focusRequester(focusRequester)
.testTag(TextFieldTag),
- value = "input",
- onValueChange = {}
+ state = rememberTextFieldState("input"),
)
}
}
@@ -370,8 +365,7 @@
.focusTarget()
.focusRequester(focusRequester)
.testTag(TextFieldTag),
- value = "input",
- onValueChange = {}
+ state = rememberTextFieldState("input"),
)
}
}
@@ -393,9 +387,8 @@
val labelPosition = Ref<Offset>()
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
- singleLine = true,
+ state = rememberTextFieldState(),
+ lineLimits = TextFieldLineLimits.SingleLine,
label = {
Box(
Modifier.size(MinTextLineHeight).onGloballyPositioned {
@@ -421,8 +414,7 @@
val labelPosition = Ref<Offset>()
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
Box(
Modifier.size(MinTextLineHeight).onGloballyPositioned {
@@ -447,8 +439,7 @@
val labelPosition = Ref<Offset>()
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
modifier = Modifier.height(height),
label = {
Box(
@@ -474,8 +465,7 @@
rule.setMaterialContent(lightColorScheme()) {
TextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
Box(
Modifier.size(MinFocusedLabelLineHeight).onGloballyPositioned {
@@ -504,8 +494,7 @@
val labelPosition = Ref<Offset>()
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "input",
- onValueChange = {},
+ state = rememberTextFieldState("input"),
label = {
Box(
Modifier.size(MinFocusedLabelLineHeight).onGloballyPositioned {
@@ -532,8 +521,7 @@
rule.setMaterialContent(lightColorScheme()) {
TextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = { Box(Modifier.size(MinFocusedLabelLineHeight)) },
placeholder = {
Box(
@@ -564,8 +552,7 @@
rule.setMaterialContent(lightColorScheme()) {
TextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
placeholder = {
Box(
Modifier.size(MinTextLineHeight).onGloballyPositioned {
@@ -594,8 +581,7 @@
rule.setMaterialContent(lightColorScheme()) {
TextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "input",
- onValueChange = {},
+ state = rememberTextFieldState("input"),
placeholder = {
Text(
text = "placeholder",
@@ -623,8 +609,7 @@
rule.setMaterialContent(lightColorScheme()) {
TextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
placeholder = {
Text("placeholder")
assertThat(LocalTextStyle.current).isEqualTo(MaterialTheme.typography.bodyLarge)
@@ -640,11 +625,9 @@
fun testTextField_placeholderColor_whenInputEmptyAndFocused() {
var focused = false
rule.setMaterialContent(lightColorScheme()) {
- val text = remember { mutableStateOf("") }
TextField(
modifier = Modifier.testTag(TextFieldTag),
- value = text.value,
- onValueChange = { text.value = it },
+ state = rememberTextFieldState(),
colors =
TextFieldDefaults.colors(
focusedPlaceholderColor = Color.Red,
@@ -680,8 +663,7 @@
rule.setMaterialContent(lightColorScheme()) {
TextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
Box(
Modifier.size(labelSize).onGloballyPositioned {
@@ -745,8 +727,7 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalDensity provides density) {
TextField(
- value = "text",
- onValueChange = {},
+ state = rememberTextFieldState("text"),
modifier = Modifier.size(TextFieldWidth, textFieldHeight),
leadingIcon = {
Icon(
@@ -817,8 +798,7 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalDensity provides density) {
TextField(
- value = "text",
- onValueChange = {},
+ state = rememberTextFieldState("text"),
modifier = Modifier.size(TextFieldWidth, textFieldHeight),
leadingIcon = {
IconButton(
@@ -889,8 +869,7 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalDensity provides density) {
TextField(
- value = "text",
- onValueChange = {},
+ state = rememberTextFieldState("text"),
modifier = Modifier.size(TextFieldWidth, textFieldHeight),
leadingIcon = {
Box(
@@ -948,8 +927,7 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalDensity provides density) {
TextField(
- value = "text",
- onValueChange = {},
+ state = rememberTextFieldState("text"),
modifier = Modifier.width(TextFieldWidth),
label = { Box(Modifier.size(MinFocusedLabelLineHeight)) },
prefix = {
@@ -1000,8 +978,7 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalDensity provides density) {
TextField(
- value = "text",
- onValueChange = {},
+ state = rememberTextFieldState("text"),
modifier = Modifier.width(TextFieldWidth),
prefix = {
Box(
@@ -1047,8 +1024,7 @@
rule.setMaterialContent(lightColorScheme()) {
CompositionLocalProvider(LocalDensity provides density) {
TextField(
- value = "text",
- onValueChange = {},
+ state = rememberTextFieldState("text"),
modifier = Modifier.width(TextFieldWidth),
prefix = {
Box(
@@ -1097,8 +1073,7 @@
val labelPosition = Ref<Offset>()
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
Text(
text = "label",
@@ -1126,8 +1101,7 @@
val labelPosition = Ref<Offset>()
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
Text(
text = "label",
@@ -1151,8 +1125,7 @@
fun testTextField_colorInLeadingTrailing_whenValidInput() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
isError = false,
leadingIcon = {
assertThat(LocalContentColor.current)
@@ -1170,8 +1143,7 @@
fun testTextField_colorInLeadingTrailing_whenInvalidInput() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
isError = true,
leadingIcon = {
assertThat(LocalContentColor.current)
@@ -1189,8 +1161,7 @@
val supportingPosition = Ref<Offset>()
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
textStyle = TextStyle(fontSize = 1.sp), // ensure text size is minimum
supportingText = {
Box(
@@ -1216,8 +1187,7 @@
val supportingSize = Ref<IntSize>()
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
modifier = Modifier.onGloballyPositioned { tfSize.value = it.size },
supportingText = {
Text(
@@ -1240,8 +1210,7 @@
val tfSize = Ref<IntSize>()
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
modifier = Modifier.onGloballyPositioned { tfSize.value = it.size },
supportingText = { Text("Supporting") }
)
@@ -1257,8 +1226,8 @@
fun testTextField_supportingText_remainsVisibleWithTallInput() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = buildString { repeat(200) { append("line $it\n") } },
- onValueChange = {},
+ state =
+ rememberTextFieldState(buildString { repeat(200) { append("line $it\n") } }),
modifier = Modifier.size(width = ExpectedDefaultTextFieldWidth, height = 150.dp),
supportingText = { Text("Supporting", modifier = Modifier.testTag("Supporting")) }
)
@@ -1273,8 +1242,7 @@
rule.setMaterialContent(lightColorScheme()) {
TextField(
modifier = Modifier.onFocusChanged { focused = it.isFocused },
- value = "input",
- onValueChange = {},
+ state = rememberTextFieldState("input"),
supportingText = { Text("Supporting") }
)
}
@@ -1287,8 +1255,7 @@
fun testTextField_supportingText_colorAndStyle() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
supportingText = {
assertThat(LocalTextStyle.current).isEqualTo(MaterialTheme.typography.bodySmall)
assertThat(LocalContentColor.current)
@@ -1302,8 +1269,7 @@
fun testTextField_supportingText_error_colorAndStyle() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
isError = true,
supportingText = {
assertThat(LocalTextStyle.current).isEqualTo(MaterialTheme.typography.bodySmall)
@@ -1326,12 +1292,10 @@
awaitCancellation()
}
rule.setContent {
- val text = remember { mutableStateOf("") }
InterceptPlatformTextInput(interceptor) {
- OutlinedTextField(
+ TextField(
modifier = Modifier.testTag(TextFieldTag),
- value = text.value,
- onValueChange = { text.value = it },
+ state = rememberTextFieldState(),
keyboardOptions =
KeyboardOptions(imeAction = ImeAction.Go, keyboardType = KeyboardType.Email)
)
@@ -1354,13 +1318,18 @@
@Test
@LargeTest
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- fun testTextField_visualTransformationPropagated() {
+ fun testTextField_outputTransformationPropagated() {
rule.setMaterialContent(lightColorScheme()) {
TextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "qwerty",
- onValueChange = {},
- visualTransformation = PasswordVisualTransformation('\u0020'),
+ state = rememberTextFieldState("qwerty"),
+ outputTransformation = {
+ // transform all chars to blank spaces
+ val size = length
+ delete(0, length)
+ insert(0, " ".repeat(size))
+ placeCursorAtEnd()
+ },
shape = RectangleShape,
colors = TextFieldDefaults.colors(unfocusedContainerColor = Color.White)
)
@@ -1388,8 +1357,7 @@
Box(Modifier.background(color = Color.White)) {
TextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "test",
- onValueChange = {},
+ state = rememberTextFieldState("test"),
label = { Text("label") },
shape = RectangleShape,
leadingIcon = { Icon(Icons.Default.Favorite, null, tint = Color.Transparent) },
@@ -1441,28 +1409,12 @@
@LargeTest
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
fun testTextField_transformedTextIsUsed_toDefineLabelPosition() {
- // if non-transformed value were used to check if the text input is empty, the label
- // wouldn't be aligned to the top, as a result it would be obscured by text
- val prefixTransformation = VisualTransformation { text ->
- val prefix = "prefix"
- val transformed = buildAnnotatedString {
- append(prefix)
- append(text)
- }
- val mapping =
- object : OffsetMapping {
- override fun originalToTransformed(offset: Int) = offset + prefix.length
-
- override fun transformedToOriginal(offset: Int) =
- (offset - prefix.length).coerceAtLeast(0)
- }
- TransformedText(transformed, mapping)
- }
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
- visualTransformation = prefixTransformation,
+ state = rememberTextFieldState(),
+ // if non-transformed value were used to check if the text input is empty, the label
+ // wouldn't be aligned to the top, as a result it would be obscured by text
+ outputTransformation = { insert(0, "prefix") },
label = {
Text("label", color = Color.Red, modifier = Modifier.background(Color.Red))
},
@@ -1480,31 +1432,15 @@
@LargeTest
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
fun testTextField_transformedTextIsUsed_toDefineIfPlaceholderNeeded() {
- // if original value were used to check if the text input is empty, the placeholder would be
- // displayed on top of the text
- val prefixTransformation = VisualTransformation { text ->
- val prefix = "prefix"
- val transformed = buildAnnotatedString {
- append(prefix)
- append(text)
- }
- val mapping =
- object : OffsetMapping {
- override fun originalToTransformed(offset: Int) = offset + prefix.length
-
- override fun transformedToOriginal(offset: Int) =
- (offset - prefix.length).coerceAtLeast(0)
- }
- TransformedText(transformed, mapping)
- }
// While surface is not used in TextField, setMaterialContent wraps content in a Surface
// component which is checked during assertPixels.
rule.setMaterialContent(lightColorScheme(surface = Color.White)) {
TextField(
modifier = Modifier.testTag(TextFieldTag),
- value = "",
- onValueChange = {},
- visualTransformation = prefixTransformation,
+ state = rememberTextFieldState(),
+ // if original value were used to check if the text input is empty, the placeholder
+ // would be displayed on top of the text
+ outputTransformation = { insert(0, "prefix") },
placeholder = {
Text(
text = "placeholder",
@@ -1527,7 +1463,7 @@
fun testTextField_errorSemantics_defaultMessage() {
lateinit var errorMessage: String
rule.setMaterialContent(lightColorScheme()) {
- TextField(value = "test", onValueChange = {}, isError = true)
+ TextField(state = rememberTextFieldState("test"), isError = true)
errorMessage = getString(DefaultErrorMessage)
}
@@ -1544,8 +1480,7 @@
rule.setMaterialContent(lightColorScheme()) {
val isError = remember { mutableStateOf(true) }
TextField(
- value = "test",
- onValueChange = {},
+ state = rememberTextFieldState("test"),
modifier =
Modifier.testTag(TextFieldTag).semantics {
if (isError.value) error(errorMessage)
@@ -1582,7 +1517,10 @@
thickness = 10.dp,
modifier = Modifier.onGloballyPositioned { dividerSize = it.size }
)
- TextField(value = "", label = { Text(text = "Label") }, onValueChange = {})
+ TextField(
+ state = rememberTextFieldState(),
+ label = { Text(text = "Label") },
+ )
}
}
}
@@ -1599,7 +1537,6 @@
var textFieldSize: IntSize? = null
var dividerSize: IntSize? = null
rule.setMaterialContent(lightColorScheme()) {
- val text = remember { mutableStateOf("") }
Box {
Column(Modifier.width(IntrinsicSize.Min)) {
HorizontalDivider(
@@ -1607,9 +1544,8 @@
modifier = Modifier.onGloballyPositioned { dividerSize = it.size }
)
TextField(
- value = text.value,
+ state = rememberTextFieldState(),
label = { Text(text = "Label") },
- onValueChange = { text.value = it },
modifier = Modifier.onGloballyPositioned { textFieldSize = it.size }
)
}
@@ -1634,8 +1570,7 @@
rule.setMaterialContent(lightColorScheme()) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
textStyle = LocalTextStyle.current
contentColor = LocalContentColor.current
@@ -1676,8 +1611,7 @@
val bodySmall = MaterialTheme.typography.bodySmall.copy(color = bodySmallColor)
MaterialTheme(typography = Typography(bodySmall = bodySmall)) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
textStyle = LocalTextStyle.current
contentColor = LocalContentColor.current
@@ -1718,8 +1652,7 @@
val bodySmall = MaterialTheme.typography.bodySmall.copy(color = expectedLabelColor)
MaterialTheme(typography = Typography(bodySmall = bodySmall)) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
textStyle = LocalTextStyle.current
contentColor = LocalContentColor.current
@@ -1763,8 +1696,7 @@
val bodyLarge = MaterialTheme.typography.bodyLarge.copy(color = bodyLargeColor)
MaterialTheme(typography = Typography(bodySmall = bodySmall, bodyLarge = bodyLarge)) {
TextField(
- value = "",
- onValueChange = {},
+ state = rememberTextFieldState(),
label = {
textStyle = LocalTextStyle.current
contentColor = LocalContentColor.current
@@ -1796,13 +1728,9 @@
fun testTextField_intrinsicHeight_withOnlyEmptyInput() {
var height = 0
rule.setMaterialContent(lightColorScheme()) {
- val text = remember { mutableStateOf("") }
Box(Modifier.onGloballyPositioned { height = it.size.height }) {
Row(Modifier.height(IntrinsicSize.Min)) {
- TextField(
- value = text.value,
- onValueChange = { text.value = it },
- )
+ TextField(rememberTextFieldState())
VerticalDivider()
}
}
@@ -1817,12 +1745,10 @@
fun testTextField_intrinsicHeight_withEmptyInput_andDecorations() {
var height = 0
rule.setMaterialContent(lightColorScheme()) {
- val text = remember { mutableStateOf("") }
Box(Modifier.onGloballyPositioned { height = it.size.height }) {
Row(Modifier.height(IntrinsicSize.Min)) {
TextField(
- value = text.value,
- onValueChange = { text.value = it },
+ state = rememberTextFieldState(),
leadingIcon = { Icon(Icons.Default.Favorite, null) },
trailingIcon = { Icon(Icons.Default.Favorite, null) },
prefix = { Text("P") },
@@ -1847,8 +1773,7 @@
Row {
Box(Modifier.width(150.dp).height(IntrinsicSize.Min)) {
TextField(
- value = text,
- onValueChange = {},
+ state = rememberTextFieldState(text),
modifier =
Modifier.onGloballyPositioned { tfHeightIntrinsic = it.size.height },
leadingIcon = { Icon(Icons.Default.Favorite, null) },
@@ -1860,8 +1785,7 @@
Box(Modifier.width(150.dp)) {
TextField(
- value = text,
- onValueChange = {},
+ state = rememberTextFieldState(text),
modifier =
Modifier.onGloballyPositioned { tfHeightNoIntrinsic = it.size.height },
leadingIcon = { Icon(Icons.Default.Favorite, null) },
@@ -1909,14 +1833,13 @@
}
@Test
- fun testTextFields_noCrashConstraintsInfinity() {
-
+ fun testTextField_noCrashConstraintsInfinity() {
rule.setMaterialContent(lightColorScheme()) {
Column(
modifier =
Modifier.height(IntrinsicSize.Min).horizontalScroll(rememberScrollState())
) {
- TextField(value = "Cat", onValueChange = {}, leadingIcon = { Text("Icon") })
+ TextField(state = rememberTextFieldState(), leadingIcon = { Text("Icon") })
}
}
}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingActionButton.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingActionButton.kt
index 9fc92ad..dc27443 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingActionButton.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingActionButton.kt
@@ -191,6 +191,60 @@
/**
* <a href="https://m3.material.io/components/floating-action-button/overview" class="external"
+ * target="_blank">Material Design medium floating action button</a>.
+ *
+ * The FAB represents the most important action on a screen. It puts key actions within reach.
+ *
+ * @sample androidx.compose.material3.samples.MediumFloatingActionButtonSample
+ * @param onClick called when this FAB is clicked
+ * @param modifier the [Modifier] to be applied to this FAB
+ * @param shape defines the shape of this FAB's container and shadow (when using [elevation])
+ * @param containerColor the color used for the background of this FAB. Use [Color.Transparent] to
+ * have no color.
+ * @param contentColor the preferred color for content inside this FAB. Defaults to either the
+ * matching content color for [containerColor], or to the current [LocalContentColor] if
+ * [containerColor] is not a color from the theme.
+ * @param elevation [FloatingActionButtonElevation] used to resolve the elevation for this FAB in
+ * different states. This controls the size of the shadow below the FAB. Additionally, when the
+ * container color is [ColorScheme.surface], this controls the amount of primary color applied as
+ * an overlay. See also: [Surface].
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ * emitting [Interaction]s for this FAB. You can use this to change the FAB's appearance or
+ * preview the FAB in different states. Note that if `null` is provided, interactions will still
+ * happen internally.
+ * @param content the content of this FAB, typically an [Icon]
+ */
+@ExperimentalMaterial3ExpressiveApi
+@Composable
+fun MediumFloatingActionButton(
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ shape: Shape = FloatingActionButtonDefaults.mediumShape,
+ containerColor: Color = FloatingActionButtonDefaults.containerColor,
+ contentColor: Color = contentColorFor(containerColor),
+ elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
+ interactionSource: MutableInteractionSource? = null,
+ content: @Composable () -> Unit,
+) {
+ FloatingActionButton(
+ onClick = onClick,
+ modifier =
+ // TODO: update sizes to use tokens
+ modifier.sizeIn(
+ minWidth = 80.dp,
+ minHeight = 80.dp,
+ ),
+ shape = shape,
+ containerColor = containerColor,
+ contentColor = contentColor,
+ elevation = elevation,
+ interactionSource = interactionSource,
+ content = content,
+ )
+}
+
+/**
+ * <a href="https://m3.material.io/components/floating-action-button/overview" class="external"
* target="_blank">Material Design large floating action button</a>.
*
* The FAB represents the most important action on a screen. It puts key actions within reach.
@@ -397,6 +451,12 @@
/** Contains the default values used by [FloatingActionButton] */
object FloatingActionButtonDefaults {
+ /** The recommended size of the icon inside a [MediumFloatingActionButton]. */
+ @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+ @get:ExperimentalMaterial3ExpressiveApi
+ @ExperimentalMaterial3ExpressiveApi
+ val MediumIconSize = 28.dp // TODO: update to use token
+
/** The recommended size of the icon inside a [LargeFloatingActionButton]. */
val LargeIconSize = FabPrimaryLargeTokens.IconSize
@@ -408,6 +468,13 @@
val smallShape: Shape
@Composable get() = FabPrimarySmallTokens.ContainerShape.value
+ /** Default shape for a medium floating action button. */
+ @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+ @get:ExperimentalMaterial3ExpressiveApi
+ @ExperimentalMaterial3ExpressiveApi
+ val mediumShape: Shape
+ @Composable get() = ShapeDefaults.LargeIncreased // TODO: update to use token
+
/** Default shape for a large floating action button. */
val largeShape: Shape
@Composable get() = FabPrimaryLargeTokens.ContainerShape.value
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/OutlinedTextField.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/OutlinedTextField.kt
index 12ce5d5..d2affed 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/OutlinedTextField.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/OutlinedTextField.kt
@@ -16,6 +16,7 @@
package androidx.compose.material3
+import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
@@ -27,9 +28,17 @@
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.InputTransformation
+import androidx.compose.foundation.text.input.KeyboardActionHandler
+import androidx.compose.foundation.text.input.OutputTransformation
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine
+import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
+import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.material3.internal.ContainerId
import androidx.compose.material3.internal.HorizontalIconPadding
@@ -78,12 +87,14 @@
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.coerceAtLeast
@@ -108,9 +119,192 @@
* 
*
+ * This overload of [OutlinedTextField] uses [TextFieldState] to keep track of its text content and
+ * position of the cursor or selection.
+ *
* See example usage:
*
* @sample androidx.compose.material3.samples.SimpleOutlinedTextFieldSample
+ * @sample androidx.compose.material3.samples.OutlinedTextFieldWithInitialValueAndSelection
+ * @param state [TextFieldState] object that holds the internal editing state of the text field.
+ * @param modifier the [Modifier] to be applied to this text field.
+ * @param enabled controls the enabled state of this text field. When `false`, this component will
+ * not respond to user input, and it will appear visually disabled and disabled to accessibility
+ * services.
+ * @param readOnly controls the editable state of the text field. When `true`, the text field cannot
+ * be modified. However, a user can focus it and copy text from it. Read-only text fields are
+ * usually used to display pre-filled forms that a user cannot edit.
+ * @param textStyle the style to be applied to the input text. Defaults to [LocalTextStyle].
+ * @param label the optional label to be displayed with this text field. The default text style uses
+ * [Typography.bodySmall] when the text field is in focus and [Typography.bodyLarge] when the text
+ * field is not in focus.
+ * @param placeholder the optional placeholder to be displayed when the input text is empty. The
+ * default text style uses [Typography.bodyLarge].
+ * @param leadingIcon the optional leading icon to be displayed at the beginning of the text field
+ * container.
+ * @param trailingIcon the optional trailing icon to be displayed at the end of the text field
+ * container.
+ * @param prefix the optional prefix to be displayed before the input text in the text field.
+ * @param suffix the optional suffix to be displayed after the input text in the text field.
+ * @param supportingText the optional supporting text to be displayed below the text field.
+ * @param isError indicates if the text field's current value is in error. When `true`, the
+ * components of the text field will be displayed in an error color, and an error will be
+ * announced to accessibility services.
+ * @param inputTransformation optional [InputTransformation] that will be used to transform changes
+ * to the [TextFieldState] made by the user. The transformation will be applied to changes made by
+ * hardware and software keyboard events, pasting or dropping text, accessibility services, and
+ * tests. The transformation will _not_ be applied when changing the [state] programmatically, or
+ * when the transformation is changed. If the transformation is changed on an existing text field,
+ * it will be applied to the next user edit. The transformation will not immediately affect the
+ * current [state].
+ * @param outputTransformation optional [OutputTransformation] that transforms how the contents of
+ * the text field are presented.
+ * @param keyboardOptions software keyboard options that contains configuration such as
+ * [KeyboardType] and [ImeAction].
+ * @param onKeyboardAction called when the user presses the action button in the input method editor
+ * (IME), or by pressing the enter key on a hardware keyboard. By default this parameter is null,
+ * and would execute the default behavior for a received IME Action e.g., [ImeAction.Done] would
+ * close the keyboard, [ImeAction.Next] would switch the focus to the next focusable item on the
+ * screen.
+ * @param lineLimits whether the text field should be [SingleLine], scroll horizontally, and ignore
+ * newlines; or [MultiLine] and grow and scroll vertically. If [SingleLine] is passed, all newline
+ * characters ('\n') within the text will be replaced with regular whitespace (' ').
+ * @param onTextLayout Callback that is executed when the text layout becomes queryable. The
+ * callback receives a function that returns a [TextLayoutResult] if the layout can be calculated,
+ * or null if it cannot. The function reads the layout result from a snapshot state object, and
+ * will invalidate its caller when the layout result changes. A [TextLayoutResult] object contains
+ * paragraph information, size of the text, baselines and other details. [Density] scope is the
+ * one that was used while creating the given text layout.
+ * @param scrollState scroll state that manages either horizontal or vertical scroll of the text
+ * field. If [lineLimits] is [SingleLine], this text field is treated as single line with
+ * horizontal scroll behavior. Otherwise, the text field becomes vertically scrollable.
+ * @param shape defines the shape of this text field's border.
+ * @param colors [TextFieldColors] that will be used to resolve the colors used for this text field
+ * in different states. See [OutlinedTextFieldDefaults.colors].
+ * @param contentPadding the padding applied to the inner text field that separates it from the
+ * surrounding elements of the text field. Note that the padding values may not be respected if
+ * they are incompatible with the text field's size constraints or layout. See
+ * [OutlinedTextFieldDefaults.contentPadding].
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ * emitting [Interaction]s for this text field. You can use this to change the text field's
+ * appearance or preview the text field in different states. Note that if `null` is provided,
+ * interactions will still happen internally.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun OutlinedTextField(
+ state: TextFieldState,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ readOnly: Boolean = false,
+ textStyle: TextStyle = LocalTextStyle.current,
+ label: @Composable (() -> Unit)? = null,
+ placeholder: @Composable (() -> Unit)? = null,
+ leadingIcon: @Composable (() -> Unit)? = null,
+ trailingIcon: @Composable (() -> Unit)? = null,
+ prefix: @Composable (() -> Unit)? = null,
+ suffix: @Composable (() -> Unit)? = null,
+ supportingText: @Composable (() -> Unit)? = null,
+ isError: Boolean = false,
+ inputTransformation: InputTransformation? = null,
+ outputTransformation: OutputTransformation? = null,
+ keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
+ onKeyboardAction: KeyboardActionHandler? = null,
+ lineLimits: TextFieldLineLimits = TextFieldLineLimits.Default,
+ onTextLayout: (Density.(getResult: () -> TextLayoutResult?) -> Unit)? = null,
+ scrollState: ScrollState = rememberScrollState(),
+ shape: Shape = OutlinedTextFieldDefaults.shape,
+ colors: TextFieldColors = OutlinedTextFieldDefaults.colors(),
+ contentPadding: PaddingValues = OutlinedTextFieldDefaults.contentPadding(),
+ interactionSource: MutableInteractionSource? = null,
+) {
+ @Suppress("NAME_SHADOWING")
+ val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
+ // If color is not provided via the text style, use content color as a default
+ val textColor =
+ textStyle.color.takeOrElse {
+ val focused = interactionSource.collectIsFocusedAsState().value
+ colors.textColor(enabled, isError, focused)
+ }
+ val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
+
+ val density = LocalDensity.current
+
+ CompositionLocalProvider(LocalTextSelectionColors provides colors.textSelectionColors) {
+ BasicTextField(
+ state = state,
+ modifier =
+ modifier
+ .then(
+ if (label != null) {
+ Modifier
+ // Merge semantics at the beginning of the modifier chain to ensure
+ // padding is considered part of the text field.
+ .semantics(mergeDescendants = true) {}
+ .padding(top = with(density) { OutlinedTextFieldTopPadding.toDp() })
+ } else {
+ Modifier
+ }
+ )
+ .defaultErrorSemantics(isError, getString(Strings.DefaultErrorMessage))
+ .defaultMinSize(
+ minWidth = OutlinedTextFieldDefaults.MinWidth,
+ minHeight = OutlinedTextFieldDefaults.MinHeight
+ ),
+ enabled = enabled,
+ readOnly = readOnly,
+ textStyle = mergedTextStyle,
+ cursorBrush = SolidColor(colors.cursorColor(isError)),
+ keyboardOptions = keyboardOptions,
+ onKeyboardAction = onKeyboardAction,
+ lineLimits = lineLimits,
+ onTextLayout = onTextLayout,
+ interactionSource = interactionSource,
+ inputTransformation = inputTransformation,
+ outputTransformation = outputTransformation,
+ scrollState = scrollState,
+ decorator =
+ OutlinedTextFieldDefaults.decorator(
+ state = state,
+ enabled = enabled,
+ lineLimits = lineLimits,
+ outputTransformation = outputTransformation,
+ interactionSource = interactionSource,
+ label = label,
+ placeholder = placeholder,
+ leadingIcon = leadingIcon,
+ trailingIcon = trailingIcon,
+ prefix = prefix,
+ suffix = suffix,
+ supportingText = supportingText,
+ isError = isError,
+ colors = colors,
+ contentPadding = contentPadding,
+ container = {
+ OutlinedTextFieldDefaults.Container(
+ enabled = enabled,
+ isError = isError,
+ interactionSource = interactionSource,
+ colors = colors,
+ shape = shape,
+ )
+ }
+ )
+ )
+ }
+}
+
+/**
+ * <a href="https://m3.material.io/components/text-fields/overview" class="external"
+ * target="_blank">Material Design outlined text field</a>.
+ *
+ * Text fields allow users to enter text into a UI. They typically appear in forms and dialogs.
+ * Outlined text fields have less visual emphasis than filled text fields. When they appear in
+ * places like forms, where many text fields are placed together, their reduced emphasis helps
+ * simplify the layout.
+ *
+ * 
*
* If apart from input text change you also want to observe the cursor location, selection range, or
* IME composition use the OutlinedTextField overload with the [TextFieldValue] parameter instead.
@@ -282,10 +476,6 @@
* 
*
- * See example usage:
- *
- * @sample androidx.compose.material3.samples.OutlinedTextFieldSample
- *
* This overload provides access to the input text, cursor position and selection range and IME
* composition. If you only want to observe an input text change, use the OutlinedTextField overload
* with the [String] parameter instead.
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Shapes.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Shapes.kt
index ed14759..7619c29 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Shapes.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Shapes.kt
@@ -276,7 +276,7 @@
@get:ExperimentalMaterial3ExpressiveApi
@ExperimentalMaterial3ExpressiveApi
/** Large sized corner shape, slightly larger than [Large] */
- val LargeIncreased: CornerBasedShape = RoundedCornerShape(16.dp)
+ val LargeIncreased: CornerBasedShape = RoundedCornerShape(20.dp)
/** Extra large sized corner shape */
val ExtraLarge: CornerBasedShape = ShapeTokens.CornerExtraLarge
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextField.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextField.kt
index 68c7c3d..b3e6fed 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextField.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextField.kt
@@ -17,6 +17,7 @@
package androidx.compose.material3
import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
@@ -28,9 +29,17 @@
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.InputTransformation
+import androidx.compose.foundation.text.input.KeyboardActionHandler
+import androidx.compose.foundation.text.input.OutputTransformation
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine
+import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
+import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.material3.internal.ContainerId
import androidx.compose.material3.internal.HorizontalIconPadding
@@ -76,6 +85,7 @@
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
@@ -107,10 +117,21 @@
*
* If you are looking for an outlined version, see [OutlinedTextField].
*
+ * This overload of [TextField] uses [TextFieldState] to keep track of its text content and position
+ * of the cursor or selection.
+ *
* A simple single line text field looks like:
*
* @sample androidx.compose.material3.samples.SimpleTextFieldSample
*
+ * You can control the initial text input and selection:
+ *
+ * @sample androidx.compose.material3.samples.TextFieldWithInitialValueAndSelection
+ *
+ * Use input and output transformations to control user input and the displayed text:
+ *
+ * @sample androidx.compose.material3.samples.TextFieldWithTransformations
+ *
* You may provide a placeholder:
*
* @sample androidx.compose.material3.samples.TextFieldWithPlaceholder
@@ -131,13 +152,185 @@
*
* @sample androidx.compose.material3.samples.TextFieldWithSupportingText
*
- * Password text field example:
+ * You can change the content padding to create a dense text field:
*
- * @sample androidx.compose.material3.samples.PasswordTextField
+ * @sample androidx.compose.material3.samples.DenseTextFieldContentPadding
*
* Hiding a software keyboard on IME action performed:
*
* @sample androidx.compose.material3.samples.TextFieldWithHideKeyboardOnImeAction
+ * @param state [TextFieldState] object that holds the internal editing state of the text field.
+ * @param modifier the [Modifier] to be applied to this text field.
+ * @param enabled controls the enabled state of this text field. When `false`, this component will
+ * not respond to user input, and it will appear visually disabled and disabled to accessibility
+ * services.
+ * @param readOnly controls the editable state of the text field. When `true`, the text field cannot
+ * be modified. However, a user can focus it and copy text from it. Read-only text fields are
+ * usually used to display pre-filled forms that a user cannot edit.
+ * @param textStyle the style to be applied to the input text. Defaults to [LocalTextStyle].
+ * @param label the optional label to be displayed with this text field. The default text style uses
+ * [Typography.bodySmall] when the text field is in focus and [Typography.bodyLarge] when the text
+ * field is not in focus.
+ * @param placeholder the optional placeholder to be displayed when the input text is empty. The
+ * default text style uses [Typography.bodyLarge].
+ * @param leadingIcon the optional leading icon to be displayed at the beginning of the text field
+ * container.
+ * @param trailingIcon the optional trailing icon to be displayed at the end of the text field
+ * container.
+ * @param prefix the optional prefix to be displayed before the input text in the text field.
+ * @param suffix the optional suffix to be displayed after the input text in the text field.
+ * @param supportingText the optional supporting text to be displayed below the text field.
+ * @param isError indicates if the text field's current value is in error. When `true`, the
+ * components of the text field will be displayed in an error color, and an error will be
+ * announced to accessibility services.
+ * @param inputTransformation optional [InputTransformation] that will be used to transform changes
+ * to the [TextFieldState] made by the user. The transformation will be applied to changes made by
+ * hardware and software keyboard events, pasting or dropping text, accessibility services, and
+ * tests. The transformation will _not_ be applied when changing the [state] programmatically, or
+ * when the transformation is changed. If the transformation is changed on an existing text field,
+ * it will be applied to the next user edit. The transformation will not immediately affect the
+ * current [state].
+ * @param outputTransformation optional [OutputTransformation] that transforms how the contents of
+ * the text field are presented.
+ * @param keyboardOptions software keyboard options that contains configuration such as
+ * [KeyboardType] and [ImeAction].
+ * @param onKeyboardAction called when the user presses the action button in the input method editor
+ * (IME), or by pressing the enter key on a hardware keyboard. By default this parameter is null,
+ * and would execute the default behavior for a received IME Action e.g., [ImeAction.Done] would
+ * close the keyboard, [ImeAction.Next] would switch the focus to the next focusable item on the
+ * screen.
+ * @param lineLimits whether the text field should be [SingleLine], scroll horizontally, and ignore
+ * newlines; or [MultiLine] and grow and scroll vertically. If [SingleLine] is passed, all newline
+ * characters ('\n') within the text will be replaced with regular whitespace (' ').
+ * @param onTextLayout Callback that is executed when the text layout becomes queryable. The
+ * callback receives a function that returns a [TextLayoutResult] if the layout can be calculated,
+ * or null if it cannot. The function reads the layout result from a snapshot state object, and
+ * will invalidate its caller when the layout result changes. A [TextLayoutResult] object contains
+ * paragraph information, size of the text, baselines and other details. [Density] scope is the
+ * one that was used while creating the given text layout.
+ * @param scrollState scroll state that manages either horizontal or vertical scroll of the text
+ * field. If [lineLimits] is [SingleLine], this text field is treated as single line with
+ * horizontal scroll behavior. Otherwise, the text field becomes vertically scrollable.
+ * @param shape defines the shape of this text field's container.
+ * @param colors [TextFieldColors] that will be used to resolve the colors used for this text field
+ * in different states. See [TextFieldDefaults.colors].
+ * @param contentPadding the padding applied to the inner text field that separates it from the
+ * surrounding elements of the text field. Note that the padding values may not be respected if
+ * they are incompatible with the text field's size constraints or layout. See
+ * [TextFieldDefaults.contentPaddingWithLabel] and [TextFieldDefaults.contentPaddingWithoutLabel].
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ * emitting [Interaction]s for this text field. You can use this to change the text field's
+ * appearance or preview the text field in different states. Note that if `null` is provided,
+ * interactions will still happen internally.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun TextField(
+ state: TextFieldState,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ readOnly: Boolean = false,
+ textStyle: TextStyle = LocalTextStyle.current,
+ label: @Composable (() -> Unit)? = null,
+ placeholder: @Composable (() -> Unit)? = null,
+ leadingIcon: @Composable (() -> Unit)? = null,
+ trailingIcon: @Composable (() -> Unit)? = null,
+ prefix: @Composable (() -> Unit)? = null,
+ suffix: @Composable (() -> Unit)? = null,
+ supportingText: @Composable (() -> Unit)? = null,
+ isError: Boolean = false,
+ inputTransformation: InputTransformation? = null,
+ outputTransformation: OutputTransformation? = null,
+ keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
+ onKeyboardAction: KeyboardActionHandler? = null,
+ lineLimits: TextFieldLineLimits = TextFieldLineLimits.Default,
+ onTextLayout: (Density.(getResult: () -> TextLayoutResult?) -> Unit)? = null,
+ scrollState: ScrollState = rememberScrollState(),
+ shape: Shape = TextFieldDefaults.shape,
+ colors: TextFieldColors = TextFieldDefaults.colors(),
+ contentPadding: PaddingValues =
+ if (label == null) {
+ TextFieldDefaults.contentPaddingWithoutLabel()
+ } else {
+ TextFieldDefaults.contentPaddingWithLabel()
+ },
+ interactionSource: MutableInteractionSource? = null,
+) {
+ @Suppress("NAME_SHADOWING")
+ val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
+ // If color is not provided via the text style, use content color as a default
+ val textColor =
+ textStyle.color.takeOrElse {
+ val focused = interactionSource.collectIsFocusedAsState().value
+ colors.textColor(enabled, isError, focused)
+ }
+ val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
+
+ CompositionLocalProvider(LocalTextSelectionColors provides colors.textSelectionColors) {
+ BasicTextField(
+ state = state,
+ modifier =
+ modifier
+ .defaultErrorSemantics(isError, getString(Strings.DefaultErrorMessage))
+ .defaultMinSize(
+ minWidth = TextFieldDefaults.MinWidth,
+ minHeight = TextFieldDefaults.MinHeight
+ ),
+ enabled = enabled,
+ readOnly = readOnly,
+ textStyle = mergedTextStyle,
+ cursorBrush = SolidColor(colors.cursorColor(isError)),
+ keyboardOptions = keyboardOptions,
+ onKeyboardAction = onKeyboardAction,
+ lineLimits = lineLimits,
+ onTextLayout = onTextLayout,
+ interactionSource = interactionSource,
+ inputTransformation = inputTransformation,
+ outputTransformation = outputTransformation,
+ scrollState = scrollState,
+ decorator =
+ TextFieldDefaults.decorator(
+ state = state,
+ enabled = enabled,
+ lineLimits = lineLimits,
+ outputTransformation = outputTransformation,
+ interactionSource = interactionSource,
+ label = label,
+ placeholder = placeholder,
+ leadingIcon = leadingIcon,
+ trailingIcon = trailingIcon,
+ prefix = prefix,
+ suffix = suffix,
+ supportingText = supportingText,
+ isError = isError,
+ colors = colors,
+ contentPadding = contentPadding,
+ container = {
+ TextFieldDefaults.Container(
+ enabled = enabled,
+ isError = isError,
+ interactionSource = interactionSource,
+ colors = colors,
+ shape = shape,
+ )
+ }
+ )
+ )
+ }
+}
+
+/**
+ * <a href="https://m3.material.io/components/text-fields/overview" class="external"
+ * target="_blank">Material Design filled text field</a>.
+ *
+ * Text fields allow users to enter text into a UI. They typically appear in forms and dialogs.
+ * Filled text fields have more visual emphasis than outlined text fields, making them stand out
+ * when surrounded by other content and components.
+ *
+ * 
+ *
+ * If you are looking for an outlined version, see [OutlinedTextField].
*
* If apart from input text change you also want to observe the cursor location, selection range, or
* IME composition use the TextField overload with the [TextFieldValue] parameter instead.
@@ -290,10 +483,6 @@
*
* If you are looking for an outlined version, see [OutlinedTextField].
*
- * See example usage:
- *
- * @sample androidx.compose.material3.samples.TextFieldSample
- *
* This overload provides access to the input text, cursor position, selection range and IME
* composition. If you only want to observe an input text change, use the TextField overload with
* the [String] parameter instead.
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt
index af7ea36..d1c94dd 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt
@@ -26,6 +26,13 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.input.OutputTransformation
+import androidx.compose.foundation.text.input.TextFieldBuffer
+import androidx.compose.foundation.text.input.TextFieldDecorator
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine
+import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
+import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.material3.internal.CommonDecorationBox
@@ -40,12 +47,14 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@@ -79,6 +88,128 @@
val FocusedIndicatorThickness = 2.dp
/**
+ * A decorator used to create custom text fields based on <a
+ * href="https://m3.material.io/components/text-fields/overview" class="external"
+ * target="_blank">Material Design filled text field</a>.
+ *
+ * If your text field requires customising elements that aren't exposed by [TextField], such as
+ * the indicator line thickness, consider using this decorator to achieve the desired design.
+ *
+ * For example, if you wish to customise the bottom indicator line, you can pass a custom
+ * [Container] to this decorator's [container].
+ *
+ * This decorator is meant to be used in conjunction with the overload of [BasicTextField] that
+ * accepts a [TextFieldDecorator] parameter. For other overloads of [BasicTextField] that use a
+ * `decorationBox`, see [DecorationBox].
+ *
+ * An example of building a custom text field using [decorator]:
+ *
+ * @sample androidx.compose.material3.samples.CustomTextFieldUsingDecorator
+ * @param state [TextFieldState] object that holds the internal editing state of the text field.
+ * @param enabled the enabled state of the text field. When `false`, this decorator will appear
+ * visually disabled. This must be the same value that is passed to [BasicTextField].
+ * @param lineLimits whether the text field is [SingleLine] or [MultiLine]. This must be the
+ * same value that is passed to [BasicTextField].
+ * @param outputTransformation [OutputTransformation] that transforms how the contents of the
+ * text field are presented. This must be the same value that is passed to [BasicTextField].
+ * @param interactionSource the read-only [InteractionSource] representing the stream of
+ * [Interaction]s for this text field. You must first create and pass in your own `remember`ed
+ * [MutableInteractionSource] instance to the [BasicTextField] for it to dispatch events. And
+ * then pass the same instance to this decorator to observe [Interaction]s and customize the
+ * appearance/behavior of the text field in different states.
+ * @param label the optional label to be displayed with this text field. The default text style
+ * uses [Typography.bodySmall] when the text field is in focus and [Typography.bodyLarge] when
+ * the text field is not in focus.
+ * @param placeholder the optional placeholder to be displayed when the input text is empty. The
+ * default text style uses [Typography.bodyLarge].
+ * @param leadingIcon the optional leading icon to be displayed at the beginning of the text
+ * field container.
+ * @param trailingIcon the optional trailing icon to be displayed at the end of the text field
+ * container.
+ * @param prefix the optional prefix to be displayed before the input text in the text field.
+ * @param suffix the optional suffix to be displayed after the input text in the text field.
+ * @param supportingText the optional supporting text to be displayed below the text field.
+ * @param isError indicates if the text field's current value is in an error state. When `true`,
+ * this decorator will display its contents in an error color.
+ * @param colors [TextFieldColors] that will be used to resolve the colors used for this text
+ * field decorator in different states. See [TextFieldDefaults.colors].
+ * @param contentPadding the padding between the input field and the surrounding elements of the
+ * decorator. Note that the padding values may not be respected if they are incompatible with
+ * the text field's size constraints or layout. See
+ * [TextFieldDefaults.contentPaddingWithLabel] and
+ * [TextFieldDefaults.contentPaddingWithoutLabel].
+ * @param container the container to be drawn behind the text field. By default, this uses
+ * [Container]. Default colors for the container come from the [colors].
+ */
+ @Composable
+ @ExperimentalMaterial3Api
+ fun decorator(
+ state: TextFieldState,
+ enabled: Boolean,
+ lineLimits: TextFieldLineLimits,
+ outputTransformation: OutputTransformation?,
+ interactionSource: InteractionSource,
+ label: @Composable (() -> Unit)? = null,
+ placeholder: @Composable (() -> Unit)? = null,
+ leadingIcon: @Composable (() -> Unit)? = null,
+ trailingIcon: @Composable (() -> Unit)? = null,
+ prefix: @Composable (() -> Unit)? = null,
+ suffix: @Composable (() -> Unit)? = null,
+ supportingText: @Composable (() -> Unit)? = null,
+ isError: Boolean = false,
+ colors: TextFieldColors = colors(),
+ contentPadding: PaddingValues =
+ if (label == null) {
+ contentPaddingWithoutLabel()
+ } else {
+ contentPaddingWithLabel()
+ },
+ container: @Composable () -> Unit = {
+ Container(
+ enabled = enabled,
+ isError = isError,
+ interactionSource = interactionSource,
+ colors = colors,
+ shape = shape,
+ focusedIndicatorLineThickness = FocusedIndicatorThickness,
+ unfocusedIndicatorLineThickness = UnfocusedIndicatorThickness,
+ )
+ }
+ ): TextFieldDecorator = TextFieldDecorator { innerTextField ->
+ val visualText =
+ if (outputTransformation == null) state.text
+ else {
+ // TODO: use constructor to create TextFieldBuffer from TextFieldState when
+ // available
+ lateinit var buffer: TextFieldBuffer
+ state.edit { buffer = this }
+ // after edit completes, mutations on buffer are ineffective
+ with(outputTransformation) { buffer.transformOutput() }
+ buffer.asCharSequence()
+ }
+
+ CommonDecorationBox(
+ type = TextFieldType.Filled,
+ visualText = visualText,
+ innerTextField = innerTextField,
+ placeholder = placeholder,
+ label = label,
+ leadingIcon = leadingIcon,
+ trailingIcon = trailingIcon,
+ prefix = prefix,
+ suffix = suffix,
+ supportingText = supportingText,
+ singleLine = lineLimits == SingleLine,
+ enabled = enabled,
+ isError = isError,
+ interactionSource = interactionSource,
+ colors = colors,
+ contentPadding = contentPadding,
+ container = container
+ )
+ }
+
+ /**
* Composable that draws a default container for a [TextField] with an indicator line at the
* bottom. You can apply it to a [BasicTextField] using [DecorationBox] to create a custom text
* field based on the styling of a Material filled text field. The [TextField] component applies
@@ -191,6 +322,10 @@
* For example, if you wish to customise the bottom indicator line, you can pass a custom
* [Container] to this decoration box's [container].
*
+ * This decoration box is meant to be used in conjunction with overloads of [BasicTextField]
+ * that accept a `decorationBox` parameter. For other overloads of [BasicTextField] that use a
+ * [TextFieldDecorator], see [decorator].
+ *
* An example of building a custom text field using [DecorationBox]:
*
* @sample androidx.compose.material3.samples.CustomTextFieldBasedOnDecorationBox
@@ -273,11 +408,17 @@
)
}
) {
+ val visualText =
+ remember(value, visualTransformation) {
+ visualTransformation.filter(AnnotatedString(value))
+ }
+ .text
+ .text
+
CommonDecorationBox(
type = TextFieldType.Filled,
- value = value,
+ visualText = visualText,
innerTextField = innerTextField,
- visualTransformation = visualTransformation,
placeholder = placeholder,
label = label,
leadingIcon = leadingIcon,
@@ -753,6 +894,124 @@
val FocusedBorderThickness = 2.dp
/**
+ * A decorator used to create custom text fields based on <a
+ * href="https://m3.material.io/components/text-fields/overview" class="external"
+ * target="_blank">Material Design outlined text field</a>.
+ *
+ * If your text field requires customising elements that aren't exposed by [OutlinedTextField],
+ * such as the border thickness, consider using this decorator to achieve the desired design.
+ *
+ * For example, if you wish to customize the thickness of the border, you can pass a custom
+ * [Container] to this decoration box's [container].
+ *
+ * This decorator is meant to be used in conjunction with the overload of [BasicTextField] that
+ * accepts a [TextFieldDecorator] parameter. For other overloads of [BasicTextField] that use a
+ * `decorationBox`, see [DecorationBox].
+ *
+ * An example of building a custom text field using [decorator]:
+ *
+ * @sample androidx.compose.material3.samples.CustomOutlinedTextFieldUsingDecorator
+ * @param state [TextFieldState] object that holds the internal editing state of the text field.
+ * @param enabled the enabled state of the text field. When `false`, this decorator will appear
+ * visually disabled. This must be the same value that is passed to [BasicTextField].
+ * @param lineLimits whether the text field is [SingleLine] or [MultiLine]. This must be the
+ * same value that is passed to [BasicTextField].
+ * @param outputTransformation [OutputTransformation] that transforms how the contents of the
+ * text field are presented. This must be the same value that is passed to [BasicTextField].
+ * @param interactionSource the read-only [InteractionSource] representing the stream of
+ * [Interaction]s for this text field. You must first create and pass in your own `remember`ed
+ * [MutableInteractionSource] instance to the [BasicTextField] for it to dispatch events. And
+ * then pass the same instance to this decorator to observe [Interaction]s and customize the
+ * appearance/behavior of the text field in different states.
+ * @param label the optional label to be displayed with this text field. The default text style
+ * uses [Typography.bodySmall] when the text field is in focus and [Typography.bodyLarge] when
+ * the text field is not in focus.
+ * @param placeholder the optional placeholder to be displayed when the input text is empty. The
+ * default text style uses [Typography.bodyLarge].
+ * @param leadingIcon the optional leading icon to be displayed at the beginning of the text
+ * field container.
+ * @param trailingIcon the optional trailing icon to be displayed at the end of the text field
+ * container.
+ * @param prefix the optional prefix to be displayed before the input text in the text field.
+ * @param suffix the optional suffix to be displayed after the input text in the text field.
+ * @param supportingText the optional supporting text to be displayed below the text field.
+ * @param isError indicates if the text field's current value is in an error state. When `true`,
+ * this decorator will display its contents in an error color.
+ * @param colors [TextFieldColors] that will be used to resolve the colors used for this text
+ * field decorator in different states. See [OutlinedTextFieldDefaults.colors].
+ * @param contentPadding the padding between the input field and the surrounding elements of the
+ * decorator. Note that the padding values may not be respected if they are incompatible with
+ * the text field's size constraints or layout. See
+ * [OutlinedTextFieldDefaults.contentPadding].
+ * @param container the container to be drawn behind the text field. By default, this is
+ * transparent and only includes a border. The cutout in the border to fit the [label] will be
+ * automatically added by the framework. Default colors for the container come from the
+ * [colors].
+ */
+ @Composable
+ @ExperimentalMaterial3Api
+ fun decorator(
+ state: TextFieldState,
+ enabled: Boolean,
+ lineLimits: TextFieldLineLimits,
+ outputTransformation: OutputTransformation?,
+ interactionSource: InteractionSource,
+ label: @Composable (() -> Unit)? = null,
+ placeholder: @Composable (() -> Unit)? = null,
+ leadingIcon: @Composable (() -> Unit)? = null,
+ trailingIcon: @Composable (() -> Unit)? = null,
+ prefix: @Composable (() -> Unit)? = null,
+ suffix: @Composable (() -> Unit)? = null,
+ supportingText: @Composable (() -> Unit)? = null,
+ isError: Boolean = false,
+ colors: TextFieldColors = colors(),
+ contentPadding: PaddingValues = contentPadding(),
+ container: @Composable () -> Unit = {
+ Container(
+ enabled = enabled,
+ isError = isError,
+ interactionSource = interactionSource,
+ colors = colors,
+ shape = shape,
+ focusedBorderThickness = FocusedBorderThickness,
+ unfocusedBorderThickness = UnfocusedBorderThickness,
+ )
+ }
+ ): TextFieldDecorator = TextFieldDecorator { innerTextField ->
+ val visualText =
+ if (outputTransformation == null) state.text
+ else {
+ // TODO: use constructor to create TextFieldBuffer from TextFieldState when
+ // available
+ lateinit var buffer: TextFieldBuffer
+ state.edit { buffer = this }
+ // after edit completes, mutations on buffer are ineffective
+ with(outputTransformation) { buffer.transformOutput() }
+ buffer.asCharSequence()
+ }
+
+ CommonDecorationBox(
+ type = TextFieldType.Outlined,
+ visualText = visualText,
+ innerTextField = innerTextField,
+ placeholder = placeholder,
+ label = label,
+ leadingIcon = leadingIcon,
+ trailingIcon = trailingIcon,
+ prefix = prefix,
+ suffix = suffix,
+ supportingText = supportingText,
+ singleLine = lineLimits == SingleLine,
+ enabled = enabled,
+ isError = isError,
+ interactionSource = interactionSource,
+ colors = colors,
+ contentPadding = contentPadding,
+ container = container
+ )
+ }
+
+ /**
* Composable that draws a default container for an [OutlinedTextField] with a border stroke.
* You can apply it to a [BasicTextField] using [DecorationBox] to create a custom text field
* based on the styling of a Material outlined text field. The [OutlinedTextField] component
@@ -813,6 +1072,10 @@
* For example, if you wish to customize the thickness of the border, you can pass a custom
* [Container] to this decoration box's [container].
*
+ * This decoration box is meant to be used in conjunction with overloads of [BasicTextField]
+ * that accept a `decorationBox` parameter. For other overloads of [BasicTextField] that use a
+ * [TextFieldDecorator], see [decorator].
+ *
* An example of building a custom text field using [DecorationBox]:
*
* @sample androidx.compose.material3.samples.CustomOutlinedTextFieldBasedOnDecorationBox
@@ -889,10 +1152,16 @@
)
}
) {
+ val visualText =
+ remember(value, visualTransformation) {
+ visualTransformation.filter(AnnotatedString(value))
+ }
+ .text
+ .text
+
CommonDecorationBox(
type = TextFieldType.Outlined,
- value = value,
- visualTransformation = visualTransformation,
+ visualText = visualText,
innerTextField = innerTextField,
placeholder = placeholder,
label = label,
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/TextFieldImpl.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/TextFieldImpl.kt
index 5a3e768..d4bb175 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/TextFieldImpl.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/TextFieldImpl.kt
@@ -57,9 +57,7 @@
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.semantics.error
import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.lerp
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
@@ -71,40 +69,31 @@
Outlined
}
-/** Implementation of the [TextField] and [OutlinedTextField] */
@Composable
internal fun CommonDecorationBox(
type: TextFieldType,
- value: String,
+ visualText: CharSequence,
innerTextField: @Composable () -> Unit,
- visualTransformation: VisualTransformation,
label: @Composable (() -> Unit)?,
- placeholder: @Composable (() -> Unit)? = null,
- leadingIcon: @Composable (() -> Unit)? = null,
- trailingIcon: @Composable (() -> Unit)? = null,
- prefix: @Composable (() -> Unit)? = null,
- suffix: @Composable (() -> Unit)? = null,
- supportingText: @Composable (() -> Unit)? = null,
- singleLine: Boolean = false,
- enabled: Boolean = true,
- isError: Boolean = false,
+ placeholder: @Composable (() -> Unit)?,
+ leadingIcon: @Composable (() -> Unit)?,
+ trailingIcon: @Composable (() -> Unit)?,
+ prefix: @Composable (() -> Unit)?,
+ suffix: @Composable (() -> Unit)?,
+ supportingText: @Composable (() -> Unit)?,
+ singleLine: Boolean,
+ enabled: Boolean,
+ isError: Boolean,
interactionSource: InteractionSource,
contentPadding: PaddingValues,
colors: TextFieldColors,
container: @Composable () -> Unit,
) {
- val transformedText =
- remember(value, visualTransformation) {
- visualTransformation.filter(AnnotatedString(value))
- }
- .text
- .text
-
val isFocused = interactionSource.collectIsFocusedAsState().value
val inputState =
when {
isFocused -> InputPhase.Focused
- transformedText.isEmpty() -> InputPhase.UnfocusedEmpty
+ visualText.isEmpty() -> InputPhase.UnfocusedEmpty
else -> InputPhase.UnfocusedNotEmpty
}
@@ -155,7 +144,7 @@
derivedStateOf(structuralEqualityPolicy()) { placeholderAlpha.value > 0f }
}
val decoratedPlaceholder: @Composable ((Modifier) -> Unit)? =
- if (placeholder != null && transformedText.isEmpty() && showPlaceholder) {
+ if (placeholder != null && visualText.isEmpty() && showPlaceholder) {
@Composable { modifier ->
Box(modifier.graphicsLayer { alpha = placeholderAlpha.value }) {
Decoration(
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.kt
index 55f08fb..4c00359 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.kt
@@ -74,7 +74,7 @@
infix fun DeviceConfigurationOverride.then(
other: DeviceConfigurationOverride
): DeviceConfigurationOverride = DeviceConfigurationOverride { contentUnderTest ->
- this.Override { other.Override { contentUnderTest() } }
+ this.Override { other.Override(contentUnderTest) }
}
/**
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/LayoutCoordinatesBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/LayoutCoordinatesBenchmark.kt
new file mode 100644
index 0000000..c5f313a3
--- /dev/null
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/LayoutCoordinatesBenchmark.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2024 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.ui.benchmark
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.testutils.ComposeTestCase
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.doFramesUntilNoChangesPending
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class LayoutCoordinatesBenchmark {
+
+ @get:Rule val benchmarkRule = ComposeBenchmarkRule()
+
+ @Test
+ fun localPositionOfWithLayer() {
+ benchmarkRule.runBenchmarkFor({ LayoutCoordinatesTestCase(true) }) {
+ benchmarkRule.runOnUiThread { doFramesUntilNoChangesPending() }
+
+ benchmarkRule.measureRepeatedOnUiThread {
+ val testCase = getTestCase()
+ testCase.coordinates1.localPositionOf(testCase.coordinates2)
+ testCase.coordinates2.localPositionOf(testCase.coordinates1)
+ }
+ }
+ }
+
+ @Test
+ fun localPositionOfNoLayer() {
+ benchmarkRule.runBenchmarkFor({ LayoutCoordinatesTestCase(false) }) {
+ benchmarkRule.runOnUiThread { doFramesUntilNoChangesPending() }
+
+ benchmarkRule.measureRepeatedOnUiThread {
+ val testCase = getTestCase()
+ testCase.coordinates1.localPositionOf(testCase.coordinates2)
+ testCase.coordinates2.localPositionOf(testCase.coordinates1)
+ }
+ }
+ }
+
+ private class LayoutCoordinatesTestCase(val useLayer: Boolean) : ComposeTestCase {
+ lateinit var coordinates1: LayoutCoordinates
+ lateinit var coordinates2: LayoutCoordinates
+
+ @Composable
+ private fun NestedContent(depth: Int, isFirst: Boolean) {
+ if (depth == 0) {
+ Box(
+ Modifier.fillMaxSize().onPlaced {
+ if (isFirst) coordinates1 = it else coordinates2 = it
+ }
+ )
+ } else {
+ val modifier = if (useLayer) Modifier.graphicsLayer {} else Modifier
+ Box(modifier.padding(1.dp)) { NestedContent(depth - 1, isFirst) }
+ }
+ }
+
+ @Composable
+ override fun Content() {
+ Column(Modifier.fillMaxSize()) {
+ Box(Modifier.weight(1f).fillMaxWidth()) { NestedContent(10, true) }
+ Box(Modifier.weight(1f).fillMaxWidth()) { NestedContent(10, false) }
+ }
+ }
+ }
+}
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposeOneFingerInputUIOnlyBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposeOneFingerInputUIOnlyBenchmark.kt
index 0281c2b..278ffba 100644
--- a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposeOneFingerInputUIOnlyBenchmark.kt
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposeOneFingerInputUIOnlyBenchmark.kt
@@ -119,7 +119,7 @@
// half height of an item + top of the chosen item = middle of the chosen item
val y = (ItemHeightPx / 2) + (item * ItemHeightPx)
val xDown = 0f
- val xMoveInitial = xDown + MOVE_AMOUNT_PX
+ val xMoveInitial = xDown + POINTER_INPUT_MOVE_AMOUNT_PX
benchmarkRule.runBenchmarkFor({ ComposeTapTestCase() }) {
lateinit var case: ComposeTapTestCase
@@ -147,7 +147,7 @@
)
val (time, x, moves) =
- createMoves(
+ createMoveMotionEvents(
initialX = xMoveInitial,
initialTime = 100,
y = y,
@@ -190,117 +190,6 @@
}
}
- private fun createMoves(
- initialX: Float,
- initialTime: Int,
- y: Float, // Same Y used for all moves
- rootView: View,
- numberOfMoveEvents: Int,
- enableFlingStyleHistory: Boolean = false,
- timeDelta: Int = 100,
- moveDelta: Float = MOVE_AMOUNT_PX
- ): Triple<Int, Float, Array<MotionEvent>> {
- var time = initialTime
- var x = initialX
-
- val moveMotionEvents =
- Array(numberOfMoveEvents) { index ->
- val move =
- if (enableFlingStyleHistory) {
- val historicalEventCount =
- numberOfHistoricalEventsBasedOnArrayLocation(index)
-
- // Move the time and x to the last known time (to start the historical time)
- // and offset them so they do not conflict with the last Down or Move event.
- var historicalTime: Int = time - timeDelta + 10
-
- var accountForMoveOffset = -1
- var historicalX =
- if (moveDelta > 0) {
- // accountForMoveOffset stays -1 (to account for +1 offset [below])
- x - moveDelta + 1
- } else {
- // accountForMoveOffset changes to 1 (to account for -1 offset
- // [below])
- accountForMoveOffset = 1
- x - moveDelta - 1
- }
-
- val historicalTimeDelta: Int = (timeDelta - 10) / historicalEventCount
- val historicalXDelta: Float =
- (moveDelta + accountForMoveOffset) / historicalEventCount
-
- // First "historical" event (it will be pushed into history once another is
- // added via `addBatch()`).
- val moveWithHistory =
- MotionEvent(
- historicalTime,
- MotionEvent.ACTION_MOVE,
- 1,
- 0,
- arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(historicalX, y)),
- rootView
- )
-
- // Start on the second historical event (1), since the event added when we
- // created the [MotionEvent] above will be pushed into the history and will
- // then become the first historical event.
- for (historyIndex in 1 until historicalEventCount) {
- historicalTime += historicalTimeDelta
- historicalX += historicalXDelta
-
- moveWithHistory.addBatch(
- historicalTime.toLong(),
- arrayOf(PointerCoords(historicalX, y)),
- 0
- )
- }
-
- // Since The event's current location, position and size are updated to
- // the last values added via `addBatch()`, we need to add the main
- // [MotionEvent] time, x, and y values last.
- moveWithHistory.addBatch(time.toLong(), arrayOf(PointerCoords(x, y)), 0)
- // return move event with history added
- moveWithHistory
- } else {
- MotionEvent(
- time,
- MotionEvent.ACTION_MOVE,
- 1,
- 0,
- arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(x, y)),
- rootView
- )
- }
-
- time += timeDelta
- x += moveDelta
- move
- }
- return Triple(time, x, moveMotionEvents)
- }
-
- /*
- * Based on traces of fling events, the first events in a series of "MOVES" have more
- * historical [MotionEvent]s than the subsequent events.
- *
- * Remember, historical events within a [MotionEvent] represent extra [MotionEvent]s
- * that occurred faster than the refresh rate of the phone. A fling will have many more events
- * in the beginning (and between the refresh rate since they are happening so quick) than in
- * the end.
- */
- private fun numberOfHistoricalEventsBasedOnArrayLocation(index: Int): Int {
- return when (index) {
- 0 -> 12
- 1 -> 9
- 2,
- 3 -> 4
- else -> 2
- }
- }
-
private class ComposeTapTestCase : ComposeTestCase {
private var itemHeightDp: Dp? = null // Is set to correct value during composition.
var actualPressCount = 0
@@ -362,8 +251,4 @@
)
}
}
-
- companion object {
- private const val MOVE_AMOUNT_PX = 10f
- }
}
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/utils.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/utils.kt
index 190917e..68d39ca 100644
--- a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/utils.kt
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/utils.kt
@@ -20,6 +20,8 @@
import android.view.MotionEvent
import android.view.View
+internal const val POINTER_INPUT_MOVE_AMOUNT_PX = 10f
+
/**
* Creates a simple [MotionEvent].
*
@@ -86,3 +88,128 @@
this.x = x
this.y = y
}
+
+/**
+ * Creates an array of subsequent [MotionEvent.ACTION_MOVE]s to pair with a
+ * [MotionEvent.ACTION_DOWN] and a [MotionEvent.ACTION_UP] to recreate a user input sequence.
+ *
+ * @param initialX Starting x coordinate for the first [MotionEvent.ACTION_MOVE]
+ * @param initialTime Starting time for the first [MotionEvent.ACTION_MOVE]
+ * @param y - Y used for all [MotionEvent.ACTION_MOVE]s (only x is updated for each moves).
+ * @param rootView - [View] that the [MotionEvent] is dispatched to.
+ * @param numberOfMoveEvents Number of [MotionEvent.ACTION_MOVE]s to create.
+ * @param enableFlingStyleHistory - Adds a history of [MotionEvent.ACTION_MOVE]s to each
+ * [MotionEvent.ACTION_MOVE] to mirror a fling event (where you will get more
+ * [MotionEvent.ACTION_MOVE]s than the refresh rate of the phone).
+ * @param timeDelta - Time between each [MotionEvent.ACTION_MOVE] in milliseconds.
+ * @param moveDelta - Amount to move in pixels for each [MotionEvent.ACTION_MOVE]
+ */
+internal fun createMoveMotionEvents(
+ initialX: Float,
+ initialTime: Int,
+ y: Float, // Same Y used for all moves
+ rootView: View,
+ numberOfMoveEvents: Int,
+ enableFlingStyleHistory: Boolean = false,
+ timeDelta: Int = 100,
+ moveDelta: Float = POINTER_INPUT_MOVE_AMOUNT_PX
+): Triple<Int, Float, Array<MotionEvent>> {
+ var time = initialTime
+ var x = initialX
+
+ val moveMotionEvents =
+ Array(numberOfMoveEvents) { index ->
+ val move =
+ if (enableFlingStyleHistory) {
+ val historicalEventCount = numberOfHistoricalEventsBasedOnArrayLocation(index)
+
+ // Move the time and x to the last known time (to start the historical time)
+ // and offset them so they do not conflict with the last Down or Move event.
+ var historicalTime: Int = time - timeDelta + 10
+
+ var accountForMoveOffset = -1
+ var historicalX =
+ if (moveDelta > 0) {
+ // accountForMoveOffset stays -1 (to account for +1 offset [below])
+ x - moveDelta + 1
+ } else {
+ // accountForMoveOffset changes to 1 (to account for -1 offset
+ // [below])
+ accountForMoveOffset = 1
+ x - moveDelta - 1
+ }
+
+ val historicalTimeDelta: Int = (timeDelta - 10) / historicalEventCount
+ val historicalXDelta: Float =
+ (moveDelta + accountForMoveOffset) / historicalEventCount
+
+ // First "historical" event (it will be pushed into history once another is
+ // added via `addBatch()`).
+ val moveWithHistory =
+ MotionEvent(
+ historicalTime,
+ MotionEvent.ACTION_MOVE,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(historicalX, y)),
+ rootView
+ )
+
+ // Start on the second historical event (1), since the event added when we
+ // created the [MotionEvent] above will be pushed into the history and will
+ // then become the first historical event.
+ for (historyIndex in 1 until historicalEventCount) {
+ historicalTime += historicalTimeDelta
+ historicalX += historicalXDelta
+
+ moveWithHistory.addBatch(
+ historicalTime.toLong(),
+ arrayOf(PointerCoords(historicalX, y)),
+ 0
+ )
+ }
+
+ // Since The event's current location, position and size are updated to
+ // the last values added via `addBatch()`, we need to add the main
+ // [MotionEvent] time, x, and y values last.
+ moveWithHistory.addBatch(time.toLong(), arrayOf(PointerCoords(x, y)), 0)
+ // return move event with history added
+ moveWithHistory
+ } else {
+ MotionEvent(
+ time,
+ MotionEvent.ACTION_MOVE,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(x, y)),
+ rootView
+ )
+ }
+
+ time += timeDelta
+ x += moveDelta
+ move
+ }
+ return Triple(time, x, moveMotionEvents)
+}
+
+/*
+ * Based on traces of fling events, the first events in a series of "MOVES" have more
+ * historical [MotionEvent]s than the subsequent events.
+ *
+ * Remember, historical events within a [MotionEvent] represent extra [MotionEvent]s
+ * that occurred faster than the refresh rate of the phone. A fling will have many more events
+ * in the beginning (and between the refresh rate since they are happening so quick) than in
+ * the end.
+ */
+internal fun numberOfHistoricalEventsBasedOnArrayLocation(index: Int): Int {
+ return when (index) {
+ 0 -> 12
+ 1 -> 9
+ 2,
+ 3 -> 4
+ else -> 2
+ }
+}
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/RulerTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/RulerTest.kt
index 6083ac6..edd8b302 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/RulerTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/RulerTest.kt
@@ -21,6 +21,7 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.absoluteOffset
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
@@ -635,7 +636,8 @@
var rulerChanged = CountDownLatch(1)
rule.setContent {
Box(
- Modifier.onPlaced { rootX = it.positionInWindow().x }
+ Modifier.fillMaxSize()
+ .onPlaced { rootX = it.positionInWindow().x }
.offset { IntOffset(offset, 0) }
) {
AndroidView(
@@ -659,12 +661,13 @@
) {
Box(
Modifier.layout { measurable, constraints ->
- val p = measurable.measure(constraints)
- layout(p.width, p.height) {
- rulerValue = verticalRuler.current(Float.NaN)
- rulerChanged.countDown()
+ val p = measurable.measure(constraints)
+ layout(p.width, p.height) {
+ rulerValue = verticalRuler.current(Float.NaN)
+ rulerChanged.countDown()
+ }
}
- }
+ .fillMaxSize(0.5f)
)
}
}
@@ -678,7 +681,6 @@
assertThat(rulerValue).isWithin(0.01f).of(-rootX)
rulerChanged = CountDownLatch(1)
offset = 100
- rule.activity.window.decorView.invalidate()
}
assertThat(rulerChanged.await(1, TimeUnit.SECONDS)).isTrue()
rule.runOnIdle { assertThat(rulerValue).isWithin(0.01f).of(-100f - rootX) }
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
index 88b3f3d..8e3e45e 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
@@ -1230,9 +1230,10 @@
fun removeAndroidView(view: AndroidViewHolder) {
registerOnEndApplyChangesListener {
androidViewsHandler.removeViewInLayout(view)
- androidViewsHandler.layoutNodeToHolder.remove(
- androidViewsHandler.holderToLayoutNode.remove(view)
- )
+ val layoutNode = androidViewsHandler.holderToLayoutNode.remove(view)
+ if (layoutNode != null) {
+ androidViewsHandler.layoutNodeToHolder.remove(layoutNode)
+ }
view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO)
}
}
@@ -1296,6 +1297,7 @@
requestLayout()
}
measureAndLayoutDelegate.dispatchOnPositionedCallbacks()
+ _androidViewsHandler?.layoutChildViewsIfNeeded()
dispatchPendingInteropLayoutCallbacks()
}
}
@@ -1309,6 +1311,7 @@
// it allows us to not traverse the hierarchy twice.
if (!measureAndLayoutDelegate.hasPendingMeasureOrLayout) {
measureAndLayoutDelegate.dispatchOnPositionedCallbacks()
+ _androidViewsHandler?.layoutChildViewsIfNeeded()
dispatchPendingInteropLayoutCallbacks()
}
}
@@ -1434,6 +1437,7 @@
// View is not yet laid out.
updatePositionCacheAndDispatch()
if (_androidViewsHandler != null) {
+ androidViewsHandler.layoutChildViewsIfNeeded()
// Even if we laid out during onMeasure, we want to set the bounds of the
// AndroidViewsHandler for accessibility and for Views making assumptions based on
// the size of their ancestors. Usually the Views in the hierarchy will not
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidViewsHandler.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidViewsHandler.android.kt
index 40eb0306..6df225f 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidViewsHandler.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidViewsHandler.android.kt
@@ -24,6 +24,7 @@
import android.view.View.MeasureSpec.EXACTLY
import android.view.View.MeasureSpec.getMode
import android.view.ViewGroup
+import androidx.collection.mutableScatterMapOf
import androidx.compose.ui.internal.requirePrecondition
import androidx.compose.ui.node.LayoutNode
import androidx.compose.ui.viewinterop.AndroidViewHolder
@@ -38,8 +39,8 @@
clipChildren = false
}
- val holderToLayoutNode = hashMapOf<AndroidViewHolder, LayoutNode>()
- val layoutNodeToHolder = hashMapOf<LayoutNode, AndroidViewHolder>()
+ val holderToLayoutNode = mutableScatterMapOf<AndroidViewHolder, LayoutNode>()
+ val layoutNodeToHolder = mutableScatterMapOf<LayoutNode, AndroidViewHolder>()
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
// Layout will be handled by component nodes. However, we act like proper measurement
@@ -57,14 +58,18 @@
// Remeasure children, such that, if ViewRootImpl did forceLayout(), the holders
// will be set PFLAG_LAYOUT_REQUIRED and they will be relaid out during the next layout.
// This will ensure that the need relayout flags will be cleared correctly.
- holderToLayoutNode.keys.forEach { it.remeasure() }
+ holderToLayoutNode.forEachKey { it.remeasure() }
+ }
+
+ fun layoutChildViewsIfNeeded() {
+ holderToLayoutNode.forEachKey { androidViewHolder -> androidViewHolder.layoutIfNeeded() }
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
// Layout was already handled by component nodes, but replace here because
// the View system has forced relayout on children. This method will only be called
// when forceLayout is called on the Views hierarchy.
- holderToLayoutNode.keys.forEach { it.layout(it.left, it.top, it.right, it.bottom) }
+ holderToLayoutNode.forEachKey { it.layout(it.left, it.top, it.right, it.bottom) }
}
// No call to super to avoid invalidating the AndroidComposeView and the handler, and rely on
@@ -91,7 +96,7 @@
// requestLayout() was called by a child, so we have to request remeasurement for
// their corresponding layout node.
for (i in 0 until childCount) {
- val child = getChildAt(i)
+ val child = getChildAt(i) as AndroidViewHolder
val node = holderToLayoutNode[child]
if (child.isLayoutRequested && node != null) {
node.requestRemeasure()
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GraphicsLayerOwnerLayer.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GraphicsLayerOwnerLayer.android.kt
index 2f492d15..9a2afc9 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GraphicsLayerOwnerLayer.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GraphicsLayerOwnerLayer.android.kt
@@ -34,6 +34,7 @@
import androidx.compose.ui.graphics.drawscope.CanvasDrawScope
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
+import androidx.compose.ui.graphics.isIdentity
import androidx.compose.ui.graphics.layer.CompositingStrategy
import androidx.compose.ui.graphics.layer.GraphicsLayer
import androidx.compose.ui.graphics.layer.drawLayer
@@ -83,6 +84,9 @@
* somewhat transparent (i.e. alpha less than 1.0f)
*/
private var softwareLayerPaint: Paint? = null
+ private var isMatrixDirty = false
+ private var isInverseMatrixDirty = false
+ private var isIdentity = true
override fun updateLayerProperties(scope: ReusableGraphicsLayerScope) {
val maybeChangedFields = scope.mutatedFields or mutatedFields
@@ -161,6 +165,10 @@
else -> throw IllegalStateException("Not supported composition strategy")
}
}
+ if (maybeChangedFields and Fields.MatrixAffectingFields != 0) {
+ isMatrixDirty = true
+ isInverseMatrixDirty = true
+ }
var outlineChanged = false
@@ -314,23 +322,27 @@
}
override fun mapOffset(point: Offset, inverse: Boolean): Offset {
- return if (inverse) {
- getInverseMatrix()?.map(point) ?: Offset.Infinite
+ val matrix =
+ if (inverse) {
+ getInverseMatrix() ?: return Offset.Infinite
+ } else {
+ getMatrix()
+ }
+ return if (isIdentity) {
+ point
} else {
- getMatrix().map(point)
+ matrix.map(point)
}
}
override fun mapBounds(rect: MutableRect, inverse: Boolean) {
- if (inverse) {
- val matrix = getInverseMatrix()
+ val matrix = if (inverse) getInverseMatrix() else getMatrix()
+ if (!isIdentity) {
if (matrix == null) {
rect.set(0f, 0f, 0f, 0f)
} else {
matrix.map(rect)
}
- } else {
- getMatrix().map(rect)
}
}
@@ -383,38 +395,53 @@
}
private fun getInverseMatrix(): Matrix? {
- val matrix = getMatrix()
val inverseMatrix = inverseMatrixCache ?: Matrix().also { inverseMatrixCache = it }
- return if (matrix.invertTo(inverseMatrix)) {
+ if (!isInverseMatrixDirty) {
+ if (inverseMatrix[0, 0].isNaN()) {
+ return null
+ }
+ return inverseMatrix
+ }
+ isInverseMatrixDirty = false
+ val matrix = getMatrix()
+ return if (isIdentity) {
+ matrix
+ } else if (matrix.invertTo(inverseMatrix)) {
inverseMatrix
} else {
+ inverseMatrix[0, 0] = Float.NaN
null
}
}
- private fun updateMatrix() =
- with(graphicsLayer) {
- val (x, y) =
- if (pivotOffset.isUnspecified) {
- this@GraphicsLayerOwnerLayer.size.toSize().center
- } else {
- pivotOffset
- }
+ private fun updateMatrix() {
+ if (isMatrixDirty) {
+ with(graphicsLayer) {
+ val (x, y) =
+ if (pivotOffset.isUnspecified) {
+ this@GraphicsLayerOwnerLayer.size.toSize().center
+ } else {
+ pivotOffset
+ }
- matrixCache.resetToPivotedTransform(
- x,
- y,
- translationX,
- translationY,
- 1.0f,
- rotationX,
- rotationY,
- rotationZ,
- scaleX,
- scaleY,
- 1.0f
- )
+ matrixCache.resetToPivotedTransform(
+ x,
+ y,
+ translationX,
+ translationY,
+ 1.0f,
+ rotationX,
+ rotationY,
+ rotationZ,
+ scaleX,
+ scaleY,
+ 1.0f
+ )
+ }
+ isMatrixDirty = false
+ isIdentity = matrixCache.isIdentity()
}
+ }
/**
* Manually clips the content of the RenderNodeLayer in the provided canvas. This is used only
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/LayerMatrixCache.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/LayerMatrixCache.android.kt
index dd372bc..3438c74 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/LayerMatrixCache.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/LayerMatrixCache.android.kt
@@ -17,7 +17,10 @@
package androidx.compose.ui.platform
import android.graphics.Matrix as AndroidMatrix
+import androidx.compose.ui.geometry.MutableRect
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Matrix
+import androidx.compose.ui.graphics.isIdentity
import androidx.compose.ui.graphics.setFrom
/**
@@ -33,12 +36,13 @@
) {
private var androidMatrixCache: AndroidMatrix? = null
private var previousAndroidMatrix: AndroidMatrix? = null
- private var matrixCache: Matrix? = null
- private var inverseMatrixCache: Matrix? = null
+ private var matrixCache: Matrix = Matrix()
+ private var inverseMatrixCache: Matrix = Matrix()
- private var isDirty = true
- private var isInverseDirty = true
+ private var isDirty = false
+ private var isInverseDirty = false
private var isInverseValid = true
+ private var isIdentity = true
/**
* Ensures that the internal matrix will be updated next time [calculateMatrix] or
@@ -54,7 +58,7 @@
* Returns the cached [Matrix], updating it if required (if [invalidate] was previously called).
*/
fun calculateMatrix(target: T): Matrix {
- val matrix = matrixCache ?: Matrix().also { matrixCache = it }
+ val matrix = matrixCache
if (!isDirty) {
return matrix
}
@@ -71,6 +75,7 @@
}
isDirty = false
+ isIdentity = matrix.isIdentity()
return matrix
}
@@ -80,7 +85,7 @@
* when scaling is 0.
*/
fun calculateInverseMatrix(target: T): Matrix? {
- val matrix = inverseMatrixCache ?: Matrix().also { inverseMatrixCache = it }
+ val matrix = inverseMatrixCache
if (isInverseDirty) {
val normalMatrix = calculateMatrix(target)
isInverseValid = normalMatrix.invertTo(matrix)
@@ -88,4 +93,40 @@
}
return if (isInverseValid) matrix else null
}
+
+ fun map(target: T, rect: MutableRect) {
+ val matrix = calculateMatrix(target)
+ if (!isIdentity) {
+ matrix.map(rect)
+ }
+ }
+
+ fun mapInverse(target: T, rect: MutableRect) {
+ val matrix = calculateInverseMatrix(target)
+ if (matrix == null) {
+ rect.set(0f, 0f, 0f, 0f)
+ } else if (!isIdentity) {
+ matrix.map(rect)
+ }
+ }
+
+ fun map(target: T, offset: Offset): Offset {
+ val matrix = calculateMatrix(target)
+ return if (!isIdentity) {
+ matrix.map(offset)
+ } else {
+ offset
+ }
+ }
+
+ fun mapInverse(target: T, offset: Offset): Offset {
+ val matrix = calculateInverseMatrix(target)
+ return if (matrix == null) {
+ Offset.Infinite
+ } else if (!isIdentity) {
+ matrix.map(offset)
+ } else {
+ offset
+ }
+ }
}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeLayer.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeLayer.android.kt
index 3217154..f862297 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeLayer.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeLayer.android.kt
@@ -340,22 +340,17 @@
override fun mapOffset(point: Offset, inverse: Boolean): Offset {
return if (inverse) {
- matrixCache.calculateInverseMatrix(renderNode)?.map(point) ?: Offset.Infinite
+ matrixCache.mapInverse(renderNode, point)
} else {
- matrixCache.calculateMatrix(renderNode).map(point)
+ matrixCache.map(renderNode, point)
}
}
override fun mapBounds(rect: MutableRect, inverse: Boolean) {
if (inverse) {
- val matrix = matrixCache.calculateInverseMatrix(renderNode)
- if (matrix == null) {
- rect.set(0f, 0f, 0f, 0f)
- } else {
- matrix.map(rect)
- }
+ matrixCache.mapInverse(renderNode, rect)
} else {
- matrixCache.calculateMatrix(renderNode).map(rect)
+ matrixCache.map(renderNode, rect)
}
}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/SemanticsUtils.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/SemanticsUtils.android.kt
index 2d633c1..c97f4b0 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/SemanticsUtils.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/SemanticsUtils.android.kt
@@ -144,8 +144,14 @@
)
/** This function retrieves the View corresponding to a semanticsId, if it exists. */
-internal fun AndroidViewsHandler.semanticsIdToView(id: Int): View? =
- layoutNodeToHolder.entries.firstOrNull { it.key.semanticsId == id }?.value
+internal fun AndroidViewsHandler.semanticsIdToView(id: Int): View? {
+ layoutNodeToHolder.forEach { key, value ->
+ if (key.semanticsId == id) {
+ return value
+ }
+ }
+ return null
+}
// TODO(mnuzen): refactor `currentSemanticsNodes` in the AccessibilityDelegate file to also use
// IntObjectMap's. Then ACVADC can also call `getAllUncoveredSemanticsNodesToIntObjectMap` instead
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt
index 4caf691..9ec051d 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt
@@ -364,22 +364,17 @@
override fun mapOffset(point: Offset, inverse: Boolean): Offset {
return if (inverse) {
- matrixCache.calculateInverseMatrix(this)?.map(point) ?: Offset.Infinite
+ matrixCache.mapInverse(this, point)
} else {
- matrixCache.calculateMatrix(this).map(point)
+ matrixCache.map(this, point)
}
}
override fun mapBounds(rect: MutableRect, inverse: Boolean) {
if (inverse) {
- val matrix = matrixCache.calculateInverseMatrix(this)
- if (matrix != null) {
- matrix.map(rect)
- } else {
- rect.set(0f, 0f, 0f, 0f)
- }
+ matrixCache.mapInverse(this, rect)
} else {
- matrixCache.calculateMatrix(this).map(rect)
+ matrixCache.map(this, rect)
}
}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
index 9dd30ec..b4b9355 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
@@ -190,6 +190,9 @@
private var isDrawing = false
+ /** `true` when the parent has requested this View to layout, but it hasn't been laid out yet */
+ private var isLayoutNeeded = false
+
override val isValidOwnerScope: Boolean
get() = isAttachedToWindow
@@ -245,6 +248,21 @@
measure(lastWidthMeasureSpec, lastHeightMeasureSpec)
}
+ /**
+ * Layout the View if a layout has been requested or do nothing if no layout has been requested.
+ */
+ fun layoutIfNeeded() {
+ if (isLayoutNeeded) {
+ isLayoutNeeded = false
+ if (isAttachedToWindow) {
+ val position = layoutNode.coordinates.positionInRoot()
+ val x = position.x.fastRoundToInt()
+ val y = position.y.fastRoundToInt()
+ layout(x, y, x + measuredWidth, y + measuredHeight)
+ }
+ }
+ }
+
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
view.layout(0, 0, r - l, b - t)
}
@@ -363,7 +381,7 @@
.onGloballyPositioned {
// The global position of this LayoutNode can change with it being replaced. For
// these cases, we need to inform the View.
- layoutAccordingTo(layoutNode)
+ isLayoutNeeded = true
@OptIn(InternalComposeUiApi::class) owner.onInteropViewLayoutChange(this)
}
layoutNode.compositeKeyHash = compositeKeyHash
@@ -411,7 +429,7 @@
layoutParams!!.height
)
)
- return layout(measuredWidth, measuredHeight) { layoutAccordingTo(layoutNode) }
+ return layout(measuredWidth, measuredHeight) { isLayoutNeeded = true }
}
override fun IntrinsicMeasureScope.minIntrinsicWidth(
@@ -584,13 +602,6 @@
}
}
-private fun View.layoutAccordingTo(layoutNode: LayoutNode) {
- val position = layoutNode.coordinates.positionInRoot()
- val x = position.x.fastRoundToInt()
- val y = position.y.fastRoundToInt()
- layout(x, y, x + measuredWidth, y + measuredHeight)
-}
-
private const val Unmeasured = Int.MIN_VALUE
/**
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt
index 0ec0516..9f7099d 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt
@@ -178,11 +178,7 @@
// TODO(b/159900354): draw a scrim and add margins around the Compose
// Dialog, and
// consume clicks so they can't pass through to the underlying UI
- DialogLayout(
- Modifier.semantics { dialog() },
- ) {
- currentContent()
- }
+ DialogLayout(Modifier.semantics { dialog() }, currentContent)
}
}
}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.android.kt
index c8a6b1a..2269723 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidPopup.android.kt
@@ -322,10 +322,9 @@
updatePosition()
}
// Hide the popup while we can't position it correctly
- .alpha(if (canCalculatePosition) 1f else 0f)
- ) {
- currentContent()
- }
+ .alpha(if (canCalculatePosition) 1f else 0f),
+ currentContent
+ )
}
}
}
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/CallsManagerTest.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/CallsManagerTest.kt
index 0c6f8f2..cd56b97 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/CallsManagerTest.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/CallsManagerTest.kt
@@ -111,6 +111,7 @@
mCallsManager.registerAppWithTelecom(CallsManager.CAPABILITY_BASELINE)
val account = mCallsManager.getBuiltPhoneAccount()!!
assertNotNull(account.extras)
+ assertTrue(account.extras.getBoolean(CallsManager.PLACEHOLDER_VALUE_ACCOUNT_BUNDLE))
if (Utils.hasPlatformV2Apis()) {
assertTrue(
Utils.hasCapability(
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/CallsManager.kt b/core/core-telecom/src/main/java/androidx/core/telecom/CallsManager.kt
index 81f271e..0110a0c 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/CallsManager.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/CallsManager.kt
@@ -183,6 +183,7 @@
internal const val PACKAGE_LABEL: String = "Telecom-Jetpack"
internal const val CONNECTION_SERVICE_CLASS =
"androidx.core.telecom.internal.JetpackConnectionService"
+ internal const val PLACEHOLDER_VALUE_ACCOUNT_BUNDLE = "isCoreTelecomAccount"
// fail messages specific to addCall
internal const val CALL_CREATION_FAILURE_MSG = "The call failed to be added."
@@ -213,7 +214,11 @@
// remap and set capabilities
phoneAccountBuilder.setCapabilities(remapJetpackCapsToPlatformCaps(capabilities))
// see b/343674176. Some OEMs expect the PhoneAccount.getExtras() to be non-null
- phoneAccountBuilder.setExtras(Bundle())
+ // see b/352526256. The bundle must contain a placeholder value. otherwise, the bundle
+ // empty bundle will be nulled out on reboot.
+ val defaultBundle = Bundle()
+ defaultBundle.putBoolean(PLACEHOLDER_VALUE_ACCOUNT_BUNDLE, true)
+ phoneAccountBuilder.setExtras(defaultBundle)
// build and register the PhoneAccount via the Platform API
mPhoneAccount = phoneAccountBuilder.build()
diff --git a/docs/api_guidelines/compat.md b/docs/api_guidelines/compat.md
index 6a72ec4..09be0694 100644
--- a/docs/api_guidelines/compat.md
+++ b/docs/api_guidelines/compat.md
@@ -380,10 +380,10 @@
In cases where a hidden API is a constant value, **do not** inline the value.
Hidden APIs cannot be tested by CTS and carry no stability guarantees.
-On earlier devices or in cases where an API is marked with
-`@UnsupportedAppUsage`, reflection on hidden platform APIs is allowed **only**
-when an alternative public platform API exists in a later revision of the
-Android SDK. For example, the following implementation is allowed:
+Per go/platform-parity, on earlier devices or in cases where an API is marked
+with `@UnsupportedAppUsage`, reflection on hidden platform APIs is allowed
+**only** when an alternative public platform API exists in a later revision of
+the Android SDK. For example, the following implementation is allowed:
```java
public AccessibilityDelegate getAccessibilityDelegate(View v) {
diff --git a/docs/onboarding.md b/docs/onboarding.md
index 6355b24..72e092b 100644
--- a/docs/onboarding.md
+++ b/docs/onboarding.md
@@ -1022,26 +1022,6 @@
Make sure the library versions are the same before and after replacement. Then
you can build the Android platform code with the new `androidx` code.
-### How do I measure library size? {#library-size}
-
-Method count and bytecode size are tracked in CI
-[alongside benchmarks](/docs/benchmarking.md#monitoring) to
-detect regressions.
-
-For local measurements, use the `:reportLibraryMetrics` task. For example:
-
-```shell
-./gradlew benchmark:benchmark-macro:reportLibraryMetrics
-cat ../../out/dist/librarymetrics/androidx.benchmark_benchmark-macro.json
-```
-
-Will output something like: `{"method_count":1256,"bytecode_size":178822}`
-
-Note: this only counts the weight of your library's jar/aar, including
-resources. It does not count library dependencies. It does not account for a
-minification step (e.g. with R8), as that is dynamic, and done at app build time
-(and depend on which entrypoints the app uses).
-
### How do I add content to a library's Overview reference doc page?
Put content in a markdown file that ends with `-documentation.md` in the
diff --git a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
index 1e4745c..0d41640 100644
--- a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
+++ b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
@@ -18,6 +18,7 @@
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static com.google.common.base.Preconditions.checkState;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
@@ -193,33 +194,88 @@
}
/**
- * Returns the number of times {@code pattern} appears in {@code source}.
+ * {@link R.raw#jpeg_with_xmp_in_exif_first_then_separate_app1} contains an Exif APP1 segment
+ * with the same XMP as {@link R.raw#jpeg_with_exif_with_xmp}, a separate XMP APP1 segment
+ * containing {@link #TEST_XMP}.
*
- * <p>Overlapping occurrences are counted multiple times, e.g. {@code countOccurrences([0, 1, 0,
- * 1, 0], [0, 1, 0])} will return 2.
+ * <p>This test asserts that the Exif XMP is returned, but that the separate XMP APP1 segment is
+ * preserved when saving.
*/
- private static int countOccurrences(byte[] source, byte[] pattern) {
- int count = 0;
- for (int i = 0; i < source.length - pattern.length; i++) {
- if (containsAtIndex(source, i, pattern)) {
- count++;
- }
- }
- return count;
+ @Test
+ @LargeTest
+ public void testJpegWithXmpInTwoSegments_exifFirst_exifXmpReturned_separateXmpPreserved()
+ throws Throwable {
+ File imageFile =
+ copyFromResourceToFile(
+ R.raw.jpeg_with_xmp_in_exif_first_then_separate_app1,
+ "jpeg_with_xmp_in_exif_first_then_separate_app1.jpg");
+ ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
+
+ String xmp =
+ new String(exifInterface.getAttributeBytes(ExifInterface.TAG_XMP), Charsets.UTF_8);
+
+ String expectedXmp =
+ ExpectedAttributes.JPEG_WITH_EXIF_WITH_XMP.getXmp(
+ getApplicationContext().getResources());
+ assertThat(xmp).isEqualTo(expectedXmp);
+
+ exifInterface.saveAttributes();
+
+ xmp =
+ new String(exifInterface.getAttributeBytes(ExifInterface.TAG_XMP), Charsets.UTF_8);
+ assertThat(xmp).isEqualTo(expectedXmp);
+ byte[] imageBytes = Files.toByteArray(imageFile);
+ assertThat(countOccurrences(imageBytes, TEST_XMP.getBytes(Charsets.UTF_8))).isEqualTo(1);
}
/**
- * Returns {@code true} if {@code source} contains {@code pattern} starting at {@code index}.
- *
- * @throws IndexOutOfBoundsException if {@code source.length < index + pattern.length}.
+ * Same as {@link
+ * #testJpegWithXmpInTwoSegments_exifFirst_exifXmpReturned_separateXmpPreserved()} but with the
+ * standalone XMP APP1 segment before the Exif one.
*/
- private static boolean containsAtIndex(byte[] source, int index, byte[] pattern) {
- for (int i = 0; i < pattern.length; i++) {
- if (pattern[i] != source[index + i]) {
- return false;
- }
- }
- return true;
+ @Test
+ @LargeTest
+ public void
+ testJpegWithXmpInTwoSegmentsWithSeparateApp1First_exifXmpReturnedSeparateXmpPreserved()
+ throws Throwable {
+ File imageFile =
+ copyFromResourceToFile(
+ R.raw.jpeg_with_xmp_in_separate_app1_first_then_exif,
+ "jpeg_with_xmp_in_separate_app1_first_then_exif.jpg");
+ ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
+
+ String xmp =
+ new String(exifInterface.getAttributeBytes(ExifInterface.TAG_XMP), Charsets.UTF_8);
+
+ String expectedXmp =
+ ExpectedAttributes.JPEG_WITH_EXIF_WITH_XMP.getXmp(
+ getApplicationContext().getResources());
+ assertThat(xmp).isEqualTo(expectedXmp);
+
+ exifInterface.saveAttributes();
+
+ xmp =
+ new String(exifInterface.getAttributeBytes(ExifInterface.TAG_XMP), Charsets.UTF_8);
+ assertThat(xmp).isEqualTo(expectedXmp);
+ byte[] imageBytes = Files.toByteArray(imageFile);
+ assertThat(countOccurrences(imageBytes, TEST_XMP.getBytes(Charsets.UTF_8))).isEqualTo(1);
+ }
+
+ @Test
+ @LargeTest
+ public void testJpeg_noXmp_addXmp_writtenInSeparateSegment() throws Throwable {
+ File imageFile =
+ copyFromResourceToFile(
+ R.raw.jpeg_with_exif_byte_order_ii, "jpeg_with_exif_byte_order_ii.jpg");
+ ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
+
+ checkState(!exifInterface.hasAttribute(ExifInterface.TAG_XMP));
+ exifInterface.setAttribute(ExifInterface.TAG_XMP, TEST_XMP);
+ exifInterface.saveAttributes();
+
+ byte[] imageBytes = Files.toByteArray(imageFile);
+ byte[] xmpApp1SegmentMarker = "http://ns.adobe.com/xap/1.0/\0".getBytes(Charsets.US_ASCII);
+ assertThat(countOccurrences(imageBytes, xmpApp1SegmentMarker)).isEqualTo(1);
}
// https://issuetracker.google.com/264729367
@@ -2129,6 +2185,36 @@
}
/**
+ * Returns the number of times {@code pattern} appears in {@code source}.
+ *
+ * <p>Overlapping occurrences are counted multiple times, e.g. {@code countOccurrences([0, 1, 0,
+ * 1, 0], [0, 1, 0])} will return 2.
+ */
+ private static int countOccurrences(byte[] source, byte[] pattern) {
+ int count = 0;
+ for (int i = 0; i < source.length - pattern.length; i++) {
+ if (containsAtIndex(source, i, pattern)) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Returns {@code true} if {@code source} contains {@code pattern} starting at {@code index}.
+ *
+ * @throws IndexOutOfBoundsException if {@code source.length < index + pattern.length}.
+ */
+ private static boolean containsAtIndex(byte[] source, int index, byte[] pattern) {
+ for (int i = 0; i < pattern.length; i++) {
+ if (pattern[i] != source[index + i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* An operation that can be applied to an {@link ExifInterface} instance.
*
* <p>We would use java.util.Consumer but it's not available before API 24, and there's no Guava
diff --git a/exifinterface/exifinterface/src/androidTest/res/raw/jpeg_with_xmp_in_exif_first_then_separate_app1.jpg b/exifinterface/exifinterface/src/androidTest/res/raw/jpeg_with_xmp_in_exif_first_then_separate_app1.jpg
new file mode 100644
index 0000000..fb1ca3e
--- /dev/null
+++ b/exifinterface/exifinterface/src/androidTest/res/raw/jpeg_with_xmp_in_exif_first_then_separate_app1.jpg
Binary files differ
diff --git a/exifinterface/exifinterface/src/androidTest/res/raw/jpeg_with_xmp_in_separate_app1_first_then_exif.jpg b/exifinterface/exifinterface/src/androidTest/res/raw/jpeg_with_xmp_in_separate_app1_first_then_exif.jpg
new file mode 100644
index 0000000..4b187fd
--- /dev/null
+++ b/exifinterface/exifinterface/src/androidTest/res/raw/jpeg_with_xmp_in_separate_app1_first_then_exif.jpg
Binary files differ
diff --git a/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java b/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
index 863584a0..e347d51 100644
--- a/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
+++ b/exifinterface/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
@@ -87,11 +87,42 @@
/**
* This is a class for reading and writing Exif tags in various image file formats.
+ *
+ * <p>Supported for reading: JPEG, PNG, WebP, HEIF, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW,
+ * RAF.
+ *
+ * <p>Supported for writing: JPEG, PNG, WebP.
+ *
* <p>
- * Supported for reading: JPEG, PNG, WebP, HEIF, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW, RAF.
- * <p>
- * Supported for writing: JPEG, PNG, WebP.
- * <p>
+ *
+ * <h3>XMP Support</h3>
+ *
+ * This class can read raw XMP data from the supported image file formats.
+ *
+ * <p>XMP data can be stored within Exif data (under tag 700), but many of the formats also define a
+ * separate storage location for XMP. ExifInterface handles this ambiguity as follows:
+ *
+ * <ul>
+ * <li>JPEG
+ * <ul>
+ * <li>The XMP spec part 3 section 3.3.2 forbids the XMP tag (700) being present in the Exif
+ * segment of JPEG files (i.e. XMP should always be in a separate APP1 segment).
+ * <li>If XMP is present in both Exif and separate segments, the XMP from the Exif segment
+ * is returned from {@link #getAttributeBytes} and modifications to the XMP with {@link
+ * #setAttribute} are written back to the XMP in the Exif segment, the XMP in the
+ * separate segment is preserved unmodified. This is contrary to the spec described
+ * above (which suggests the standalone XMP should be preferred over the XMP in the Exif
+ * segment).
+ * <li>If XMP is not present in either location, and is added with {@link #setAttribute}, it
+ * is written as a standalone segment, in line with the spec described above.
+ * </ul>
+ * <li>HEIF
+ * <ul>
+ * <li>If XMP is present in both Exif and separate segments, the XMP from the Exif segment
+ * is returned from {@link #getAttributeBytes}.
+ * </ul>
+ * </ul>
+ *
* Note: JPEG and HEIF files may contain XMP data either inside the Exif data chunk or outside of
* it. This class will search both locations for XMP data, but if XMP data exist both inside and
* outside Exif, will favor the XMP data inside Exif over the one outside.
@@ -2234,8 +2265,11 @@
public static final String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw";
/**
* Type is byte[]. See <a href=
- * "https://en.wikipedia.org/wiki/Extensible_Metadata_Platform">Extensible
- * Metadata Platform (XMP)</a> for details on contents.
+ * "https://en.wikipedia.org/wiki/Extensible_Metadata_Platform">Extensible Metadata Platform
+ * (XMP)</a> for details on contents.
+ *
+ * <p>See also notes about XMP handling in different containers in the class-level javadoc of
+ * this class.
*/
public static final String TAG_XMP = "Xmp";
/** Type is int. See JEITA CP-3451C Spec Section 3: Bilevel Images. */
@@ -5707,6 +5741,7 @@
length = 0;
if (startsWith(bytes, IDENTIFIER_EXIF_APP1)) {
+ byte[] xmpBeforeReadingExif = getAttributeBytes(TAG_XMP);
final byte[] value = Arrays.copyOfRange(bytes, IDENTIFIER_EXIF_APP1.length,
bytes.length);
// Save offset to EXIF data for handling thumbnail and attribute offsets.
@@ -5716,6 +5751,16 @@
readExifSegment(value, imageType);
setThumbnailData(new ByteOrderedDataInputStream(value));
+
+ if (getAttributeBytes(TAG_XMP) == null) {
+ // XMP should be stored in a separate APP1 segment (see XMP spec part 3
+ // section 3.3.2). If the Exif segment didn't contain XMP then we set
+ // this to true to ensure any XMP data added will get written out to a
+ // separate segment.
+ mXmpIsFromSeparateMarker = true;
+ } else if (xmpBeforeReadingExif != getAttributeBytes(TAG_XMP)) {
+ mXmpIsFromSeparateMarker = false;
+ }
} else if (startsWith(bytes, IDENTIFIER_XMP_APP1)) {
// See XMP Specification Part 3: Storage in Files, 1.1.3 JPEG, Table 6
final int offset = start + IDENTIFIER_XMP_APP1.length;
@@ -6449,7 +6494,8 @@
if (identifier != null) {
dataInputStream.readFully(identifier);
if (startsWith(identifier, IDENTIFIER_EXIF_APP1)
- || startsWith(identifier, IDENTIFIER_XMP_APP1)) {
+ || (startsWith(identifier, IDENTIFIER_XMP_APP1)
+ && mXmpIsFromSeparateMarker)) {
// Skip the original EXIF or XMP APP1 segment.
dataInputStream.skipFully(length - identifier.length);
break;
diff --git a/libraryversions.toml b/libraryversions.toml
index 82f9751..ff04648 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -42,7 +42,7 @@
CORE_REMOTEVIEWS = "1.1.0-rc01"
CORE_ROLE = "1.2.0-alpha01"
CORE_SPLASHSCREEN = "1.2.0-alpha01"
-CORE_TELECOM = "1.0.0-alpha08"
+CORE_TELECOM = "1.0.0-alpha09"
CORE_UWB = "1.0.0-alpha08"
CREDENTIALS = "1.3.0-rc01"
CREDENTIALS_E2EE_QUARANTINE = "1.0.0-alpha02"
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderAdapter.kt b/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderAdapter.kt
deleted file mode 100644
index 6bb10bb..0000000
--- a/privacysandbox/sdkruntime/sdkruntime-core/src/main/java/androidx/privacysandbox/sdkruntime/core/SandboxedSdkProviderAdapter.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2022 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.privacysandbox.sdkruntime.core
-
-import android.app.sdksandbox.LoadSdkException
-import android.app.sdksandbox.SandboxedSdk
-import android.app.sdksandbox.SandboxedSdkProvider
-import android.content.Context
-import android.os.Bundle
-import android.view.View
-import androidx.annotation.RequiresApi
-import androidx.annotation.RestrictTo
-
-/**
- * Implementation of platform [SandboxedSdkProvider] that delegate to [SandboxedSdkProviderCompat]
- * Gets compat class name from asset "SandboxedSdkProviderCompatClassName.txt"
- */
-@RequiresApi(34)
-// TODO(b/301437557) Remove after documentation migration to sdkruntime-provider
-@Deprecated(
- message = "Use SandboxedSdkProviderAdapter from sdkruntime-provider library",
- replaceWith =
- ReplaceWith(
- expression = "SandboxedSdkProviderAdapter",
- imports =
- arrayOf("androidx.privacysandbox.sdkruntime.provider.SandboxedSdkProviderAdapter")
- ),
- level = DeprecationLevel.HIDDEN
-)
-@RestrictTo(RestrictTo.Scope.LIBRARY) // removing from public API surface
-class SandboxedSdkProviderAdapter
-internal constructor(private val classNameProvider: CompatClassNameProvider) :
- SandboxedSdkProvider() {
-
- /** Provides classname of [SandboxedSdkProviderCompat] implementation. */
- internal interface CompatClassNameProvider {
- fun getCompatProviderClassName(context: Context): String
- }
-
- constructor() : this(DefaultClassNameProvider())
-
- internal val delegate: SandboxedSdkProviderCompat by lazy {
- val currentContext = context!!
- val compatSdkProviderClassName =
- classNameProvider.getCompatProviderClassName(currentContext)
- val clz = Class.forName(compatSdkProviderClassName)
- val newDelegate = clz.getConstructor().newInstance() as SandboxedSdkProviderCompat
- newDelegate.attachContext(currentContext)
- newDelegate
- }
-
- @Throws(LoadSdkException::class)
- override fun onLoadSdk(params: Bundle): SandboxedSdk {
- return try {
- delegate.onLoadSdk(params).toSandboxedSdk()
- } catch (e: LoadSdkCompatException) {
- throw e.toLoadSdkException()
- }
- }
-
- override fun beforeUnloadSdk() {
- delegate.beforeUnloadSdk()
- }
-
- override fun getView(windowContext: Context, params: Bundle, width: Int, height: Int): View {
- return delegate.getView(windowContext, params, width, height)
- }
-
- private class DefaultClassNameProvider : CompatClassNameProvider {
- override fun getCompatProviderClassName(context: Context): String {
- // TODO(b/257966930) Read classname from SDK manifest property
- return context.assets.open(COMPAT_SDK_PROVIDER_CLASS_ASSET_NAME).use { inputStream ->
- inputStream.bufferedReader().readLine()
- }
- }
- }
-
- private companion object {
- private const val COMPAT_SDK_PROVIDER_CLASS_ASSET_NAME =
- "SandboxedSdkProviderCompatClassName.txt"
- }
-}
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
index fcaaf42..78e59dc 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
@@ -482,14 +482,14 @@
@Test
fun relationManytoMany() = runTest {
- val sampleEntity1 = SampleEntity(1, 1)
- val sampleEntity1s = listOf(sampleEntity1, SampleEntity(2, 2))
+ val sampleEntity1 = StringSampleEntity1("1", "1")
+ val sampleEntity1s = listOf(sampleEntity1, StringSampleEntity1("2", "2"))
- val sampleEntity2 = SampleEntity2(1, 1)
- val sampleEntity2s = listOf(sampleEntity2, SampleEntity2(2, 2))
+ val sampleEntity2 = StringSampleEntity2("1", "1")
+ val sampleEntity2s = listOf(sampleEntity2, StringSampleEntity2("2", "2"))
- db.dao().insertSampleEntityList(sampleEntity1s)
- db.dao().insertSampleEntity2List(sampleEntity2s)
+ db.dao().insertSampleEntity1WithString(sampleEntity1s)
+ db.dao().insertSampleEntity2WithString(sampleEntity2s)
assertThat(db.dao().getSampleManyToMany())
.isEqualTo(SampleDao.SampleManyAndMany(sample1 = sampleEntity1, sample2s = listOf()))
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
index e1b545c..840251d 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
@@ -67,13 +67,25 @@
@ColumnInfo(defaultValue = "0") val dataCopy: Long
)
+@Entity
+data class StringSampleEntity1(
+ @PrimaryKey val stringPk1: String,
+ @ColumnInfo(defaultValue = "0") val data1: String
+)
+
+@Entity
+data class StringSampleEntity2(
+ @PrimaryKey val stringPk2: String,
+ @ColumnInfo(defaultValue = "0") val data2: String
+)
+
@Entity(
primaryKeys = ["sample1Key", "sample2Key"],
indices = [Index("sample1Key"), Index("sample2Key")]
)
data class Sample1Sample2XRef(
- val sample1Key: Long,
- val sample2Key: Long,
+ val sample1Key: String,
+ val sample2Key: String,
)
@Dao
@@ -157,6 +169,10 @@
@Insert suspend fun insertSampleEntityList(entities: List<SampleEntity>)
+ @Insert suspend fun insertSampleEntity1WithString(entities: List<StringSampleEntity1>)
+
+ @Insert suspend fun insertSampleEntity2WithString(entities: List<StringSampleEntity2>)
+
@Insert suspend fun insertSampleEntity2List(entities: List<SampleEntity2>)
@Insert suspend fun insert(entity: SampleEntity2)
@@ -182,7 +198,7 @@
@Transaction @Query("SELECT * FROM SampleEntity") suspend fun getSample1ToMany(): Sample1AndMany
@Transaction
- @Query("SELECT * FROM SampleEntity")
+ @Query("SELECT * FROM StringSampleEntity1")
suspend fun getSampleManyToMany(): SampleManyAndMany
data class Sample1And2(
@@ -196,10 +212,10 @@
)
data class SampleManyAndMany(
- @Embedded val sample1: SampleEntity,
+ @Embedded val sample1: StringSampleEntity1,
@Relation(
- parentColumn = "pk",
- entityColumn = "pk2",
+ parentColumn = "stringPk1",
+ entityColumn = "stringPk2",
associateBy =
Junction(
value = Sample1Sample2XRef::class,
@@ -207,7 +223,7 @@
entityColumn = "sample2Key"
)
)
- val sample2s: List<SampleEntity2>
+ val sample2s: List<StringSampleEntity2>
)
}
@@ -218,6 +234,8 @@
SampleEntity2::class,
SampleEntity3::class,
SampleEntityCopy::class,
+ StringSampleEntity1::class,
+ StringSampleEntity2::class,
Sample1Sample2XRef::class
],
version = 1,
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/SimpleEntityReadWriteTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/SimpleEntityReadWriteTest.java
index d95285a..2194c1f 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/SimpleEntityReadWriteTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/SimpleEntityReadWriteTest.java
@@ -102,7 +102,7 @@
}
@Test
- public void insertNull() throws Exception {
+ public void insertNullColumn() throws Exception {
@SuppressWarnings("ConstantConditions")
Product product = new Product(1, null);
Throwable throwable = null;
@@ -116,6 +116,20 @@
}
@Test
+ public void insertNullEntity() throws Exception {
+ @SuppressWarnings("ConstantConditions")
+ Throwable throwable = null;
+ try {
+ //noinspection DataFlowIssue - testing insert of null arg on @NonNull param
+ mProductDao.insert((Product) null);
+ } catch (Throwable t) {
+ throwable = t;
+ }
+ assertNotNull("Was expecting an exception", throwable);
+ assertThat(throwable, instanceOf(NullPointerException.class));
+ }
+
+ @Test
public void insertQueryForVoid() {
mProductDao.insert("Product X");
assertThat(mProductDao.countProducts(), is(1));
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
index 86ff171..295e26f 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
@@ -610,7 +610,8 @@
val canUseLongSparseArray =
context.processingEnv.findTypeElement(LONG_SPARSE_ARRAY.canonicalName) != null
val canUseArrayMap =
- context.processingEnv.findTypeElement(ARRAY_MAP.canonicalName) != null
+ context.processingEnv.findTypeElement(ARRAY_MAP.canonicalName) != null &&
+ context.isAndroidOnlyTarget()
return when {
canUseLongSparseArray && affinity == SQLTypeAffinity.INTEGER ->
LONG_SPARSE_ARRAY.parametrizedBy(valueTypeName)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutQueryParameter.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutQueryParameter.kt
index 72e060a..90196b8 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutQueryParameter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutQueryParameter.kt
@@ -16,6 +16,7 @@
package androidx.room.vo
+import androidx.room.compiler.processing.XNullability
import androidx.room.compiler.processing.XType
import androidx.room.compiler.processing.XVariableElement
@@ -34,4 +35,6 @@
} else {
"handle"
}
+
+ val isNonNull = type.nullability == XNullability.NONNULL
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
index 1174009..8ff5d2d 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
@@ -471,6 +471,7 @@
}
val useDriverApi = method.methodBinder.isMigratedToDriver()
val scope = CodeGenScope(writer = this, useDriverApi = useDriverApi)
+ ShortcutQueryParameterWriter.addNullCheckValidation(scope, method.parameters)
if (useDriverApi) {
method.methodBinder.convertAndReturn(
parameters = method.parameters,
@@ -557,6 +558,7 @@
}
val useDriverApi = method.methodBinder.isMigratedToDriver()
val scope = CodeGenScope(writer = this, useDriverApi = useDriverApi)
+ ShortcutQueryParameterWriter.addNullCheckValidation(scope, method.parameters)
if (useDriverApi) {
method.methodBinder.convertAndReturn(
parameters = method.parameters,
@@ -608,7 +610,7 @@
}
val useDriverApi = method.methodBinder.isMigratedToDriver()
val scope = CodeGenScope(writer = this, useDriverApi = useDriverApi)
-
+ ShortcutQueryParameterWriter.addNullCheckValidation(scope, method.parameters)
if (useDriverApi) {
method.methodBinder.convertAndReturn(
parameters = method.parameters,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/ShortcutQueryParameterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/ShortcutQueryParameterWriter.kt
new file mode 100644
index 0000000..c8b3e89
--- /dev/null
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/ShortcutQueryParameterWriter.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2024 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.room.writer
+
+import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.asClassName
+import androidx.room.solver.CodeGenScope
+import androidx.room.vo.ShortcutQueryParameter
+
+/** Writer for [ShortcutQueryParameter] related statements. */
+object ShortcutQueryParameterWriter {
+ fun addNullCheckValidation(scope: CodeGenScope, parameters: List<ShortcutQueryParameter>) {
+ // The null checks are only needed for Java since Kotlin instinctively adds null checks to
+ // params that are not null.
+ if (scope.language != CodeLanguage.JAVA) {
+ return
+ }
+ parameters
+ .filter { it.isNonNull }
+ .forEach {
+ scope.builder.addStatement(
+ "if (%L == null) throw %L",
+ it.name,
+ XCodeBlock.ofNewInstance(
+ scope.language,
+ NullPointerException::class.asClassName()
+ )
+ )
+ }
+ }
+}
diff --git a/room/room-runtime/bcv/native/current.txt b/room/room-runtime/bcv/native/current.txt
index 0ed78ab..a643540 100644
--- a/room/room-runtime/bcv/native/current.txt
+++ b/room/room-runtime/bcv/native/current.txt
@@ -55,6 +55,10 @@
constructor <init>(androidx.sqlite/SQLiteDriver) // androidx.room/BaseRoomConnectionManager.DriverWrapper.<init>|<init>(androidx.sqlite.SQLiteDriver){}[0]
final fun open(kotlin/String): androidx.sqlite/SQLiteConnection // androidx.room/BaseRoomConnectionManager.DriverWrapper.open|open(kotlin.String){}[0]
}
+ final object Companion { // androidx.room/BaseRoomConnectionManager.Companion|null[0]
+ final const val BUSY_TIMEOUT_MS // androidx.room/BaseRoomConnectionManager.Companion.BUSY_TIMEOUT_MS|<get-BUSY_TIMEOUT_MS>(){}[0]
+ final fun <get-BUSY_TIMEOUT_MS>(): kotlin/Int // androidx.room/BaseRoomConnectionManager.Companion.BUSY_TIMEOUT_MS.<get-BUSY_TIMEOUT_MS>|<get-BUSY_TIMEOUT_MS>(){}[0]
+ }
}
abstract class androidx.room/RoomDatabase { // androidx.room/RoomDatabase|null[0]
abstract class Callback { // androidx.room/RoomDatabase.Callback|null[0]
@@ -320,6 +324,7 @@
}
final fun <#A: kotlin/Any, #B: kotlin/Any?> androidx.room.util/recursiveFetchMap(kotlin.collections/MutableMap<#A, #B>, kotlin/Boolean, kotlin/Function1<kotlin.collections/MutableMap<#A, #B>, kotlin/Unit>) // androidx.room.util/recursiveFetchMap|recursiveFetchMap(kotlin.collections.MutableMap<0:0,0:1>;kotlin.Boolean;kotlin.Function1<kotlin.collections.MutableMap<0:0,0:1>,kotlin.Unit>){0§<kotlin.Any>;1§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> androidx.room.coroutines/createFlow(androidx.room/RoomDatabase, kotlin/Boolean, kotlin/Array<kotlin/String>, kotlin/Function1<androidx.sqlite/SQLiteConnection, #A>): kotlinx.coroutines.flow/Flow<#A> // androidx.room.coroutines/createFlow|createFlow(androidx.room.RoomDatabase;kotlin.Boolean;kotlin.Array<kotlin.String>;kotlin.Function1<androidx.sqlite.SQLiteConnection,0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> androidx.room.util/recursiveFetchLongSparseArray(androidx.collection/LongSparseArray<#A>, kotlin/Boolean, kotlin/Function1<androidx.collection/LongSparseArray<#A>, kotlin/Unit>) // androidx.room.util/recursiveFetchLongSparseArray|recursiveFetchLongSparseArray(androidx.collection.LongSparseArray<0:0>;kotlin.Boolean;kotlin.Function1<androidx.collection.LongSparseArray<0:0>,kotlin.Unit>){0§<kotlin.Any?>}[0]
final fun androidx.room.util/appendPlaceholders(kotlin.text/StringBuilder, kotlin/Int) // androidx.room.util/appendPlaceholders|appendPlaceholders(kotlin.text.StringBuilder;kotlin.Int){}[0]
final fun androidx.room.util/dropFtsSyncTriggers(androidx.sqlite/SQLiteConnection) // androidx.room.util/dropFtsSyncTriggers|dropFtsSyncTriggers(androidx.sqlite.SQLiteConnection){}[0]
final fun androidx.room.util/foreignKeyCheck(androidx.sqlite/SQLiteConnection, kotlin/String) // androidx.room.util/foreignKeyCheck|foreignKeyCheck(androidx.sqlite.SQLiteConnection;kotlin.String){}[0]
diff --git a/settings.gradle b/settings.gradle
index 2eb9d90..08922f5 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -28,7 +28,7 @@
classpath("com.google.protobuf:protobuf-java:3.22.3")
classpath("com.gradle:develocity-gradle-plugin:3.17.2")
classpath("com.gradle:common-custom-user-data-gradle-plugin:2.0.1")
- classpath("androidx.build.gradle.gcpbuildcache:gcpbuildcache:1.0.0-beta08")
+ classpath("androidx.build.gradle.gcpbuildcache:gcpbuildcache:1.0.0-beta10")
}
}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ProgressIndicatorDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ProgressIndicatorDemo.kt
index 807a43e..c1c15b3 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ProgressIndicatorDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ProgressIndicatorDemo.kt
@@ -37,6 +37,7 @@
import androidx.wear.compose.material3.samples.FullScreenProgressIndicatorSample
import androidx.wear.compose.material3.samples.MediaButtonProgressIndicatorSample
import androidx.wear.compose.material3.samples.OverflowProgressIndicatorSample
+import androidx.wear.compose.material3.samples.SmallValuesProgressIndicatorSample
val ProgressIndicatorDemos =
listOf(
@@ -64,5 +65,8 @@
)
}
}
- }
+ },
+ ComposableDemo("Small progress values") {
+ Centralize { SmallValuesProgressIndicatorSample() }
+ },
)
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ProgressIndicatorSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ProgressIndicatorSample.kt
index b6794f3..7557fc9 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ProgressIndicatorSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ProgressIndicatorSample.kt
@@ -136,3 +136,23 @@
)
}
}
+
+@Sampled
+@Composable
+fun SmallValuesProgressIndicatorSample() {
+ Box {
+ CircularProgressIndicator(
+ // Small progress values like 2% will be rounded up to at least the stroke width.
+ progress = { 0.02f },
+ modifier = Modifier.fillMaxSize().padding(FullScreenPadding),
+ startAngle = 120f,
+ endAngle = 60f,
+ strokeWidth = 10.dp,
+ colors =
+ ProgressIndicatorDefaults.colors(
+ indicatorColor = Color.Green,
+ trackColor = Color.White
+ ),
+ )
+ }
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorTest.kt
index 329c719..b1b05a8 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ProgressIndicatorTest.kt
@@ -152,7 +152,7 @@
setContentWithTheme {
CircularProgressIndicator(
modifier = Modifier.testTag(TEST_TAG),
- progress = { 0.05f },
+ progress = { 0.02f },
colors =
ProgressIndicatorDefaults.colors(
indicatorColor = Color.Yellow,
@@ -161,6 +161,7 @@
)
}
rule.waitForIdle()
+ // Small progress values like 2% should be rounded up to at least the stroke width.
rule
.onNodeWithTag(TEST_TAG)
.captureToImage()
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ProgressIndicator.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ProgressIndicator.kt
index cdfab19..0362590 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ProgressIndicator.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ProgressIndicator.kt
@@ -40,6 +40,7 @@
import androidx.wear.compose.materialcore.toRadians
import kotlin.math.asin
import kotlin.math.cos
+import kotlin.math.max
import kotlin.math.min
import kotlin.math.sin
@@ -59,6 +60,10 @@
*
* @sample androidx.wear.compose.material3.samples.MediaButtonProgressIndicatorSample
*
+ * Example of a [CircularProgressIndicator] with small progress values:
+ *
+ * @sample androidx.wear.compose.material3.samples.SmallValuesProgressIndicatorSample
+ *
* Progress indicators express the proportion of completion of an ongoing task.
*
* @param progress The progress of this progress indicator where 0.0 represents no progress and 1.0
@@ -96,7 +101,7 @@
.focusable()
.drawWithCache {
val fullSweep = 360f - ((startAngle - endAngle) % 360 + 360) % 360
- val progressSweep = fullSweep * coercedProgress()
+ var progressSweep = fullSweep * coercedProgress()
val stroke = Stroke(width = strokeWidth.toPx(), cap = StrokeCap.Round)
val minSize = min(size.height, size.width)
// Sweep angle between two progress indicator segments.
@@ -104,6 +109,10 @@
asin((stroke.width + gapSize.toPx()) / (minSize - stroke.width)).toDegrees() *
2f
+ if (progressSweep > 0) {
+ progressSweep = max(progressSweep, gapSweep)
+ }
+
onDrawWithContent {
// Draw an indicator.
drawIndicatorSegment(
@@ -262,7 +271,7 @@
brush: Brush,
stroke: Stroke
) {
- if (sweep < gapSweep) {
+ if (sweep <= gapSweep) {
// Draw a small indicator.
val angle = (startAngle + sweep / 2f).toRadians()
val radius = size.minDimension / 2 - stroke.width / 2
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScreenScaffold.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScreenScaffold.kt
index 031cecb..1842f55 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScreenScaffold.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ScreenScaffold.kt
@@ -255,8 +255,9 @@
}
}
}
- Box(modifier = Modifier.fillMaxSize().graphicsLayer { alpha = alphaValue.floatValue }) {
- scrollIndicator()
- }
+ Box(
+ modifier = Modifier.fillMaxSize().graphicsLayer { alpha = alphaValue.floatValue },
+ content = scrollIndicator
+ )
}
}