mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-06-30 12:49:57 +00:00
Compare commits
40 commits
v3.2-beta1
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
d2217f099a | ||
|
4f58e5d013 | ||
|
10a5eab3bc | ||
|
8d876a15f0 | ||
|
945a700b71 | ||
|
53a899794e | ||
|
c2068224a0 | ||
|
9193c95c2b | ||
|
76ebf99921 | ||
|
77a728e390 | ||
|
7b0c511857 | ||
|
e062efb3d4 | ||
|
d356f9f54b | ||
|
9549389be7 | ||
|
2dc838798d | ||
|
24a2eddc1f | ||
|
ef3191a2eb | ||
|
e430d13c4a | ||
|
9c97a6b9bf | ||
|
a37de668c0 | ||
|
63dad1549e | ||
|
f06a553d2c | ||
|
e9e3bdac17 | ||
|
d5cd18ecaa | ||
|
871ac110ad | ||
|
49c9d77978 | ||
|
79726f1a9d | ||
|
f2ec441f45 | ||
|
62f82d15cf | ||
|
f8d3795302 | ||
|
9cec401e1e | ||
|
83ff9b3345 | ||
|
8ae241b032 | ||
|
80ba394b95 | ||
|
52744b7427 | ||
|
af5c41c83c | ||
|
e21168b1d3 | ||
|
11f45a6209 | ||
|
c8322dd4a2 | ||
|
896e207c5f |
71 changed files with 1139 additions and 600 deletions
|
@ -12,13 +12,13 @@ android {
|
||||||
applicationId = "helium314.keyboard"
|
applicationId = "helium314.keyboard"
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 3200
|
versionCode = 3201
|
||||||
versionName = "3.2-beta1"
|
versionName = "3.2"
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters.clear()
|
abiFilters.clear()
|
||||||
abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64"))
|
abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64"))
|
||||||
}
|
}
|
||||||
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|
|
@ -136,32 +136,6 @@
|
||||||
®️
|
®️
|
||||||
™️
|
™️
|
||||||
|
|
||||||
🇦
|
|
||||||
🇧
|
|
||||||
🇨
|
|
||||||
🇩
|
|
||||||
🇪
|
|
||||||
🇫
|
|
||||||
🇬
|
|
||||||
🇭
|
|
||||||
🇮
|
|
||||||
🇯
|
|
||||||
🇰
|
|
||||||
🇱
|
|
||||||
🇲
|
|
||||||
🇳
|
|
||||||
🇴
|
|
||||||
🇵
|
|
||||||
🇶
|
|
||||||
🇷
|
|
||||||
🇸
|
|
||||||
🇹
|
|
||||||
🇺
|
|
||||||
🇻
|
|
||||||
🇼
|
|
||||||
🇽
|
|
||||||
🇾
|
|
||||||
🇿
|
|
||||||
#️⃣
|
#️⃣
|
||||||
*️⃣
|
*️⃣
|
||||||
0️⃣
|
0️⃣
|
||||||
|
|
|
@ -139,6 +139,16 @@ class Event private constructor(
|
||||||
null, if (isKeyRepeat) FLAG_REPEAT else FLAG_NONE, null)
|
null, if (isKeyRepeat) FLAG_REPEAT else FLAG_NONE, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A helper method to split the code point and the key code.
|
||||||
|
// todo: Ultimately, they should not be squashed into the same variable, and this method should be removed.
|
||||||
|
@JvmStatic
|
||||||
|
fun createSoftwareKeypressEvent(keyCodeOrCodePoint: Int, metaState: Int, keyX: Int, keyY: Int, isKeyRepeat: Boolean) =
|
||||||
|
if (keyCodeOrCodePoint <= 0) {
|
||||||
|
createSoftwareKeypressEvent(NOT_A_CODE_POINT, keyCodeOrCodePoint, metaState, keyX, keyY, isKeyRepeat)
|
||||||
|
} else {
|
||||||
|
createSoftwareKeypressEvent(keyCodeOrCodePoint, NOT_A_KEY_CODE, metaState, keyX, keyY, isKeyRepeat)
|
||||||
|
}
|
||||||
|
|
||||||
fun createHardwareKeypressEvent(codePoint: Int, keyCode: Int, metaState: Int, next: Event?, isKeyRepeat: Boolean): Event {
|
fun createHardwareKeypressEvent(codePoint: Int, keyCode: Int, metaState: Int, next: Event?, isKeyRepeat: Boolean): Event {
|
||||||
return Event(EVENT_TYPE_INPUT_KEYPRESS, null, codePoint, keyCode, metaState,
|
return Event(EVENT_TYPE_INPUT_KEYPRESS, null, codePoint, keyCode, metaState,
|
||||||
Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE,
|
Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE,
|
||||||
|
@ -256,11 +266,9 @@ class Event private constructor(
|
||||||
source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags or FLAG_COMBINING, source.mNextEvent)
|
source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags or FLAG_COMBINING, source.mNextEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createNotHandledEvent(): Event {
|
val notHandledEvent = Event(EVENT_TYPE_NOT_HANDLED, null, NOT_A_CODE_POINT, NOT_A_KEY_CODE, 0,
|
||||||
return Event(EVENT_TYPE_NOT_HANDLED, null, NOT_A_CODE_POINT, NOT_A_KEY_CODE, 0,
|
|
||||||
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, null, FLAG_NONE, null)
|
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, null, FLAG_NONE, null)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// This method is private - to create a new event, use one of the create* utility methods.
|
// This method is private - to create a new event, use one of the create* utility methods.
|
||||||
init {
|
init {
|
||||||
|
|
|
@ -24,7 +24,8 @@ class HardwareKeyboardEventDecoder(val mDeviceId: Int) : HardwareEventDecoder {
|
||||||
// KeyEvent#getUnicodeChar() does not exactly returns a unicode char, but rather a value
|
// KeyEvent#getUnicodeChar() does not exactly returns a unicode char, but rather a value
|
||||||
// that includes both the unicode char in the lower 21 bits and flags in the upper bits,
|
// that includes both the unicode char in the lower 21 bits and flags in the upper bits,
|
||||||
// hence the name "codePointAndFlags". {@see KeyEvent#getUnicodeChar()} for more info.
|
// hence the name "codePointAndFlags". {@see KeyEvent#getUnicodeChar()} for more info.
|
||||||
val codePointAndFlags = keyEvent.unicodeChar
|
val codePointAndFlags = keyEvent.unicodeChar.takeIf { it != 0 }
|
||||||
|
?: Event.NOT_A_CODE_POINT // KeyEvent has 0 if no codePoint, but that's actually valid so we convert it to -1
|
||||||
// The keyCode is the abstraction used by the KeyEvent to represent different keys that
|
// The keyCode is the abstraction used by the KeyEvent to represent different keys that
|
||||||
// do not necessarily map to a unicode character. This represents a physical key, like
|
// do not necessarily map to a unicode character. This represents a physical key, like
|
||||||
// the key for 'A' or Space, but also Backspace or Ctrl or Caps Lock.
|
// the key for 'A' or Space, but also Backspace or Ctrl or Caps Lock.
|
||||||
|
@ -48,6 +49,21 @@ class HardwareKeyboardEventDecoder(val mDeviceId: Int) : HardwareEventDecoder {
|
||||||
} else Event.createHardwareKeypressEvent(codePointAndFlags, keyCode, metaState, null, isKeyRepeat)
|
} else Event.createHardwareKeypressEvent(codePointAndFlags, keyCode, metaState, null, isKeyRepeat)
|
||||||
// If not Enter, then this is just a regular keypress event for a normal character
|
// If not Enter, then this is just a regular keypress event for a normal character
|
||||||
// that can be committed right away, taking into account the current state.
|
// that can be committed right away, taking into account the current state.
|
||||||
} else Event.createNotHandledEvent()
|
} else if (isDpadDirection(keyCode)) {
|
||||||
|
Event.createHardwareKeypressEvent(codePointAndFlags, keyCode, metaState, null, isKeyRepeat)
|
||||||
|
// } else if (KeyEvent.isModifierKey(keyCode)) {
|
||||||
|
// todo: we could synchronize meta state across HW and SW keyboard, but that's more work for little benefit (especially with shift & caps lock)
|
||||||
|
} else {
|
||||||
|
Event.notHandledEvent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun isDpadDirection(keyCode: Int) = when (keyCode) {
|
||||||
|
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||||
|
KeyEvent.KEYCODE_DPAD_DOWN_LEFT, KeyEvent.KEYCODE_DPAD_DOWN_RIGHT, KeyEvent.KEYCODE_DPAD_UP_RIGHT,
|
||||||
|
KeyEvent.KEYCODE_DPAD_UP_LEFT -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
package helium314.keyboard.keyboard;
|
package helium314.keyboard.keyboard;
|
||||||
|
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
|
||||||
import helium314.keyboard.latin.common.Constants;
|
import helium314.keyboard.latin.common.Constants;
|
||||||
import helium314.keyboard.latin.common.InputPointers;
|
import helium314.keyboard.latin.common.InputPointers;
|
||||||
|
|
||||||
|
@ -31,6 +33,12 @@ public interface KeyboardActionListener {
|
||||||
*/
|
*/
|
||||||
void onReleaseKey(int primaryCode, boolean withSliding);
|
void onReleaseKey(int primaryCode, boolean withSliding);
|
||||||
|
|
||||||
|
/** For handling hardware key presses. Returns whether the event was handled. */
|
||||||
|
boolean onKeyDown(int keyCode, KeyEvent keyEvent);
|
||||||
|
|
||||||
|
/** For handling hardware key presses. Returns whether the event was handled. */
|
||||||
|
boolean onKeyUp(int keyCode, KeyEvent keyEvent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a key code to the listener.
|
* Send a key code to the listener.
|
||||||
*
|
*
|
||||||
|
@ -117,6 +125,10 @@ public interface KeyboardActionListener {
|
||||||
@Override
|
@Override
|
||||||
public void onReleaseKey(int primaryCode, boolean withSliding) {}
|
public void onReleaseKey(int primaryCode, boolean withSliding) {}
|
||||||
@Override
|
@Override
|
||||||
|
public boolean onKeyDown(int keyCode, KeyEvent keyEvent) { return false; }
|
||||||
|
@Override
|
||||||
|
public boolean onKeyUp(int keyCode, KeyEvent keyEvent) { return false; }
|
||||||
|
@Override
|
||||||
public void onCodeInput(int primaryCode, int x, int y, boolean isKeyRepeat) {}
|
public void onCodeInput(int primaryCode, int x, int y, boolean isKeyRepeat) {}
|
||||||
@Override
|
@Override
|
||||||
public void onTextInput(String text) {}
|
public void onTextInput(String text) {}
|
||||||
|
|
|
@ -1,16 +1,24 @@
|
||||||
package helium314.keyboard.keyboard
|
package helium314.keyboard.keyboard
|
||||||
|
|
||||||
import android.text.InputType
|
import android.text.InputType
|
||||||
|
import android.util.SparseArray
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.inputmethod.InputMethodSubtype
|
import android.view.inputmethod.InputMethodSubtype
|
||||||
|
import helium314.keyboard.event.Event
|
||||||
|
import helium314.keyboard.event.HangulEventDecoder
|
||||||
|
import helium314.keyboard.event.HardwareEventDecoder
|
||||||
|
import helium314.keyboard.event.HardwareKeyboardEventDecoder
|
||||||
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode
|
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode
|
||||||
|
import helium314.keyboard.latin.EmojiAltPhysicalKeyDetector
|
||||||
import helium314.keyboard.latin.LatinIME
|
import helium314.keyboard.latin.LatinIME
|
||||||
import helium314.keyboard.latin.RichInputMethodManager
|
import helium314.keyboard.latin.RichInputMethodManager
|
||||||
import helium314.keyboard.latin.common.Constants
|
import helium314.keyboard.latin.common.Constants
|
||||||
import helium314.keyboard.latin.common.InputPointers
|
import helium314.keyboard.latin.common.InputPointers
|
||||||
import helium314.keyboard.latin.common.StringUtils
|
import helium314.keyboard.latin.common.StringUtils
|
||||||
|
import helium314.keyboard.latin.common.combiningRange
|
||||||
import helium314.keyboard.latin.common.loopOverCodePoints
|
import helium314.keyboard.latin.common.loopOverCodePoints
|
||||||
import helium314.keyboard.latin.common.loopOverCodePointsBackwards
|
import helium314.keyboard.latin.common.loopOverCodePointsBackwards
|
||||||
|
import helium314.keyboard.latin.define.ProductionFlags
|
||||||
import helium314.keyboard.latin.inputlogic.InputLogic
|
import helium314.keyboard.latin.inputlogic.InputLogic
|
||||||
import helium314.keyboard.latin.settings.Settings
|
import helium314.keyboard.latin.settings.Settings
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
@ -19,6 +27,11 @@ import kotlin.math.min
|
||||||
class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inputLogic: InputLogic) : KeyboardActionListener {
|
class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inputLogic: InputLogic) : KeyboardActionListener {
|
||||||
|
|
||||||
private val connection = inputLogic.mConnection
|
private val connection = inputLogic.mConnection
|
||||||
|
private val emojiAltPhysicalKeyDetector by lazy { EmojiAltPhysicalKeyDetector(latinIME.resources) }
|
||||||
|
|
||||||
|
// We expect to have only one decoder in almost all cases, hence the default capacity of 1.
|
||||||
|
// If it turns out we need several, it will get grown seamlessly.
|
||||||
|
private val hardwareEventDecoders: SparseArray<HardwareEventDecoder> = SparseArray(1)
|
||||||
|
|
||||||
private val keyboardSwitcher = KeyboardSwitcher.getInstance()
|
private val keyboardSwitcher = KeyboardSwitcher.getInstance()
|
||||||
private val settings = Settings.getInstance()
|
private val settings = Settings.getInstance()
|
||||||
|
@ -58,9 +71,62 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp
|
||||||
keyboardSwitcher.onReleaseKey(primaryCode, withSliding, latinIME.currentAutoCapsState, latinIME.currentRecapitalizeState)
|
keyboardSwitcher.onReleaseKey(primaryCode, withSliding, latinIME.currentAutoCapsState, latinIME.currentRecapitalizeState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onKeyUp(keyCode: Int, keyEvent: KeyEvent): Boolean {
|
||||||
|
emojiAltPhysicalKeyDetector.onKeyUp(keyEvent)
|
||||||
|
if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED)
|
||||||
|
return false
|
||||||
|
|
||||||
|
val keyIdentifier = keyEvent.deviceId.toLong() shl 32 + keyEvent.keyCode
|
||||||
|
return inputLogic.mCurrentlyPressedHardwareKeys.remove(keyIdentifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onKeyDown(keyCode: Int, keyEvent: KeyEvent): Boolean {
|
||||||
|
emojiAltPhysicalKeyDetector.onKeyDown(keyEvent)
|
||||||
|
if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED)
|
||||||
|
return false
|
||||||
|
|
||||||
|
val event: Event
|
||||||
|
if (settings.current.mLocale.language == "ko") { // todo: this does not appear to be the right place
|
||||||
|
val subtype = keyboardSwitcher.keyboard?.mId?.mSubtype ?: RichInputMethodManager.getInstance().currentSubtype
|
||||||
|
event = HangulEventDecoder.decodeHardwareKeyEvent(subtype, keyEvent) {
|
||||||
|
getHardwareKeyEventDecoder(keyEvent.deviceId).decodeHardwareKey(keyEvent)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
event = getHardwareKeyEventDecoder(keyEvent.deviceId).decodeHardwareKey(keyEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.isHandled) {
|
||||||
|
inputLogic.onCodeInput(
|
||||||
|
settings.current, event,
|
||||||
|
keyboardSwitcher.getKeyboardShiftMode(), // TODO: this is not necessarily correct for a hardware keyboard right now
|
||||||
|
keyboardSwitcher.getCurrentKeyboardScript(),
|
||||||
|
latinIME.mHandler
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCodeInput(primaryCode: Int, x: Int, y: Int, isKeyRepeat: Boolean) {
|
override fun onCodeInput(primaryCode: Int, x: Int, y: Int, isKeyRepeat: Boolean) {
|
||||||
|
when (primaryCode) {
|
||||||
|
KeyCode.TOGGLE_AUTOCORRECT -> return Settings.getInstance().toggleAutoCorrect()
|
||||||
|
KeyCode.TOGGLE_INCOGNITO_MODE -> return Settings.getInstance().toggleAlwaysIncognitoMode()
|
||||||
|
}
|
||||||
val mkv = keyboardSwitcher.mainKeyboardView
|
val mkv = keyboardSwitcher.mainKeyboardView
|
||||||
latinIME.onCodeInput(primaryCode, metaState, mkv.getKeyX(x), mkv.getKeyY(y), isKeyRepeat)
|
|
||||||
|
// checking if the character is a combining accent
|
||||||
|
val event = if (primaryCode in combiningRange) { // todo: should this be done later, maybe in inputLogic?
|
||||||
|
Event.createSoftwareDeadEvent(primaryCode, 0, metaState, mkv.getKeyX(x), mkv.getKeyY(y), null)
|
||||||
|
} else {
|
||||||
|
// todo:
|
||||||
|
// setting meta shift should only be done for arrow and similar cursor movement keys
|
||||||
|
// should only be enabled once it works more reliably (currently depends on app for some reason)
|
||||||
|
// if (mkv.keyboard?.mId?.isAlphabetShiftedManually == true)
|
||||||
|
// Event.createSoftwareKeypressEvent(primaryCode, metaState or KeyEvent.META_SHIFT_ON, mkv.getKeyX(x), mkv.getKeyY(y), isKeyRepeat)
|
||||||
|
// else Event.createSoftwareKeypressEvent(primaryCode, metaState, mkv.getKeyX(x), mkv.getKeyY(y), isKeyRepeat)
|
||||||
|
Event.createSoftwareKeypressEvent(primaryCode, metaState, mkv.getKeyX(x), mkv.getKeyY(y), isKeyRepeat)
|
||||||
|
}
|
||||||
|
latinIME.onEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTextInput(text: String?) = latinIME.onTextInput(text)
|
override fun onTextInput(text: String?) = latinIME.onTextInput(text)
|
||||||
|
@ -257,4 +323,13 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp
|
||||||
}
|
}
|
||||||
return -min(-actualSteps, text.length)
|
return -min(-actualSteps, text.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getHardwareKeyEventDecoder(deviceId: Int): HardwareEventDecoder {
|
||||||
|
hardwareEventDecoders.get(deviceId)?.let { return it }
|
||||||
|
|
||||||
|
// TODO: create the decoder according to the specification
|
||||||
|
val newDecoder = HardwareKeyboardEventDecoder(deviceId)
|
||||||
|
hardwareEventDecoders.put(deviceId, newDecoder)
|
||||||
|
return newDecoder
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,6 +184,11 @@ public final class KeyboardId {
|
||||||
|| mElementId == ELEMENT_ALPHABET_AUTOMATIC_SHIFTED || mElementId == ELEMENT_ALPHABET_MANUAL_SHIFTED;
|
|| mElementId == ELEMENT_ALPHABET_AUTOMATIC_SHIFTED || mElementId == ELEMENT_ALPHABET_MANUAL_SHIFTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isAlphabetShiftedManually() {
|
||||||
|
return mElementId == ELEMENT_ALPHABET_SHIFT_LOCKED || mElementId == ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED
|
||||||
|
|| mElementId == ELEMENT_ALPHABET_MANUAL_SHIFTED;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isNumberLayout() {
|
public boolean isNumberLayout() {
|
||||||
return mElementId == ELEMENT_NUMBER || mElementId == ELEMENT_NUMPAD
|
return mElementId == ELEMENT_NUMBER || mElementId == ELEMENT_NUMPAD
|
||||||
|| mElementId == ELEMENT_PHONE || mElementId == ELEMENT_PHONE_SYMBOLS;
|
|| mElementId == ELEMENT_PHONE || mElementId == ELEMENT_PHONE_SYMBOLS;
|
||||||
|
|
|
@ -138,7 +138,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues,
|
private void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues,
|
||||||
final int currentAutoCapsState, final int currentRecapitalizeState) {
|
final int currentAutoCapsState, final int currentRecapitalizeState) {
|
||||||
final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
|
final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
|
||||||
mThemeContext, editorInfo);
|
mThemeContext, editorInfo);
|
||||||
|
@ -527,6 +527,10 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
|
||||||
if (mCurrentInputView == null)
|
if (mCurrentInputView == null)
|
||||||
return;
|
return;
|
||||||
mEmojiPalettesView.clearKeyboardCache();
|
mEmojiPalettesView.clearKeyboardCache();
|
||||||
|
reloadMainKeyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reloadMainKeyboard() {
|
||||||
loadKeyboard(mLatinIME.getCurrentInputEditorInfo(), Settings.getValues(),
|
loadKeyboard(mLatinIME.getCurrentInputEditorInfo(), Settings.getValues(),
|
||||||
mLatinIME.getCurrentAutoCapsState(), mLatinIME.getCurrentRecapitalizeState());
|
mLatinIME.getCurrentAutoCapsState(), mLatinIME.getCurrentRecapitalizeState());
|
||||||
}
|
}
|
||||||
|
@ -597,7 +601,10 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
|
||||||
if (mKeyboardView == null || !mKeyboardView.isShown()) {
|
if (mKeyboardView == null || !mKeyboardView.isShown()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int activeKeyboardId = mKeyboardView.getKeyboard().mId.mElementId;
|
final Keyboard keyboard = mKeyboardView.getKeyboard();
|
||||||
|
if (keyboard == null) // may happen when using hardware keyboard
|
||||||
|
return false;
|
||||||
|
int activeKeyboardId = keyboard.mId.mElementId;
|
||||||
for (int keyboardId : keyboardIds) {
|
for (int keyboardId : keyboardIds) {
|
||||||
if (activeKeyboardId == keyboardId) {
|
if (activeKeyboardId == keyboardId) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -360,25 +360,21 @@ public final class MainKeyboardView extends KeyboardView implements DrawingProxy
|
||||||
public void onKeyPressed(@NonNull final Key key, final boolean withPreview) {
|
public void onKeyPressed(@NonNull final Key key, final boolean withPreview) {
|
||||||
key.onPressed();
|
key.onPressed();
|
||||||
invalidateKey(key);
|
invalidateKey(key);
|
||||||
if (withPreview && !key.noKeyPreview()) {
|
|
||||||
|
final Keyboard keyboard = getKeyboard();
|
||||||
|
if (keyboard == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mKeyPreviewDrawParams.setVisibleOffset(-keyboard.mVerticalGap);
|
||||||
|
if (withPreview && !key.noKeyPreview() && mKeyPreviewDrawParams.isPopupEnabled()) {
|
||||||
showKeyPreview(key);
|
showKeyPreview(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showKeyPreview(@NonNull final Key key) {
|
private void showKeyPreview(@NonNull final Key key) {
|
||||||
final Keyboard keyboard = getKeyboard();
|
|
||||||
if (keyboard == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams;
|
|
||||||
if (!previewParams.isPopupEnabled()) {
|
|
||||||
previewParams.setVisibleOffset(-keyboard.mVerticalGap);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
locatePreviewPlacerView();
|
locatePreviewPlacerView();
|
||||||
getLocationInWindow(mOriginCoords);
|
getLocationInWindow(mOriginCoords);
|
||||||
mKeyPreviewChoreographer.placeAndShowKeyPreview(key, keyboard.mIconsSet, getKeyDrawParams(),
|
mKeyPreviewChoreographer.placeAndShowKeyPreview(key, getKeyboard().mIconsSet, getKeyDrawParams(),
|
||||||
KeyboardSwitcher.getInstance().getWrapperView().getWidth(), mOriginCoords, mDrawingPreviewPlacerView);
|
KeyboardSwitcher.getInstance().getWrapperView().getWidth(), mOriginCoords, mDrawingPreviewPlacerView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,15 +12,15 @@ import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.view.Gravity;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import helium314.keyboard.accessibility.AccessibilityUtils;
|
import helium314.keyboard.accessibility.AccessibilityUtils;
|
||||||
import helium314.keyboard.accessibility.PopupKeysKeyboardAccessibilityDelegate;
|
import helium314.keyboard.accessibility.PopupKeysKeyboardAccessibilityDelegate;
|
||||||
import helium314.keyboard.keyboard.emoji.OnKeyEventListener;
|
import helium314.keyboard.keyboard.emoji.EmojiViewCallback;
|
||||||
import helium314.keyboard.keyboard.internal.KeyDrawParams;
|
import helium314.keyboard.keyboard.internal.KeyDrawParams;
|
||||||
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode;
|
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode;
|
||||||
import helium314.keyboard.latin.R;
|
import helium314.keyboard.latin.R;
|
||||||
|
@ -39,7 +39,7 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane
|
||||||
protected final KeyDetector mKeyDetector;
|
protected final KeyDetector mKeyDetector;
|
||||||
private Controller mController = EMPTY_CONTROLLER;
|
private Controller mController = EMPTY_CONTROLLER;
|
||||||
protected KeyboardActionListener mListener;
|
protected KeyboardActionListener mListener;
|
||||||
protected OnKeyEventListener mKeyEventListener;
|
protected EmojiViewCallback mEmojiViewCallback;
|
||||||
private int mOriginX;
|
private int mOriginX;
|
||||||
private int mOriginY;
|
private int mOriginY;
|
||||||
private Key mCurrentKey;
|
private Key mCurrentKey;
|
||||||
|
@ -122,7 +122,7 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane
|
||||||
public void showPopupKeysPanel(final View parentView, final Controller controller,
|
public void showPopupKeysPanel(final View parentView, final Controller controller,
|
||||||
final int pointX, final int pointY, final KeyboardActionListener listener) {
|
final int pointX, final int pointY, final KeyboardActionListener listener) {
|
||||||
mListener = listener;
|
mListener = listener;
|
||||||
mKeyEventListener = null;
|
mEmojiViewCallback = null;
|
||||||
showPopupKeysPanelInternal(parentView, controller, pointX, pointY);
|
showPopupKeysPanelInternal(parentView, controller, pointX, pointY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,9 +131,9 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void showPopupKeysPanel(final View parentView, final Controller controller,
|
public void showPopupKeysPanel(final View parentView, final Controller controller,
|
||||||
final int pointX, final int pointY, final OnKeyEventListener listener) {
|
final int pointX, final int pointY, final EmojiViewCallback emojiViewCallback) {
|
||||||
mListener = null;
|
mListener = null;
|
||||||
mKeyEventListener = listener;
|
mEmojiViewCallback = emojiViewCallback;
|
||||||
showPopupKeysPanelInternal(parentView, controller, pointX, pointY);
|
showPopupKeysPanelInternal(parentView, controller, pointX, pointY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,6 +157,9 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane
|
||||||
|
|
||||||
mOriginX = x + container.getPaddingLeft();
|
mOriginX = x + container.getPaddingLeft();
|
||||||
mOriginY = y + container.getPaddingTop();
|
mOriginY = y + container.getPaddingTop();
|
||||||
|
var center = panelX + getMeasuredWidth() / 2;
|
||||||
|
// This is needed for cases where there's also a long text popup above this keyboard
|
||||||
|
controller.setLayoutGravity(center < pointX? Gravity.RIGHT : center > pointX? Gravity.LEFT : Gravity.CENTER_HORIZONTAL);
|
||||||
controller.onShowPopupKeysPanel(this);
|
controller.onShowPopupKeysPanel(this);
|
||||||
final PopupKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
|
final PopupKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
|
||||||
if (accessibilityDelegate != null
|
if (accessibilityDelegate != null
|
||||||
|
@ -222,8 +225,8 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane
|
||||||
false /* isKeyRepeat */);
|
false /* isKeyRepeat */);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (mKeyEventListener != null) {
|
} else if (mEmojiViewCallback != null) {
|
||||||
mKeyEventListener.onReleaseKey(key);
|
mEmojiViewCallback.onReleaseKey(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,28 +317,4 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane
|
||||||
}
|
}
|
||||||
return super.onHoverEvent(event);
|
return super.onHoverEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
private View getContainerView() {
|
|
||||||
return (View)getParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showInParent(final ViewGroup parentView) {
|
|
||||||
removeFromParent();
|
|
||||||
parentView.addView(getContainerView());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeFromParent() {
|
|
||||||
final View containerView = getContainerView();
|
|
||||||
final ViewGroup currentParent = (ViewGroup)containerView.getParent();
|
|
||||||
if (currentParent != null) {
|
|
||||||
currentParent.removeView(containerView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isShowingInParent() {
|
|
||||||
return (getContainerView().getParent() != null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,17 @@ package helium314.keyboard.keyboard;
|
||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import helium314.keyboard.keyboard.emoji.OnKeyEventListener;
|
import helium314.keyboard.keyboard.emoji.EmojiViewCallback;
|
||||||
|
|
||||||
public interface PopupKeysPanel {
|
public interface PopupKeysPanel {
|
||||||
interface Controller {
|
interface Controller {
|
||||||
|
/**
|
||||||
|
* Set the layout gravity.
|
||||||
|
* @param layoutGravity requested by the popup
|
||||||
|
*/
|
||||||
|
default void setLayoutGravity(int layoutGravity) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the {@link PopupKeysPanel} to the target view.
|
* Add the {@link PopupKeysPanel} to the target view.
|
||||||
* @param panel the panel to be shown.
|
* @param panel the panel to be shown.
|
||||||
|
@ -59,19 +66,18 @@ public interface PopupKeysPanel {
|
||||||
* Initializes the layout and event handling of this {@link PopupKeysPanel} and calls the
|
* Initializes the layout and event handling of this {@link PopupKeysPanel} and calls the
|
||||||
* controller's onShowPopupKeysPanel to add the panel's container view.
|
* controller's onShowPopupKeysPanel to add the panel's container view.
|
||||||
* Same as {@link PopupKeysPanel#showPopupKeysPanel(View, Controller, int, int, KeyboardActionListener)},
|
* Same as {@link PopupKeysPanel#showPopupKeysPanel(View, Controller, int, int, KeyboardActionListener)},
|
||||||
* but with a {@link OnKeyEventListener}.
|
* but with a {@link EmojiViewCallback}.
|
||||||
*
|
*
|
||||||
* @param parentView the parent view of this {@link PopupKeysPanel}
|
* @param parentView the parent view of this {@link PopupKeysPanel}
|
||||||
* @param controller the controller that can dismiss this {@link PopupKeysPanel}
|
* @param controller the controller that can dismiss this {@link PopupKeysPanel}
|
||||||
* @param pointX x coordinate of this {@link PopupKeysPanel}
|
* @param pointX x coordinate of this {@link PopupKeysPanel}
|
||||||
* @param pointY y coordinate of this {@link PopupKeysPanel}
|
* @param pointY y coordinate of this {@link PopupKeysPanel}
|
||||||
* @param listener the listener that will receive keyboard action from this
|
* @param emojiViewCallback to receive keyboard actions from this {@link PopupKeysPanel}.
|
||||||
* {@link PopupKeysPanel}.
|
|
||||||
*/
|
*/
|
||||||
// TODO: Currently the PopupKeysPanel is inside a container view that is added to the parent.
|
// TODO: Currently the PopupKeysPanel is inside a container view that is added to the parent.
|
||||||
// Consider the simpler approach of placing the PopupKeysPanel itself into the parent view.
|
// Consider the simpler approach of placing the PopupKeysPanel itself into the parent view.
|
||||||
void showPopupKeysPanel(View parentView, Controller controller, int pointX,
|
void showPopupKeysPanel(View parentView, Controller controller, int pointX,
|
||||||
int pointY, OnKeyEventListener listener);
|
int pointY, EmojiViewCallback emojiViewCallback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dismisses the popup keys panel and calls the controller's onDismissPopupKeysPanel to remove
|
* Dismisses the popup keys panel and calls the controller's onDismissPopupKeysPanel to remove
|
||||||
|
@ -127,20 +133,35 @@ public interface PopupKeysPanel {
|
||||||
*/
|
*/
|
||||||
int translateY(int y);
|
int translateY(int y);
|
||||||
|
|
||||||
|
default View getContainerView() {
|
||||||
|
return (View) ((View) this).getParent();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show this {@link PopupKeysPanel} in the parent view.
|
* Show this {@link PopupKeysPanel} in the parent view.
|
||||||
*
|
*
|
||||||
* @param parentView the {@link ViewGroup} that hosts this {@link PopupKeysPanel}.
|
* @param parentView the {@link ViewGroup} that hosts this {@link PopupKeysPanel}.
|
||||||
*/
|
*/
|
||||||
void showInParent(ViewGroup parentView);
|
default void showInParent(ViewGroup parentView) {
|
||||||
|
removeFromParent();
|
||||||
|
parentView.addView(getContainerView());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove this {@link PopupKeysPanel} from the parent view.
|
* Remove this {@link PopupKeysPanel} from the parent view.
|
||||||
*/
|
*/
|
||||||
void removeFromParent();
|
default void removeFromParent() {
|
||||||
|
final View containerView = getContainerView();
|
||||||
|
final ViewGroup currentParent = (ViewGroup)containerView.getParent();
|
||||||
|
if (currentParent != null) {
|
||||||
|
currentParent.removeView(containerView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether the panel is currently being shown.
|
* Return whether the panel is currently being shown.
|
||||||
*/
|
*/
|
||||||
boolean isShowingInParent();
|
default boolean isShowingInParent() {
|
||||||
|
return getContainerView().getParent() != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
121
app/src/main/java/helium314/keyboard/keyboard/PopupTextView.java
Normal file
121
app/src/main/java/helium314/keyboard/keyboard/PopupTextView.java
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
* modified
|
||||||
|
* SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package helium314.keyboard.keyboard;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import helium314.keyboard.keyboard.emoji.EmojiViewCallback;
|
||||||
|
import helium314.keyboard.keyboard.internal.KeyDrawParams;
|
||||||
|
import helium314.keyboard.latin.R;
|
||||||
|
import helium314.keyboard.latin.common.ColorType;
|
||||||
|
import helium314.keyboard.latin.common.CoordinateUtils;
|
||||||
|
import helium314.keyboard.latin.settings.Settings;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A view that displays popup text.
|
||||||
|
*/
|
||||||
|
public class PopupTextView extends TextView implements PopupKeysPanel {
|
||||||
|
private final int[] mCoordinates = CoordinateUtils.newInstance();
|
||||||
|
private final Typeface mTypeface;
|
||||||
|
private Controller mController = EMPTY_CONTROLLER;
|
||||||
|
private int mOriginX;
|
||||||
|
private int mOriginY;
|
||||||
|
private Key mKey;
|
||||||
|
private EmojiViewCallback mEmojiViewCallback;
|
||||||
|
|
||||||
|
public PopupTextView(final Context context, final AttributeSet attrs) {
|
||||||
|
this(context, attrs, R.attr.popupKeysKeyboardViewStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PopupTextView(final Context context, final AttributeSet attrs,
|
||||||
|
final int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
mTypeface = Settings.getInstance().getCustomTypeface();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyDrawParams(Key key, KeyDrawParams drawParams) {
|
||||||
|
mKey = key;
|
||||||
|
Settings.getValues().mColors.setBackground(this, ColorType.KEY_PREVIEW_BACKGROUND);
|
||||||
|
setTextColor(drawParams.mPreviewTextColor);
|
||||||
|
setTextSize(TypedValue.COMPLEX_UNIT_PX, key.selectHintTextSize(drawParams) << 1);
|
||||||
|
setTypeface(mTypeface == null ? key.selectTypeface(drawParams) : mTypeface);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showPopupKeysPanel(final View parentView, final Controller controller,
|
||||||
|
final int pointX, final int pointY, final KeyboardActionListener listener) {
|
||||||
|
showPopupKeysPanelInternal(parentView, controller, pointX, pointY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showPopupKeysPanel(final View parentView, final Controller controller,
|
||||||
|
final int pointX, final int pointY, final EmojiViewCallback emojiViewCallback) {
|
||||||
|
mEmojiViewCallback = emojiViewCallback;
|
||||||
|
showPopupKeysPanelInternal(parentView, controller, pointX, pointY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showPopupKeysPanelInternal(final View parentView, final Controller controller,
|
||||||
|
final int pointX, final int pointY) {
|
||||||
|
mController = controller;
|
||||||
|
final View container = getContainerView();
|
||||||
|
// The coordinates of panel's left-top corner in parentView's coordinate system.
|
||||||
|
// We need to consider background drawable paddings.
|
||||||
|
final int x = pointX - getMeasuredWidth() / 2 - container.getPaddingLeft() - getPaddingLeft();
|
||||||
|
final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom()
|
||||||
|
+ getPaddingBottom();
|
||||||
|
|
||||||
|
parentView.getLocationInWindow(mCoordinates);
|
||||||
|
// Ensure the horizontal position of the panel does not extend past the parentView edges.
|
||||||
|
final int maxX = parentView.getMeasuredWidth() - container.getMeasuredWidth();
|
||||||
|
final int panelX = Math.max(0, Math.min(maxX, x)) + CoordinateUtils.x(mCoordinates);
|
||||||
|
final int panelY = y + CoordinateUtils.y(mCoordinates);
|
||||||
|
container.setX(panelX);
|
||||||
|
container.setY(panelY);
|
||||||
|
|
||||||
|
mOriginX = x + container.getPaddingLeft();
|
||||||
|
mOriginY = y + container.getPaddingTop();
|
||||||
|
controller.setLayoutGravity(Gravity.NO_GRAVITY);
|
||||||
|
controller.onShowPopupKeysPanel(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDownEvent(final int x, final int y, final int pointerId, final long eventTime) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMoveEvent(final int x, final int y, final int pointerId, final long eventTime) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpEvent(final int x, final int y, final int pointerId, final long eventTime) {
|
||||||
|
mEmojiViewCallback.onReleaseKey(mKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dismissPopupKeysPanel() {
|
||||||
|
if (!isShowingInParent()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mController.onDismissPopupKeysPanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int translateX(final int x) {
|
||||||
|
return x - mOriginX;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int translateY(final int y) {
|
||||||
|
return y - mOriginY;
|
||||||
|
}
|
||||||
|
}
|
|
@ -112,12 +112,12 @@ final class DynamicGridKeyboard extends Keyboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDynamicOccupiedHeight() {
|
public int getDynamicOccupiedHeight() {
|
||||||
final int row = (mGridKeys.size() - 1) / mColumnsNum + 1;
|
final int row = (mGridKeys.size() - 1) / getOccupiedColumnCount() + 1;
|
||||||
return row * mVerticalStep;
|
return row * mVerticalStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getColumnsCount() {
|
public int getOccupiedColumnCount() {
|
||||||
return mColumnsNum;
|
return mColumnsNum - mEmptyColumnIndices.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPendingKey(final Key usedKey) {
|
public void addPendingKey(final Key usedKey) {
|
||||||
|
|
|
@ -324,7 +324,7 @@ final class EmojiCategory {
|
||||||
final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs,
|
final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs,
|
||||||
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
|
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
|
||||||
0, 0, ResourceUtils.getKeyboardWidth(mContext, Settings.getValues()));
|
0, 0, ResourceUtils.getKeyboardWidth(mContext, Settings.getValues()));
|
||||||
return MAX_LINE_COUNT_PER_PAGE * tempKeyboard.getColumnsCount();
|
return MAX_LINE_COUNT_PER_PAGE * tempKeyboard.getOccupiedColumnCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Comparator<Key> EMOJI_KEY_COMPARATOR = (lhs, rhs) -> {
|
private static final Comparator<Key> EMOJI_KEY_COMPARATOR = (lhs, rhs) -> {
|
||||||
|
|
|
@ -13,6 +13,9 @@ import android.graphics.PorterDuff;
|
||||||
import android.graphics.PorterDuffXfermode;
|
import android.graphics.PorterDuffXfermode;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import helium314.keyboard.keyboard.PopupTextView;
|
||||||
import helium314.keyboard.latin.utils.Log;
|
import helium314.keyboard.latin.utils.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
@ -53,14 +56,18 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
private static final long KEY_PRESS_DELAY_TIME = 250; // msec
|
private static final long KEY_PRESS_DELAY_TIME = 250; // msec
|
||||||
private static final long KEY_RELEASE_DELAY_TIME = 30; // msec
|
private static final long KEY_RELEASE_DELAY_TIME = 30; // msec
|
||||||
|
|
||||||
private static final OnKeyEventListener EMPTY_LISTENER = new OnKeyEventListener() {
|
private static final EmojiViewCallback EMPTY_EMOJI_VIEW_CALLBACK = new EmojiViewCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPressKey(final Key key) {}
|
public void onPressKey(final Key key) {}
|
||||||
@Override
|
@Override
|
||||||
public void onReleaseKey(final Key key) {}
|
public void onReleaseKey(final Key key) {}
|
||||||
|
@Override
|
||||||
|
public String getDescription(String emoji) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private OnKeyEventListener mListener = EMPTY_LISTENER;
|
private EmojiViewCallback mEmojiViewCallback = EMPTY_EMOJI_VIEW_CALLBACK;
|
||||||
private final KeyDetector mKeyDetector = new KeyDetector();
|
private final KeyDetector mKeyDetector = new KeyDetector();
|
||||||
private KeyboardAccessibilityDelegate<EmojiPageKeyboardView> mAccessibilityDelegate;
|
private KeyboardAccessibilityDelegate<EmojiPageKeyboardView> mAccessibilityDelegate;
|
||||||
|
|
||||||
|
@ -74,6 +81,8 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
|
|
||||||
// More keys keyboard
|
// More keys keyboard
|
||||||
private final View mPopupKeysKeyboardContainer;
|
private final View mPopupKeysKeyboardContainer;
|
||||||
|
private final PopupTextView mDescriptionView;
|
||||||
|
private final PopupKeysKeyboardView mPopupKeysKeyboardView;
|
||||||
private final WeakHashMap<Key, Keyboard> mPopupKeysKeyboardCache = new WeakHashMap<>();
|
private final WeakHashMap<Key, Keyboard> mPopupKeysKeyboardCache = new WeakHashMap<>();
|
||||||
private final boolean mConfigShowPopupKeysKeyboardAtTouchedPoint;
|
private final boolean mConfigShowPopupKeysKeyboardAtTouchedPoint;
|
||||||
private final ViewGroup mPopupKeysPlacerView;
|
private final ViewGroup mPopupKeysPlacerView;
|
||||||
|
@ -102,6 +111,8 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
|
|
||||||
final LayoutInflater inflater = LayoutInflater.from(getContext());
|
final LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||||
mPopupKeysKeyboardContainer = inflater.inflate(popupKeysKeyboardLayoutId, null);
|
mPopupKeysKeyboardContainer = inflater.inflate(popupKeysKeyboardLayoutId, null);
|
||||||
|
mDescriptionView = mPopupKeysKeyboardContainer.findViewById(R.id.description_view);
|
||||||
|
mPopupKeysKeyboardView = mPopupKeysKeyboardContainer.findViewById(R.id.popup_keys_keyboard_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -146,8 +157,8 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnKeyEventListener(final OnKeyEventListener listener) {
|
public void setEmojiViewCallback(final EmojiViewCallback emojiViewCallback) {
|
||||||
mListener = listener;
|
mEmojiViewCallback = emojiViewCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -169,7 +180,8 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public PopupKeysPanel showPopupKeysKeyboard(@NonNull final Key key, final int lastX, final int lastY) {
|
private PopupKeysPanel showPopupKeysKeyboard(@NonNull final Key key) {
|
||||||
|
mPopupKeysKeyboardView.setVisibility(GONE);
|
||||||
final PopupKeySpec[] popupKeys = key.getPopupKeys();
|
final PopupKeySpec[] popupKeys = key.getPopupKeys();
|
||||||
if (popupKeys == null) {
|
if (popupKeys == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -182,21 +194,9 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
mPopupKeysKeyboardCache.put(key, popupKeysKeyboard);
|
mPopupKeysKeyboardCache.put(key, popupKeysKeyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
final View container = mPopupKeysKeyboardContainer;
|
mPopupKeysKeyboardView.setKeyboard(popupKeysKeyboard);
|
||||||
final PopupKeysKeyboardView popupKeysKeyboardView = container.findViewById(R.id.popup_keys_keyboard_view);
|
mPopupKeysKeyboardView.setVisibility(VISIBLE);
|
||||||
popupKeysKeyboardView.setKeyboard(popupKeysKeyboard);
|
return mPopupKeysKeyboardView;
|
||||||
container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
|
||||||
|
|
||||||
final int[] lastCoords = CoordinateUtils.newCoordinateArray(1, lastX, lastY);
|
|
||||||
// The popup keys keyboard is usually horizontally aligned with the center of the parent key.
|
|
||||||
// If showPopupKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more
|
|
||||||
// keys keyboard is placed at the touch point of the parent key.
|
|
||||||
final int pointX = mConfigShowPopupKeysKeyboardAtTouchedPoint
|
|
||||||
? CoordinateUtils.x(lastCoords)
|
|
||||||
: key.getX() + key.getWidth() / 2;
|
|
||||||
final int pointY = key.getY();
|
|
||||||
popupKeysKeyboardView.showPopupKeysPanel(this, this, pointX, pointY, mListener);
|
|
||||||
return popupKeysKeyboardView;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dismissPopupKeysPanel() {
|
private void dismissPopupKeysPanel() {
|
||||||
|
@ -209,6 +209,17 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
return mPopupKeysPanel != null;
|
return mPopupKeysPanel != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLayoutGravity(int layoutGravity) {
|
||||||
|
var layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
layoutParams.gravity = mDescriptionView.getMeasuredWidth() > mPopupKeysKeyboardView.getMeasuredWidth()?
|
||||||
|
layoutGravity : Gravity.CENTER_HORIZONTAL;
|
||||||
|
mPopupKeysKeyboardContainer.setLayoutParams(layoutParams);
|
||||||
|
mDescriptionView.setLayoutParams(layoutParams);
|
||||||
|
mPopupKeysKeyboardView.setLayoutParams(layoutParams);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onShowPopupKeysPanel(final PopupKeysPanel panel) {
|
public void onShowPopupKeysPanel(final PopupKeysPanel panel) {
|
||||||
// install placer view only when needed instead of when this
|
// install placer view only when needed instead of when this
|
||||||
|
@ -290,9 +301,11 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var descriptionPanel = showDescription(key);
|
||||||
|
final PopupKeysPanel popupKeysPanel = showPopupKeysKeyboard(key);
|
||||||
|
|
||||||
final int x = mLastX;
|
final int x = mLastX;
|
||||||
final int y = mLastY;
|
final int y = mLastY;
|
||||||
final PopupKeysPanel popupKeysPanel = showPopupKeysKeyboard(key, x, y);
|
|
||||||
if (popupKeysPanel != null) {
|
if (popupKeysPanel != null) {
|
||||||
final int translatedX = popupKeysPanel.translateX(x);
|
final int translatedX = popupKeysPanel.translateX(x);
|
||||||
final int translatedY = popupKeysPanel.translateY(y);
|
final int translatedY = popupKeysPanel.translateY(y);
|
||||||
|
@ -301,6 +314,34 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
// want any scroll to append during this entire input.
|
// want any scroll to append during this entire input.
|
||||||
disallowParentInterceptTouchEvent(true);
|
disallowParentInterceptTouchEvent(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (popupKeysPanel != null || descriptionPanel != null) {
|
||||||
|
mPopupKeysKeyboardContainer.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
|
||||||
|
final int[] lastCoords = CoordinateUtils.newCoordinateArray(1, x, y);
|
||||||
|
// The popup keys keyboard is usually horizontally aligned with the center of the parent key.
|
||||||
|
// If showPopupKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more
|
||||||
|
// keys keyboard is placed at the touch point of the parent key.
|
||||||
|
final int pointX = mConfigShowPopupKeysKeyboardAtTouchedPoint
|
||||||
|
? CoordinateUtils.x(lastCoords)
|
||||||
|
: key.getX() + key.getWidth() / 2;
|
||||||
|
final int pointY = key.getY() - getKeyboard().mVerticalGap;
|
||||||
|
(popupKeysPanel != null? popupKeysPanel : descriptionPanel)
|
||||||
|
.showPopupKeysPanel(this, this, pointX, pointY, mEmojiViewCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PopupKeysPanel showDescription(Key key) {
|
||||||
|
mDescriptionView.setVisibility(GONE);
|
||||||
|
var description = mEmojiViewCallback.getDescription(key.getLabel());
|
||||||
|
if (description == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDescriptionView.setText(description);
|
||||||
|
mDescriptionView.setKeyDrawParams(key, getKeyDrawParams());
|
||||||
|
mDescriptionView.setVisibility(VISIBLE);
|
||||||
|
return mDescriptionView;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerPress(final Key key) {
|
private void registerPress(final Key key) {
|
||||||
|
@ -318,7 +359,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
releasedKey.onReleased();
|
releasedKey.onReleased();
|
||||||
invalidateKey(releasedKey);
|
invalidateKey(releasedKey);
|
||||||
if (withKeyRegistering) {
|
if (withKeyRegistering) {
|
||||||
mListener.onReleaseKey(releasedKey);
|
mEmojiViewCallback.onReleaseKey(releasedKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +367,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
mPendingKeyDown = null;
|
mPendingKeyDown = null;
|
||||||
pressedKey.onPressed();
|
pressedKey.onPressed();
|
||||||
invalidateKey(pressedKey);
|
invalidateKey(pressedKey);
|
||||||
mListener.onPressKey(pressedKey);
|
mEmojiViewCallback.onPressKey(pressedKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void releaseCurrentKey(final boolean withKeyRegistering) {
|
public void releaseCurrentKey(final boolean withKeyRegistering) {
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
package helium314.keyboard.keyboard.emoji;
|
package helium314.keyboard.keyboard.emoji;
|
||||||
|
|
||||||
import helium314.keyboard.latin.utils.Log;
|
import helium314.keyboard.latin.utils.Log;
|
||||||
import android.util.SparseArray;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -23,13 +22,13 @@ final class EmojiPalettesAdapter extends RecyclerView.Adapter<EmojiPalettesAdapt
|
||||||
private static final boolean DEBUG_PAGER = false;
|
private static final boolean DEBUG_PAGER = false;
|
||||||
|
|
||||||
private final int mCategoryId;
|
private final int mCategoryId;
|
||||||
private final OnKeyEventListener mListener;
|
private final EmojiViewCallback mEmojiViewCallback;
|
||||||
private final EmojiCategory mEmojiCategory;
|
private final EmojiCategory mEmojiCategory;
|
||||||
|
|
||||||
public EmojiPalettesAdapter(final EmojiCategory emojiCategory, int categoryId, final OnKeyEventListener listener) {
|
public EmojiPalettesAdapter(final EmojiCategory emojiCategory, int categoryId, final EmojiViewCallback emojiViewCallback) {
|
||||||
mEmojiCategory = emojiCategory;
|
mEmojiCategory = emojiCategory;
|
||||||
mCategoryId = categoryId;
|
mCategoryId = categoryId;
|
||||||
mListener = listener;
|
mEmojiViewCallback = emojiViewCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -38,6 +37,7 @@ final class EmojiPalettesAdapter extends RecyclerView.Adapter<EmojiPalettesAdapt
|
||||||
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||||
final EmojiPageKeyboardView keyboardView = (EmojiPageKeyboardView)inflater.inflate(
|
final EmojiPageKeyboardView keyboardView = (EmojiPageKeyboardView)inflater.inflate(
|
||||||
R.layout.emoji_keyboard_page, parent, false);
|
R.layout.emoji_keyboard_page, parent, false);
|
||||||
|
keyboardView.setEmojiViewCallback(mEmojiViewCallback);
|
||||||
return new ViewHolder(keyboardView);
|
return new ViewHolder(keyboardView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,6 @@ final class EmojiPalettesAdapter extends RecyclerView.Adapter<EmojiPalettesAdapt
|
||||||
final Keyboard keyboard =
|
final Keyboard keyboard =
|
||||||
mEmojiCategory.getKeyboardFromAdapterPosition(mCategoryId, position);
|
mEmojiCategory.getKeyboardFromAdapterPosition(mCategoryId, position);
|
||||||
holder.getKeyboardView().setKeyboard(keyboard);
|
holder.getKeyboardView().setKeyboard(keyboard);
|
||||||
holder.getKeyboardView().setOnKeyEventListener(mListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -38,12 +38,16 @@ import helium314.keyboard.keyboard.internal.KeyDrawParams;
|
||||||
import helium314.keyboard.keyboard.internal.KeyVisualAttributes;
|
import helium314.keyboard.keyboard.internal.KeyVisualAttributes;
|
||||||
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode;
|
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode;
|
||||||
import helium314.keyboard.latin.AudioAndHapticFeedbackManager;
|
import helium314.keyboard.latin.AudioAndHapticFeedbackManager;
|
||||||
|
import helium314.keyboard.latin.DictionaryFactory;
|
||||||
import helium314.keyboard.latin.R;
|
import helium314.keyboard.latin.R;
|
||||||
|
import helium314.keyboard.latin.RichInputMethodManager;
|
||||||
import helium314.keyboard.latin.RichInputMethodSubtype;
|
import helium314.keyboard.latin.RichInputMethodSubtype;
|
||||||
|
import helium314.keyboard.latin.SingleDictionaryFacilitator;
|
||||||
import helium314.keyboard.latin.common.ColorType;
|
import helium314.keyboard.latin.common.ColorType;
|
||||||
import helium314.keyboard.latin.common.Colors;
|
import helium314.keyboard.latin.common.Colors;
|
||||||
import helium314.keyboard.latin.settings.Settings;
|
import helium314.keyboard.latin.settings.Settings;
|
||||||
import helium314.keyboard.latin.settings.SettingsValues;
|
import helium314.keyboard.latin.settings.SettingsValues;
|
||||||
|
import helium314.keyboard.latin.utils.DictionaryInfoUtils;
|
||||||
import helium314.keyboard.latin.utils.ResourceUtils;
|
import helium314.keyboard.latin.utils.ResourceUtils;
|
||||||
|
|
||||||
import static helium314.keyboard.latin.common.Constants.NOT_A_COORDINATE;
|
import static helium314.keyboard.latin.common.Constants.NOT_A_COORDINATE;
|
||||||
|
@ -60,7 +64,7 @@ import static helium314.keyboard.latin.common.Constants.NOT_A_COORDINATE;
|
||||||
* Because of the above reasons, this class doesn't extend {@link KeyboardView}.
|
* Because of the above reasons, this class doesn't extend {@link KeyboardView}.
|
||||||
*/
|
*/
|
||||||
public final class EmojiPalettesView extends LinearLayout
|
public final class EmojiPalettesView extends LinearLayout
|
||||||
implements View.OnClickListener, OnKeyEventListener {
|
implements View.OnClickListener, EmojiViewCallback {
|
||||||
private static final class PagerViewHolder extends RecyclerView.ViewHolder {
|
private static final class PagerViewHolder extends RecyclerView.ViewHolder {
|
||||||
private long mCategoryId;
|
private long mCategoryId;
|
||||||
|
|
||||||
|
@ -71,7 +75,7 @@ public final class EmojiPalettesView extends LinearLayout
|
||||||
|
|
||||||
private final class PagerAdapter extends RecyclerView.Adapter<PagerViewHolder> {
|
private final class PagerAdapter extends RecyclerView.Adapter<PagerViewHolder> {
|
||||||
private boolean mInitialized;
|
private boolean mInitialized;
|
||||||
private Map<Integer, RecyclerView> mViews = new HashMap<>(mEmojiCategory.getShownCategories().size());
|
private final Map<Integer, RecyclerView> mViews = new HashMap<>(mEmojiCategory.getShownCategories().size());
|
||||||
|
|
||||||
private PagerAdapter(ViewPager2 pager) {
|
private PagerAdapter(ViewPager2 pager) {
|
||||||
setHasStableIds(true);
|
setHasStableIds(true);
|
||||||
|
@ -179,15 +183,14 @@ public final class EmojiPalettesView extends LinearLayout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static SingleDictionaryFacilitator sDictionaryFacilitator;
|
||||||
|
|
||||||
private boolean initialized = false;
|
private boolean initialized = false;
|
||||||
private final Colors mColors;
|
private final Colors mColors;
|
||||||
private final EmojiLayoutParams mEmojiLayoutParams;
|
private final EmojiLayoutParams mEmojiLayoutParams;
|
||||||
|
|
||||||
private LinearLayout mTabStrip;
|
private LinearLayout mTabStrip;
|
||||||
private EmojiCategoryPageIndicatorView mEmojiCategoryPageIndicatorView;
|
private EmojiCategoryPageIndicatorView mEmojiCategoryPageIndicatorView;
|
||||||
|
|
||||||
private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
|
private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
|
||||||
|
|
||||||
private final EmojiCategory mEmojiCategory;
|
private final EmojiCategory mEmojiCategory;
|
||||||
private ViewPager2 mPager;
|
private ViewPager2 mPager;
|
||||||
|
|
||||||
|
@ -254,9 +257,7 @@ public final class EmojiPalettesView extends LinearLayout
|
||||||
mEmojiLayoutParams.setEmojiListProperties(mPager);
|
mEmojiLayoutParams.setEmojiListProperties(mPager);
|
||||||
mEmojiCategoryPageIndicatorView = findViewById(R.id.emoji_category_page_id_view);
|
mEmojiCategoryPageIndicatorView = findViewById(R.id.emoji_category_page_id_view);
|
||||||
mEmojiLayoutParams.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView);
|
mEmojiLayoutParams.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView);
|
||||||
|
|
||||||
setCurrentCategoryId(mEmojiCategory.getCurrentCategoryId(), true);
|
setCurrentCategoryId(mEmojiCategory.getCurrentCategoryId(), true);
|
||||||
|
|
||||||
mEmojiCategoryPageIndicatorView.setColors(mColors.get(ColorType.EMOJI_CATEGORY_SELECTED), mColors.get(ColorType.STRIP_BACKGROUND));
|
mEmojiCategoryPageIndicatorView.setColors(mColors.get(ColorType.EMOJI_CATEGORY_SELECTED), mColors.get(ColorType.STRIP_BACKGROUND));
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
@ -280,8 +281,7 @@ public final class EmojiPalettesView extends LinearLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from {@link EmojiPageKeyboardView} through
|
* Called from {@link EmojiPageKeyboardView} through {@link EmojiViewCallback}
|
||||||
* {@link helium314.keyboard.keyboard.emoji.OnKeyEventListener}
|
|
||||||
* interface to handle touch events from non-View-based elements such as Emoji buttons.
|
* interface to handle touch events from non-View-based elements such as Emoji buttons.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -291,10 +291,9 @@ public final class EmojiPalettesView extends LinearLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from {@link EmojiPageKeyboardView} through
|
* Called from {@link EmojiPageKeyboardView} through {@link EmojiViewCallback}
|
||||||
* {@link helium314.keyboard.keyboard.emoji.OnKeyEventListener}
|
|
||||||
* interface to handle touch events from non-View-based elements such as Emoji buttons.
|
* interface to handle touch events from non-View-based elements such as Emoji buttons.
|
||||||
* This may be called without any prior call to {@link OnKeyEventListener#onPressKey(Key)}.
|
* This may be called without any prior call to {@link EmojiViewCallback#onPressKey(Key)}.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onReleaseKey(final Key key) {
|
public void onReleaseKey(final Key key) {
|
||||||
|
@ -310,6 +309,20 @@ public final class EmojiPalettesView extends LinearLayout
|
||||||
mKeyboardActionListener.onCodeInput(KeyCode.ALPHA, NOT_A_COORDINATE, NOT_A_COORDINATE, false);
|
mKeyboardActionListener.onCodeInput(KeyCode.ALPHA, NOT_A_COORDINATE, NOT_A_COORDINATE, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription(String emoji) {
|
||||||
|
if (sDictionaryFacilitator == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var wordProperty = sDictionaryFacilitator.getWordProperty(emoji);
|
||||||
|
if (wordProperty == null || ! wordProperty.mHasShortcuts) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wordProperty.mShortcutTargets.get(0).mWord;
|
||||||
|
}
|
||||||
|
|
||||||
public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
|
public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
|
||||||
if (!enabled) return;
|
if (!enabled) return;
|
||||||
// TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off?
|
// TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off?
|
||||||
|
@ -324,6 +337,7 @@ public final class EmojiPalettesView extends LinearLayout
|
||||||
final KeyDrawParams params = new KeyDrawParams();
|
final KeyDrawParams params = new KeyDrawParams();
|
||||||
params.updateParams(mEmojiLayoutParams.getBottomRowKeyboardHeight(), keyVisualAttr);
|
params.updateParams(mEmojiLayoutParams.getBottomRowKeyboardHeight(), keyVisualAttr);
|
||||||
setupSidePadding();
|
setupSidePadding();
|
||||||
|
initDictionaryFacilitator();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addRecentKey(final Key key) {
|
private void addRecentKey(final Key key) {
|
||||||
|
@ -403,7 +417,7 @@ public final class EmojiPalettesView extends LinearLayout
|
||||||
if (mPager.getScrollState() != ViewPager2.SCROLL_STATE_DRAGGING) {
|
if (mPager.getScrollState() != ViewPager2.SCROLL_STATE_DRAGGING) {
|
||||||
// Not swiping
|
// Not swiping
|
||||||
mPager.setCurrentItem(mEmojiCategory.getTabIdFromCategoryId(
|
mPager.setCurrentItem(mEmojiCategory.getTabIdFromCategoryId(
|
||||||
mEmojiCategory.getCurrentCategoryId()), ! initial);
|
mEmojiCategory.getCurrentCategoryId()), ! initial && ! isAnimationsDisabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings.getValues().mSecondaryStripVisible) {
|
if (Settings.getValues().mSecondaryStripVisible) {
|
||||||
|
@ -418,6 +432,11 @@ public final class EmojiPalettesView extends LinearLayout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isAnimationsDisabled() {
|
||||||
|
return android.provider.Settings.Global.getFloat(getContext().getContentResolver(),
|
||||||
|
android.provider.Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f) == 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
public void clearKeyboardCache() {
|
public void clearKeyboardCache() {
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
return;
|
return;
|
||||||
|
@ -425,5 +444,27 @@ public final class EmojiPalettesView extends LinearLayout
|
||||||
|
|
||||||
mEmojiCategory.clearKeyboardCache();
|
mEmojiCategory.clearKeyboardCache();
|
||||||
mPager.getAdapter().notifyDataSetChanged();
|
mPager.getAdapter().notifyDataSetChanged();
|
||||||
|
closeDictionaryFacilitator();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDictionaryFacilitator() {
|
||||||
|
if (Settings.getValues().mShowEmojiDescriptions) {
|
||||||
|
var locale = RichInputMethodManager.getInstance().getCurrentSubtype().getLocale();
|
||||||
|
if (sDictionaryFacilitator == null || ! sDictionaryFacilitator.isForLocale(locale)) {
|
||||||
|
closeDictionaryFacilitator();
|
||||||
|
var dictFile = DictionaryInfoUtils.getCachedDictForLocaleAndType(locale, "emoji", getContext());
|
||||||
|
var dictionary = dictFile != null? DictionaryFactory.getDictionary(dictFile, locale) : null;
|
||||||
|
sDictionaryFacilitator = dictionary != null? new SingleDictionaryFacilitator(dictionary) : null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
closeDictionaryFacilitator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void closeDictionaryFacilitator() {
|
||||||
|
if (sDictionaryFacilitator != null) {
|
||||||
|
sDictionaryFacilitator.closeDictionaries();
|
||||||
|
sDictionaryFacilitator = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,10 @@ package helium314.keyboard.keyboard.emoji;
|
||||||
import helium314.keyboard.keyboard.Key;
|
import helium314.keyboard.keyboard.Key;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface to handle touch events from non-View-based elements
|
* Interface to handle callbacks from child elements
|
||||||
* such as Emoji buttons.
|
* such as Emoji buttons and keyboard views.
|
||||||
*/
|
*/
|
||||||
public interface OnKeyEventListener {
|
public interface EmojiViewCallback {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a key is pressed by the user
|
* Called when a key is pressed by the user
|
||||||
|
@ -17,8 +17,13 @@ public interface OnKeyEventListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a key is released.
|
* Called when a key is released.
|
||||||
* This may be called without any prior call to {@link OnKeyEventListener#onPressKey(Key)},
|
* This may be called without any prior call to {@link EmojiViewCallback#onPressKey(Key)},
|
||||||
* for example when a key from a popup keys keyboard is selected by releasing touch on it.
|
* for example when a key from a popup keys keyboard is selected by releasing touch on it.
|
||||||
*/
|
*/
|
||||||
void onReleaseKey(Key key);
|
void onReleaseKey(Key key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called from keyboard view to get an emoji description
|
||||||
|
*/
|
||||||
|
String getDescription(String emoji);
|
||||||
}
|
}
|
|
@ -229,13 +229,15 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co
|
||||||
return
|
return
|
||||||
// replace comma / period if 2 keys in normal bottom row
|
// replace comma / period if 2 keys in normal bottom row
|
||||||
if (baseKeys.last().size == 2) {
|
if (baseKeys.last().size == 2) {
|
||||||
|
val newComma = baseKeys.last()[0]
|
||||||
functionalKeysBottom.replaceFirst(
|
functionalKeysBottom.replaceFirst(
|
||||||
{ it.label == KeyLabel.COMMA || it.groupId == KeyData.GROUP_COMMA},
|
{ it.label == KeyLabel.COMMA || it.groupId == KeyData.GROUP_COMMA},
|
||||||
{ baseKeys.last()[0].copy(newGroupId = 1, newType = baseKeys.last()[0].type ?: it.type) }
|
{ newComma.copy(newGroupId = 1, newType = newComma.type, newLabelFlags = it.labelFlags or newComma.labelFlags) }
|
||||||
)
|
)
|
||||||
|
val newPeriod = baseKeys.last()[1]
|
||||||
functionalKeysBottom.replaceFirst(
|
functionalKeysBottom.replaceFirst(
|
||||||
{ it.label == KeyLabel.PERIOD || it.groupId == KeyData.GROUP_PERIOD},
|
{ it.label == KeyLabel.PERIOD || it.groupId == KeyData.GROUP_PERIOD},
|
||||||
{ baseKeys.last()[1].copy(newGroupId = 2, newType = baseKeys.last()[1].type ?: it.type) }
|
{ newPeriod.copy(newGroupId = 2, newType = newPeriod.type, newLabelFlags = it.labelFlags or newPeriod.labelFlags) }
|
||||||
)
|
)
|
||||||
baseKeys.removeAt(baseKeys.lastIndex)
|
baseKeys.removeAt(baseKeys.lastIndex)
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ class LocaleKeyboardInfos(dataStream: InputStream?, locale: Locale) {
|
||||||
"mns" -> Key.LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO
|
"mns" -> Key.LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO
|
||||||
else -> 0
|
else -> 0
|
||||||
}
|
}
|
||||||
val tlds = getLocaleTlds(locale)
|
val tlds = mutableListOf(Key.POPUP_KEYS_HAS_LABELS)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
readStream(dataStream, false, true)
|
readStream(dataStream, false, true)
|
||||||
|
@ -84,7 +84,7 @@ class LocaleKeyboardInfos(dataStream: InputStream?, locale: Locale) {
|
||||||
READER_MODE_EXTRA_KEYS -> if (!onlyPopupKeys) addExtraKey(line.split(colonSpaceRegex, 2))
|
READER_MODE_EXTRA_KEYS -> if (!onlyPopupKeys) addExtraKey(line.split(colonSpaceRegex, 2))
|
||||||
READER_MODE_LABELS -> if (!onlyPopupKeys) addLabel(line.split(colonSpaceRegex, 2))
|
READER_MODE_LABELS -> if (!onlyPopupKeys) addLabel(line.split(colonSpaceRegex, 2))
|
||||||
READER_MODE_NUMBER_ROW -> localizedNumberKeys = line.splitOnWhitespace()
|
READER_MODE_NUMBER_ROW -> localizedNumberKeys = line.splitOnWhitespace()
|
||||||
READER_MODE_TLD -> SpacedTokens(line).forEach { tlds.add(".$it") }
|
READER_MODE_TLD -> tlds.addAll(SpacedTokens(line).map { ".$it" })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,6 +156,16 @@ class LocaleKeyboardInfos(dataStream: InputStream?, locale: Locale) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addLocaleTlds(locale: Locale) {
|
||||||
|
tlds.add(0, comTld)
|
||||||
|
val ccLower = locale.country.lowercase()
|
||||||
|
if (ccLower.isNotEmpty() && locale.language != SubtypeLocaleUtils.NO_LANGUAGE) {
|
||||||
|
specialCountryTlds[ccLower]?.let { tlds.addAll(SpacedTokens(it)) } ?: tlds.add(".$ccLower")
|
||||||
|
}
|
||||||
|
if ((locale.language != "en" && euroLocales.matches(locale.language)) || euroCountries.matches(locale.country))
|
||||||
|
tlds.add(".eu")
|
||||||
|
tlds.addAll(SpacedTokens(otherDefaultTlds))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addFixedColumnOrder(popupKeys: MutableCollection<String>) {
|
private fun addFixedColumnOrder(popupKeys: MutableCollection<String>) {
|
||||||
|
@ -205,6 +215,7 @@ private fun createLocaleKeyTexts(context: Context, params: KeyboardParams, popup
|
||||||
POPUP_KEYS_MORE -> lkt.addFile(context.assets.open("$LOCALE_TEXTS_FOLDER/more_popups_more.txt"), false)
|
POPUP_KEYS_MORE -> lkt.addFile(context.assets.open("$LOCALE_TEXTS_FOLDER/more_popups_more.txt"), false)
|
||||||
POPUP_KEYS_ALL -> lkt.addFile(context.assets.open("$LOCALE_TEXTS_FOLDER/more_popups_all.txt"), false)
|
POPUP_KEYS_ALL -> lkt.addFile(context.assets.open("$LOCALE_TEXTS_FOLDER/more_popups_all.txt"), false)
|
||||||
}
|
}
|
||||||
|
lkt.addLocaleTlds(params.mId.locale)
|
||||||
return lkt
|
return lkt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,28 +231,6 @@ private fun getStreamForLocale(locale: Locale, context: Context) =
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getLocaleTlds(locale: Locale): LinkedHashSet<String> {
|
|
||||||
val tlds = getDefaultTlds(locale)
|
|
||||||
val ccLower = locale.country.lowercase()
|
|
||||||
if (ccLower.isEmpty() || locale.language == SubtypeLocaleUtils.NO_LANGUAGE)
|
|
||||||
return tlds
|
|
||||||
specialCountryTlds.forEach {
|
|
||||||
if (ccLower != it.first) return@forEach
|
|
||||||
tlds.addAll(SpacedTokens(it.second))
|
|
||||||
return@getLocaleTlds tlds
|
|
||||||
}
|
|
||||||
tlds.add(".$ccLower")
|
|
||||||
return tlds
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getDefaultTlds(locale: Locale): LinkedHashSet<String> {
|
|
||||||
val tlds = linkedSetOf<String>()
|
|
||||||
tlds.addAll(SpacedTokens(defaultTlds))
|
|
||||||
if ((locale.language != "en" && euroLocales.matches(locale.language)) || euroCountries.matches(locale.country))
|
|
||||||
tlds.add(".eu")
|
|
||||||
return tlds
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clearCache() = localeKeyboardInfosCache.clear()
|
fun clearCache() = localeKeyboardInfosCache.clear()
|
||||||
|
|
||||||
// cache the texts, so they don't need to be read over and over
|
// cache the texts, so they don't need to be read over and over
|
||||||
|
@ -341,7 +330,7 @@ const val POPUP_KEYS_NORMAL = "normal"
|
||||||
private const val LOCALE_TEXTS_FOLDER = "locale_key_texts"
|
private const val LOCALE_TEXTS_FOLDER = "locale_key_texts"
|
||||||
|
|
||||||
// either tld is not simply lowercase ISO 3166-1 code, or there are multiple according to some list
|
// either tld is not simply lowercase ISO 3166-1 code, or there are multiple according to some list
|
||||||
private val specialCountryTlds = listOf(
|
private val specialCountryTlds = hashMapOf<String, String>(
|
||||||
"bd" to ".bd .com.bd",
|
"bd" to ".bd .com.bd",
|
||||||
"bq" to ".bq .an .nl",
|
"bq" to ".bq .an .nl",
|
||||||
"bl" to ".bl .gp .fr",
|
"bl" to ".bl .gp .fr",
|
||||||
|
@ -351,4 +340,5 @@ private val specialCountryTlds = listOf(
|
||||||
"mf" to ".mf .gp .fr",
|
"mf" to ".mf .gp .fr",
|
||||||
"tl" to ".tl .tp",
|
"tl" to ".tl .tp",
|
||||||
)
|
)
|
||||||
private const val defaultTlds = ".com .gov .edu .org .net"
|
private const val comTld = ".com"
|
||||||
|
private const val otherDefaultTlds = ".gov .edu .org .net"
|
||||||
|
|
|
@ -83,7 +83,7 @@ object KeyCode {
|
||||||
const val LANGUAGE_SWITCH = -227
|
const val LANGUAGE_SWITCH = -227
|
||||||
|
|
||||||
//const val IME_SHOW_UI = -231
|
//const val IME_SHOW_UI = -231
|
||||||
//const val IME_HIDE_UI = -232
|
const val IME_HIDE_UI = -232
|
||||||
const val VOICE_INPUT = -233
|
const val VOICE_INPUT = -233
|
||||||
|
|
||||||
//const val TOGGLE_SMARTBAR_VISIBILITY = -241
|
//const val TOGGLE_SMARTBAR_VISIBILITY = -241
|
||||||
|
@ -175,6 +175,12 @@ object KeyCode {
|
||||||
const val META_LEFT = -10048
|
const val META_LEFT = -10048
|
||||||
const val META_RIGHT = -10049
|
const val META_RIGHT = -10049
|
||||||
|
|
||||||
|
|
||||||
|
// Intents
|
||||||
|
const val SEND_INTENT_ONE = -20000
|
||||||
|
const val SEND_INTENT_TWO = -20001
|
||||||
|
const val SEND_INTENT_THREE = -20002
|
||||||
|
|
||||||
/** to make sure a FlorisBoard code works when reading a JSON layout */
|
/** to make sure a FlorisBoard code works when reading a JSON layout */
|
||||||
fun Int.checkAndConvertCode(): Int = if (this > 0) this else when (this) {
|
fun Int.checkAndConvertCode(): Int = if (this > 0) this else when (this) {
|
||||||
// working
|
// working
|
||||||
|
@ -183,14 +189,14 @@ object KeyCode {
|
||||||
REDO, ARROW_DOWN, ARROW_UP, ARROW_RIGHT, ARROW_LEFT, CLIPBOARD_COPY, CLIPBOARD_PASTE, CLIPBOARD_SELECT_ALL,
|
REDO, ARROW_DOWN, ARROW_UP, ARROW_RIGHT, ARROW_LEFT, CLIPBOARD_COPY, CLIPBOARD_PASTE, CLIPBOARD_SELECT_ALL,
|
||||||
CLIPBOARD_SELECT_WORD, TOGGLE_INCOGNITO_MODE, TOGGLE_AUTOCORRECT, MOVE_START_OF_LINE, MOVE_END_OF_LINE,
|
CLIPBOARD_SELECT_WORD, TOGGLE_INCOGNITO_MODE, TOGGLE_AUTOCORRECT, MOVE_START_OF_LINE, MOVE_END_OF_LINE,
|
||||||
MOVE_START_OF_PAGE, MOVE_END_OF_PAGE, SHIFT, CAPS_LOCK, MULTIPLE_CODE_POINTS, UNSPECIFIED, CTRL, ALT,
|
MOVE_START_OF_PAGE, MOVE_END_OF_PAGE, SHIFT, CAPS_LOCK, MULTIPLE_CODE_POINTS, UNSPECIFIED, CTRL, ALT,
|
||||||
FN, CLIPBOARD_CLEAR_HISTORY, NUMPAD,
|
FN, CLIPBOARD_CLEAR_HISTORY, NUMPAD, IME_HIDE_UI,
|
||||||
|
|
||||||
// heliboard only
|
// heliboard only
|
||||||
SYMBOL_ALPHA, TOGGLE_ONE_HANDED_MODE, SWITCH_ONE_HANDED_MODE, SPLIT_LAYOUT, SHIFT_ENTER,
|
SYMBOL_ALPHA, TOGGLE_ONE_HANDED_MODE, SWITCH_ONE_HANDED_MODE, SPLIT_LAYOUT, SHIFT_ENTER,
|
||||||
ACTION_NEXT, ACTION_PREVIOUS, NOT_SPECIFIED, CLIPBOARD_COPY_ALL, WORD_LEFT, WORD_RIGHT, PAGE_UP,
|
ACTION_NEXT, ACTION_PREVIOUS, NOT_SPECIFIED, CLIPBOARD_COPY_ALL, WORD_LEFT, WORD_RIGHT, PAGE_UP,
|
||||||
PAGE_DOWN, META, TAB, ESCAPE, INSERT, SLEEP, MEDIA_PLAY, MEDIA_PAUSE, MEDIA_PLAY_PAUSE, MEDIA_NEXT,
|
PAGE_DOWN, META, TAB, ESCAPE, INSERT, SLEEP, MEDIA_PLAY, MEDIA_PAUSE, MEDIA_PLAY_PAUSE, MEDIA_NEXT,
|
||||||
MEDIA_PREVIOUS, VOL_UP, VOL_DOWN, MUTE, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, BACK,
|
MEDIA_PREVIOUS, VOL_UP, VOL_DOWN, MUTE, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, BACK,
|
||||||
TIMESTAMP, CTRL_LEFT, CTRL_RIGHT, ALT_LEFT, ALT_RIGHT, META_LEFT, META_RIGHT
|
TIMESTAMP, CTRL_LEFT, CTRL_RIGHT, ALT_LEFT, ALT_RIGHT, META_LEFT, META_RIGHT, SEND_INTENT_ONE, SEND_INTENT_TWO, SEND_INTENT_THREE,
|
||||||
-> this
|
-> this
|
||||||
|
|
||||||
// conversion
|
// conversion
|
||||||
|
@ -210,12 +216,54 @@ object KeyCode {
|
||||||
|
|
||||||
// todo: there are many more keys, see near https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_0
|
// todo: there are many more keys, see near https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_0
|
||||||
/**
|
/**
|
||||||
* Convert a keyCode / codePoint to a KeyEvent.KEYCODE_<xxx>.
|
* Convert an internal keyCode to a KeyEvent.KEYCODE_<xxx>.
|
||||||
|
* Positive codes are passed through, unknown negative codes result in KeyEvent.KEYCODE_UNKNOWN.
|
||||||
|
* To be uses for fake hardware key press.
|
||||||
|
*/
|
||||||
|
@JvmStatic fun keyCodeToKeyEventCode(keyCode: Int) = when (keyCode) {
|
||||||
|
ARROW_UP -> KeyEvent.KEYCODE_DPAD_UP
|
||||||
|
ARROW_RIGHT -> KeyEvent.KEYCODE_DPAD_RIGHT
|
||||||
|
ARROW_DOWN -> KeyEvent.KEYCODE_DPAD_DOWN
|
||||||
|
ARROW_LEFT -> KeyEvent.KEYCODE_DPAD_LEFT
|
||||||
|
MOVE_START_OF_LINE -> KeyEvent.KEYCODE_MOVE_HOME
|
||||||
|
MOVE_END_OF_LINE -> KeyEvent.KEYCODE_MOVE_END
|
||||||
|
TAB -> KeyEvent.KEYCODE_TAB
|
||||||
|
PAGE_UP -> KeyEvent.KEYCODE_PAGE_UP
|
||||||
|
PAGE_DOWN -> KeyEvent.KEYCODE_PAGE_DOWN
|
||||||
|
ESCAPE -> KeyEvent.KEYCODE_ESCAPE
|
||||||
|
INSERT -> KeyEvent.KEYCODE_INSERT
|
||||||
|
SLEEP -> KeyEvent.KEYCODE_SLEEP
|
||||||
|
MEDIA_PLAY -> KeyEvent.KEYCODE_MEDIA_PLAY
|
||||||
|
MEDIA_PAUSE -> KeyEvent.KEYCODE_MEDIA_PAUSE
|
||||||
|
MEDIA_PLAY_PAUSE -> KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
|
||||||
|
MEDIA_NEXT -> KeyEvent.KEYCODE_MEDIA_NEXT
|
||||||
|
MEDIA_PREVIOUS -> KeyEvent.KEYCODE_MEDIA_PREVIOUS
|
||||||
|
VOL_UP -> KeyEvent.KEYCODE_VOLUME_UP
|
||||||
|
VOL_DOWN -> KeyEvent.KEYCODE_VOLUME_DOWN
|
||||||
|
MUTE -> KeyEvent.KEYCODE_VOLUME_MUTE
|
||||||
|
BACK -> KeyEvent.KEYCODE_BACK
|
||||||
|
F1 -> KeyEvent.KEYCODE_F1
|
||||||
|
F2 -> KeyEvent.KEYCODE_F2
|
||||||
|
F3 -> KeyEvent.KEYCODE_F3
|
||||||
|
F4 -> KeyEvent.KEYCODE_F4
|
||||||
|
F5 -> KeyEvent.KEYCODE_F5
|
||||||
|
F6 -> KeyEvent.KEYCODE_F6
|
||||||
|
F7 -> KeyEvent.KEYCODE_F7
|
||||||
|
F8 -> KeyEvent.KEYCODE_F8
|
||||||
|
F9 -> KeyEvent.KEYCODE_F9
|
||||||
|
F10 -> KeyEvent.KEYCODE_F10
|
||||||
|
F11 -> KeyEvent.KEYCODE_F11
|
||||||
|
F12 -> KeyEvent.KEYCODE_F12
|
||||||
|
else -> if (keyCode < 0) KeyEvent.KEYCODE_UNKNOWN else keyCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: there are many more keys, see near https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_0
|
||||||
|
/**
|
||||||
|
* Convert a codePoint to a KeyEvent.KEYCODE_<xxx>.
|
||||||
* Fallback to KeyEvent.KEYCODE_UNKNOWN.
|
* Fallback to KeyEvent.KEYCODE_UNKNOWN.
|
||||||
* To be uses for fake hardware key press.
|
* To be uses for fake hardware key press.
|
||||||
* */
|
*/
|
||||||
fun Int.toKeyEventCode(): Int = if (this > 0)
|
@JvmStatic fun codePointToKeyEventCode(codePoint: Int): Int = when (codePoint.toChar().uppercaseChar()) {
|
||||||
when (this.toChar().uppercaseChar()) {
|
|
||||||
'/' -> KeyEvent.KEYCODE_SLASH
|
'/' -> KeyEvent.KEYCODE_SLASH
|
||||||
'\\' -> KeyEvent.KEYCODE_BACKSLASH
|
'\\' -> KeyEvent.KEYCODE_BACKSLASH
|
||||||
';' -> KeyEvent.KEYCODE_SEMICOLON
|
';' -> KeyEvent.KEYCODE_SEMICOLON
|
||||||
|
@ -269,40 +317,4 @@ object KeyCode {
|
||||||
'Z' -> KeyEvent.KEYCODE_Z
|
'Z' -> KeyEvent.KEYCODE_Z
|
||||||
else -> KeyEvent.KEYCODE_UNKNOWN
|
else -> KeyEvent.KEYCODE_UNKNOWN
|
||||||
}
|
}
|
||||||
else when (this) {
|
|
||||||
ARROW_UP -> KeyEvent.KEYCODE_DPAD_UP
|
|
||||||
ARROW_RIGHT -> KeyEvent.KEYCODE_DPAD_RIGHT
|
|
||||||
ARROW_DOWN -> KeyEvent.KEYCODE_DPAD_DOWN
|
|
||||||
ARROW_LEFT -> KeyEvent.KEYCODE_DPAD_LEFT
|
|
||||||
MOVE_START_OF_LINE -> KeyEvent.KEYCODE_MOVE_HOME
|
|
||||||
MOVE_END_OF_LINE -> KeyEvent.KEYCODE_MOVE_END
|
|
||||||
TAB -> KeyEvent.KEYCODE_TAB
|
|
||||||
PAGE_UP -> KeyEvent.KEYCODE_PAGE_UP
|
|
||||||
PAGE_DOWN -> KeyEvent.KEYCODE_PAGE_DOWN
|
|
||||||
ESCAPE -> KeyEvent.KEYCODE_ESCAPE
|
|
||||||
INSERT -> KeyEvent.KEYCODE_INSERT
|
|
||||||
SLEEP -> KeyEvent.KEYCODE_SLEEP
|
|
||||||
MEDIA_PLAY -> KeyEvent.KEYCODE_MEDIA_PLAY
|
|
||||||
MEDIA_PAUSE -> KeyEvent.KEYCODE_MEDIA_PAUSE
|
|
||||||
MEDIA_PLAY_PAUSE -> KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
|
|
||||||
MEDIA_NEXT -> KeyEvent.KEYCODE_MEDIA_NEXT
|
|
||||||
MEDIA_PREVIOUS -> KeyEvent.KEYCODE_MEDIA_PREVIOUS
|
|
||||||
VOL_UP -> KeyEvent.KEYCODE_VOLUME_UP
|
|
||||||
VOL_DOWN -> KeyEvent.KEYCODE_VOLUME_DOWN
|
|
||||||
MUTE -> KeyEvent.KEYCODE_VOLUME_MUTE
|
|
||||||
BACK -> KeyEvent.KEYCODE_BACK
|
|
||||||
F1 -> KeyEvent.KEYCODE_F1
|
|
||||||
F2 -> KeyEvent.KEYCODE_F2
|
|
||||||
F3 -> KeyEvent.KEYCODE_F3
|
|
||||||
F4 -> KeyEvent.KEYCODE_F4
|
|
||||||
F5 -> KeyEvent.KEYCODE_F5
|
|
||||||
F6 -> KeyEvent.KEYCODE_F6
|
|
||||||
F7 -> KeyEvent.KEYCODE_F7
|
|
||||||
F8 -> KeyEvent.KEYCODE_F8
|
|
||||||
F9 -> KeyEvent.KEYCODE_F9
|
|
||||||
F10 -> KeyEvent.KEYCODE_F10
|
|
||||||
F11 -> KeyEvent.KEYCODE_F11
|
|
||||||
F12 -> KeyEvent.KEYCODE_F12
|
|
||||||
else -> KeyEvent.KEYCODE_UNKNOWN
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import java.util.Locale;
|
||||||
|
|
||||||
import helium314.keyboard.latin.SuggestedWords.SuggestedWordInfo;
|
import helium314.keyboard.latin.SuggestedWords.SuggestedWordInfo;
|
||||||
import helium314.keyboard.latin.common.ComposedData;
|
import helium314.keyboard.latin.common.ComposedData;
|
||||||
|
import helium314.keyboard.latin.makedict.WordProperty;
|
||||||
import helium314.keyboard.latin.settings.SettingsValuesForSuggestion;
|
import helium314.keyboard.latin.settings.SettingsValuesForSuggestion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -177,6 +178,10 @@ public abstract class Dictionary {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WordProperty getWordProperty(final String word, final boolean isBeginningOfSentence) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Not a true dictionary. A placeholder used to indicate suggestions that don't come from any
|
* Not a true dictionary. A placeholder used to indicate suggestions that don't come from any
|
||||||
* real dictionary.
|
* real dictionary.
|
||||||
|
|
|
@ -381,6 +381,7 @@ class DictionaryFacilitatorImpl : DictionaryFacilitator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addToPersonalDictionaryIfInvalidButInHistory(word: String) {
|
private fun addToPersonalDictionaryIfInvalidButInHistory(word: String) {
|
||||||
|
if (word.length <= 1) return
|
||||||
val dictionaryGroup = clearlyPreferredDictionaryGroup ?: return
|
val dictionaryGroup = clearlyPreferredDictionaryGroup ?: return
|
||||||
val userDict = dictionaryGroup.getSubDict(Dictionary.TYPE_USER) ?: return
|
val userDict = dictionaryGroup.getSubDict(Dictionary.TYPE_USER) ?: return
|
||||||
val userHistoryDict = dictionaryGroup.getSubDict(Dictionary.TYPE_USER_HISTORY) ?: return
|
val userHistoryDict = dictionaryGroup.getSubDict(Dictionary.TYPE_USER_HISTORY) ?: return
|
||||||
|
@ -396,7 +397,10 @@ class DictionaryFacilitatorImpl : DictionaryFacilitator {
|
||||||
// This is not too bad, but it delays adding in case a user wants to fill a dictionary using this functionality
|
// This is not too bad, but it delays adding in case a user wants to fill a dictionary using this functionality
|
||||||
if (userHistoryDict.getFrequency(word) > 120) {
|
if (userHistoryDict.getFrequency(word) > 120) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
UserDictionary.Words.addWord(userDict.mContext, word, 250, null, dictionaryGroup.locale)
|
// adding can throw IllegalArgumentException: Unknown URL content://user_dictionary/words
|
||||||
|
// https://stackoverflow.com/q/41474623 https://github.com/AnySoftKeyboard/AnySoftKeyboard/issues/490
|
||||||
|
// apparently some devices don't have a dictionary? or it's just sporadic hiccups?
|
||||||
|
runCatching { UserDictionary.Words.addWord(userDict.mContext, word, 250, null, dictionaryGroup.locale) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -730,7 +734,7 @@ private class DictionaryGroup(
|
||||||
else {
|
else {
|
||||||
val file = File(context.filesDir.absolutePath + File.separator + "blacklists" + File.separator + locale.toLanguageTag() + ".txt")
|
val file = File(context.filesDir.absolutePath + File.separator + "blacklists" + File.separator + locale.toLanguageTag() + ".txt")
|
||||||
if (file.isDirectory) file.delete() // this apparently was an issue in some versions
|
if (file.isDirectory) file.delete() // this apparently was an issue in some versions
|
||||||
if (file.parentFile?.mkdirs() == true) file
|
if (file.parentFile?.exists() == true || file.parentFile?.mkdirs() == true) file
|
||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,10 +64,26 @@ object DictionaryFactory {
|
||||||
* if the dictionary type already exists in [dicts], the [file] is skipped
|
* if the dictionary type already exists in [dicts], the [file] is skipped
|
||||||
*/
|
*/
|
||||||
private fun checkAndAddDictionaryToListNewType(file: File, dicts: MutableList<Dictionary>, locale: Locale) {
|
private fun checkAndAddDictionaryToListNewType(file: File, dicts: MutableList<Dictionary>, locale: Locale) {
|
||||||
if (!file.isFile) return
|
val dictionary = getDictionary(file, locale) ?: return
|
||||||
val header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(file) ?: return killDictionary(file)
|
if (dicts.any { it.mDictType == dictionary.mDictType }) {
|
||||||
|
dictionary.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dicts.add(dictionary)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getDictionary(
|
||||||
|
file: File,
|
||||||
|
locale: Locale
|
||||||
|
): Dictionary? {
|
||||||
|
if (!file.isFile) return null
|
||||||
|
val header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(file)
|
||||||
|
if (header == null) {
|
||||||
|
killDictionary(file)
|
||||||
|
return null
|
||||||
|
}
|
||||||
val dictType = header.mIdString.split(":").first()
|
val dictType = header.mIdString.split(":").first()
|
||||||
if (dicts.any { it.mDictType == dictType }) return
|
|
||||||
val readOnlyBinaryDictionary = ReadOnlyBinaryDictionary(
|
val readOnlyBinaryDictionary = ReadOnlyBinaryDictionary(
|
||||||
file.absolutePath, 0, file.length(), false, locale, dictType
|
file.absolutePath, 0, file.length(), false, locale, dictType
|
||||||
)
|
)
|
||||||
|
@ -75,14 +91,13 @@ object DictionaryFactory {
|
||||||
if (readOnlyBinaryDictionary.isValidDictionary) {
|
if (readOnlyBinaryDictionary.isValidDictionary) {
|
||||||
if (locale.language == "ko") {
|
if (locale.language == "ko") {
|
||||||
// Use KoreanDictionary for Korean locale
|
// Use KoreanDictionary for Korean locale
|
||||||
dicts.add(KoreanDictionary(readOnlyBinaryDictionary))
|
return KoreanDictionary(readOnlyBinaryDictionary)
|
||||||
} else {
|
}
|
||||||
dicts.add(readOnlyBinaryDictionary)
|
return readOnlyBinaryDictionary
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
readOnlyBinaryDictionary.close()
|
readOnlyBinaryDictionary.close()
|
||||||
killDictionary(file)
|
killDictionary(file)
|
||||||
}
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun killDictionary(file: File) {
|
private fun killDictionary(file: File) {
|
||||||
|
|
|
@ -23,7 +23,7 @@ import java.util.List;
|
||||||
/**
|
/**
|
||||||
* A class for detecting Emoji-Alt physical key.
|
* A class for detecting Emoji-Alt physical key.
|
||||||
*/
|
*/
|
||||||
final class EmojiAltPhysicalKeyDetector {
|
public final class EmojiAltPhysicalKeyDetector {
|
||||||
private static final String TAG = "EmojiAltPhysKeyDetector";
|
private static final String TAG = "EmojiAltPhysKeyDetector";
|
||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ package helium314.keyboard.latin;
|
||||||
|
|
||||||
import helium314.keyboard.event.HangulCombiner;
|
import helium314.keyboard.event.HangulCombiner;
|
||||||
import helium314.keyboard.latin.common.ComposedData;
|
import helium314.keyboard.latin.common.ComposedData;
|
||||||
|
import helium314.keyboard.latin.makedict.WordProperty;
|
||||||
import helium314.keyboard.latin.settings.SettingsValuesForSuggestion;
|
import helium314.keyboard.latin.settings.SettingsValuesForSuggestion;
|
||||||
|
|
||||||
import java.text.Normalizer;
|
import java.text.Normalizer;
|
||||||
|
@ -72,6 +73,11 @@ public class KoreanDictionary extends Dictionary {
|
||||||
return mDictionary.getMaxFrequencyOfExactMatches(processInput(word));
|
return mDictionary.getMaxFrequencyOfExactMatches(processInput(word));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WordProperty getWordProperty(String word, boolean isBeginningOfSentence) {
|
||||||
|
return mDictionary.getWordProperty(processInput(word), isBeginningOfSentence);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean same(char[] word, int length, String typedWord) {
|
protected boolean same(char[] word, int length, String typedWord) {
|
||||||
word = processInput(new String(word)).toCharArray();
|
word = processInput(new String(word)).toCharArray();
|
||||||
|
|
|
@ -26,7 +26,6 @@ import android.os.Process;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.util.PrintWriterPrinter;
|
import android.util.PrintWriterPrinter;
|
||||||
import android.util.Printer;
|
import android.util.Printer;
|
||||||
import android.util.SparseArray;
|
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -51,9 +50,6 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode;
|
||||||
import helium314.keyboard.latin.common.InsetsOutlineProvider;
|
import helium314.keyboard.latin.common.InsetsOutlineProvider;
|
||||||
import helium314.keyboard.dictionarypack.DictionaryPackConstants;
|
import helium314.keyboard.dictionarypack.DictionaryPackConstants;
|
||||||
import helium314.keyboard.event.Event;
|
import helium314.keyboard.event.Event;
|
||||||
import helium314.keyboard.event.HangulEventDecoder;
|
|
||||||
import helium314.keyboard.event.HardwareEventDecoder;
|
|
||||||
import helium314.keyboard.event.HardwareKeyboardEventDecoder;
|
|
||||||
import helium314.keyboard.event.InputTransaction;
|
import helium314.keyboard.event.InputTransaction;
|
||||||
import helium314.keyboard.keyboard.Keyboard;
|
import helium314.keyboard.keyboard.Keyboard;
|
||||||
import helium314.keyboard.keyboard.KeyboardId;
|
import helium314.keyboard.keyboard.KeyboardId;
|
||||||
|
@ -68,7 +64,6 @@ import helium314.keyboard.latin.common.InputPointers;
|
||||||
import helium314.keyboard.latin.common.LocaleUtils;
|
import helium314.keyboard.latin.common.LocaleUtils;
|
||||||
import helium314.keyboard.latin.common.ViewOutlineProviderUtilsKt;
|
import helium314.keyboard.latin.common.ViewOutlineProviderUtilsKt;
|
||||||
import helium314.keyboard.latin.define.DebugFlags;
|
import helium314.keyboard.latin.define.DebugFlags;
|
||||||
import helium314.keyboard.latin.define.ProductionFlags;
|
|
||||||
import helium314.keyboard.latin.inputlogic.InputLogic;
|
import helium314.keyboard.latin.inputlogic.InputLogic;
|
||||||
import helium314.keyboard.latin.personalization.PersonalizationHelper;
|
import helium314.keyboard.latin.personalization.PersonalizationHelper;
|
||||||
import helium314.keyboard.latin.settings.Settings;
|
import helium314.keyboard.latin.settings.Settings;
|
||||||
|
@ -134,9 +129,6 @@ public class LatinIME extends InputMethodService implements
|
||||||
private final DictionaryFacilitator mDictionaryFacilitator =
|
private final DictionaryFacilitator mDictionaryFacilitator =
|
||||||
DictionaryFacilitatorProvider.getDictionaryFacilitator(false);
|
DictionaryFacilitatorProvider.getDictionaryFacilitator(false);
|
||||||
final InputLogic mInputLogic = new InputLogic(this, this, mDictionaryFacilitator);
|
final InputLogic mInputLogic = new InputLogic(this, this, mDictionaryFacilitator);
|
||||||
// We expect to have only one decoder in almost all cases, hence the default capacity of 1.
|
|
||||||
// If it turns out we need several, it will get grown seamlessly.
|
|
||||||
final SparseArray<HardwareEventDecoder> mHardwareEventDecoders = new SparseArray<>(1);
|
|
||||||
|
|
||||||
// TODO: Move these {@link View}s to {@link KeyboardSwitcher}.
|
// TODO: Move these {@link View}s to {@link KeyboardSwitcher}.
|
||||||
private View mInputView;
|
private View mInputView;
|
||||||
|
@ -146,7 +138,6 @@ public class LatinIME extends InputMethodService implements
|
||||||
private RichInputMethodManager mRichImm;
|
private RichInputMethodManager mRichImm;
|
||||||
final KeyboardSwitcher mKeyboardSwitcher;
|
final KeyboardSwitcher mKeyboardSwitcher;
|
||||||
private final SubtypeState mSubtypeState = new SubtypeState();
|
private final SubtypeState mSubtypeState = new SubtypeState();
|
||||||
private EmojiAltPhysicalKeyDetector mEmojiAltPhysicalKeyDetector;
|
|
||||||
private final StatsUtilsManager mStatsUtilsManager;
|
private final StatsUtilsManager mStatsUtilsManager;
|
||||||
// Working variable for {@link #startShowingInputView()} and
|
// Working variable for {@link #startShowingInputView()} and
|
||||||
// {@link #onEvaluateInputViewShown()}.
|
// {@link #onEvaluateInputViewShown()}.
|
||||||
|
@ -279,9 +270,7 @@ public class LatinIME extends InputMethodService implements
|
||||||
msg.arg2 /* remainingTries */, this /* handler */)) {
|
msg.arg2 /* remainingTries */, this /* handler */)) {
|
||||||
// If we were able to reset the caches, then we can reload the keyboard.
|
// If we were able to reset the caches, then we can reload the keyboard.
|
||||||
// Otherwise, we'll do it when we can.
|
// Otherwise, we'll do it when we can.
|
||||||
latinIme.mKeyboardSwitcher.loadKeyboard(latinIme.getCurrentInputEditorInfo(),
|
latinIme.mKeyboardSwitcher.reloadMainKeyboard();
|
||||||
settingsValues, latinIme.getCurrentAutoCapsState(),
|
|
||||||
latinIme.getCurrentRecapitalizeState());
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MSG_WAIT_FOR_DICTIONARY_LOAD:
|
case MSG_WAIT_FOR_DICTIONARY_LOAD:
|
||||||
|
@ -637,6 +626,7 @@ public class LatinIME extends InputMethodService implements
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
|
mSettings.startListener();
|
||||||
KeyboardIconsSet.Companion.getInstance().loadIcons(this);
|
KeyboardIconsSet.Companion.getInstance().loadIcons(this);
|
||||||
mRichImm = RichInputMethodManager.getInstance();
|
mRichImm = RichInputMethodManager.getInstance();
|
||||||
AudioAndHapticFeedbackManager.init(this);
|
AudioAndHapticFeedbackManager.init(this);
|
||||||
|
@ -664,7 +654,8 @@ public class LatinIME extends InputMethodService implements
|
||||||
|
|
||||||
final IntentFilter newDictFilter = new IntentFilter();
|
final IntentFilter newDictFilter = new IntentFilter();
|
||||||
newDictFilter.addAction(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION);
|
newDictFilter.addAction(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION);
|
||||||
ContextCompat.registerReceiver(this, mDictionaryPackInstallReceiver, newDictFilter, ContextCompat.RECEIVER_NOT_EXPORTED);
|
// RECEIVER_EXPORTED is necessary because apparently Android 15 (and others?) don't recognize if the sender and receiver are the same app, see https://github.com/Helium314/HeliBoard/pull/1756
|
||||||
|
ContextCompat.registerReceiver(this, mDictionaryPackInstallReceiver, newDictFilter, ContextCompat.RECEIVER_EXPORTED);
|
||||||
|
|
||||||
final IntentFilter dictDumpFilter = new IntentFilter();
|
final IntentFilter dictDumpFilter = new IntentFilter();
|
||||||
dictDumpFilter.addAction(DictionaryDumpBroadcastReceiver.DICTIONARY_DUMP_INTENT_ACTION);
|
dictDumpFilter.addAction(DictionaryDumpBroadcastReceiver.DICTIONARY_DUMP_INTENT_ACTION);
|
||||||
|
@ -1071,7 +1062,7 @@ public class LatinIME extends InputMethodService implements
|
||||||
if (isDifferentTextField) {
|
if (isDifferentTextField) {
|
||||||
mainKeyboardView.closing();
|
mainKeyboardView.closing();
|
||||||
suggest.setAutoCorrectionThreshold(currentSettingsValues.mAutoCorrectionThreshold);
|
suggest.setAutoCorrectionThreshold(currentSettingsValues.mAutoCorrectionThreshold);
|
||||||
switcher.loadKeyboard(editorInfo, currentSettingsValues, getCurrentAutoCapsState(), getCurrentRecapitalizeState());
|
switcher.reloadMainKeyboard();
|
||||||
if (needToCallLoadKeyboardLater) {
|
if (needToCallLoadKeyboardLater) {
|
||||||
// If we need to call loadKeyboard again later, we need to save its state now. The
|
// If we need to call loadKeyboard again later, we need to save its state now. The
|
||||||
// later call will be done in #retryResetCaches.
|
// later call will be done in #retryResetCaches.
|
||||||
|
@ -1119,6 +1110,7 @@ public class LatinIME extends InputMethodService implements
|
||||||
@Override
|
@Override
|
||||||
public void onWindowHidden() {
|
public void onWindowHidden() {
|
||||||
super.onWindowHidden();
|
super.onWindowHidden();
|
||||||
|
Log.i(TAG, "onWindowHidden");
|
||||||
final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
|
final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
|
||||||
if (mainKeyboardView != null) {
|
if (mainKeyboardView != null) {
|
||||||
mainKeyboardView.closing();
|
mainKeyboardView.closing();
|
||||||
|
@ -1175,8 +1167,12 @@ public class LatinIME extends InputMethodService implements
|
||||||
if (isInputViewShown()
|
if (isInputViewShown()
|
||||||
&& mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
|
&& mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
|
||||||
composingSpanStart, composingSpanEnd, settingsValues)) {
|
composingSpanStart, composingSpanEnd, settingsValues)) {
|
||||||
mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(),
|
// we don't want to update a manually set shift state if selection changed towards one side
|
||||||
getCurrentRecapitalizeState());
|
// because this may end the manual shift, which is unwanted in case of shift + arrow keys for changing selection
|
||||||
|
// todo: this is not fully implemented yet, and maybe should be behind a setting
|
||||||
|
if (mKeyboardSwitcher.getKeyboard() != null && mKeyboardSwitcher.getKeyboard().mId.isAlphabetShiftedManually()
|
||||||
|
&& !((oldSelEnd == newSelEnd && oldSelStart != newSelStart) || (oldSelEnd != newSelEnd && oldSelStart == newSelStart)))
|
||||||
|
mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(), getCurrentRecapitalizeState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1217,6 +1213,7 @@ public class LatinIME extends InputMethodService implements
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hideWindow() {
|
public void hideWindow() {
|
||||||
|
Log.i(TAG, "hideWindow");
|
||||||
if (hasSuggestionStripView() && mSettings.getCurrent().mToolbarMode == ToolbarMode.EXPANDABLE)
|
if (hasSuggestionStripView() && mSettings.getCurrent().mToolbarMode == ToolbarMode.EXPANDABLE)
|
||||||
mSuggestionStripView.setToolbarVisibility(false);
|
mSuggestionStripView.setToolbarVisibility(false);
|
||||||
mKeyboardSwitcher.onHideWindow();
|
mKeyboardSwitcher.onHideWindow();
|
||||||
|
@ -1229,6 +1226,12 @@ public class LatinIME extends InputMethodService implements
|
||||||
super.hideWindow();
|
super.hideWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestHideSelf(int flags) {
|
||||||
|
super.requestHideSelf(flags);
|
||||||
|
Log.i(TAG, "requestHideSelf: " + flags);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisplayCompletions(final CompletionInfo[] applicationSpecifiedCompletions) {
|
public void onDisplayCompletions(final CompletionInfo[] applicationSpecifiedCompletions) {
|
||||||
if (DebugFlags.DEBUG_ENABLED) {
|
if (DebugFlags.DEBUG_ENABLED) {
|
||||||
|
@ -1280,8 +1283,8 @@ public class LatinIME extends InputMethodService implements
|
||||||
if (isImeSuppressedByHardwareKeyboard() && !visibleKeyboardView.isShown()) {
|
if (isImeSuppressedByHardwareKeyboard() && !visibleKeyboardView.isShown()) {
|
||||||
// If there is a hardware keyboard and a visible software keyboard view has been hidden,
|
// If there is a hardware keyboard and a visible software keyboard view has been hidden,
|
||||||
// no visual element will be shown on the screen.
|
// no visual element will be shown on the screen.
|
||||||
outInsets.contentTopInsets = inputHeight;
|
// for some reason setting contentTopInsets and visibleTopInsets broke somewhere along the
|
||||||
outInsets.visibleTopInsets = inputHeight;
|
// way from OpenBoard to HeliBoard (GH-702, GH-1455), but not setting anything seems to work
|
||||||
mInsetsUpdater.setInsets(outInsets);
|
mInsetsUpdater.setInsets(outInsets);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1386,6 +1389,10 @@ public class LatinIME extends InputMethodService implements
|
||||||
@RequiresApi(api = Build.VERSION_CODES.R)
|
@RequiresApi(api = Build.VERSION_CODES.R)
|
||||||
public boolean onInlineSuggestionsResponse(InlineSuggestionsResponse response) {
|
public boolean onInlineSuggestionsResponse(InlineSuggestionsResponse response) {
|
||||||
Log.d(TAG,"onInlineSuggestionsResponse called");
|
Log.d(TAG,"onInlineSuggestionsResponse called");
|
||||||
|
if (Settings.getValues().mSuggestionStripHiddenPerUserSettings) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
final List<InlineSuggestion> inlineSuggestions = response.getInlineSuggestions();
|
final List<InlineSuggestion> inlineSuggestions = response.getInlineSuggestions();
|
||||||
if (inlineSuggestions.isEmpty()) {
|
if (inlineSuggestions.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1535,25 +1542,7 @@ public class LatinIME extends InputMethodService implements
|
||||||
// Implementation of {@link SuggestionStripView.Listener}.
|
// Implementation of {@link SuggestionStripView.Listener}.
|
||||||
@Override
|
@Override
|
||||||
public void onCodeInput(final int codePoint, final int x, final int y, final boolean isKeyRepeat) {
|
public void onCodeInput(final int codePoint, final int x, final int y, final boolean isKeyRepeat) {
|
||||||
onCodeInput(codePoint, 0, x, y, isKeyRepeat);
|
mKeyboardActionListener.onCodeInput(codePoint, x, y, isKeyRepeat);
|
||||||
}
|
|
||||||
|
|
||||||
public void onCodeInput(final int codePoint, final int metaState, final int x, final int y, final boolean isKeyRepeat) {
|
|
||||||
if (codePoint < 0) {
|
|
||||||
switch (codePoint) {
|
|
||||||
case KeyCode.TOGGLE_AUTOCORRECT -> {mSettings.toggleAutoCorrect(); return; }
|
|
||||||
case KeyCode.TOGGLE_INCOGNITO_MODE -> {mSettings.toggleAlwaysIncognitoMode(); return; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final Event event;
|
|
||||||
// checking if the character is a combining accent
|
|
||||||
if (0x300 <= codePoint && codePoint <= 0x35b) {
|
|
||||||
event = Event.createSoftwareDeadEvent(codePoint, 0, metaState, x, y, null);
|
|
||||||
} else {
|
|
||||||
event = createSoftwareKeypressEvent(codePoint, metaState, x, y, isKeyRepeat);
|
|
||||||
}
|
|
||||||
|
|
||||||
onEvent(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method is public for testability of LatinIME, but also in the future it should
|
// This method is public for testability of LatinIME, but also in the future it should
|
||||||
|
@ -1570,24 +1559,6 @@ public class LatinIME extends InputMethodService implements
|
||||||
mKeyboardSwitcher.onEvent(event, getCurrentAutoCapsState(), getCurrentRecapitalizeState());
|
mKeyboardSwitcher.onEvent(event, getCurrentAutoCapsState(), getCurrentRecapitalizeState());
|
||||||
}
|
}
|
||||||
|
|
||||||
// A helper method to split the code point and the key code. Ultimately, they should not be
|
|
||||||
// squashed into the same variable, and this method should be removed.
|
|
||||||
// public for testing, as we don't want to copy the same logic into test code
|
|
||||||
@NonNull
|
|
||||||
public static Event createSoftwareKeypressEvent(final int keyCodeOrCodePoint, final int metaState,
|
|
||||||
final int keyX, final int keyY, final boolean isKeyRepeat) {
|
|
||||||
final int keyCode;
|
|
||||||
final int codePoint;
|
|
||||||
if (keyCodeOrCodePoint <= 0) {
|
|
||||||
keyCode = keyCodeOrCodePoint;
|
|
||||||
codePoint = Event.NOT_A_CODE_POINT;
|
|
||||||
} else {
|
|
||||||
keyCode = Event.NOT_A_KEY_CODE;
|
|
||||||
codePoint = keyCodeOrCodePoint;
|
|
||||||
}
|
|
||||||
return Event.createSoftwareKeypressEvent(codePoint, keyCode, metaState, keyX, keyY, isKeyRepeat);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onTextInput(final String rawText) {
|
public void onTextInput(final String rawText) {
|
||||||
// TODO: have the keyboard pass the correct key code when we need it.
|
// TODO: have the keyboard pass the correct key code when we need it.
|
||||||
final Event event = Event.createSoftwareTextEvent(rawText, KeyCode.MULTIPLE_CODE_POINTS);
|
final Event event = Event.createSoftwareTextEvent(rawText, KeyCode.MULTIPLE_CODE_POINTS);
|
||||||
|
@ -1759,8 +1730,7 @@ public class LatinIME extends InputMethodService implements
|
||||||
loadSettings();
|
loadSettings();
|
||||||
if (mKeyboardSwitcher.getMainKeyboardView() != null) {
|
if (mKeyboardSwitcher.getMainKeyboardView() != null) {
|
||||||
// Reload keyboard because the current language has been changed.
|
// Reload keyboard because the current language has been changed.
|
||||||
mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettings.getCurrent(),
|
mKeyboardSwitcher.reloadMainKeyboard();
|
||||||
getCurrentAutoCapsState(), getCurrentRecapitalizeState());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1828,63 +1798,18 @@ public class LatinIME extends InputMethodService implements
|
||||||
feedbackManager.performAudioFeedback(code);
|
feedbackManager.performAudioFeedback(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HardwareEventDecoder getHardwareKeyEventDecoder(final int deviceId) {
|
|
||||||
final HardwareEventDecoder decoder = mHardwareEventDecoders.get(deviceId);
|
|
||||||
if (null != decoder) return decoder;
|
|
||||||
// TODO: create the decoder according to the specification
|
|
||||||
final HardwareEventDecoder newDecoder = new HardwareKeyboardEventDecoder(deviceId);
|
|
||||||
mHardwareEventDecoders.put(deviceId, newDecoder);
|
|
||||||
return newDecoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hooks for hardware keyboard
|
// Hooks for hardware keyboard
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) {
|
public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) {
|
||||||
if (mEmojiAltPhysicalKeyDetector == null) {
|
if (mKeyboardActionListener.onKeyDown(keyCode, keyEvent))
|
||||||
mEmojiAltPhysicalKeyDetector = new EmojiAltPhysicalKeyDetector(
|
|
||||||
getApplicationContext().getResources());
|
|
||||||
}
|
|
||||||
mEmojiAltPhysicalKeyDetector.onKeyDown(keyEvent);
|
|
||||||
if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) {
|
|
||||||
return super.onKeyDown(keyCode, keyEvent);
|
|
||||||
}
|
|
||||||
final Event event;
|
|
||||||
if (mRichImm.getCurrentSubtypeLocale().getLanguage().equals("ko")) {
|
|
||||||
final RichInputMethodSubtype subtype = mKeyboardSwitcher.getKeyboard() == null
|
|
||||||
? mRichImm.getCurrentSubtype()
|
|
||||||
: mKeyboardSwitcher.getKeyboard().mId.mSubtype;
|
|
||||||
event = HangulEventDecoder.decodeHardwareKeyEvent(subtype, keyEvent,
|
|
||||||
() -> getHardwareKeyEventDecoder(keyEvent.getDeviceId()).decodeHardwareKey(keyEvent));
|
|
||||||
} else {
|
|
||||||
event = getHardwareKeyEventDecoder(keyEvent.getDeviceId()).decodeHardwareKey(keyEvent);
|
|
||||||
}
|
|
||||||
// If the event is not handled by LatinIME, we just pass it to the parent implementation.
|
|
||||||
// If it's handled, we return true because we did handle it.
|
|
||||||
if (event.isHandled()) {
|
|
||||||
mInputLogic.onCodeInput(mSettings.getCurrent(), event,
|
|
||||||
mKeyboardSwitcher.getKeyboardShiftMode(),
|
|
||||||
// TODO: this is not necessarily correct for a hardware keyboard right now
|
|
||||||
mKeyboardSwitcher.getCurrentKeyboardScript(),
|
|
||||||
mHandler);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
return super.onKeyDown(keyCode, keyEvent);
|
return super.onKeyDown(keyCode, keyEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyUp(final int keyCode, final KeyEvent keyEvent) {
|
public boolean onKeyUp(final int keyCode, final KeyEvent keyEvent) {
|
||||||
if (mEmojiAltPhysicalKeyDetector == null) {
|
if (mKeyboardActionListener.onKeyUp(keyCode, keyEvent))
|
||||||
mEmojiAltPhysicalKeyDetector = new EmojiAltPhysicalKeyDetector(
|
|
||||||
getApplicationContext().getResources());
|
|
||||||
}
|
|
||||||
mEmojiAltPhysicalKeyDetector.onKeyUp(keyEvent);
|
|
||||||
if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) {
|
|
||||||
return super.onKeyUp(keyCode, keyEvent);
|
|
||||||
}
|
|
||||||
final long keyIdentifier = (long) keyEvent.getDeviceId() << 32 + keyEvent.getKeyCode();
|
|
||||||
if (mInputLogic.mCurrentlyPressedHardwareKeys.remove(keyIdentifier)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
return super.onKeyUp(keyCode, keyEvent);
|
return super.onKeyUp(keyCode, keyEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.android.inputmethod.latin.BinaryDictionary;
|
||||||
|
|
||||||
import helium314.keyboard.latin.SuggestedWords.SuggestedWordInfo;
|
import helium314.keyboard.latin.SuggestedWords.SuggestedWordInfo;
|
||||||
import helium314.keyboard.latin.common.ComposedData;
|
import helium314.keyboard.latin.common.ComposedData;
|
||||||
|
import helium314.keyboard.latin.makedict.WordProperty;
|
||||||
import helium314.keyboard.latin.settings.SettingsValuesForSuggestion;
|
import helium314.keyboard.latin.settings.SettingsValuesForSuggestion;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -107,6 +108,18 @@ public final class ReadOnlyBinaryDictionary extends Dictionary {
|
||||||
return NOT_A_PROBABILITY;
|
return NOT_A_PROBABILITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WordProperty getWordProperty(String word, boolean isBeginningOfSentence) {
|
||||||
|
if (mLock.readLock().tryLock()) {
|
||||||
|
try {
|
||||||
|
return mBinaryDictionary.getWordProperty(word, isBeginningOfSentence);
|
||||||
|
} finally {
|
||||||
|
mLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
mLock.writeLock().lock();
|
mLock.writeLock().lock();
|
||||||
|
|
|
@ -40,8 +40,6 @@ import helium314.keyboard.latin.settings.SpacingAndPunctuations;
|
||||||
import helium314.keyboard.latin.utils.CapsModeUtils;
|
import helium314.keyboard.latin.utils.CapsModeUtils;
|
||||||
import helium314.keyboard.latin.utils.DebugLogUtils;
|
import helium314.keyboard.latin.utils.DebugLogUtils;
|
||||||
import helium314.keyboard.latin.utils.NgramContextUtils;
|
import helium314.keyboard.latin.utils.NgramContextUtils;
|
||||||
import helium314.keyboard.latin.utils.ScriptUtils;
|
|
||||||
import helium314.keyboard.latin.utils.SpannableStringUtils;
|
|
||||||
import helium314.keyboard.latin.utils.StatsUtils;
|
import helium314.keyboard.latin.utils.StatsUtils;
|
||||||
import helium314.keyboard.latin.utils.TextRange;
|
import helium314.keyboard.latin.utils.TextRange;
|
||||||
|
|
||||||
|
@ -441,7 +439,7 @@ public final class RichInputConnection implements PrivateCommandPerformer {
|
||||||
// test for this explicitly)
|
// test for this explicitly)
|
||||||
if (INVALID_CURSOR_POSITION != mExpectedSelStart
|
if (INVALID_CURSOR_POSITION != mExpectedSelStart
|
||||||
&& (cachedLength >= n || cachedLength >= mExpectedSelStart)) {
|
&& (cachedLength >= n || cachedLength >= mExpectedSelStart)) {
|
||||||
final StringBuilder s = new StringBuilder(mCommittedTextBeforeComposingText);
|
final StringBuilder s = new StringBuilder(mCommittedTextBeforeComposingText.toString());
|
||||||
// We call #toString() here to create a temporary object.
|
// We call #toString() here to create a temporary object.
|
||||||
// In some situations, this method is called on a worker thread, and it's possible
|
// In some situations, this method is called on a worker thread, and it's possible
|
||||||
// the main thread touches the contents of mComposingText while this worker thread
|
// the main thread touches the contents of mComposingText while this worker thread
|
||||||
|
@ -718,8 +716,13 @@ public final class RichInputConnection implements PrivateCommandPerformer {
|
||||||
if (start < 0 || end < 0) {
|
if (start < 0 || end < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (start > end) {
|
||||||
|
mExpectedSelStart = end;
|
||||||
|
mExpectedSelEnd = start;
|
||||||
|
} else {
|
||||||
mExpectedSelStart = start;
|
mExpectedSelStart = start;
|
||||||
mExpectedSelEnd = end;
|
mExpectedSelEnd = end;
|
||||||
|
}
|
||||||
if (isConnected()) {
|
if (isConnected()) {
|
||||||
final boolean isIcValid = mIC.setSelection(start, end);
|
final boolean isIcValid = mIC.setSelection(start, end);
|
||||||
if (!isIcValid) {
|
if (!isIcValid) {
|
||||||
|
@ -825,15 +828,6 @@ public final class RichInputConnection implements PrivateCommandPerformer {
|
||||||
return NgramContextUtils.getNgramContextFromNthPreviousWord(prev, spacingAndPunctuations, n);
|
return NgramContextUtils.getNgramContextFromNthPreviousWord(prev, spacingAndPunctuations, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isPartOfCompositionForScript(final int codePoint,
|
|
||||||
final SpacingAndPunctuations spacingAndPunctuations, final String script) {
|
|
||||||
// We always consider word connectors part of compositions.
|
|
||||||
return spacingAndPunctuations.isWordConnector(codePoint)
|
|
||||||
// Otherwise, it's part of composition if it's part of script and not a separator.
|
|
||||||
|| (!spacingAndPunctuations.isWordSeparator(codePoint)
|
|
||||||
&& ScriptUtils.isLetterPartOfScript(codePoint, script));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the text surrounding the cursor.
|
* Returns the text surrounding the cursor.
|
||||||
*
|
*
|
||||||
|
@ -860,90 +854,7 @@ public final class RichInputConnection implements PrivateCommandPerformer {
|
||||||
if (before == null || after == null) {
|
if (before == null || after == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return StringUtilsKt.getTouchedWordRange(before, after, script, spacingAndPunctuations);
|
||||||
// Going backward, find the first breaking point (separator)
|
|
||||||
int startIndexInBefore = before.length();
|
|
||||||
int endIndexInAfter = -1;
|
|
||||||
while (startIndexInBefore > 0) {
|
|
||||||
final int codePoint = Character.codePointBefore(before, startIndexInBefore);
|
|
||||||
if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, script)) {
|
|
||||||
if (Character.isWhitespace(codePoint) || !spacingAndPunctuations.mCurrentLanguageHasSpaces)
|
|
||||||
break;
|
|
||||||
// continue to the next whitespace and see whether this contains a sometimesWordConnector
|
|
||||||
for (int i = startIndexInBefore - 1; i >= 0; i--) {
|
|
||||||
final char c = before.charAt(i);
|
|
||||||
if (spacingAndPunctuations.isSometimesWordConnector(c)) {
|
|
||||||
// if yes -> whitespace is the index
|
|
||||||
startIndexInBefore = Math.max(StringUtils.charIndexOfLastWhitespace(before), 0);
|
|
||||||
final int firstSpaceAfter = StringUtils.charIndexOfFirstWhitespace(after);
|
|
||||||
endIndexInAfter = firstSpaceAfter == -1 ? after.length() : firstSpaceAfter -1;
|
|
||||||
break;
|
|
||||||
} else if (Character.isWhitespace(c)) {
|
|
||||||
// if no, just break normally
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
--startIndexInBefore;
|
|
||||||
if (Character.isSupplementaryCodePoint(codePoint)) {
|
|
||||||
--startIndexInBefore;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find last word separator after the cursor
|
|
||||||
if (endIndexInAfter == -1) {
|
|
||||||
while (++endIndexInAfter < after.length()) {
|
|
||||||
final int codePoint = Character.codePointAt(after, endIndexInAfter);
|
|
||||||
if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, script)) {
|
|
||||||
if (Character.isWhitespace(codePoint) || !spacingAndPunctuations.mCurrentLanguageHasSpaces)
|
|
||||||
break;
|
|
||||||
// continue to the next whitespace and see whether this contains a sometimesWordConnector
|
|
||||||
for (int i = endIndexInAfter; i < after.length(); i++) {
|
|
||||||
final char c = after.charAt(i);
|
|
||||||
if (spacingAndPunctuations.isSometimesWordConnector(c)) {
|
|
||||||
// if yes -> whitespace is next to the index
|
|
||||||
startIndexInBefore = Math.max(StringUtils.charIndexOfLastWhitespace(before), 0);
|
|
||||||
final int firstSpaceAfter = StringUtils.charIndexOfFirstWhitespace(after);
|
|
||||||
endIndexInAfter = firstSpaceAfter == -1 ? after.length() : firstSpaceAfter - 1;
|
|
||||||
break;
|
|
||||||
} else if (Character.isWhitespace(c)) {
|
|
||||||
// if no, just break normally
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (Character.isSupplementaryCodePoint(codePoint)) {
|
|
||||||
++endIndexInAfter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// strip stuff before "//" (i.e. ignore http and other protocols)
|
|
||||||
final String beforeConsideringStart = before.subSequence(startIndexInBefore, before.length()).toString();
|
|
||||||
final int protocolEnd = beforeConsideringStart.lastIndexOf("//");
|
|
||||||
if (protocolEnd != -1)
|
|
||||||
startIndexInBefore += protocolEnd + 1;
|
|
||||||
|
|
||||||
// we don't want the end characters to be word separators
|
|
||||||
while (endIndexInAfter > 0 && spacingAndPunctuations.isWordSeparator(after.charAt(endIndexInAfter - 1))) {
|
|
||||||
--endIndexInAfter;
|
|
||||||
}
|
|
||||||
while (startIndexInBefore < before.length() && spacingAndPunctuations.isWordSeparator(before.charAt(startIndexInBefore))) {
|
|
||||||
++startIndexInBefore;
|
|
||||||
}
|
|
||||||
|
|
||||||
final boolean hasUrlSpans =
|
|
||||||
SpannableStringUtils.hasUrlSpans(before, startIndexInBefore, before.length())
|
|
||||||
|| SpannableStringUtils.hasUrlSpans(after, 0, endIndexInAfter);
|
|
||||||
// We don't use TextUtils#concat because it copies all spans without respect to their
|
|
||||||
// nature. If the text includes a PARAGRAPH span and it has been split, then
|
|
||||||
// TextUtils#concat will crash when it tries to concat both sides of it.
|
|
||||||
return new TextRange(
|
|
||||||
SpannableStringUtils.concatWithNonParagraphSuggestionSpansOnly(before, after),
|
|
||||||
startIndexInBefore, before.length() + endIndexInAfter, before.length(),
|
|
||||||
hasUrlSpans);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCursorTouchingWord(final SpacingAndPunctuations spacingAndPunctuations,
|
public boolean isCursorTouchingWord(final SpacingAndPunctuations spacingAndPunctuations,
|
||||||
|
@ -956,19 +867,7 @@ public final class RichInputConnection implements PrivateCommandPerformer {
|
||||||
// a composing region should always count as a word
|
// a composing region should always count as a word
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
final String textBeforeCursor = mCommittedTextBeforeComposingText.toString();
|
return StringUtilsKt.endsWithWordCodepoint(mCommittedTextBeforeComposingText.toString(), spacingAndPunctuations);
|
||||||
int indexOfCodePointInJavaChars = textBeforeCursor.length();
|
|
||||||
int consideredCodePoint = 0 == indexOfCodePointInJavaChars ? Constants.NOT_A_CODE
|
|
||||||
: textBeforeCursor.codePointBefore(indexOfCodePointInJavaChars);
|
|
||||||
// Search for the first non word-connector char
|
|
||||||
if (spacingAndPunctuations.isWordConnector(consideredCodePoint)) {
|
|
||||||
indexOfCodePointInJavaChars -= Character.charCount(consideredCodePoint);
|
|
||||||
consideredCodePoint = 0 == indexOfCodePointInJavaChars ? Constants.NOT_A_CODE
|
|
||||||
: textBeforeCursor.codePointBefore(indexOfCodePointInJavaChars);
|
|
||||||
}
|
|
||||||
return !(Constants.NOT_A_CODE == consideredCodePoint
|
|
||||||
|| spacingAndPunctuations.isWordSeparator(consideredCodePoint)
|
|
||||||
|| spacingAndPunctuations.isWordConnector(consideredCodePoint));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCursorFollowedByWordCharacter(
|
public boolean isCursorFollowedByWordCharacter(
|
||||||
|
|
|
@ -7,6 +7,7 @@ import helium314.keyboard.keyboard.Keyboard
|
||||||
import helium314.keyboard.keyboard.KeyboardSwitcher
|
import helium314.keyboard.keyboard.KeyboardSwitcher
|
||||||
import helium314.keyboard.latin.DictionaryFacilitator.DictionaryInitializationListener
|
import helium314.keyboard.latin.DictionaryFacilitator.DictionaryInitializationListener
|
||||||
import helium314.keyboard.latin.common.ComposedData
|
import helium314.keyboard.latin.common.ComposedData
|
||||||
|
import helium314.keyboard.latin.makedict.WordProperty
|
||||||
import helium314.keyboard.latin.settings.SettingsValuesForSuggestion
|
import helium314.keyboard.latin.settings.SettingsValuesForSuggestion
|
||||||
import helium314.keyboard.latin.utils.SuggestionResults
|
import helium314.keyboard.latin.utils.SuggestionResults
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
@ -47,6 +48,8 @@ class SingleDictionaryFacilitator(private val dict: Dictionary) : DictionaryFaci
|
||||||
return suggestionResults
|
return suggestionResults
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getWordProperty(word: String): WordProperty? = dict.getWordProperty(word, false)
|
||||||
|
|
||||||
// ------------ dummy functionality ----------------
|
// ------------ dummy functionality ----------------
|
||||||
|
|
||||||
override fun setValidSpellingWordReadCache(cache: LruCache<String, Boolean>) {}
|
override fun setValidSpellingWordReadCache(cache: LruCache<String, Boolean>) {}
|
||||||
|
|
|
@ -12,3 +12,5 @@ object Links {
|
||||||
const val CUSTOM_LAYOUTS = "$GITHUB/discussions/categories/custom-layout"
|
const val CUSTOM_LAYOUTS = "$GITHUB/discussions/categories/custom-layout"
|
||||||
const val CUSTOM_COLORS = "$GITHUB/discussions/categories/custom-colors"
|
const val CUSTOM_COLORS = "$GITHUB/discussions/categories/custom-colors"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val combiningRange = 0x300..0x35b
|
||||||
|
|
|
@ -6,13 +6,18 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode
|
||||||
import helium314.keyboard.latin.common.StringUtils.mightBeEmoji
|
import helium314.keyboard.latin.common.StringUtils.mightBeEmoji
|
||||||
import helium314.keyboard.latin.common.StringUtils.newSingleCodePointString
|
import helium314.keyboard.latin.common.StringUtils.newSingleCodePointString
|
||||||
import helium314.keyboard.latin.settings.SpacingAndPunctuations
|
import helium314.keyboard.latin.settings.SpacingAndPunctuations
|
||||||
|
import helium314.keyboard.latin.utils.ScriptUtils
|
||||||
import helium314.keyboard.latin.utils.SpacedTokens
|
import helium314.keyboard.latin.utils.SpacedTokens
|
||||||
|
import helium314.keyboard.latin.utils.SpannableStringUtils
|
||||||
|
import helium314.keyboard.latin.utils.TextRange
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
fun CharSequence.codePointAt(offset: Int) = Character.codePointAt(this, offset)
|
fun CharSequence.codePointAt(offset: Int) = Character.codePointAt(this, offset)
|
||||||
fun CharSequence.codePointBefore(offset: Int) = Character.codePointBefore(this, offset)
|
fun CharSequence.codePointBefore(offset: Int) = Character.codePointBefore(this, offset)
|
||||||
|
|
||||||
|
/** Loops over the codepoints in [text]. Exits when [loop] returns true */
|
||||||
inline fun loopOverCodePoints(text: CharSequence, loop: (cp: Int, charCount: Int) -> Boolean) {
|
inline fun loopOverCodePoints(text: CharSequence, loop: (cp: Int, charCount: Int) -> Boolean) {
|
||||||
var offset = 0
|
var offset = 0
|
||||||
while (offset < text.length) {
|
while (offset < text.length) {
|
||||||
|
@ -23,6 +28,7 @@ inline fun loopOverCodePoints(text: CharSequence, loop: (cp: Int, charCount: Int
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Loops backwards over the codepoints in [text]. Exits when [loop] returns true */
|
||||||
inline fun loopOverCodePointsBackwards(text: CharSequence, loop: (cp: Int, charCount: Int) -> Boolean) {
|
inline fun loopOverCodePointsBackwards(text: CharSequence, loop: (cp: Int, charCount: Int) -> Boolean) {
|
||||||
var offset = text.length
|
var offset = text.length
|
||||||
while (offset > 0) {
|
while (offset > 0) {
|
||||||
|
@ -88,6 +94,111 @@ fun getFullEmojiAtEnd(text: CharSequence): String {
|
||||||
return s.substring(offset)
|
return s.substring(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the [text] does not end with word separator, ignoring all word connectors.
|
||||||
|
* If the [text] is empty (after ignoring word connectors), the method returns false.
|
||||||
|
*/
|
||||||
|
// todo: this returns true on numbers, why isn't Character.isLetter(code) used?
|
||||||
|
fun endsWithWordCodepoint(text: String, spacingAndPunctuations: SpacingAndPunctuations): Boolean {
|
||||||
|
if (text.isEmpty()) return false
|
||||||
|
var codePoint = 0 // initial value irrelevant since length is always > 0
|
||||||
|
loopOverCodePointsBackwards(text) { cp, _ ->
|
||||||
|
codePoint = cp
|
||||||
|
!spacingAndPunctuations.isWordConnector(cp)
|
||||||
|
}
|
||||||
|
// codePoint might still be a wordConnector (if text consists of wordConnectors)
|
||||||
|
return !spacingAndPunctuations.isWordConnector(codePoint) && !spacingAndPunctuations.isWordSeparator(codePoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: simplify... maybe compare with original code?
|
||||||
|
fun getTouchedWordRange(before: CharSequence, after: CharSequence, script: String, spacingAndPunctuations: SpacingAndPunctuations): TextRange {
|
||||||
|
// Going backward, find the first breaking point (separator)
|
||||||
|
var startIndexInBefore = before.length
|
||||||
|
var endIndexInAfter = -1 // todo: clarify why might we want to set it when checking before
|
||||||
|
loopOverCodePointsBackwards(before) { codePoint, cpLength ->
|
||||||
|
if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, script)) {
|
||||||
|
if (Character.isWhitespace(codePoint) || !spacingAndPunctuations.mCurrentLanguageHasSpaces)
|
||||||
|
return@loopOverCodePointsBackwards true
|
||||||
|
// continue to the next whitespace and see whether this contains a sometimesWordConnector
|
||||||
|
for (i in startIndexInBefore - 1 downTo 0) {
|
||||||
|
val c = before[i]
|
||||||
|
if (spacingAndPunctuations.isSometimesWordConnector(c.code)) {
|
||||||
|
// if yes -> whitespace is the index
|
||||||
|
startIndexInBefore = max(StringUtils.charIndexOfLastWhitespace(before).toDouble(), 0.0).toInt()
|
||||||
|
val firstSpaceAfter = StringUtils.charIndexOfFirstWhitespace(after)
|
||||||
|
endIndexInAfter = if (firstSpaceAfter == -1) after.length else firstSpaceAfter - 1
|
||||||
|
return@loopOverCodePointsBackwards true
|
||||||
|
} else if (Character.isWhitespace(c)) {
|
||||||
|
// if no, just break normally
|
||||||
|
return@loopOverCodePointsBackwards true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return@loopOverCodePointsBackwards true
|
||||||
|
}
|
||||||
|
startIndexInBefore -= cpLength
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find last word separator after the cursor
|
||||||
|
if (endIndexInAfter == -1) {
|
||||||
|
endIndexInAfter = 0
|
||||||
|
loopOverCodePoints(after) { codePoint, cpLength ->
|
||||||
|
if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, script)) {
|
||||||
|
if (Character.isWhitespace(codePoint) || !spacingAndPunctuations.mCurrentLanguageHasSpaces)
|
||||||
|
return@loopOverCodePoints true
|
||||||
|
// continue to the next whitespace and see whether this contains a sometimesWordConnector
|
||||||
|
for (i in endIndexInAfter..<after.length) {
|
||||||
|
val c = after[i]
|
||||||
|
if (spacingAndPunctuations.isSometimesWordConnector(c.code)) {
|
||||||
|
// if yes -> whitespace is next to the index
|
||||||
|
startIndexInBefore = max(StringUtils.charIndexOfLastWhitespace(before), 0)
|
||||||
|
val firstSpaceAfter = StringUtils.charIndexOfFirstWhitespace(after)
|
||||||
|
endIndexInAfter = if (firstSpaceAfter == -1) after.length else firstSpaceAfter - 1
|
||||||
|
return@loopOverCodePoints true
|
||||||
|
} else if (Character.isWhitespace(c)) {
|
||||||
|
// if no, just break normally
|
||||||
|
return@loopOverCodePoints true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return@loopOverCodePoints true
|
||||||
|
}
|
||||||
|
endIndexInAfter += cpLength
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// strip text before "//" (i.e. ignore http and other protocols)
|
||||||
|
val beforeConsideringStart = before.substring(startIndexInBefore, before.length)
|
||||||
|
val protocolEnd = beforeConsideringStart.lastIndexOf("//")
|
||||||
|
if (protocolEnd != -1) startIndexInBefore += protocolEnd + 1
|
||||||
|
|
||||||
|
// we don't want the end characters to be word separators
|
||||||
|
while (endIndexInAfter > 0 && spacingAndPunctuations.isWordSeparator(after[endIndexInAfter - 1].code)) {
|
||||||
|
--endIndexInAfter
|
||||||
|
}
|
||||||
|
while (startIndexInBefore < before.length && spacingAndPunctuations.isWordSeparator(before[startIndexInBefore].code)) {
|
||||||
|
++startIndexInBefore
|
||||||
|
}
|
||||||
|
|
||||||
|
val hasUrlSpans = SpannableStringUtils.hasUrlSpans(before, startIndexInBefore, before.length)
|
||||||
|
|| SpannableStringUtils.hasUrlSpans(after, 0, endIndexInAfter)
|
||||||
|
|
||||||
|
// We don't use TextUtils#concat because it copies all spans without respect to their
|
||||||
|
// nature. If the text includes a PARAGRAPH span and it has been split, then
|
||||||
|
// TextUtils#concat will crash when it tries to concat both sides of it.
|
||||||
|
return TextRange(
|
||||||
|
SpannableStringUtils.concatWithNonParagraphSuggestionSpansOnly(before, after),
|
||||||
|
startIndexInBefore, before.length + endIndexInAfter, before.length,
|
||||||
|
hasUrlSpans
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// actually this should not be in STRING Utils, but only used for getTouchedWordRange
|
||||||
|
private fun isPartOfCompositionForScript(codePoint: Int, spacingAndPunctuations: SpacingAndPunctuations, script: String) =
|
||||||
|
spacingAndPunctuations.isWordConnector(codePoint) // We always consider word connectors part of compositions.
|
||||||
|
// Otherwise, it's part of composition if it's part of script and not a separator.
|
||||||
|
|| (!spacingAndPunctuations.isWordSeparator(codePoint) && ScriptUtils.isLetterPartOfScript(codePoint, script))
|
||||||
|
|
||||||
/** split the string on the first of consecutive space only, further consecutive spaces are added to the next split */
|
/** split the string on the first of consecutive space only, further consecutive spaces are added to the next split */
|
||||||
fun String.splitOnFirstSpacesOnly(): List<String> {
|
fun String.splitOnFirstSpacesOnly(): List<String> {
|
||||||
val out = mutableListOf<String>()
|
val out = mutableListOf<String>()
|
||||||
|
|
|
@ -7,11 +7,7 @@
|
||||||
package helium314.keyboard.latin.define
|
package helium314.keyboard.latin.define
|
||||||
|
|
||||||
object ProductionFlags {
|
object ProductionFlags {
|
||||||
const val IS_HARDWARE_KEYBOARD_SUPPORTED = false
|
const val IS_HARDWARE_KEYBOARD_SUPPORTED = true
|
||||||
// todo: make it work
|
|
||||||
// was set to true in hangul branch (and there is the hangul hardware event decoder in latinIme)
|
|
||||||
// but disabled again because this breaks ctrl+c / ctrl+v, and most likely other things
|
|
||||||
// so it looks like the HardwareKeyboardEventDecoder needs some work before it's ready
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Include all suggestions from all dictionaries in
|
* Include all suggestions from all dictionaries in
|
||||||
|
|
|
@ -49,6 +49,7 @@ import helium314.keyboard.latin.settings.SpacingAndPunctuations;
|
||||||
import helium314.keyboard.latin.suggestions.SuggestionStripViewAccessor;
|
import helium314.keyboard.latin.suggestions.SuggestionStripViewAccessor;
|
||||||
import helium314.keyboard.latin.utils.AsyncResultHolder;
|
import helium314.keyboard.latin.utils.AsyncResultHolder;
|
||||||
import helium314.keyboard.latin.utils.InputTypeUtils;
|
import helium314.keyboard.latin.utils.InputTypeUtils;
|
||||||
|
import helium314.keyboard.latin.utils.IntentUtils;
|
||||||
import helium314.keyboard.latin.utils.Log;
|
import helium314.keyboard.latin.utils.Log;
|
||||||
import helium314.keyboard.latin.utils.RecapitalizeStatus;
|
import helium314.keyboard.latin.utils.RecapitalizeStatus;
|
||||||
import helium314.keyboard.latin.utils.ScriptUtils;
|
import helium314.keyboard.latin.utils.ScriptUtils;
|
||||||
|
@ -89,6 +90,7 @@ public final class InputLogic {
|
||||||
|
|
||||||
private int mDeleteCount;
|
private int mDeleteCount;
|
||||||
private long mLastKeyTime;
|
private long mLastKeyTime;
|
||||||
|
// todo: this is not used, so either remove it or do something with it
|
||||||
public final TreeSet<Long> mCurrentlyPressedHardwareKeys = new TreeSet<>();
|
public final TreeSet<Long> mCurrentlyPressedHardwareKeys = new TreeSet<>();
|
||||||
|
|
||||||
// Keeps track of most recently inserted text (multi-character key) for reverting
|
// Keeps track of most recently inserted text (multi-character key) for reverting
|
||||||
|
@ -399,7 +401,11 @@ public final class InputLogic {
|
||||||
// Stop the last recapitalization, if started.
|
// Stop the last recapitalization, if started.
|
||||||
mRecapitalizeStatus.stop();
|
mRecapitalizeStatus.stop();
|
||||||
mWordBeingCorrectedByCursor = null;
|
mWordBeingCorrectedByCursor = null;
|
||||||
return true;
|
|
||||||
|
// we do not return true if
|
||||||
|
final boolean oneSidedSelectionMove = hasOrHadSelection
|
||||||
|
&& ((oldSelEnd == newSelEnd && oldSelStart != newSelStart) || (oldSelEnd != newSelEnd && oldSelStart == newSelStart));
|
||||||
|
return !oneSidedSelectionMove;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean moveCursorByAndReturnIfInsideComposingWord(int distance) {
|
public boolean moveCursorByAndReturnIfInsideComposingWord(int distance) {
|
||||||
|
@ -643,7 +649,8 @@ public final class InputLogic {
|
||||||
*/
|
*/
|
||||||
private void handleFunctionalEvent(final Event event, final InputTransaction inputTransaction,
|
private void handleFunctionalEvent(final Event event, final InputTransaction inputTransaction,
|
||||||
final String currentKeyboardScript, final LatinIME.UIHandler handler) {
|
final String currentKeyboardScript, final LatinIME.UIHandler handler) {
|
||||||
switch (event.getMKeyCode()) {
|
final int keyCode = event.getMKeyCode();
|
||||||
|
switch (keyCode) {
|
||||||
case KeyCode.DELETE:
|
case KeyCode.DELETE:
|
||||||
handleBackspaceEvent(event, inputTransaction, currentKeyboardScript);
|
handleBackspaceEvent(event, inputTransaction, currentKeyboardScript);
|
||||||
// Backspace is a functional key, but it affects the contents of the editor.
|
// Backspace is a functional key, but it affects the contents of the editor.
|
||||||
|
@ -686,7 +693,7 @@ public final class InputLogic {
|
||||||
case KeyCode.SHIFT_ENTER:
|
case KeyCode.SHIFT_ENTER:
|
||||||
// todo: try using sendDownUpKeyEventWithMetaState() and remove the key code maybe
|
// todo: try using sendDownUpKeyEventWithMetaState() and remove the key code maybe
|
||||||
final Event tmpEvent = Event.createSoftwareKeypressEvent(Constants.CODE_ENTER,
|
final Event tmpEvent = Event.createSoftwareKeypressEvent(Constants.CODE_ENTER,
|
||||||
event.getMKeyCode(), 0, event.getMX(), event.getMY(), event.isKeyRepeat());
|
keyCode, 0, event.getMX(), event.getMY(), event.isKeyRepeat());
|
||||||
handleNonSpecialCharacterEvent(tmpEvent, inputTransaction, handler);
|
handleNonSpecialCharacterEvent(tmpEvent, inputTransaction, handler);
|
||||||
// Shift + Enter is treated as a functional key but it results in adding a new
|
// Shift + Enter is treated as a functional key but it results in adding a new
|
||||||
// line, so that does affect the contents of the editor.
|
// line, so that does affect the contents of the editor.
|
||||||
|
@ -717,37 +724,43 @@ public final class InputLogic {
|
||||||
if (mConnection.hasSelection()) {
|
if (mConnection.hasSelection()) {
|
||||||
mConnection.copyText(true);
|
mConnection.copyText(true);
|
||||||
// fake delete keypress to remove the text
|
// fake delete keypress to remove the text
|
||||||
final Event backspaceEvent = LatinIME.createSoftwareKeypressEvent(KeyCode.DELETE, 0,
|
final Event backspaceEvent = Event.createSoftwareKeypressEvent(KeyCode.DELETE, 0,
|
||||||
event.getMX(), event.getMY(), event.isKeyRepeat());
|
event.getMX(), event.getMY(), event.isKeyRepeat());
|
||||||
handleBackspaceEvent(backspaceEvent, inputTransaction, currentKeyboardScript);
|
handleBackspaceEvent(backspaceEvent, inputTransaction, currentKeyboardScript);
|
||||||
inputTransaction.setDidAffectContents();
|
inputTransaction.setDidAffectContents();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case KeyCode.WORD_LEFT:
|
case KeyCode.WORD_LEFT:
|
||||||
sendDownUpKeyEventWithMetaState(ScriptUtils.isScriptRtl(currentKeyboardScript)?
|
sendDownUpKeyEventWithMetaState(
|
||||||
KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.META_CTRL_ON);
|
ScriptUtils.isScriptRtl(currentKeyboardScript) ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT,
|
||||||
|
KeyEvent.META_CTRL_ON | event.getMMetaState());
|
||||||
break;
|
break;
|
||||||
case KeyCode.WORD_RIGHT:
|
case KeyCode.WORD_RIGHT:
|
||||||
sendDownUpKeyEventWithMetaState(ScriptUtils.isScriptRtl(currentKeyboardScript)?
|
sendDownUpKeyEventWithMetaState(
|
||||||
KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.META_CTRL_ON);
|
ScriptUtils.isScriptRtl(currentKeyboardScript) ? KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||||
|
KeyEvent.META_CTRL_ON | event.getMMetaState());
|
||||||
break;
|
break;
|
||||||
case KeyCode.MOVE_START_OF_PAGE:
|
case KeyCode.MOVE_START_OF_PAGE:
|
||||||
final int selectionEnd = mConnection.getExpectedSelectionEnd();
|
final int selectionEnd1 = mConnection.getExpectedSelectionEnd();
|
||||||
sendDownUpKeyEventWithMetaState(KeyEvent.KEYCODE_MOVE_HOME, KeyEvent.META_CTRL_ON);
|
final int selectionStart1 = mConnection.getExpectedSelectionStart();
|
||||||
if (mConnection.getExpectedSelectionStart() > 0 && mConnection.getExpectedSelectionEnd() == selectionEnd) {
|
sendDownUpKeyEventWithMetaState(KeyEvent.KEYCODE_MOVE_HOME, KeyEvent.META_CTRL_ON | event.getMMetaState());
|
||||||
// unchanged, and we're not at the top -> try a different method (necessary for compose fields)
|
if (mConnection.getExpectedSelectionStart() == selectionStart1 && mConnection.getExpectedSelectionEnd() == selectionEnd1) {
|
||||||
mConnection.setSelection(0, 0);
|
// unchanged -> try a different method (necessary for compose fields)
|
||||||
|
final int newEnd = (event.getMMetaState() & KeyEvent.META_SHIFT_MASK) != 0 ? selectionEnd1 : 0;
|
||||||
|
mConnection.setSelection(0, newEnd);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case KeyCode.MOVE_END_OF_PAGE:
|
case KeyCode.MOVE_END_OF_PAGE:
|
||||||
final int selectionStart = mConnection.getExpectedSelectionEnd();
|
final int selectionStart2 = mConnection.getExpectedSelectionStart();
|
||||||
sendDownUpKeyEventWithMetaState(KeyEvent.KEYCODE_MOVE_END, KeyEvent.META_CTRL_ON);
|
final int selectionEnd2 = mConnection.getExpectedSelectionEnd();
|
||||||
if (mConnection.getExpectedSelectionStart() == selectionStart) {
|
sendDownUpKeyEventWithMetaState(KeyEvent.KEYCODE_MOVE_END, KeyEvent.META_CTRL_ON | event.getMMetaState());
|
||||||
|
if (mConnection.getExpectedSelectionStart() == selectionStart2 && mConnection.getExpectedSelectionEnd() == selectionEnd2) {
|
||||||
// unchanged, try fallback e.g. for compose fields that don't care about ctrl + end
|
// unchanged, try fallback e.g. for compose fields that don't care about ctrl + end
|
||||||
// we just move to a very large index, and hope the field is prepared to deal with this
|
// we just move to a very large index, and hope the field is prepared to deal with this
|
||||||
// getting the actual length of the text for setting the correct position can be tricky for some apps...
|
// getting the actual length of the text for setting the correct position can be tricky for some apps...
|
||||||
try {
|
try {
|
||||||
mConnection.setSelection(Integer.MAX_VALUE, Integer.MAX_VALUE);
|
final int newStart = (event.getMMetaState() & KeyEvent.META_SHIFT_MASK) != 0 ? selectionStart2 : Integer.MAX_VALUE;
|
||||||
|
mConnection.setSelection(newStart, Integer.MAX_VALUE);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// better catch potential errors and just do nothing in this case
|
// better catch potential errors and just do nothing in this case
|
||||||
Log.i(TAG, "error when trying to move cursor to last position: " + e);
|
Log.i(TAG, "error when trying to move cursor to last position: " + e);
|
||||||
|
@ -766,6 +779,11 @@ public final class InputLogic {
|
||||||
case KeyCode.TIMESTAMP:
|
case KeyCode.TIMESTAMP:
|
||||||
mLatinIME.onTextInput(TimestampKt.getTimestamp(mLatinIME));
|
mLatinIME.onTextInput(TimestampKt.getTimestamp(mLatinIME));
|
||||||
break;
|
break;
|
||||||
|
case KeyCode.SEND_INTENT_ONE, KeyCode.SEND_INTENT_TWO, KeyCode.SEND_INTENT_THREE:
|
||||||
|
IntentUtils.handleSendIntentKey(mLatinIME, event.getMKeyCode());
|
||||||
|
case KeyCode.IME_HIDE_UI:
|
||||||
|
mLatinIME.requestHideSelf(0);
|
||||||
|
break;
|
||||||
case KeyCode.VOICE_INPUT:
|
case KeyCode.VOICE_INPUT:
|
||||||
// switching to shortcut IME, shift state, keyboard,... is handled by LatinIME,
|
// switching to shortcut IME, shift state, keyboard,... is handled by LatinIME,
|
||||||
// {@link KeyboardSwitcher#onEvent(Event)}, or {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
|
// {@link KeyboardSwitcher#onEvent(Event)}, or {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
|
||||||
|
@ -774,23 +792,20 @@ public final class InputLogic {
|
||||||
case KeyCode.CAPS_LOCK, KeyCode.EMOJI, KeyCode.TOGGLE_ONE_HANDED_MODE, KeyCode.SWITCH_ONE_HANDED_MODE:
|
case KeyCode.CAPS_LOCK, KeyCode.EMOJI, KeyCode.TOGGLE_ONE_HANDED_MODE, KeyCode.SWITCH_ONE_HANDED_MODE:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (KeyCode.INSTANCE.isModifier(event.getMKeyCode()))
|
if (KeyCode.INSTANCE.isModifier(keyCode))
|
||||||
return; // continuation of previous switch case, but modifiers are in a separate place
|
return; // continuation of previous switch case above, but modifiers are held in a separate place
|
||||||
if (event.getMMetaState() != 0) {
|
final int keyEventCode = keyCode > 0
|
||||||
// need to convert codepoint to KeyEvent.KEYCODE_<xxx>
|
? keyCode
|
||||||
final int codeToConvert = event.getMKeyCode() < 0 ? event.getMKeyCode() : event.getMCodePoint();
|
: event.getMCodePoint() >= 0 ? KeyCode.codePointToKeyEventCode(event.getMCodePoint())
|
||||||
int keyEventCode = KeyCode.INSTANCE.toKeyEventCode(codeToConvert);
|
: KeyCode.keyCodeToKeyEventCode(keyCode);
|
||||||
if (keyEventCode != KeyEvent.KEYCODE_UNKNOWN)
|
|
||||||
sendDownUpKeyEventWithMetaState(keyEventCode, event.getMMetaState());
|
|
||||||
return; // never crash if user inputs sth we don't have a KeyEvent.KEYCODE for
|
|
||||||
} else if (event.getMKeyCode() < 0) {
|
|
||||||
int keyEventCode = KeyCode.INSTANCE.toKeyEventCode(event.getMKeyCode());
|
|
||||||
if (keyEventCode != KeyEvent.KEYCODE_UNKNOWN) {
|
if (keyEventCode != KeyEvent.KEYCODE_UNKNOWN) {
|
||||||
sendDownUpKeyEvent(keyEventCode);
|
sendDownUpKeyEventWithMetaState(keyEventCode, event.getMMetaState());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
// unknown event
|
||||||
throw new RuntimeException("Unknown key code : " + event.getMKeyCode());
|
Log.e(TAG, "unknown event, key code: "+keyCode+", meta: "+event.getMMetaState());
|
||||||
|
if (DebugFlags.DEBUG_ENABLED)
|
||||||
|
throw new RuntimeException("Unknown event");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ object Defaults {
|
||||||
const val PREF_VIBRATE_ON = false
|
const val PREF_VIBRATE_ON = false
|
||||||
const val PREF_VIBRATE_IN_DND_MODE = false
|
const val PREF_VIBRATE_IN_DND_MODE = false
|
||||||
const val PREF_SOUND_ON = false
|
const val PREF_SOUND_ON = false
|
||||||
|
const val PREF_SHOW_EMOJI_DESCRIPTIONS = true
|
||||||
@JvmField
|
@JvmField
|
||||||
var PREF_POPUP_ON = true
|
var PREF_POPUP_ON = true
|
||||||
const val PREF_AUTO_CORRECTION = true
|
const val PREF_AUTO_CORRECTION = true
|
||||||
|
|
|
@ -67,6 +67,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
|
||||||
public static final String PREF_VIBRATE_ON = "vibrate_on";
|
public static final String PREF_VIBRATE_ON = "vibrate_on";
|
||||||
public static final String PREF_VIBRATE_IN_DND_MODE = "vibrate_in_dnd_mode";
|
public static final String PREF_VIBRATE_IN_DND_MODE = "vibrate_in_dnd_mode";
|
||||||
public static final String PREF_SOUND_ON = "sound_on";
|
public static final String PREF_SOUND_ON = "sound_on";
|
||||||
|
public static final String PREF_SHOW_EMOJI_DESCRIPTIONS = "show_emoji_descriptions";
|
||||||
public static final String PREF_POPUP_ON = "popup_on";
|
public static final String PREF_POPUP_ON = "popup_on";
|
||||||
public static final String PREF_AUTO_CORRECTION = "auto_correction";
|
public static final String PREF_AUTO_CORRECTION = "auto_correction";
|
||||||
public static final String PREF_MORE_AUTO_CORRECTION = "more_auto_correction";
|
public static final String PREF_MORE_AUTO_CORRECTION = "more_auto_correction";
|
||||||
|
|
|
@ -54,6 +54,7 @@ public class SettingsValues {
|
||||||
public final boolean mVibrateOn;
|
public final boolean mVibrateOn;
|
||||||
public final boolean mVibrateInDndMode;
|
public final boolean mVibrateInDndMode;
|
||||||
public final boolean mSoundOn;
|
public final boolean mSoundOn;
|
||||||
|
public final boolean mShowEmojiDescriptions;
|
||||||
public final boolean mKeyPreviewPopupOn;
|
public final boolean mKeyPreviewPopupOn;
|
||||||
public final boolean mShowsVoiceInputKey;
|
public final boolean mShowsVoiceInputKey;
|
||||||
public final boolean mLanguageSwitchKeyToOtherImes;
|
public final boolean mLanguageSwitchKeyToOtherImes;
|
||||||
|
@ -169,6 +170,7 @@ public class SettingsValues {
|
||||||
mVibrateOn = Settings.readVibrationEnabled(prefs);
|
mVibrateOn = Settings.readVibrationEnabled(prefs);
|
||||||
mVibrateInDndMode = prefs.getBoolean(Settings.PREF_VIBRATE_IN_DND_MODE, Defaults.PREF_VIBRATE_IN_DND_MODE);
|
mVibrateInDndMode = prefs.getBoolean(Settings.PREF_VIBRATE_IN_DND_MODE, Defaults.PREF_VIBRATE_IN_DND_MODE);
|
||||||
mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON, Defaults.PREF_SOUND_ON);
|
mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON, Defaults.PREF_SOUND_ON);
|
||||||
|
mShowEmojiDescriptions = prefs.getBoolean(Settings.PREF_SHOW_EMOJI_DESCRIPTIONS, Defaults.PREF_SHOW_EMOJI_DESCRIPTIONS);
|
||||||
mKeyPreviewPopupOn = prefs.getBoolean(Settings.PREF_POPUP_ON, Defaults.PREF_POPUP_ON);
|
mKeyPreviewPopupOn = prefs.getBoolean(Settings.PREF_POPUP_ON, Defaults.PREF_POPUP_ON);
|
||||||
mSlidingKeyInputPreviewEnabled = prefs.getBoolean(
|
mSlidingKeyInputPreviewEnabled = prefs.getBoolean(
|
||||||
DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW, Defaults.PREF_SLIDING_KEY_INPUT_PREVIEW);
|
DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW, Defaults.PREF_SLIDING_KEY_INPUT_PREVIEW);
|
||||||
|
|
|
@ -116,6 +116,7 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int)
|
||||||
private val toolbarArrowIcon = KeyboardIconsSet.instance.getNewDrawable(KeyboardIconsSet.NAME_TOOLBAR_KEY, context)
|
private val toolbarArrowIcon = KeyboardIconsSet.instance.getNewDrawable(KeyboardIconsSet.NAME_TOOLBAR_KEY, context)
|
||||||
private val defaultToolbarBackground: Drawable = toolbarExpandKey.background
|
private val defaultToolbarBackground: Drawable = toolbarExpandKey.background
|
||||||
private val enabledToolKeyBackground = GradientDrawable()
|
private val enabledToolKeyBackground = GradientDrawable()
|
||||||
|
private var direction = 1 // 1 if LTR, -1 if RTL
|
||||||
init {
|
init {
|
||||||
val colors = Settings.getValues().mColors
|
val colors = Settings.getValues().mColors
|
||||||
|
|
||||||
|
@ -171,7 +172,6 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int)
|
||||||
private lateinit var listener: Listener
|
private lateinit var listener: Listener
|
||||||
private var suggestedWords = SuggestedWords.getEmptyInstance()
|
private var suggestedWords = SuggestedWords.getEmptyInstance()
|
||||||
private var startIndexOfMoreSuggestions = 0
|
private var startIndexOfMoreSuggestions = 0
|
||||||
private var direction = 1 // 1 if LTR, -1 if RTL
|
|
||||||
private var isExternalSuggestionVisible = false // Required to disable the more suggestions if other suggestions are visible
|
private var isExternalSuggestionVisible = false // Required to disable the more suggestions if other suggestions are visible
|
||||||
private val layoutHelper = SuggestionStripLayoutHelper(context, attrs, defStyle, wordViews, dividerViews, debugInfoViews)
|
private val layoutHelper = SuggestionStripLayoutHelper(context, attrs, defStyle, wordViews, dividerViews, debugInfoViews)
|
||||||
private val moreSuggestionsView = moreSuggestionsContainer.findViewById<MoreSuggestionsView>(R.id.more_suggestions_view).apply {
|
private val moreSuggestionsView = moreSuggestionsContainer.findViewById<MoreSuggestionsView>(R.id.more_suggestions_view).apply {
|
||||||
|
@ -234,6 +234,7 @@ class SuggestionStripView(context: Context, attrs: AttributeSet?, defStyle: Int)
|
||||||
context, suggestedWords, suggestionsStrip, this
|
context, suggestedWords, suggestionsStrip, this
|
||||||
)
|
)
|
||||||
isExternalSuggestionVisible = false
|
isExternalSuggestionVisible = false
|
||||||
|
updateKeys();
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setExternalSuggestionView(view: View?) {
|
fun setExternalSuggestionView(view: View?) {
|
||||||
|
|
|
@ -101,6 +101,10 @@ object DictionaryInfoUtils {
|
||||||
return absoluteDirectoryName
|
return absoluteDirectoryName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getCachedDictForLocaleAndType(locale: Locale, type: String, context: Context): File? =
|
||||||
|
getCachedDictsForLocale(locale, context).firstOrNull { it.name.substringBefore("_") == type }
|
||||||
|
|
||||||
fun getCachedDictsForLocale(locale: Locale, context: Context) =
|
fun getCachedDictsForLocale(locale: Locale, context: Context) =
|
||||||
getCacheDirectoryForLocale(locale, context)?.let { File(it).listFiles() }.orEmpty()
|
getCacheDirectoryForLocale(locale, context)?.let { File(it).listFiles() }.orEmpty()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package helium314.keyboard.latin.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode
|
||||||
|
import helium314.keyboard.latin.inputlogic.InputLogic
|
||||||
|
import helium314.keyboard.latin.utils.Log.i
|
||||||
|
|
||||||
|
object IntentUtils {
|
||||||
|
val TAG: String = InputLogic::class.java.simpleName
|
||||||
|
private val ACTION_SEND_INTENT = "helium314.keyboard.latin.ACTION_SEND_INTENT"
|
||||||
|
private val EXTRA_NUMBER = "EXTRA_NUMBER"
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun handleSendIntentKey(context: Context, mKeyCode: Int) {
|
||||||
|
val intentNumber = (KeyCode.SEND_INTENT_ONE + 1) - mKeyCode;
|
||||||
|
|
||||||
|
val intent: Intent = Intent(ACTION_SEND_INTENT).apply {
|
||||||
|
putExtra(EXTRA_NUMBER, intentNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.sendBroadcast(intent)
|
||||||
|
i(TAG, "Sent broadcast for intent number: $intentNumber")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,9 +7,13 @@
|
||||||
package helium314.keyboard.latin.utils;
|
package helium314.keyboard.latin.utils;
|
||||||
|
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.text.style.SuggestionSpan;
|
import android.text.style.SuggestionSpan;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a range of text, relative to the current cursor position.
|
* Represents a range of text, relative to the current cursor position.
|
||||||
|
@ -95,6 +99,28 @@ public final class TextRange {
|
||||||
return writeIndex == readIndex ? spans : Arrays.copyOfRange(spans, 0, writeIndex);
|
return writeIndex == readIndex ? spans : Arrays.copyOfRange(spans, 0, writeIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (!(other instanceof TextRange textRange)) return false;
|
||||||
|
return mWordAtCursorStartIndex == textRange.mWordAtCursorStartIndex
|
||||||
|
&& mWordAtCursorEndIndex == textRange.mWordAtCursorEndIndex
|
||||||
|
&& mCursorIndex == textRange.mCursorIndex
|
||||||
|
&& mHasUrlSpans == textRange.mHasUrlSpans
|
||||||
|
&& TextUtils.equals(mTextAtCursor, textRange.mTextAtCursor)
|
||||||
|
&& TextUtils.equals(mWord, textRange.mWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(mTextAtCursor, mWordAtCursorStartIndex, mWordAtCursorEndIndex, mCursorIndex, mWord, mHasUrlSpans);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return mTextAtCursor + ", " + mWord + ", " + mCursorIndex;
|
||||||
|
}
|
||||||
|
|
||||||
public TextRange(final CharSequence textAtCursor, final int wordAtCursorStartIndex,
|
public TextRange(final CharSequence textAtCursor, final int wordAtCursorStartIndex,
|
||||||
final int wordAtCursorEndIndex, final int cursorIndex, final boolean hasUrlSpans) {
|
final int wordAtCursorEndIndex, final int cursorIndex, final boolean hasUrlSpans) {
|
||||||
if (wordAtCursorStartIndex < 0 || cursorIndex < wordAtCursorStartIndex
|
if (wordAtCursorStartIndex < 0 || cursorIndex < wordAtCursorStartIndex
|
||||||
|
|
|
@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.safeDrawing
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
@ -45,6 +46,7 @@ import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import helium314.keyboard.latin.R
|
import helium314.keyboard.latin.R
|
||||||
|
@ -241,7 +243,8 @@ fun ExpandableSearchField(
|
||||||
}) { CloseIcon(android.R.string.cancel) } },
|
}) { CloseIcon(android.R.string.cancel) } },
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
colors = colors,
|
colors = colors,
|
||||||
textStyle = contentTextDirectionStyle
|
textStyle = contentTextDirectionStyle,
|
||||||
|
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,12 +47,12 @@ fun NewDictionaryDialog(
|
||||||
} else if (header != null) {
|
} else if (header != null) {
|
||||||
val ctx = LocalContext.current
|
val ctx = LocalContext.current
|
||||||
val dictLocale = header.mLocaleString.constructLocale()
|
val dictLocale = header.mLocaleString.constructLocale()
|
||||||
var locale by remember { mutableStateOf(mainLocale ?: dictLocale) }
|
|
||||||
val enabledLanguages = SubtypeSettings.getEnabledSubtypes().map { it.locale().language }
|
val enabledLanguages = SubtypeSettings.getEnabledSubtypes().map { it.locale().language }
|
||||||
val comparer = compareBy<Locale>({ it != mainLocale }, { it != dictLocale }, { it.language !in enabledLanguages }, { it.script() != dictLocale.script() })
|
val comparer = compareBy<Locale>({ it != mainLocale }, { it != dictLocale }, { it.language !in enabledLanguages }, { it.script() != dictLocale.script() })
|
||||||
val locales = SubtypeSettings.getAvailableSubtypeLocales()
|
val locales = SubtypeSettings.getAvailableSubtypeLocales()
|
||||||
.filter { it.script() == dictLocale.script() || it.script() == mainLocale?.script() }
|
.filter { it.script() == dictLocale.script() || it.script() == mainLocale?.script() }
|
||||||
.sortedWith(comparer)
|
.sortedWith(comparer)
|
||||||
|
var locale by remember { mutableStateOf(mainLocale ?: dictLocale.takeIf { it in locales } ?: locales.first()) }
|
||||||
val cacheDir = DictionaryInfoUtils.getCacheDirectoryForLocale(locale, ctx)
|
val cacheDir = DictionaryInfoUtils.getCacheDirectoryForLocale(locale, ctx)
|
||||||
val dictFile = File(cacheDir, header.mIdString.substringBefore(":") + "_" + DictionaryInfoUtils.USER_DICTIONARY_SUFFIX)
|
val dictFile = File(cacheDir, header.mIdString.substringBefore(":") + "_" + DictionaryInfoUtils.USER_DICTIONARY_SUFFIX)
|
||||||
val type = header.mIdString.substringBefore(":")
|
val type = header.mIdString.substringBefore(":")
|
||||||
|
|
|
@ -79,7 +79,8 @@ fun AppearanceScreen(
|
||||||
SettingsWithoutKey.CUSTOM_FONT,
|
SettingsWithoutKey.CUSTOM_FONT,
|
||||||
Settings.PREF_FONT_SCALE,
|
Settings.PREF_FONT_SCALE,
|
||||||
Settings.PREF_EMOJI_FONT_SCALE,
|
Settings.PREF_EMOJI_FONT_SCALE,
|
||||||
Settings.PREF_EMOJI_KEY_FIT,
|
if (prefs.getFloat(Settings.PREF_EMOJI_FONT_SCALE, Defaults.PREF_EMOJI_FONT_SCALE) != 1f)
|
||||||
|
Settings.PREF_EMOJI_KEY_FIT else null,
|
||||||
if (prefs.getInt(Settings.PREF_EMOJI_MAX_SDK, Defaults.PREF_EMOJI_MAX_SDK) >= 24)
|
if (prefs.getInt(Settings.PREF_EMOJI_MAX_SDK, Defaults.PREF_EMOJI_MAX_SDK) >= 24)
|
||||||
Settings.PREF_EMOJI_SKIN_TONE else null,
|
Settings.PREF_EMOJI_SKIN_TONE else null,
|
||||||
)
|
)
|
||||||
|
@ -109,6 +110,7 @@ fun createAppearanceSettings(context: Context) = listOf(
|
||||||
prefs.edit().remove(Settings.PREF_THEME_COLORS_NIGHT).apply()
|
prefs.edit().remove(Settings.PREF_THEME_COLORS_NIGHT).apply()
|
||||||
}
|
}
|
||||||
KeyboardIconsSet.needsReload = true // only relevant for Settings.PREF_CUSTOM_ICON_NAMES
|
KeyboardIconsSet.needsReload = true // only relevant for Settings.PREF_CUSTOM_ICON_NAMES
|
||||||
|
KeyboardSwitcher.getInstance().setThemeNeedsReload()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Setting(context, Settings.PREF_ICON_STYLE, R.string.icon_style) { setting ->
|
Setting(context, Settings.PREF_ICON_STYLE, R.string.icon_style) { setting ->
|
||||||
|
|
|
@ -57,8 +57,11 @@ fun DictionaryScreen(
|
||||||
val enabledLanguages = SubtypeSettings.getEnabledSubtypes(true).map { it.locale().language }
|
val enabledLanguages = SubtypeSettings.getEnabledSubtypes(true).map { it.locale().language }
|
||||||
val cachedDictFolders = DictionaryInfoUtils.getCacheDirectories(ctx).map { it.name }
|
val cachedDictFolders = DictionaryInfoUtils.getCacheDirectories(ctx).map { it.name }
|
||||||
val comparer = compareBy<Locale>({ it.language !in enabledLanguages }, { it.toLanguageTag() !in cachedDictFolders}, { it.displayName })
|
val comparer = compareBy<Locale>({ it.language !in enabledLanguages }, { it.toLanguageTag() !in cachedDictFolders}, { it.displayName })
|
||||||
val dictionaryLocales = remember { getDictionaryLocales(ctx).sortedWith(comparer).toMutableList() }
|
val dictionaryLocales = remember {
|
||||||
dictionaryLocales.add(0, Locale(SubtypeLocaleUtils.NO_LANGUAGE))
|
getDictionaryLocales(ctx).sortedWith(comparer).toMutableList().apply {
|
||||||
|
add(0, Locale(SubtypeLocaleUtils.NO_LANGUAGE))
|
||||||
|
}
|
||||||
|
}
|
||||||
var selectedLocale: Locale? by remember { mutableStateOf(null) }
|
var selectedLocale: Locale? by remember { mutableStateOf(null) }
|
||||||
var showAddDictDialog by remember { mutableStateOf(false) }
|
var showAddDictDialog by remember { mutableStateOf(false) }
|
||||||
val dictPicker = dictionaryFilePicker(selectedLocale)
|
val dictPicker = dictionaryFilePicker(selectedLocale)
|
||||||
|
|
|
@ -20,12 +20,15 @@ import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextField
|
import androidx.compose.material3.TextField
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
@ -83,6 +86,11 @@ fun PersonalDictionaryScreen(
|
||||||
)
|
)
|
||||||
if (selectedWord != null) {
|
if (selectedWord != null) {
|
||||||
val selWord = selectedWord!!
|
val selWord = selectedWord!!
|
||||||
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
LaunchedEffect(selectedWord) {
|
||||||
|
if (selWord.word == "" && selWord.weight == null && selWord.shortcut == null)
|
||||||
|
focusRequester.requestFocus() // user clicked add word
|
||||||
|
}
|
||||||
var newWord by remember { mutableStateOf(selWord) }
|
var newWord by remember { mutableStateOf(selWord) }
|
||||||
var newLocale by remember { mutableStateOf(locale) }
|
var newLocale by remember { mutableStateOf(locale) }
|
||||||
val wordValid = (newWord.word == selWord.word && locale == newLocale) || !doesWordExist(newWord.word, newLocale, ctx)
|
val wordValid = (newWord.word == selWord.word && locale == newLocale) || !doesWordExist(newWord.word, newLocale, ctx)
|
||||||
|
@ -119,7 +127,7 @@ fun PersonalDictionaryScreen(
|
||||||
TextField(
|
TextField(
|
||||||
value = newWord.word,
|
value = newWord.word,
|
||||||
onValueChange = { newWord = newWord.copy(word = it) },
|
onValueChange = { newWord = newWord.copy(word = it) },
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth().focusRequester(focusRequester),
|
||||||
singleLine = true
|
singleLine = true
|
||||||
)
|
)
|
||||||
Row(
|
Row(
|
||||||
|
|
|
@ -57,6 +57,7 @@ fun PreferencesScreen(
|
||||||
Settings.PREF_SOUND_ON,
|
Settings.PREF_SOUND_ON,
|
||||||
if (prefs.getBoolean(Settings.PREF_SOUND_ON, Defaults.PREF_SOUND_ON))
|
if (prefs.getBoolean(Settings.PREF_SOUND_ON, Defaults.PREF_SOUND_ON))
|
||||||
Settings.PREF_KEYPRESS_SOUND_VOLUME else null,
|
Settings.PREF_KEYPRESS_SOUND_VOLUME else null,
|
||||||
|
Settings.PREF_SHOW_EMOJI_DESCRIPTIONS,
|
||||||
R.string.settings_category_additional_keys,
|
R.string.settings_category_additional_keys,
|
||||||
Settings.PREF_SHOW_NUMBER_ROW,
|
Settings.PREF_SHOW_NUMBER_ROW,
|
||||||
if (SubtypeSettings.getEnabledSubtypes(true).any { it.locale().language in localesWithLocalizedNumberRow })
|
if (SubtypeSettings.getEnabledSubtypes(true).any { it.locale().language in localesWithLocalizedNumberRow })
|
||||||
|
@ -111,6 +112,14 @@ fun createPreferencesSettings(context: Context) = listOf(
|
||||||
Setting(context, Settings.PREF_SOUND_ON, R.string.sound_on_keypress) {
|
Setting(context, Settings.PREF_SOUND_ON, R.string.sound_on_keypress) {
|
||||||
SwitchPreference(it, Defaults.PREF_SOUND_ON)
|
SwitchPreference(it, Defaults.PREF_SOUND_ON)
|
||||||
},
|
},
|
||||||
|
Setting(
|
||||||
|
context, Settings.PREF_SHOW_EMOJI_DESCRIPTIONS, R.string.show_emoji_descriptions,
|
||||||
|
R.string.show_emoji_descriptions_summary
|
||||||
|
) {
|
||||||
|
SwitchPreference(it, Defaults.PREF_SHOW_EMOJI_DESCRIPTIONS) {
|
||||||
|
KeyboardSwitcher.getInstance().reloadKeyboard()
|
||||||
|
}
|
||||||
|
},
|
||||||
Setting(context, Settings.PREF_ENABLE_CLIPBOARD_HISTORY,
|
Setting(context, Settings.PREF_ENABLE_CLIPBOARD_HISTORY,
|
||||||
R.string.enable_clipboard_history, R.string.enable_clipboard_history_summary)
|
R.string.enable_clipboard_history, R.string.enable_clipboard_history_summary)
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,6 +24,7 @@ import helium314.keyboard.latin.settings.Defaults
|
||||||
import helium314.keyboard.latin.settings.Settings
|
import helium314.keyboard.latin.settings.Settings
|
||||||
import helium314.keyboard.latin.utils.JniUtils
|
import helium314.keyboard.latin.utils.JniUtils
|
||||||
import helium314.keyboard.latin.utils.Log
|
import helium314.keyboard.latin.utils.Log
|
||||||
|
import helium314.keyboard.latin.utils.ToolbarMode
|
||||||
import helium314.keyboard.latin.utils.getActivity
|
import helium314.keyboard.latin.utils.getActivity
|
||||||
import helium314.keyboard.latin.utils.prefs
|
import helium314.keyboard.latin.utils.prefs
|
||||||
import helium314.keyboard.settings.NextScreenIcon
|
import helium314.keyboard.settings.NextScreenIcon
|
||||||
|
@ -49,7 +50,8 @@ fun TextCorrectionScreen(
|
||||||
if ((b?.value ?: 0) < 0)
|
if ((b?.value ?: 0) < 0)
|
||||||
Log.v("irrelevant", "stupid way to trigger recomposition on preference change")
|
Log.v("irrelevant", "stupid way to trigger recomposition on preference change")
|
||||||
val autocorrectEnabled = prefs.getBoolean(Settings.PREF_AUTO_CORRECTION, Defaults.PREF_AUTO_CORRECTION)
|
val autocorrectEnabled = prefs.getBoolean(Settings.PREF_AUTO_CORRECTION, Defaults.PREF_AUTO_CORRECTION)
|
||||||
val suggestionsEnabled = prefs.getBoolean(Settings.PREF_SHOW_SUGGESTIONS, Defaults.PREF_SHOW_SUGGESTIONS)
|
val suggestionsVisible = Settings.readToolbarMode(prefs) in setOf(ToolbarMode.SUGGESTION_STRIP, ToolbarMode.EXPANDABLE)
|
||||||
|
val suggestionsEnabled = suggestionsVisible && prefs.getBoolean(Settings.PREF_SHOW_SUGGESTIONS, Defaults.PREF_SHOW_SUGGESTIONS)
|
||||||
val gestureEnabled = JniUtils.sHaveGestureLib && prefs.getBoolean(Settings.PREF_GESTURE_INPUT, Defaults.PREF_GESTURE_INPUT)
|
val gestureEnabled = JniUtils.sHaveGestureLib && prefs.getBoolean(Settings.PREF_GESTURE_INPUT, Defaults.PREF_GESTURE_INPUT)
|
||||||
val items = listOf(
|
val items = listOf(
|
||||||
SettingsWithoutKey.EDIT_PERSONAL_DICTIONARY,
|
SettingsWithoutKey.EDIT_PERSONAL_DICTIONARY,
|
||||||
|
@ -69,7 +71,7 @@ fun TextCorrectionScreen(
|
||||||
if (gestureEnabled) Settings.PREF_AUTOSPACE_AFTER_GESTURE_TYPING else null,
|
if (gestureEnabled) Settings.PREF_AUTOSPACE_AFTER_GESTURE_TYPING else null,
|
||||||
Settings.PREF_SHIFT_REMOVES_AUTOSPACE,
|
Settings.PREF_SHIFT_REMOVES_AUTOSPACE,
|
||||||
R.string.settings_category_suggestions,
|
R.string.settings_category_suggestions,
|
||||||
Settings.PREF_SHOW_SUGGESTIONS,
|
if (suggestionsVisible) Settings.PREF_SHOW_SUGGESTIONS else null,
|
||||||
if (suggestionsEnabled) Settings.PREF_ALWAYS_SHOW_SUGGESTIONS else null,
|
if (suggestionsEnabled) Settings.PREF_ALWAYS_SHOW_SUGGESTIONS else null,
|
||||||
if (suggestionsEnabled && prefs.getBoolean(Settings.PREF_ALWAYS_SHOW_SUGGESTIONS, Defaults.PREF_ALWAYS_SHOW_SUGGESTIONS))
|
if (suggestionsEnabled && prefs.getBoolean(Settings.PREF_ALWAYS_SHOW_SUGGESTIONS, Defaults.PREF_ALWAYS_SHOW_SUGGESTIONS))
|
||||||
Settings.PREF_ALWAYS_SHOW_SUGGESTIONS_EXCEPT_WEB_TEXT else null,
|
Settings.PREF_ALWAYS_SHOW_SUGGESTIONS_EXCEPT_WEB_TEXT else null,
|
||||||
|
|
|
@ -10,6 +10,15 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
>
|
>
|
||||||
|
<helium314.keyboard.keyboard.PopupTextView
|
||||||
|
android:id="@+id/description_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:maxLines="1" android:ellipsize="end"
|
||||||
|
style="?attr/popupKeysKeyboardViewStyle" />
|
||||||
<helium314.keyboard.keyboard.PopupKeysKeyboardView
|
<helium314.keyboard.keyboard.PopupKeysKeyboardView
|
||||||
android:id="@+id/popup_keys_keyboard_view"
|
android:id="@+id/popup_keys_keyboard_view"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
@ -426,4 +426,13 @@
|
||||||
<string name="use_apps_dict_summary">Namen der installierten Apps für Vorschläge und Korrekturen verwenden</string>
|
<string name="use_apps_dict_summary">Namen der installierten Apps für Vorschläge und Korrekturen verwenden</string>
|
||||||
<string name="prefs_emoji_skin_tone">Standard-Emoji-Hautton</string>
|
<string name="prefs_emoji_skin_tone">Standard-Emoji-Hautton</string>
|
||||||
<string name="prefs_emoji_skin_tone_neutral">Neutral</string>
|
<string name="prefs_emoji_skin_tone_neutral">Neutral</string>
|
||||||
|
<string name="toolbar_mode_expandable">Symbolleiste Tasten und Vorschläge</string>
|
||||||
|
<string name="toolbar_mode_toolbar_keys">Symbolleiste nur Tasten</string>
|
||||||
|
<string name="popup_order_and_hint_source">Popup Tasten Reihenfolge und Vorschlag Quelle</string>
|
||||||
|
<string name="toolbar_mode_hidden">Versteckt</string>
|
||||||
|
<string name="toolbar_hiding_global">Auch Zwischenablage und Emoji Symbolleisten verstecken</string>
|
||||||
|
<string name="subtype_with_layout_generic">%1$s (%2$s)</string>
|
||||||
|
<string name="landscape">Querformat</string>
|
||||||
|
<string name="toolbar_mode_suggestion_strip">Nur Vorschläge</string>
|
||||||
|
<string name="toolbar_mode">Symbolleiste Modus</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
Copyright (C) 2008 The Android Open Source Project
|
Copyright (C) 2008 The Android Open Source Project
|
||||||
modified
|
modified
|
||||||
SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
|
||||||
-->
|
--><resources>
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
|
||||||
<string name="use_contacts_dict">"Look up contact names"</string>
|
<string name="use_contacts_dict">"Look up contact names"</string>
|
||||||
<string name="vibrate_on_keypress">"Vibrate on keypress"</string>
|
<string name="vibrate_on_keypress">"Vibrate on keypress"</string>
|
||||||
<string name="sound_on_keypress">"Sound on keypress"</string>
|
<string name="sound_on_keypress">"Sound on keypress"</string>
|
||||||
|
@ -109,4 +108,29 @@
|
||||||
<string name="label_search_key">"Search"</string>
|
<string name="label_search_key">"Search"</string>
|
||||||
<string name="label_pause_key">"Pause"</string>
|
<string name="label_pause_key">"Pause"</string>
|
||||||
<string name="label_wait_key">"Wait"</string>
|
<string name="label_wait_key">"Wait"</string>
|
||||||
|
<string name="split_spacer_scale_landscape">Split distance (landscape)</string>
|
||||||
|
<string name="spell_checker_service_name">HeliBoard Spell Checker</string>
|
||||||
|
<string name="ime_settings">HeliBoard Settings</string>
|
||||||
|
<string name="android_spell_checker_settings">HeliBoard Spell Checker Settings</string>
|
||||||
|
<string name="split_spacer_scale">Split distance</string>
|
||||||
|
<string name="show_emoji_key">Emoji key</string>
|
||||||
|
<string name="switch_language">Switch Language</string>
|
||||||
|
<string name="settings_category_input">Input</string>
|
||||||
|
<string name="settings_category_additional_keys">Additional keys</string>
|
||||||
|
<string name="settings_category_suggestions">Suggestions</string>
|
||||||
|
<string name="vibrate_in_dnd_mode">Vibrate in do not disturb mode</string>
|
||||||
|
<string name="settings_category_correction">Corrections</string>
|
||||||
|
<string name="enable_split_keyboard_landscape">Enable split keyboard (landscape)</string>
|
||||||
|
<string name="disable_personalized_dicts_message">Warning: Disabling this setting will clear learned data</string>
|
||||||
|
<string name="settings_category_clipboard_history">Clipboard history</string>
|
||||||
|
<string name="settings_category_space">Space</string>
|
||||||
|
<string name="settings_category_experimental">Experimental</string>
|
||||||
|
<string name="settings_category_miscellaneous">Uncategorised</string>
|
||||||
|
<string name="language_switch_key_switch_both">Switch both</string>
|
||||||
|
<string name="language_switch_key_behavior">Language switch key behavior</string>
|
||||||
|
<string name="abbreviation_unit_minutes">%s min</string>
|
||||||
|
<string name="settings_no_limit">No limit</string>
|
||||||
|
<string name="add_to_personal_dictionary">Add learnt words to personal dictionary</string>
|
||||||
|
<string name="use_apps_dict">Look up app names</string>
|
||||||
|
<string name="use_apps_dict_summary">Use names of installed apps for suggestions and corrections</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -124,7 +124,7 @@ Nouveau dictionnaire:
|
||||||
<string name="dictionary_file_wrong_script">Erreur : script non compatible avec ce clavier</string>
|
<string name="dictionary_file_wrong_script">Erreur : script non compatible avec ce clavier</string>
|
||||||
<string name="dictionary_file_wrong_locale_ok">"Utiliser quand même"</string>
|
<string name="dictionary_file_wrong_locale_ok">"Utiliser quand même"</string>
|
||||||
<string name="dictionary_load_error">"Erreur de chargement du fichier dictionnaire"</string>
|
<string name="dictionary_load_error">"Erreur de chargement du fichier dictionnaire"</string>
|
||||||
<string name="dictionary_available">"Dictionnaire disponible"</string>
|
<string name="dictionary_available">Dictionnaires disponibles</string>
|
||||||
<string name="last_update">"Dernière mise à jour"</string>
|
<string name="last_update">"Dernière mise à jour"</string>
|
||||||
<string name="delete">"Supprimer"</string>
|
<string name="delete">"Supprimer"</string>
|
||||||
<string name="version_text">Version %s</string>
|
<string name="version_text">Version %s</string>
|
||||||
|
|
|
@ -428,11 +428,19 @@
|
||||||
<string name="timestamp_format_title">Formato per il tasto data/ora</string>
|
<string name="timestamp_format_title">Formato per il tasto data/ora</string>
|
||||||
<string name="subtype_dag">Dagbani</string>
|
<string name="subtype_dag">Dagbani</string>
|
||||||
<string name="subtype_st">Sesotho</string>
|
<string name="subtype_st">Sesotho</string>
|
||||||
<string name="prefs_emoji_key_fit">Scala la dimensione dei tasti con la dimensione dei caratteri</string>
|
<string name="prefs_emoji_key_fit">Ridimensiona il tasto emoji in base alla dimensione dei caratteri</string>
|
||||||
<string name="subtype_with_layout_generic">%1$s (%2$s)</string>
|
<string name="subtype_with_layout_generic">%1$s (%2$s)</string>
|
||||||
<string name="backup_restored">Backup ripristinato</string>
|
<string name="backup_restored">Backup ripristinato</string>
|
||||||
<string name="use_apps_dict">Suggerisci nomi delle app installate</string>
|
<string name="use_apps_dict">Suggerisci nomi delle app installate</string>
|
||||||
<string name="use_apps_dict_summary">Aggiunge i nomi delle app installate alla lista di suggerimenti e correzioni</string>
|
<string name="use_apps_dict_summary">Aggiunge i nomi delle app installate alla lista di suggerimenti e correzioni</string>
|
||||||
<string name="prefs_emoji_skin_tone">Tonalità di pelle predefinita per le emoji</string>
|
<string name="prefs_emoji_skin_tone">Tonalità di pelle predefinita per le emoji</string>
|
||||||
<string name="prefs_emoji_skin_tone_neutral">Neutro (giallo)</string>
|
<string name="prefs_emoji_skin_tone_neutral">Neutro (giallo)</string>
|
||||||
|
<string name="toolbar_mode_expandable">Tasti funzione e suggerimenti</string>
|
||||||
|
<string name="toolbar_mode_toolbar_keys">Solo tasti funzione</string>
|
||||||
|
<string name="toolbar_mode_hidden">Nascosta</string>
|
||||||
|
<string name="toolbar_hiding_global">Nascondi anche le barre degli appunti e delle emoji</string>
|
||||||
|
<string name="landscape">Orizzontale</string>
|
||||||
|
<string name="popup_order_and_hint_source">Ordine dei caratteri popup e fonti dei suggerimenti</string>
|
||||||
|
<string name="toolbar_mode_suggestion_strip">Solo suggerimenti</string>
|
||||||
|
<string name="toolbar_mode">Modalità</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
<string name="vibrate_in_dnd_mode">Vibrate in do not disturb mode</string>
|
<string name="vibrate_in_dnd_mode">Vibrate in do not disturb mode</string>
|
||||||
<!-- Option to play back sound on keypress in soft keyboard -->
|
<!-- Option to play back sound on keypress in soft keyboard -->
|
||||||
<string name="sound_on_keypress">Sound on keypress</string>
|
<string name="sound_on_keypress">Sound on keypress</string>
|
||||||
|
<!-- Option to show emoji descriptions on long press -->
|
||||||
|
<string name="show_emoji_descriptions">Show emoji description on long press</string>
|
||||||
|
<!-- Description for option to show emoji descriptions on long press -->
|
||||||
|
<string name="show_emoji_descriptions_summary">Requires an emoji dictionary</string>
|
||||||
<!-- Option to control whether or not to show a popup with a larger font on each key press. -->
|
<!-- Option to control whether or not to show a popup with a larger font on each key press. -->
|
||||||
<string name="popup_on_keypress">Popup on keypress</string>
|
<string name="popup_on_keypress">Popup on keypress</string>
|
||||||
<!-- Settings screen title for preferences-->
|
<!-- Settings screen title for preferences-->
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
kn_IN: Kannada (India)/kannada
|
kn_IN: Kannada (India)/kannada
|
||||||
kn_IN: Kannada Extended (India)/kannada
|
kn_IN: Kannada Extended (India)/kannada
|
||||||
ky: Kyrgyz/russian
|
ky: Kyrgyz/russian
|
||||||
|
la: Latin
|
||||||
lg: Luganda/luganda # This is a preliminary keyboard layout.
|
lg: Luganda/luganda # This is a preliminary keyboard layout.
|
||||||
ln: Lingala/lingala # This is a preliminary keyboard layout.
|
ln: Lingala/lingala # This is a preliminary keyboard layout.
|
||||||
lo_LA: Lao (Laos)/lao
|
lo_LA: Lao (Laos)/lao
|
||||||
|
@ -828,6 +829,15 @@
|
||||||
android:imeSubtypeMode="keyboard"
|
android:imeSubtypeMode="keyboard"
|
||||||
android:imeSubtypeExtraValue="KeyboardLayoutSet=MAIN:qwertz,AsciiCapable,EmojiCapable"
|
android:imeSubtypeExtraValue="KeyboardLayoutSet=MAIN:qwertz,AsciiCapable,EmojiCapable"
|
||||||
/>
|
/>
|
||||||
|
<subtype android:icon="@drawable/ic_ime_switcher"
|
||||||
|
android:label="@string/subtype_generic"
|
||||||
|
android:subtypeId="0x37885a0c"
|
||||||
|
android:imeSubtypeLocale="la"
|
||||||
|
android:languageTag="la"
|
||||||
|
android:imeSubtypeMode="keyboard"
|
||||||
|
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
|
||||||
|
android:isAsciiCapable="true"
|
||||||
|
/>
|
||||||
<subtype android:icon="@drawable/ic_ime_switcher"
|
<subtype android:icon="@drawable/ic_ime_switcher"
|
||||||
android:label="@string/subtype_generic"
|
android:label="@string/subtype_generic"
|
||||||
android:subtypeId="0x1ec2b4c9"
|
android:subtypeId="0x1ec2b4c9"
|
||||||
|
|
|
@ -92,6 +92,9 @@
|
||||||
<subtype
|
<subtype
|
||||||
android:label="@string/subtype_generic"
|
android:label="@string/subtype_generic"
|
||||||
android:subtypeLocale="kn" />
|
android:subtypeLocale="kn" />
|
||||||
|
<subtype
|
||||||
|
android:label="@string/subtype_generic"
|
||||||
|
android:subtypeLocale="la" />
|
||||||
<subtype
|
<subtype
|
||||||
android:label="@string/subtype_generic"
|
android:label="@string/subtype_generic"
|
||||||
android:subtypeLocale="lb" />
|
android:subtypeLocale="lb" />
|
||||||
|
|
|
@ -4,9 +4,13 @@ package helium314.keyboard.latin
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import helium314.keyboard.ShadowInputMethodManager2
|
import helium314.keyboard.ShadowInputMethodManager2
|
||||||
import helium314.keyboard.latin.common.StringUtils
|
import helium314.keyboard.latin.common.StringUtils
|
||||||
|
import helium314.keyboard.latin.common.endsWithWordCodepoint
|
||||||
import helium314.keyboard.latin.common.getFullEmojiAtEnd
|
import helium314.keyboard.latin.common.getFullEmojiAtEnd
|
||||||
|
import helium314.keyboard.latin.common.getTouchedWordRange
|
||||||
import helium314.keyboard.latin.common.nonWordCodePointAndNoSpaceBeforeCursor
|
import helium314.keyboard.latin.common.nonWordCodePointAndNoSpaceBeforeCursor
|
||||||
import helium314.keyboard.latin.settings.SpacingAndPunctuations
|
import helium314.keyboard.latin.settings.SpacingAndPunctuations
|
||||||
|
import helium314.keyboard.latin.utils.ScriptUtils
|
||||||
|
import helium314.keyboard.latin.utils.TextRange
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.robolectric.RobolectricTestRunner
|
import org.robolectric.RobolectricTestRunner
|
||||||
import org.robolectric.annotation.Config
|
import org.robolectric.annotation.Config
|
||||||
|
@ -60,6 +64,54 @@ class StringUtilsTest {
|
||||||
assert(nonWordCodePointAndNoSpaceBeforeCursor("th.is", sp))
|
assert(nonWordCodePointAndNoSpaceBeforeCursor("th.is", sp))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test fun `is word-like at end`() {
|
||||||
|
val sp = SpacingAndPunctuations(ApplicationProvider.getApplicationContext<App>().resources, false)
|
||||||
|
assert(!endsWithWordCodepoint("", sp))
|
||||||
|
assert(endsWithWordCodepoint("don'", sp))
|
||||||
|
assert(!endsWithWordCodepoint("hello!", sp))
|
||||||
|
assert(!endsWithWordCodepoint("when ", sp))
|
||||||
|
assert(endsWithWordCodepoint("3-", sp)) // todo: this seems wrong
|
||||||
|
assert(endsWithWordCodepoint("5'", sp)) // todo: this seems wrong
|
||||||
|
assert(endsWithWordCodepoint("1", sp)) // todo: this seems wrong
|
||||||
|
assert(endsWithWordCodepoint("a-", sp))
|
||||||
|
assert(!endsWithWordCodepoint("--", sp))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun `get touched text range`() {
|
||||||
|
val sp = SpacingAndPunctuations(ApplicationProvider.getApplicationContext<App>().resources, false)
|
||||||
|
val spUrl = SpacingAndPunctuations(ApplicationProvider.getApplicationContext<App>().resources, true)
|
||||||
|
val script = ScriptUtils.SCRIPT_LATIN
|
||||||
|
checkTextRange("blabla this is v", "ery good", sp, script, 15, 19)
|
||||||
|
checkTextRange(".hel", "lo...", sp, script, 1, 6)
|
||||||
|
checkTextRange("(hi", ")", sp, script, 1, 3)
|
||||||
|
checkTextRange("", "word", sp, script, 0, 4)
|
||||||
|
|
||||||
|
checkTextRange("mail: blorb@", "florb.com or", sp, script, 12, 17)
|
||||||
|
checkTextRange("mail: blorb@", "florb.com or", spUrl, script, 6, 21)
|
||||||
|
checkTextRange("mail: blor", "b@florb.com or", sp, script, 6, 11)
|
||||||
|
checkTextRange("mail: blor", "b@florb.com or", spUrl, script, 6, 21)
|
||||||
|
checkTextRange("mail: blorb@f", "lorb.com or", sp, script, 12, 17)
|
||||||
|
checkTextRange("mail: blorb@f", "lorb.com or", spUrl, script, 6, 21)
|
||||||
|
|
||||||
|
checkTextRange("http://exam", "ple.com", sp, script, 7, 14)
|
||||||
|
checkTextRange("http://exam", "ple.com", spUrl, script, 7, 18)
|
||||||
|
checkTextRange("http://example.", "com", sp, script, 15, 18)
|
||||||
|
checkTextRange("http://example.", "com", spUrl, script, 7, 18)
|
||||||
|
checkTextRange("htt", "p://example.com", sp, script, 0, 4)
|
||||||
|
checkTextRange("htt", "p://example.com", spUrl, script, 0, 18)
|
||||||
|
checkTextRange("http:/", "/example.com", sp, script, 6, 6)
|
||||||
|
checkTextRange("http:/", "/example.com", spUrl, script, 0, 18)
|
||||||
|
|
||||||
|
checkTextRange("..", ".", spUrl, script, 2, 2)
|
||||||
|
checkTextRange("...", "", spUrl, script, 3, 3)
|
||||||
|
|
||||||
|
// todo: these are bad cases of url detection
|
||||||
|
// also: sometimesWordConnectors are for URL and should be named accordingly
|
||||||
|
checkTextRange("@@@", "@@@", spUrl, script, 0, 6)
|
||||||
|
checkTextRange("a...", "", spUrl, script, 0, 4)
|
||||||
|
checkTextRange("@@@", "", spUrl, script, 0, 3)
|
||||||
|
}
|
||||||
|
|
||||||
@Test fun detectEmojisAtEnd() {
|
@Test fun detectEmojisAtEnd() {
|
||||||
assertEquals("", getFullEmojiAtEnd("\uD83C\uDF83 "))
|
assertEquals("", getFullEmojiAtEnd("\uD83C\uDF83 "))
|
||||||
assertEquals("", getFullEmojiAtEnd("a"))
|
assertEquals("", getFullEmojiAtEnd("a"))
|
||||||
|
@ -87,4 +139,10 @@ class StringUtilsTest {
|
||||||
// could help towards fully fixing https://github.com/Helium314/HeliBoard/issues/22
|
// could help towards fully fixing https://github.com/Helium314/HeliBoard/issues/22
|
||||||
// though this might be tricky, as some emojis will show as one on new Android versions, and
|
// though this might be tricky, as some emojis will show as one on new Android versions, and
|
||||||
// as two on older versions
|
// as two on older versions
|
||||||
|
|
||||||
|
private fun checkTextRange(before: String, after: String, sp: SpacingAndPunctuations, script: String, wordStart: Int, WordEnd: Int) {
|
||||||
|
val got = getTouchedWordRange(before, after, script, sp)
|
||||||
|
val wanted = TextRange(before + after, wordStart, WordEnd, before.length, false)
|
||||||
|
assertEquals(wanted, got)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
10
fastlane/metadata/android/ar/changelogs/3200.txt
Normal file
10
fastlane/metadata/android/ar/changelogs/3200.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
* إضافة أوضاع شريط الأدوات (تسمح بإخفاء شريط الأدوات)
|
||||||
|
* إضافة بعض متغيرات الرموز التعبيرية المفقودة
|
||||||
|
* تحسين شاشة النوع الفرعي وحوار القاموس
|
||||||
|
* إصلاح الألوان عند فرض الوضع الداكن
|
||||||
|
* نقل معظم إعدادات مقياس الوضع الرأسي/الأفقي إلى حوار
|
||||||
|
* إزالة ترجمات السلاسل الموسومة على أنها غير قابلة للترجمة
|
||||||
|
* إصلاح اتجاه سهم الشاشة التالية للغات اليمين إلى اليسار
|
||||||
|
* إصلاح التحميل الصحيح للغة العبرية على أندرويد 15
|
||||||
|
* توفير لوحة مفاتيح أساسية على الأقل عندما لا تعمل المكتبة على الإطلاق
|
||||||
|
* إصلاحات أخطاء طفيفة
|
10
fastlane/metadata/android/ar/changelogs/3201.txt
Normal file
10
fastlane/metadata/android/ar/changelogs/3201.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
* إضافة أوضاع شريط الأدوات (تسمح بإخفاء شريط الأدوات)
|
||||||
|
* إضافة بعض متغيرات الرموز التعبيرية المفقودة
|
||||||
|
* تحسين شاشة النوع الفرعي وحوار القاموس
|
||||||
|
* إصلاح الألوان عند فرض الوضع الداكن
|
||||||
|
* نقل معظم إعدادات مقياس الوضع الرأسي/الأفقي إلى حوار
|
||||||
|
* إزالة ترجمات السلاسل الموسومة على أنها غير قابلة للترجمة
|
||||||
|
* إصلاح اتجاه سهم الشاشة التالية للغات اليمين إلى اليسار
|
||||||
|
* إصلاح التحميل الصحيح للغة العبرية على أندرويد 15
|
||||||
|
* توفير لوحة مفاتيح أساسية على الأقل عندما لا تعمل المكتبة على الإطلاق
|
||||||
|
* إصلاحات أخطاء طفيفة
|
10
fastlane/metadata/android/ca/changelogs/3200.txt
Normal file
10
fastlane/metadata/android/ca/changelogs/3200.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
* af. modes de barra d'eines (permet ocultar-la)
|
||||||
|
* af. alguns emojis
|
||||||
|
* millorar pantalla de subtipus i el diàleg del dicc.
|
||||||
|
* corr. colors en forçar el mode fosc
|
||||||
|
* moure la majoria de la configuració d'escala vert/horiz a un diàleg
|
||||||
|
* elim. les trads. de les cadenes marcades com a no traduïbles
|
||||||
|
* corr. direcció de fletxa de la pantalla següent a idiomes RTL
|
||||||
|
* corr. la càrrega correcta de la config. regional hebrea a Android 15
|
||||||
|
* tenir almenys un teclat bàsic quan la biblio. no funciona
|
||||||
|
* corr. etc
|
10
fastlane/metadata/android/ca/changelogs/3201.txt
Normal file
10
fastlane/metadata/android/ca/changelogs/3201.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
* af. modes de barra d'eines (permet ocultar-la)
|
||||||
|
* af. alguns emojis
|
||||||
|
* millorar pantalla de subtipus i el diàleg del dicc.
|
||||||
|
* corr. colors en forçar el mode fosc
|
||||||
|
* moure la majoria de la configuració d'escala vert/horiz a un diàleg
|
||||||
|
* elim. les trads. de les cadenes marcades com a no traduïbles
|
||||||
|
* corr. direcció de fletxa de la pantalla següent a idiomes RTL
|
||||||
|
* corr. la càrrega correcta de la config. regional hebrea a Android 15
|
||||||
|
* tenir almenys un teclat bàsic quan la biblio. no funciona
|
||||||
|
* corr. etc
|
10
fastlane/metadata/android/cs-CZ/changelogs/3200.txt
Normal file
10
fastlane/metadata/android/cs-CZ/changelogs/3200.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
* přidány režimy panelu nástrojů (umožňuje skrýt p. n.)
|
||||||
|
* přidány chybějící varianty emodži
|
||||||
|
* vylepšena obrazovka podtypů a dialog slovníku
|
||||||
|
* opraveny barvy při vynuceném tmavém režimu
|
||||||
|
* přesun většiny nastavení měřítka do dialogu
|
||||||
|
* odstraněny překlady řetězců označených jako nepřekládatelné
|
||||||
|
* opraven směr šipky „dále“ pro jazyky RTL
|
||||||
|
* opraveno správné načítání hebrejského jazyka na Androidu 15
|
||||||
|
* zajištění alespoň základní klávesnice, když knihovna vůbec nefunguje
|
||||||
|
* drobné opravy chyb
|
10
fastlane/metadata/android/cs-CZ/changelogs/3201.txt
Normal file
10
fastlane/metadata/android/cs-CZ/changelogs/3201.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
* přidány režimy panelu nástrojů (umožňuje skrýt p. n.)
|
||||||
|
* přidány chybějící varianty emodži
|
||||||
|
* vylepšena obrazovka podtypů a dialog slovníku
|
||||||
|
* opraveny barvy při vynuceném tmavém režimu
|
||||||
|
* přesun většiny nastavení měřítka do dialogu
|
||||||
|
* odstraněny překlady řetězců označených jako nepřekládatelné
|
||||||
|
* opraven směr šipky „dále“ pro jazyky RTL
|
||||||
|
* opraveno správné načítání hebrejského jazyka na Androidu 15
|
||||||
|
* zajištění alespoň základní klávesnice, když knihovna vůbec nefunguje
|
||||||
|
* drobné opravy chyb
|
10
fastlane/metadata/android/en-US/changelogs/3201.txt
Normal file
10
fastlane/metadata/android/en-US/changelogs/3201.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
* add toolbar modes (allows hiding toolbar)
|
||||||
|
* add some missing emoji variants
|
||||||
|
* improve subtype screen and dictionary dialog
|
||||||
|
* fix colors when forcing dark mode
|
||||||
|
* move most of the portrait / landscape scale settings into a dialog
|
||||||
|
* remove translations of strings marked as non-translatable
|
||||||
|
* fix next-screen arrow direction for RTL languages
|
||||||
|
* fix proper loading of Hebrew locale on Android 15
|
||||||
|
* have at least a basic keyboard when library doesn't work at all
|
||||||
|
* minor bug fixes
|
10
fastlane/metadata/android/iw-IL/changelogs/3200.txt
Normal file
10
fastlane/metadata/android/iw-IL/changelogs/3200.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
* הוספת מצבי סרגל כלים (מאפשר הסתרת סרגלי כלים)
|
||||||
|
* הוספת גווני עור חסרים לחלק מהאמוג'ים
|
||||||
|
* שיפור מסך הכתב התחתי ודו-שיח המילון
|
||||||
|
* תיקון הצבעים בעת כפיית מצב כהה
|
||||||
|
* העברת מרבית הגדרות קנה-המידה של מצב אנכי / אופקי לדו-שיח
|
||||||
|
* הסרת תרגומי מחרוזות המסומנות כבלתי ניתנות לתרגום
|
||||||
|
* תיקון כיווני החץ למעבר למסך הבא עבור שםות הנכתבות מימין לשמאל
|
||||||
|
* תיקון טעינה נכונה של הגדרות השפה העברית באנדרואיד 15
|
||||||
|
* הצגת מקלדת בסיסית לכל הפחות כאשר ספריה איננה עובדת כלל
|
||||||
|
* תיקוני שגיאות שוליות
|
10
fastlane/metadata/android/iw-IL/changelogs/3201.txt
Normal file
10
fastlane/metadata/android/iw-IL/changelogs/3201.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
* הוספת מצבי סרגל כלים (מאפשר הסתרת סרגלי כלים)
|
||||||
|
* הוספת גווני עור חסרים לחלק מהאמוג'ים
|
||||||
|
* שיפור מסך הכתב התחתי ודו-שיח המילון
|
||||||
|
* תיקון הצבעים בעת כפיית מצב כהה
|
||||||
|
* העברת מרבית הגדרות קנה-המידה של מצב אנכי / אופקי לדו-שיח
|
||||||
|
* הסרת תרגומי מחרוזות המסומנות כבלתי ניתנות לתרגום
|
||||||
|
* תיקון כיווני החץ למעבר למסך הבא עבור שםות הנכתבות מימין לשמאל
|
||||||
|
* תיקון טעינה נכונה של הגדרות השפה העברית באנדרואיד 15
|
||||||
|
* הצגת מקלדת בסיסית לכל הפחות כאשר ספריה איננה עובדת כלל
|
||||||
|
* תיקוני שגיאות שוליות
|
10
fastlane/metadata/android/ru-RU/changelogs/3200.txt
Normal file
10
fastlane/metadata/android/ru-RU/changelogs/3200.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
* добавлены режимы панели инструментов (появилась возможность скрывать панель)
|
||||||
|
* добавлены некоторые недостающие варианты эмодзи
|
||||||
|
* улучшены экран выбора подтипа и диалог словаря
|
||||||
|
* исправлены цвета при принудительном тёмном режиме
|
||||||
|
* большинство настроек масштабирования для портретного и ландшафтного режимов перенесены в отдельный диалог
|
||||||
|
* удалены переводы строк, отмеченных как не подлежащие переводу
|
||||||
|
* исправлено направление стрелки перехода на следующий экран для языков с письмом справа налево (RTL)
|
||||||
|
* исправлена корректная загрузка иврита на Android 15
|
||||||
|
* обеспечена работа хотя бы базовой клавиатуры, если библиотека не функционирует вовсе
|
||||||
|
* мелкие исправления ошибок
|
10
fastlane/metadata/android/ru-RU/changelogs/3201.txt
Normal file
10
fastlane/metadata/android/ru-RU/changelogs/3201.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
* добавлены режимы панели инструментов (появилась возможность скрывать панель)
|
||||||
|
* добавлены некоторые недостающие варианты эмодзи
|
||||||
|
* улучшены экран выбора подтипа и диалог словаря
|
||||||
|
* исправлены цвета при принудительном тёмном режиме
|
||||||
|
* большинство настроек масштабирования для портретного и ландшафтного режимов перенесены в отдельный диалог
|
||||||
|
* удалены переводы строк, отмеченных как не подлежащие переводу
|
||||||
|
* исправлено направление стрелки перехода на следующий экран для языков с письмом справа налево (RTL)
|
||||||
|
* исправлена корректная загрузка иврита на Android 15
|
||||||
|
* обеспечена работа хотя бы базовой клавиатуры, если библиотека не функционирует вовсе
|
||||||
|
* мелкие исправления ошибок
|
|
@ -73,7 +73,7 @@ If the layout has exactly 2 keys in the bottom row, these keys will replace comm
|
||||||
* `0.1` for phones
|
* `0.1` for phones
|
||||||
* `0.09` for tablets
|
* `0.09` for tablets
|
||||||
* If the sum of widths in a row is greater than 1, keys are rescaled to fit on the screen
|
* If the sum of widths in a row is greater than 1, keys are rescaled to fit on the screen
|
||||||
* `labelFlags`: allows specific effects, see [here](app/src/main/res/values/attrs.xml#L251-L287) in the section _keyLabelFlags_ for names and numeric values
|
* `labelFlags`: allows specific effects, see [here](app/src/main/res/values/attrs.xml#L250-L282) in the section _keyLabelFlags_ for names and numeric values
|
||||||
* Since json does not support hexadecimal-values, you have to use the decimal values in the comments in the same line.
|
* Since json does not support hexadecimal-values, you have to use the decimal values in the comments in the same line.
|
||||||
* In case you want to apply multiple flags, you will need to combine them using [bitwise OR](https://en.wikipedia.org/wiki/Bitwise_operation#OR). In most cases this means you can just add the individual values, only exceptions are `fontDefault`, `followKeyLabelRatio`, `followKeyHintLabelRatio`, and `autoScale`.
|
* In case you want to apply multiple flags, you will need to combine them using [bitwise OR](https://en.wikipedia.org/wiki/Bitwise_operation#OR). In most cases this means you can just add the individual values, only exceptions are `fontDefault`, `followKeyLabelRatio`, `followKeyHintLabelRatio`, and `autoScale`.
|
||||||
|
|
||||||
|
@ -106,6 +106,12 @@ Usually the label is what is displayed on the key. However, there are some speci
|
||||||
You can also specify special key codes like `a|!code/key_action_previous` or `abc|!code/-10043`, but it's cleaner to use a json layout and specify the code explicitly. Note that when specifying a code in the label, and a code in a json layout, the code in the label will be ignored.
|
You can also specify special key codes like `a|!code/key_action_previous` or `abc|!code/-10043`, but it's cleaner to use a json layout and specify the code explicitly. Note that when specifying a code in the label, and a code in a json layout, the code in the label will be ignored.
|
||||||
* It's also possible to specify an icon, like `!icon/previous_key|!code/key_action_previous`.
|
* It's also possible to specify an icon, like `!icon/previous_key|!code/key_action_previous`.
|
||||||
* You can find available icon names in [KeyboardIconsSet](/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt). You can also use toolbar key icons using the uppercase name of the [toolbar key](/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt#L109), e.g. `!icon/redo`
|
* You can find available icon names in [KeyboardIconsSet](/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardIconsSet.kt). You can also use toolbar key icons using the uppercase name of the [toolbar key](/app/src/main/java/helium314/keyboard/latin/utils/ToolbarUtils.kt#L109), e.g. `!icon/redo`
|
||||||
|
* There are some further special labels to be used in popup keys (i.e. one of the popup keys should have the label)
|
||||||
|
* `!noPanelAutoPopupKey!`: no popups are shown, a long press will result in the first normal popup of the key being selected
|
||||||
|
* `!needsDividers!`: dividers are shown between popup keys
|
||||||
|
* `!hasLabels!`: reduces text size in popup keys for nicer display of labels instead of letters
|
||||||
|
* `!autoColumnOrder!`: use with a number, e.g. _!autoColumnOrder!4_ will result in 4 popup columns
|
||||||
|
* `!fixedColumnOrder!`: use with a number, e.g. _!fixedColumnOrder!4_ will result in 4 popup columns. Keys will not be re-ordered if the result is a single line.
|
||||||
|
|
||||||
## Adding new layouts / languages
|
## Adding new layouts / languages
|
||||||
* You need a layout file in one of the formats above, and add it to [layouts](app/src/main/assets/layouts)
|
* You need a layout file in one of the formats above, and add it to [layouts](app/src/main/assets/layouts)
|
||||||
|
|
|
@ -33,36 +33,6 @@ class EmojiData {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onEmojiInserted(group: EmojiGroup, emoji: EmojiSpec): Boolean {
|
private fun onEmojiInserted(group: EmojiGroup, emoji: EmojiSpec): Boolean {
|
||||||
// Unicode RGI does not include letter symbols but Android supports them, so we inject them manually.
|
|
||||||
if (emoji.codes contentEquals RAW_CPS_KEYCAP_HASH) {
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_A), 2.0f, "regional indicator symbol letter a")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_B), 2.0f, "regional indicator symbol letter b")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_C), 2.0f, "regional indicator symbol letter c")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_D), 2.0f, "regional indicator symbol letter d")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_E), 2.0f, "regional indicator symbol letter e")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_F), 2.0f, "regional indicator symbol letter f")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_G), 2.0f, "regional indicator symbol letter g")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_H), 2.0f, "regional indicator symbol letter h")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_I), 2.0f, "regional indicator symbol letter i")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_J), 2.0f, "regional indicator symbol letter j")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_K), 2.0f, "regional indicator symbol letter k")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_L), 2.0f, "regional indicator symbol letter l")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_M), 2.0f, "regional indicator symbol letter m")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_N), 2.0f, "regional indicator symbol letter n")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_O), 2.0f, "regional indicator symbol letter o")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_P), 2.0f, "regional indicator symbol letter p")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_Q), 2.0f, "regional indicator symbol letter q")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_R), 2.0f, "regional indicator symbol letter r")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_S), 2.0f, "regional indicator symbol letter s")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_T), 2.0f, "regional indicator symbol letter t")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_U), 2.0f, "regional indicator symbol letter u")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_V), 2.0f, "regional indicator symbol letter v")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_W), 2.0f, "regional indicator symbol letter w")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_X), 2.0f, "regional indicator symbol letter x")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_Y), 2.0f, "regional indicator symbol letter y")
|
|
||||||
insertEmoji(group, intArrayOf(CP_REGIONAL_INDICATOR_SYMBOL_LETTER_Z), 2.0f, "regional indicator symbol letter z")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some multi-skin-tone variants use a different base code than their non-multi-skin-tone counterparts,
|
// Some multi-skin-tone variants use a different base code than their non-multi-skin-tone counterparts,
|
||||||
// so they don't get grouped. We drop them here, to prevent each variant from being displayed separately.
|
// so they don't get grouped. We drop them here, to prevent each variant from being displayed separately.
|
||||||
return ! hasMultipleSkinModifiers(emoji.codes)
|
return ! hasMultipleSkinModifiers(emoji.codes)
|
||||||
|
@ -118,9 +88,6 @@ class EmojiData {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val RAW_CPS_KEYCAP_HASH = intArrayOf(0x0023, 0xFE0F, 0x20E3)
|
|
||||||
|
|
||||||
const val CP_NUL = 0x0000
|
const val CP_NUL = 0x0000
|
||||||
|
|
||||||
private const val CP_ZWJ = 0x200D
|
private const val CP_ZWJ = 0x200D
|
||||||
|
@ -136,34 +103,5 @@ class EmojiData {
|
||||||
private const val CP_WHITE_HAIR = 0x1F9B3
|
private const val CP_WHITE_HAIR = 0x1F9B3
|
||||||
private const val CP_BARLD = 0x1F9B2
|
private const val CP_BARLD = 0x1F9B2
|
||||||
private const val CP_VARIANT_SELECTOR = 0xFE0F
|
private const val CP_VARIANT_SELECTOR = 0xFE0F
|
||||||
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_A = 0x1F1E6
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_B = 0x1F1E7
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_C = 0x1F1E8
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_D = 0x1F1E9
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_E = 0x1F1EA
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_F = 0x1F1EB
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_G = 0x1F1EC
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_H = 0x1F1ED
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_I = 0x1F1EE
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_J = 0x1F1EF
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_K = 0x1F1F0
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_L = 0x1F1F1
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_M = 0x1F1F2
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_N = 0x1F1F3
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_O = 0x1F1F4
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_P = 0x1F1F5
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_Q = 0x1F1F6
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_R = 0x1F1F7
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_S = 0x1F1F8
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_T = 0x1F1F9
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_U = 0x1F1FA
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_V = 0x1F1FB
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_W = 0x1F1FC
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_X = 0x1F1FD
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_Y = 0x1F1FE
|
|
||||||
private const val CP_REGIONAL_INDICATOR_SYMBOL_LETTER_Z = 0x1F1FF
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3866,31 +3866,5 @@ U+1F532 # black square button
|
||||||
U+1F3C1 # chequered flag
|
U+1F3C1 # chequered flag
|
||||||
U+1F6A9 # triangular flag
|
U+1F6A9 # triangular flag
|
||||||
U+1F38C # crossed flags
|
U+1F38C # crossed flags
|
||||||
U+1F1E6 # regional indicator symbol letter a
|
|
||||||
U+1F1E7 # regional indicator symbol letter b
|
|
||||||
U+1F1E8 # regional indicator symbol letter c
|
|
||||||
U+1F1E9 # regional indicator symbol letter d
|
|
||||||
U+1F1EA # regional indicator symbol letter e
|
|
||||||
U+1F1EB # regional indicator symbol letter f
|
|
||||||
U+1F1EC # regional indicator symbol letter g
|
|
||||||
U+1F1ED # regional indicator symbol letter h
|
|
||||||
U+1F1EE # regional indicator symbol letter i
|
|
||||||
U+1F1EF # regional indicator symbol letter j
|
|
||||||
U+1F1F0 # regional indicator symbol letter k
|
|
||||||
U+1F1F1 # regional indicator symbol letter l
|
|
||||||
U+1F1F2 # regional indicator symbol letter m
|
|
||||||
U+1F1F3 # regional indicator symbol letter n
|
|
||||||
U+1F1F4 # regional indicator symbol letter o
|
|
||||||
U+1F1F5 # regional indicator symbol letter p
|
|
||||||
U+1F1F6 # regional indicator symbol letter q
|
|
||||||
U+1F1F7 # regional indicator symbol letter r
|
|
||||||
U+1F1F8 # regional indicator symbol letter s
|
|
||||||
U+1F1F9 # regional indicator symbol letter t
|
|
||||||
U+1F1FA # regional indicator symbol letter u
|
|
||||||
U+1F1FB # regional indicator symbol letter v
|
|
||||||
U+1F1FC # regional indicator symbol letter w
|
|
||||||
U+1F1FD # regional indicator symbol letter x
|
|
||||||
U+1F1FE # regional indicator symbol letter y
|
|
||||||
U+1F1FF # regional indicator symbol letter z
|
|
||||||
# Above emojis are supported from Android 4.4 (API level 19)
|
# Above emojis are supported from Android 4.4 (API level 19)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue