Skip to content

Commit 5835a3c

Browse files
committed
Add API and launcher for NonEditableBasicTextField
1 parent c23b3d6 commit 5835a3c

File tree

5 files changed

+150
-1
lines changed

5 files changed

+150
-1
lines changed

Diff for: composables/api/current.api

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ package com.google.android.horologist.composables {
1010
method @androidx.compose.runtime.Composable @com.google.android.horologist.annotations.ExperimentalHorologistApi public static void MarqueeText(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional androidx.compose.ui.text.TextStyle style, optional int textAlign, optional float followGap, optional float edgeGradientWidth, optional float marqueeDpPerSecond, optional long pauseTime);
1111
}
1212

13+
public final class NonEditableBasicTextFieldKt {
14+
method @androidx.compose.runtime.Composable public static void NonEditableBasicTextField(optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onTextChanged, androidx.compose.ui.text.input.TextFieldValue value, optional boolean enabled, optional boolean readOnly, optional androidx.compose.ui.text.TextStyle textStyle, optional androidx.compose.foundation.text.KeyboardOptions keyboardOptions, optional androidx.compose.foundation.text.KeyboardActions keyboardActions, optional boolean singleLine, optional int maxLines, optional int minLines, optional androidx.compose.ui.text.input.VisualTransformation visualTransformation, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Brush cursorBrush, optional kotlin.jvm.functions.Function1<? super kotlin.jvm.functions.Function0<kotlin.Unit>,kotlin.Unit> decorationBox);
15+
}
16+
1317
public final class PlaceholderChipKt {
1418
method @androidx.compose.runtime.Composable @com.google.android.horologist.annotations.ExperimentalHorologistApi public static void PlaceholderChip(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.wear.compose.material.PlaceholderState placeholderState, optional boolean secondaryLabel, optional boolean icon, optional androidx.wear.compose.material.ChipColors colors, optional boolean enabled, optional String contentDescription);
1519
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright 2024 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.android.horologist.composables
18+
19+
import android.app.Activity.RESULT_OK
20+
import android.content.Context
21+
import android.content.Intent
22+
import androidx.activity.compose.rememberLauncherForActivityResult
23+
import androidx.activity.result.contract.ActivityResultContract
24+
import androidx.compose.foundation.clickable
25+
import androidx.compose.foundation.interaction.MutableInteractionSource
26+
import androidx.compose.foundation.text.BasicTextField
27+
import androidx.compose.foundation.text.KeyboardActions
28+
import androidx.compose.foundation.text.KeyboardOptions
29+
import androidx.compose.runtime.Composable
30+
import androidx.compose.ui.Modifier
31+
import androidx.compose.ui.graphics.Brush
32+
import androidx.compose.ui.graphics.Color
33+
import androidx.compose.ui.graphics.SolidColor
34+
import androidx.compose.ui.text.TextLayoutResult
35+
import androidx.compose.ui.text.TextStyle
36+
import androidx.compose.ui.text.input.TextFieldValue
37+
import androidx.compose.ui.text.input.VisualTransformation
38+
39+
/**
40+
* This is a wrapper for [BasicTextField] that when clicked launches the keyboard. It cannot be
41+
* edited, but works as a helper to open the keyboard.
42+
*/
43+
@Composable
44+
public fun NonEditableBasicTextField(
45+
modifier: Modifier = Modifier,
46+
onTextChanged: (value: String) -> Unit,
47+
value: TextFieldValue,
48+
enabled: Boolean = true,
49+
readOnly: Boolean = false,
50+
textStyle: TextStyle = TextStyle.Default,
51+
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
52+
keyboardActions: KeyboardActions = KeyboardActions.Default,
53+
singleLine: Boolean = false,
54+
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
55+
minLines: Int = 1,
56+
visualTransformation: VisualTransformation = VisualTransformation.None,
57+
onTextLayout: (TextLayoutResult) -> Unit = {},
58+
interactionSource: MutableInteractionSource? = null,
59+
cursorBrush: Brush = SolidColor(Color.Black),
60+
decorationBox: @Composable (innerTextField: @Composable () -> Unit) -> Unit =
61+
@Composable { innerTextField -> innerTextField() },
62+
) {
63+
val keyboardLauncher = rememberLauncherForActivityResult(
64+
KeyBoardContract(),
65+
) {
66+
if (it is KeyBoardContract.Result.TextChanged) {
67+
onTextChanged(it.text)
68+
}
69+
}
70+
val keyboardIntent = Intent("com.google.android.wearable.action.LAUNCH_KEYBOARD")
71+
72+
BasicTextField(
73+
modifier = modifier.clickable {
74+
keyboardLauncher.launch(keyboardIntent)
75+
},
76+
value = value,
77+
onValueChange = {},
78+
enabled = enabled,
79+
readOnly = readOnly,
80+
textStyle = textStyle,
81+
keyboardOptions = keyboardOptions,
82+
keyboardActions = keyboardActions,
83+
singleLine = singleLine,
84+
maxLines = maxLines,
85+
minLines = minLines,
86+
visualTransformation = visualTransformation,
87+
onTextLayout = onTextLayout,
88+
interactionSource = interactionSource,
89+
cursorBrush = cursorBrush,
90+
decorationBox = decorationBox,
91+
)
92+
}
93+
94+
private class KeyBoardContract : ActivityResultContract<Intent, KeyBoardContract.Result>() {
95+
override fun createIntent(context: Context, input: Intent): Intent = input
96+
97+
override fun parseResult(resultCode: Int, intent: Intent?): Result {
98+
println("resultCode: $resultCode")
99+
return if (resultCode == RESULT_OK) {
100+
val resultText = intent?.extras?.getString("result_text")
101+
if (!resultText.isNullOrBlank()) {
102+
Result.TextChanged(resultText)
103+
} else {
104+
Result.Empty
105+
}
106+
} else {
107+
Result.Empty
108+
}
109+
}
110+
111+
sealed class Result {
112+
data class TextChanged(val text: String) : Result()
113+
data object Empty : Result()
114+
}
115+
}

Diff for: sample/src/main/java/com/google/android/horologist/sample/MenuScreen.kt

+6
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ fun MenuScreen(
9898
onClick = { navigateToRoute(Screen.ToDatePicker.route) },
9999
)
100100
}
101+
item {
102+
Chip(
103+
label = "NonEditableTextField",
104+
onClick = { navigateToRoute(Screen.NonEditableTextField.route) },
105+
)
106+
}
101107
item {
102108
Chip(
103109
label = "Time With Seconds Picker",

Diff for: sample/src/main/java/com/google/android/horologist/sample/SampleWearApp.kt

+24
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,18 @@
1616

1717
package com.google.android.horologist.sample
1818

19+
import androidx.compose.foundation.background
20+
import androidx.compose.foundation.layout.Box
21+
import androidx.compose.foundation.layout.fillMaxSize
1922
import androidx.compose.runtime.Composable
2023
import androidx.compose.runtime.getValue
2124
import androidx.compose.runtime.mutableStateOf
2225
import androidx.compose.runtime.remember
2326
import androidx.compose.runtime.setValue
27+
import androidx.compose.ui.Alignment
28+
import androidx.compose.ui.Modifier
29+
import androidx.compose.ui.graphics.Color
30+
import androidx.compose.ui.text.input.TextFieldValue
2431
import androidx.navigation.NavType
2532
import androidx.navigation.navArgument
2633
import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
@@ -30,6 +37,7 @@ import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
3037
import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
3138
import com.google.android.horologist.audio.ui.VolumeScreen
3239
import com.google.android.horologist.composables.DatePicker
40+
import com.google.android.horologist.composables.NonEditableBasicTextField
3341
import com.google.android.horologist.composables.TimePicker
3442
import com.google.android.horologist.composables.TimePickerWith12HourClock
3543
import com.google.android.horologist.compose.layout.AppScaffold
@@ -117,6 +125,22 @@ fun SampleWearApp() {
117125
},
118126
)
119127
}
128+
composable(Screen.NonEditableTextField.route) {
129+
var text by remember { mutableStateOf("Text") }
130+
Box(
131+
modifier = Modifier.fillMaxSize(),
132+
) {
133+
NonEditableBasicTextField(
134+
modifier = Modifier
135+
.align(Alignment.Center)
136+
.background(Color.Gray),
137+
onTextChanged = {
138+
text = it
139+
},
140+
value = TextFieldValue(text),
141+
)
142+
}
143+
}
120144
composable(Screen.ToDatePicker.route) {
121145
val date = time.toLocalDate()
122146
DatePicker(

Diff for: sample/src/main/java/com/google/android/horologist/sample/Screen.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ sealed class Screen(
2929
object TimeWithSecondsPicker : Screen("timeWithSecondsPicker")
3030
object TimeWithoutSecondsPicker : Screen("timeWithoutSecondsPicker")
3131
object Network : Screen("network")
32-
32+
object NonEditableTextField : Screen("nonEditableTextField")
3333
object MaterialAlertDialog : Screen("materialAlertDialog")
3434
object MaterialAnimatedComponents : Screen("materialAnimatedComponents")
3535
object MaterialButtonsScreen : Screen("materialButtonsScreen")

0 commit comments

Comments
 (0)