FindActionModeCallback.java revision fe026bdd3c0fd7543ceaf0732aeb824cfddb5b23
1/*
2 * Copyright (C) 2010 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.webkit;
18
19import android.content.Context;
20import android.content.res.Resources;
21import android.text.Editable;
22import android.text.Selection;
23import android.text.Spannable;
24import android.text.TextWatcher;
25import android.webkit.WebView;
26import android.widget.EditText;
27import android.widget.TextView;
28import android.view.ActionMode;
29import android.view.LayoutInflater;
30import android.view.Menu;
31import android.view.MenuInflater;
32import android.view.MenuItem;
33import android.view.View;
34import android.view.inputmethod.InputMethodManager;
35
36class FindActionModeCallback implements ActionMode.Callback, TextWatcher,
37        View.OnLongClickListener {
38    private View mCustomView;
39    private EditText mEditText;
40    private TextView mMatches;
41    private WebView mWebView;
42    private InputMethodManager mInput;
43    private Resources mResources;
44    private boolean mMatchesFound;
45    private int mNumberOfMatches;
46    private View mTitleBar;
47
48    FindActionModeCallback(Context context) {
49        mCustomView = LayoutInflater.from(context).inflate(
50                com.android.internal.R.layout.webview_find, null);
51        mEditText = (EditText) mCustomView.findViewById(
52                com.android.internal.R.id.edit);
53        // Override long click so that select ActionMode is not opened, which
54        // would exit find ActionMode.
55        mEditText.setOnLongClickListener(this);
56        setText("");
57        mMatches = (TextView) mCustomView.findViewById(
58                com.android.internal.R.id.matches);
59        mInput = (InputMethodManager)
60                context.getSystemService(Context.INPUT_METHOD_SERVICE);
61        mResources = context.getResources();
62    }
63
64    void setTitleBar(View v) { mTitleBar = v; }
65
66    /*
67     * Place text in the text field so it can be searched for.  Need to press
68     * the find next or find previous button to find all of the matches.
69     */
70    void setText(String text) {
71        mEditText.setText(text);
72        Spannable span = (Spannable) mEditText.getText();
73        int length = span.length();
74        // Ideally, we would like to set the selection to the whole field,
75        // but this brings up the Text selection CAB, which dismisses this
76        // one.
77        Selection.setSelection(span, length, length);
78        // Necessary each time we set the text, so that this will watch
79        // changes to it.
80        span.setSpan(this, 0, length, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
81        mMatchesFound = false;
82    }
83
84    /*
85     * Set the WebView to search.  Must be non null, and set before calling
86     * startActionMode.
87     */
88    void setWebView(WebView webView) {
89        if (null == webView) {
90            throw new AssertionError("WebView supplied to "
91                    + "FindActionModeCallback cannot be null");
92        }
93        mWebView = webView;
94    }
95
96    /*
97     * Move the highlight to the next match.
98     * @param next If true, find the next match further down in the document.
99     *             If false, find the previous match, up in the document.
100     */
101    private void findNext(boolean next) {
102        if (mWebView == null) {
103            throw new AssertionError(
104                    "No WebView for FindActionModeCallback::findNext");
105        }
106        mWebView.findNext(next);
107    }
108
109    /*
110     * Highlight all the instances of the string from mEditText in mWebView.
111     */
112    void findAll() {
113        if (mWebView == null) {
114            throw new AssertionError(
115                    "No WebView for FindActionModeCallback::findAll");
116        }
117        CharSequence find = mEditText.getText();
118        if (0 == find.length()) {
119            mWebView.clearMatches();
120            mMatches.setVisibility(View.GONE);
121            mMatchesFound = false;
122        } else {
123            mMatchesFound = true;
124            mMatches.setVisibility(View.VISIBLE);
125            mNumberOfMatches = mWebView.findAll(find.toString());
126            if (0 == mNumberOfMatches) {
127                mMatches.setText(mResources.getString(
128                        com.android.internal.R.string.no_matches));
129            } else {
130                updateMatchesString();
131            }
132        }
133    }
134
135    /*
136     * Update the string which tells the user how many matches were found, and
137     * which match is currently highlighted.
138     */
139    private void updateMatchesString() {
140        String template = mResources.getQuantityString(
141                com.android.internal.R.plurals.matches_found, mNumberOfMatches,
142                mWebView.findIndex() + 1, mNumberOfMatches);
143
144        mMatches.setText(template);
145    }
146
147    // OnLongClickListener implementation
148
149    @Override
150    public boolean onLongClick(View v) { return true; }
151
152    // ActionMode.Callback implementation
153
154    @Override
155    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
156        mode.setCustomView(mCustomView);
157        mode.getMenuInflater().inflate(com.android.internal.R.menu.webview_find,
158                menu);
159        Editable edit = mEditText.getText();
160        Selection.setSelection(edit, edit.length());
161        mMatches.setVisibility(View.GONE);
162        mMatchesFound = false;
163        mMatches.setText("0");
164        mEditText.requestFocus();
165        mInput.showSoftInput(mEditText, 0);
166        return true;
167    }
168
169    @Override
170    public void onDestroyActionMode(ActionMode mode) {
171        if (mTitleBar != null) mWebView.setEmbeddedTitleBar(mTitleBar);
172        mWebView.notifyFindDialogDismissed();
173        mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
174    }
175
176    @Override
177    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
178        return false;
179    }
180
181    @Override
182    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
183        if (!mMatchesFound) {
184            findAll();
185            return true;
186        }
187        switch(item.getItemId()) {
188            case com.android.internal.R.id.find_prev:
189                findNext(false);
190                break;
191            case com.android.internal.R.id.find_next:
192                findNext(true);
193                break;
194            default:
195                return false;
196        }
197        updateMatchesString();
198        return true;
199    }
200
201    // TextWatcher implementation
202
203    @Override
204    public void beforeTextChanged(CharSequence s,
205                                  int start,
206                                  int count,
207                                  int after) {
208        // Does nothing.  Needed to implement TextWatcher.
209    }
210
211    @Override
212    public void onTextChanged(CharSequence s,
213                              int start,
214                              int before,
215                              int count) {
216        findAll();
217    }
218
219    @Override
220    public void afterTextChanged(Editable s) {
221        // Does nothing.  Needed to implement TextWatcher.
222    }
223
224}
225