Add singleLine IME Option

Added a KeyboardOptions object since there will be more
configuration options such as autoCorrect, keyboard capitalization,
maybe suggestions, maybe password.

Test: Added new tests
Test: ./gradlew compose:foundation:foundation:test
Test: ./gradlew compose:foundation:foundation:connectedAndroidTest
Test: ./gradlew compose:foundation:foundation-text:test
Test: ./gradlew compose:foundation:foundation-text:connectedAndroidTest
Test: ./gradlew compose:ui:ui-text:test
Test: ./gradlew compose:ui:ui-text:connectedAndroidTest
Test: ./gradlew compose:ui:ui:test
Test: ./gradlew compose:ui:ui:connectedAndroidTest

RelNote: Added single line keyboard option to CoreTextField

Bug: 170482203
Change-Id: I72e6d9f84abbf4ff6a9ede5355de4c30d37c3d8c
diff --git a/compose/foundation/foundation-text/api/current.txt b/compose/foundation/foundation-text/api/current.txt
index cdc3cf3..46efe42 100644
--- a/compose/foundation/foundation-text/api/current.txt
+++ b/compose/foundation/foundation-text/api/current.txt
@@ -2,7 +2,7 @@
 package androidx.compose.foundation.text {
 
   public final class CoreTextFieldKt {
-    method @androidx.compose.runtime.Composable public static void CoreTextField-J0BxG4A(androidx.compose.ui.text.input.TextFieldValue value, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.ui.text.input.KeyboardType keyboardType, optional androidx.compose.ui.text.input.ImeAction imeAction, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional long cursorColor, optional boolean softWrap, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void CoreTextField-wQ2hrV0(androidx.compose.ui.text.input.TextFieldValue value, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.ui.text.input.KeyboardType keyboardType, optional androidx.compose.ui.text.input.ImeAction imeAction, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional long cursorColor, optional boolean softWrap, optional int maxLines, optional androidx.compose.ui.text.input.KeyboardOptions keyboardOptions);
     method @VisibleForTesting public static void setBlinkingCursorEnabled(boolean p);
   }
 
diff --git a/compose/foundation/foundation-text/api/public_plus_experimental_current.txt b/compose/foundation/foundation-text/api/public_plus_experimental_current.txt
index cdc3cf3..46efe42 100644
--- a/compose/foundation/foundation-text/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation-text/api/public_plus_experimental_current.txt
@@ -2,7 +2,7 @@
 package androidx.compose.foundation.text {
 
   public final class CoreTextFieldKt {
-    method @androidx.compose.runtime.Composable public static void CoreTextField-J0BxG4A(androidx.compose.ui.text.input.TextFieldValue value, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.ui.text.input.KeyboardType keyboardType, optional androidx.compose.ui.text.input.ImeAction imeAction, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional long cursorColor, optional boolean softWrap, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void CoreTextField-wQ2hrV0(androidx.compose.ui.text.input.TextFieldValue value, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.ui.text.input.KeyboardType keyboardType, optional androidx.compose.ui.text.input.ImeAction imeAction, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional long cursorColor, optional boolean softWrap, optional int maxLines, optional androidx.compose.ui.text.input.KeyboardOptions keyboardOptions);
     method @VisibleForTesting public static void setBlinkingCursorEnabled(boolean p);
   }
 
diff --git a/compose/foundation/foundation-text/api/restricted_current.txt b/compose/foundation/foundation-text/api/restricted_current.txt
index cdc3cf3..46efe42 100644
--- a/compose/foundation/foundation-text/api/restricted_current.txt
+++ b/compose/foundation/foundation-text/api/restricted_current.txt
@@ -2,7 +2,7 @@
 package androidx.compose.foundation.text {
 
   public final class CoreTextFieldKt {
-    method @androidx.compose.runtime.Composable public static void CoreTextField-J0BxG4A(androidx.compose.ui.text.input.TextFieldValue value, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.ui.text.input.KeyboardType keyboardType, optional androidx.compose.ui.text.input.ImeAction imeAction, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional long cursorColor, optional boolean softWrap, optional int maxLines);
+    method @androidx.compose.runtime.Composable public static void CoreTextField-wQ2hrV0(androidx.compose.ui.text.input.TextFieldValue value, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.ui.text.input.KeyboardType keyboardType, optional androidx.compose.ui.text.input.ImeAction imeAction, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit> onTextInputStarted, optional long cursorColor, optional boolean softWrap, optional int maxLines, optional androidx.compose.ui.text.input.KeyboardOptions keyboardOptions);
     method @VisibleForTesting public static void setBlinkingCursorEnabled(boolean p);
   }
 
diff --git a/compose/foundation/foundation-text/integration-tests/ui-text-compose-demos/src/main/java/androidx/compose/foundation/text/demos/KeyboardOptionsDemo.kt b/compose/foundation/foundation-text/integration-tests/ui-text-compose-demos/src/main/java/androidx/compose/foundation/text/demos/KeyboardOptionsDemo.kt
new file mode 100644
index 0000000..de95e3aa
--- /dev/null
+++ b/compose/foundation/foundation-text/integration-tests/ui-text-compose-demos/src/main/java/androidx/compose/foundation/text/demos/KeyboardOptionsDemo.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2019 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.text.demos
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.ScrollableColumn
+import androidx.compose.foundation.layout.defaultMinSizeConstraints
+import androidx.compose.foundation.text.CoreTextField
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.SoftwareKeyboardController
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardOptions
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.unit.dp
+
+@OptIn(ExperimentalTextApi::class)
+private class Data(
+    val keyboardOptions: KeyboardOptions,
+    val keyboardType: KeyboardType,
+    val name: String,
+    val imeAction: ImeAction = ImeAction.Unspecified
+)
+
+@OptIn(ExperimentalTextApi::class)
+private val KeyboardOptionsList = listOf(
+    Data(
+        keyboardOptions = KeyboardOptions(singleLine = true),
+        keyboardType = KeyboardType.Text,
+        name = "singleLine/Text"
+    ),
+    Data(
+        keyboardOptions = KeyboardOptions(singleLine = false),
+        keyboardType = KeyboardType.Text,
+        name = "multiLine/Text"
+    ),
+    Data(
+        keyboardOptions = KeyboardOptions(singleLine = true),
+        keyboardType = KeyboardType.Text,
+        imeAction = ImeAction.Search,
+        name = "singleLine/Text/Search"
+    ),
+    Data(
+        keyboardOptions = KeyboardOptions(singleLine = true),
+        keyboardType = KeyboardType.Number,
+        name = "singleLine/Number"
+    ),
+    Data(
+        keyboardOptions = KeyboardOptions(singleLine = false),
+        keyboardType = KeyboardType.Number,
+        name = "multiLine/Number"
+    ),
+)
+
+@OptIn(ExperimentalTextApi::class)
+@Composable
+fun KeyboardOptionsDemo() {
+    ScrollableColumn {
+        for (data in KeyboardOptionsList) {
+            TagLine(tag = "${data.name}")
+            MyTextField(data)
+        }
+    }
+}
+
+@Composable
+@OptIn(
+    ExperimentalFoundationApi::class,
+    ExperimentalTextApi::class
+)
+private fun MyTextField(data: Data) {
+    val controller = remember { mutableStateOf<SoftwareKeyboardController?>(null) }
+    val state = savedInstanceState(saver = TextFieldValue.Saver) { TextFieldValue() }
+    CoreTextField(
+        modifier = demoTextFieldModifiers.defaultMinSizeConstraints(100.dp),
+        value = state.value,
+        keyboardType = data.keyboardType,
+        imeAction = data.imeAction,
+        keyboardOptions = data.keyboardOptions,
+        onValueChange = { state.value = it },
+        textStyle = TextStyle(fontSize = fontSize8),
+        onTextInputStarted = { controller.value = it },
+        onImeActionPerformed = {
+            controller.value?.hideSoftwareKeyboard()
+        },
+        cursorColor = Color.Red
+    )
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation-text/integration-tests/ui-text-compose-demos/src/main/java/androidx/compose/foundation/text/demos/TextDemos.kt b/compose/foundation/foundation-text/integration-tests/ui-text-compose-demos/src/main/java/androidx/compose/foundation/text/demos/TextDemos.kt
index 670c653..392f211 100644
--- a/compose/foundation/foundation-text/integration-tests/ui-text-compose-demos/src/main/java/androidx/compose/foundation/text/demos/TextDemos.kt
+++ b/compose/foundation/foundation-text/integration-tests/ui-text-compose-demos/src/main/java/androidx/compose/foundation/text/demos/TextDemos.kt
@@ -38,6 +38,7 @@
                 ComposableDemo("TextField in Scroller") { TextFieldWithScrollerDemo() },
                 ComposableDemo("Soft Wrap") { SoftWrapDemo() },
                 ComposableDemo("Min/Max Lines") { CoreTextFieldMinMaxDemo() },
+                ComposableDemo("Keyboard Options") { KeyboardOptionsDemo() }
             )
         ),
         ComposableDemo("Text Accessibility") { TextAccessibilityDemo() }
diff --git a/compose/foundation/foundation-text/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldInputServiceIntegrationTest.kt b/compose/foundation/foundation-text/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldInputServiceIntegrationTest.kt
new file mode 100644
index 0000000..bfb5964
--- /dev/null
+++ b/compose/foundation/foundation-text/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/CoreTextFieldInputServiceIntegrationTest.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.foundation.text
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.ExperimentalFocus
+import androidx.compose.ui.focus.isFocused
+import androidx.compose.ui.focusObserver
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.text.ExperimentalTextApi
+import androidx.compose.ui.text.InternalTextApi
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardOptions
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.PlatformTextInputService
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.text.input.TextInputService
+import androidx.compose.ui.text.input.textInputServiceFactory
+import androidx.test.filters.LargeTest
+import androidx.ui.test.createComposeRule
+import androidx.ui.test.onNodeWithTag
+import androidx.ui.test.performClick
+import com.google.common.truth.Truth.assertThat
+import com.nhaarman.mockitokotlin2.any
+import com.nhaarman.mockitokotlin2.eq
+import com.nhaarman.mockitokotlin2.mock
+import com.nhaarman.mockitokotlin2.times
+import com.nhaarman.mockitokotlin2.verify
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(
+    ExperimentalTextApi::class,
+    InternalTextApi::class,
+    ExperimentalFocus::class
+)
+@LargeTest
+@RunWith(JUnit4::class)
+class CoreTextFieldInputServiceIntegrationTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun textField_KeyboardOptions_isPassedTo_platformTextInputService() {
+        val platformTextInputService = mock<PlatformTextInputService>()
+        @Suppress("DEPRECATION_ERROR")
+        textInputServiceFactory = { TextInputService(platformTextInputService) }
+
+        val testTag = "KeyboardOption"
+        val value = TextFieldValue("abc")
+        val keyboardOptions = KeyboardOptions(singleLine = true)
+        val keyboardType = KeyboardType.Phone
+        val imeAction = ImeAction.Search
+        var focused = false
+
+        rule.setContent {
+            CoreTextField(
+                value = value,
+                keyboardOptions = keyboardOptions,
+                keyboardType = keyboardType,
+                imeAction = imeAction,
+                modifier = Modifier
+                    .testTag(testTag)
+                    .focusObserver { focused = it.isFocused },
+                onValueChange = {}
+            )
+        }
+
+        rule.onNodeWithTag(testTag).performClick()
+
+        rule.runOnIdle {
+            assertThat(focused).isTrue()
+
+            verify(platformTextInputService, times(1)).startInput(
+                eq(value),
+                eq(keyboardType),
+                eq(imeAction),
+                eq(keyboardOptions),
+                any(), // onEditCommand
+                any() // onImeActionPerformed
+            )
+        }
+    }
+}
diff --git a/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt b/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
index a942674..926d215 100644
--- a/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
+++ b/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
@@ -81,6 +81,7 @@
 import androidx.compose.ui.semantics.text
 import androidx.compose.ui.semantics.textSelectionRange
 import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.SoftwareKeyboardController
 import androidx.compose.ui.text.TextDelegate
@@ -90,6 +91,7 @@
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.input.EditProcessor
 import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardOptions
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.NO_SESSION
 import androidx.compose.ui.text.input.OffsetMap
@@ -152,7 +154,8 @@
 @Composable
 @OptIn(
     ExperimentalFocus::class,
-    InternalTextApi::class
+    InternalTextApi::class,
+    ExperimentalTextApi::class
 )
 fun CoreTextField(
     value: TextFieldValue,
@@ -167,7 +170,8 @@
     onTextInputStarted: (SoftwareKeyboardController) -> Unit = {},
     cursorColor: Color = Color.Unspecified,
     softWrap: Boolean = true,
-    maxLines: Int = Int.MAX_VALUE
+    maxLines: Int = Int.MAX_VALUE,
+    keyboardOptions: KeyboardOptions = KeyboardOptions.Default
 ) {
     require(maxLines > 0) {
         "maxLines should be greater than 0"
@@ -244,6 +248,7 @@
                 state.processor,
                 keyboardType,
                 imeAction,
+                keyboardOptions,
                 onValueChangeWrapper,
                 onImeActionPerformedWrapper
             )
diff --git a/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt b/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt
index e541372..8eb3f9b 100644
--- a/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt
+++ b/compose/foundation/foundation-text/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt
@@ -24,6 +24,7 @@
 import androidx.compose.ui.graphics.Paint
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.Paragraph
 import androidx.compose.ui.text.SpanStyle
@@ -39,6 +40,7 @@
 import androidx.compose.ui.text.input.INVALID_SESSION
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.InputSessionToken
+import androidx.compose.ui.text.input.KeyboardOptions
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.OffsetMap
 import androidx.compose.ui.text.input.SetSelectionEditOp
@@ -86,7 +88,10 @@
 private fun Float.toIntPx(): Int = ceil(this).roundToInt()
 
 /** @suppress **/
-@OptIn(InternalTextApi::class)
+@OptIn(
+    InternalTextApi::class,
+    ExperimentalTextApi::class
+)
 @InternalTextApi
 class TextFieldDelegate {
     companion object {
@@ -276,6 +281,7 @@
             editProcessor: EditProcessor,
             keyboardType: KeyboardType,
             imeAction: ImeAction,
+            keyboardOptions: KeyboardOptions,
             onValueChange: (TextFieldValue) -> Unit,
             onImeActionPerformed: (ImeAction) -> Unit
         ): InputSessionToken {
@@ -283,6 +289,7 @@
                 value = TextFieldValue(value.text, value.selection, value.composition),
                 keyboardType = keyboardType,
                 imeAction = imeAction,
+                keyboardOptions = keyboardOptions,
                 onEditCommand = { onEditCommand(it, editProcessor, onValueChange) },
                 onImeActionPerformed = onImeActionPerformed
             ) ?: INVALID_SESSION
diff --git a/compose/foundation/foundation-text/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt b/compose/foundation/foundation-text/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt
index d8f4d2b..1c91499 100644
--- a/compose/foundation/foundation-text/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt
+++ b/compose/foundation/foundation-text/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt
@@ -24,6 +24,7 @@
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.InternalTextApi
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.MultiParagraphIntrinsics
 import androidx.compose.ui.text.SpanStyle
 import androidx.compose.ui.text.TextDelegate
@@ -37,6 +38,7 @@
 import androidx.compose.ui.text.input.FinishComposingTextEditOp
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.KeyboardOptions
 import androidx.compose.ui.text.input.OffsetMap
 import androidx.compose.ui.text.input.SetSelectionEditOp
 import androidx.compose.ui.text.input.TextFieldValue
@@ -65,7 +67,10 @@
 import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.anyLong
 
-@OptIn(InternalTextApi::class)
+@OptIn(
+    InternalTextApi::class,
+    ExperimentalTextApi::class
+)
 @RunWith(JUnit4::class)
 class TextFieldDelegateTest {
 
@@ -157,10 +162,19 @@
     @Test
     fun on_focus() {
         val editorState = TextFieldValue(text = "Hello, World", selection = TextRange(1))
+        val keyboardOptions = KeyboardOptions(singleLine = true)
+        val keyboardType = KeyboardType.Phone
+        val imeAction = ImeAction.Search
 
         TextFieldDelegate.onFocus(
-            textInputService, editorState, processor,
-            KeyboardType.Text, ImeAction.Unspecified, onValueChange, onEditorActionPerformed
+            textInputService = textInputService,
+            value = editorState,
+            editProcessor = processor,
+            keyboardType = keyboardType,
+            imeAction = imeAction,
+            keyboardOptions = keyboardOptions,
+            onValueChange = onValueChange,
+            onImeActionPerformed = onEditorActionPerformed
         )
         verify(textInputService).startInput(
             eq(
@@ -169,8 +183,9 @@
                     selection = editorState.selection
                 )
             ),
-            eq(KeyboardType.Text),
-            eq(ImeAction.Unspecified),
+            eq(keyboardType),
+            eq(imeAction),
+            eq(keyboardOptions),
             any(),
             eq(onEditorActionPerformed)
         )
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/SoftwareKeyboardTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/SoftwareKeyboardTest.kt
index ded0103..eee18da 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/SoftwareKeyboardTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/SoftwareKeyboardTest.kt
@@ -51,7 +51,7 @@
         val textInputService = mock<TextInputService>()
         val inputSessionToken = 10 // any positive number is fine.
 
-        whenever(textInputService.startInput(any(), any(), any(), any(), any()))
+        whenever(textInputService.startInput(any(), any(), any(), any(), any(), any()))
             .thenReturn(inputSessionToken)
 
         val onTextInputStarted: (SoftwareKeyboardController) -> Unit = mock()
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldOnValueChangeTextFieldValueTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldOnValueChangeTextFieldValueTest.kt
index c795a76..6e0279c 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldOnValueChangeTextFieldValueTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldOnValueChangeTextFieldValueTest.kt
@@ -66,7 +66,7 @@
         val textInputService = mock<TextInputService>()
         val inputSessionToken = 10 // any positive number is fine.
 
-        whenever(textInputService.startInput(any(), any(), any(), any(), any()))
+        whenever(textInputService.startInput(any(), any(), any(), any(), any(), any()))
             .thenReturn(inputSessionToken)
 
         rule.setContent {
@@ -102,6 +102,7 @@
                 value = any(),
                 keyboardType = any(),
                 imeAction = any(),
+                keyboardOptions = any(),
                 onEditCommand = onEditCommandCaptor.capture(),
                 onImeActionPerformed = any()
             )
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldTest.kt
index 255743c..0439062 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldTest.kt
@@ -149,7 +149,7 @@
         val textInputService = mock<TextInputService>()
         val inputSessionToken = 10 // any positive number is fine.
 
-        whenever(textInputService.startInput(any(), any(), any(), any(), any()))
+        whenever(textInputService.startInput(any(), any(), any(), any(), any(), any()))
             .thenReturn(inputSessionToken)
 
         rule.setContent {
@@ -170,6 +170,7 @@
                 value = any(),
                 keyboardType = any(),
                 imeAction = any(),
+                keyboardOptions = any(),
                 onEditCommand = onEditCommandCaptor.capture(),
                 onImeActionPerformed = any()
             )
@@ -221,7 +222,7 @@
         val textInputService = mock<TextInputService>()
         val inputSessionToken = 10 // any positive number is fine.
 
-        whenever(textInputService.startInput(any(), any(), any(), any(), any()))
+        whenever(textInputService.startInput(any(), any(), any(), any(), any(), any()))
             .thenReturn(inputSessionToken)
 
         rule.setContent {
@@ -242,6 +243,7 @@
                 value = any(),
                 keyboardType = any(),
                 imeAction = any(),
+                keyboardOptions = any(),
                 onEditCommand = onEditCommandCaptor.capture(),
                 onImeActionPerformed = any()
             )
@@ -280,7 +282,7 @@
         val textInputService = mock<TextInputService>()
         val inputSessionToken = 10 // any positive number is fine.
 
-        whenever(textInputService.startInput(any(), any(), any(), any(), any()))
+        whenever(textInputService.startInput(any(), any(), any(), any(), any(), any()))
             .thenReturn(inputSessionToken)
 
         val onTextLayout: (TextLayoutResult) -> Unit = mock()
@@ -310,6 +312,7 @@
                 value = any(),
                 keyboardType = any(),
                 imeAction = any(),
+                keyboardOptions = any(),
                 onEditCommand = onEditCommandCaptor.capture(),
                 onImeActionPerformed = any()
             )
diff --git a/compose/integration-tests/src/main/java/androidx/ui/integration/test/core/text/TextFieldToggleTextTestCase.kt b/compose/integration-tests/src/main/java/androidx/ui/integration/test/core/text/TextFieldToggleTextTestCase.kt
index b5ccede..944a9fe 100644
--- a/compose/integration-tests/src/main/java/androidx/ui/integration/test/core/text/TextFieldToggleTextTestCase.kt
+++ b/compose/integration-tests/src/main/java/androidx/ui/integration/test/core/text/TextFieldToggleTextTestCase.kt
@@ -31,10 +31,12 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.EditOperation
 import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardOptions
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.PlatformTextInputService
 import androidx.compose.ui.text.input.TextFieldValue
@@ -98,11 +100,13 @@
         }
     }
 
+    @OptIn(ExperimentalTextApi::class)
     private class TestPlatformTextInputService : PlatformTextInputService {
         override fun startInput(
             value: TextFieldValue,
             keyboardType: KeyboardType,
             imeAction: ImeAction,
+            keyboardOptions: KeyboardOptions,
             onEditCommand: (List<EditOperation>) -> Unit,
             onImeActionPerformed: (ImeAction) -> Unit
         ) { /*do nothing*/ }
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt
index 986006d..11559b0 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/OutlinedTextFieldTest.kt
@@ -610,6 +610,7 @@
                 value = any(),
                 keyboardType = eq(KeyboardType.Email),
                 imeAction = eq(ImeAction.Go),
+                keyboardOptions = any(),
                 onEditCommand = any(),
                 onImeActionPerformed = any()
             )
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
index 96d2e60..5c39c09 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
@@ -763,6 +763,7 @@
                 value = any(),
                 keyboardType = eq(KeyboardType.Email),
                 imeAction = eq(ImeAction.Go),
+                keyboardOptions = any(),
                 onEditCommand = any(),
                 onImeActionPerformed = any()
             )
diff --git a/compose/ui/ui-text/api/current.txt b/compose/ui/ui-text/api/current.txt
index d0156749..45bc91f 100644
--- a/compose/ui/ui-text/api/current.txt
+++ b/compose/ui/ui-text/api/current.txt
@@ -72,6 +72,9 @@
     method public static inline <R> R withStyle(androidx.compose.ui.text.AnnotatedString.Builder, androidx.compose.ui.text.ParagraphStyle style, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
   }
 
+  @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level, message="This is an experimental text API that may change without warning.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface ExperimentalTextApi {
+  }
+
   @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface InternalTextApi {
   }
 
@@ -780,6 +783,20 @@
     method public void onImeAction(androidx.compose.ui.text.input.ImeAction imeAction);
   }
 
