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/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 3c44e58..08c3026 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.view;
 
-import com.android.internal.view.IInputContext;
-
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -38,6 +36,7 @@
         public boolean mHaveValue;
         public CharSequence mTextBeforeCursor;
         public CharSequence mTextAfterCursor;
+        public CharSequence mSelectedText;
         public ExtractedText mExtractedText;
         public int mCursorCapsMode;
         
@@ -114,6 +113,19 @@
             }
         }
 
+        public void setSelectedText(CharSequence selectedText, int seq) {
+            synchronized (this) {
+                if (seq == mSeq) {
+                    mSelectedText = selectedText;
+                    mHaveValue = true;
+                    notifyAll();
+                } else {
+                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
+                            + ") in setSelectedText, ignoring.");
+                }
+            }
+        }
+
         public void setCursorCapsMode(int capsMode, int seq) {
             synchronized (this) {
                 if (seq == mSeq) {
@@ -203,6 +215,24 @@
         return value;
     }
     
+    public CharSequence getSelectedText(int flags) {
+        CharSequence value = null;
+        try {
+            InputContextCallback callback = InputContextCallback.getInstance();
+            mIInputContext.getSelectedText(flags, callback.mSeq, callback);
+            synchronized (callback) {
+                callback.waitForResultLocked();
+                if (callback.mHaveValue) {
+                    value = callback.mSelectedText;
+                }
+            }
+            callback.dispose();
+        } catch (RemoteException e) {
+            return null;
+        }
+        return value;
+    }
+
     public int getCursorCapsMode(int reqModes) {
         int value = 0;
         try {
@@ -283,7 +313,16 @@
             return false;
         }
     }
-    
+
+    public boolean setComposingRegion(int start, int end) {
+        try {
+            mIInputContext.setComposingRegion(start, end);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
     public boolean setComposingText(CharSequence text, int newCursorPosition) {
         try {
             mIInputContext.setComposingText(text, newCursorPosition);