diff --git a/README.md b/README.md index c69294e3..a0904a83 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,10 @@ Features that may go unnoticed * Long-press comma to access clipboard view, emoji view, one-handed mode, settings, or switch language * emoji view and language switch will disappear if you have the corresponding key enabled * for some layouts it's not the comma-key, but the key at the same position (e.g. it's `q` for Dvorak layout) +* Press the incognito icon to get the toolbar (probably only hidden if you use _force incognito mode_) * Sliding key input: swipe from shift to another key to type a single uppercase key * also works for the `?123` key to type a single symbol from the symbols keyboard, and for related keys -* Long-press a suggestion to show more suggestions, and a delete button to remove this suggestion +* Long-press a suggestion in suggestion strip to show more suggestions, and a delete button to remove this suggestion * Swipe up from a suggestion to open more suggestions, and release on the suggestion to select it * You can add dictionaries by opening them in a file explorer * only works with content-uris and not with file-uris, meaning that it may not work with some file explorers diff --git a/app/src/test/java/org/dslul/openboard/inputmethod/latin/InputLogicTest.kt b/app/src/test/java/org/dslul/openboard/inputmethod/latin/InputLogicTest.kt index 13689403..5b27658b 100644 --- a/app/src/test/java/org/dslul/openboard/inputmethod/latin/InputLogicTest.kt +++ b/app/src/test/java/org/dslul/openboard/inputmethod/latin/InputLogicTest.kt @@ -61,7 +61,7 @@ class InputLogicTest { @Test fun inputCode() { reset() - input('c'.code) + input('c') assertEquals("c", textBeforeCursor) assertEquals("c", getText()) assertEquals("", textAfterCursor) @@ -91,7 +91,7 @@ class InputLogicTest { reset() setText("hello") setCursorPosition(3) // after first l - input('i'.code) + input('i') assertEquals("helilo", getWordAtCursor()) assertEquals("helilo", getText()) assertEquals(4, getCursorPosition()) @@ -104,7 +104,7 @@ class InputLogicTest { currentInputType = 180225 // should not change much, but just to be sure setText("hello") setCursorPosition(3, weirdTextField = true) // after first l - input('i'.code) + input('i') assertEquals("helilo", getWordAtCursor()) assertEquals("helilo", getText()) assertEquals(4, getCursorPosition()) @@ -115,7 +115,7 @@ class InputLogicTest { reset() setText("hello my friend") setCursorPosition(7) // between m and y - input('a'.code) + input('a') assertEquals("may", getWordAtCursor()) assertEquals("hello may friend", getText()) assertEquals(8, getCursorPosition()) @@ -127,7 +127,7 @@ class InputLogicTest { currentScript = ScriptUtils.SCRIPT_HANGUL setText("ㅛㅎㄹㅎㅕㅛ") setCursorPosition(3) - input('ㄲ'.code) // fails, as expected from the hangul issue when processing the event in onCodeInput + input('ㄲ') // fails, as expected from the hangul issue when processing the event in onCodeInput assertEquals("ㅛㅎㄹㄲㅎㅕㅛ", getWordAtCursor()) assertEquals("ㅛㅎㄹㄲㅎㅕㅛ", getText()) assertEquals("ㅛㅎㄹㄲㅎㅕㅛ", textBeforeCursor + textAfterCursor) @@ -139,7 +139,7 @@ class InputLogicTest { reset() setText("hello") assertEquals("hello", composingText) - input('.'.code) + input('.') assertEquals("", composingText) } @@ -147,13 +147,13 @@ class InputLogicTest { @Test fun autospace() { reset() setText("hello") - input('.'.code) - input('a'.code) + input('.') + input('a') assertEquals("hello.a", textBeforeCursor) DeviceProtectedUtils.getSharedPreferences(latinIME).edit { putBoolean(Settings.PREF_AUTOSPACE_AFTER_PUNCTUATION, true) } setText("hello") - input('.'.code) - input('a'.code) + input('.') + input('a') assertEquals("hello. a", textBeforeCursor) } @@ -161,15 +161,15 @@ class InputLogicTest { reset() setText("hello there") setCursorPosition(5) // after hello - input('.'.code) - input('a'.code) + input('.') + input('a') assertEquals("hello.a", textBeforeCursor) assertEquals("hello.a there", text) DeviceProtectedUtils.getSharedPreferences(latinIME).edit { putBoolean(Settings.PREF_AUTOSPACE_AFTER_PUNCTUATION, true) } setText("hello there") setCursorPosition(5) // after hello - input('.'.code) - input('a'.code) + input('.') + input('a') assertEquals("hello. a", textBeforeCursor) assertEquals("hello. a there", text) } @@ -177,33 +177,33 @@ class InputLogicTest { @Test fun urlDetectionThings() { reset() DeviceProtectedUtils.getSharedPreferences(latinIME).edit { putBoolean(Settings.PREF_URL_DETECTION, true) } - input('.'.code) - input('.'.code) - input('.'.code) - input('h'.code) + input('.') + input('.') + input('.') + input('h') assertEquals("...h", text) assertEquals("h", composingText) reset() DeviceProtectedUtils.getSharedPreferences(latinIME).edit { putBoolean(Settings.PREF_URL_DETECTION, true) } input("bla") - input('.'.code) - input('.'.code) + input('.') + input('.') assertEquals("bla..", text) assertEquals("", composingText) reset() DeviceProtectedUtils.getSharedPreferences(latinIME).edit { putBoolean(Settings.PREF_URL_DETECTION, true) } input("bla") - input('.'.code) - input('c'.code) + input('.') + input('c') assertEquals("bla.c", text) assertEquals("bla.c", composingText) reset() DeviceProtectedUtils.getSharedPreferences(latinIME).edit { putBoolean(Settings.PREF_URL_DETECTION, true) } DeviceProtectedUtils.getSharedPreferences(latinIME).edit { putBoolean(Settings.PREF_AUTOSPACE_AFTER_PUNCTUATION, true) } input("bla") - input('.'.code) + input('.') functionalKeyPress(Constants.CODE_SHIFT) // should remove the phantom space (in addition to normal effect) - input('c'.code) + input('c') assertEquals("bla.c", text) assertEquals("bla.c", composingText) } @@ -212,10 +212,10 @@ class InputLogicTest { reset() DeviceProtectedUtils.getSharedPreferences(latinIME).edit { putBoolean(Settings.PREF_URL_DETECTION, true) } setText("example.co") - input('m'.code) - input('.'.code) + input('m') + input('.') assertEquals("example.com.", composingText) - input(' '.code) + input(' ') assertEquals("example.com", ShadowFacilitator2.lastAddedWord) } @@ -223,9 +223,9 @@ class InputLogicTest { reset() DeviceProtectedUtils.getSharedPreferences(latinIME).edit { putBoolean(Settings.PREF_URL_DETECTION, true) } setText("bl") - input('a'.code) - input('.'.code) - input('.'.code) + input('a') + input('.') + input('.') assertEquals("", composingText) assertEquals("bla..", text) } @@ -237,6 +237,18 @@ class InputLogicTest { assertEquals("s is ", text.substring(3, 8)) } + @Test fun noComposingForPasswordFields() { + reset() + setInputType(InputType.TYPE_CLASS_TEXT and InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) + input('a') + input('b') + assertEquals("", composingText) + DeviceProtectedUtils.getSharedPreferences(latinIME).edit { putBoolean(Settings.PREF_URL_DETECTION, true) } + input('.') + input('c') + assertEquals("", composingText) + } + // ------- helper functions --------- // should be called before every test, so the same state is guaranteed @@ -258,6 +270,7 @@ class InputLogicTest { setText("") } + private fun input(char: Char) = input(char.code) private fun input(codePoint: Int) { val oldBefore = textBeforeCursor val oldAfter = textAfterCursor @@ -373,6 +386,12 @@ class InputLogicTest { private fun getText() = connection.getTextBeforeCursor(100, 0).toString() + (connection.getSelectedText(0) ?: "") + connection.getTextAfterCursor(100, 0) + private fun setInputType(inputType: Int) { + // set text to actually apply input type + currentInputType = inputType + setText(text) + } + // always need to handle messages for proper simulation private fun handleMessages() { while (messages.isNotEmpty()) { @@ -608,4 +627,4 @@ class ShadowFacilitator2 { companion object { var lastAddedWord = "" } -} \ No newline at end of file +} diff --git a/app/src/test/java/org/dslul/openboard/inputmethod/latin/SuggestTest.kt b/app/src/test/java/org/dslul/openboard/inputmethod/latin/SuggestTest.kt index 6f8fb3c6..0208081f 100644 --- a/app/src/test/java/org/dslul/openboard/inputmethod/latin/SuggestTest.kt +++ b/app/src/test/java/org/dslul/openboard/inputmethod/latin/SuggestTest.kt @@ -1,7 +1,6 @@ package org.dslul.openboard.inputmethod.latin import androidx.core.content.edit -import androidx.test.runner.AndroidJUnit4 import org.dslul.openboard.inputmethod.latin.SuggestedWords.SuggestedWordInfo import org.dslul.openboard.inputmethod.latin.SuggestedWords.SuggestedWordInfo.KIND_FLAG_APPROPRIATE_FOR_AUTO_CORRECTION import org.dslul.openboard.inputmethod.latin.SuggestedWords.SuggestedWordInfo.KIND_WHITELIST @@ -15,13 +14,14 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.robolectric.annotation.Implementation import org.robolectric.annotation.Implements import org.robolectric.shadows.ShadowLog import java.util.* -@RunWith(AndroidJUnit4::class) +@RunWith(RobolectricTestRunner::class) @Config(shadows = [ ShadowLocaleManagerCompat::class, ShadowInputMethodManager2::class, @@ -46,7 +46,7 @@ class SuggestTest { .edit { putBoolean(Settings.PREF_AUTO_CORRECTION, true) } // need to enable, off by default } - @Test fun `"on" to "in" if "in" was used before in this context`() { + @Test fun `'on' to 'in' if 'in' was used before in this context`() { val locale = Locale.ENGLISH val result = shouldBeAutoCorrected( "on", @@ -60,7 +60,7 @@ class SuggestTest { // not corrected because first suggestion score is too low } - @Test fun `"ill" to "I'll" if "ill" not used before in this context, and I'll has shortcut`() { + @Test fun `'ill' to 'I'll' if 'ill' not used before in this context, and I'll has shortcut`() { val locale = Locale.ENGLISH val result = shouldBeAutoCorrected( "ill", @@ -74,7 +74,7 @@ class SuggestTest { // correction because both empty scores are 0, which should be fine (next check is comparing empty scores) } - @Test fun `not "ill" to "I'll" if only "ill" was used before in this context`() { + @Test fun `not 'ill' to 'I'll' if only 'ill' was used before in this context`() { val locale = Locale.ENGLISH val result = shouldBeAutoCorrected( "ill", @@ -88,7 +88,7 @@ class SuggestTest { // not corrected because first empty score not high enough } - @Test fun `"ill" to "I'll" if both have same ngram score`() { + @Test fun `'ill' to 'I'll' if both have same ngram score`() { val locale = Locale.ENGLISH val result = shouldBeAutoCorrected( "ill", @@ -101,7 +101,7 @@ class SuggestTest { assert(result.last()) // should be corrected } - @Test fun `no "ill" to "I'll" if "ill" has somewhat better ngram score`() { + @Test fun `no 'ill' to 'I'll' if 'ill' has somewhat better ngram score`() { val locale = Locale.ENGLISH val result = shouldBeAutoCorrected( "ill", @@ -114,7 +114,7 @@ class SuggestTest { assert(!result.last()) // should not be corrected } - @Test fun `no English "I" for Polish "i" when typing in Polish`() { + @Test fun `no English 'I' for Polish 'i' when typing in Polish`() { val result = shouldBeAutoCorrected( "i", listOf(suggestion("I", Int.MAX_VALUE, Locale.ENGLISH), suggestion("i", 1500000, Locale("pl"))), @@ -128,7 +128,7 @@ class SuggestTest { // if very aggressive, still no correction because locale matches with typed word only } - @Test fun `English "I" instead of Polish "i" when typing in English`() { + @Test fun `English 'I' instead of Polish 'i' when typing in English`() { val result = shouldBeAutoCorrected( "i", listOf(suggestion("I", Int.MAX_VALUE, Locale.ENGLISH), suggestion("i", 1500000, Locale("pl"))), @@ -144,7 +144,7 @@ class SuggestTest { // todo: consider special score for case-only difference? } - @Test fun `no English "in" instead of French "un" when typing in French`() { + @Test fun `no English 'in' instead of French 'un' when typing in French`() { val result = shouldBeAutoCorrected( "un", listOf(suggestion("in", Int.MAX_VALUE, Locale.ENGLISH), suggestion("un", 1500000, Locale.FRENCH)), @@ -157,7 +157,7 @@ class SuggestTest { // not corrected because of locale matching } - @Test fun `no "né" instead of "ne"`() { + @Test fun `no 'né' instead of 'ne'`() { val result = shouldBeAutoCorrected( "ne", listOf(suggestion("ne", 1900000, Locale.FRENCH), suggestion("né", 1900000-1, Locale.FRENCH)), @@ -170,7 +170,7 @@ class SuggestTest { // not corrected because score is lower } - @Test fun `"né" instead of "ne" if "né" in ngram context`() { + @Test fun `'né' instead of 'ne' if 'né' in ngram context`() { val locale = Locale.FRENCH val result = shouldBeAutoCorrected( "ne", @@ -183,7 +183,7 @@ class SuggestTest { assert(result.last()) // should be corrected } - @Test fun `"né" instead of "ne" if "né" has clearly better score in ngram context`() { + @Test fun `'né' instead of 'ne' if 'né' has clearly better score in ngram context`() { val locale = Locale.FRENCH val result = shouldBeAutoCorrected( "ne", @@ -196,7 +196,7 @@ class SuggestTest { assert(result.last()) // should be corrected } - @Test fun `no "né" instead of "ne" if both with same score in ngram context`() { + @Test fun `no 'né' instead of 'ne' if both with same score in ngram context`() { val locale = Locale.FRENCH val result = shouldBeAutoCorrected( "ne", @@ -209,7 +209,7 @@ class SuggestTest { assert(!result.last()) // should not be corrected } - @Test fun `no "ne" instead of "né"`() { + @Test fun `no 'ne' instead of 'né'`() { val locale = Locale.FRENCH val result = shouldBeAutoCorrected( "né", @@ -298,7 +298,7 @@ fun suggestion(word: String, score: Int, locale: Locale) = @Implements(DictionaryFacilitatorImpl::class) class ShadowFacilitator { @Implementation - fun getCurrentLocale() = currentTypingLocale + fun getCurrentLocale(): Locale = currentTypingLocale @Implementation fun hasAtLeastOneInitializedMainDictionary() = true // otherwise no autocorrect }