+  @androidx.compose.runtime.Immutable @androidx.compose.ui.text.ExperimentalTextApi public final class KeyboardOptions {
+    ctor public KeyboardOptions(boolean singleLine);
+    method public boolean component1();
+    method @androidx.compose.runtime.Immutable @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.input.KeyboardOptions copy(boolean singleLine);
+    method public boolean getSingleLine();
+    property public final boolean singleLine;
+    field public static final androidx.compose.ui.text.input.KeyboardOptions.Companion Companion;
+  }
+
+  public static final class KeyboardOptions.Companion {
+    method public androidx.compose.ui.text.input.KeyboardOptions getDefault();
+    property public final androidx.compose.ui.text.input.KeyboardOptions Default;
+  }
+
   public enum KeyboardType {
     enum_constant public static final androidx.compose.ui.text.input.KeyboardType Ascii;
     enum_constant public static final androidx.compose.ui.text.input.KeyboardType Email;
@@ -826,7 +843,7 @@
     method public void notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
     method public void onStateUpdated(androidx.compose.ui.text.input.TextFieldValue value);
     method public void showSoftwareKeyboard();
-    method public void startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.KeyboardType keyboardType, androidx.compose.ui.text.input.ImeAction imeAction, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditOperation>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
+    method public void startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.KeyboardType keyboardType, androidx.compose.ui.text.input.ImeAction imeAction, androidx.compose.ui.text.input.KeyboardOptions keyboardOptions, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditOperation>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
     method public void stopInput();
   }
 
