Add support for forward delete key.

Change-Id: Ib356abddd92db12d6b33e19234136c0d167f0e15
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index 350c9a8..191c250 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -33,75 +33,116 @@
         implements KeyListener {
     /* package */ static final Object OLD_SEL_START = new NoCopySpan.Concrete();
 
+    private static final int MODIFIER_NONE = 0;
+    private static final int MODIFIER_ALT = 1;
+    private static final int MODIFIER_INVALID = 2;
+
+    private static int getModifier(Editable content, KeyEvent event) {
+        if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
+            return MODIFIER_ALT;
+        }
+        if (!event.hasNoModifiers()) {
+            return MODIFIER_INVALID;
+        }
+        if (getMetaState(content, META_ALT_ON) == 1) {
+            return MODIFIER_ALT;
+        }
+        return MODIFIER_NONE;
+    }
+
     /**
-     * Performs the action that happens when you press the DEL key in
-     * a TextView.  If there is a selection, deletes the selection;
-     * otherwise, DEL alone deletes the character before the cursor,
-     * if any;
-     * ALT+DEL deletes everything on the line the cursor is on.
+     * Performs the action that happens when you press the {@link KeyEvent#KEYCODE_DEL} key in
+     * a {@link TextView}.  If there is a selection, deletes the selection; otherwise,
+     * deletes the character before the cursor, if any; ALT+DEL deletes everything on
+     * the line the cursor is on.
      *
      * @return true if anything was deleted; false otherwise.
      */
