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