@@ -897,7 +914,7 @@
     method public void notifyFocusedRect(int token, androidx.compose.ui.geometry.Rect rect);
     method public void onStateUpdated(int token, androidx.compose.ui.text.input.TextFieldValue value);
     method public void showSoftwareKeyboard(int token);
-    method public int startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.KeyboardType keyboardType, androidx.compose.ui.text.input.ImeAction imeAction, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditOperation>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
+    method public int startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.KeyboardType keyboardType, androidx.compose.ui.text.input.ImeAction imeAction, androidx.compose.ui.text.input.KeyboardOptions keyboardOptions, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditOperation>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
     method public void stopInput(int token);
   }
 
diff --git a/compose/ui/ui-text/api/public_plus_experimental_current.txt b/compose/ui/ui-text/api/public_plus_experimental_current.txt
index d0156749..45bc91f 100644
--- a/compose/ui/ui-text/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-text/api/public_plus_experimental_current.txt
@@ -72,6 +72,9 @@
     method public static inline <R> R withStyle(androidx.compose.ui.text.AnnotatedString.Builder, androidx.compose.ui.text.ParagraphStyle style, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
   }
 
+  @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level, message="This is an experimental text API that may change without warning.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface ExperimentalTextApi {
+  }
+
   @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface InternalTextApi {
   }
 
@@ -780,6 +783,20 @@
     method public void onImeAction(androidx.compose.ui.text.input.ImeAction imeAction);
   }
 
+  @androidx.compose.runtime.Immutable @androidx.compose.ui.text.ExperimentalTextApi public final class KeyboardOptions {
+    ctor public KeyboardOptions(boolean singleLine);
+    method public boolean component1();
+    method @androidx.compose.runtime.Immutable @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.input.KeyboardOptions copy(boolean singleLine);
+    method public boolean getSingleLine();
+    property public final boolean singleLine;
+    field public static final androidx.compose.ui.text.input.KeyboardOptions.Companion Companion;
+  }
+
+  public static final class KeyboardOptions.Companion {
+    method public androidx.compose.ui.text.input.KeyboardOptions getDefault();
+    property public final androidx.compose.ui.text.input.KeyboardOptions Default;
+  }
+
   public enum KeyboardType {
     enum_constant public static final androidx.compose.ui.text.input.KeyboardType Ascii;
     enum_constant public static final androidx.compose.ui.text.input.KeyboardType Email;
@@ -826,7 +843,7 @@
     method public void notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
     method public void onStateUpdated(androidx.compose.ui.text.input.TextFieldValue value);
     method public void showSoftwareKeyboard();
-    method public void startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.KeyboardType keyboardType, androidx.compose.ui.text.input.ImeAction imeAction, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditOperation>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
+    method public void startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.KeyboardType keyboardType, androidx.compose.ui.text.input.ImeAction imeAction, androidx.compose.ui.text.input.KeyboardOptions keyboardOptions, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditOperation>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
     method public void stopInput();
   }
 
