mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-06-17 11:59:34 +00:00
Extended 'more keys' support to emoji keyboards
This commit is contained in:
parent
c30fce58c4
commit
2079be88d3
11 changed files with 596 additions and 84 deletions
|
@ -22,6 +22,7 @@ import android.graphics.Typeface;
|
|||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import android.util.Log;
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.KeyDrawParams;
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.KeySpecParser;
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.KeyStyle;
|
||||
|
@ -208,8 +209,7 @@ public class Key implements Comparable<Key> {
|
|||
private boolean mEnabled = true;
|
||||
|
||||
/**
|
||||
* Constructor for a key on <code>MoreKeyKeyboard</code>, on <code>MoreSuggestions</code>,
|
||||
* and in a <GridRows/>.
|
||||
* Constructor for a key on <code>MoreKeyKeyboard</code> and on <code>MoreSuggestions</code>.
|
||||
*/
|
||||
public Key(@Nullable final String label, final int iconId, final int code,
|
||||
@Nullable final String outputText, @Nullable final String hintLabel,
|
||||
|
@ -241,6 +241,82 @@ public class Key implements Comparable<Key> {
|
|||
mHashCode = computeHashCode(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a key in a <GridRows/>.
|
||||
*/
|
||||
public Key(@Nullable final String label, final int code, @Nullable final String outputText,
|
||||
@Nullable final String hintLabel, @Nullable final String moreKeySpecs,
|
||||
final int labelFlags, final int backgroundType, final int x, final int y,
|
||||
final int width, final int height, final KeyboardParams params) {
|
||||
mWidth = width - params.mHorizontalGap;
|
||||
mHeight = height - params.mVerticalGap;
|
||||
mHorizontalGap = params.mHorizontalGap;
|
||||
mVerticalGap = params.mVerticalGap;
|
||||
mHintLabel = hintLabel;
|
||||
mLabelFlags = labelFlags;
|
||||
mBackgroundType = backgroundType;
|
||||
|
||||
if (moreKeySpecs != null) {
|
||||
String[] moreKeys = MoreKeySpec.splitKeySpecs(moreKeySpecs);
|
||||
// Get maximum column order number and set a relevant mode value.
|
||||
int moreKeysColumnAndFlags = MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER
|
||||
| params.mMaxMoreKeysKeyboardColumn;
|
||||
int value;
|
||||
if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) {
|
||||
// Override with fixed column order number and set a relevant mode value.
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_AUTO_ORDER
|
||||
| (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
}
|
||||
if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) {
|
||||
// Override with fixed column order number and set a relevant mode value.
|
||||
moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER
|
||||
| (value & MORE_KEYS_COLUMN_NUMBER_MASK);
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_HAS_LABELS)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_HAS_LABELS;
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NEEDS_DIVIDERS)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NEEDS_DIVIDERS;
|
||||
}
|
||||
if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NO_PANEL_AUTO_MORE_KEY)) {
|
||||
moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY;
|
||||
}
|
||||
mMoreKeysColumnAndFlags = moreKeysColumnAndFlags;
|
||||
|
||||
moreKeys = MoreKeySpec.insertAdditionalMoreKeys(moreKeys, null);
|
||||
int actionFlags = 0;
|
||||
if (moreKeys != null) {
|
||||
actionFlags |= ACTION_FLAGS_ENABLE_LONG_PRESS;
|
||||
mMoreKeys = new MoreKeySpec[moreKeys.length];
|
||||
for (int i = 0; i < moreKeys.length; i++) {
|
||||
mMoreKeys[i] = new MoreKeySpec(moreKeys[i], false, Locale.getDefault());
|
||||
}
|
||||
} else {
|
||||
mMoreKeys = null;
|
||||
}
|
||||
mActionFlags = actionFlags;
|
||||
} else {
|
||||
// TODO: Pass keyActionFlags as an argument.
|
||||
mActionFlags = ACTION_FLAGS_NO_KEY_PREVIEW;
|
||||
mMoreKeys = null;
|
||||
mMoreKeysColumnAndFlags = 0;
|
||||
}
|
||||
|
||||
mLabel = label;
|
||||
mOptionalAttributes = OptionalAttributes.newInstance(outputText, CODE_UNSPECIFIED,
|
||||
ICON_UNDEFINED, 0 /* visualInsetsLeft */, 0 /* visualInsetsRight */);
|
||||
mCode = code;
|
||||
mEnabled = (code != CODE_UNSPECIFIED);
|
||||
mIconId = KeyboardIconsSet.ICON_UNDEFINED;
|
||||
// Horizontal gap is divided equally to both sides of the key.
|
||||
mX = x + mHorizontalGap / 2;
|
||||
mY = y;
|
||||
mHitBox.set(x, y, x + width + 1, y + height);
|
||||
mKeyVisualAttributes = null;
|
||||
|
||||
mHashCode = computeHashCode(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a key with the given top-left coordinate and extract its attributes from a key
|
||||
* specification string, Key attribute array, key style, and etc.
|
||||
|
@ -410,9 +486,35 @@ public class Key implements Comparable<Key> {
|
|||
* Copy constructor for DynamicGridKeyboard.GridKey.
|
||||
*
|
||||
* @param key the original key.
|
||||
* @param moreKeys the more keys that should be assigned to this key.
|
||||
* @param labelHint the label hint that should be assigned to this key.
|
||||
* @param backgroundType the background type that should be assigned to this key.
|
||||
*/
|
||||
protected Key(@Nonnull final Key key) {
|
||||
this(key, key.mMoreKeys);
|
||||
protected Key(@Nonnull final Key key, @Nullable final MoreKeySpec[] moreKeys,
|
||||
@Nullable final String labelHint, final int backgroundType) {
|
||||
// Final attributes.
|
||||
mCode = key.mCode;
|
||||
mLabel = key.mLabel;
|
||||
mHintLabel = labelHint;
|
||||
mLabelFlags = key.mLabelFlags;
|
||||
mIconId = key.mIconId;
|
||||
mWidth = key.mWidth;
|
||||
mHeight = key.mHeight;
|
||||
mHorizontalGap = key.mHorizontalGap;
|
||||
mVerticalGap = key.mVerticalGap;
|
||||
mX = key.mX;
|
||||
mY = key.mY;
|
||||
mHitBox.set(key.mHitBox);
|
||||
mMoreKeys = moreKeys;
|
||||
mMoreKeysColumnAndFlags = key.mMoreKeysColumnAndFlags;
|
||||
mBackgroundType = backgroundType;
|
||||
mActionFlags = key.mActionFlags;
|
||||
mKeyVisualAttributes = key.mKeyVisualAttributes;
|
||||
mOptionalAttributes = key.mOptionalAttributes;
|
||||
mHashCode = key.mHashCode;
|
||||
// Key state.
|
||||
mPressed = key.mPressed;
|
||||
mEnabled = key.mEnabled;
|
||||
}
|
||||
|
||||
private Key(@Nonnull final Key key, @Nullable final MoreKeySpec[] moreKeys) {
|
||||
|
@ -826,6 +928,21 @@ public class Key implements Comparable<Key> {
|
|||
return iconSet.getIconDrawable(getIconId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the background type of this key.
|
||||
* @return Background type.
|
||||
* @see Key#BACKGROUND_TYPE_EMPTY
|
||||
* @see Key#BACKGROUND_TYPE_NORMAL
|
||||
* @see Key#BACKGROUND_TYPE_FUNCTIONAL
|
||||
* @see Key#BACKGROUND_TYPE_STICKY_OFF
|
||||
* @see Key#BACKGROUND_TYPE_STICKY_ON
|
||||
* @see Key#BACKGROUND_TYPE_ACTION
|
||||
* @see Key#BACKGROUND_TYPE_SPACEBAR
|
||||
*/
|
||||
public int getBackgroundType() {
|
||||
return mBackgroundType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the width of the key in pixels, excluding the gap.
|
||||
* @return The width of the key in pixels, excluding the gap.
|
||||
|
|
|
@ -28,6 +28,7 @@ import android.view.ViewGroup;
|
|||
|
||||
import org.dslul.openboard.inputmethod.accessibility.AccessibilityUtils;
|
||||
import org.dslul.openboard.inputmethod.accessibility.MoreKeysKeyboardAccessibilityDelegate;
|
||||
import org.dslul.openboard.inputmethod.keyboard.emoji.OnKeyEventListener;
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.KeyDrawParams;
|
||||
import org.dslul.openboard.inputmethod.latin.R;
|
||||
import org.dslul.openboard.inputmethod.latin.common.Constants;
|
||||
|
@ -44,6 +45,7 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
|
|||
protected final KeyDetector mKeyDetector;
|
||||
private Controller mController = EMPTY_CONTROLLER;
|
||||
protected KeyboardActionListener mListener;
|
||||
protected OnKeyEventListener mKeyEventListener;
|
||||
private int mOriginX;
|
||||
private int mOriginY;
|
||||
private Key mCurrentKey;
|
||||
|
@ -118,11 +120,31 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void showMoreKeysPanel(final View parentView, final Controller controller,
|
||||
final int pointX, final int pointY, final KeyboardActionListener listener) {
|
||||
mController = controller;
|
||||
mListener = listener;
|
||||
mKeyEventListener = null;
|
||||
showMoreKeysPanelInternal(parentView, controller, pointX, pointY);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void showMoreKeysPanel(final View parentView, final Controller controller,
|
||||
final int pointX, final int pointY, final OnKeyEventListener listener) {
|
||||
mListener = null;
|
||||
mKeyEventListener = listener;
|
||||
showMoreKeysPanelInternal(parentView, controller, pointX, pointY);
|
||||
}
|
||||
|
||||
private void showMoreKeysPanelInternal(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.
|
||||
|
@ -193,6 +215,7 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
|
|||
* Performs the specific action for this panel when the user presses a key on the panel.
|
||||
*/
|
||||
protected void onKeyInput(final Key key, final int x, final int y) {
|
||||
if (mListener != null) {
|
||||
final int code = key.getCode();
|
||||
if (code == Constants.CODE_OUTPUT_TEXT) {
|
||||
mListener.onTextInput(mCurrentKey.getOutputText());
|
||||
|
@ -204,6 +227,9 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
|
|||
false /* isKeyRepeat */);
|
||||
}
|
||||
}
|
||||
} else if (mKeyEventListener != null) {
|
||||
mKeyEventListener.onReleaseKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
private Key detectKey(int x, int y) {
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.dslul.openboard.inputmethod.keyboard;
|
|||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import org.dslul.openboard.inputmethod.keyboard.emoji.OnKeyEventListener;
|
||||
|
||||
public interface MoreKeysPanel {
|
||||
interface Controller {
|
||||
|
@ -63,6 +64,25 @@ public interface MoreKeysPanel {
|
|||
void showMoreKeysPanel(View parentView, Controller controller, int pointX,
|
||||
int pointY, KeyboardActionListener listener);
|
||||
|
||||
/**
|
||||
*
|
||||
* Initializes the layout and event handling of this {@link MoreKeysPanel} and calls the
|
||||
* controller's onShowMoreKeysPanel to add the panel's container view.
|
||||
* Same as {@link MoreKeysPanel#showMoreKeysPanel(View, Controller, int, int, KeyboardActionListener)},
|
||||
* but with a {@link OnKeyEventListener}.
|
||||
*
|
||||
* @param parentView the parent view of this {@link MoreKeysPanel}
|
||||
* @param controller the controller that can dismiss this {@link MoreKeysPanel}
|
||||
* @param pointX x coordinate of this {@link MoreKeysPanel}
|
||||
* @param pointY y coordinate of this {@link MoreKeysPanel}
|
||||
* @param listener the listener that will receive keyboard action from this
|
||||
* {@link MoreKeysPanel}.
|
||||
*/
|
||||
// TODO: Currently the MoreKeysPanel is inside a container view that is added to the parent.
|
||||
// Consider the simpler approach of placing the MoreKeysPanel itself into the parent view.
|
||||
void showMoreKeysPanel(View parentView, Controller controller, int pointX,
|
||||
int pointY, OnKeyEventListener listener);
|
||||
|
||||
/**
|
||||
* Dismisses the more keys panel and calls the controller's onDismissMoreKeysPanel to remove
|
||||
* the panel's container view.
|
||||
|
|
|
@ -22,9 +22,12 @@ import android.util.Log;
|
|||
|
||||
import org.dslul.openboard.inputmethod.keyboard.Key;
|
||||
import org.dslul.openboard.inputmethod.keyboard.Keyboard;
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.MoreKeySpec;
|
||||
import org.dslul.openboard.inputmethod.latin.settings.Settings;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.JsonUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
@ -105,7 +108,16 @@ final class DynamicGridKeyboard extends Keyboard {
|
|||
}
|
||||
synchronized (mLock) {
|
||||
mCachedGridKeys = null;
|
||||
final GridKey key = new GridKey(usedKey);
|
||||
// When a key is added to recents keyboard, we don't want to keep its more keys
|
||||
// neither its hint label. Also, we make sure its background type is matching our keyboard
|
||||
// if key comes from another keyboard (ie. a {@link MoreKeysKeyboard}).
|
||||
final boolean dropMoreKeys = mIsRecents;
|
||||
// Check if hint was a more emoji indicator and prevent its copy if more keys aren't copied
|
||||
final boolean dropHintLabel = dropMoreKeys && "\u25E5".equals(usedKey.getHintLabel());
|
||||
final GridKey key = new GridKey(usedKey,
|
||||
dropMoreKeys ? null : usedKey.getMoreKeys(),
|
||||
dropHintLabel ? null : usedKey.getHintLabel(),
|
||||
mIsRecents ? Key.BACKGROUND_TYPE_EMPTY : usedKey.getBackgroundType());
|
||||
while (mGridKeys.remove(key)) {
|
||||
// Remove duplicate keys.
|
||||
}
|
||||
|
@ -227,8 +239,9 @@ final class DynamicGridKeyboard extends Keyboard {
|
|||
private int mCurrentX;
|
||||
private int mCurrentY;
|
||||
|
||||
public GridKey(final Key originalKey) {
|
||||
super(originalKey);
|
||||
public GridKey(@Nonnull final Key originalKey, @Nullable final MoreKeySpec[] moreKeys,
|
||||
@Nullable final String labelHint, final int backgroundType) {
|
||||
super(originalKey, moreKeys, labelHint, backgroundType);
|
||||
}
|
||||
|
||||
public void updateCoordinates(final int x0, final int y0, final int x1, final int y1) {
|
||||
|
|
|
@ -17,19 +17,37 @@
|
|||
package org.dslul.openboard.inputmethod.keyboard.emoji;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.os.Handler;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.GestureDetector;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import android.widget.FrameLayout;
|
||||
import org.dslul.openboard.inputmethod.accessibility.AccessibilityUtils;
|
||||
import org.dslul.openboard.inputmethod.accessibility.KeyboardAccessibilityDelegate;
|
||||
import org.dslul.openboard.inputmethod.keyboard.Key;
|
||||
import org.dslul.openboard.inputmethod.keyboard.KeyDetector;
|
||||
import org.dslul.openboard.inputmethod.keyboard.Keyboard;
|
||||
import org.dslul.openboard.inputmethod.keyboard.KeyboardView;
|
||||
import org.dslul.openboard.inputmethod.keyboard.MoreKeysKeyboard;
|
||||
import org.dslul.openboard.inputmethod.keyboard.MoreKeysKeyboardView;
|
||||
import org.dslul.openboard.inputmethod.keyboard.MoreKeysPanel;
|
||||
import org.dslul.openboard.inputmethod.keyboard.internal.MoreKeySpec;
|
||||
import org.dslul.openboard.inputmethod.latin.R;
|
||||
import org.dslul.openboard.inputmethod.latin.common.CoordinateUtils;
|
||||
import org.dslul.openboard.inputmethod.latin.settings.Settings;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/**
|
||||
* This is an extended {@link KeyboardView} class that hosts an emoji page keyboard.
|
||||
|
@ -37,15 +55,12 @@ import org.dslul.openboard.inputmethod.latin.R;
|
|||
*/
|
||||
// TODO: Implement key popup preview.
|
||||
final class EmojiPageKeyboardView extends KeyboardView implements
|
||||
GestureDetector.OnGestureListener {
|
||||
MoreKeysPanel.Controller {
|
||||
private static final String TAG = "EmojiPageKeyboardView";
|
||||
private static final boolean LOG = true;
|
||||
private static final long KEY_PRESS_DELAY_TIME = 250; // msec
|
||||
private static final long KEY_RELEASE_DELAY_TIME = 30; // msec
|
||||
|
||||
public interface OnKeyEventListener {
|
||||
void onPressKey(Key key);
|
||||
void onReleaseKey(Key key);
|
||||
}
|
||||
|
||||
private static final OnKeyEventListener EMPTY_LISTENER = new OnKeyEventListener() {
|
||||
@Override
|
||||
public void onPressKey(final Key key) {}
|
||||
|
@ -55,9 +70,25 @@ final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
|
||||
private OnKeyEventListener mListener = EMPTY_LISTENER;
|
||||
private final KeyDetector mKeyDetector = new KeyDetector();
|
||||
private final GestureDetector mGestureDetector;
|
||||
private KeyboardAccessibilityDelegate<EmojiPageKeyboardView> mAccessibilityDelegate;
|
||||
|
||||
// Touch inputs
|
||||
private int mPointerId = MotionEvent.INVALID_POINTER_ID;
|
||||
private int mLastX, mLastY;
|
||||
private Key mCurrentKey;
|
||||
private Runnable mPendingKeyDown;
|
||||
private Runnable mPendingLongPress;
|
||||
private final Handler mHandler;
|
||||
|
||||
// More keys keyboard
|
||||
private final View mMoreKeysKeyboardContainer;
|
||||
private final WeakHashMap<Key, Keyboard> mMoreKeysKeyboardCache = new WeakHashMap<>();
|
||||
private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
|
||||
private final ViewGroup mMoreKeysPlacerView;
|
||||
// More keys panel (used by more keys keyboard view)
|
||||
// TODO: Consider extending to support multiple more keys panels
|
||||
private MoreKeysPanel mMoreKeysPanel;
|
||||
|
||||
public EmojiPageKeyboardView(final Context context, final AttributeSet attrs) {
|
||||
this(context, attrs, R.attr.keyboardViewStyle);
|
||||
}
|
||||
|
@ -65,9 +96,74 @@ final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
public EmojiPageKeyboardView(final Context context, final AttributeSet attrs,
|
||||
final int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
mGestureDetector = new GestureDetector(context, this);
|
||||
mGestureDetector.setIsLongpressEnabled(false /* isLongpressEnabled */);
|
||||
mHandler = new Handler();
|
||||
|
||||
mMoreKeysPlacerView = new FrameLayout(context, attrs);
|
||||
|
||||
final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs,
|
||||
R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
|
||||
final int moreKeysKeyboardLayoutId = keyboardViewAttr.getResourceId(
|
||||
R.styleable.MainKeyboardView_moreKeysKeyboardLayout, 0);
|
||||
mConfigShowMoreKeysKeyboardAtTouchedPoint = keyboardViewAttr.getBoolean(
|
||||
R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
|
||||
keyboardViewAttr.recycle();
|
||||
|
||||
final LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
mMoreKeysKeyboardContainer = inflater.inflate(moreKeysKeyboardLayoutId, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
|
||||
super.setHardwareAcceleratedDrawingEnabled(enabled);
|
||||
if (!enabled) return;
|
||||
final Paint layerPaint = new Paint();
|
||||
layerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
|
||||
mMoreKeysPlacerView.setLayerType(LAYER_TYPE_HARDWARE, layerPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
installMoreKeysPlacerView();
|
||||
}
|
||||
|
||||
private void installMoreKeysPlacerView() {
|
||||
final View rootView = getRootView();
|
||||
if (rootView == null) {
|
||||
Log.w(TAG, "Cannot find root view");
|
||||
return;
|
||||
}
|
||||
final ViewGroup windowContentView = rootView.findViewById(android.R.id.content);
|
||||
// Note: It'd be very weird if we get null by android.R.id.content.
|
||||
if (windowContentView == null) {
|
||||
Log.w(TAG, "Cannot find android.R.id.content view to add DrawingPreviewPlacerView");
|
||||
return;
|
||||
}
|
||||
|
||||
windowContentView.addView(mMoreKeysPlacerView);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
mMoreKeysPlacerView.removeAllViews();
|
||||
uninstallMoreKeysPlacerView();
|
||||
}
|
||||
|
||||
private void uninstallMoreKeysPlacerView() {
|
||||
final View rootView = getRootView();
|
||||
if (rootView == null) {
|
||||
Log.w(TAG, "Cannot find root view");
|
||||
return;
|
||||
}
|
||||
final ViewGroup windowContentView = rootView.findViewById(android.R.id.content);
|
||||
// Note: It'd be very weird if we get null by android.R.id.content.
|
||||
if (windowContentView == null) {
|
||||
Log.w(TAG, "Cannot find android.R.id.content view to add DrawingPreviewPlacerView");
|
||||
return;
|
||||
}
|
||||
|
||||
windowContentView.removeView(mMoreKeysPlacerView);
|
||||
}
|
||||
|
||||
public void setOnKeyEventListener(final OnKeyEventListener listener) {
|
||||
|
@ -81,6 +177,7 @@ final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
public void setKeyboard(final Keyboard keyboard) {
|
||||
super.setKeyboard(keyboard);
|
||||
mKeyDetector.setKeyboard(keyboard, 0 /* correctionX */, 0 /* correctionY */);
|
||||
mMoreKeysKeyboardCache.clear();
|
||||
if (AccessibilityUtils.Companion.getInstance().isAccessibilityEnabled()) {
|
||||
if (mAccessibilityDelegate == null) {
|
||||
mAccessibilityDelegate = new KeyboardAccessibilityDelegate<>(this, mKeyDetector);
|
||||
|
@ -91,12 +188,82 @@ final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MoreKeysPanel showMoreKeysKeyboard(@Nonnull final Key key, final int lastX, final int lastY) {
|
||||
final MoreKeySpec[] moreKeys = key.getMoreKeys();
|
||||
if (moreKeys == null) {
|
||||
return null;
|
||||
}
|
||||
Keyboard moreKeysKeyboard = mMoreKeysKeyboardCache.get(key);
|
||||
if (moreKeysKeyboard == null) {
|
||||
final MoreKeysKeyboard.Builder builder = new MoreKeysKeyboard.Builder(
|
||||
getContext(), key, getKeyboard(),
|
||||
true, key.getWidth(), key.getHeight(), // TODO This is cheating
|
||||
newLabelPaint(key));
|
||||
moreKeysKeyboard = builder.build();
|
||||
mMoreKeysKeyboardCache.put(key, moreKeysKeyboard);
|
||||
}
|
||||
|
||||
final View container = mMoreKeysKeyboardContainer;
|
||||
final MoreKeysKeyboardView moreKeysKeyboardView =
|
||||
container.findViewById(R.id.more_keys_keyboard_view);
|
||||
moreKeysKeyboardView.setKeyboard(moreKeysKeyboard);
|
||||
container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
|
||||
final int[] lastCoords = CoordinateUtils.newCoordinateArray(1, lastX, lastY);
|
||||
// The more keys keyboard is usually horizontally aligned with the center of the parent key.
|
||||
// If showMoreKeysKeyboardAtTouchedPoint 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 = mConfigShowMoreKeysKeyboardAtTouchedPoint
|
||||
? CoordinateUtils.x(lastCoords)
|
||||
: key.getX() + key.getWidth() / 2;
|
||||
final int pointY = key.getY();
|
||||
moreKeysKeyboardView.showMoreKeysPanel(this, this,
|
||||
pointX, pointY, mListener);
|
||||
return moreKeysKeyboardView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowMoreKeysPanel(final MoreKeysPanel panel) {
|
||||
// Dismiss another {@link MoreKeysPanel} that may be being showed.
|
||||
onDismissMoreKeysPanel();
|
||||
panel.showInParent(mMoreKeysPlacerView);
|
||||
mMoreKeysPanel = panel;
|
||||
}
|
||||
|
||||
public boolean isShowingMoreKeysPanel() {
|
||||
return mMoreKeysPanel != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelMoreKeysPanel() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismissMoreKeysPanel() {
|
||||
if (isShowingMoreKeysPanel()) {
|
||||
mMoreKeysPanel.removeFromParent();
|
||||
mMoreKeysPanel = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void dismissMoreKeysPanel() {
|
||||
if (isShowingMoreKeysPanel()) {
|
||||
mMoreKeysPanel.dismissMoreKeysPanel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchPopulateAccessibilityEvent(final AccessibilityEvent event) {
|
||||
// Don't populate accessibility event with all Emoji keys.
|
||||
return true;
|
||||
}
|
||||
|
||||
private int getLongPressTimeout() {
|
||||
return Settings.getInstance().getCurrent().mKeyLongpressTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -116,28 +283,66 @@ final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
*/
|
||||
@Override
|
||||
public boolean onTouchEvent(final MotionEvent e) {
|
||||
if (mGestureDetector.onTouchEvent(e)) {
|
||||
return true;
|
||||
switch (e.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mPointerId = e.getPointerId(0);
|
||||
return onDown(e);
|
||||
case MotionEvent.ACTION_UP:
|
||||
return onUp(e);
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
return onMove(e);
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
return onCancel(e);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
final Key key = getKey(e);
|
||||
if (key != null && key != mCurrentKey) {
|
||||
releaseCurrentKey(false /* withKeyRegistering */);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// {@link GestureEnabler#OnGestureListener} methods.
|
||||
private Key mCurrentKey;
|
||||
private Runnable mPendingKeyDown;
|
||||
private final Handler mHandler;
|
||||
|
||||
private Key getKey(final MotionEvent e) {
|
||||
final int index = e.getActionIndex();
|
||||
final int x = (int)e.getX(index);
|
||||
final int y = (int)e.getY(index);
|
||||
private Key getKey(final int x, final int y) {
|
||||
return mKeyDetector.detectHitKey(x, y);
|
||||
}
|
||||
|
||||
private void onLongPressed(final Key key) {
|
||||
if (isShowingMoreKeysPanel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == null) {
|
||||
if (LOG) Log.d(TAG, "Long press ignored because detected key is null");
|
||||
return;
|
||||
}
|
||||
|
||||
final int x = mLastX;
|
||||
final int y = mLastY;
|
||||
final MoreKeysPanel moreKeysPanel = showMoreKeysKeyboard(key, x, y);
|
||||
if (moreKeysPanel != null) {
|
||||
final int translatedX = moreKeysPanel.translateX(x);
|
||||
final int translatedY = moreKeysPanel.translateY(y);
|
||||
moreKeysPanel.onDownEvent(translatedX, translatedY, mPointerId, 0 /* nor used for now */);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerPress(final Key key) {
|
||||
// Do not trigger key-down effect right now in case this is actually a fling action.
|
||||
mPendingKeyDown = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callListenerOnPressKey(key);
|
||||
}
|
||||
};
|
||||
mHandler.postDelayed(mPendingKeyDown, KEY_PRESS_DELAY_TIME);
|
||||
}
|
||||
|
||||
private void registerLongPress(final Key key) {
|
||||
mPendingLongPress = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
onLongPressed(key);
|
||||
}
|
||||
};
|
||||
mHandler.postDelayed(mPendingLongPress, getLongPressTimeout());
|
||||
}
|
||||
|
||||
void callListenerOnReleaseKey(final Key releasedKey, final boolean withKeyRegistering) {
|
||||
releasedKey.onReleased();
|
||||
invalidateKey(releasedKey);
|
||||
|
@ -164,40 +369,48 @@ final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
mCurrentKey = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelLongPress() {
|
||||
mHandler.removeCallbacks(mPendingLongPress);
|
||||
mPendingLongPress = null;
|
||||
}
|
||||
|
||||
public boolean onDown(final MotionEvent e) {
|
||||
final Key key = getKey(e);
|
||||
final int x = (int) e.getX();
|
||||
final int y = (int) e.getY();
|
||||
final Key key = getKey(x, y);
|
||||
releaseCurrentKey(false /* withKeyRegistering */);
|
||||
mCurrentKey = key;
|
||||
if (key == null) {
|
||||
return false;
|
||||
}
|
||||
// Do not trigger key-down effect right now in case this is actually a fling action.
|
||||
mPendingKeyDown = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callListenerOnPressKey(key);
|
||||
}
|
||||
};
|
||||
mHandler.postDelayed(mPendingKeyDown, KEY_PRESS_DELAY_TIME);
|
||||
return false;
|
||||
registerPress(key);
|
||||
|
||||
registerLongPress(key);
|
||||
|
||||
mLastX = x;
|
||||
mLastY = y;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowPress(final MotionEvent e) {
|
||||
// User feedback is done at {@link #onDown(MotionEvent)}.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapUp(final MotionEvent e) {
|
||||
final Key key = getKey(e);
|
||||
public boolean onUp(final MotionEvent e) {
|
||||
final int x = (int) e.getX();
|
||||
final int y = (int) e.getY();
|
||||
final Key key = getKey(x, y);
|
||||
final Runnable pendingKeyDown = mPendingKeyDown;
|
||||
final Key currentKey = mCurrentKey;
|
||||
releaseCurrentKey(false /* withKeyRegistering */);
|
||||
if (key == null) {
|
||||
return false;
|
||||
}
|
||||
if (key == currentKey && pendingKeyDown != null) {
|
||||
|
||||
final boolean isShowingMoreKeysPanel = isShowingMoreKeysPanel();
|
||||
if (isShowingMoreKeysPanel) {
|
||||
final long eventTime = e.getEventTime();
|
||||
final int translatedX = mMoreKeysPanel.translateX(x);
|
||||
final int translatedY = mMoreKeysPanel.translateY(y);
|
||||
mMoreKeysPanel.onUpEvent(translatedX, translatedY, mPointerId, eventTime);
|
||||
dismissMoreKeysPanel();
|
||||
} else if (key == currentKey && pendingKeyDown != null) {
|
||||
pendingKeyDown.run();
|
||||
// Trigger key-release event a little later so that a user can see visual feedback.
|
||||
mHandler.postDelayed(new Runnable() {
|
||||
|
@ -209,25 +422,47 @@ final class EmojiPageKeyboardView extends KeyboardView implements
|
|||
} else {
|
||||
callListenerOnReleaseKey(key, true /* withRegistering */);
|
||||
}
|
||||
|
||||
cancelLongPress();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX,
|
||||
final float distanceY) {
|
||||
public boolean onCancel(final MotionEvent e) {
|
||||
releaseCurrentKey(false /* withKeyRegistering */);
|
||||
return false;
|
||||
dismissMoreKeysPanel();
|
||||
cancelLongPress();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX,
|
||||
final float velocityY) {
|
||||
public boolean onMove(final MotionEvent e) {
|
||||
final int x = (int)e.getX();
|
||||
final int y = (int)e.getY();
|
||||
final Key key = getKey(x, y);
|
||||
final boolean isShowingMoreKeysPanel = isShowingMoreKeysPanel();
|
||||
|
||||
// Touched key has changed, release previous key's callbacks and
|
||||
// re-register them for the new key.
|
||||
if (key != mCurrentKey && !isShowingMoreKeysPanel) {
|
||||
releaseCurrentKey(false /* withKeyRegistering */);
|
||||
mCurrentKey = key;
|
||||
if (key == null) {
|
||||
return false;
|
||||
}
|
||||
registerPress(key);
|
||||
|
||||
@Override
|
||||
public void onLongPress(final MotionEvent e) {
|
||||
// Long press detection of {@link #mGestureDetector} is disabled and not used.
|
||||
cancelLongPress();
|
||||
registerLongPress(key);
|
||||
}
|
||||
|
||||
if (isShowingMoreKeysPanel) {
|
||||
final long eventTime = e.getEventTime();
|
||||
final int translatedX = mMoreKeysPanel.translateX(x);
|
||||
final int translatedY = mMoreKeysPanel.translateY(y);
|
||||
mMoreKeysPanel.onMoveEvent(translatedX, translatedY, mPointerId, eventTime);
|
||||
}
|
||||
|
||||
mLastX = x;
|
||||
mLastY = y;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,14 +34,14 @@ final class EmojiPalettesAdapter extends RecyclerView.Adapter<EmojiPalettesAdapt
|
|||
private static final String TAG = EmojiPalettesAdapter.class.getSimpleName();
|
||||
private static final boolean DEBUG_PAGER = false;
|
||||
|
||||
private final EmojiPageKeyboardView.OnKeyEventListener mListener;
|
||||
private final OnKeyEventListener mListener;
|
||||
private final DynamicGridKeyboard mRecentsKeyboard;
|
||||
private final SparseArray<EmojiPageKeyboardView> mActiveKeyboardViews = new SparseArray<>();
|
||||
private final EmojiCategory mEmojiCategory;
|
||||
private int mActivePosition = 0;
|
||||
|
||||
public EmojiPalettesAdapter(final EmojiCategory emojiCategory,
|
||||
final EmojiPageKeyboardView.OnKeyEventListener listener) {
|
||||
final OnKeyEventListener listener) {
|
||||
mEmojiCategory = emojiCategory;
|
||||
mListener = listener;
|
||||
mRecentsKeyboard = mEmojiCategory.getKeyboard(EmojiCategory.ID_RECENTS, 0);
|
||||
|
|
|
@ -66,7 +66,7 @@ import static org.dslul.openboard.inputmethod.latin.common.Constants.NOT_A_COORD
|
|||
*/
|
||||
public final class EmojiPalettesView extends LinearLayout
|
||||
implements OnTabChangeListener, View.OnClickListener, View.OnTouchListener,
|
||||
EmojiPageKeyboardView.OnKeyEventListener{
|
||||
OnKeyEventListener{
|
||||
private final int mFunctionalKeyBackgroundId;
|
||||
private final int mSpacebarBackgroundId;
|
||||
private final boolean mCategoryIndicatorEnabled;
|
||||
|
@ -325,7 +325,7 @@ public final class EmojiPalettesView extends LinearLayout
|
|||
|
||||
/**
|
||||
* Called from {@link EmojiPageKeyboardView} through
|
||||
* {@link org.dslul.openboard.inputmethod.keyboard.emoji.EmojiPageKeyboardView.OnKeyEventListener}
|
||||
* {@link org.dslul.openboard.inputmethod.keyboard.emoji.OnKeyEventListener}
|
||||
* interface to handle touch events from non-View-based elements such as Emoji buttons.
|
||||
*/
|
||||
@Override
|
||||
|
@ -336,8 +336,9 @@ public final class EmojiPalettesView extends LinearLayout
|
|||
|
||||
/**
|
||||
* Called from {@link EmojiPageKeyboardView} through
|
||||
* {@link org.dslul.openboard.inputmethod.keyboard.emoji.EmojiPageKeyboardView.OnKeyEventListener}
|
||||
* {@link org.dslul.openboard.inputmethod.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 OnKeyEventListener#onPressKey(Key)}.
|
||||
*/
|
||||
@Override
|
||||
public void onReleaseKey(final Key key) {
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package org.dslul.openboard.inputmethod.keyboard.emoji;
|
||||
|
||||
import org.dslul.openboard.inputmethod.keyboard.Key;
|
||||
|
||||
/**
|
||||
* Interface to handle touch events from non-View-based elements
|
||||
* such as Emoji buttons.
|
||||
*/
|
||||
public interface OnKeyEventListener {
|
||||
|
||||
/**
|
||||
* Called when a key is pressed by the user
|
||||
*/
|
||||
void onPressKey(Key key);
|
||||
|
||||
/**
|
||||
* Called when a key is released.
|
||||
* This may be called without any prior call to {@link OnKeyEventListener#onPressKey(Key)},
|
||||
* for example when a key from a more keys keyboard is selected by releasing touch on it.
|
||||
*/
|
||||
void onReleaseKey(Key key);
|
||||
}
|
|
@ -403,6 +403,8 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
R.styleable.Keyboard_GridRows_codesArray, 0);
|
||||
final int textsArrayId = gridRowAttr.getResourceId(
|
||||
R.styleable.Keyboard_GridRows_textsArray, 0);
|
||||
final int moreCodesArrayId = gridRowAttr.getResourceId(
|
||||
R.styleable.Keyboard_GridRows_moreCodesArray, 0);
|
||||
gridRowAttr.recycle();
|
||||
if (codesArrayId == 0 && textsArrayId == 0) {
|
||||
throw new XmlParseUtils.ParseException(
|
||||
|
@ -412,9 +414,19 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
throw new XmlParseUtils.ParseException(
|
||||
"Both codesArray and textsArray attributes specifed", parser);
|
||||
}
|
||||
if (textsArrayId != 0 && moreCodesArrayId != 0) {
|
||||
throw new XmlParseUtils.ParseException(
|
||||
"moreCodesArray is not compatible with textsArray", parser);
|
||||
}
|
||||
final String[] array = mResources.getStringArray(
|
||||
codesArrayId != 0 ? codesArrayId : textsArrayId);
|
||||
final String[] arrayMore = moreCodesArrayId != 0 ?
|
||||
mResources.getStringArray(moreCodesArrayId) : null;
|
||||
final int counts = array.length;
|
||||
if (arrayMore != null && counts != arrayMore.length) {
|
||||
throw new XmlParseUtils.ParseException(
|
||||
"Inconsistent array size between codesArray and moreKeysArray", parser);
|
||||
}
|
||||
final float keyWidth = gridRows.getKeyWidth(null, 0.0f);
|
||||
final int numColumns = (int)(mParams.mOccupiedWidth / keyWidth);
|
||||
for (int index = 0; index < counts; index += numColumns) {
|
||||
|
@ -429,6 +441,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
final int code;
|
||||
final String outputText;
|
||||
final int supportedMinSdkVersion;
|
||||
final String moreKeySpecs;
|
||||
if (codesArrayId != 0) {
|
||||
final String codeArraySpec = array[i];
|
||||
label = CodesArrayParser.parseLabel(codeArraySpec);
|
||||
|
@ -436,6 +449,8 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
outputText = CodesArrayParser.parseOutputText(codeArraySpec);
|
||||
supportedMinSdkVersion =
|
||||
CodesArrayParser.getMinSupportSdkVersion(codeArraySpec);
|
||||
moreKeySpecs = MoreCodesArrayParser.parseKeySpecs(
|
||||
arrayMore != null ? arrayMore[i] : null);
|
||||
} else {
|
||||
final String textArraySpec = array[i];
|
||||
// TODO: Utilize KeySpecParser or write more generic TextsArrayParser.
|
||||
|
@ -443,6 +458,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
code = Constants.CODE_OUTPUT_TEXT;
|
||||
outputText = textArraySpec + (char)Constants.CODE_SPACE;
|
||||
supportedMinSdkVersion = 0;
|
||||
moreKeySpecs = null;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT < supportedMinSdkVersion) {
|
||||
continue;
|
||||
|
@ -454,9 +470,10 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
|||
final int y = row.getKeyY();
|
||||
final int width = (int)keyWidth;
|
||||
final int height = row.getRowHeight();
|
||||
final Key key = new Key(label, KeyboardIconsSet.ICON_UNDEFINED, code, outputText,
|
||||
null /* hintLabel */, labelFlags, backgroundType, x, y, width, height,
|
||||
mParams.mHorizontalGap, mParams.mVerticalGap);
|
||||
final String hintLabel = moreKeySpecs != null ? "\u25E5" : null;
|
||||
final KeyboardParams params = mParams;
|
||||
final Key key = new Key(label, code, outputText, hintLabel, moreKeySpecs,
|
||||
labelFlags, backgroundType, x, y, width, height, params);
|
||||
endKey(key);
|
||||
row.advanceXPos(keyWidth);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dslul.openboard.inputmethod.keyboard.internal;
|
||||
|
||||
import org.dslul.openboard.inputmethod.latin.common.StringUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The string parser of moreCodesArray specification for <GridRows />. The attribute moreCodesArray is an
|
||||
* array of string.
|
||||
* The more codes array specification is semicolon separated "codes array specification" each of which represents one
|
||||
* "more key".
|
||||
* Each element of the array defines a sequence of key labels specified as hexadecimal strings
|
||||
* representing code points separated by a vertical bar.
|
||||
*
|
||||
*/
|
||||
public final class MoreCodesArrayParser {
|
||||
// Constants for parsing.
|
||||
private static final char SEMICOLON = ';';
|
||||
private static final String SEMICOLON_REGEX = StringUtils.newSingleCodePointString(SEMICOLON);
|
||||
|
||||
private MoreCodesArrayParser() {
|
||||
// This utility class is not publicly instantiable.
|
||||
}
|
||||
|
||||
public static String parseKeySpecs(@Nullable String codeArraySpecs) {
|
||||
if (codeArraySpecs == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (final String codeArraySpec : codeArraySpecs.split(SEMICOLON_REGEX)) {
|
||||
final String label = CodesArrayParser.parseLabel(codeArraySpec);
|
||||
final String outputText = CodesArrayParser.parseOutputText(codeArraySpec);
|
||||
|
||||
sb.append(label).append("|").append(outputText);
|
||||
sb.append(",");
|
||||
}
|
||||
|
||||
// Remove last comma
|
||||
if (sb.length() > 0) sb.deleteCharAt(sb.length() - 1);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -291,6 +291,7 @@
|
|||
<declare-styleable name="Keyboard_GridRows">
|
||||
<attr name="codesArray" format="reference" />
|
||||
<attr name="textsArray" format="reference" />
|
||||
<attr name="moreCodesArray" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="Keyboard_Key">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue