Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.internal.view;
     18 
     19 import android.os.Bundle;
     20 import android.os.Handler;
     21 import android.os.Looper;
     22 import android.os.Message;
     23 import android.os.RemoteException;
     24 import android.util.Log;
     25 import android.view.KeyEvent;
     26 import android.view.inputmethod.CompletionInfo;
     27 import android.view.inputmethod.CorrectionInfo;
     28 import android.view.inputmethod.ExtractedTextRequest;
     29 import android.view.inputmethod.InputConnection;
     30 
     31 import java.lang.ref.WeakReference;
     32 
     33 public class IInputConnectionWrapper extends IInputContext.Stub {
     34     static final String TAG = "IInputConnectionWrapper";
     35 
     36     private static final int DO_GET_TEXT_AFTER_CURSOR = 10;
     37     private static final int DO_GET_TEXT_BEFORE_CURSOR = 20;
     38     private static final int DO_GET_SELECTED_TEXT = 25;
     39     private static final int DO_GET_CURSOR_CAPS_MODE = 30;
     40     private static final int DO_GET_EXTRACTED_TEXT = 40;
     41     private static final int DO_COMMIT_TEXT = 50;
     42     private static final int DO_COMMIT_COMPLETION = 55;
     43     private static final int DO_COMMIT_CORRECTION = 56;
     44     private static final int DO_SET_SELECTION = 57;
     45     private static final int DO_PERFORM_EDITOR_ACTION = 58;
     46     private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59;
     47     private static final int DO_SET_COMPOSING_TEXT = 60;
     48     private static final int DO_SET_COMPOSING_REGION = 63;
     49     private static final int DO_FINISH_COMPOSING_TEXT = 65;
     50     private static final int DO_SEND_KEY_EVENT = 70;
     51     private static final int DO_DELETE_SURROUNDING_TEXT = 80;
     52     private static final int DO_BEGIN_BATCH_EDIT = 90;
     53     private static final int DO_END_BATCH_EDIT = 95;
     54     private static final int DO_REPORT_FULLSCREEN_MODE = 100;
     55     private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
     56     private static final int DO_CLEAR_META_KEY_STATES = 130;
     57     private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
     58 
     59     private WeakReference<InputConnection> mInputConnection;
     60 
     61     private Looper mMainLooper;
     62     private Handler mH;
     63 
     64     static class SomeArgs {
     65         Object arg1;
     66         Object arg2;
     67         IInputContextCallback callback;
     68         int seq;
     69     }
     70 
     71     class MyHandler extends Handler {
     72         MyHandler(Looper looper) {
     73             super(looper);
     74         }
     75 
     76         @Override
     77         public void handleMessage(Message msg) {
     78             executeMessage(msg);
     79         }
     80     }
     81 
     82     public IInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
     83         mInputConnection = new WeakReference<InputConnection>(conn);
     84         mMainLooper = mainLooper;
     85         mH = new MyHandler(mMainLooper);
     86     }
     87 
     88     public boolean isActive() {
     89         return true;
     90     }
     91 
     92     public void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback) {
     93         dispatchMessage(obtainMessageIISC(DO_GET_TEXT_AFTER_CURSOR, length, flags, seq, callback));
     94     }
     95 
     96     public void getTextBeforeCursor(int length, int flags, int seq, IInputContextCallback callback) {
     97         dispatchMessage(obtainMessageIISC(DO_GET_TEXT_BEFORE_CURSOR, length, flags, seq, callback));
     98     }
     99 
    100     public void getSelectedText(int flags, int seq, IInputContextCallback callback) {
    101         dispatchMessage(obtainMessageISC(DO_GET_SELECTED_TEXT, flags, seq, callback));
    102     }
    103 
    104     public void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback) {
    105         dispatchMessage(obtainMessageISC(DO_GET_CURSOR_CAPS_MODE, reqModes, seq, callback));
    106     }
    107 
    108     public void getExtractedText(ExtractedTextRequest request,
    109             int flags, int seq, IInputContextCallback callback) {
    110         dispatchMessage(obtainMessageIOSC(DO_GET_EXTRACTED_TEXT, flags,
    111                 request, seq, callback));
    112     }
    113 
    114     public void commitText(CharSequence text, int newCursorPosition) {
    115         dispatchMessage(obtainMessageIO(DO_COMMIT_TEXT, newCursorPosition, text));
    116     }
    117 
    118     public void commitCompletion(CompletionInfo text) {
    119         dispatchMessage(obtainMessageO(DO_COMMIT_COMPLETION, text));
    120     }
    121 
    122     public void commitCorrection(CorrectionInfo info) {
    123         dispatchMessage(obtainMessageO(DO_COMMIT_CORRECTION, info));
    124     }
    125 
    126     public void setSelection(int start, int end) {
    127         dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end));
    128     }
    129 
    130     public void performEditorAction(int id) {
    131         dispatchMessage(obtainMessageII(DO_PERFORM_EDITOR_ACTION, id, 0));
    132     }
    133 
    134     public void performContextMenuAction(int id) {
    135         dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0));
    136     }
    137 
    138     public void setComposingRegion(int start, int end) {
    139         dispatchMessage(obtainMessageII(DO_SET_COMPOSING_REGION, start, end));
    140     }
    141 
    142     public void setComposingText(CharSequence text, int newCursorPosition) {
    143         dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text));
    144     }
    145 
    146     public void finishComposingText() {
    147         dispatchMessage(obtainMessage(DO_FINISH_COMPOSING_TEXT));
    148     }
    149 
    150     public void sendKeyEvent(KeyEvent event) {
    151         dispatchMessage(obtainMessageO(DO_SEND_KEY_EVENT, event));
    152     }
    153 
    154     public void clearMetaKeyStates(int states) {
    155         dispatchMessage(obtainMessageII(DO_CLEAR_META_KEY_STATES, states, 0));
    156     }
    157 
    158     public void deleteSurroundingText(int leftLength, int rightLength) {
    159         dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT,
    160             leftLength, rightLength));
    161     }
    162 
    163     public void beginBatchEdit() {
    164         dispatchMessage(obtainMessage(DO_BEGIN_BATCH_EDIT));
    165     }
    166 
    167     public void endBatchEdit() {
    168         dispatchMessage(obtainMessage(DO_END_BATCH_EDIT));
    169     }
    170 
    171     public void reportFullscreenMode(boolean enabled) {
    172         dispatchMessage(obtainMessageII(DO_REPORT_FULLSCREEN_MODE, enabled ? 1 : 0, 0));
    173     }
    174 
    175     public void performPrivateCommand(String action, Bundle data) {
    176         dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data));
    177     }
    178 
    179     public void requestUpdateCursorAnchorInfo(int cursorUpdateMode, int seq,
    180             IInputContextCallback callback) {
    181         dispatchMessage(obtainMessageISC(DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO, cursorUpdateMode,
    182                 seq, callback));
    183     }
    184 
    185     void dispatchMessage(Message msg) {
    186         // If we are calling this from the main thread, then we can call
    187         // right through.  Otherwise, we need to send the message to the
    188         // main thread.
    189         if (Looper.myLooper() == mMainLooper) {
    190             executeMessage(msg);
    191             msg.recycle();
    192             return;
    193         }
    194 
    195         mH.sendMessage(msg);
    196     }
    197 
    198     void executeMessage(Message msg) {
    199         switch (msg.what) {
    200             case DO_GET_TEXT_AFTER_CURSOR: {
    201                 SomeArgs args = (SomeArgs)msg.obj;
    202                 try {
    203                     InputConnection ic = mInputConnection.get();
    204                     if (ic == null || !isActive()) {
    205                         Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
    206                         args.callback.setTextAfterCursor(null, args.seq);
    207                         return;
    208                     }
    209                     args.callback.setTextAfterCursor(ic.getTextAfterCursor(
    210                             msg.arg1, msg.arg2), args.seq);
    211                 } catch (RemoteException e) {
    212                     Log.w(TAG, "Got RemoteException calling setTextAfterCursor", e);
    213                 }
    214                 return;
    215             }
    216             case DO_GET_TEXT_BEFORE_CURSOR: {
    217                 SomeArgs args = (SomeArgs)msg.obj;
    218                 try {
    219                     InputConnection ic = mInputConnection.get();
    220                     if (ic == null || !isActive()) {
    221                         Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
    222                         args.callback.setTextBeforeCursor(null, args.seq);
    223                         return;
    224                     }
    225                     args.callback.setTextBeforeCursor(ic.getTextBeforeCursor(
    226                             msg.arg1, msg.arg2), args.seq);
    227                 } catch (RemoteException e) {
    228                     Log.w(TAG, "Got RemoteException calling setTextBeforeCursor", e);
    229                 }
    230                 return;
    231             }
    232             case DO_GET_SELECTED_TEXT: {
    233                 SomeArgs args = (SomeArgs)msg.obj;
    234                 try {
    235                     InputConnection ic = mInputConnection.get();
    236                     if (ic == null || !isActive()) {
    237                         Log.w(TAG, "getSelectedText on inactive InputConnection");
    238                         args.callback.setSelectedText(null, args.seq);
    239                         return;
    240                     }
    241                     args.callback.setSelectedText(ic.getSelectedText(
    242                             msg.arg1), args.seq);
    243                 } catch (RemoteException e) {
    244                     Log.w(TAG, "Got RemoteException calling setSelectedText", e);
    245                 }
    246                 return;
    247             }
    248             case DO_GET_CURSOR_CAPS_MODE: {
    249                 SomeArgs args = (SomeArgs)msg.obj;
    250                 try {
    251                     InputConnection ic = mInputConnection.get();
    252                     if (ic == null || !isActive()) {
    253                         Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
    254                         args.callback.setCursorCapsMode(0, args.seq);
    255                         return;
    256                     }
    257                     args.callback.setCursorCapsMode(ic.getCursorCapsMode(msg.arg1),
    258                             args.seq);
    259                 } catch (RemoteException e) {
    260                     Log.w(TAG, "Got RemoteException calling setCursorCapsMode", e);
    261                 }
    262                 return;
    263             }
    264             case DO_GET_EXTRACTED_TEXT: {
    265                 SomeArgs args = (SomeArgs)msg.obj;
    266                 try {
    267                     InputConnection ic = mInputConnection.get();
    268                     if (ic == null || !isActive()) {
    269                         Log.w(TAG, "getExtractedText on inactive InputConnection");
    270                         args.callback.setExtractedText(null, args.seq);
    271                         return;
    272                     }
    273                     args.callback.setExtractedText(ic.getExtractedText(
    274                             (ExtractedTextRequest)args.arg1, msg.arg1), args.seq);
    275                 } catch (RemoteException e) {
    276                     Log.w(TAG, "Got RemoteException calling setExtractedText", e);
    277                 }
    278                 return;
    279             }
    280             case DO_COMMIT_TEXT: {
    281                 InputConnection ic = mInputConnection.get();
    282                 if (ic == null || !isActive()) {
    283                     Log.w(TAG, "commitText on inactive InputConnection");
    284                     return;
    285                 }
    286                 ic.commitText((CharSequence)msg.obj, msg.arg1);
    287                 return;
    288             }
    289             case DO_SET_SELECTION: {
    290                 InputConnection ic = mInputConnection.get();
    291                 if (ic == null || !isActive()) {
    292                     Log.w(TAG, "setSelection on inactive InputConnection");
    293                     return;
    294                 }
    295                 ic.setSelection(msg.arg1, msg.arg2);
    296                 return;
    297             }
    298             case DO_PERFORM_EDITOR_ACTION: {
    299                 InputConnection ic = mInputConnection.get();
    300                 if (ic == null || !isActive()) {
    301                     Log.w(TAG, "performEditorAction on inactive InputConnection");
    302                     return;
    303                 }
    304                 ic.performEditorAction(msg.arg1);
    305                 return;
    306             }
    307             case DO_PERFORM_CONTEXT_MENU_ACTION: {
    308                 InputConnection ic = mInputConnection.get();
    309                 if (ic == null || !isActive()) {
    310                     Log.w(TAG, "performContextMenuAction on inactive InputConnection");
    311                     return;
    312                 }
    313                 ic.performContextMenuAction(msg.arg1);
    314                 return;
    315             }
    316             case DO_COMMIT_COMPLETION: {
    317                 InputConnection ic = mInputConnection.get();
    318                 if (ic == null || !isActive()) {
    319                     Log.w(TAG, "commitCompletion on inactive InputConnection");
    320                     return;
    321                 }
    322                 ic.commitCompletion((CompletionInfo)msg.obj);
    323                 return;
    324             }
    325             case DO_COMMIT_CORRECTION: {
    326                 InputConnection ic = mInputConnection.get();
    327                 if (ic == null || !isActive()) {
    328                     Log.w(TAG, "commitCorrection on inactive InputConnection");
    329                     return;
    330                 }
    331                 ic.commitCorrection((CorrectionInfo)msg.obj);
    332                 return;
    333             }
    334             case DO_SET_COMPOSING_TEXT: {
    335                 InputConnection ic = mInputConnection.get();
    336                 if (ic == null || !isActive()) {
    337                     Log.w(TAG, "setComposingText on inactive InputConnection");
    338                     return;
    339                 }
    340                 ic.setComposingText((CharSequence)msg.obj, msg.arg1);
    341                 return;
    342             }
    343             case DO_SET_COMPOSING_REGION: {
    344                 InputConnection ic = mInputConnection.get();
    345                 if (ic == null || !isActive()) {
    346                     Log.w(TAG, "setComposingRegion on inactive InputConnection");
    347                     return;
    348                 }
    349                 ic.setComposingRegion(msg.arg1, msg.arg2);
    350                 return;
    351             }
    352             case DO_FINISH_COMPOSING_TEXT: {
    353                 InputConnection ic = mInputConnection.get();
    354                 // Note we do NOT check isActive() here, because this is safe
    355                 // for an IME to call at any time, and we need to allow it
    356                 // through to clean up our state after the IME has switched to
    357                 // another client.
    358                 if (ic == null) {
    359                     Log.w(TAG, "finishComposingText on inactive InputConnection");
    360                     return;
    361                 }
    362                 ic.finishComposingText();
    363                 return;
    364             }
    365             case DO_SEND_KEY_EVENT: {
    366                 InputConnection ic = mInputConnection.get();
    367                 if (ic == null || !isActive()) {
    368                     Log.w(TAG, "sendKeyEvent on inactive InputConnection");
    369                     return;
    370                 }
    371                 ic.sendKeyEvent((KeyEvent)msg.obj);
    372                 return;
    373             }
    374             case DO_CLEAR_META_KEY_STATES: {
    375                 InputConnection ic = mInputConnection.get();
    376                 if (ic == null || !isActive()) {
    377                     Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
    378                     return;
    379                 }
    380                 ic.clearMetaKeyStates(msg.arg1);
    381                 return;
    382             }
    383             case DO_DELETE_SURROUNDING_TEXT: {
    384                 InputConnection ic = mInputConnection.get();
    385                 if (ic == null || !isActive()) {
    386                     Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
    387                     return;
    388                 }
    389                 ic.deleteSurroundingText(msg.arg1, msg.arg2);
    390                 return;
    391             }
    392             case DO_BEGIN_BATCH_EDIT: {
    393                 InputConnection ic = mInputConnection.get();
    394                 if (ic == null || !isActive()) {
    395                     Log.w(TAG, "beginBatchEdit on inactive InputConnection");
    396                     return;
    397                 }
    398                 ic.beginBatchEdit();
    399                 return;
    400             }
    401             case DO_END_BATCH_EDIT: {
    402                 InputConnection ic = mInputConnection.get();
    403                 if (ic == null || !isActive()) {
    404                     Log.w(TAG, "endBatchEdit on inactive InputConnection");
    405                     return;
    406                 }
    407                 ic.endBatchEdit();
    408                 return;
    409             }
    410             case DO_REPORT_FULLSCREEN_MODE: {
    411                 InputConnection ic = mInputConnection.get();
    412                 if (ic == null || !isActive()) {
    413                     Log.w(TAG, "showStatusIcon on inactive InputConnection");
    414                     return;
    415                 }
    416                 ic.reportFullscreenMode(msg.arg1 == 1);
    417                 return;
    418             }
    419             case DO_PERFORM_PRIVATE_COMMAND: {
    420                 InputConnection ic = mInputConnection.get();
    421                 if (ic == null || !isActive()) {
    422                     Log.w(TAG, "performPrivateCommand on inactive InputConnection");
    423                     return;
    424                 }
    425                 SomeArgs args = (SomeArgs)msg.obj;
    426                 ic.performPrivateCommand((String)args.arg1,
    427                         (Bundle)args.arg2);
    428                 return;
    429             }
    430             case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: {
    431                 SomeArgs args = (SomeArgs)msg.obj;
    432                 try {
    433                     InputConnection ic = mInputConnection.get();
    434                     if (ic == null || !isActive()) {
    435                         Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
    436                         args.callback.setRequestUpdateCursorAnchorInfoResult(false, args.seq);
    437                         return;
    438                     }
    439                     args.callback.setRequestUpdateCursorAnchorInfoResult(
    440                             ic.requestCursorUpdates(msg.arg1), args.seq);
    441                 } catch (RemoteException e) {
    442                     Log.w(TAG, "Got RemoteException calling requestCursorAnchorInfo", e);
    443                 }
    444                 return;
    445             }
    446         }
    447         Log.w(TAG, "Unhandled message code: " + msg.what);
    448     }
    449 
    450     Message obtainMessage(int what) {
    451         return mH.obtainMessage(what);
    452     }
    453 
    454     Message obtainMessageII(int what, int arg1, int arg2) {
    455         return mH.obtainMessage(what, arg1, arg2);
    456     }
    457 
    458     Message obtainMessageO(int what, Object arg1) {
    459         return mH.obtainMessage(what, 0, 0, arg1);
    460     }
    461 
    462     Message obtainMessageISC(int what, int arg1, int seq, IInputContextCallback callback) {
    463         SomeArgs args = new SomeArgs();
    464         args.callback = callback;
    465         args.seq = seq;
    466         return mH.obtainMessage(what, arg1, 0, args);
    467     }
    468 
    469     Message obtainMessageIISC(int what, int arg1, int arg2, int seq, IInputContextCallback callback) {
    470         SomeArgs args = new SomeArgs();
    471         args.callback = callback;
    472         args.seq = seq;
    473         return mH.obtainMessage(what, arg1, arg2, args);
    474     }
    475 
    476     Message obtainMessageOSC(int what, Object arg1, int seq, IInputContextCallback callback) {
    477         SomeArgs args = new SomeArgs();
    478         args.arg1 = arg1;
    479         args.callback = callback;
    480         args.seq = seq;
    481         return mH.obtainMessage(what, 0, 0, args);
    482     }
    483 
    484     Message obtainMessageIOSC(int what, int arg1, Object arg2, int seq,
    485             IInputContextCallback callback) {
    486         SomeArgs args = new SomeArgs();
    487         args.arg1 = arg2;
    488         args.callback = callback;
    489         args.seq = seq;
    490         return mH.obtainMessage(what, arg1, 0, args);
    491     }
    492 
    493     Message obtainMessageIO(int what, int arg1, Object arg2) {
    494         return mH.obtainMessage(what, arg1, 0, arg2);
    495     }
    496 
    497     Message obtainMessageOO(int what, Object arg1, Object arg2) {
    498         SomeArgs args = new SomeArgs();
    499         args.arg1 = arg1;
    500         args.arg2 = arg2;
    501         return mH.obtainMessage(what, 0, 0, args);
    502     }
    503 }
    504