Add InputConnection#insertContent().

Providing an official protocol for IMEs to insert an image to the
application is something that has been requested from many IME
developers to Android OS.  With this CL, IMEs are able to ask
applications to insert a content including image files as follows.

 1. An application that opts in to this protocol specifies a list of
    supported content MIME types in EditorInfo#contentMimeTypes.
 2. When an IME is actively interacting with such an application, the
    IME can call InputConnection#insertContent() with a InputContentInfo
    that contains content URI, metadata (ClipDescription), and an
    optional link URI.
 3. The application can read the stream data from the given content URI
    to insert the content into somewhere in the application.

Detailed design background can be found in the JavaDoc of
InputConnection#insertContent().

Bug: 22830793
Change-Id: Iaadf934a997ffcd6000a516cc3c1873db56e60ad
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index f9884d8..6d4d45c 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.view;
 
+import android.content.ClipData;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -29,6 +30,7 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputConnectionInspector;
 import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
+import android.view.inputmethod.InputContentInfo;
 
 public class InputConnectionWrapper implements InputConnection {
     private static final int MAX_WAIT_TIME_MILLIS = 2000;
@@ -46,7 +48,8 @@
         public ExtractedText mExtractedText;
         public int mCursorCapsMode;
         public boolean mRequestUpdateCursorAnchorInfoResult;
-        
+        public boolean mInsertContentResult;
+
         // A 'pool' of one InputContextCallback.  Each ICW request will attempt to gain
         // exclusive access to this object.
         private static InputContextCallback sInstance = new InputContextCallback();
@@ -172,6 +175,19 @@
             }
         }
 
+        public void setInsertContentResult(boolean result, int seq) {
+            synchronized (this) {
+                if (seq == mSeq) {
+                    mInsertContentResult = result;
+                    mHaveValue = true;
+                    notifyAll();
+                } else {
+                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
+                            + ") in setInsertContentResult, ignoring.");
+                }
+            }
+        }
+
         /**
          * Waits for a result for up to {@link #MAX_WAIT_TIME_MILLIS} milliseconds.
          * 
@@ -491,6 +507,28 @@
         // Nothing should happen when called from input method.
     }
 
+    public boolean insertContent(InputContentInfo inputContentInfo, Bundle opts) {
+        boolean result = false;
+        if (isMethodMissing(MissingMethodFlags.INSERT_CONTENT)) {
+            // This method is not implemented.
+            return false;
+        }
+        try {
+            InputContextCallback callback = InputContextCallback.getInstance();
+            mIInputContext.insertContent(inputContentInfo, opts, callback.mSeq, callback);
+            synchronized (callback) {
+                callback.waitForResultLocked();
+                if (callback.mHaveValue) {
+                    result = callback.mInsertContentResult;
+                }
+            }
+            callback.dispose();
+        } catch (RemoteException e) {
+            return false;
+        }
+        return result;
+    }
+
     private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) {
         return (mMissingMethods & methodFlag) == methodFlag;
     }