Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2007-2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * 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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.android.internal.widget;
     18 
     19 import android.os.Bundle;
     20 import android.text.Editable;
     21 import android.text.Spanned;
     22 import android.text.method.KeyListener;
     23 import android.text.style.SuggestionSpan;
     24 import android.util.Log;
     25 import android.view.inputmethod.BaseInputConnection;
     26 import android.view.inputmethod.CompletionInfo;
     27 import android.view.inputmethod.CorrectionInfo;
     28 import android.view.inputmethod.ExtractedText;
     29 import android.view.inputmethod.ExtractedTextRequest;
     30 import android.widget.TextView;
     31 
     32 public class EditableInputConnection extends BaseInputConnection {
     33     private static final boolean DEBUG = false;
     34     private static final String TAG = "EditableInputConnection";
     35 
     36     private final TextView mTextView;
     37 
     38     // Keeps track of nested begin/end batch edit to ensure this connection always has a
     39     // balanced impact on its associated TextView.
     40     // A negative value means that this connection has been finished by the InputMethodManager.
     41     private int mBatchEditNesting;
     42 
     43     public EditableInputConnection(TextView textview) {
     44         super(textview, true);
     45         mTextView = textview;
     46     }
     47 
     48     @Override
     49     public Editable getEditable() {
     50         TextView tv = mTextView;
     51         if (tv != null) {
     52             return tv.getEditableText();
     53         }
     54         return null;
     55     }
     56 
     57     @Override
     58     public boolean beginBatchEdit() {
     59         synchronized(this) {
     60             if (mBatchEditNesting >= 0) {
     61                 mTextView.beginBatchEdit();
     62                 mBatchEditNesting++;
     63                 return true;
     64             }
     65         }
     66         return false;
     67     }
     68 
     69     @Override
     70     public boolean endBatchEdit() {
     71         synchronized(this) {
     72             if (mBatchEditNesting > 0) {
     73                 // When the connection is reset by the InputMethodManager and reportFinish
     74                 // is called, some endBatchEdit calls may still be asynchronously received from the
     75                 // IME. Do not take these into account, thus ensuring that this IC's final
     76                 // contribution to mTextView's nested batch edit count is zero.
     77                 mTextView.endBatchEdit();
     78                 mBatchEditNesting--;
     79                 return true;
     80             }
     81         }
     82         return false;
     83     }
     84 
     85     @Override
     86     protected void reportFinish() {
     87         super.reportFinish();
     88 
     89         synchronized(this) {
     90             while (mBatchEditNesting > 0) {
     91                 endBatchEdit();
     92             }
     93             // Will prevent any further calls to begin or endBatchEdit
     94             mBatchEditNesting = -1;
     95         }
     96     }
     97 
     98     @Override
     99     public boolean clearMetaKeyStates(int states) {
    100         final Editable content = getEditable();
    101         if (content == null) return false;
    102         KeyListener kl = mTextView.getKeyListener();
    103         if (kl != null) {
    104             try {
    105                 kl.clearMetaKeyState(mTextView, content, states);
    106             } catch (AbstractMethodError e) {
    107                 // This is an old listener that doesn't implement the
    108                 // new method.
    109             }
    110         }
    111         return true;
    112     }
    113 
    114     @Override
    115     public boolean commitCompletion(CompletionInfo text) {
    116         if (DEBUG) Log.v(TAG, "commitCompletion " + text);
    117         mTextView.beginBatchEdit();
    118         mTextView.onCommitCompletion(text);
    119         mTextView.endBatchEdit();
    120         return true;
    121     }
    122 
    123     /**
    124      * Calls the {@link TextView#onCommitCorrection} method of the associated TextView.
    125      */
    126     @Override
    127     public boolean commitCorrection(CorrectionInfo correctionInfo) {
    128         if (DEBUG) Log.v(TAG, "commitCorrection" + correctionInfo);
    129         mTextView.beginBatchEdit();
    130         mTextView.onCommitCorrection(correctionInfo);
    131         mTextView.endBatchEdit();
    132         return true;
    133     }
    134 
    135     @Override
    136     public boolean performEditorAction(int actionCode) {
    137         if (DEBUG) Log.v(TAG, "performEditorAction " + actionCode);
    138         mTextView.onEditorAction(actionCode);
    139         return true;
    140     }
    141 
    142     @Override
    143     public boolean performContextMenuAction(int id) {
    144         if (DEBUG) Log.v(TAG, "performContextMenuAction " + id);
    145         mTextView.beginBatchEdit();
    146         mTextView.onTextContextMenuItem(id);
    147         mTextView.endBatchEdit();
    148         return true;
    149     }
    150 
    151     @Override
    152     public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
    153         if (mTextView != null) {
    154             ExtractedText et = new ExtractedText();
    155             if (mTextView.extractText(request, et)) {
    156                 if ((flags&GET_EXTRACTED_TEXT_MONITOR) != 0) {
    157                     mTextView.setExtracting(request);
    158                 }
    159                 return et;
    160             }
    161         }
    162         return null;
    163     }
    164 
    165     @Override
    166     public boolean performPrivateCommand(String action, Bundle data) {
    167         mTextView.onPrivateIMECommand(action, data);
    168         return true;
    169     }
    170 
    171     @Override
    172     public boolean commitText(CharSequence text, int newCursorPosition) {
    173         if (mTextView == null) {
    174             return super.commitText(text, newCursorPosition);
    175         }
    176         if (text instanceof Spanned) {
    177             Spanned spanned = ((Spanned) text);
    178             SuggestionSpan[] spans = spanned.getSpans(0, text.length(), SuggestionSpan.class);
    179             mIMM.registerSuggestionSpansForNotification(spans);
    180         }
    181 
    182         mTextView.resetErrorChangedFlag();
    183         boolean success = super.commitText(text, newCursorPosition);
    184         mTextView.hideErrorIfUnchanged();
    185 
    186         return success;
    187     }
    188 }
    189