diff options
Diffstat (limited to 'src/com/android/swe/browser/UrlInputView.java')
-rw-r--r-- | src/com/android/swe/browser/UrlInputView.java | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/src/com/android/swe/browser/UrlInputView.java b/src/com/android/swe/browser/UrlInputView.java new file mode 100644 index 00000000..02bba3cb --- /dev/null +++ b/src/com/android/swe/browser/UrlInputView.java @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2010 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 com.android.browser; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.util.Patterns; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AutoCompleteTextView; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; + +import com.android.browser.SuggestionsAdapter.CompletionListener; +import com.android.browser.SuggestionsAdapter.SuggestItem; +import com.android.browser.search.SearchEngine; +import com.android.browser.search.SearchEngineInfo; +import com.android.browser.search.SearchEngines; +import com.android.internal.R; + +import java.util.List; + +/** + * url/search input view + * handling suggestions + */ +public class UrlInputView extends AutoCompleteTextView + implements OnEditorActionListener, + CompletionListener, OnItemClickListener, TextWatcher { + + static final String TYPED = "browser-type"; + static final String SUGGESTED = "browser-suggest"; + + static final int POST_DELAY = 100; + + static interface StateListener { + static final int STATE_NORMAL = 0; + static final int STATE_HIGHLIGHTED = 1; + static final int STATE_EDITED = 2; + + public void onStateChanged(int state); + } + + private UrlInputListener mListener; + private InputMethodManager mInputManager; + private SuggestionsAdapter mAdapter; + private View mContainer; + private boolean mLandscape; + private boolean mIncognitoMode; + private boolean mNeedsUpdate; + + private int mState; + private StateListener mStateListener; + private Rect mPopupPadding; + + public UrlInputView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + TypedArray a = context.obtainStyledAttributes( + attrs, com.android.internal.R.styleable.PopupWindow, + R.attr.autoCompleteTextViewStyle, 0); + + Drawable popupbg = a.getDrawable(R.styleable.PopupWindow_popupBackground); + a.recycle(); + mPopupPadding = new Rect(); + popupbg.getPadding(mPopupPadding); + init(context); + } + + public UrlInputView(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.autoCompleteTextViewStyle); + } + + public UrlInputView(Context context) { + this(context, null); + } + + private void init(Context ctx) { + mInputManager = (InputMethodManager) ctx.getSystemService(Context.INPUT_METHOD_SERVICE); + setOnEditorActionListener(this); + mAdapter = new SuggestionsAdapter(ctx, this); + setAdapter(mAdapter); + setSelectAllOnFocus(true); + onConfigurationChanged(ctx.getResources().getConfiguration()); + setThreshold(1); + setOnItemClickListener(this); + mNeedsUpdate = false; + addTextChangedListener(this); + + mState = StateListener.STATE_NORMAL; + } + + protected void onFocusChanged(boolean focused, int direction, Rect prevRect) { + super.onFocusChanged(focused, direction, prevRect); + int state = -1; + if (focused) { + if (hasSelection()) { + state = StateListener.STATE_HIGHLIGHTED; + } else { + state = StateListener.STATE_EDITED; + } + } else { + // reset the selection state + state = StateListener.STATE_NORMAL; + } + final int s = state; + post(new Runnable() { + public void run() { + changeState(s); + } + }); + } + + @Override + public boolean onTouchEvent(MotionEvent evt) { + boolean hasSelection = hasSelection(); + boolean res = super.onTouchEvent(evt); + if ((MotionEvent.ACTION_DOWN == evt.getActionMasked()) + && hasSelection) { + postDelayed(new Runnable() { + public void run() { + changeState(StateListener.STATE_EDITED); + }}, POST_DELAY); + } + return res; + } + + /** + * check if focus change requires a title bar update + */ + boolean needsUpdate() { + return mNeedsUpdate; + } + + /** + * clear the focus change needs title bar update flag + */ + void clearNeedsUpdate() { + mNeedsUpdate = false; + } + + void setController(UiController controller) { + UrlSelectionActionMode urlSelectionMode + = new UrlSelectionActionMode(controller); + setCustomSelectionActionModeCallback(urlSelectionMode); + } + + void setContainer(View container) { + mContainer = container; + } + + public void setUrlInputListener(UrlInputListener listener) { + mListener = listener; + } + + public void setStateListener(StateListener listener) { + mStateListener = listener; + // update listener + changeState(mState); + } + + private void changeState(int newState) { + mState = newState; + if (mStateListener != null) { + mStateListener.onStateChanged(mState); + } + } + + int getState() { + return mState; + } + + @Override + protected void onConfigurationChanged(Configuration config) { + super.onConfigurationChanged(config); + mLandscape = (config.orientation & + Configuration.ORIENTATION_LANDSCAPE) != 0; + mAdapter.setLandscapeMode(mLandscape); + if (isPopupShowing() && (getVisibility() == View.VISIBLE)) { + setupDropDown(); + performFiltering(getText(), 0); + } + } + + @Override + public void showDropDown() { + setupDropDown(); + super.showDropDown(); + } + + @Override + public void dismissDropDown() { + super.dismissDropDown(); + mAdapter.clearCache(); + } + + private void setupDropDown() { + int width = mContainer != null ? mContainer.getWidth() : getWidth(); + width += mPopupPadding.left + mPopupPadding.right; + if (width != getDropDownWidth()) { + setDropDownWidth(width); + } + int left = getLeft(); + left += mPopupPadding.left; + if (left != -getDropDownHorizontalOffset()) { + setDropDownHorizontalOffset(-left); + } + } + + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + finishInput(getText().toString(), null, TYPED); + return true; + } + + void forceFilter() { + showDropDown(); + } + + void hideIME() { + mInputManager.hideSoftInputFromWindow(getWindowToken(), 0); + } + + void showIME() { + mInputManager.focusIn(this); + mInputManager.showSoftInput(this, 0); + } + + private void finishInput(String url, String extra, String source) { + mNeedsUpdate = true; + dismissDropDown(); + mInputManager.hideSoftInputFromWindow(getWindowToken(), 0); + if (TextUtils.isEmpty(url)) { + mListener.onDismiss(); + } else { + if (mIncognitoMode && isSearch(url)) { + // To prevent logging, intercept this request + // TODO: This is a quick hack, refactor this + SearchEngine searchEngine = BrowserSettings.getInstance() + .getSearchEngine(); + if (searchEngine == null) return; + SearchEngineInfo engineInfo = SearchEngines + .getSearchEngineInfo(mContext, searchEngine.getName()); + if (engineInfo == null) return; + url = engineInfo.getSearchUriForQuery(url); + // mLister.onAction can take it from here without logging + } + mListener.onAction(url, extra, source); + } + } + + boolean isSearch(String inUrl) { + String url = UrlUtils.fixUrl(inUrl).trim(); + if (TextUtils.isEmpty(url)) return false; + + if (Patterns.WEB_URL.matcher(url).matches() + || UrlUtils.ACCEPTED_URI_SCHEMA.matcher(url).matches()) { + return false; + } + return true; + } + + // Completion Listener + + @Override + public void onSearch(String search) { + mListener.onCopySuggestion(search); + } + + @Override + public void onSelect(String url, int type, String extra) { + finishInput(url, extra, SUGGESTED); + } + + @Override + public void onItemClick( + AdapterView<?> parent, View view, int position, long id) { + SuggestItem item = mAdapter.getItem(position); + onSelect(SuggestionsAdapter.getSuggestionUrl(item), item.type, item.extra); + } + + interface UrlInputListener { + + public void onDismiss(); + + public void onAction(String text, String extra, String source); + + public void onCopySuggestion(String text); + + } + + public void setIncognitoMode(boolean incognito) { + mIncognitoMode = incognito; + mAdapter.setIncognitoMode(mIncognitoMode); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent evt) { + if (keyCode == KeyEvent.KEYCODE_ESCAPE && !isInTouchMode()) { + finishInput(null, null, null); + return true; + } + return super.onKeyDown(keyCode, evt); + } + + public SuggestionsAdapter getAdapter() { + return mAdapter; + } + + /* + * no-op to prevent scrolling of webview when embedded titlebar + * gets edited + */ + @Override + public boolean requestRectangleOnScreen(Rect rect, boolean immediate) { + return false; + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (StateListener.STATE_HIGHLIGHTED == mState) { + changeState(StateListener.STATE_EDITED); + } + } + + @Override + public void afterTextChanged(Editable s) { } + +} |