Merge "Introduce Backspace implemenation for flag emoji sequence."

This commit is contained in:
TreeHugger Robot
2017-02-16 05:00:44 +00:00
committed by Android (Google) Code Review
5 changed files with 127 additions and 49 deletions

View File

@@ -164,6 +164,8 @@ public class Emoji {
public static int VARIATION_SELECTOR_16 = 0xFE0F;
public static int CANCEL_TAG = 0xE007F;
// Returns true if the given code point is regional indicator symbol.
public static boolean isRegionalIndicatorSymbol(int codepoint) {
return 0x1F1E6 <= codepoint && codepoint <= 0x1F1FF;
@@ -188,4 +190,13 @@ public class Emoji {
public static boolean isKeycapBase(int codePoint) {
return ('0' <= codePoint && codePoint <= '9') || codePoint == '#' || codePoint == '*';
}
/**
* Returns true if the character can be a part of tag_spec in emoji tag sequence.
*
* Note that 0xE007F (CANCEL TAG) is not included.
*/
public static boolean isTagSpecChar(int codePoint) {
return 0xE0020 <= codePoint && codePoint <= 0xE007E;
}
}

View File

@@ -145,8 +145,11 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener
// The number of following RIS code points is even.
final int STATE_EVEN_NUMBERED_RIS = 11;
// The offset is in emoji tag sequence.
final int STATE_IN_TAG_SEQUENCE = 12;
// The state machine has been stopped.
final int STATE_FINISHED = 12;
final int STATE_FINISHED = 13;
int deleteCharCount = 0; // Char count to be deleted by backspace.
int lastSeenVSCharCount = 0; // Char count of previous variation selector.
@@ -173,6 +176,8 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener
state = STATE_BEFORE_KEYCAP;
} else if (Emoji.isEmoji(codePoint)) {
state = STATE_BEFORE_EMOJI;
} else if (codePoint == Emoji.CANCEL_TAG) {
state = STATE_IN_TAG_SEQUENCE;
} else {
state = STATE_FINISHED;
}
@@ -275,6 +280,20 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener
state = STATE_FINISHED;
}
break;
case STATE_IN_TAG_SEQUENCE:
if (Emoji.isTagSpecChar(codePoint)) {
deleteCharCount += 2; /* Char count of emoji tag spec character. */
// Keep the same state.
} else if (Emoji.isEmoji(codePoint)) {
deleteCharCount += Character.charCount(codePoint);
state = STATE_FINISHED;
} else {
// Couldn't find tag_base character. Delete the last tag_term character.
deleteCharCount = 2; // for U+E007F
state = STATE_FINISHED;
}
// TODO: Need handle emoji variation selectors. Issue 35224297
break;
default:
throw new IllegalArgumentException("state " + state + " is unknown");
}

View File

