mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-06-30 12:49:57 +00:00
Compare commits
No commits in common. "main" and "v3.2" have entirely different histories.
47 changed files with 472 additions and 740 deletions
|
@ -18,7 +18,7 @@ android {
|
|||
abiFilters.clear()
|
||||
abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64"))
|
||||
}
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
|
|
@ -136,6 +136,32 @@
|
|||
®️
|
||||
™️
|
||||
|
||||
🇦
|
||||
🇧
|
||||
🇨
|
||||
🇩
|
||||
🇪
|
||||
🇫
|
||||
🇬
|
||||
🇭
|
||||
🇮
|
||||
🇯
|
||||
🇰
|
||||
🇱
|
||||
🇲
|
||||
🇳
|
||||
🇴
|
||||
🇵
|
||||
🇶
|
||||
🇷
|
||||
🇸
|
||||
🇹
|
||||
🇺
|
||||
🇻
|
||||
🇼
|
||||
🇽
|
||||
🇾
|
||||
🇿
|
||||
#️⃣
|
||||
*️⃣
|
||||
0️⃣
|
||||
|
@ -221,4 +247,4 @@
|
|||
💠
|
||||
🔘
|
||||
🔳
|
||||
🔲
|
||||
🔲
|
|
@ -139,16 +139,6 @@ class Event private constructor(
|
|||
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 {
|
||||
return Event(EVENT_TYPE_INPUT_KEYPRESS, null, codePoint, keyCode, metaState,
|
||||
Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE,
|
||||
|
@ -266,8 +256,10 @@ class Event private constructor(
|
|||
source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags or FLAG_COMBINING, source.mNextEvent)
|
||||
}
|
||||
|
||||
val notHandledEvent = Event(EVENT_TYPE_NOT_HANDLED, null, NOT_A_CODE_POINT, NOT_A_KEY_CODE, 0,
|
||||
fun createNotHandledEvent(): Event {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// This method is private - to create a new event, use one of the create* utility methods.
|
||||
|
|
|
@ -24,8 +24,7 @@ class HardwareKeyboardEventDecoder(val mDeviceId: Int) : HardwareEventDecoder {
|
|||
// 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,
|
||||
// hence the name "codePointAndFlags". {@see KeyEvent#getUnicodeChar()} for more info.
|
||||
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
|
||||
val codePointAndFlags = keyEvent.unicodeChar
|
||||
// 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
|
||||
// the key for 'A' or Space, but also Backspace or Ctrl or Caps Lock.
|
||||
|
@ -49,21 +48,6 @@ class HardwareKeyboardEventDecoder(val mDeviceId: Int) : HardwareEventDecoder {
|
|||
} else Event.createHardwareKeypressEvent(codePointAndFlags, keyCode, metaState, null, isKeyRepeat)
|
||||
// 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.
|
||||
} 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
|
||||
}
|
||||
} else Event.createNotHandledEvent()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
package helium314.keyboard.keyboard;
|
||||
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import helium314.keyboard.latin.common.Constants;
|
||||
import helium314.keyboard.latin.common.InputPointers;
|
||||
|
||||
|
@ -33,12 +31,6 @@ public interface KeyboardActionListener {
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -125,10 +117,6 @@ public interface KeyboardActionListener {
|
|||
@Override
|
||||
public void onReleaseKey(int primaryCode, boolean withSliding) {}
|
||||
@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) {}
|
||||
@Override
|
||||
public void onTextInput(String text) {}
|
||||
|
|
|
@ -1,24 +1,16 @@
|
|||
package helium314.keyboard.keyboard
|
||||
|
||||
import android.text.InputType
|
||||
import android.util.SparseArray
|
||||
import android.view.KeyEvent
|
||||
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.latin.EmojiAltPhysicalKeyDetector
|
||||
import helium314.keyboard.latin.LatinIME
|
||||
import helium314.keyboard.latin.RichInputMethodManager
|
||||
import helium314.keyboard.latin.common.Constants
|
||||
import helium314.keyboard.latin.common.InputPointers
|
||||
import helium314.keyboard.latin.common.StringUtils
|
||||
import helium314.keyboard.latin.common.combiningRange
|
||||
import helium314.keyboard.latin.common.loopOverCodePoints
|
||||
import helium314.keyboard.latin.common.loopOverCodePointsBackwards
|
||||
import helium314.keyboard.latin.define.ProductionFlags
|
||||
import helium314.keyboard.latin.inputlogic.InputLogic
|
||||
import helium314.keyboard.latin.settings.Settings
|
||||
import kotlin.math.abs
|
||||
|
@ -27,11 +19,6 @@ import kotlin.math.min
|
|||
class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inputLogic: InputLogic) : KeyboardActionListener {
|
||||
|
||||
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 settings = Settings.getInstance()
|
||||
|
@ -71,62 +58,9 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp
|
|||
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) {
|
||||
when (primaryCode) {
|
||||
KeyCode.TOGGLE_AUTOCORRECT -> return Settings.getInstance().toggleAutoCorrect()
|
||||
KeyCode.TOGGLE_INCOGNITO_MODE -> return Settings.getInstance().toggleAlwaysIncognitoMode()
|
||||
}
|
||||
val mkv = keyboardSwitcher.mainKeyboardView
|
||||
|
||||
// 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)
|
||||
latinIME.onCodeInput(primaryCode, metaState, mkv.getKeyX(x), mkv.getKeyY(y), isKeyRepeat)
|
||||
}
|
||||
|
||||
override fun onTextInput(text: String?) = latinIME.onTextInput(text)
|
||||
|
@ -323,13 +257,4 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp
|
|||
}
|
||||
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,11 +184,6 @@ public final class KeyboardId {
|
|||
|| 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() {
|
||||
return mElementId == ELEMENT_NUMBER || mElementId == ELEMENT_NUMPAD
|
||||
|| mElementId == ELEMENT_PHONE || mElementId == ELEMENT_PHONE_SYMBOLS;
|
||||
|
|
|
@ -138,7 +138,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
|
|||
return false;
|
||||
}
|
||||
|
||||
private void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues,
|
||||
public void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues,
|
||||
final int currentAutoCapsState, final int currentRecapitalizeState) {
|
||||
final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
|
||||
mThemeContext, editorInfo);
|
||||
|
@ -527,10 +527,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
|
|||
if (mCurrentInputView == null)
|
||||
return;
|
||||
mEmojiPalettesView.clearKeyboardCache();
|
||||
reloadMainKeyboard();
|
||||
}
|
||||
|
||||
public void reloadMainKeyboard() {
|
||||
loadKeyboard(mLatinIME.getCurrentInputEditorInfo(), Settings.getValues(),
|
||||
mLatinIME.getCurrentAutoCapsState(), mLatinIME.getCurrentRecapitalizeState());
|
||||
}
|
||||
|
@ -601,10 +597,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
|
|||
if (mKeyboardView == null || !mKeyboardView.isShown()) {
|
||||
return false;
|
||||
}
|
||||
final Keyboard keyboard = mKeyboardView.getKeyboard();
|
||||
if (keyboard == null) // may happen when using hardware keyboard
|
||||
return false;
|
||||
int activeKeyboardId = keyboard.mId.mElementId;
|
||||
int activeKeyboardId = mKeyboardView.getKeyboard().mId.mElementId;
|
||||
for (int keyboardId : keyboardIds) {
|
||||
if (activeKeyboardId == keyboardId) {
|
||||
return true;
|
||||
|
|
|
@ -360,21 +360,25 @@ public final class MainKeyboardView extends KeyboardView implements DrawingProxy
|
|||
public void onKeyPressed(@NonNull final Key key, final boolean withPreview) {
|
||||
key.onPressed();
|
||||
invalidateKey(key);
|
||||
|
||||
final Keyboard keyboard = getKeyboard();
|
||||
if (keyboard == null) {
|
||||
return;
|
||||
}
|
||||
mKeyPreviewDrawParams.setVisibleOffset(-keyboard.mVerticalGap);
|
||||
if (withPreview && !key.noKeyPreview() && mKeyPreviewDrawParams.isPopupEnabled()) {
|
||||
if (withPreview && !key.noKeyPreview()) {
|
||||
showKeyPreview(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();
|
||||
getLocationInWindow(mOriginCoords);
|
||||
mKeyPreviewChoreographer.placeAndShowKeyPreview(key, getKeyboard().mIconsSet, getKeyDrawParams(),
|
||||
mKeyPreviewChoreographer.placeAndShowKeyPreview(key, keyboard.mIconsSet, getKeyDrawParams(),
|
||||
KeyboardSwitcher.getInstance().getWrapperView().getWidth(), mOriginCoords, mDrawingPreviewPlacerView);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,15 +12,15 @@ import android.graphics.Canvas;
|
|||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import helium314.keyboard.accessibility.AccessibilityUtils;
|
||||
import helium314.keyboard.accessibility.PopupKeysKeyboardAccessibilityDelegate;
|
||||
import helium314.keyboard.keyboard.emoji.EmojiViewCallback;
|
||||
import helium314.keyboard.keyboard.emoji.OnKeyEventListener;
|
||||
import helium314.keyboard.keyboard.internal.KeyDrawParams;
|
||||
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode;
|
||||
import helium314.keyboard.latin.R;
|
||||
|
@ -39,7 +39,7 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane
|
|||
protected final KeyDetector mKeyDetector;
|
||||
private Controller mController = EMPTY_CONTROLLER;
|
||||
protected KeyboardActionListener mListener;
|
||||
protected EmojiViewCallback mEmojiViewCallback;
|
||||
protected OnKeyEventListener mKeyEventListener;
|
||||
private int mOriginX;
|
||||
private int mOriginY;
|
||||
private Key mCurrentKey;
|
||||
|
@ -122,7 +122,7 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane
|
|||
public void showPopupKeysPanel(final View parentView, final Controller controller,
|
||||
final int pointX, final int pointY, final KeyboardActionListener listener) {
|
||||
mListener = listener;
|
||||
mEmojiViewCallback = null;
|
||||
mKeyEventListener = null;
|
||||
showPopupKeysPanelInternal(parentView, controller, pointX, pointY);
|
||||
}
|
||||
|
||||
|
@ -131,9 +131,9 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane
|
|||
*/
|
||||
@Override
|
||||
public void showPopupKeysPanel(final View parentView, final Controller controller,
|
||||
final int pointX, final int pointY, final EmojiViewCallback emojiViewCallback) {
|
||||
final int pointX, final int pointY, final OnKeyEventListener listener) {
|
||||
mListener = null;
|
||||
mEmojiViewCallback = emojiViewCallback;
|
||||
mKeyEventListener = listener;
|
||||
showPopupKeysPanelInternal(parentView, controller, pointX, pointY);
|
||||
}
|
||||
|
||||
|
@ -157,9 +157,6 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane
|
|||
|
||||
mOriginX = x + container.getPaddingLeft();
|
||||
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);
|
||||
final PopupKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate;
|
||||
if (accessibilityDelegate != null
|
||||
|
@ -225,8 +222,8 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane
|
|||
false /* isKeyRepeat */);
|
||||
}
|
||||
}
|
||||
} else if (mEmojiViewCallback != null) {
|
||||
mEmojiViewCallback.onReleaseKey(key);
|
||||
} else if (mKeyEventListener != null) {
|
||||
mKeyEventListener.onReleaseKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,4 +314,28 @@ public class PopupKeysKeyboardView extends KeyboardView implements PopupKeysPane
|
|||
}
|
||||
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,17 +8,10 @@ package helium314.keyboard.keyboard;
|
|||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import helium314.keyboard.keyboard.emoji.EmojiViewCallback;
|
||||
import helium314.keyboard.keyboard.emoji.OnKeyEventListener;
|
||||
|
||||
public interface PopupKeysPanel {
|
||||
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.
|
||||
* @param panel the panel to be shown.
|
||||
|
@ -66,18 +59,19 @@ public interface PopupKeysPanel {
|
|||
* Initializes the layout and event handling of this {@link PopupKeysPanel} and calls the
|
||||
* controller's onShowPopupKeysPanel to add the panel's container view.
|
||||
* Same as {@link PopupKeysPanel#showPopupKeysPanel(View, Controller, int, int, KeyboardActionListener)},
|
||||
* but with a {@link EmojiViewCallback}.
|
||||
* but with a {@link OnKeyEventListener}.
|
||||
*
|
||||
* @param parentView the parent view of this {@link PopupKeysPanel}
|
||||
* @param controller the controller that can dismiss this {@link PopupKeysPanel}
|
||||
* @param pointX x coordinate of this {@link PopupKeysPanel}
|
||||
* @param pointY y coordinate of this {@link PopupKeysPanel}
|
||||
* @param emojiViewCallback to receive keyboard actions from this {@link PopupKeysPanel}.
|
||||
* @param listener the listener that will receive keyboard action from this
|
||||
* {@link PopupKeysPanel}.
|
||||
*/
|
||||
// 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.
|
||||
void showPopupKeysPanel(View parentView, Controller controller, int pointX,
|
||||
int pointY, EmojiViewCallback emojiViewCallback);
|
||||
int pointY, OnKeyEventListener listener);
|
||||
|
||||
/**
|
||||
* Dismisses the popup keys panel and calls the controller's onDismissPopupKeysPanel to remove
|
||||
|
@ -133,35 +127,20 @@ public interface PopupKeysPanel {
|
|||
*/
|
||||
int translateY(int y);
|
||||
|
||||
default View getContainerView() {
|
||||
return (View) ((View) this).getParent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show this {@link PopupKeysPanel} in the parent view.
|
||||
*
|
||||
* @param parentView the {@link ViewGroup} that hosts this {@link PopupKeysPanel}.
|
||||
*/
|
||||
default void showInParent(ViewGroup parentView) {
|
||||
removeFromParent();
|
||||
parentView.addView(getContainerView());
|
||||
}
|
||||
void showInParent(ViewGroup parentView);
|
||||
|
||||
/**
|
||||
* Remove this {@link PopupKeysPanel} from the parent view.
|
||||
*/
|
||||
default void removeFromParent() {
|
||||
final View containerView = getContainerView();
|
||||
final ViewGroup currentParent = (ViewGroup)containerView.getParent();
|
||||
if (currentParent != null) {
|
||||
currentParent.removeView(containerView);
|
||||
}
|
||||
}
|
||||
void removeFromParent();
|
||||
|
||||
/**
|
||||
* Return whether the panel is currently being shown.
|
||||
*/
|
||||
default boolean isShowingInParent() {
|
||||
return getContainerView().getParent() != null;
|
||||
}
|
||||
boolean isShowingInParent();
|
||||
}
|
||||
|
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
* 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() {
|
||||
final int row = (mGridKeys.size() - 1) / getOccupiedColumnCount() + 1;
|
||||
final int row = (mGridKeys.size() - 1) / mColumnsNum + 1;
|
||||
return row * mVerticalStep;
|
||||
}
|
||||
|
||||
public int getOccupiedColumnCount() {
|
||||
return mColumnsNum - mEmptyColumnIndices.size();
|
||||
public int getColumnsCount() {
|
||||
return mColumnsNum;
|
||||
}
|
||||
|
||||
public void addPendingKey(final Key usedKey) {
|
||||
|
|
|
@ -324,7 +324,7 @@ final class EmojiCategory {
|
|||
final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs,
|
||||
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
|
||||
0, 0, ResourceUtils.getKeyboardWidth(mContext, Settings.getValues()));
|
||||
return MAX_LINE_COUNT_PER_PAGE * tempKeyboard.getOccupiedColumnCount();
|
||||
return MAX_LINE_COUNT_PER_PAGE * tempKeyboard.getColumnsCount();
|
||||
}
|
||||
|
||||
private static final Comparator<Key> EMOJI_KEY_COMPARATOR = (lhs, rhs) -> {
|
||||
|
|
|
@ -13,9 +13,6 @@ import android.graphics.PorterDuff;
|
|||
import android.graphics.PorterDuffXfermode;
|
||||
import android.os.Handler;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.widget.LinearLayout;
|
||||
import helium314.keyboard.keyboard.PopupTextView;
|
||||
import helium314.keyboard.latin.utils.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
|
@ -56,18 +53,14 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
private static final long KEY_PRESS_DELAY_TIME = 250; // msec
|
||||
private static final long KEY_RELEASE_DELAY_TIME = 30; // msec
|
||||
|
||||
private static final EmojiViewCallback EMPTY_EMOJI_VIEW_CALLBACK = new EmojiViewCallback() {
|
||||
private static final OnKeyEventListener EMPTY_LISTENER = new OnKeyEventListener() {
|
||||
@Override
|
||||
public void onPressKey(final Key key) {}
|
||||
@Override
|
||||
public void onReleaseKey(final Key key) {}
|
||||
@Override
|
||||
public String getDescription(String emoji) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
private EmojiViewCallback mEmojiViewCallback = EMPTY_EMOJI_VIEW_CALLBACK;
|
||||
private OnKeyEventListener mListener = EMPTY_LISTENER;
|
||||
private final KeyDetector mKeyDetector = new KeyDetector();
|
||||
private KeyboardAccessibilityDelegate<EmojiPageKeyboardView> mAccessibilityDelegate;
|
||||
|
||||
|
@ -81,8 +74,6 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
|
||||
// More keys keyboard
|
||||
private final View mPopupKeysKeyboardContainer;
|
||||
private final PopupTextView mDescriptionView;
|
||||
private final PopupKeysKeyboardView mPopupKeysKeyboardView;
|
||||
private final WeakHashMap<Key, Keyboard> mPopupKeysKeyboardCache = new WeakHashMap<>();
|
||||
private final boolean mConfigShowPopupKeysKeyboardAtTouchedPoint;
|
||||
private final ViewGroup mPopupKeysPlacerView;
|
||||
|
@ -111,8 +102,6 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
|
||||
final LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
mPopupKeysKeyboardContainer = inflater.inflate(popupKeysKeyboardLayoutId, null);
|
||||
mDescriptionView = mPopupKeysKeyboardContainer.findViewById(R.id.description_view);
|
||||
mPopupKeysKeyboardView = mPopupKeysKeyboardContainer.findViewById(R.id.popup_keys_keyboard_view);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -157,8 +146,8 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
}
|
||||
}
|
||||
|
||||
public void setEmojiViewCallback(final EmojiViewCallback emojiViewCallback) {
|
||||
mEmojiViewCallback = emojiViewCallback;
|
||||
public void setOnKeyEventListener(final OnKeyEventListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -180,8 +169,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
}
|
||||
|
||||
@Nullable
|
||||
private PopupKeysPanel showPopupKeysKeyboard(@NonNull final Key key) {
|
||||
mPopupKeysKeyboardView.setVisibility(GONE);
|
||||
public PopupKeysPanel showPopupKeysKeyboard(@NonNull final Key key, final int lastX, final int lastY) {
|
||||
final PopupKeySpec[] popupKeys = key.getPopupKeys();
|
||||
if (popupKeys == null) {
|
||||
return null;
|
||||
|
@ -194,9 +182,21 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
mPopupKeysKeyboardCache.put(key, popupKeysKeyboard);
|
||||
}
|
||||
|
||||
mPopupKeysKeyboardView.setKeyboard(popupKeysKeyboard);
|
||||
mPopupKeysKeyboardView.setVisibility(VISIBLE);
|
||||
return mPopupKeysKeyboardView;
|
||||
final View container = mPopupKeysKeyboardContainer;
|
||||
final PopupKeysKeyboardView popupKeysKeyboardView = container.findViewById(R.id.popup_keys_keyboard_view);
|
||||
popupKeysKeyboardView.setKeyboard(popupKeysKeyboard);
|
||||
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() {
|
||||
|
@ -209,17 +209,6 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
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
|
||||
public void onShowPopupKeysPanel(final PopupKeysPanel panel) {
|
||||
// install placer view only when needed instead of when this
|
||||
|
@ -301,11 +290,9 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
return;
|
||||
}
|
||||
|
||||
var descriptionPanel = showDescription(key);
|
||||
final PopupKeysPanel popupKeysPanel = showPopupKeysKeyboard(key);
|
||||
|
||||
final int x = mLastX;
|
||||
final int y = mLastY;
|
||||
final PopupKeysPanel popupKeysPanel = showPopupKeysKeyboard(key, x, y);
|
||||
if (popupKeysPanel != null) {
|
||||
final int translatedX = popupKeysPanel.translateX(x);
|
||||
final int translatedY = popupKeysPanel.translateY(y);
|
||||
|
@ -314,34 +301,6 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
// want any scroll to append during this entire input.
|
||||
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) {
|
||||
|
@ -359,7 +318,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
releasedKey.onReleased();
|
||||
invalidateKey(releasedKey);
|
||||
if (withKeyRegistering) {
|
||||
mEmojiViewCallback.onReleaseKey(releasedKey);
|
||||
mListener.onReleaseKey(releasedKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -367,7 +326,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
mPendingKeyDown = null;
|
||||
pressedKey.onPressed();
|
||||
invalidateKey(pressedKey);
|
||||
mEmojiViewCallback.onPressKey(pressedKey);
|
||||
mListener.onPressKey(pressedKey);
|
||||
}
|
||||
|
||||
public void releaseCurrentKey(final boolean withKeyRegistering) {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package helium314.keyboard.keyboard.emoji;
|
||||
|
||||
import helium314.keyboard.latin.utils.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -22,13 +23,13 @@ final class EmojiPalettesAdapter extends RecyclerView.Adapter<EmojiPalettesAdapt
|
|||
private static final boolean DEBUG_PAGER = false;
|
||||
|
||||
private final int mCategoryId;
|
||||
private final EmojiViewCallback mEmojiViewCallback;
|
||||
private final OnKeyEventListener mListener;
|
||||
private final EmojiCategory mEmojiCategory;
|
||||
|
||||
public EmojiPalettesAdapter(final EmojiCategory emojiCategory, int categoryId, final EmojiViewCallback emojiViewCallback) {
|
||||
public EmojiPalettesAdapter(final EmojiCategory emojiCategory, int categoryId, final OnKeyEventListener listener) {
|
||||
mEmojiCategory = emojiCategory;
|
||||
mCategoryId = categoryId;
|
||||
mEmojiViewCallback = emojiViewCallback;
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
@ -37,7 +38,6 @@ final class EmojiPalettesAdapter extends RecyclerView.Adapter<EmojiPalettesAdapt
|
|||
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||
final EmojiPageKeyboardView keyboardView = (EmojiPageKeyboardView)inflater.inflate(
|
||||
R.layout.emoji_keyboard_page, parent, false);
|
||||
keyboardView.setEmojiViewCallback(mEmojiViewCallback);
|
||||
return new ViewHolder(keyboardView);
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,7 @@ final class EmojiPalettesAdapter extends RecyclerView.Adapter<EmojiPalettesAdapt
|
|||
final Keyboard keyboard =
|
||||
mEmojiCategory.getKeyboardFromAdapterPosition(mCategoryId, position);
|
||||
holder.getKeyboardView().setKeyboard(keyboard);
|
||||
holder.getKeyboardView().setOnKeyEventListener(mListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -38,16 +38,12 @@ import helium314.keyboard.keyboard.internal.KeyDrawParams;
|
|||
import helium314.keyboard.keyboard.internal.KeyVisualAttributes;
|
||||
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode;
|
||||
import helium314.keyboard.latin.AudioAndHapticFeedbackManager;
|
||||
import helium314.keyboard.latin.DictionaryFactory;
|
||||
import helium314.keyboard.latin.R;
|
||||
import helium314.keyboard.latin.RichInputMethodManager;
|
||||
import helium314.keyboard.latin.RichInputMethodSubtype;
|
||||
import helium314.keyboard.latin.SingleDictionaryFacilitator;
|
||||
import helium314.keyboard.latin.common.ColorType;
|
||||
import helium314.keyboard.latin.common.Colors;
|
||||
import helium314.keyboard.latin.settings.Settings;
|
||||
import helium314.keyboard.latin.settings.SettingsValues;
|
||||
import helium314.keyboard.latin.utils.DictionaryInfoUtils;
|
||||
import helium314.keyboard.latin.utils.ResourceUtils;
|
||||
|
||||
import static helium314.keyboard.latin.common.Constants.NOT_A_COORDINATE;
|
||||
|
@ -64,7 +60,7 @@ import static helium314.keyboard.latin.common.Constants.NOT_A_COORDINATE;
|
|||
* Because of the above reasons, this class doesn't extend {@link KeyboardView}.
|
||||
*/
|
||||
public final class EmojiPalettesView extends LinearLayout
|
||||
implements View.OnClickListener, EmojiViewCallback {
|
||||
implements View.OnClickListener, OnKeyEventListener {
|
||||
private static final class PagerViewHolder extends RecyclerView.ViewHolder {
|
||||
private long mCategoryId;
|
||||
|
||||
|
@ -183,14 +179,15 @@ public final class EmojiPalettesView extends LinearLayout
|
|||
}
|
||||
}
|
||||
|
||||
private static SingleDictionaryFacilitator sDictionaryFacilitator;
|
||||
|
||||
private boolean initialized = false;
|
||||
private final Colors mColors;
|
||||
private final EmojiLayoutParams mEmojiLayoutParams;
|
||||
|
||||
private LinearLayout mTabStrip;
|
||||
private EmojiCategoryPageIndicatorView mEmojiCategoryPageIndicatorView;
|
||||
|
||||
private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
|
||||
|
||||
private final EmojiCategory mEmojiCategory;
|
||||
private ViewPager2 mPager;
|
||||
|
||||
|
@ -257,7 +254,9 @@ public final class EmojiPalettesView extends LinearLayout
|
|||
mEmojiLayoutParams.setEmojiListProperties(mPager);
|
||||
mEmojiCategoryPageIndicatorView = findViewById(R.id.emoji_category_page_id_view);
|
||||
mEmojiLayoutParams.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView);
|
||||
|
||||
setCurrentCategoryId(mEmojiCategory.getCurrentCategoryId(), true);
|
||||
|
||||
mEmojiCategoryPageIndicatorView.setColors(mColors.get(ColorType.EMOJI_CATEGORY_SELECTED), mColors.get(ColorType.STRIP_BACKGROUND));
|
||||
initialized = true;
|
||||
}
|
||||
|
@ -281,7 +280,8 @@ public final class EmojiPalettesView extends LinearLayout
|
|||
}
|
||||
|
||||
/**
|
||||
* Called from {@link EmojiPageKeyboardView} through {@link EmojiViewCallback}
|
||||
* Called from {@link EmojiPageKeyboardView} through
|
||||
* {@link helium314.keyboard.keyboard.emoji.OnKeyEventListener}
|
||||
* interface to handle touch events from non-View-based elements such as Emoji buttons.
|
||||
*/
|
||||
@Override
|
||||
|
@ -291,9 +291,10 @@ public final class EmojiPalettesView extends LinearLayout
|
|||
}
|
||||
|
||||
/**
|
||||
* Called from {@link EmojiPageKeyboardView} through {@link EmojiViewCallback}
|
||||
* Called from {@link EmojiPageKeyboardView} through
|
||||
* {@link helium314.keyboard.keyboard.emoji.OnKeyEventListener}
|
||||
* interface to handle touch events from non-View-based elements such as Emoji buttons.
|
||||
* This may be called without any prior call to {@link EmojiViewCallback#onPressKey(Key)}.
|
||||
* This may be called without any prior call to {@link OnKeyEventListener#onPressKey(Key)}.
|
||||
*/
|
||||
@Override
|
||||
public void onReleaseKey(final Key key) {
|
||||
|
@ -309,20 +310,6 @@ public final class EmojiPalettesView extends LinearLayout
|
|||
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) {
|
||||
if (!enabled) return;
|
||||
// TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off?
|
||||
|
@ -337,7 +324,6 @@ public final class EmojiPalettesView extends LinearLayout
|
|||
final KeyDrawParams params = new KeyDrawParams();
|
||||
params.updateParams(mEmojiLayoutParams.getBottomRowKeyboardHeight(), keyVisualAttr);
|
||||
setupSidePadding();
|
||||
initDictionaryFacilitator();
|
||||
}
|
||||
|
||||
private void addRecentKey(final Key key) {
|
||||
|
@ -444,27 +430,5 @@ public final class EmojiPalettesView extends LinearLayout
|
|||
|
||||
mEmojiCategory.clearKeyboardCache();
|
||||
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;
|
||||
|
||||
/**
|
||||
* Interface to handle callbacks from child elements
|
||||
* such as Emoji buttons and keyboard views.
|
||||
* Interface to handle touch events from non-View-based elements
|
||||
* such as Emoji buttons.
|
||||
*/
|
||||
public interface EmojiViewCallback {
|
||||
public interface OnKeyEventListener {
|
||||
|
||||
/**
|
||||
* Called when a key is pressed by the user
|
||||
|
@ -17,13 +17,8 @@ public interface EmojiViewCallback {
|
|||
|
||||
/**
|
||||
* Called when a key is released.
|
||||
* This may be called without any prior call to {@link EmojiViewCallback#onPressKey(Key)},
|
||||
* This may be called without any prior call to {@link OnKeyEventListener#onPressKey(Key)},
|
||||
* for example when a key from a popup keys keyboard is selected by releasing touch on it.
|
||||
*/
|
||||
void onReleaseKey(Key key);
|
||||
|
||||
/**
|
||||
* Called from keyboard view to get an emoji description
|
||||
*/
|
||||
String getDescription(String emoji);
|
||||
}
|
|
@ -45,7 +45,7 @@ class LocaleKeyboardInfos(dataStream: InputStream?, locale: Locale) {
|
|||
"mns" -> Key.LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO
|
||||
else -> 0
|
||||
}
|
||||
val tlds = mutableListOf(Key.POPUP_KEYS_HAS_LABELS)
|
||||
val tlds = getLocaleTlds(locale)
|
||||
|
||||
init {
|
||||
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_LABELS -> if (!onlyPopupKeys) addLabel(line.split(colonSpaceRegex, 2))
|
||||
READER_MODE_NUMBER_ROW -> localizedNumberKeys = line.splitOnWhitespace()
|
||||
READER_MODE_TLD -> tlds.addAll(SpacedTokens(line).map { ".$it" })
|
||||
READER_MODE_TLD -> SpacedTokens(line).forEach { tlds.add(".$it") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,16 +156,6 @@ 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>) {
|
||||
|
@ -215,7 +205,6 @@ 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_ALL -> lkt.addFile(context.assets.open("$LOCALE_TEXTS_FOLDER/more_popups_all.txt"), false)
|
||||
}
|
||||
lkt.addLocaleTlds(params.mId.locale)
|
||||
return lkt
|
||||
}
|
||||
|
||||
|
@ -231,6 +220,28 @@ 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()
|
||||
|
||||
// cache the texts, so they don't need to be read over and over
|
||||
|
@ -330,7 +341,7 @@ const val POPUP_KEYS_NORMAL = "normal"
|
|||
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
|
||||
private val specialCountryTlds = hashMapOf<String, String>(
|
||||
private val specialCountryTlds = listOf(
|
||||
"bd" to ".bd .com.bd",
|
||||
"bq" to ".bq .an .nl",
|
||||
"bl" to ".bl .gp .fr",
|
||||
|
@ -340,5 +351,4 @@ private val specialCountryTlds = hashMapOf<String, String>(
|
|||
"mf" to ".mf .gp .fr",
|
||||
"tl" to ".tl .tp",
|
||||
)
|
||||
private const val comTld = ".com"
|
||||
private const val otherDefaultTlds = ".gov .edu .org .net"
|
||||
private const val defaultTlds = ".com .gov .edu .org .net"
|
||||
|
|
|
@ -175,12 +175,6 @@ object KeyCode {
|
|||
const val META_LEFT = -10048
|
||||
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 */
|
||||
fun Int.checkAndConvertCode(): Int = if (this > 0) this else when (this) {
|
||||
// working
|
||||
|
@ -196,7 +190,7 @@ object KeyCode {
|
|||
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,
|
||||
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, SEND_INTENT_ONE, SEND_INTENT_TWO, SEND_INTENT_THREE,
|
||||
TIMESTAMP, CTRL_LEFT, CTRL_RIGHT, ALT_LEFT, ALT_RIGHT, META_LEFT, META_RIGHT
|
||||
-> this
|
||||
|
||||
// conversion
|
||||
|
@ -216,11 +210,66 @@ object KeyCode {
|
|||
|
||||
// todo: there are many more keys, see near https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_0
|
||||
/**
|
||||
* Convert an internal keyCode to a KeyEvent.KEYCODE_<xxx>.
|
||||
* Positive codes are passed through, unknown negative codes result in KeyEvent.KEYCODE_UNKNOWN.
|
||||
* Convert a keyCode / codePoint to a KeyEvent.KEYCODE_<xxx>.
|
||||
* Fallback to KeyEvent.KEYCODE_UNKNOWN.
|
||||
* To be uses for fake hardware key press.
|
||||
*/
|
||||
@JvmStatic fun keyCodeToKeyEventCode(keyCode: Int) = when (keyCode) {
|
||||
* */
|
||||
fun Int.toKeyEventCode(): Int = if (this > 0)
|
||||
when (this.toChar().uppercaseChar()) {
|
||||
'/' -> KeyEvent.KEYCODE_SLASH
|
||||
'\\' -> KeyEvent.KEYCODE_BACKSLASH
|
||||
';' -> KeyEvent.KEYCODE_SEMICOLON
|
||||
',' -> KeyEvent.KEYCODE_COMMA
|
||||
'.' -> KeyEvent.KEYCODE_PERIOD
|
||||
'\'' -> KeyEvent.KEYCODE_APOSTROPHE
|
||||
'`' -> KeyEvent.KEYCODE_GRAVE
|
||||
'*' -> KeyEvent.KEYCODE_STAR
|
||||
']' -> KeyEvent.KEYCODE_RIGHT_BRACKET
|
||||
'[' -> KeyEvent.KEYCODE_LEFT_BRACKET
|
||||
'+' -> KeyEvent.KEYCODE_PLUS
|
||||
'-' -> KeyEvent.KEYCODE_MINUS
|
||||
'=' -> KeyEvent.KEYCODE_EQUALS
|
||||
'\n' -> KeyEvent.KEYCODE_ENTER
|
||||
'\t' -> KeyEvent.KEYCODE_TAB
|
||||
'0' -> KeyEvent.KEYCODE_0
|
||||
'1' -> KeyEvent.KEYCODE_1
|
||||
'2' -> KeyEvent.KEYCODE_2
|
||||
'3' -> KeyEvent.KEYCODE_3
|
||||
'4' -> KeyEvent.KEYCODE_4
|
||||
'5' -> KeyEvent.KEYCODE_5
|
||||
'6' -> KeyEvent.KEYCODE_6
|
||||
'7' -> KeyEvent.KEYCODE_7
|
||||
'8' -> KeyEvent.KEYCODE_8
|
||||
'9' -> KeyEvent.KEYCODE_9
|
||||
'A' -> KeyEvent.KEYCODE_A
|
||||
'B' -> KeyEvent.KEYCODE_B
|
||||
'C' -> KeyEvent.KEYCODE_C
|
||||
'D' -> KeyEvent.KEYCODE_D
|
||||
'E' -> KeyEvent.KEYCODE_E
|
||||
'F' -> KeyEvent.KEYCODE_F
|
||||
'G' -> KeyEvent.KEYCODE_G
|
||||
'H' -> KeyEvent.KEYCODE_H
|
||||
'I' -> KeyEvent.KEYCODE_I
|
||||
'J' -> KeyEvent.KEYCODE_J
|
||||
'K' -> KeyEvent.KEYCODE_K
|
||||
'L' -> KeyEvent.KEYCODE_L
|
||||
'M' -> KeyEvent.KEYCODE_M
|
||||
'N' -> KeyEvent.KEYCODE_N
|
||||
'O' -> KeyEvent.KEYCODE_O
|
||||
'P' -> KeyEvent.KEYCODE_P
|
||||
'Q' -> KeyEvent.KEYCODE_Q
|
||||
'R' -> KeyEvent.KEYCODE_R
|
||||
'S' -> KeyEvent.KEYCODE_S
|
||||
'T' -> KeyEvent.KEYCODE_T
|
||||
'U' -> KeyEvent.KEYCODE_U
|
||||
'V' -> KeyEvent.KEYCODE_V
|
||||
'W' -> KeyEvent.KEYCODE_W
|
||||
'X' -> KeyEvent.KEYCODE_X
|
||||
'Y' -> KeyEvent.KEYCODE_Y
|
||||
'Z' -> KeyEvent.KEYCODE_Z
|
||||
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
|
||||
|
@ -254,67 +303,6 @@ object KeyCode {
|
|||
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.
|
||||
* To be uses for fake hardware key press.
|
||||
*/
|
||||
@JvmStatic fun codePointToKeyEventCode(codePoint: Int): Int = when (codePoint.toChar().uppercaseChar()) {
|
||||
'/' -> KeyEvent.KEYCODE_SLASH
|
||||
'\\' -> KeyEvent.KEYCODE_BACKSLASH
|
||||
';' -> KeyEvent.KEYCODE_SEMICOLON
|
||||
',' -> KeyEvent.KEYCODE_COMMA
|
||||
'.' -> KeyEvent.KEYCODE_PERIOD
|
||||
'\'' -> KeyEvent.KEYCODE_APOSTROPHE
|
||||
'`' -> KeyEvent.KEYCODE_GRAVE
|
||||
'*' -> KeyEvent.KEYCODE_STAR
|
||||
']' -> KeyEvent.KEYCODE_RIGHT_BRACKET
|
||||
'[' -> KeyEvent.KEYCODE_LEFT_BRACKET
|
||||
'+' -> KeyEvent.KEYCODE_PLUS
|
||||
'-' -> KeyEvent.KEYCODE_MINUS
|
||||
'=' -> KeyEvent.KEYCODE_EQUALS
|
||||
'\n' -> KeyEvent.KEYCODE_ENTER
|
||||
'\t' -> KeyEvent.KEYCODE_TAB
|
||||
'0' -> KeyEvent.KEYCODE_0
|
||||
'1' -> KeyEvent.KEYCODE_1
|
||||
'2' -> KeyEvent.KEYCODE_2
|
||||
'3' -> KeyEvent.KEYCODE_3
|
||||
'4' -> KeyEvent.KEYCODE_4
|
||||
'5' -> KeyEvent.KEYCODE_5
|
||||
'6' -> KeyEvent.KEYCODE_6
|
||||
'7' -> KeyEvent.KEYCODE_7
|
||||
'8' -> KeyEvent.KEYCODE_8
|
||||
'9' -> KeyEvent.KEYCODE_9
|
||||
'A' -> KeyEvent.KEYCODE_A
|
||||
'B' -> KeyEvent.KEYCODE_B
|
||||
'C' -> KeyEvent.KEYCODE_C
|
||||
'D' -> KeyEvent.KEYCODE_D
|
||||
'E' -> KeyEvent.KEYCODE_E
|
||||
'F' -> KeyEvent.KEYCODE_F
|
||||
'G' -> KeyEvent.KEYCODE_G
|
||||
'H' -> KeyEvent.KEYCODE_H
|
||||
'I' -> KeyEvent.KEYCODE_I
|
||||
'J' -> KeyEvent.KEYCODE_J
|
||||
'K' -> KeyEvent.KEYCODE_K
|
||||
'L' -> KeyEvent.KEYCODE_L
|
||||
'M' -> KeyEvent.KEYCODE_M
|
||||
'N' -> KeyEvent.KEYCODE_N
|
||||
'O' -> KeyEvent.KEYCODE_O
|
||||
'P' -> KeyEvent.KEYCODE_P
|
||||
'Q' -> KeyEvent.KEYCODE_Q
|
||||
'R' -> KeyEvent.KEYCODE_R
|
||||
'S' -> KeyEvent.KEYCODE_S
|
||||
'T' -> KeyEvent.KEYCODE_T
|
||||
'U' -> KeyEvent.KEYCODE_U
|
||||
'V' -> KeyEvent.KEYCODE_V
|
||||
'W' -> KeyEvent.KEYCODE_W
|
||||
'X' -> KeyEvent.KEYCODE_X
|
||||
'Y' -> KeyEvent.KEYCODE_Y
|
||||
'Z' -> KeyEvent.KEYCODE_Z
|
||||
else -> KeyEvent.KEYCODE_UNKNOWN
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import java.util.Locale;
|
|||
|
||||
import helium314.keyboard.latin.SuggestedWords.SuggestedWordInfo;
|
||||
import helium314.keyboard.latin.common.ComposedData;
|
||||
import helium314.keyboard.latin.makedict.WordProperty;
|
||||
import helium314.keyboard.latin.settings.SettingsValuesForSuggestion;
|
||||
|
||||
/**
|
||||
|
@ -178,10 +177,6 @@ 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
|
||||
* real dictionary.
|
||||
|
|
|
@ -397,10 +397,7 @@ class DictionaryFacilitatorImpl : DictionaryFacilitator {
|
|||
// 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) {
|
||||
scope.launch {
|
||||
// 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) }
|
||||
UserDictionary.Words.addWord(userDict.mContext, word, 250, null, dictionaryGroup.locale)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -734,7 +731,7 @@ private class DictionaryGroup(
|
|||
else {
|
||||
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.parentFile?.exists() == true || file.parentFile?.mkdirs() == true) file
|
||||
if (file.parentFile?.mkdirs() == true) file
|
||||
else null
|
||||
}
|
||||
|
||||
|
|
|
@ -64,26 +64,10 @@ object DictionaryFactory {
|
|||
* if the dictionary type already exists in [dicts], the [file] is skipped
|
||||
*/
|
||||
private fun checkAndAddDictionaryToListNewType(file: File, dicts: MutableList<Dictionary>, locale: Locale) {
|
||||
val dictionary = getDictionary(file, locale) ?: return
|
||||
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
|
||||
}
|
||||
if (!file.isFile) return
|
||||
val header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(file) ?: return killDictionary(file)
|
||||
val dictType = header.mIdString.split(":").first()
|
||||
if (dicts.any { it.mDictType == dictType }) return
|
||||
val readOnlyBinaryDictionary = ReadOnlyBinaryDictionary(
|
||||
file.absolutePath, 0, file.length(), false, locale, dictType
|
||||
)
|
||||
|
@ -91,13 +75,14 @@ object DictionaryFactory {
|
|||
if (readOnlyBinaryDictionary.isValidDictionary) {
|
||||
if (locale.language == "ko") {
|
||||
// Use KoreanDictionary for Korean locale
|
||||
return KoreanDictionary(readOnlyBinaryDictionary)
|
||||
dicts.add(KoreanDictionary(readOnlyBinaryDictionary))
|
||||
} else {
|
||||
dicts.add(readOnlyBinaryDictionary)
|
||||
}
|
||||
return readOnlyBinaryDictionary
|
||||
} else {
|
||||
readOnlyBinaryDictionary.close()
|
||||
killDictionary(file)
|
||||
}
|
||||
readOnlyBinaryDictionary.close()
|
||||
killDictionary(file)
|
||||
return null
|
||||
}
|
||||
|
||||
private fun killDictionary(file: File) {
|
||||
|
|
|
@ -23,7 +23,7 @@ import java.util.List;
|
|||
/**
|
||||
* A class for detecting Emoji-Alt physical key.
|
||||
*/
|
||||
public final class EmojiAltPhysicalKeyDetector {
|
||||
final class EmojiAltPhysicalKeyDetector {
|
||||
private static final String TAG = "EmojiAltPhysKeyDetector";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ package helium314.keyboard.latin;
|
|||
|
||||
import helium314.keyboard.event.HangulCombiner;
|
||||
import helium314.keyboard.latin.common.ComposedData;
|
||||
import helium314.keyboard.latin.makedict.WordProperty;
|
||||
import helium314.keyboard.latin.settings.SettingsValuesForSuggestion;
|
||||
|
||||
import java.text.Normalizer;
|
||||
|
@ -73,11 +72,6 @@ public class KoreanDictionary extends Dictionary {
|
|||
return mDictionary.getMaxFrequencyOfExactMatches(processInput(word));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WordProperty getWordProperty(String word, boolean isBeginningOfSentence) {
|
||||
return mDictionary.getWordProperty(processInput(word), isBeginningOfSentence);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean same(char[] word, int length, String typedWord) {
|
||||
word = processInput(new String(word)).toCharArray();
|
||||
|
|
|
@ -26,6 +26,7 @@ import android.os.Process;
|
|||
import android.text.InputType;
|
||||
import android.util.PrintWriterPrinter;
|
||||
import android.util.Printer;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
|
@ -50,6 +51,9 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode;
|
|||
import helium314.keyboard.latin.common.InsetsOutlineProvider;
|
||||
import helium314.keyboard.dictionarypack.DictionaryPackConstants;
|
||||
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.keyboard.Keyboard;
|
||||
import helium314.keyboard.keyboard.KeyboardId;
|
||||
|
@ -64,6 +68,7 @@ import helium314.keyboard.latin.common.InputPointers;
|
|||
import helium314.keyboard.latin.common.LocaleUtils;
|
||||
import helium314.keyboard.latin.common.ViewOutlineProviderUtilsKt;
|
||||
import helium314.keyboard.latin.define.DebugFlags;
|
||||
import helium314.keyboard.latin.define.ProductionFlags;
|
||||
import helium314.keyboard.latin.inputlogic.InputLogic;
|
||||
import helium314.keyboard.latin.personalization.PersonalizationHelper;
|
||||
import helium314.keyboard.latin.settings.Settings;
|
||||
|
@ -129,6 +134,9 @@ public class LatinIME extends InputMethodService implements
|
|||
private final DictionaryFacilitator mDictionaryFacilitator =
|
||||
DictionaryFacilitatorProvider.getDictionaryFacilitator(false);
|
||||
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}.
|
||||
private View mInputView;
|
||||
|
@ -138,6 +146,7 @@ public class LatinIME extends InputMethodService implements
|
|||
private RichInputMethodManager mRichImm;
|
||||
final KeyboardSwitcher mKeyboardSwitcher;
|
||||
private final SubtypeState mSubtypeState = new SubtypeState();
|
||||
private EmojiAltPhysicalKeyDetector mEmojiAltPhysicalKeyDetector;
|
||||
private final StatsUtilsManager mStatsUtilsManager;
|
||||
// Working variable for {@link #startShowingInputView()} and
|
||||
// {@link #onEvaluateInputViewShown()}.
|
||||
|
@ -270,7 +279,9 @@ public class LatinIME extends InputMethodService implements
|
|||
msg.arg2 /* remainingTries */, this /* handler */)) {
|
||||
// If we were able to reset the caches, then we can reload the keyboard.
|
||||
// Otherwise, we'll do it when we can.
|
||||
latinIme.mKeyboardSwitcher.reloadMainKeyboard();
|
||||
latinIme.mKeyboardSwitcher.loadKeyboard(latinIme.getCurrentInputEditorInfo(),
|
||||
settingsValues, latinIme.getCurrentAutoCapsState(),
|
||||
latinIme.getCurrentRecapitalizeState());
|
||||
}
|
||||
break;
|
||||
case MSG_WAIT_FOR_DICTIONARY_LOAD:
|
||||
|
@ -626,7 +637,6 @@ public class LatinIME extends InputMethodService implements
|
|||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
mSettings.startListener();
|
||||
KeyboardIconsSet.Companion.getInstance().loadIcons(this);
|
||||
mRichImm = RichInputMethodManager.getInstance();
|
||||
AudioAndHapticFeedbackManager.init(this);
|
||||
|
@ -654,8 +664,7 @@ public class LatinIME extends InputMethodService implements
|
|||
|
||||
final IntentFilter newDictFilter = new IntentFilter();
|
||||
newDictFilter.addAction(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION);
|
||||
// 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);
|
||||
ContextCompat.registerReceiver(this, mDictionaryPackInstallReceiver, newDictFilter, ContextCompat.RECEIVER_NOT_EXPORTED);
|
||||
|
||||
final IntentFilter dictDumpFilter = new IntentFilter();
|
||||
dictDumpFilter.addAction(DictionaryDumpBroadcastReceiver.DICTIONARY_DUMP_INTENT_ACTION);
|
||||
|
@ -1062,7 +1071,7 @@ public class LatinIME extends InputMethodService implements
|
|||
if (isDifferentTextField) {
|
||||
mainKeyboardView.closing();
|
||||
suggest.setAutoCorrectionThreshold(currentSettingsValues.mAutoCorrectionThreshold);
|
||||
switcher.reloadMainKeyboard();
|
||||
switcher.loadKeyboard(editorInfo, currentSettingsValues, getCurrentAutoCapsState(), getCurrentRecapitalizeState());
|
||||
if (needToCallLoadKeyboardLater) {
|
||||
// If we need to call loadKeyboard again later, we need to save its state now. The
|
||||
// later call will be done in #retryResetCaches.
|
||||
|
@ -1110,7 +1119,6 @@ public class LatinIME extends InputMethodService implements
|
|||
@Override
|
||||
public void onWindowHidden() {
|
||||
super.onWindowHidden();
|
||||
Log.i(TAG, "onWindowHidden");
|
||||
final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
|
||||
if (mainKeyboardView != null) {
|
||||
mainKeyboardView.closing();
|
||||
|
@ -1167,12 +1175,8 @@ public class LatinIME extends InputMethodService implements
|
|||
if (isInputViewShown()
|
||||
&& mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
|
||||
composingSpanStart, composingSpanEnd, settingsValues)) {
|
||||
// we don't want to update a manually set shift state if selection changed towards one side
|
||||
// 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());
|
||||
mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(),
|
||||
getCurrentRecapitalizeState());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1213,7 +1217,6 @@ public class LatinIME extends InputMethodService implements
|
|||
|
||||
@Override
|
||||
public void hideWindow() {
|
||||
Log.i(TAG, "hideWindow");
|
||||
if (hasSuggestionStripView() && mSettings.getCurrent().mToolbarMode == ToolbarMode.EXPANDABLE)
|
||||
mSuggestionStripView.setToolbarVisibility(false);
|
||||
mKeyboardSwitcher.onHideWindow();
|
||||
|
@ -1226,12 +1229,6 @@ public class LatinIME extends InputMethodService implements
|
|||
super.hideWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestHideSelf(int flags) {
|
||||
super.requestHideSelf(flags);
|
||||
Log.i(TAG, "requestHideSelf: " + flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayCompletions(final CompletionInfo[] applicationSpecifiedCompletions) {
|
||||
if (DebugFlags.DEBUG_ENABLED) {
|
||||
|
@ -1283,8 +1280,8 @@ public class LatinIME extends InputMethodService implements
|
|||
if (isImeSuppressedByHardwareKeyboard() && !visibleKeyboardView.isShown()) {
|
||||
// If there is a hardware keyboard and a visible software keyboard view has been hidden,
|
||||
// no visual element will be shown on the screen.
|
||||
// for some reason setting contentTopInsets and visibleTopInsets broke somewhere along the
|
||||
// way from OpenBoard to HeliBoard (GH-702, GH-1455), but not setting anything seems to work
|
||||
outInsets.contentTopInsets = inputHeight;
|
||||
outInsets.visibleTopInsets = inputHeight;
|
||||
mInsetsUpdater.setInsets(outInsets);
|
||||
return;
|
||||
}
|
||||
|
@ -1542,7 +1539,25 @@ public class LatinIME extends InputMethodService implements
|
|||
// Implementation of {@link SuggestionStripView.Listener}.
|
||||
@Override
|
||||
public void onCodeInput(final int codePoint, final int x, final int y, final boolean isKeyRepeat) {
|
||||
mKeyboardActionListener.onCodeInput(codePoint, x, y, isKeyRepeat);
|
||||
onCodeInput(codePoint, 0, 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
|
||||
|
@ -1559,6 +1574,24 @@ public class LatinIME extends InputMethodService implements
|
|||
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) {
|
||||
// TODO: have the keyboard pass the correct key code when we need it.
|
||||
final Event event = Event.createSoftwareTextEvent(rawText, KeyCode.MULTIPLE_CODE_POINTS);
|
||||
|
@ -1730,7 +1763,8 @@ public class LatinIME extends InputMethodService implements
|
|||
loadSettings();
|
||||
if (mKeyboardSwitcher.getMainKeyboardView() != null) {
|
||||
// Reload keyboard because the current language has been changed.
|
||||
mKeyboardSwitcher.reloadMainKeyboard();
|
||||
mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettings.getCurrent(),
|
||||
getCurrentAutoCapsState(), getCurrentRecapitalizeState());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1798,18 +1832,63 @@ public class LatinIME extends InputMethodService implements
|
|||
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
|
||||
@Override
|
||||
public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) {
|
||||
if (mKeyboardActionListener.onKeyDown(keyCode, keyEvent))
|
||||
if (mEmojiAltPhysicalKeyDetector == null) {
|
||||
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 super.onKeyDown(keyCode, keyEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(final int keyCode, final KeyEvent keyEvent) {
|
||||
if (mKeyboardActionListener.onKeyUp(keyCode, keyEvent))
|
||||
if (mEmojiAltPhysicalKeyDetector == null) {
|
||||
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 super.onKeyUp(keyCode, keyEvent);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import com.android.inputmethod.latin.BinaryDictionary;
|
|||
|
||||
import helium314.keyboard.latin.SuggestedWords.SuggestedWordInfo;
|
||||
import helium314.keyboard.latin.common.ComposedData;
|
||||
import helium314.keyboard.latin.makedict.WordProperty;
|
||||
import helium314.keyboard.latin.settings.SettingsValuesForSuggestion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -108,18 +107,6 @@ public final class ReadOnlyBinaryDictionary extends Dictionary {
|
|||
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
|
||||
public void close() {
|
||||
mLock.writeLock().lock();
|
||||
|
|
|
@ -439,7 +439,7 @@ public final class RichInputConnection implements PrivateCommandPerformer {
|
|||
// test for this explicitly)
|
||||
if (INVALID_CURSOR_POSITION != mExpectedSelStart
|
||||
&& (cachedLength >= n || cachedLength >= mExpectedSelStart)) {
|
||||
final StringBuilder s = new StringBuilder(mCommittedTextBeforeComposingText.toString());
|
||||
final StringBuilder s = new StringBuilder(mCommittedTextBeforeComposingText);
|
||||
// We call #toString() here to create a temporary object.
|
||||
// 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
|
||||
|
@ -716,13 +716,8 @@ public final class RichInputConnection implements PrivateCommandPerformer {
|
|||
if (start < 0 || end < 0) {
|
||||
return false;
|
||||
}
|
||||
if (start > end) {
|
||||
mExpectedSelStart = end;
|
||||
mExpectedSelEnd = start;
|
||||
} else {
|
||||
mExpectedSelStart = start;
|
||||
mExpectedSelEnd = end;
|
||||
}
|
||||
mExpectedSelStart = start;
|
||||
mExpectedSelEnd = end;
|
||||
if (isConnected()) {
|
||||
final boolean isIcValid = mIC.setSelection(start, end);
|
||||
if (!isIcValid) {
|
||||
|
|
|
@ -7,7 +7,6 @@ import helium314.keyboard.keyboard.Keyboard
|
|||
import helium314.keyboard.keyboard.KeyboardSwitcher
|
||||
import helium314.keyboard.latin.DictionaryFacilitator.DictionaryInitializationListener
|
||||
import helium314.keyboard.latin.common.ComposedData
|
||||
import helium314.keyboard.latin.makedict.WordProperty
|
||||
import helium314.keyboard.latin.settings.SettingsValuesForSuggestion
|
||||
import helium314.keyboard.latin.utils.SuggestionResults
|
||||
import java.util.Locale
|
||||
|
@ -48,8 +47,6 @@ class SingleDictionaryFacilitator(private val dict: Dictionary) : DictionaryFaci
|
|||
return suggestionResults
|
||||
}
|
||||
|
||||
fun getWordProperty(word: String): WordProperty? = dict.getWordProperty(word, false)
|
||||
|
||||
// ------------ dummy functionality ----------------
|
||||
|
||||
override fun setValidSpellingWordReadCache(cache: LruCache<String, Boolean>) {}
|
||||
|
|
|
@ -12,5 +12,3 @@ object Links {
|
|||
const val CUSTOM_LAYOUTS = "$GITHUB/discussions/categories/custom-layout"
|
||||
const val CUSTOM_COLORS = "$GITHUB/discussions/categories/custom-colors"
|
||||
}
|
||||
|
||||
val combiningRange = 0x300..0x35b
|
||||
|
|
|
@ -7,11 +7,15 @@
|
|||
package helium314.keyboard.latin.define
|
||||
|
||||
object ProductionFlags {
|
||||
const val IS_HARDWARE_KEYBOARD_SUPPORTED = true
|
||||
const val IS_HARDWARE_KEYBOARD_SUPPORTED = false
|
||||
// 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
|
||||
* [helium314.keyboard.latin.SuggestedWords.mRawSuggestions].
|
||||
*/
|
||||
const val INCLUDE_RAW_SUGGESTIONS = false
|
||||
}
|
||||
}
|
|
@ -49,7 +49,6 @@ import helium314.keyboard.latin.settings.SpacingAndPunctuations;
|
|||
import helium314.keyboard.latin.suggestions.SuggestionStripViewAccessor;
|
||||
import helium314.keyboard.latin.utils.AsyncResultHolder;
|
||||
import helium314.keyboard.latin.utils.InputTypeUtils;
|
||||
import helium314.keyboard.latin.utils.IntentUtils;
|
||||
import helium314.keyboard.latin.utils.Log;
|
||||
import helium314.keyboard.latin.utils.RecapitalizeStatus;
|
||||
import helium314.keyboard.latin.utils.ScriptUtils;
|
||||
|
@ -90,7 +89,6 @@ public final class InputLogic {
|
|||
|
||||
private int mDeleteCount;
|
||||
private long mLastKeyTime;
|
||||
// todo: this is not used, so either remove it or do something with it
|
||||
public final TreeSet<Long> mCurrentlyPressedHardwareKeys = new TreeSet<>();
|
||||
|
||||
// Keeps track of most recently inserted text (multi-character key) for reverting
|
||||
|
@ -401,11 +399,7 @@ public final class InputLogic {
|
|||
// Stop the last recapitalization, if started.
|
||||
mRecapitalizeStatus.stop();
|
||||
mWordBeingCorrectedByCursor = null;
|
||||
|
||||
// we do not return true if
|
||||
final boolean oneSidedSelectionMove = hasOrHadSelection
|
||||
&& ((oldSelEnd == newSelEnd && oldSelStart != newSelStart) || (oldSelEnd != newSelEnd && oldSelStart == newSelStart));
|
||||
return !oneSidedSelectionMove;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean moveCursorByAndReturnIfInsideComposingWord(int distance) {
|
||||
|
@ -649,8 +643,7 @@ public final class InputLogic {
|
|||
*/
|
||||
private void handleFunctionalEvent(final Event event, final InputTransaction inputTransaction,
|
||||
final String currentKeyboardScript, final LatinIME.UIHandler handler) {
|
||||
final int keyCode = event.getMKeyCode();
|
||||
switch (keyCode) {
|
||||
switch (event.getMKeyCode()) {
|
||||
case KeyCode.DELETE:
|
||||
handleBackspaceEvent(event, inputTransaction, currentKeyboardScript);
|
||||
// Backspace is a functional key, but it affects the contents of the editor.
|
||||
|
@ -693,7 +686,7 @@ public final class InputLogic {
|
|||
case KeyCode.SHIFT_ENTER:
|
||||
// todo: try using sendDownUpKeyEventWithMetaState() and remove the key code maybe
|
||||
final Event tmpEvent = Event.createSoftwareKeypressEvent(Constants.CODE_ENTER,
|
||||
keyCode, 0, event.getMX(), event.getMY(), event.isKeyRepeat());
|
||||
event.getMKeyCode(), 0, event.getMX(), event.getMY(), event.isKeyRepeat());
|
||||
handleNonSpecialCharacterEvent(tmpEvent, inputTransaction, handler);
|
||||
// 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.
|
||||
|
@ -724,43 +717,37 @@ public final class InputLogic {
|
|||
if (mConnection.hasSelection()) {
|
||||
mConnection.copyText(true);
|
||||
// fake delete keypress to remove the text
|
||||
final Event backspaceEvent = Event.createSoftwareKeypressEvent(KeyCode.DELETE, 0,
|
||||
final Event backspaceEvent = LatinIME.createSoftwareKeypressEvent(KeyCode.DELETE, 0,
|
||||
event.getMX(), event.getMY(), event.isKeyRepeat());
|
||||
handleBackspaceEvent(backspaceEvent, inputTransaction, currentKeyboardScript);
|
||||
inputTransaction.setDidAffectContents();
|
||||
}
|
||||
break;
|
||||
case KeyCode.WORD_LEFT:
|
||||
sendDownUpKeyEventWithMetaState(
|
||||
ScriptUtils.isScriptRtl(currentKeyboardScript) ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT,
|
||||
KeyEvent.META_CTRL_ON | event.getMMetaState());
|
||||
sendDownUpKeyEventWithMetaState(ScriptUtils.isScriptRtl(currentKeyboardScript)?
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.META_CTRL_ON);
|
||||
break;
|
||||
case KeyCode.WORD_RIGHT:
|
||||
sendDownUpKeyEventWithMetaState(
|
||||
ScriptUtils.isScriptRtl(currentKeyboardScript) ? KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||
KeyEvent.META_CTRL_ON | event.getMMetaState());
|
||||
sendDownUpKeyEventWithMetaState(ScriptUtils.isScriptRtl(currentKeyboardScript)?
|
||||
KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.META_CTRL_ON);
|
||||
break;
|
||||
case KeyCode.MOVE_START_OF_PAGE:
|
||||
final int selectionEnd1 = mConnection.getExpectedSelectionEnd();
|
||||
final int selectionStart1 = mConnection.getExpectedSelectionStart();
|
||||
sendDownUpKeyEventWithMetaState(KeyEvent.KEYCODE_MOVE_HOME, KeyEvent.META_CTRL_ON | event.getMMetaState());
|
||||
if (mConnection.getExpectedSelectionStart() == selectionStart1 && mConnection.getExpectedSelectionEnd() == selectionEnd1) {
|
||||
// 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);
|
||||
final int selectionEnd = mConnection.getExpectedSelectionEnd();
|
||||
sendDownUpKeyEventWithMetaState(KeyEvent.KEYCODE_MOVE_HOME, KeyEvent.META_CTRL_ON);
|
||||
if (mConnection.getExpectedSelectionStart() > 0 && mConnection.getExpectedSelectionEnd() == selectionEnd) {
|
||||
// unchanged, and we're not at the top -> try a different method (necessary for compose fields)
|
||||
mConnection.setSelection(0, 0);
|
||||
}
|
||||
break;
|
||||
case KeyCode.MOVE_END_OF_PAGE:
|
||||
final int selectionStart2 = mConnection.getExpectedSelectionStart();
|
||||
final int selectionEnd2 = mConnection.getExpectedSelectionEnd();
|
||||
sendDownUpKeyEventWithMetaState(KeyEvent.KEYCODE_MOVE_END, KeyEvent.META_CTRL_ON | event.getMMetaState());
|
||||
if (mConnection.getExpectedSelectionStart() == selectionStart2 && mConnection.getExpectedSelectionEnd() == selectionEnd2) {
|
||||
final int selectionStart = mConnection.getExpectedSelectionEnd();
|
||||
sendDownUpKeyEventWithMetaState(KeyEvent.KEYCODE_MOVE_END, KeyEvent.META_CTRL_ON);
|
||||
if (mConnection.getExpectedSelectionStart() == selectionStart) {
|
||||
// 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
|
||||
// getting the actual length of the text for setting the correct position can be tricky for some apps...
|
||||
try {
|
||||
final int newStart = (event.getMMetaState() & KeyEvent.META_SHIFT_MASK) != 0 ? selectionStart2 : Integer.MAX_VALUE;
|
||||
mConnection.setSelection(newStart, Integer.MAX_VALUE);
|
||||
mConnection.setSelection(Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||
} catch (Exception e) {
|
||||
// better catch potential errors and just do nothing in this case
|
||||
Log.i(TAG, "error when trying to move cursor to last position: " + e);
|
||||
|
@ -779,10 +766,8 @@ public final class InputLogic {
|
|||
case KeyCode.TIMESTAMP:
|
||||
mLatinIME.onTextInput(TimestampKt.getTimestamp(mLatinIME));
|
||||
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);
|
||||
mLatinIME.hideWindow();
|
||||
break;
|
||||
case KeyCode.VOICE_INPUT:
|
||||
// switching to shortcut IME, shift state, keyboard,... is handled by LatinIME,
|
||||
|
@ -792,20 +777,23 @@ public final class InputLogic {
|
|||
case KeyCode.CAPS_LOCK, KeyCode.EMOJI, KeyCode.TOGGLE_ONE_HANDED_MODE, KeyCode.SWITCH_ONE_HANDED_MODE:
|
||||
break;
|
||||
default:
|
||||
if (KeyCode.INSTANCE.isModifier(keyCode))
|
||||
return; // continuation of previous switch case above, but modifiers are held in a separate place
|
||||
final int keyEventCode = keyCode > 0
|
||||
? keyCode
|
||||
: event.getMCodePoint() >= 0 ? KeyCode.codePointToKeyEventCode(event.getMCodePoint())
|
||||
: KeyCode.keyCodeToKeyEventCode(keyCode);
|
||||
if (keyEventCode != KeyEvent.KEYCODE_UNKNOWN) {
|
||||
sendDownUpKeyEventWithMetaState(keyEventCode, event.getMMetaState());
|
||||
return;
|
||||
if (KeyCode.INSTANCE.isModifier(event.getMKeyCode()))
|
||||
return; // continuation of previous switch case, but modifiers are in a separate place
|
||||
if (event.getMMetaState() != 0) {
|
||||
// need to convert codepoint to KeyEvent.KEYCODE_<xxx>
|
||||
final int codeToConvert = event.getMKeyCode() < 0 ? event.getMKeyCode() : event.getMCodePoint();
|
||||
int keyEventCode = KeyCode.INSTANCE.toKeyEventCode(codeToConvert);
|
||||
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) {
|
||||
sendDownUpKeyEvent(keyEventCode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// unknown event
|
||||
Log.e(TAG, "unknown event, key code: "+keyCode+", meta: "+event.getMMetaState());
|
||||
if (DebugFlags.DEBUG_ENABLED)
|
||||
throw new RuntimeException("Unknown event");
|
||||
throw new RuntimeException("Unknown key code : " + event.getMKeyCode());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@ object Defaults {
|
|||
const val PREF_VIBRATE_ON = false
|
||||
const val PREF_VIBRATE_IN_DND_MODE = false
|
||||
const val PREF_SOUND_ON = false
|
||||
const val PREF_SHOW_EMOJI_DESCRIPTIONS = true
|
||||
@JvmField
|
||||
var PREF_POPUP_ON = true
|
||||
const val PREF_AUTO_CORRECTION = true
|
||||
|
|
|
@ -67,7 +67,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
|
|||
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_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_AUTO_CORRECTION = "auto_correction";
|
||||
public static final String PREF_MORE_AUTO_CORRECTION = "more_auto_correction";
|
||||
|
|
|
@ -54,7 +54,6 @@ public class SettingsValues {
|
|||
public final boolean mVibrateOn;
|
||||
public final boolean mVibrateInDndMode;
|
||||
public final boolean mSoundOn;
|
||||
public final boolean mShowEmojiDescriptions;
|
||||
public final boolean mKeyPreviewPopupOn;
|
||||
public final boolean mShowsVoiceInputKey;
|
||||
public final boolean mLanguageSwitchKeyToOtherImes;
|
||||
|
@ -170,7 +169,6 @@ public class SettingsValues {
|
|||
mVibrateOn = Settings.readVibrationEnabled(prefs);
|
||||
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);
|
||||
mShowEmojiDescriptions = prefs.getBoolean(Settings.PREF_SHOW_EMOJI_DESCRIPTIONS, Defaults.PREF_SHOW_EMOJI_DESCRIPTIONS);
|
||||
mKeyPreviewPopupOn = prefs.getBoolean(Settings.PREF_POPUP_ON, Defaults.PREF_POPUP_ON);
|
||||
mSlidingKeyInputPreviewEnabled = prefs.getBoolean(
|
||||
DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW, Defaults.PREF_SLIDING_KEY_INPUT_PREVIEW);
|
||||
|
|
|
@ -101,10 +101,6 @@ object DictionaryInfoUtils {
|
|||
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) =
|
||||
getCacheDirectoryForLocale(locale, context)?.let { File(it).listFiles() }.orEmpty()
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
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")
|
||||
}
|
||||
}
|
||||
|
|
@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.safeDrawing
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
|
@ -46,7 +45,6 @@ import androidx.compose.ui.focus.FocusRequester
|
|||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.unit.dp
|
||||
import helium314.keyboard.latin.R
|
||||
|
@ -243,8 +241,7 @@ fun ExpandableSearchField(
|
|||
}) { CloseIcon(android.R.string.cancel) } },
|
||||
singleLine = true,
|
||||
colors = colors,
|
||||
textStyle = contentTextDirectionStyle,
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search)
|
||||
textStyle = contentTextDirectionStyle
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,12 +47,12 @@ fun NewDictionaryDialog(
|
|||
} else if (header != null) {
|
||||
val ctx = LocalContext.current
|
||||
val dictLocale = header.mLocaleString.constructLocale()
|
||||
var locale by remember { mutableStateOf(mainLocale ?: dictLocale) }
|
||||
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 locales = SubtypeSettings.getAvailableSubtypeLocales()
|
||||
.filter { it.script() == dictLocale.script() || it.script() == mainLocale?.script() }
|
||||
.sortedWith(comparer)
|
||||
var locale by remember { mutableStateOf(mainLocale ?: dictLocale.takeIf { it in locales } ?: locales.first()) }
|
||||
val cacheDir = DictionaryInfoUtils.getCacheDirectoryForLocale(locale, ctx)
|
||||
val dictFile = File(cacheDir, header.mIdString.substringBefore(":") + "_" + DictionaryInfoUtils.USER_DICTIONARY_SUFFIX)
|
||||
val type = header.mIdString.substringBefore(":")
|
||||
|
|
|
@ -57,7 +57,6 @@ fun PreferencesScreen(
|
|||
Settings.PREF_SOUND_ON,
|
||||
if (prefs.getBoolean(Settings.PREF_SOUND_ON, Defaults.PREF_SOUND_ON))
|
||||
Settings.PREF_KEYPRESS_SOUND_VOLUME else null,
|
||||
Settings.PREF_SHOW_EMOJI_DESCRIPTIONS,
|
||||
R.string.settings_category_additional_keys,
|
||||
Settings.PREF_SHOW_NUMBER_ROW,
|
||||
if (SubtypeSettings.getEnabledSubtypes(true).any { it.locale().language in localesWithLocalizedNumberRow })
|
||||
|
@ -112,14 +111,6 @@ fun createPreferencesSettings(context: Context) = listOf(
|
|||
Setting(context, Settings.PREF_SOUND_ON, R.string.sound_on_keypress) {
|
||||
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,
|
||||
R.string.enable_clipboard_history, R.string.enable_clipboard_history_summary)
|
||||
{
|
||||
|
|
|
@ -10,15 +10,6 @@
|
|||
android:layout_height="wrap_content"
|
||||
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
|
||||
android:id="@+id/popup_keys_keyboard_view"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
@ -17,10 +17,6 @@
|
|||
<string name="vibrate_in_dnd_mode">Vibrate in do not disturb mode</string>
|
||||
<!-- Option to play back sound on keypress in soft keyboard -->
|
||||
<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. -->
|
||||
<string name="popup_on_keypress">Popup on keypress</string>
|
||||
<!-- Settings screen title for preferences-->
|
||||
|
|
|
@ -79,7 +79,6 @@
|
|||
kn_IN: Kannada (India)/kannada
|
||||
kn_IN: Kannada Extended (India)/kannada
|
||||
ky: Kyrgyz/russian
|
||||
la: Latin
|
||||
lg: Luganda/luganda # This is a preliminary keyboard layout.
|
||||
ln: Lingala/lingala # This is a preliminary keyboard layout.
|
||||
lo_LA: Lao (Laos)/lao
|
||||
|
@ -829,15 +828,6 @@
|
|||
android:imeSubtypeMode="keyboard"
|
||||
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"
|
||||
android:label="@string/subtype_generic"
|
||||
android:subtypeId="0x1ec2b4c9"
|
||||
|
@ -1053,7 +1043,7 @@
|
|||
android:imeSubtypeMode="keyboard"
|
||||
android:imeSubtypeExtraValue="KeyboardLayoutSet=MAIN:russian_extended,SupportTouchPositionCorrection,EmojiCapable"
|
||||
android:isAsciiCapable="false"
|
||||
|
||||
|
||||
/>
|
||||
<subtype android:icon="@drawable/ic_ime_switcher"
|
||||
android:label="@string/subtype_generic_student"
|
||||
|
|
|
@ -92,9 +92,6 @@
|
|||
<subtype
|
||||
android:label="@string/subtype_generic"
|
||||
android:subtypeLocale="kn" />
|
||||
<subtype
|
||||
android:label="@string/subtype_generic"
|
||||
android:subtypeLocale="la" />
|
||||
<subtype
|
||||
android:label="@string/subtype_generic"
|
||||
android:subtypeLocale="lb" />
|
||||
|
|
|
@ -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.09` for tablets
|
||||
* 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#L250-L282) in the section _keyLabelFlags_ for names and numeric values
|
||||
* `labelFlags`: allows specific effects, see [here](app/src/main/res/values/attrs.xml#L251-L287) 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.
|
||||
* 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,12 +106,6 @@ 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.
|
||||
* 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`
|
||||
* 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
|
||||
* You need a layout file in one of the formats above, and add it to [layouts](app/src/main/assets/layouts)
|
||||
|
|
|
@ -33,6 +33,36 @@ class EmojiData {
|
|||
}
|
||||
|
||||
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,
|
||||
// so they don't get grouped. We drop them here, to prevent each variant from being displayed separately.
|
||||
return ! hasMultipleSkinModifiers(emoji.codes)
|
||||
|
@ -88,6 +118,9 @@ class EmojiData {
|
|||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val RAW_CPS_KEYCAP_HASH = intArrayOf(0x0023, 0xFE0F, 0x20E3)
|
||||
|
||||
const val CP_NUL = 0x0000
|
||||
|
||||
private const val CP_ZWJ = 0x200D
|
||||
|
@ -103,5 +136,34 @@ class EmojiData {
|
|||
private const val CP_WHITE_HAIR = 0x1F9B3
|
||||
private const val CP_BARLD = 0x1F9B2
|
||||
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,5 +3866,31 @@ U+1F532 # black square button
|
|||
U+1F3C1 # chequered flag
|
||||
U+1F6A9 # triangular flag
|
||||
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)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue