• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
EditableInputConnection(TextView textview)43     public EditableInputConnection(TextView textview) {
44         super(textview, true);
45         mTextView = textview;
46     }
47 
48     @Override
getEditable()49     public Editable getEditable() {
50         TextView tv = mTextView;
51         if (tv != null) {
52             return tv.getEditableText();
53         }
54         return null;
55     }
56 
57     @Override
beginBatchEdit()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
endBatchEdit()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
reportFinish()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
clearMetaKeyStates(int states)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
commitCompletion(CompletionInfo text)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
commitCorrection(CorrectionInfo correctionInfo)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
performEditorAction(int actionCode)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
performContextMenuAction(int id)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
getExtractedText(ExtractedTextRequest request, int flags)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
performPrivateCommand(String action, Bundle data)166     public boolean performPrivateCommand(String action, Bundle data) {
167         mTextView.onPrivateIMECommand(action, data);
168         return true;
169     }
170 
171     @Override
commitText(CharSequence text, int newCursorPosition)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