-    public boolean backspace(View view, Editable content, int keyCode,
-                             KeyEvent event) {
-        int selStart, selEnd;
-        boolean result = true;
-
-        {
-            int a = Selection.getSelectionStart(content);
-            int b = Selection.getSelectionEnd(content);
-
-            selStart = Math.min(a, b);
-            selEnd = Math.max(a, b);
+    public boolean backspace(View view, Editable content, int keyCode, KeyEvent event) {
+        int modifier = getModifier(content, event);
+        if (modifier == MODIFIER_INVALID) {
+            return false;
         }
 
-        if (selStart != selEnd) {
-            content.delete(selStart, selEnd);
-        } else if (altBackspace(view, content, keyCode, event)) {
-            result = true;
-        } else {
-            int to = TextUtils.getOffsetBefore(content, selEnd);
-
-            if (to != selEnd) {
-                content.delete(Math.min(to, selEnd), Math.max(to, selEnd));
-            }
-            else {
-                result = false;
-            }
+        if (deleteSelection(view, content)) {
+            return true;
         }
 
-        if (result)
-            adjustMetaAfterKeypress(content);
+        if (modifier == MODIFIER_ALT && deleteLine(view, content)) {
+            return true;
+        }
 
-        return result;
+        final int start = Selection.getSelectionEnd(content);
+        final int end = TextUtils.getOffsetBefore(content, start);
+        if (start != end) {
+            content.delete(Math.min(start, end), Math.max(start, end));
+            return true;
+        }
+
+        return false;
     }
 
-    private boolean altBackspace(View view, Editable content, int keyCode,
-                                 KeyEvent event) {
-        if (!event.isAltPressed() && getMetaState(content, META_ALT_ON) != 1) {
+    /**
+     * Performs the action that happens when you press the {@link KeyEvent#KEYCODE_FORWARD_DEL}
+     * key in a {@link TextView}.  If there is a selection, deletes the selection; otherwise,
+     * deletes the character before the cursor, if any; ALT+FORWARD_DEL deletes everything on
+     * the line the cursor is on.
+     *
+     * @return true if anything was deleted; false otherwise.
+     */
+    public boolean forwardDelete(View view, Editable content, int keyCode, KeyEvent event) {
+        int modifier = getModifier(content, event);
+        if (modifier == MODIFIER_INVALID) {
             return false;
         }
 
-        if (!(view instanceof TextView)) {
-            return false;
+        if (deleteSelection(view, content)) {
+            return true;
         }
 
-        Layout layout = ((TextView) view).getLayout();
-
-        if (layout == null) {
-            return false;
+        if (modifier == MODIFIER_ALT && deleteLine(view, content)) {
+            return true;
         }
 
-        int l = layout.getLineForOffset(Selection.getSelectionStart(content));
-        int start = layout.getLineStart(l);
-        int end = layout.getLineEnd(l);
-
-        if (end == start) {
-            return false;
+        final int start = Selection.getSelectionEnd(content);
+        final int end = TextUtils.getOffsetAfter(content, start);
+        if (start != end) {
+            content.delete(Math.min(start, end), Math.max(start, end));
+            return true;
         }
 
-        content.delete(start, end);
-        return true;
+        return false;
+    }
+
+    private boolean deleteSelection(View view, Editable content) {
+        int selectionStart = Selection.getSelectionStart(content);
+        int selectionEnd = Selection.getSelectionEnd(content);
+        if (selectionEnd < selectionStart) {
+            int temp = selectionEnd;
+            selectionEnd = selectionStart;
+            selectionStart = temp;
+        }
+        if (selectionStart != selectionEnd) {
+            content.delete(selectionStart, selectionEnd);
+            return true;
+        }
+        return false;
+    }
+
+    private boolean deleteLine(View view, Editable content) {
+        if (view instanceof TextView) {
+            final Layout layout = ((TextView) view).getLayout();
+            if (layout != null) {
+                final int line = layout.getLineForOffset(Selection.getSelectionStart(content));
+                final int start = layout.getLineStart(line);
+                final int end = layout.getLineEnd(line);
+                if (end != start) {
+                    content.delete(start, end);
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
     static int makeTextContentType(Capitalize caps, boolean autoText) {
@@ -122,17 +163,29 @@
         }
         return contentType;
     }
-    
+
     public boolean onKeyDown(View view, Editable content,
                              int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_DEL) {
-            backspace(view, content, keyCode, event);
-            return true;
+        boolean handled;
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DEL:
+                handled = backspace(view, content, keyCode, event);
+                break;
+            case KeyEvent.KEYCODE_FORWARD_DEL:
+                handled = forwardDelete(view, content, keyCode, event);
+                break;
+            default:
+                handled = false;
+                break;
         }
-        
+
+        if (handled) {
+            adjustMetaAfterKeypress(content);
+        }
+
         return super.onKeyDown(view, content, keyCode, event);
     }
-    
+
     /**
      * Base implementation handles ACTION_MULTIPLE KEYCODE_UNKNOWN by inserting
      * the event's text into the content.
@@ -143,23 +196,21 @@
             // Not something we are interested in.
             return false;
         }
-        
-        int selStart, selEnd;
 
-        {
-            int a = Selection.getSelectionStart(content);
-            int b = Selection.getSelectionEnd(content);
-
-            selStart = Math.min(a, b);
-            selEnd = Math.max(a, b);
+        int selectionStart = Selection.getSelectionStart(content);
+        int selectionEnd = Selection.getSelectionEnd(content);
+        if (selectionEnd < selectionStart) {
+            int temp = selectionEnd;
+            selectionEnd = selectionStart;
+            selectionStart = temp;
         }
 
         CharSequence text = event.getCharacters();
         if (text == null) {
             return false;
         }
-        
-        content.replace(selStart, selEnd, text);
+
+        content.replace(selectionStart, selectionEnd, text);
         return true;
     }
 }
diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java
index 09388c0..4c82b81 100644
--- a/core/java/android/text/method/QwertyKeyListener.java
+++ b/core/java/android/text/method/QwertyKeyListener.java
@@ -295,7 +295,9 @@
             }
 
             return true;
-        } else if (keyCode == KeyEvent.KEYCODE_DEL && selStart == selEnd) {
+        } else if (keyCode == KeyEvent.KEYCODE_DEL
+                && (event.hasNoModifiers() || event.hasModifiers(KeyEvent.META_ALT_ON))
+                && selStart == selEnd) {
             // special backspace case for undoing autotext
 
             int consider = 1;