@Override public Editable getEditable() { return mEditableClient.getEditable(); }
public Editable getEditableForUiThread( final Handler uiHandler, final GoannaEditableClient client) { if (DEBUG) { ThreadUtils.assertOnThread(uiHandler.getLooper().getThread(), AssertBehavior.THROW); } final Handler icHandler = client.getInputConnectionHandler(); if (icHandler.getLooper() == uiHandler.getLooper()) { // IC thread is UI thread; safe to use Editable directly return client.getEditable(); } // IC thread is not UI thread; we need to return a proxy Editable in order // to safely use the Editable from the UI thread if (mUiEditable != null) { return mUiEditable; } final InvocationHandler invokeEditable = new InvocationHandler() { @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { if (DEBUG) { ThreadUtils.assertOnThread(uiHandler.getLooper().getThread(), AssertBehavior.THROW); Log.d(LOGTAG, "UiEditable." + method.getName() + "() blocking"); } synchronized (icHandler) { // Now we are on UI thread mUiEditableReturn = null; mUiEditableException = null; // Post a Runnable that calls the real Editable and saves any // result/exception. Then wait on the Runnable to finish runOnIcThread( icHandler, new Runnable() { @Override public void run() { synchronized (icHandler) { try { mUiEditableReturn = method.invoke(client.getEditable(), args); } catch (Exception e) { mUiEditableException = e; } if (DEBUG) { Log.d(LOGTAG, "UiEditable." + method.getName() + "() returning"); } icHandler.notify(); } } }); // let InterruptedException propagate icHandler.wait(); if (mUiEditableException != null) { throw mUiEditableException; } return mUiEditableReturn; } } }; mUiEditable = (Editable) Proxy.newProxyInstance( Editable.class.getClassLoader(), new Class<?>[] {Editable.class}, invokeEditable); return mUiEditable; }