Merge "Add Material 2 wrappers for BasicSecureTextField" into androidx-main
diff --git a/compose/material/material/api/current.txt b/compose/material/material/api/current.txt
index db7832e..c7fe760 100644
--- a/compose/material/material/api/current.txt
+++ b/compose/material/material/api/current.txt
@@ -688,6 +688,11 @@
     property public final androidx.compose.material.SnackbarHostState snackbarHostState;
   }
 
+  public final class SecureTextFieldKt {
+    method @androidx.compose.runtime.Composable public static void OutlinedSecureTextField(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 boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional int textObfuscationMode, optional char textObfuscationCharacter, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
+    method @androidx.compose.runtime.Composable public static void SecureTextField(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 boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional int textObfuscationMode, optional char textObfuscationCharacter, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
+  }
+
   @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public interface SelectableChipColors {
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> backgroundColor(boolean enabled, boolean selected);
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled, boolean selected);
diff --git a/compose/material/material/api/restricted_current.txt b/compose/material/material/api/restricted_current.txt
index db7832e..c7fe760 100644
--- a/compose/material/material/api/restricted_current.txt
+++ b/compose/material/material/api/restricted_current.txt
@@ -688,6 +688,11 @@
     property public final androidx.compose.material.SnackbarHostState snackbarHostState;
   }
 
+  public final class SecureTextFieldKt {
+    method @androidx.compose.runtime.Composable public static void OutlinedSecureTextField(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 boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional int textObfuscationMode, optional char textObfuscationCharacter, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
+    method @androidx.compose.runtime.Composable public static void SecureTextField(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 boolean isError, optional androidx.compose.foundation.text.input.InputTransformation? inputTransformation, optional int textObfuscationMode, optional char textObfuscationCharacter, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.input.KeyboardActionHandler? onKeyboardAction, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material.TextFieldColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
+  }
+
   @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public interface SelectableChipColors {
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> backgroundColor(boolean enabled, boolean selected);
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled, boolean selected);
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt
index 8b5dca9..07927ce 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/TextFieldSamples.kt
@@ -25,6 +25,7 @@
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.TextObfuscationMode
 import androidx.compose.foundation.text.input.clearText
 import androidx.compose.foundation.text.input.rememberTextFieldState
 import androidx.compose.material.ContentAlpha
@@ -33,6 +34,7 @@
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.OutlinedTextField
+import androidx.compose.material.SecureTextField
 import androidx.compose.material.Text
 import androidx.compose.material.TextField
 import androidx.compose.material.TextFieldDefaults
@@ -163,24 +165,21 @@
     }
 }
 
-// TODO: update sample with TextFieldState once we have wrappers for BasicSecureTextField
+@Sampled
 @Composable
 fun PasswordTextField() {
-    var password by rememberSaveable { mutableStateOf("") }
     var passwordHidden by rememberSaveable { mutableStateOf(true) }
-    TextField(
-        value = password,
-        onValueChange = { password = it },
-        singleLine = true,
+    SecureTextField(
+        state = rememberTextFieldState(),
         label = { Text("Enter password") },
-        visualTransformation =
-            if (passwordHidden) PasswordVisualTransformation() else VisualTransformation.None,
-        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
+        textObfuscationMode =
+            if (passwordHidden) TextObfuscationMode.RevealLastTyped
+            else TextObfuscationMode.Visible,
         trailingIcon = {
             IconButton(onClick = { passwordHidden = !passwordHidden }) {
                 val visibilityIcon =
                     if (passwordHidden) Icons.Filled.Visibility else Icons.Filled.VisibilityOff
-                // Please provide localized description for accessibility services
+                // Provide localized description for accessibility services
                 val description = if (passwordHidden) "Show password" else "Hide password"
                 Icon(imageVector = visibilityIcon, contentDescription = description)
             }
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/SecureTextFieldScreenshotTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/SecureTextFieldScreenshotTest.kt
new file mode 100644
index 0000000..6ca996f
--- /dev/null
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/SecureTextFieldScreenshotTest.kt
@@ -0,0 +1,115 @@
+/*
+ * 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.textfield
+
+import android.os.Build
+import androidx.compose.foundation.text.input.TextObfuscationMode
+import androidx.compose.foundation.text.input.rememberTextFieldState
+import androidx.compose.material.GOLDEN_MATERIAL
+import androidx.compose.material.OutlinedSecureTextField
+import androidx.compose.material.SecureTextField
+import androidx.compose.material.Text
+import androidx.compose.material.setMaterialContent
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+class SecureTextFieldScreenshotTest {
+    private val TextFieldTag = "TextField"
+
+    @get:Rule val rule = createComposeRule()
+
+    @get:Rule val screenshotRule = AndroidXScreenshotTestRule(GOLDEN_MATERIAL)
+
+    @Test
+    fun secureTextField_filled_noObfuscation() {
+        rule.setMaterialContent {
+            SecureTextField(
+                state = rememberTextFieldState("password"),
+                label = { Text("Label") },
+                modifier = Modifier.testTag(TextFieldTag),
+                textObfuscationMode = TextObfuscationMode.Visible,
+            )
+        }
+
+        assertAgainstGolden("secureTextField_filled_noObfuscation")
+    }
+
+    @Test
+    fun secureTextField_outlined_noObfuscation() {
+        rule.setMaterialContent {
+            OutlinedSecureTextField(
+                state = rememberTextFieldState("password"),
+                label = { Text("Label") },
+                modifier = Modifier.testTag(TextFieldTag),
+                textObfuscationMode = TextObfuscationMode.Visible,
+            )
+        }
+
+        assertAgainstGolden("secureTextField_outlined_noObfuscation")
+    }
+
+    @Test
+    fun secureTextField_filled_customObfuscationCharacter() {
+        rule.setMaterialContent {
+            SecureTextField(
+                state = rememberTextFieldState("password"),
+                label = { Text("Label") },
+                modifier = Modifier.testTag(TextFieldTag),
+                textObfuscationMode = TextObfuscationMode.Hidden,
+                textObfuscationCharacter = '*',
+            )
+        }
+
+        assertAgainstGolden("secureTextField_filled_customObfuscationCharacter")
+    }
+
+    @Test
+    fun secureTextField_outlined_customObfuscationCharacter() {
+        rule.setMaterialContent {
+            OutlinedSecureTextField(
+                state = rememberTextFieldState("password"),
+                label = { Text("Label") },
+                modifier = Modifier.testTag(TextFieldTag),
+                textObfuscationMode = TextObfuscationMode.Hidden,
+                textObfuscationCharacter = '*',
+            )
+        }
+
+        assertAgainstGolden("secureTextField_outlined_customObfuscationCharacter")
+    }
+
+    private fun assertAgainstGolden(goldenIdentifier: String) {
+        rule
+            .onNodeWithTag(TextFieldTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, goldenIdentifier)
+    }
+}
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/SecureTextFieldTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/SecureTextFieldTest.kt
new file mode 100644
index 0000000..d3f9dca
--- /dev/null
+++ b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/textfield/SecureTextFieldTest.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.textfield
+
+import androidx.compose.foundation.text.input.rememberTextFieldState
+import androidx.compose.material.OutlinedSecureTextField
+import androidx.compose.material.SecureTextField
+import androidx.compose.material.setMaterialContent
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertTextEquals
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class SecureTextFieldTest {
+    private val TextFieldTag = "TextField"
+
+    @get:Rule val rule = createComposeRule()
+
+    @Test
+    fun testSecureTextField_filled_textContentIsNotObfuscated() {
+        rule.setMaterialContent {
+            SecureTextField(
+                state = rememberTextFieldState("password"),
+                modifier = Modifier.testTag(TextFieldTag)
+            )
+        }
+
+        rule.onNodeWithTag(TextFieldTag).assertTextEquals("password")
+    }
+
+    @Test
+    fun testSecureTextField_outlined_textContentIsNotObfuscated() {
+        rule.setMaterialContent {
+            OutlinedSecureTextField(
+                state = rememberTextFieldState("password"),
+                modifier = Modifier.testTag(TextFieldTag)
+            )
+        }
+
+        rule.onNodeWithTag(TextFieldTag).assertTextEquals("password")
+    }
+}
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
index 076cf77..ac52028 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
@@ -79,7 +79,7 @@
 import kotlin.math.roundToInt
 
 /**
- * <a href="https://material.io/components/text-fields#outlined-text-field" class="external"
+ * <a href="https://m2.material.io/components/text-fields#outlined-text-field" class="external"
  * target="_blank">Material Design outlined text field</a>.
  *
  * Outlined text fields have less visual emphasis than filled text fields. When they appear in
@@ -89,6 +89,9 @@
  * ![Outlined text field
  * image](https://developer.android.com/images/reference/androidx/compose/material/outlined-text-field.png)
  *
+ * If you are looking for a filled version, see [TextField]. For a text field specifically designed
+ * for passwords or other secure content, see [OutlinedSecureTextField].
+ *
  * This overload of [OutlinedTextField] uses [TextFieldState] to keep track of its text content and
  * position of the cursor or selection.
  *
@@ -248,7 +251,7 @@
 }
 
 /**
- * <a href="https://material.io/components/text-fields#outlined-text-field" class="external"
+ * <a href="https://m2.material.io/components/text-fields#outlined-text-field" class="external"
  * target="_blank">Material Design outlined text field</a>.
  *
  * Outlined text fields have less visual emphasis than filled text fields. When they appear in
@@ -455,7 +458,7 @@
 }
 
 /**
- * <a href="https://material.io/components/text-fields#outlined-text-field" class="external"
+ * <a href="https://m2.material.io/components/text-fields#outlined-text-field" class="external"
  * target="_blank">Material Design outlined text field</a>.
  *
  * Outlined text fields have less visual emphasis than filled text fields. When they appear in
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SecureTextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SecureTextField.kt
new file mode 100644
index 0000000..948751b
--- /dev/null
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/SecureTextField.kt
@@ -0,0 +1,320 @@
+/*
+ * 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
+
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.text.BasicSecureTextField
+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.TextFieldState
+import androidx.compose.foundation.text.input.TextObfuscationMode
+import androidx.compose.material.TextFieldDefaults.indicatorLine
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.takeOrElse
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.semantics.semantics
+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.VisualTransformation
+
+/**
+ * <a href="https://m2.material.io/components/text-fields#filled-text-field" class="external"
+ * target="_blank">Material Design filled text field for secure content</a>.
+ *
+ * Text fields allow users to enter text into a UI. [SecureTextField] is specifically designed for
+ * password entry fields. It only supports a single line of content and comes with default settings
+ * that are appropriate for entering secure content. Additionally, some context menu actions like
+ * cut, copy, and drag are disabled for added security.
+ *
+ * Filled text fields have more visual emphasis than outlined text fields, making them stand out
+ * when surrounded by other content and components. For an outlined version, see
+ * [OutlinedSecureTextField].
+ *
+ * Example of a password text field:
+ *
+ * @sample androidx.compose.material.samples.PasswordTextField
+ * @param state [TextFieldState] object that holds the internal editing state of this text field.
+ * @param modifier a [Modifier] for this text field.
+ * @param enabled controls the enabled state of the [TextField]. When `false`, the text field will
+ *   be neither editable nor focusable, the input of the text field will not be selectable, visually
+ *   text field will appear in the disabled UI state.
+ * @param readOnly controls the editable state of the [TextField]. When `true`, the text field can
+ *   not be modified. However, a user can focus it. Read-only text fields are usually used to
+ *   display pre-filled forms that user can not edit.
+ * @param textStyle the style to be applied to the input text. The default [textStyle] uses the
+ *   [LocalTextStyle] defined by the theme.
+ * @param label the optional label to be displayed inside the text field container. The default text
+ *   style for internal [Text] is [Typography.caption] when the text field is in focus and
+ *   [Typography.subtitle1] when the text field is not in focus.
+ * @param placeholder the optional placeholder to be displayed when the text field is in focus and
+ *   the input text is empty. The default text style for internal [Text] is [Typography.subtitle1].
+ * @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 isError indicates if the text field's current value is in error. If set to true, the
+ *   label, bottom indicator and trailing icon by default will be displayed in error color.
+ * @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 textObfuscationMode the method used to obscure the input text.
+ * @param textObfuscationCharacter the character to use while obfuscating the text. It doesn't have
+ *   an effect when [textObfuscationMode] is set to [TextObfuscationMode.Visible].
+ * @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 shape the shape of the text field's container
+ * @param colors [TextFieldColors] that will be used to resolve color of the text, content
+ *   (including label, placeholder, leading and trailing icons, indicator line) and background for
+ *   this text field in different states. See [TextFieldDefaults.textFieldColors]
+ * @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.
+ */
+@Composable
+fun SecureTextField(
+    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,
+    isError: Boolean = false,
+    inputTransformation: InputTransformation? = null,
+    textObfuscationMode: TextObfuscationMode = TextObfuscationMode.RevealLastTyped,
+    textObfuscationCharacter: Char = DefaultObfuscationCharacter,
+    keyboardOptions: KeyboardOptions = SecureTextFieldKeyboardOptions,
+    onKeyboardAction: KeyboardActionHandler? = null,
+    shape: Shape = TextFieldDefaults.TextFieldShape,
+    colors: TextFieldColors = TextFieldDefaults.textFieldColors(),
+    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 { colors.textColor(enabled).value }
+    val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
+
+    @OptIn(ExperimentalMaterialApi::class)
+    BasicSecureTextField(
+        state = state,
+        modifier =
+            modifier
+                .indicatorLine(enabled, isError, interactionSource, colors)
+                .defaultErrorSemantics(isError, getString(Strings.DefaultErrorMessage))
+                .defaultMinSize(
+                    minWidth = TextFieldDefaults.MinWidth,
+                    minHeight = TextFieldDefaults.MinHeight
+                ),
+        enabled = enabled,
+        readOnly = readOnly,
+        textStyle = mergedTextStyle,
+        cursorBrush = SolidColor(colors.cursorColor(isError).value),
+        inputTransformation = inputTransformation,
+        textObfuscationMode = textObfuscationMode,
+        textObfuscationCharacter = textObfuscationCharacter,
+        keyboardOptions = keyboardOptions,
+        onKeyboardAction = onKeyboardAction,
+        interactionSource = interactionSource,
+        decorator = { innerTextField ->
+            TextFieldDefaults.TextFieldDecorationBox(
+                value = state.text.toString(),
+                visualTransformation = VisualTransformation.None,
+                innerTextField = innerTextField,
+                placeholder = placeholder,
+                label = label,
+                leadingIcon = leadingIcon,
+                trailingIcon = trailingIcon,
+                singleLine = true,
+                enabled = enabled,
+                isError = isError,
+                interactionSource = interactionSource,
+                shape = shape,
+                colors = colors,
+            )
+        }
+    )
+}
+
+/**
+ * <a href="https://m2.material.io/components/text-fields#outlined-text-field" class="external"
+ * target="_blank">Material Design outlined text field for secure content</a>.
+ *
+ * Text fields allow users to enter text into a UI. [OutlinedSecureTextField] is specifically
+ * designed for password entry fields. It only supports a single line of content and comes with
+ * default settings that are appropriate for entering secure content. Additionally, some context
+ * menu actions like cut, copy, and drag are disabled for added security.
+ *
+ * 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. For a filled version, see [SecureTextField].
+ *
+ * @param state [TextFieldState] object that holds the internal editing state of this text field.
+ * @param modifier a [Modifier] for this text field
+ * @param enabled controls the enabled state of the [OutlinedTextField]. When `false`, the text
+ *   field will be neither editable nor focusable, the input of the text field will not be
+ *   selectable, visually text field will appear in the disabled UI state
+ * @param readOnly controls the editable state of the [OutlinedTextField]. When `true`, the text
+ *   field can not 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 user can not edit
+ * @param textStyle the style to be applied to the input text. The default [textStyle] uses the
+ *   [LocalTextStyle] defined by the theme
+ * @param label the optional label to be displayed inside the text field container. The default text
+ *   style for internal [Text] is [Typography.caption] when the text field is in focus and
+ *   [Typography.subtitle1] when the text field is not in focus
+ * @param placeholder the optional placeholder to be displayed when the text field is in focus and
+ *   the input text is empty. The default text style for internal [Text] is [Typography.subtitle1]
+ * @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 isError indicates if the text field's current value is in error. If set to true, the
+ *   label, bottom indicator and trailing icon by default will be displayed in error color
+ * @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 textObfuscationMode the method used to obscure the input text.
+ * @param textObfuscationCharacter the character to use while obfuscating the text. It doesn't have
+ *   an effect when [textObfuscationMode] is set to [TextObfuscationMode.Visible].
+ * @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 shape the shape of the text field's border
+ * @param colors [TextFieldColors] that will be used to resolve color of the text and content
+ *   (including label, placeholder, leading and trailing icons, border) for this text field in
+ *   different states. See [TextFieldDefaults.outlinedTextFieldColors]
+ * @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.
+ */
+@Composable
+fun OutlinedSecureTextField(
+    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,
+    isError: Boolean = false,
+    inputTransformation: InputTransformation? = null,
+    textObfuscationMode: TextObfuscationMode = TextObfuscationMode.RevealLastTyped,
+    textObfuscationCharacter: Char = DefaultObfuscationCharacter,
+    keyboardOptions: KeyboardOptions = SecureTextFieldKeyboardOptions,
+    onKeyboardAction: KeyboardActionHandler? = null,
+    shape: Shape = TextFieldDefaults.OutlinedTextFieldShape,
+    colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors(),
+    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 { colors.textColor(enabled).value }
+    val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
+
+    val density = LocalDensity.current
+
+    @OptIn(ExperimentalMaterialApi::class)
+    BasicSecureTextField(
+        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 = TextFieldDefaults.MinWidth,
+                    minHeight = TextFieldDefaults.MinHeight
+                ),
+        enabled = enabled,
+        readOnly = readOnly,
+        textStyle = mergedTextStyle,
+        cursorBrush = SolidColor(colors.cursorColor(isError).value),
+        inputTransformation = inputTransformation,
+        textObfuscationMode = textObfuscationMode,
+        textObfuscationCharacter = textObfuscationCharacter,
+        keyboardOptions = keyboardOptions,
+        onKeyboardAction = onKeyboardAction,
+        interactionSource = interactionSource,
+        decorator = { innerTextField ->
+            TextFieldDefaults.OutlinedTextFieldDecorationBox(
+                value = state.text.toString(),
+                visualTransformation = VisualTransformation.None,
+                innerTextField = innerTextField,
+                placeholder = placeholder,
+                label = label,
+                leadingIcon = leadingIcon,
+                trailingIcon = trailingIcon,
+                singleLine = true,
+                enabled = enabled,
+                isError = isError,
+                interactionSource = interactionSource,
+                shape = shape,
+                colors = colors,
+                border = {
+                    TextFieldDefaults.BorderBox(enabled, isError, interactionSource, colors, shape)
+                }
+            )
+        }
+    )
+}
+
+private val SecureTextFieldKeyboardOptions =
+    KeyboardOptions(autoCorrectEnabled = false, keyboardType = KeyboardType.Password)
+
+private const val DefaultObfuscationCharacter: Char = '\u2022'
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
index 20fa7fe..c7a23d0 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
@@ -77,7 +77,7 @@
 import kotlin.math.roundToInt
 
 /**
- * <a href="https://material.io/components/text-fields#filled-text-field" class="external"
+ * <a href="https://m2.material.io/components/text-fields#filled-text-field" class="external"
  * target="_blank">Material Design filled text field</a>.
  *
  * Filled text fields have more visual emphasis than outlined text fields, making them stand out
@@ -255,7 +255,7 @@
 }
 
 /**
- * <a href="https://material.io/components/text-fields#filled-text-field" class="external"
+ * <a href="https://m2.material.io/components/text-fields#filled-text-field" class="external"
  * target="_blank">Material Design filled text field</a>.
  *
  * Filled text fields have more visual emphasis than outlined text fields, making them stand out
@@ -444,7 +444,7 @@
 }
 
 /**
- * <a href="https://material.io/components/text-fields#filled-text-field" class="external"
+ * <a href="https://m2.material.io/components/text-fields#filled-text-field" class="external"
  * target="_blank">Material Design filled text field</a>.
  *
  * Filled text fields have more visual emphasis than outlined text fields, making them stand out
@@ -453,7 +453,8 @@
  * ![Filled text field
  * image](https://developer.android.com/images/reference/androidx/compose/material/filled-text-field.png)
  *
- * If you are looking for an outlined version, see [OutlinedTextField].
+ * If you are looking for an outlined version, see [OutlinedTextField]. For a text field
+ * specifically designed for passwords or other secure content, see [SecureTextField].
  *
  * 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