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
17package android.inputmethodservice;
18
19import android.content.Context;
20import android.util.AttributeSet;
21import android.view.inputmethod.ExtractedText;
22import android.view.inputmethod.InputMethodManager;
23import android.widget.EditText;
24
25/***
26 * Specialization of {@link EditText} for showing and interacting with the
27 * extracted text in a full-screen input method.
28 */
29public class ExtractEditText extends EditText {
30    private InputMethodService mIME;
31    private int mSettingExtractedText;
32
33    public ExtractEditText(Context context) {
34        super(context, null);
35    }
36
37    public ExtractEditText(Context context, AttributeSet attrs) {
38        super(context, attrs, com.android.internal.R.attr.editTextStyle);
39    }
40
41    public ExtractEditText(Context context, AttributeSet attrs, int defStyle) {
42        super(context, attrs, defStyle);
43    }
44
45    void setIME(InputMethodService ime) {
46        mIME = ime;
47    }
48
49    /**
50     * Start making changes that will not be reported to the client.  That
51     * is, {@link #onSelectionChanged(int, int)} will not result in sending
52     * the new selection to the client
53     */
54    public void startInternalChanges() {
55        mSettingExtractedText += 1;
56    }
57
58    /**
59     * Finish making changes that will not be reported to the client.  That
60     * is, {@link #onSelectionChanged(int, int)} will not result in sending
61     * the new selection to the client
62     */
63    public void finishInternalChanges() {
64        mSettingExtractedText -= 1;
65    }
66
67    /**
68     * Implement just to keep track of when we are setting text from the
69     * client (vs. seeing changes in ourself from the user).
70     */
71    @Override public void setExtractedText(ExtractedText text) {
72        try {
73            mSettingExtractedText++;
74            super.setExtractedText(text);
75        } finally {
76            mSettingExtractedText--;
77        }
78    }
79
80    /**
81     * Report to the underlying text editor about selection changes.
82     */
83    @Override protected void onSelectionChanged(int selStart, int selEnd) {
84        if (mSettingExtractedText == 0 && mIME != null && selStart >= 0 && selEnd >= 0) {
85            mIME.onExtractedSelectionChanged(selStart, selEnd);
86        }
87    }
88
89    /**
90     * Redirect clicks to the IME for handling there.  First allows any
91     * on click handler to run, though.
92     */
93    @Override public boolean performClick() {
94        if (!super.performClick() && mIME != null) {
95            mIME.onExtractedTextClicked();
96            return true;
97        }
98        return false;
99    }
100
101    @Override public boolean onTextContextMenuItem(int id) {
102        if (mIME != null && mIME.onExtractTextContextMenuItem(id)) {
103            // Mode was started on Extracted, needs to be stopped here.
104            // Cut and paste will change the text, which stops selection mode.
105            if (id == android.R.id.copy) stopSelectionActionMode();
106            return true;
107        }
108        return super.onTextContextMenuItem(id);
109    }
110
111    /**
112     * We are always considered to be an input method target.
113     */
114    @Override
115    public boolean isInputMethodTarget() {
116        return true;
117    }
118
119    /**
120     * Return true if the edit text is currently showing a scroll bar.
121     */
122    public boolean hasVerticalScrollBar() {
123        return computeVerticalScrollRange() > computeVerticalScrollExtent();
124    }
125
126    /**
127     * Pretend like the window this view is in always has focus, so its
128     * highlight and cursor will be displayed.
129     */
130    @Override public boolean hasWindowFocus() {
131        return this.isEnabled();
132    }
133
134    /**
135     * Pretend like this view always has focus, so its
136     * highlight and cursor will be displayed.
137     */
138    @Override public boolean isFocused() {
139        return this.isEnabled();
140    }
141
142    /**
143     * Pretend like this view always has focus, so its
144     * highlight and cursor will be displayed.
145     */
146    @Override public boolean hasFocus() {
147        return this.isEnabled();
148    }
149
150    /**
151     * @hide
152     */
153    @Override protected void viewClicked(InputMethodManager imm) {
154        // As an instance of this class is supposed to be owned by IMS,
155        // and it has a reference to the IMS (the current IME),
156        // we just need to call back its onViewClicked() here.
157        // It should be good to avoid unnecessary IPCs by doing this as well.
158        if (mIME != null) {
159            mIME.onViewClicked(false);
160        }
161    }
162
163    /**
164     * {@inheritDoc}
165     * @hide
166     */
167    @Override
168    protected void deleteText_internal(int start, int end) {
169        // Do not call the super method.
170        // This will change the source TextView instead, which will update the ExtractTextView.
171        mIME.onExtractedDeleteText(start, end);
172    }
173
174    /**
175     * {@inheritDoc}
176     * @hide
177     */
178    @Override
179    protected void replaceText_internal(int start, int end, CharSequence text) {
180        // Do not call the super method.
181        // This will change the source TextView instead, which will update the ExtractTextView.
182        mIME.onExtractedReplaceText(start, end, text);
183    }
184
185    /**
186     * {@inheritDoc}
187     * @hide
188     */
189    @Override
190    protected void setSpan_internal(Object span, int start, int end, int flags) {
191        // Do not call the super method.
192        // This will change the source TextView instead, which will update the ExtractTextView.
193        mIME.onExtractedSetSpan(span, start, end, flags);
194    }
195
196    /**
197     * {@inheritDoc}
198     * @hide
199     */
200    @Override
201    protected void setCursorPosition_internal(int start, int end) {
202        // Do not call the super method.
203        // This will change the source TextView instead, which will update the ExtractTextView.
204        mIME.onExtractedSelectionChanged(start, end);
205    }
206}
207