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.graphics.drawable.Drawable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
import org.dslul.openboard.inputmethod.keyboard.internal.KeyDrawParams;
|
import org.dslul.openboard.inputmethod.keyboard.internal.KeyDrawParams;
|
||||||
import org.dslul.openboard.inputmethod.keyboard.internal.KeySpecParser;
|
import org.dslul.openboard.inputmethod.keyboard.internal.KeySpecParser;
|
||||||
import org.dslul.openboard.inputmethod.keyboard.internal.KeyStyle;
|
import org.dslul.openboard.inputmethod.keyboard.internal.KeyStyle;
|
||||||
|
@ -208,8 +209,7 @@ public class Key implements Comparable<Key> {
|
||||||
private boolean mEnabled = true;
|
private boolean mEnabled = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for a key on <code>MoreKeyKeyboard</code>, on <code>MoreSuggestions</code>,
|
* Constructor for a key on <code>MoreKeyKeyboard</code> and on <code>MoreSuggestions</code>.
|
||||||
* and in a <GridRows/>.
|
|
||||||
*/
|
*/
|
||||||
public Key(@Nullable final String label, final int iconId, final int code,
|
public Key(@Nullable final String label, final int iconId, final int code,
|
||||||
@Nullable final String outputText, @Nullable final String hintLabel,
|
@Nullable final String outputText, @Nullable final String hintLabel,
|
||||||
|
@ -241,6 +241,82 @@ public class Key implements Comparable<Key> {
|
||||||
mHashCode = computeHashCode(this);
|
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
|
* 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.
|
* specification string, Key attribute array, key style, and etc.
|
||||||
|
@ -410,9 +486,35 @@ public class Key implements Comparable<Key> {
|
||||||
* Copy constructor for DynamicGridKeyboard.GridKey.
|
* Copy constructor for DynamicGridKeyboard.GridKey.
|
||||||
*
|
*
|
||||||
* @param key the original key.
|
* @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) {
|
protected Key(@Nonnull final Key key, @Nullable final MoreKeySpec[] moreKeys,
|
||||||
this(key, key.mMoreKeys);
|
@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) {
|
private Key(@Nonnull final Key key, @Nullable final MoreKeySpec[] moreKeys) {
|
||||||
|
@ -826,6 +928,21 @@ public class Key implements Comparable<Key> {
|
||||||
return iconSet.getIconDrawable(getIconId());
|
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.
|
* Gets the width of the key in pixels, excluding the gap.
|
||||||
* @return 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.AccessibilityUtils;
|
||||||
import org.dslul.openboard.inputmethod.accessibility.MoreKeysKeyboardAccessibilityDelegate;
|
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.keyboard.internal.KeyDrawParams;
|
||||||
import org.dslul.openboard.inputmethod.latin.R;
|
import org.dslul.openboard.inputmethod.latin.R;
|
||||||
import org.dslul.openboard.inputmethod.latin.common.Constants;
|
import org.dslul.openboard.inputmethod.latin.common.Constants;
|
||||||
|
@ -44,6 +45,7 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
|
||||||
protected final KeyDetector mKeyDetector;
|
protected final KeyDetector mKeyDetector;
|
||||||
private Controller mController = EMPTY_CONTROLLER;
|
private Controller mController = EMPTY_CONTROLLER;
|
||||||
protected KeyboardActionListener mListener;
|
protected KeyboardActionListener mListener;
|
||||||
|
protected OnKeyEventListener mKeyEventListener;
|
||||||
private int mOriginX;
|
private int mOriginX;
|
||||||
private int mOriginY;
|
private int mOriginY;
|
||||||
private Key mCurrentKey;
|
private Key mCurrentKey;
|
||||||
|
@ -118,11 +120,31 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void showMoreKeysPanel(final View parentView, final Controller controller,
|
public void showMoreKeysPanel(final View parentView, final Controller controller,
|
||||||
final int pointX, final int pointY, final KeyboardActionListener listener) {
|
final int pointX, final int pointY, final KeyboardActionListener listener) {
|
||||||
mController = controller;
|
|
||||||
mListener = listener;
|
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();
|
final View container = getContainerView();
|
||||||
// The coordinates of panel's left-top corner in parentView's coordinate system.
|
// The coordinates of panel's left-top corner in parentView's coordinate system.
|
||||||
// We need to consider background drawable paddings.
|
// We need to consider background drawable paddings.
|
||||||
|
@ -193,16 +215,20 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
|
||||||
* Performs the specific action for this panel when the user presses a key on the panel.
|
* 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) {
|
protected void onKeyInput(final Key key, final int x, final int y) {
|
||||||
final int code = key.getCode();
|
if (mListener != null) {
|
||||||
if (code == Constants.CODE_OUTPUT_TEXT) {
|
final int code = key.getCode();
|
||||||
mListener.onTextInput(mCurrentKey.getOutputText());
|
if (code == Constants.CODE_OUTPUT_TEXT) {
|
||||||
} else if (code != Constants.CODE_UNSPECIFIED) {
|
mListener.onTextInput(mCurrentKey.getOutputText());
|
||||||
if (getKeyboard().hasProximityCharsCorrection(code)) {
|
} else if (code != Constants.CODE_UNSPECIFIED) {
|
||||||
mListener.onCodeInput(code, x, y, false /* isKeyRepeat */);
|
if (getKeyboard().hasProximityCharsCorrection(code)) {
|
||||||
} else {
|
mListener.onCodeInput(code, x, y, false /* isKeyRepeat */);
|
||||||
mListener.onCodeInput(code, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
|
} else {
|
||||||
false /* isKeyRepeat */);
|
mListener.onCodeInput(code, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
|
||||||
|
false /* isKeyRepeat */);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else if (mKeyEventListener != null) {
|
||||||
|
mKeyEventListener.onReleaseKey(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.dslul.openboard.inputmethod.keyboard;
|
||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import org.dslul.openboard.inputmethod.keyboard.emoji.OnKeyEventListener;
|
||||||
|
|
||||||
public interface MoreKeysPanel {
|
public interface MoreKeysPanel {
|
||||||
interface Controller {
|
interface Controller {
|
||||||
|
@ -63,6 +64,25 @@ public interface MoreKeysPanel {
|
||||||
void showMoreKeysPanel(View parentView, Controller controller, int pointX,
|
void showMoreKeysPanel(View parentView, Controller controller, int pointX,
|
||||||
int pointY, KeyboardActionListener listener);
|
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
|
* Dismisses the more keys panel and calls the controller's onDismissMoreKeysPanel to remove
|
||||||
* the panel's container view.
|
* 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.Key;
|
||||||
import org.dslul.openboard.inputmethod.keyboard.Keyboard;
|
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.settings.Settings;
|
||||||
import org.dslul.openboard.inputmethod.latin.utils.JsonUtils;
|
import org.dslul.openboard.inputmethod.latin.utils.JsonUtils;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -105,7 +108,16 @@ final class DynamicGridKeyboard extends Keyboard {
|
||||||
}
|
}
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
mCachedGridKeys = null;
|
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)) {
|
while (mGridKeys.remove(key)) {
|
||||||
// Remove duplicate keys.
|
// Remove duplicate keys.
|
||||||
}
|
}
|
||||||
|
@ -227,8 +239,9 @@ final class DynamicGridKeyboard extends Keyboard {
|
||||||
private int mCurrentX;
|
private int mCurrentX;
|
||||||
private int mCurrentY;
|
private int mCurrentY;
|
||||||
|
|
||||||
public GridKey(final Key originalKey) {
|
public GridKey(@Nonnull final Key originalKey, @Nullable final MoreKeySpec[] moreKeys,
|
||||||
super(originalKey);
|
@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) {
|
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;
|
package org.dslul.openboard.inputmethod.keyboard.emoji;
|
||||||
|
|
||||||
import android.content.Context;
|
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.os.Handler;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.GestureDetector;
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.view.accessibility.AccessibilityEvent;
|
import android.view.accessibility.AccessibilityEvent;
|
||||||
|
|
||||||
|
import android.widget.FrameLayout;
|
||||||
import org.dslul.openboard.inputmethod.accessibility.AccessibilityUtils;
|
import org.dslul.openboard.inputmethod.accessibility.AccessibilityUtils;
|
||||||
import org.dslul.openboard.inputmethod.accessibility.KeyboardAccessibilityDelegate;
|
import org.dslul.openboard.inputmethod.accessibility.KeyboardAccessibilityDelegate;
|
||||||
import org.dslul.openboard.inputmethod.keyboard.Key;
|
import org.dslul.openboard.inputmethod.keyboard.Key;
|
||||||
import org.dslul.openboard.inputmethod.keyboard.KeyDetector;
|
import org.dslul.openboard.inputmethod.keyboard.KeyDetector;
|
||||||
import org.dslul.openboard.inputmethod.keyboard.Keyboard;
|
import org.dslul.openboard.inputmethod.keyboard.Keyboard;
|
||||||
import org.dslul.openboard.inputmethod.keyboard.KeyboardView;
|
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.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.
|
* 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.
|
// TODO: Implement key popup preview.
|
||||||
final class EmojiPageKeyboardView extends KeyboardView implements
|
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_PRESS_DELAY_TIME = 250; // msec
|
||||||
private static final long KEY_RELEASE_DELAY_TIME = 30; // msec
|
private static final long KEY_RELEASE_DELAY_TIME = 30; // msec
|
||||||
|
|
||||||
public interface OnKeyEventListener {
|
|
||||||
void onPressKey(Key key);
|
|
||||||
void onReleaseKey(Key key);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final OnKeyEventListener EMPTY_LISTENER = new OnKeyEventListener() {
|
private static final OnKeyEventListener EMPTY_LISTENER = new OnKeyEventListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onPressKey(final Key key) {}
|
public void onPressKey(final Key key) {}
|
||||||
|
@ -55,9 +70,25 @@ final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
|
|
||||||
private OnKeyEventListener mListener = EMPTY_LISTENER;
|
private OnKeyEventListener mListener = EMPTY_LISTENER;
|
||||||
private final KeyDetector mKeyDetector = new KeyDetector();
|
private final KeyDetector mKeyDetector = new KeyDetector();
|
||||||
private final GestureDetector mGestureDetector;
|
|
||||||
private KeyboardAccessibilityDelegate<EmojiPageKeyboardView> mAccessibilityDelegate;
|
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) {
|
public EmojiPageKeyboardView(final Context context, final AttributeSet attrs) {
|
||||||
this(context, attrs, R.attr.keyboardViewStyle);
|
this(context, attrs, R.attr.keyboardViewStyle);
|
||||||
}
|
}
|
||||||
|
@ -65,9 +96,74 @@ final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
public EmojiPageKeyboardView(final Context context, final AttributeSet attrs,
|
public EmojiPageKeyboardView(final Context context, final AttributeSet attrs,
|
||||||
final int defStyle) {
|
final int defStyle) {
|
||||||
super(context, attrs, defStyle);
|
super(context, attrs, defStyle);
|
||||||
mGestureDetector = new GestureDetector(context, this);
|
|
||||||
mGestureDetector.setIsLongpressEnabled(false /* isLongpressEnabled */);
|
|
||||||
mHandler = new Handler();
|
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) {
|
public void setOnKeyEventListener(final OnKeyEventListener listener) {
|
||||||
|
@ -81,6 +177,7 @@ final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
public void setKeyboard(final Keyboard keyboard) {
|
public void setKeyboard(final Keyboard keyboard) {
|
||||||
super.setKeyboard(keyboard);
|
super.setKeyboard(keyboard);
|
||||||
mKeyDetector.setKeyboard(keyboard, 0 /* correctionX */, 0 /* correctionY */);
|
mKeyDetector.setKeyboard(keyboard, 0 /* correctionX */, 0 /* correctionY */);
|
||||||
|
mMoreKeysKeyboardCache.clear();
|
||||||
if (AccessibilityUtils.Companion.getInstance().isAccessibilityEnabled()) {
|
if (AccessibilityUtils.Companion.getInstance().isAccessibilityEnabled()) {
|
||||||
if (mAccessibilityDelegate == null) {
|
if (mAccessibilityDelegate == null) {
|
||||||
mAccessibilityDelegate = new KeyboardAccessibilityDelegate<>(this, mKeyDetector);
|
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
|
@Override
|
||||||
public boolean dispatchPopulateAccessibilityEvent(final AccessibilityEvent event) {
|
public boolean dispatchPopulateAccessibilityEvent(final AccessibilityEvent event) {
|
||||||
// Don't populate accessibility event with all Emoji keys.
|
// Don't populate accessibility event with all Emoji keys.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getLongPressTimeout() {
|
||||||
|
return Settings.getInstance().getCurrent().mKeyLongpressTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -116,28 +283,66 @@ final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouchEvent(final MotionEvent e) {
|
public boolean onTouchEvent(final MotionEvent e) {
|
||||||
if (mGestureDetector.onTouchEvent(e)) {
|
switch (e.getActionMasked()) {
|
||||||
return true;
|
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 getKey(final int x, final int y) {
|
||||||
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);
|
|
||||||
return mKeyDetector.detectHitKey(x, 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) {
|
void callListenerOnReleaseKey(final Key releasedKey, final boolean withKeyRegistering) {
|
||||||
releasedKey.onReleased();
|
releasedKey.onReleased();
|
||||||
invalidateKey(releasedKey);
|
invalidateKey(releasedKey);
|
||||||
|
@ -164,40 +369,48 @@ final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
mCurrentKey = null;
|
mCurrentKey = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void cancelLongPress() {
|
||||||
|
mHandler.removeCallbacks(mPendingLongPress);
|
||||||
|
mPendingLongPress = null;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean onDown(final MotionEvent e) {
|
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 */);
|
releaseCurrentKey(false /* withKeyRegistering */);
|
||||||
mCurrentKey = key;
|
mCurrentKey = key;
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Do not trigger key-down effect right now in case this is actually a fling action.
|
registerPress(key);
|
||||||
mPendingKeyDown = new Runnable() {
|
|
||||||
@Override
|
registerLongPress(key);
|
||||||
public void run() {
|
|
||||||
callListenerOnPressKey(key);
|
mLastX = x;
|
||||||
}
|
mLastY = y;
|
||||||
};
|
return true;
|
||||||
mHandler.postDelayed(mPendingKeyDown, KEY_PRESS_DELAY_TIME);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean onUp(final MotionEvent e) {
|
||||||
public void onShowPress(final MotionEvent e) {
|
final int x = (int) e.getX();
|
||||||
// User feedback is done at {@link #onDown(MotionEvent)}.
|
final int y = (int) e.getY();
|
||||||
}
|
final Key key = getKey(x, y);
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onSingleTapUp(final MotionEvent e) {
|
|
||||||
final Key key = getKey(e);
|
|
||||||
final Runnable pendingKeyDown = mPendingKeyDown;
|
final Runnable pendingKeyDown = mPendingKeyDown;
|
||||||
final Key currentKey = mCurrentKey;
|
final Key currentKey = mCurrentKey;
|
||||||
releaseCurrentKey(false /* withKeyRegistering */);
|
releaseCurrentKey(false /* withKeyRegistering */);
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
return false;
|
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();
|
pendingKeyDown.run();
|
||||||
// Trigger key-release event a little later so that a user can see visual feedback.
|
// Trigger key-release event a little later so that a user can see visual feedback.
|
||||||
mHandler.postDelayed(new Runnable() {
|
mHandler.postDelayed(new Runnable() {
|
||||||
|
@ -209,25 +422,47 @@ final class EmojiPageKeyboardView extends KeyboardView implements
|
||||||
} else {
|
} else {
|
||||||
callListenerOnReleaseKey(key, true /* withRegistering */);
|
callListenerOnReleaseKey(key, true /* withRegistering */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancelLongPress();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean onCancel(final MotionEvent e) {
|
||||||
public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX,
|
|
||||||
final float distanceY) {
|
|
||||||
releaseCurrentKey(false /* withKeyRegistering */);
|
releaseCurrentKey(false /* withKeyRegistering */);
|
||||||
return false;
|
dismissMoreKeysPanel();
|
||||||
|
cancelLongPress();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean onMove(final MotionEvent e) {
|
||||||
public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX,
|
final int x = (int)e.getX();
|
||||||
final float velocityY) {
|
final int y = (int)e.getY();
|
||||||
releaseCurrentKey(false /* withKeyRegistering */);
|
final Key key = getKey(x, y);
|
||||||
return false;
|
final boolean isShowingMoreKeysPanel = isShowingMoreKeysPanel();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
// Touched key has changed, release previous key's callbacks and
|
||||||
public void onLongPress(final MotionEvent e) {
|
// re-register them for the new key.
|
||||||
// Long press detection of {@link #mGestureDetector} is disabled and not used.
|
if (key != mCurrentKey && !isShowingMoreKeysPanel) {
|
||||||
|
releaseCurrentKey(false /* withKeyRegistering */);
|
||||||
|
mCurrentKey = key;
|
||||||
|
if (key == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
registerPress(key);
|
||||||
|
|
||||||
|
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 String TAG = EmojiPalettesAdapter.class.getSimpleName();
|
||||||
private static final boolean DEBUG_PAGER = false;
|
private static final boolean DEBUG_PAGER = false;
|
||||||
|
|
||||||
private final EmojiPageKeyboardView.OnKeyEventListener mListener;
|
private final OnKeyEventListener mListener;
|
||||||
private final DynamicGridKeyboard mRecentsKeyboard;
|
private final DynamicGridKeyboard mRecentsKeyboard;
|
||||||
private final SparseArray<EmojiPageKeyboardView> mActiveKeyboardViews = new SparseArray<>();
|
private final SparseArray<EmojiPageKeyboardView> mActiveKeyboardViews = new SparseArray<>();
|
||||||
private final EmojiCategory mEmojiCategory;
|
private final EmojiCategory mEmojiCategory;
|
||||||
private int mActivePosition = 0;
|
private int mActivePosition = 0;
|
||||||
|
|
||||||
public EmojiPalettesAdapter(final EmojiCategory emojiCategory,
|
public EmojiPalettesAdapter(final EmojiCategory emojiCategory,
|
||||||
final EmojiPageKeyboardView.OnKeyEventListener listener) {
|
final OnKeyEventListener listener) {
|
||||||
mEmojiCategory = emojiCategory;
|
mEmojiCategory = emojiCategory;
|
||||||
mListener = listener;
|
mListener = listener;
|
||||||
mRecentsKeyboard = mEmojiCategory.getKeyboard(EmojiCategory.ID_RECENTS, 0);
|
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
|
public final class EmojiPalettesView extends LinearLayout
|
||||||
implements OnTabChangeListener, View.OnClickListener, View.OnTouchListener,
|
implements OnTabChangeListener, View.OnClickListener, View.OnTouchListener,
|
||||||
EmojiPageKeyboardView.OnKeyEventListener{
|
OnKeyEventListener{
|
||||||
private final int mFunctionalKeyBackgroundId;
|
private final int mFunctionalKeyBackgroundId;
|
||||||
private final int mSpacebarBackgroundId;
|
private final int mSpacebarBackgroundId;
|
||||||
private final boolean mCategoryIndicatorEnabled;
|
private final boolean mCategoryIndicatorEnabled;
|
||||||
|
@ -325,7 +325,7 @@ public final class EmojiPalettesView extends LinearLayout
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from {@link EmojiPageKeyboardView} through
|
* 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.
|
* interface to handle touch events from non-View-based elements such as Emoji buttons.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -336,8 +336,9 @@ public final class EmojiPalettesView extends LinearLayout
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from {@link EmojiPageKeyboardView} through
|
* 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.
|
* 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
|
@Override
|
||||||
public void onReleaseKey(final Key key) {
|
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);
|
R.styleable.Keyboard_GridRows_codesArray, 0);
|
||||||
final int textsArrayId = gridRowAttr.getResourceId(
|
final int textsArrayId = gridRowAttr.getResourceId(
|
||||||
R.styleable.Keyboard_GridRows_textsArray, 0);
|
R.styleable.Keyboard_GridRows_textsArray, 0);
|
||||||
|
final int moreCodesArrayId = gridRowAttr.getResourceId(
|
||||||
|
R.styleable.Keyboard_GridRows_moreCodesArray, 0);
|
||||||
gridRowAttr.recycle();
|
gridRowAttr.recycle();
|
||||||
if (codesArrayId == 0 && textsArrayId == 0) {
|
if (codesArrayId == 0 && textsArrayId == 0) {
|
||||||
throw new XmlParseUtils.ParseException(
|
throw new XmlParseUtils.ParseException(
|
||||||
|
@ -412,9 +414,19 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
||||||
throw new XmlParseUtils.ParseException(
|
throw new XmlParseUtils.ParseException(
|
||||||
"Both codesArray and textsArray attributes specifed", parser);
|
"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(
|
final String[] array = mResources.getStringArray(
|
||||||
codesArrayId != 0 ? codesArrayId : textsArrayId);
|
codesArrayId != 0 ? codesArrayId : textsArrayId);
|
||||||
|
final String[] arrayMore = moreCodesArrayId != 0 ?
|
||||||
|
mResources.getStringArray(moreCodesArrayId) : null;
|
||||||
final int counts = array.length;
|
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 float keyWidth = gridRows.getKeyWidth(null, 0.0f);
|
||||||
final int numColumns = (int)(mParams.mOccupiedWidth / keyWidth);
|
final int numColumns = (int)(mParams.mOccupiedWidth / keyWidth);
|
||||||
for (int index = 0; index < counts; index += numColumns) {
|
for (int index = 0; index < counts; index += numColumns) {
|
||||||
|
@ -429,6 +441,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
||||||
final int code;
|
final int code;
|
||||||
final String outputText;
|
final String outputText;
|
||||||
final int supportedMinSdkVersion;
|
final int supportedMinSdkVersion;
|
||||||
|
final String moreKeySpecs;
|
||||||
if (codesArrayId != 0) {
|
if (codesArrayId != 0) {
|
||||||
final String codeArraySpec = array[i];
|
final String codeArraySpec = array[i];
|
||||||
label = CodesArrayParser.parseLabel(codeArraySpec);
|
label = CodesArrayParser.parseLabel(codeArraySpec);
|
||||||
|
@ -436,6 +449,8 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
||||||
outputText = CodesArrayParser.parseOutputText(codeArraySpec);
|
outputText = CodesArrayParser.parseOutputText(codeArraySpec);
|
||||||
supportedMinSdkVersion =
|
supportedMinSdkVersion =
|
||||||
CodesArrayParser.getMinSupportSdkVersion(codeArraySpec);
|
CodesArrayParser.getMinSupportSdkVersion(codeArraySpec);
|
||||||
|
moreKeySpecs = MoreCodesArrayParser.parseKeySpecs(
|
||||||
|
arrayMore != null ? arrayMore[i] : null);
|
||||||
} else {
|
} else {
|
||||||
final String textArraySpec = array[i];
|
final String textArraySpec = array[i];
|
||||||
// TODO: Utilize KeySpecParser or write more generic TextsArrayParser.
|
// TODO: Utilize KeySpecParser or write more generic TextsArrayParser.
|
||||||
|
@ -443,6 +458,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
||||||
code = Constants.CODE_OUTPUT_TEXT;
|
code = Constants.CODE_OUTPUT_TEXT;
|
||||||
outputText = textArraySpec + (char)Constants.CODE_SPACE;
|
outputText = textArraySpec + (char)Constants.CODE_SPACE;
|
||||||
supportedMinSdkVersion = 0;
|
supportedMinSdkVersion = 0;
|
||||||
|
moreKeySpecs = null;
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT < supportedMinSdkVersion) {
|
if (Build.VERSION.SDK_INT < supportedMinSdkVersion) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -454,9 +470,10 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
|
||||||
final int y = row.getKeyY();
|
final int y = row.getKeyY();
|
||||||
final int width = (int)keyWidth;
|
final int width = (int)keyWidth;
|
||||||
final int height = row.getRowHeight();
|
final int height = row.getRowHeight();
|
||||||
final Key key = new Key(label, KeyboardIconsSet.ICON_UNDEFINED, code, outputText,
|
final String hintLabel = moreKeySpecs != null ? "\u25E5" : null;
|
||||||
null /* hintLabel */, labelFlags, backgroundType, x, y, width, height,
|
final KeyboardParams params = mParams;
|
||||||
mParams.mHorizontalGap, mParams.mVerticalGap);
|
final Key key = new Key(label, code, outputText, hintLabel, moreKeySpecs,
|
||||||
|
labelFlags, backgroundType, x, y, width, height, params);
|
||||||
endKey(key);
|
endKey(key);
|
||||||
row.advanceXPos(keyWidth);
|
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">
|
<declare-styleable name="Keyboard_GridRows">
|
||||||
<attr name="codesArray" format="reference" />
|
<attr name="codesArray" format="reference" />
|
||||||
<attr name="textsArray" format="reference" />
|
<attr name="textsArray" format="reference" />
|
||||||
|
<attr name="moreCodesArray" format="reference" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
<declare-styleable name="Keyboard_Key">
|
<declare-styleable name="Keyboard_Key">
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue