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