Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ import android.Manifest
import android.content.pm.PackageManager
import android.inputmethodservice.InputMethodService
import android.os.SystemClock
import android.text.InputType
import android.util.Log
import android.view.KeyEvent
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputConnection
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.platform.ComposeView
Expand Down Expand Up @@ -90,7 +93,7 @@ class WearImeService :
WearSettingsStore.load(applicationContext)
}

override fun onStartInputView(info: android.view.inputmethod.EditorInfo?, restarting: Boolean) {
override fun onStartInputView(info: EditorInfo?, restarting: Boolean) {
super.onStartInputView(info, restarting)
// Opening the keyboard is a good moment to reconcile with the phone: read the latest replicated
// settings DataItem (accent/provider/key/prompt) and nudge a republish. The cached copy is used
Expand Down Expand Up @@ -146,7 +149,7 @@ class WearImeService :
private val actions = WearImeActions(
commitText = { text -> ic()?.commitText(text, 1) },
deleteBackward = { ic()?.deleteSurroundingText(1, 0) },
performEnter = { ic()?.commitText("\n", 1) },
performEnter = { performEnter() },
toggleDictation = { toggleDictation() },
togglePause = { togglePause() },
cancelDictation = { cancelDictation() },
Expand All @@ -155,6 +158,40 @@ class WearImeService :

private fun ic(): InputConnection? = currentInputConnection

/**
* The submit action the focused field declares (SEND / SEARCH / GO / DONE …), or null when it has
* none for the IME to fire — either because it declares no action, or because it asked the IME to
* leave Enter alone with [EditorInfo.IME_FLAG_NO_ENTER_ACTION].
*/
private fun editorAction(): Int? {
val imeOptions = currentInputEditorInfo?.imeOptions ?: return null
if (imeOptions and EditorInfo.IME_FLAG_NO_ENTER_ACTION != 0) return null
return (imeOptions and EditorInfo.IME_MASK_ACTION).takeUnless {
it == EditorInfo.IME_ACTION_NONE || it == EditorInfo.IME_ACTION_UNSPECIFIED
}
}

/**
* The ⏎ key. Committing a literal newline is the wrong thing for most fields: WhatsApp's compose box
* and the browser's search bar declare an *editor action* (SEND / SEARCH) and act on that, never on
* "\n", which is why Enter looked dead in those apps (#294). So fire the declared action when there
* is one, fall back to a real ENTER key event for fields that want the key press, and only insert a
* newline into genuinely multi-line editors.
*/
private fun performEnter() {
val ic = ic() ?: return
val action = editorAction()
if (action != null) {
// The action *is* the submit, so the keyboard is done once it lands: the app is already
// showing its result, and the opaque input view would otherwise sit on top of it.
if (ic.performEditorAction(action)) requestHideSelf(0)
return
}
val multiline = (currentInputEditorInfo?.inputType ?: 0) and
InputType.TYPE_TEXT_FLAG_MULTI_LINE != 0
if (multiline) ic.commitText("\n", 1) else sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER)
}

/**
* Voice-page record button. Tap once to start recording, tap again to stop; on stop the audio is
* transcribed (tethered via the phone when reachable, else standalone) and committed at the cursor.
Expand Down Expand Up @@ -278,9 +315,11 @@ class WearImeService :
ic()?.commitText(text, 1)
recordingInfo.value = WearRecordingInfo()
dictationState.value = WearDictationState.IDLE
// Dictation is the primary action — once the text is in, get out of the way so the
// user sees their field again instead of a keyboard stuck open over it.
requestHideSelf(0)
// Dictation is the primary action, so getting out of the way is the right default —
// but not over a field that declares a submit action. There the dictation is only
// half the job: hiding steals the ⏎ the user still needs, and the teardown races the
// commit we just made, which Samsung's browser loses the text to entirely (#294).
if (editorAction() == null) requestHideSelf(0)
}
}
}
Expand Down