Add methods to InputConnection: setComposingRegion() to select a region of text for correction, and getSelectedText()
to return the selected text.

setComposingRegion:

The TextView may choose to highlight the text in some way (underline for now) to indicate
that the text is selected for correction, if the IME wants to provider alternatives.

Choosing an alternative in the IME can then call IC.commitText() to replace the highlighted
(not selected) text with a different candidate.

This change also ensures that any existing spans/styles are not wiped out. So we can now
correct rich text as well.

getSelectedText:

This is a convenience to get the selected text instead of using extracted text that is
more heavy weight. Existing getTextBeforeCursor() and getTextAfterCursor() fail to
retrieve the selected text, only what's before and after the selection.

Change-Id: Ieb5ecd5ff947ea04958589f501e7bd5228e00fb5
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index a765e38..986ba38 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -31,9 +31,10 @@
 
 public class IInputConnectionWrapper extends IInputContext.Stub {
     static final String TAG = "IInputConnectionWrapper";
-    
+
     private static final int DO_GET_TEXT_AFTER_CURSOR = 10;
     private static final int DO_GET_TEXT_BEFORE_CURSOR = 20;
+    private static final int DO_GET_SELECTED_TEXT = 25;
     private static final int DO_GET_CURSOR_CAPS_MODE = 30;
     private static final int DO_GET_EXTRACTED_TEXT = 40;
     private static final int DO_COMMIT_TEXT = 50;
@@ -42,6 +43,7 @@
     private static final int DO_PERFORM_EDITOR_ACTION = 58;
     private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59;
     private static final int DO_SET_COMPOSING_TEXT = 60;
+    private static final int DO_SET_COMPOSING_REGION = 63;
     private static final int DO_FINISH_COMPOSING_TEXT = 65;
     private static final int DO_SEND_KEY_EVENT = 70;
     private static final int DO_DELETE_SURROUNDING_TEXT = 80;
@@ -50,7 +52,7 @@
     private static final int DO_REPORT_FULLSCREEN_MODE = 100;
     private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
     private static final int DO_CLEAR_META_KEY_STATES = 130;
-        
+
     private WeakReference<InputConnection> mInputConnection;
 
     private Looper mMainLooper;
@@ -92,6 +94,10 @@
         dispatchMessage(obtainMessageIISC(DO_GET_TEXT_BEFORE_CURSOR, length, flags, seq, callback));
     }
 
+    public void getSelectedText(int flags, int seq, IInputContextCallback callback) {
+        dispatchMessage(obtainMessageISC(DO_GET_SELECTED_TEXT, flags, seq, callback));
+    }
+
     public void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback) {
         dispatchMessage(obtainMessageISC(DO_GET_CURSOR_CAPS_MODE, reqModes, seq, callback));
     }
@@ -122,6 +128,10 @@
         dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0));
     }
     
+    public void setComposingRegion(int start, int end) {
+        dispatchMessage(obtainMessageII(DO_SET_COMPOSING_REGION, start, end));
+    }
+
     public void setComposingText(CharSequence text, int newCursorPosition) {
         dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text));
     }
@@ -206,6 +216,22 @@
                 }
                 return;
             }
+            case DO_GET_SELECTED_TEXT: {
+                SomeArgs args = (SomeArgs)msg.obj;
+                try {
+                    InputConnection ic = mInputConnection.get();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "getSelectedText on inactive InputConnection");
+                        args.callback.setSelectedText(null, args.seq);
+                        return;
+                    }
+                    args.callback.setSelectedText(ic.getSelectedText(
+                            msg.arg1), args.seq);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Got RemoteException calling setSelectedText", e);
+                }
+                return;
+            }
             case DO_GET_CURSOR_CAPS_MODE: {
                 SomeArgs args = (SomeArgs)msg.obj;
                 try {
@@ -292,6 +318,15 @@
                 ic.setComposingText((CharSequence)msg.obj, msg.arg1);
                 return;
             }
+            case DO_SET_COMPOSING_REGION: {
+                InputConnection ic = mInputConnection.get();
+                if (ic == null || !isActive()) {
+                    Log.w(TAG, "setComposingRegion on inactive InputConnection");
+                    return;
+                }
+                ic.setComposingRegion(msg.arg1, msg.arg2);
+                return;
+            }
             case DO_FINISH_COMPOSING_TEXT: {
                 InputConnection ic = mInputConnection.get();
                 // Note we do NOT check isActive() here, because this is safe