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