diff options
| author | Narayan Kamath <narayan@google.com> | 2011-02-22 12:05:34 +0000 |
|---|---|---|
| committer | Narayan Kamath <narayan@google.com> | 2011-03-01 11:11:00 +0000 |
| commit | 9497c5f8c4bc7c47789e5ccde01179abc31ffeb2 (patch) | |
| tree | 3f5080139a3fd6809df6b7ce077e885def2d7e36 /core/java/android/webkit/SearchBoxImpl.java | |
| parent | 43cdf9b4191e18b5a4d646d23b06438c0e10b8dd (diff) | |
A Java implementation of the SearchBox API.
Implemented as a Java bridge object and a JS adapter.
Works on both V8 and JSC.
Change-Id: I8a4268fb3c6688b5ece9e27c11724c0776017255
Diffstat (limited to 'core/java/android/webkit/SearchBoxImpl.java')
| -rw-r--r-- | core/java/android/webkit/SearchBoxImpl.java | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/core/java/android/webkit/SearchBoxImpl.java b/core/java/android/webkit/SearchBoxImpl.java new file mode 100644 index 000000000000..480f5d7a5b44 --- /dev/null +++ b/core/java/android/webkit/SearchBoxImpl.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import android.util.Log; +import android.webkit.WebViewCore.EventHub; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONStringer; + +/** + * The default implementation of the SearchBox interface. Implemented + * as a java bridge object and a javascript adapter that is called into + * by the page hosted in the frame. + */ +final class SearchBoxImpl implements SearchBox { + private static final String TAG = "WebKit.SearchBoxImpl"; + + /* package */ static final String JS_INTERFACE_NAME = "searchBoxJavaBridge_"; + + /* package */ static final String JS_BRIDGE + = "(function()" + + "{" + + "if (!window.chrome) {" + + " window.chrome = {};" + + "}" + + "if (!window.chrome.searchBox) {" + + " var sb = window.chrome.searchBox = {};" + + " sb.setSuggestions = function(suggestions) {" + + " if (window.searchBoxJavaBridge_) {" + + " window.searchBoxJavaBridge_.setSuggestions(JSON.stringify(suggestions));" + + " }" + + " };" + + " sb.setValue = function(valueArray) { sb.value = valueArray[0]; };" + + " sb.value = '';" + + " sb.x = 0;" + + " sb.y = 0;" + + " sb.width = 0;" + + " sb.height = 0;" + + " sb.selectionStart = 0;" + + " sb.selectionEnd = 0;" + + " sb.verbatim = false;" + + "}" + + "})();"; + + private static final String SET_QUERY_SCRIPT + = "if (window.chrome && window.chrome.searchBox) {" + + " window.chrome.searchBox.setValue(%s);" + + "}"; + + private static final String SET_VERBATIM_SCRIPT + = "if (window.chrome && window.chrome.searchBox) {" + + " window.chrome.searchBox.verbatim = %s;" + + "}"; + + private static final String SET_SELECTION_SCRIPT + = "if (window.chrome && window.chrome.searchBox) {" + + " var f = window.chrome.searchBox;" + + " f.selectionStart = %d" + + " f.selectionEnd = %d" + + "}"; + + private static final String SET_DIMENSIONS_SCRIPT + = "if (window.chrome && window.chrome.searchBox) { " + + " var f = window.chrome.searchBox;" + + " f.x = %d;" + + " f.y = %d;" + + " f.width = %d;" + + " f.height = %d;" + + "}"; + + private static final String DISPATCH_EVENT_SCRIPT + = "if (window.chrome && window.chrome.searchBox &&" + + " window.chrome.searchBox.on%1$s) { window.chrome.searchBox.on%1$s(); }"; + + private final List<SearchBoxListener> mListeners; + private final WebViewCore mWebViewCore; + private final CallbackProxy mCallbackProxy; + + SearchBoxImpl(WebViewCore webViewCore, CallbackProxy callbackProxy) { + mListeners = new ArrayList<SearchBoxListener>(); + mWebViewCore = webViewCore; + mCallbackProxy = callbackProxy; + } + + @Override + public void setQuery(String query) { + final String formattedQuery = jsonSerialize(query); + if (formattedQuery != null) { + final String js = String.format(SET_QUERY_SCRIPT, formattedQuery); + dispatchJs(js); + } + } + + @Override + public void setVerbatim(boolean verbatim) { + final String js = String.format(SET_VERBATIM_SCRIPT, String.valueOf(verbatim)); + dispatchJs(js); + } + + + @Override + public void setSelection(int selectionStart, int selectionEnd) { + final String js = String.format(SET_SELECTION_SCRIPT, selectionStart, selectionEnd); + dispatchJs(js); + } + + @Override + public void setDimensions(int x, int y, int width, int height) { + final String js = String.format(SET_DIMENSIONS_SCRIPT, x, y, width, height); + dispatchJs(js); + } + + @Override + public void onchange() { + dispatchEvent("change"); + } + + @Override + public void onsubmit() { + dispatchEvent("submit"); + } + + @Override + public void onresize() { + dispatchEvent("resize"); + } + + @Override + public void oncancel() { + dispatchEvent("cancel"); + } + + private void dispatchEvent(String eventName) { + final String js = String.format(DISPATCH_EVENT_SCRIPT, eventName); + dispatchJs(js); + } + + private void dispatchJs(String js) { + mWebViewCore.sendMessage(EventHub.EXECUTE_JS, js); + } + + @Override + public void addSearchBoxListener(SearchBoxListener l) { + synchronized (mListeners) { + mListeners.add(l); + } + } + + @Override + public void removeSearchBoxListener(SearchBoxListener l) { + synchronized (mListeners) { + mListeners.remove(l); + } + } + + // This is used as a hackish alternative to javascript escaping. + // There appears to be no such functionality in the core framework. + private String jsonSerialize(String query) { + JSONStringer stringer = new JSONStringer(); + try { + stringer.array().value(query).endArray(); + } catch (JSONException e) { + Log.w(TAG, "Error serializing query : " + query); + return null; + } + return stringer.toString(); + } + + // Called by Javascript through the Java bridge. + public void setSuggestions(String jsonArguments) { + if (jsonArguments == null) { + return; + } + + String query = null; + List<String> suggestions = new ArrayList<String>(); + try { + JSONObject suggestionsJson = new JSONObject(jsonArguments); + query = suggestionsJson.getString("query"); + + final JSONArray suggestionsArray = suggestionsJson.getJSONArray("suggestions"); + for (int i = 0; i < suggestionsArray.length(); ++i) { + final JSONObject suggestion = suggestionsArray.getJSONObject(i); + final String value = suggestion.getString("value"); + if (value != null) { + suggestions.add(value); + } + // We currently ignore the "type" of the suggestion. This isn't + // documented anywhere in the API documents. + // final String type = suggestions.getString("type"); + } + } catch (JSONException je) { + Log.w(TAG, "Error parsing json [" + jsonArguments + "], exception = " + je); + return; + } + + mCallbackProxy.onSearchboxSuggestionsReceived(query, suggestions); + } + + /* package */ void handleSuggestions(String query, List<String> suggestions) { + synchronized (mListeners) { + for (int i = mListeners.size() - 1; i >= 0; i--) { + mListeners.get(i).onSuggestionsReceived(query, suggestions); + } + } + } +} |