@@ -897,7 +914,7 @@
     method public void notifyFocusedRect(int token, androidx.compose.ui.geometry.Rect rect);
     method public void onStateUpdated(int token, androidx.compose.ui.text.input.TextFieldValue value);
     method public void showSoftwareKeyboard(int token);
-    method public int startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.KeyboardType keyboardType, androidx.compose.ui.text.input.ImeAction imeAction, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditOperation>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
+    method public int startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.KeyboardType keyboardType, androidx.compose.ui.text.input.ImeAction imeAction, androidx.compose.ui.text.input.KeyboardOptions keyboardOptions, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditOperation>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
     method public void stopInput(int token);
   }
 
diff --git a/compose/ui/ui-text/api/restricted_current.txt b/compose/ui/ui-text/api/restricted_current.txt
index d0156749..45bc91f 100644
--- a/compose/ui/ui-text/api/restricted_current.txt
+++ b/compose/ui/ui-text/api/restricted_current.txt
@@ -72,6 +72,9 @@
     method public static inline <R> R withStyle(androidx.compose.ui.text.AnnotatedString.Builder, androidx.compose.ui.text.ParagraphStyle style, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.AnnotatedString.Builder,? extends R> block);
   }
 
+  @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level, message="This is an experimental text API that may change without warning.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface ExperimentalTextApi {
+  }
+
   @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level, message="This is internal API that may change frequently and without warning.") @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface InternalTextApi {
   }
 
@@ -780,6 +783,20 @@
     method public void onImeAction(androidx.compose.ui.text.input.ImeAction imeAction);
   }
 
+  @androidx.compose.runtime.Immutable @androidx.compose.ui.text.ExperimentalTextApi public final class KeyboardOptions {
+    ctor public KeyboardOptions(boolean singleLine);
+    method public boolean component1();
+    method @androidx.compose.runtime.Immutable @androidx.compose.ui.text.ExperimentalTextApi public androidx.compose.ui.text.input.KeyboardOptions copy(boolean singleLine);
+    method public boolean getSingleLine();
+    property public final boolean singleLine;
+    field public static final androidx.compose.ui.text.input.KeyboardOptions.Companion Companion;
+  }
+
+  public static final class KeyboardOptions.Companion {
+    method public androidx.compose.ui.text.input.KeyboardOptions getDefault();
+    property public final androidx.compose.ui.text.input.KeyboardOptions Default;
+  }
+
   public enum KeyboardType {
     enum_constant public static final androidx.compose.ui.text.input.KeyboardType Ascii;
     enum_constant public static final androidx.compose.ui.text.input.KeyboardType Email;
@@ -826,7 +843,7 @@
     method public void notifyFocusedRect(androidx.compose.ui.geometry.Rect rect);
     method public void onStateUpdated(androidx.compose.ui.text.input.TextFieldValue value);
     method public void showSoftwareKeyboard();
-    method public void startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.KeyboardType keyboardType, androidx.compose.ui.text.input.ImeAction imeAction, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditOperation>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
+    method public void startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.KeyboardType keyboardType, androidx.compose.ui.text.input.ImeAction imeAction, androidx.compose.ui.text.input.KeyboardOptions keyboardOptions, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditOperation>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
     method public void stopInput();
   }
 
@@ -897,7 +914,7 @@
     method public void notifyFocusedRect(int token, androidx.compose.ui.geometry.Rect rect);
     method public void onStateUpdated(int token, androidx.compose.ui.text.input.TextFieldValue value);
     method public void showSoftwareKeyboard(int token);
-    method public int startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.KeyboardType keyboardType, androidx.compose.ui.text.input.ImeAction imeAction, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditOperation>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
+    method public int startInput(androidx.compose.ui.text.input.TextFieldValue value, androidx.compose.ui.text.input.KeyboardType keyboardType, androidx.compose.ui.text.input.ImeAction imeAction, androidx.compose.ui.text.input.KeyboardOptions keyboardOptions, kotlin.jvm.functions.Function1<? super java.util.List<? extends androidx.compose.ui.text.input.EditOperation>,kotlin.Unit> onEditCommand, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.ImeAction,kotlin.Unit> onImeActionPerformed);
     method public void stopInput(int token);
   }
 
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ExperimentalTextApi.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ExperimentalTextApi.kt
new file mode 100644
index 0000000..c8559ed
--- /dev/null
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/ExperimentalTextApi.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.ui.text
+
+@RequiresOptIn(
+    level = RequiresOptIn.Level.ERROR,
+    message = "This is an experimental text API that may change without warning."
+)
+@Target(
+    AnnotationTarget.CLASS,
+    AnnotationTarget.FUNCTION,
+    AnnotationTarget.PROPERTY
+)
+annotation class ExperimentalTextApi
\ No newline at end of file
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/KeyboardOptions.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/KeyboardOptions.kt
new file mode 100644
index 0000000..132f359
--- /dev/null
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/KeyboardOptions.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.ui.text.input
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.ui.text.ExperimentalTextApi
+
+/**
+ * The keyboard configuration options for text field. It is not guaranteed if a software keyboard
+ * will comply with the options provided here.
+ *
+ * @param singleLine signals the keyboard that the text field is single line and keyboard should
+ * not show enter action.
+ */
+@ExperimentalTextApi
+@Immutable
+data class KeyboardOptions(val singleLine: Boolean) {
+    companion object {
+        val Default = KeyboardOptions(
+            singleLine = false
+        )
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt
index 22097e8..af755c7 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt
@@ -17,6 +17,7 @@
 package androidx.compose.ui.text.input
 
 import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.util.annotation.VisibleForTesting
 
@@ -43,6 +44,7 @@
  * Provide a communication with platform text input service.
  */
 // Open for testing purposes.
+@OptIn(ExperimentalTextApi::class)
 open class TextInputService(private val platformTextInputService: PlatformTextInputService) {
 
     private var nextSessionToken: Int = 1
@@ -61,6 +63,7 @@
         value: TextFieldValue,
         keyboardType: KeyboardType,
         imeAction: ImeAction,
+        keyboardOptions: KeyboardOptions,
         onEditCommand: (List<EditOperation>) -> Unit,
         onImeActionPerformed: (ImeAction) -> Unit
     ): InputSessionToken {
@@ -68,6 +71,7 @@
             value,
             keyboardType,
             imeAction,
+            keyboardOptions,
             onEditCommand,
             onImeActionPerformed
         )
@@ -120,6 +124,7 @@
 /**
  * Platform specific text input service.
  */
+@OptIn(ExperimentalTextApi::class)
 interface PlatformTextInputService {
     /**
      * Start text input session for given client.
@@ -128,6 +133,7 @@
         value: TextFieldValue,
         keyboardType: KeyboardType,
         imeAction: ImeAction,
+        keyboardOptions: KeyboardOptions,
         onEditCommand: (List<EditOperation>) -> Unit,
         onImeActionPerformed: (ImeAction) -> Unit
     )
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextInputServiceTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextInputServiceTest.kt
index 1e812de..48ae334 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextInputServiceTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextInputServiceTest.kt
@@ -20,6 +20,7 @@
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardOptions
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.PlatformTextInputService
 import androidx.compose.ui.text.input.TextFieldValue
@@ -35,6 +36,7 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
+@OptIn(ExperimentalTextApi::class)
 @RunWith(JUnit4::class)
 class TextInputServiceTest {
 
@@ -48,6 +50,7 @@
             TextFieldValue(),
             KeyboardType.Text,
             ImeAction.NoAction,
+            KeyboardOptions.Default,
             {}, // onEditCommand
             {} // onImeActionPerformed
         )
@@ -55,6 +58,7 @@
             TextFieldValue(),
             KeyboardType.Text,
             ImeAction.NoAction,
+            KeyboardOptions.Default,
             {}, // onEditCommand
             {} // onImeActionPerformed
         )
@@ -72,6 +76,7 @@
             TextFieldValue(),
             KeyboardType.Text,
             ImeAction.NoAction,
+            KeyboardOptions.Default,
             {}, // onEditCommand
             {} // onImeActionPerformed
         )
@@ -90,6 +95,7 @@
             TextFieldValue(),
             KeyboardType.Text,
             ImeAction.NoAction,
+            KeyboardOptions.Default,
             {}, // onEditCommand
             {} // onImeActionPerformed
         )
@@ -99,6 +105,7 @@
             TextFieldValue(),
             KeyboardType.Text,
             ImeAction.NoAction,
+            KeyboardOptions.Default,
             {}, // onEditCommand
             {} // onImeActionPerformed
         )
@@ -117,6 +124,7 @@
             TextFieldValue(),
             KeyboardType.Text,
             ImeAction.NoAction,
+            KeyboardOptions.Default,
             {}, // onEditCommand
             {} // onImeActionPerformed
         )
@@ -135,6 +143,7 @@
             TextFieldValue(),
             KeyboardType.Text,
             ImeAction.NoAction,
+            KeyboardOptions.Default,
             {}, // onEditCommand
             {} // onImeActionPerformed
         )
@@ -144,6 +153,7 @@
             TextFieldValue(),
             KeyboardType.Text,
             ImeAction.NoAction,
+            KeyboardOptions.Default,
             {}, // onEditCommand
             {} // onImeActionPerformed
         )
@@ -162,6 +172,7 @@
             TextFieldValue(),
             KeyboardType.Text,
             ImeAction.NoAction,
+            KeyboardOptions.Default,
             {}, // onEditCommand
             {} // onImeActionPerformed
         )
@@ -181,6 +192,7 @@
             TextFieldValue(),
             KeyboardType.Text,
             ImeAction.NoAction,
+            KeyboardOptions.Default,
             {}, // onEditCommand
             {} // onImeActionPerformed
         )
@@ -190,6 +202,7 @@
             TextFieldValue(),
             KeyboardType.Text,
             ImeAction.NoAction,
+            KeyboardOptions.Default,
             {}, // onEditCommand
             {} // onImeActionPerformed
         )
@@ -209,6 +222,7 @@
             TextFieldValue(),
             KeyboardType.Text,
             ImeAction.NoAction,
+            KeyboardOptions.Default,
             {}, // onEditCommand
             {} // onImeActionPerformed
         )
@@ -228,6 +242,7 @@
             TextFieldValue(),
             KeyboardType.Text,
             ImeAction.NoAction,
+            KeyboardOptions.Default,
             {}, // onEditCommand
             {} // onImeActionPerformed
         )
@@ -237,6 +252,7 @@
             TextFieldValue(),
             KeyboardType.Text,
             ImeAction.NoAction,
+            KeyboardOptions.Default,
             {}, // onEditCommand
             {} // onImeActionPerformed
         )
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.kt
index ff90fba..eb87d61 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.kt
@@ -24,12 +24,14 @@
 import android.view.inputmethod.InputConnection
 import android.view.inputmethod.InputMethodManager
 import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.TextRange
 import kotlin.math.roundToInt
 
 /**
  * Provide Android specific input service with the Operating System.
  */
+@OptIn(ExperimentalTextApi::class)
 internal class TextInputServiceAndroid(val view: View) : PlatformTextInputService {
     /** True if the currently editable composable has connected */
     private var editorHasFocus = false
@@ -44,8 +46,8 @@
     private var state = TextFieldValue(text = "", selection = TextRange.Zero)
     private var keyboardType = KeyboardType.Text
     private var imeAction = ImeAction.Unspecified
+    private var keyboardOptions = KeyboardOptions.Default
     private var ic: RecordingInputConnection? = null
-
     private var focusedRect: android.graphics.Rect? = null
 
     /**
@@ -78,7 +80,7 @@
         if (!editorHasFocus) {
             return null
         }
-        fillEditorInfo(keyboardType, imeAction, outAttrs)
+        fillEditorInfo(keyboardType, imeAction, keyboardOptions, outAttrs)
 
         return RecordingInputConnection(
             initState = state,
@@ -103,6 +105,7 @@
         value: TextFieldValue,
         keyboardType: KeyboardType,
         imeAction: ImeAction,
+        keyboardOptions: KeyboardOptions,
         onEditCommand: (List<EditOperation>) -> Unit,
         onImeActionPerformed: (ImeAction) -> Unit
     ) {
@@ -111,6 +114,7 @@
         state = value
         this.keyboardType = keyboardType
         this.imeAction = imeAction
+        this.keyboardOptions = keyboardOptions
         this.onEditCommand = onEditCommand
         this.onImeActionPerformed = onImeActionPerformed
 
@@ -168,10 +172,20 @@
     private fun fillEditorInfo(
         keyboardType: KeyboardType,
         imeAction: ImeAction,
+        keyboardOptions: KeyboardOptions,
         outInfo: EditorInfo
     ) {
         outInfo.imeOptions = when (imeAction) {
-            ImeAction.Unspecified -> EditorInfo.IME_ACTION_UNSPECIFIED
+            ImeAction.Unspecified -> {
+                if (keyboardOptions.singleLine) {
+                    // this is the last resort to enable single line
+                    // Android IME still show return key even if multi line is not send
+                    // TextView.java#onCreateInputConnection
+                    EditorInfo.IME_ACTION_DONE
+                } else {
+                    EditorInfo.IME_ACTION_UNSPECIFIED
+                }
+            }
             ImeAction.NoAction -> EditorInfo.IME_ACTION_NONE
             ImeAction.Go -> EditorInfo.IME_ACTION_GO
             ImeAction.Next -> EditorInfo.IME_ACTION_NEXT
@@ -204,7 +218,14 @@
             }
             else -> throw IllegalArgumentException("Unknown KeyboardType: $keyboardType")
         }
-        outInfo.imeOptions =
-            outInfo.imeOptions or outInfo.imeOptions or EditorInfo.IME_FLAG_NO_FULLSCREEN
+
+        if (!keyboardOptions.singleLine) {
+            // TextView.java#setInputTypeSingleLine
+            outInfo.inputType = outInfo.inputType or InputType.TYPE_TEXT_FLAG_MULTI_LINE
+            // TextView.java#onCreateInputConnection
+            outInfo.imeOptions = outInfo.imeOptions or EditorInfo.IME_FLAG_NO_ENTER_ACTION
+        }
+
+        outInfo.imeOptions = outInfo.imeOptions or EditorInfo.IME_FLAG_NO_FULLSCREEN
     }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopPlatformInput.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopPlatformInput.kt
index bab8084..f8102ed 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopPlatformInput.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopPlatformInput.kt
@@ -16,11 +16,13 @@
 package androidx.compose.ui.platform
 
 import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.input.BackspaceKeyEditOp
 import androidx.compose.ui.text.input.CommitTextEditOp
 import androidx.compose.ui.text.input.DeleteSurroundingTextInCodePointsEditOp
 import androidx.compose.ui.text.input.EditOperation
 import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardOptions
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.MoveCursorEditOp
 import androidx.compose.ui.text.input.PlatformTextInputService
@@ -47,6 +49,7 @@
     fun locationOnScreen(): Point
 }
 
+@OptIn(ExperimentalTextApi::class)
 internal class DesktopPlatformInput(val component: DesktopComponent) :
     PlatformTextInputService {
     data class CurrentInput(
@@ -66,6 +69,7 @@
         value: TextFieldValue,
         keyboardType: KeyboardType,
         imeAction: ImeAction,
+        keyboardOptions: KeyboardOptions,
         onEditCommand: (List<EditOperation>) -> Unit,
         onImeActionPerformed: (ImeAction) -> Unit
     ) {
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/TextInputServiceAndroidTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/TextInputServiceAndroidTest.kt
index dd0a6d5..9c44467 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/TextInputServiceAndroidTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/TextInputServiceAndroidTest.kt
@@ -21,7 +21,9 @@
 import android.view.View
 import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.InputMethodManager
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardOptions
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.input.TextInputServiceAndroid
@@ -30,11 +32,13 @@
 import com.nhaarman.mockitokotlin2.mock
 import com.nhaarman.mockitokotlin2.whenever
 import org.junit.Assert.assertTrue
+import org.junit.Assert.assertFalse
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 
+@OptIn(ExperimentalTextApi::class)
 @SmallTest
 @RunWith(JUnit4::class)
 class TextInputServiceAndroidTest {
@@ -58,6 +62,7 @@
             TextFieldValue(""),
             KeyboardType.Text,
             ImeAction.Unspecified,
+            KeyboardOptions.Default,
             onEditCommand = {},
             onImeActionPerformed = {}
         )
@@ -78,6 +83,7 @@
             TextFieldValue(""),
             KeyboardType.Ascii,
             ImeAction.Unspecified,
+            KeyboardOptions.Default,
             onEditCommand = {},
             onImeActionPerformed = {}
         )
@@ -99,6 +105,7 @@
             TextFieldValue(""),
             KeyboardType.Number,
             ImeAction.Unspecified,
+            KeyboardOptions.Default,
             onEditCommand = {},
             onImeActionPerformed = {}
         )
@@ -119,6 +126,7 @@
             TextFieldValue(""),
             KeyboardType.Phone,
             ImeAction.Unspecified,
+            KeyboardOptions.Default,
             onEditCommand = {},
             onImeActionPerformed = {}
         )
@@ -139,6 +147,7 @@
             TextFieldValue(""),
             KeyboardType.Uri,
             ImeAction.Unspecified,
+            KeyboardOptions.Default,
             onEditCommand = {},
             onImeActionPerformed = {}
         )
@@ -160,6 +169,7 @@
             TextFieldValue(""),
             KeyboardType.Email,
             ImeAction.Unspecified,
+            KeyboardOptions.Default,
             onEditCommand = {},
             onImeActionPerformed = {}
         )
@@ -181,6 +191,7 @@
             TextFieldValue(""),
             KeyboardType.Password,
             ImeAction.Unspecified,
+            KeyboardOptions.Default,
             onEditCommand = {},
             onImeActionPerformed = {}
         )
@@ -202,6 +213,7 @@
             TextFieldValue(""),
             KeyboardType.NumberPassword,
             ImeAction.Unspecified,
+            KeyboardOptions.Default,
             onEditCommand = {},
             onImeActionPerformed = {}
         )
@@ -223,6 +235,7 @@
             TextFieldValue(""),
             KeyboardType.Ascii,
             ImeAction.NoAction,
+            KeyboardOptions.Default,
             onEditCommand = {},
             onImeActionPerformed = {}
         )
@@ -244,6 +257,7 @@
             TextFieldValue(""),
             KeyboardType.Ascii,
             ImeAction.Go,
+            KeyboardOptions.Default,
             onEditCommand = {},
             onImeActionPerformed = {}
         )
@@ -265,6 +279,7 @@
             TextFieldValue(""),
             KeyboardType.Ascii,
             ImeAction.Next,
+            KeyboardOptions.Default,
             onEditCommand = {},
             onImeActionPerformed = {}
         )
@@ -286,6 +301,7 @@
             TextFieldValue(""),
             KeyboardType.Ascii,
             ImeAction.Previous,
+            KeyboardOptions.Default,
             onEditCommand = {},
             onImeActionPerformed = {}
         )
@@ -307,6 +323,7 @@
             TextFieldValue(""),
             KeyboardType.Ascii,
             ImeAction.Search,
+            KeyboardOptions.Default,
             onEditCommand = {},
             onImeActionPerformed = {}
         )
@@ -328,6 +345,7 @@
             TextFieldValue(""),
             KeyboardType.Ascii,
             ImeAction.Send,
+            KeyboardOptions.Default,
             onEditCommand = {},
             onImeActionPerformed = {}
         )
@@ -349,6 +367,7 @@
             TextFieldValue(""),
             KeyboardType.Ascii,
             ImeAction.Done,
+            KeyboardOptions.Default,
             onEditCommand = {},
             onImeActionPerformed = {}
         )
@@ -363,4 +382,58 @@
             )
         }
     }
+
+    @Test
+    fun test_fill_editor_info_multi_line() {
+        textInputService.startInput(
+            TextFieldValue(""),
+            KeyboardType.Ascii,
+            ImeAction.Done,
+            KeyboardOptions(singleLine = false),
+            onEditCommand = {},
+            onImeActionPerformed = {}
+        )
+
+        EditorInfo().let { info ->
+            textInputService.createInputConnection(info)
+            assertFalse((InputType.TYPE_TEXT_FLAG_MULTI_LINE and info.inputType) == 0)
+            assertFalse((EditorInfo.IME_FLAG_NO_ENTER_ACTION and info.imeOptions) == 0)
+        }
+    }
+
+    @Test
+    fun test_fill_editor_info_single_line() {
+        textInputService.startInput(
+            TextFieldValue(""),
+            KeyboardType.Ascii,
+            ImeAction.Done,
+            KeyboardOptions(singleLine = true),
+            onEditCommand = {},
+            onImeActionPerformed = {}
+        )
+
+        EditorInfo().let { info ->
+            textInputService.createInputConnection(info)
+            assertTrue((InputType.TYPE_TEXT_FLAG_MULTI_LINE and info.inputType) == 0)
+            assertTrue((EditorInfo.IME_FLAG_NO_ENTER_ACTION and info.imeOptions) == 0)
+        }
+    }
+
+    @Test
+    fun test_fill_editor_info_single_line_changes_ime_from_unspecified_to_done() {
+        textInputService.startInput(
+            TextFieldValue(""),
+            KeyboardType.Text,
+            ImeAction.Unspecified,
+            KeyboardOptions(singleLine = true),
+            onEditCommand = {},
+            onImeActionPerformed = {}
+        )
+
+        EditorInfo().let { info ->
+            textInputService.createInputConnection(info)
+            assertFalse((EditorInfo.IME_ACTION_DONE and info.imeOptions) == 0)
+            assertTrue((EditorInfo.IME_ACTION_UNSPECIFIED and info.imeOptions) == 0)
+        }
+    }
 }
\ No newline at end of file
diff --git a/ui/ui-test/src/commonMain/kotlin/androidx/ui/test/TextInputServiceForTests.kt b/ui/ui-test/src/commonMain/kotlin/androidx/ui/test/TextInputServiceForTests.kt
index 8cd58f7..0c38043 100644
--- a/ui/ui-test/src/commonMain/kotlin/androidx/ui/test/TextInputServiceForTests.kt
+++ b/ui/ui-test/src/commonMain/kotlin/androidx/ui/test/TextInputServiceForTests.kt
@@ -16,10 +16,12 @@
 
 package androidx.ui.test
 
+import androidx.compose.ui.text.ExperimentalTextApi
 import androidx.compose.ui.text.input.EditOperation
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.InputSessionToken
+import androidx.compose.ui.text.input.KeyboardOptions
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.PlatformTextInputService
 import androidx.compose.ui.text.input.TextInputService
@@ -31,6 +33,7 @@
  * accept input from the IME. Here we grab that callback so we can fetch it commands the same
  * way IME would do.
  */
+@OptIn(ExperimentalTextApi::class)
 internal class TextInputServiceForTests(
     platformTextInputService: PlatformTextInputService
 ) : TextInputService(platformTextInputService) {
@@ -42,6 +45,7 @@
         value: TextFieldValue,
         keyboardType: KeyboardType,
         imeAction: ImeAction,
+        keyboardOptions: KeyboardOptions,
         onEditCommand: (List<EditOperation>) -> Unit,
         onImeActionPerformed: (ImeAction) -> Unit
     ): InputSessionToken {
@@ -51,6 +55,7 @@
             value,
             keyboardType,
             imeAction,
+            keyboardOptions,
             onEditCommand,
             onImeActionPerformed
         )