@@ -37,23 +37,12 @@ public class BackspaceTest extends KeyListenerTestCase {
// Sync the state to the TextView and call onKeyDown with KEYCODE_DEL key event.
// Then update the state to the result of TextView.
private void backspace(final EditorState state, int modifiers) {
mActivity.runOnUiThread(new Runnable() {
public void run() {
mTextView.setText(state.mText, BufferType.EDITABLE);
mTextView.setKeyListener(mKeyListener);
mTextView.setSelection(state.mSelectionStart, state.mSelectionEnd);
}
});
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.hasWindowFocus());
mTextView.setText(state.mText, BufferType.EDITABLE);
mTextView.setKeyListener(mKeyListener);
mTextView.setSelection(state.mSelectionStart, state.mSelectionEnd);
final KeyEvent keyEvent = getKey(KeyEvent.KEYCODE_DEL, modifiers);
mActivity.runOnUiThread(new Runnable() {
public void run() {
mTextView.onKeyDown(keyEvent.getKeyCode(), keyEvent);
}
});
mInstrumentation.waitForIdleSync();
mTextView.onKeyDown(keyEvent.getKeyCode(), keyEvent);
state.mText = mTextView.getText();
state.mSelectionStart = mTextView.getSelectionStart();
@@ -247,6 +236,51 @@ public class BackspaceTest extends KeyListenerTestCase {
state.assertEquals("U+1F1FA U+1F1F8 |");
backspace(state, 0);
state.assertEquals("|");
// Incomplete sequence. (no tag_term: U+E007E)
state.setByString("'a' U+1F3F4 U+E0067 'b' |");
backspace(state, 0);
state.assertEquals("'a' U+1F3F4 U+E0067 |");
backspace(state, 0);
state.assertEquals("'a' U+1F3F4 |");
backspace(state, 0);
state.assertEquals("'a' |");
// No tag_base
state.setByString("'a' U+E0067 U+E007F 'b' |");
backspace(state, 0);
state.assertEquals("'a' U+E0067 U+E007F |");
backspace(state, 0);
state.assertEquals("'a' U+E0067 |");
backspace(state, 0);
state.assertEquals("'a' |");
// Isolated tag chars
state.setByString("'a' U+E0067 U+E0067 'b' |");
backspace(state, 0);
state.assertEquals("'a' U+E0067 U+E0067 |");
backspace(state, 0);
state.assertEquals("'a' U+E0067 |");
backspace(state, 0);
state.assertEquals("'a' |");
// Isolated tab term.
state.setByString("'a' U+E007F U+E007F 'b' |");
backspace(state, 0);
state.assertEquals("'a' U+E007F U+E007F |");
backspace(state, 0);
state.assertEquals("'a' U+E007F |");
backspace(state, 0);
state.assertEquals("'a' |");
// Immediate tag_term after tag_base
state.setByString("'a' U+1F3F4 U+E007F U+1F3F4 U+E007F 'b' |");
backspace(state, 0);
state.assertEquals("'a' U+1F3F4 U+E007F U+1F3F4 U+E007F |");
backspace(state, 0);
state.assertEquals("'a' U+1F3F4 U+E007F |");
backspace(state, 0);
state.assertEquals("'a' |");
}
@SmallTest

View File

@@ -37,23 +37,12 @@ public class ForwardDeleteTest extends KeyListenerTestCase {
// Sync the state to the TextView and call onKeyDown with KEYCODE_FORWARD_DEL key event.
// Then update the state to the result of TextView.
private void forwardDelete(final EditorState state, int modifiers) {
mActivity.runOnUiThread(new Runnable() {
public void run() {
mTextView.setText(state.mText, BufferType.EDITABLE);
mTextView.setKeyListener(mKeyListener);
mTextView.setSelection(state.mSelectionStart, state.mSelectionEnd);
}
});
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.hasWindowFocus());
mTextView.setText(state.mText, BufferType.EDITABLE);
mTextView.setKeyListener(mKeyListener);
mTextView.setSelection(state.mSelectionStart, state.mSelectionEnd);
final KeyEvent keyEvent = getKey(KeyEvent.KEYCODE_FORWARD_DEL, modifiers);
mActivity.runOnUiThread(new Runnable() {
public void run() {
mTextView.onKeyDown(keyEvent.getKeyCode(), keyEvent);
}
});
mInstrumentation.waitForIdleSync();
mTextView.onKeyDown(keyEvent.getKeyCode(), keyEvent);
state.mText = mTextView.getText();
state.mSelectionStart = mTextView.getSelectionStart();
@@ -186,6 +175,46 @@ public class ForwardDeleteTest extends KeyListenerTestCase {
state.assertEquals("| U+1F1FA");
forwardDelete(state, 0);
state.assertEquals("|");
// Incomplete sequence. (no tag_term:U+E007E)
state.setByString("| 'a' U+1F3F4 U+E0067 'b'");
forwardDelete(state, 0);
state.assertEquals("| U+1F3F4 U+E0067 'b'");
forwardDelete(state, 0);
state.assertEquals("| 'b'");
// No tag_base
state.setByString("| 'a' U+E0067 U+E007F 'b'");
forwardDelete(state, 0);
state.assertEquals("| 'b'");
// Isolated tag chars
state.setByString("| 'a' U+E0067 U+E0067 'b'");
forwardDelete(state, 0);
state.assertEquals("| 'b'");
// Isolated tag base.
state.setByString("| 'a' U+1F3F4 U+1F3F4 'b'");
forwardDelete(state, 0);
state.assertEquals("| U+1F3F4 U+1F3F4 'b'");
forwardDelete(state, 0);
state.assertEquals("| U+1F3F4 'b'");
forwardDelete(state, 0);
state.assertEquals("| 'b'");
// Isolated tab term.
state.setByString("| 'a' U+E007F U+E007F 'b'");
forwardDelete(state, 0);
state.assertEquals("| 'b'");
// Immediate tag_term after tag_base
state.setByString("| 'a' U+1F3F4 U+E007F U+1F3F4 U+E007F 'b'");
forwardDelete(state, 0);
state.assertEquals("| U+1F3F4 U+E007F U+1F3F4 U+E007F 'b'");
forwardDelete(state, 0);
state.assertEquals("| U+1F3F4 U+E007F 'b'");
forwardDelete(state, 0);
state.assertEquals("| 'b'");
}
@SmallTest

View File

@@ -17,41 +17,26 @@
package android.text.method;
import android.app.Instrumentation;
import android.test.ActivityInstrumentationTestCase2;
import android.text.format.DateUtils;
import android.test.InstrumentationTestCase;
import android.view.KeyEvent;
import android.widget.EditText;
import android.widget.TextViewActivity;
import com.android.frameworks.coretests.R;
public abstract class KeyListenerTestCase extends
ActivityInstrumentationTestCase2<TextViewActivity> {
public abstract class KeyListenerTestCase extends InstrumentationTestCase {
protected TextViewActivity mActivity;
protected Instrumentation mInstrumentation;
protected EditText mTextView;
public KeyListenerTestCase() {
super(TextViewActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
mActivity = getActivity();
mInstrumentation = getInstrumentation();
mTextView = (EditText) mActivity.findViewById(R.id.textview);
mActivity.runOnUiThread(new Runnable() {
public void run() {
// Ensure that the screen is on for this test.
mTextView.setKeepScreenOn(true);
}
});
assertTrue(mActivity.waitForWindowFocus(5 * DateUtils.SECOND_IN_MILLIS));
mTextView = new EditText(mInstrumentation.getContext());
}
protected static KeyEvent getKey(int keycode, int metaState) {