Home | History | Annotate | Download | only in inputmethodservice
      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 android.inputmethodservice;
     18 
     19 import android.content.Context;
     20 import android.graphics.Rect;
     21 import android.os.Bundle;
     22 import android.os.Looper;
     23 import android.os.Message;
     24 import android.util.Log;
     25 import android.util.SparseArray;
     26 import android.view.InputChannel;
     27 import android.view.InputDevice;
     28 import android.view.InputEvent;
     29 import android.view.InputEventReceiver;
     30 import android.view.KeyEvent;
     31 import android.view.MotionEvent;
     32 import android.view.inputmethod.CompletionInfo;
     33 import android.view.inputmethod.CursorAnchorInfo;
     34 import android.view.inputmethod.ExtractedText;
     35 import android.view.inputmethod.InputMethodSession;
     36 
     37 import com.android.internal.os.HandlerCaller;
     38 import com.android.internal.os.SomeArgs;
     39 import com.android.internal.view.IInputMethodSession;
     40 
     41 class IInputMethodSessionWrapper extends IInputMethodSession.Stub
     42         implements HandlerCaller.Callback {
     43     private static final String TAG = "InputMethodWrapper";
     44 
     45     private static final int DO_FINISH_INPUT = 60;
     46     private static final int DO_DISPLAY_COMPLETIONS = 65;
     47     private static final int DO_UPDATE_EXTRACTED_TEXT = 67;
     48     private static final int DO_UPDATE_SELECTION = 90;
     49     private static final int DO_UPDATE_CURSOR = 95;
     50     private static final int DO_UPDATE_CURSOR_ANCHOR_INFO = 99;
     51     private static final int DO_APP_PRIVATE_COMMAND = 100;
     52     private static final int DO_TOGGLE_SOFT_INPUT = 105;
     53     private static final int DO_FINISH_SESSION = 110;
     54     private static final int DO_VIEW_CLICKED = 115;
     55 
     56     HandlerCaller mCaller;
     57     InputMethodSession mInputMethodSession;
     58     InputChannel mChannel;
     59     ImeInputEventReceiver mReceiver;
     60 
     61     public IInputMethodSessionWrapper(Context context,
     62             InputMethodSession inputMethodSession, InputChannel channel) {
     63         mCaller = new HandlerCaller(context, null,
     64                 this, true /*asyncHandler*/);
     65         mInputMethodSession = inputMethodSession;
     66         mChannel = channel;
     67         if (channel != null) {
     68             mReceiver = new ImeInputEventReceiver(channel, context.getMainLooper());
     69         }
     70     }
     71 
     72     public InputMethodSession getInternalInputMethodSession() {
     73         return mInputMethodSession;
     74     }
     75 
     76     @Override
     77     public void executeMessage(Message msg) {
     78         if (mInputMethodSession == null) {
     79             // The session has been finished. Args needs to be recycled
     80             // for cases below.
     81             switch (msg.what) {
     82                 case DO_UPDATE_SELECTION:
     83                 case DO_APP_PRIVATE_COMMAND: {
     84                     SomeArgs args = (SomeArgs)msg.obj;
     85                     args.recycle();
     86                 }
     87             }
     88             return;
     89         }
     90 
     91         switch (msg.what) {
     92             case DO_FINISH_INPUT:
     93                 mInputMethodSession.finishInput();
     94                 return;
     95             case DO_DISPLAY_COMPLETIONS:
     96                 mInputMethodSession.displayCompletions((CompletionInfo[])msg.obj);
     97                 return;
     98             case DO_UPDATE_EXTRACTED_TEXT:
     99                 mInputMethodSession.updateExtractedText(msg.arg1,
    100                         (ExtractedText)msg.obj);
    101                 return;
    102             case DO_UPDATE_SELECTION: {
    103                 SomeArgs args = (SomeArgs)msg.obj;
    104                 mInputMethodSession.updateSelection(args.argi1, args.argi2,
    105                         args.argi3, args.argi4, args.argi5, args.argi6);
    106                 args.recycle();
    107                 return;
    108             }
    109             case DO_UPDATE_CURSOR: {
    110                 mInputMethodSession.updateCursor((Rect)msg.obj);
    111                 return;
    112             }
    113             case DO_UPDATE_CURSOR_ANCHOR_INFO: {
    114                 mInputMethodSession.updateCursorAnchorInfo((CursorAnchorInfo)msg.obj);
    115                 return;
    116             }
    117             case DO_APP_PRIVATE_COMMAND: {
    118                 SomeArgs args = (SomeArgs)msg.obj;
    119                 mInputMethodSession.appPrivateCommand((String)args.arg1,
    120                         (Bundle)args.arg2);
    121                 args.recycle();
    122                 return;
    123             }
    124             case DO_TOGGLE_SOFT_INPUT: {
    125                 mInputMethodSession.toggleSoftInput(msg.arg1, msg.arg2);
    126                 return;
    127             }
    128             case DO_FINISH_SESSION: {
    129                 doFinishSession();
    130                 return;
    131             }
    132             case DO_VIEW_CLICKED: {
    133                 mInputMethodSession.viewClicked(msg.arg1 == 1);
    134                 return;
    135             }
    136         }
    137         Log.w(TAG, "Unhandled message code: " + msg.what);
    138     }
    139 
    140     private void doFinishSession() {
    141         mInputMethodSession = null;
    142         if (mReceiver != null) {
    143             mReceiver.dispose();
    144             mReceiver = null;
    145         }
    146         if (mChannel != null) {
    147             mChannel.dispose();
    148             mChannel = null;
    149         }
    150     }
    151 
    152     @Override
    153     public void finishInput() {
    154         mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_INPUT));
    155     }
    156 
    157     @Override
    158     public void displayCompletions(CompletionInfo[] completions) {
    159         mCaller.executeOrSendMessage(mCaller.obtainMessageO(
    160                 DO_DISPLAY_COMPLETIONS, completions));
    161     }
    162 
    163     @Override
    164     public void updateExtractedText(int token, ExtractedText text) {
    165         mCaller.executeOrSendMessage(mCaller.obtainMessageIO(
    166                 DO_UPDATE_EXTRACTED_TEXT, token, text));
    167     }
    168 
    169     @Override
    170     public void updateSelection(int oldSelStart, int oldSelEnd,
    171             int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
    172         mCaller.executeOrSendMessage(mCaller.obtainMessageIIIIII(DO_UPDATE_SELECTION,
    173                 oldSelStart, oldSelEnd, newSelStart, newSelEnd,
    174                 candidatesStart, candidatesEnd));
    175     }
    176 
    177     @Override
    178     public void viewClicked(boolean focusChanged) {
    179         mCaller.executeOrSendMessage(
    180                 mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0));
    181     }
    182 
    183     @Override
    184     public void updateCursor(Rect newCursor) {
    185         mCaller.executeOrSendMessage(
    186                 mCaller.obtainMessageO(DO_UPDATE_CURSOR, newCursor));
    187     }
    188 
    189     @Override
    190     public void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
    191         mCaller.executeOrSendMessage(
    192                 mCaller.obtainMessageO(DO_UPDATE_CURSOR_ANCHOR_INFO, cursorAnchorInfo));
    193     }
    194 
    195     @Override
    196     public void appPrivateCommand(String action, Bundle data) {
    197         mCaller.executeOrSendMessage(
    198                 mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data));
    199     }
    200 
    201     @Override
    202     public void toggleSoftInput(int showFlags, int hideFlags) {
    203         mCaller.executeOrSendMessage(
    204                 mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags));
    205     }
    206 
    207     @Override
    208     public void finishSession() {
    209         mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_SESSION));
    210     }
    211 
    212     private final class ImeInputEventReceiver extends InputEventReceiver
    213             implements InputMethodSession.EventCallback {
    214         private final SparseArray<InputEvent> mPendingEvents = new SparseArray<InputEvent>();
    215 
    216         public ImeInputEventReceiver(InputChannel inputChannel, Looper looper) {
    217             super(inputChannel, looper);
    218         }
    219 
    220         @Override
    221         public void onInputEvent(InputEvent event, int displayId) {
    222             if (mInputMethodSession == null) {
    223                 // The session has been finished.
    224                 finishInputEvent(event, false);
    225                 return;
    226             }
    227 
    228             final int seq = event.getSequenceNumber();
    229             mPendingEvents.put(seq, event);
    230             if (event instanceof KeyEvent) {
    231                 KeyEvent keyEvent = (KeyEvent)event;
    232                 mInputMethodSession.dispatchKeyEvent(seq, keyEvent, this);
    233             } else {
    234                 MotionEvent motionEvent = (MotionEvent)event;
    235                 if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) {
    236                     mInputMethodSession.dispatchTrackballEvent(seq, motionEvent, this);
    237                 } else {
    238                     mInputMethodSession.dispatchGenericMotionEvent(seq, motionEvent, this);
    239                 }
    240             }
    241         }
    242 
    243         @Override
    244         public void finishedEvent(int seq, boolean handled) {
    245             int index = mPendingEvents.indexOfKey(seq);
    246             if (index >= 0) {
    247                 InputEvent event = mPendingEvents.valueAt(index);
    248                 mPendingEvents.removeAt(index);
    249                 finishInputEvent(event, handled);
    250             }
    251         }
    252     }
    253 }
    254