| Index: chrome/android/java_staging/src/org/chromium/chrome/browser/omnibox/SuggestionView.java
|
| diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/omnibox/SuggestionView.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/omnibox/SuggestionView.java
|
| deleted file mode 100644
|
| index e179347544bb4a5acb87ff9f3fe0eaa093167a1b..0000000000000000000000000000000000000000
|
| --- a/chrome/android/java_staging/src/org/chromium/chrome/browser/omnibox/SuggestionView.java
|
| +++ /dev/null
|
| @@ -1,994 +0,0 @@
|
| -// Copyright 2015 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -package org.chromium.chrome.browser.omnibox;
|
| -
|
| -import static org.chromium.chrome.browser.omnibox.OmniboxSuggestion.Type.HISTORY_URL;
|
| -
|
| -import android.annotation.SuppressLint;
|
| -import android.content.Context;
|
| -import android.content.DialogInterface;
|
| -import android.content.res.TypedArray;
|
| -import android.graphics.Bitmap;
|
| -import android.graphics.Canvas;
|
| -import android.graphics.Color;
|
| -import android.graphics.PorterDuff;
|
| -import android.graphics.drawable.Drawable;
|
| -import android.support.v7.app.AlertDialog;
|
| -import android.text.Spannable;
|
| -import android.text.SpannableString;
|
| -import android.text.TextPaint;
|
| -import android.text.TextUtils;
|
| -import android.text.style.StyleSpan;
|
| -import android.view.MotionEvent;
|
| -import android.view.View;
|
| -import android.view.ViewGroup;
|
| -import android.widget.ImageView;
|
| -import android.widget.TextView;
|
| -import android.widget.TextView.BufferType;
|
| -
|
| -import org.chromium.base.ApiCompatibilityUtils;
|
| -import org.chromium.base.metrics.RecordUserAction;
|
| -import org.chromium.chrome.R;
|
| -import org.chromium.chrome.browser.omnibox.OmniboxResultsAdapter.OmniboxResultItem;
|
| -import org.chromium.chrome.browser.omnibox.OmniboxResultsAdapter.OmniboxSuggestionDelegate;
|
| -import org.chromium.chrome.browser.omnibox.OmniboxSuggestion.Type;
|
| -import org.chromium.chrome.browser.widget.TintedDrawable;
|
| -import org.chromium.ui.base.DeviceFormFactor;
|
| -
|
| -import java.util.Locale;
|
| -
|
| -/**
|
| - * Container view for omnibox suggestions made very specific for omnibox suggestions to minimize
|
| - * any unnecessary measures and layouts.
|
| - */
|
| -class SuggestionView extends ViewGroup {
|
| - private enum SuggestionIconType {
|
| - BOOKMARK,
|
| - HISTORY,
|
| - GLOBE,
|
| - MAGNIFIER,
|
| - VOICE
|
| - }
|
| -
|
| - private static final int FIRST_LINE_TEXT_SIZE_SP = 17;
|
| - private static final int SECOND_LINE_TEXT_SIZE_SP = 14;
|
| -
|
| - private static final long RELAYOUT_DELAY_MS = 20;
|
| -
|
| - private static final int TITLE_COLOR_STANDARD_FONT_DARK = Color.rgb(51, 51, 51);
|
| - private static final int TITLE_COLOR_STANDARD_FONT_LIGHT = Color.rgb(255, 255, 255);
|
| - private static final int URL_COLOR = Color.rgb(85, 149, 254);
|
| -
|
| - private static final int ANSWER_IMAGE_HORIZONTAL_SPACING_DP = 4;
|
| - private static final int ANSWER_IMAGE_VERTICAL_SPACING_DP = 5;
|
| - private static final float ANSWER_IMAGE_SCALING_FACTOR = 1.15f;
|
| -
|
| - private LocationBar mLocationBar;
|
| - private UrlBar mUrlBar;
|
| - private ImageView mNavigationButton;
|
| -
|
| - private int mSuggestionHeight;
|
| - private int mSuggestionAnswerHeight;
|
| -
|
| - private OmniboxResultItem mSuggestionItem;
|
| - private OmniboxSuggestion mSuggestion;
|
| - private OmniboxSuggestionDelegate mSuggestionDelegate;
|
| - private Boolean mUseDarkColors;
|
| - private int mPosition;
|
| -
|
| - private SuggestionContentsContainer mContentsView;
|
| -
|
| - private int mRefineWidth;
|
| - private View mRefineView;
|
| - private TintedDrawable mRefineIcon;
|
| -
|
| - private final int[] mViewPositionHolder = new int[2];
|
| -
|
| - /**
|
| - * Constructs a new omnibox suggestion view.
|
| - *
|
| - * @param context The context used to construct the suggestion view.
|
| - * @param locationBar The location bar showing these suggestions.
|
| - */
|
| - public SuggestionView(Context context, LocationBar locationBar) {
|
| - super(context);
|
| - mLocationBar = locationBar;
|
| -
|
| - mSuggestionHeight =
|
| - context.getResources().getDimensionPixelOffset(R.dimen.omnibox_suggestion_height);
|
| - mSuggestionAnswerHeight =
|
| - context.getResources().getDimensionPixelOffset(
|
| - R.dimen.omnibox_suggestion_answer_height);
|
| -
|
| - TypedArray a = getContext().obtainStyledAttributes(
|
| - new int [] {R.attr.selectableItemBackground});
|
| - Drawable itemBackground = a.getDrawable(0);
|
| - a.recycle();
|
| -
|
| - mContentsView = new SuggestionContentsContainer(context, itemBackground);
|
| - addView(mContentsView);
|
| -
|
| - mRefineView = new View(context) {
|
| - @Override
|
| - protected void onDraw(Canvas canvas) {
|
| - super.onDraw(canvas);
|
| -
|
| - if (mRefineIcon == null) return;
|
| - canvas.save();
|
| - canvas.translate(
|
| - (getMeasuredWidth() - mRefineIcon.getIntrinsicWidth()) / 2f,
|
| - (getMeasuredHeight() - mRefineIcon.getIntrinsicHeight()) / 2f);
|
| - mRefineIcon.draw(canvas);
|
| - canvas.restore();
|
| - }
|
| -
|
| - @Override
|
| - public void setVisibility(int visibility) {
|
| - super.setVisibility(visibility);
|
| -
|
| - if (visibility == VISIBLE) {
|
| - setClickable(true);
|
| - setFocusable(true);
|
| - } else {
|
| - setClickable(false);
|
| - setFocusable(false);
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - protected void drawableStateChanged() {
|
| - super.drawableStateChanged();
|
| -
|
| - if (mRefineIcon != null && mRefineIcon.isStateful()) {
|
| - mRefineIcon.setState(getDrawableState());
|
| - }
|
| - }
|
| - };
|
| - mRefineView.setContentDescription(getContext().getString(
|
| - R.string.accessibility_omnibox_btn_refine));
|
| -
|
| - // Although this has the same background as the suggestion view, it can not be shared as
|
| - // it will result in the state of the drawable being shared and always showing up in the
|
| - // refine view.
|
| - mRefineView.setBackground(itemBackground.getConstantState().newDrawable());
|
| - mRefineView.setId(R.id.refine_view_id);
|
| - mRefineView.setClickable(true);
|
| - mRefineView.setFocusable(true);
|
| - mRefineView.setLayoutParams(new LayoutParams(0, 0));
|
| - addView(mRefineView);
|
| -
|
| - mRefineWidth = (int) (getResources().getDisplayMetrics().density * 48);
|
| -
|
| - mUrlBar = (UrlBar) locationBar.getContainerView().findViewById(R.id.url_bar);
|
| - }
|
| -
|
| - @Override
|
| - protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
| - if (getMeasuredWidth() == 0) return;
|
| -
|
| - if (mSuggestion.getType() != Type.SEARCH_SUGGEST_TAIL) {
|
| - mContentsView.resetTextWidths();
|
| - }
|
| -
|
| - boolean refineVisible = mRefineView.getVisibility() == VISIBLE;
|
| - boolean isRtl = ApiCompatibilityUtils.isLayoutRtl(this);
|
| - int contentsViewOffsetX = isRtl ? mRefineWidth : 0;
|
| - if (!refineVisible) contentsViewOffsetX = 0;
|
| - mContentsView.layout(
|
| - contentsViewOffsetX,
|
| - 0,
|
| - contentsViewOffsetX + mContentsView.getMeasuredWidth(),
|
| - mContentsView.getMeasuredHeight());
|
| - int refineViewOffsetX = isRtl ? 0 : getMeasuredWidth() - mRefineWidth;
|
| - mRefineView.layout(
|
| - refineViewOffsetX,
|
| - 0,
|
| - refineViewOffsetX + mRefineWidth,
|
| - mContentsView.getMeasuredHeight());
|
| - }
|
| -
|
| - @Override
|
| - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
| - int width = MeasureSpec.getSize(widthMeasureSpec);
|
| - int height = mSuggestionHeight;
|
| - if (!TextUtils.isEmpty(mSuggestion.getAnswerContents())) {
|
| - height = mSuggestionAnswerHeight;
|
| - }
|
| - setMeasuredDimension(width, height);
|
| -
|
| - // The width will be specified as 0 when determining the height of the popup, so exit early
|
| - // after setting the height.
|
| - if (width == 0) return;
|
| -
|
| - boolean refineVisible = mRefineView.getVisibility() == VISIBLE;
|
| - int refineWidth = refineVisible ? mRefineWidth : 0;
|
| - mContentsView.measure(
|
| - MeasureSpec.makeMeasureSpec(width - refineWidth, MeasureSpec.EXACTLY),
|
| - MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
|
| - mContentsView.getLayoutParams().width = mContentsView.getMeasuredWidth();
|
| - mContentsView.getLayoutParams().height = mContentsView.getMeasuredHeight();
|
| -
|
| - mRefineView.measure(
|
| - MeasureSpec.makeMeasureSpec(mRefineWidth, MeasureSpec.EXACTLY),
|
| - MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
|
| - mRefineView.getLayoutParams().width = mRefineView.getMeasuredWidth();
|
| - mRefineView.getLayoutParams().height = mRefineView.getMeasuredHeight();
|
| - }
|
| -
|
| - @Override
|
| - public void invalidate() {
|
| - super.invalidate();
|
| - mContentsView.invalidate();
|
| - }
|
| -
|
| - @Override
|
| - public boolean dispatchTouchEvent(MotionEvent ev) {
|
| - // Whenever the suggestion dropdown is touched, we dispatch onGestureDown which is
|
| - // used to let autocomplete controller know that it should stop updating suggestions.
|
| - if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) mSuggestionDelegate.onGestureDown();
|
| - return super.dispatchTouchEvent(ev);
|
| - }
|
| -
|
| - /**
|
| - * Sets the contents and state of the view for the given suggestion.
|
| - *
|
| - * @param suggestionItem The omnibox suggestion item this view represents.
|
| - * @param suggestionDelegate The suggestion delegate.
|
| - * @param position Position of the suggestion in the dropdown list.
|
| - * @param useDarkColors Whether dark colors should be used for fonts and icons.
|
| - */
|
| - public void init(OmniboxResultItem suggestionItem,
|
| - OmniboxSuggestionDelegate suggestionDelegate,
|
| - int position, boolean useDarkColors) {
|
| - // Update the position unconditionally.
|
| - mPosition = position;
|
| - jumpDrawablesToCurrentState();
|
| - boolean colorsChanged = mUseDarkColors == null || mUseDarkColors != useDarkColors;
|
| - if (suggestionItem.equals(mSuggestionItem) && !colorsChanged) return;
|
| - mUseDarkColors = useDarkColors;
|
| - if (colorsChanged) {
|
| - mContentsView.mTextLine1.setTextColor(getStandardFontColor());
|
| - setRefineIcon(true);
|
| - }
|
| -
|
| - mSuggestionItem = suggestionItem;
|
| - mSuggestion = suggestionItem.getSuggestion();
|
| - mSuggestionDelegate = suggestionDelegate;
|
| - // Reset old computations.
|
| - mContentsView.resetTextWidths();
|
| - mContentsView.mAnswerImage.setVisibility(GONE);
|
| - mContentsView.mAnswerImage.getLayoutParams().height = 0;
|
| - mContentsView.mAnswerImage.getLayoutParams().width = 0;
|
| - mContentsView.mAnswerImage.setImageDrawable(null);
|
| - mContentsView.mAnswerImageMaxSize = 0;
|
| - mContentsView.mTextLine1.setTextSize(FIRST_LINE_TEXT_SIZE_SP);
|
| - mContentsView.mTextLine2.setTextSize(SECOND_LINE_TEXT_SIZE_SP);
|
| -
|
| - // Suggestions with attached answers are rendered with rich results regardless of which
|
| - // suggestion type they are.
|
| - if (mSuggestion.hasAnswer()) {
|
| - setAnswer(mSuggestion.getAnswer());
|
| - mContentsView.setSuggestionIcon(SuggestionIconType.MAGNIFIER, colorsChanged);
|
| - mContentsView.mTextLine2.setVisibility(VISIBLE);
|
| - setRefinable(true);
|
| - return;
|
| - }
|
| -
|
| - boolean sameAsTyped =
|
| - suggestionItem.getMatchedQuery().equalsIgnoreCase(mSuggestion.getDisplayText());
|
| - Type suggestionType = mSuggestion.getType();
|
| - switch (suggestionType) {
|
| - case HISTORY_URL:
|
| - case URL_WHAT_YOU_TYPED:
|
| - case NAVSUGGEST:
|
| - case HISTORY_TITLE:
|
| - case HISTORY_BODY:
|
| - case HISTORY_KEYWORD:
|
| - case OPEN_HISTORY_PAGE:
|
| - if (mSuggestion.isStarred()) {
|
| - mContentsView.setSuggestionIcon(SuggestionIconType.BOOKMARK, colorsChanged);
|
| - } else if (suggestionType == HISTORY_URL) {
|
| - mContentsView.setSuggestionIcon(SuggestionIconType.HISTORY, colorsChanged);
|
| - } else {
|
| - mContentsView.setSuggestionIcon(SuggestionIconType.GLOBE, colorsChanged);
|
| - }
|
| - boolean urlShown = !TextUtils.isEmpty(mSuggestion.getUrl());
|
| - boolean urlHighlighted = false;
|
| - if (urlShown) {
|
| - urlHighlighted = setUrlText(suggestionItem);
|
| - } else {
|
| - mContentsView.mTextLine2.setVisibility(INVISIBLE);
|
| - }
|
| - setSuggestedQuery(suggestionItem, true, urlShown, urlHighlighted);
|
| - setRefinable(!sameAsTyped);
|
| - break;
|
| - case SEARCH_WHAT_YOU_TYPED:
|
| - case SEARCH_HISTORY:
|
| - case SEARCH_SUGGEST:
|
| - case SEARCH_OTHER_ENGINE:
|
| - case SEARCH_SUGGEST_ENTITY:
|
| - case SEARCH_SUGGEST_TAIL:
|
| - case SEARCH_SUGGEST_PERSONALIZED:
|
| - case SEARCH_SUGGEST_PROFILE:
|
| - case VOICE_SUGGEST:
|
| - SuggestionIconType suggestionIcon = SuggestionIconType.MAGNIFIER;
|
| - if (suggestionType == Type.VOICE_SUGGEST) {
|
| - suggestionIcon = SuggestionIconType.VOICE;
|
| - } else if ((suggestionType == Type.SEARCH_SUGGEST_PERSONALIZED)
|
| - || (suggestionType == Type.SEARCH_HISTORY)) {
|
| - // Show history icon for suggestions based on user queries.
|
| - suggestionIcon = SuggestionIconType.HISTORY;
|
| - }
|
| - mContentsView.setSuggestionIcon(suggestionIcon, colorsChanged);
|
| - setRefinable(!sameAsTyped);
|
| - setSuggestedQuery(suggestionItem, false, false, false);
|
| - if ((suggestionType == Type.SEARCH_SUGGEST_ENTITY)
|
| - || (suggestionType == Type.SEARCH_SUGGEST_PROFILE)) {
|
| - showDescriptionLine(
|
| - SpannableString.valueOf(mSuggestion.getDescription()),
|
| - getStandardFontColor());
|
| - } else {
|
| - mContentsView.mTextLine2.setVisibility(INVISIBLE);
|
| - }
|
| - break;
|
| - default:
|
| - assert false : "Suggestion type (" + mSuggestion.getType() + ") is not handled";
|
| - break;
|
| - }
|
| - }
|
| -
|
| - private void setRefinable(boolean refinable) {
|
| - if (refinable) {
|
| - mRefineView.setVisibility(VISIBLE);
|
| - mRefineView.setOnClickListener(new OnClickListener() {
|
| - @Override
|
| - public void onClick(View v) {
|
| - // Post the refine action to the end of the UI thread to allow the refine view
|
| - // a chance to update its background selection state.
|
| - PerformRefineSuggestion performRefine = new PerformRefineSuggestion();
|
| - if (!post(performRefine)) performRefine.run();
|
| - }
|
| - });
|
| - } else {
|
| - mRefineView.setOnClickListener(null);
|
| - mRefineView.setVisibility(GONE);
|
| - }
|
| - }
|
| -
|
| - private int getStandardFontColor() {
|
| - return (mUseDarkColors == null || mUseDarkColors)
|
| - ? TITLE_COLOR_STANDARD_FONT_DARK : TITLE_COLOR_STANDARD_FONT_LIGHT;
|
| - }
|
| -
|
| - @Override
|
| - public void setSelected(boolean selected) {
|
| - super.setSelected(selected);
|
| - if (selected && !isInTouchMode()) {
|
| - mSuggestionDelegate.onSetUrlToSuggestion(mSuggestion);
|
| - }
|
| - }
|
| -
|
| - private void setRefineIcon(boolean invalidateIcon) {
|
| - if (!invalidateIcon && mRefineIcon != null) return;
|
| -
|
| - mRefineIcon = TintedDrawable.constructTintedDrawable(
|
| - getResources(), R.drawable.btn_suggestion_refine);
|
| - mRefineIcon.setTint(getResources().getColorStateList(mUseDarkColors
|
| - ? R.color.dark_mode_tint
|
| - : R.color.light_mode_tint));
|
| - mRefineIcon.setBounds(
|
| - 0, 0,
|
| - mRefineIcon.getIntrinsicWidth(),
|
| - mRefineIcon.getIntrinsicHeight());
|
| - mRefineIcon.setState(mRefineView.getDrawableState());
|
| - mRefineView.postInvalidateOnAnimation();
|
| - }
|
| -
|
| - /**
|
| - * Sets (and highlights) the URL text of the second line of the omnibox suggestion.
|
| - *
|
| - * @param suggestion The suggestion containing the URL.
|
| - * @return Whether the URL was highlighted based on the user query.
|
| - */
|
| - private boolean setUrlText(OmniboxResultItem suggestion) {
|
| - String query = suggestion.getMatchedQuery();
|
| - String url = suggestion.getSuggestion().getFormattedUrl();
|
| - int index = url.indexOf(query);
|
| - Spannable str = SpannableString.valueOf(url);
|
| - if (index >= 0) {
|
| - // Bold the part of the URL that matches the user query.
|
| - str.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
|
| - index, index + query.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
| - }
|
| - showDescriptionLine(str, URL_COLOR);
|
| - return index >= 0;
|
| - }
|
| -
|
| - /**
|
| - * Sets a description line for the omnibox suggestion.
|
| - *
|
| - * @param str The description text.
|
| - */
|
| - private void showDescriptionLine(Spannable str, int textColor) {
|
| - if (mContentsView.mTextLine2.getVisibility() != VISIBLE) {
|
| - mContentsView.mTextLine2.setVisibility(VISIBLE);
|
| - }
|
| - mContentsView.mTextLine2.setTextColor(textColor);
|
| - mContentsView.mTextLine2.setText(str, BufferType.SPANNABLE);
|
| - }
|
| -
|
| - /**
|
| - * Sets the text of the first line of the omnibox suggestion.
|
| - *
|
| - * @param suggestionItem The item containing the suggestion data.
|
| - * @param showDescriptionIfPresent Whether to show the description text of the suggestion if
|
| - * the item contains valid data.
|
| - * @param isUrlQuery Whether this suggestion is showing an URL.
|
| - * @param isUrlHighlighted Whether the URL contains any highlighted matching sections.
|
| - */
|
| - private void setSuggestedQuery(
|
| - OmniboxResultItem suggestionItem, boolean showDescriptionIfPresent,
|
| - boolean isUrlQuery, boolean isUrlHighlighted) {
|
| - String userQuery = suggestionItem.getMatchedQuery();
|
| - String suggestedQuery = null;
|
| - OmniboxSuggestion suggestion = suggestionItem.getSuggestion();
|
| - if (showDescriptionIfPresent && !TextUtils.isEmpty(suggestion.getUrl())
|
| - && !TextUtils.isEmpty(suggestion.getDescription())) {
|
| - suggestedQuery = suggestion.getDescription();
|
| - } else {
|
| - suggestedQuery = suggestion.getDisplayText();
|
| - }
|
| - if (suggestedQuery == null) {
|
| - assert false : "Invalid suggestion sent with no displayable text";
|
| - suggestedQuery = "";
|
| - } else if (suggestedQuery.equals(suggestion.getUrl())) {
|
| - // This is a navigation match with the title defaulted to the URL, display formatted URL
|
| - // so that they continue matching.
|
| - suggestedQuery = suggestion.getFormattedUrl();
|
| - }
|
| -
|
| - if (mSuggestion.getType() == Type.SEARCH_SUGGEST_TAIL) {
|
| - String fillIntoEdit = mSuggestion.getFillIntoEdit();
|
| - // Data sanity checks.
|
| - if (fillIntoEdit.startsWith(userQuery)
|
| - && fillIntoEdit.endsWith(suggestedQuery)
|
| - && fillIntoEdit.length() < userQuery.length() + suggestedQuery.length()) {
|
| - String ignoredPrefix = fillIntoEdit.substring(
|
| - 0, fillIntoEdit.length() - suggestedQuery.length());
|
| - final String ellipsisPrefix = "\u2026 ";
|
| - suggestedQuery = ellipsisPrefix + suggestedQuery;
|
| - if (userQuery.startsWith(ignoredPrefix)) {
|
| - userQuery = ellipsisPrefix + userQuery.substring(ignoredPrefix.length());
|
| - }
|
| - if (DeviceFormFactor.isTablet(getContext())) {
|
| - TextPaint tp = mContentsView.mTextLine1.getPaint();
|
| - mContentsView.mRequiredWidth =
|
| - tp.measureText(fillIntoEdit, 0, fillIntoEdit.length());
|
| - mContentsView.mMatchContentsWidth =
|
| - tp.measureText(suggestedQuery, 0, suggestedQuery.length());
|
| -
|
| - // Update the max text widths values in SuggestionList. These will be passed to
|
| - // the contents view on layout.
|
| - mSuggestionDelegate.onTextWidthsUpdated(
|
| - mContentsView.mRequiredWidth, mContentsView.mMatchContentsWidth);
|
| - }
|
| - }
|
| - }
|
| -
|
| - Spannable str = SpannableString.valueOf(suggestedQuery);
|
| - int userQueryIndex = isUrlHighlighted ? -1
|
| - : suggestedQuery.toLowerCase(Locale.getDefault()).indexOf(
|
| - userQuery.toLowerCase(Locale.getDefault()));
|
| - if (userQueryIndex != -1) {
|
| - int spanStart = 0;
|
| - int spanEnd = 0;
|
| - if (isUrlQuery) {
|
| - spanStart = userQueryIndex;
|
| - spanEnd = userQueryIndex + userQuery.length();
|
| - } else {
|
| - spanStart = userQueryIndex + userQuery.length();
|
| - spanEnd = str.length();
|
| - }
|
| - spanStart = Math.min(spanStart, str.length());
|
| - spanEnd = Math.min(spanEnd, str.length());
|
| - if (spanStart != spanEnd) {
|
| - str.setSpan(
|
| - new StyleSpan(android.graphics.Typeface.BOLD),
|
| - spanStart, spanEnd,
|
| - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
| - }
|
| - }
|
| - mContentsView.mTextLine1.setText(str, BufferType.SPANNABLE);
|
| - }
|
| -
|
| - /**
|
| - * Sets both lines of the Omnibox suggestion based on an Answers in Suggest result.
|
| - *
|
| - * @param answer The answer to be displayed.
|
| - */
|
| - private void setAnswer(SuggestionAnswer answer) {
|
| - float density = getResources().getDisplayMetrics().density;
|
| -
|
| - SuggestionAnswer.ImageLine firstLine = answer.getFirstLine();
|
| - mContentsView.mTextLine1.setTextSize(AnswerTextBuilder.getMaxTextHeightSp(firstLine));
|
| - Spannable firstLineText = AnswerTextBuilder.buildSpannable(
|
| - firstLine, mContentsView.mTextLine1.getPaint().getFontMetrics(), density);
|
| - mContentsView.mTextLine1.setText(firstLineText, BufferType.SPANNABLE);
|
| -
|
| - SuggestionAnswer.ImageLine secondLine = answer.getSecondLine();
|
| - mContentsView.mTextLine2.setTextSize(AnswerTextBuilder.getMaxTextHeightSp(secondLine));
|
| - Spannable secondLineText = AnswerTextBuilder.buildSpannable(
|
| - secondLine, mContentsView.mTextLine2.getPaint().getFontMetrics(), density);
|
| - mContentsView.mTextLine2.setText(secondLineText, BufferType.SPANNABLE);
|
| -
|
| - if (secondLine.hasImage()) {
|
| - mContentsView.mAnswerImage.setVisibility(VISIBLE);
|
| -
|
| - float textSize = mContentsView.mTextLine2.getTextSize();
|
| - int imageSize = (int) (textSize * ANSWER_IMAGE_SCALING_FACTOR);
|
| - mContentsView.mAnswerImage.getLayoutParams().height = imageSize;
|
| - mContentsView.mAnswerImage.getLayoutParams().width = imageSize;
|
| - mContentsView.mAnswerImageMaxSize = imageSize;
|
| -
|
| - String url = "https:" + secondLine.getImage().replace("\\/", "/");
|
| - AnswersImage.requestAnswersImage(
|
| - mLocationBar.getCurrentTab().getProfile(),
|
| - url,
|
| - new AnswersImage.AnswersImageObserver() {
|
| - @Override
|
| - public void onAnswersImageChanged(Bitmap bitmap) {
|
| - mContentsView.mAnswerImage.setImageBitmap(bitmap);
|
| - }
|
| - });
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Handles triggering a selection request for the suggestion rendered by this view.
|
| - */
|
| - private class PerformSelectSuggestion implements Runnable {
|
| - @Override
|
| - public void run() {
|
| - mSuggestionDelegate.onSelection(mSuggestion, mPosition);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Handles triggering a refine request for the suggestion rendered by this view.
|
| - */
|
| - private class PerformRefineSuggestion implements Runnable {
|
| - @Override
|
| - public void run() {
|
| - mSuggestionDelegate.onRefineSuggestion(mSuggestion);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * @return The left offset for the suggestion text that will align it with the url text.
|
| - */
|
| - private int getUrlBarTextLeftPosition() {
|
| - mUrlBar.getLocationOnScreen(mViewPositionHolder);
|
| - return mViewPositionHolder[0] + mUrlBar.getPaddingLeft();
|
| - }
|
| -
|
| - /**
|
| - * @return The right offset for the suggestion text that will align it with the url text.
|
| - */
|
| - private int getUrlBarTextRightPosition() {
|
| - mUrlBar.getLocationOnScreen(mViewPositionHolder);
|
| - return mViewPositionHolder[0] + mUrlBar.getWidth() - mUrlBar.getPaddingRight();
|
| - }
|
| -
|
| - /**
|
| - * Container view for the contents of the suggestion (the search query, URL, and suggestion type
|
| - * icon).
|
| - */
|
| - private class SuggestionContentsContainer extends ViewGroup implements OnLayoutChangeListener {
|
| - private int mSuggestionIconLeft = Integer.MIN_VALUE;
|
| - private int mTextLeft = Integer.MIN_VALUE;
|
| - private int mTextRight = Integer.MIN_VALUE;
|
| - private Drawable mSuggestionIcon;
|
| - private SuggestionIconType mSuggestionIconType;
|
| -
|
| - private final TextView mTextLine1;
|
| - private final TextView mTextLine2;
|
| - private final ImageView mAnswerImage;
|
| -
|
| - private int mAnswerImageMaxSize; // getMaxWidth() is API 16+, so store it locally.
|
| - private float mRequiredWidth;
|
| - private float mMatchContentsWidth;
|
| - private boolean mForceIsFocused;
|
| -
|
| - private final Runnable mRelayoutRunnable = new Runnable() {
|
| - @Override
|
| - public void run() {
|
| - requestLayout();
|
| - }
|
| - };
|
| -
|
| - @SuppressLint("InlinedApi")
|
| - SuggestionContentsContainer(Context context, Drawable backgroundDrawable) {
|
| - super(context);
|
| -
|
| - ApiCompatibilityUtils.setLayoutDirection(this, View.LAYOUT_DIRECTION_INHERIT);
|
| -
|
| - setBackground(backgroundDrawable);
|
| - setClickable(true);
|
| - setFocusable(true);
|
| - setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, mSuggestionHeight));
|
| - setOnClickListener(new OnClickListener() {
|
| - @Override
|
| - public void onClick(View v) {
|
| - // Post the selection action to the end of the UI thread to allow the suggestion
|
| - // view a chance to update their background selection state.
|
| - PerformSelectSuggestion performSelection = new PerformSelectSuggestion();
|
| - if (!post(performSelection)) performSelection.run();
|
| - }
|
| - });
|
| - setOnLongClickListener(new OnLongClickListener() {
|
| - @Override
|
| - public boolean onLongClick(View v) {
|
| - RecordUserAction.record("MobileOmniboxDeleteGesture");
|
| - if (!mSuggestion.isDeletable()) return true;
|
| -
|
| - AlertDialog.Builder b =
|
| - new AlertDialog.Builder(getContext(), R.style.AlertDialogTheme);
|
| - b.setTitle(mSuggestion.getDisplayText());
|
| - b.setMessage(R.string.omnibox_confirm_delete);
|
| - DialogInterface.OnClickListener okListener =
|
| - new DialogInterface.OnClickListener() {
|
| - @Override
|
| - public void onClick(DialogInterface dialog, int which) {
|
| - RecordUserAction.record("MobileOmniboxDeleteRequested");
|
| - mSuggestionDelegate.onDeleteSuggestion(mPosition);
|
| - }
|
| - };
|
| - b.setPositiveButton(android.R.string.ok, okListener);
|
| - DialogInterface.OnClickListener cancelListener =
|
| - new DialogInterface.OnClickListener() {
|
| - @Override
|
| - public void onClick(DialogInterface dialog, int which) {
|
| - dialog.cancel();
|
| - }
|
| - };
|
| - b.setNegativeButton(android.R.string.cancel, cancelListener);
|
| -
|
| - AlertDialog dialog = b.create();
|
| - dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
|
| - @Override
|
| - public void onDismiss(DialogInterface dialog) {
|
| - mSuggestionDelegate.onHideModal();
|
| - }
|
| - });
|
| -
|
| - mSuggestionDelegate.onShowModal();
|
| - dialog.show();
|
| - return true;
|
| - }
|
| - });
|
| -
|
| - mTextLine1 = new TextView(context);
|
| - mTextLine1.setLayoutParams(
|
| - new LayoutParams(LayoutParams.WRAP_CONTENT, mSuggestionHeight));
|
| - mTextLine1.setSingleLine();
|
| - mTextLine1.setTextColor(getStandardFontColor());
|
| - addView(mTextLine1);
|
| -
|
| - mTextLine2 = new TextView(context);
|
| - mTextLine2.setLayoutParams(
|
| - new LayoutParams(LayoutParams.WRAP_CONTENT, mSuggestionHeight));
|
| - mTextLine2.setSingleLine();
|
| - mTextLine2.setVisibility(INVISIBLE);
|
| - addView(mTextLine2);
|
| -
|
| - mAnswerImage = new ImageView(context);
|
| - mAnswerImage.setVisibility(GONE);
|
| - mAnswerImage.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
| - mAnswerImage.setLayoutParams(new LayoutParams(0, 0));
|
| - mAnswerImageMaxSize = 0;
|
| - addView(mAnswerImage);
|
| - }
|
| -
|
| - private void resetTextWidths() {
|
| - mRequiredWidth = 0;
|
| - mMatchContentsWidth = 0;
|
| - }
|
| -
|
| - @Override
|
| - protected void onDraw(Canvas canvas) {
|
| - super.onDraw(canvas);
|
| -
|
| - if (DeviceFormFactor.isTablet(getContext())) {
|
| - // Use the same image transform matrix as the navigation icon to ensure the same
|
| - // scaling, which requires centering vertically based on the height of the
|
| - // navigation icon view and not the image itself.
|
| - canvas.save();
|
| - mSuggestionIconLeft = getSuggestionIconLeftPosition();
|
| - canvas.translate(
|
| - mSuggestionIconLeft,
|
| - (getMeasuredHeight() - mNavigationButton.getMeasuredHeight()) / 2f);
|
| - canvas.concat(mNavigationButton.getImageMatrix());
|
| - mSuggestionIcon.draw(canvas);
|
| - canvas.restore();
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
| - if (child != mTextLine1 && child != mTextLine2 && child != mAnswerImage) {
|
| - return super.drawChild(canvas, child, drawingTime);
|
| - }
|
| -
|
| - int height = getMeasuredHeight();
|
| - int line1Height = mTextLine1.getMeasuredHeight();
|
| - int line2Height = mTextLine2.getVisibility() == VISIBLE
|
| - ? mTextLine2.getMeasuredHeight() : 0;
|
| -
|
| - int verticalOffset = 0;
|
| - if (line1Height + line2Height > height) {
|
| - // The text lines total height is larger than this view, snap them to the top and
|
| - // bottom of the view.
|
| - if (child == mTextLine1) {
|
| - verticalOffset = 0;
|
| - } else {
|
| - verticalOffset = height - line2Height;
|
| - }
|
| - } else {
|
| - // The text lines fit comfortably, so vertically center them.
|
| - verticalOffset = (height - line1Height - line2Height) / 2;
|
| - if (child == mTextLine2) verticalOffset += line1Height;
|
| -
|
| - // When one line is larger than the other, it contains extra vertical padding. This
|
| - // produces more apparent whitespace above or below the text lines. Add a small
|
| - // offset to compensate.
|
| - if (line1Height != line2Height) {
|
| - verticalOffset += (line2Height - line1Height) / 10;
|
| - }
|
| -
|
| - // The image is positioned vertically aligned with the second text line but
|
| - // requires a small additional offset to align with the ascent of the text instead
|
| - // of the top of the text which includes some whitespace.
|
| - if (child == mAnswerImage) {
|
| - verticalOffset += ANSWER_IMAGE_VERTICAL_SPACING_DP
|
| - * getResources().getDisplayMetrics().density;
|
| - }
|
| - }
|
| -
|
| - canvas.save();
|
| - canvas.translate(0, verticalOffset);
|
| - boolean retVal = super.drawChild(canvas, child, drawingTime);
|
| - canvas.restore();
|
| - return retVal;
|
| - }
|
| -
|
| - @Override
|
| - protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
| - View locationBarView = mLocationBar.getContainerView();
|
| - if (mUrlBar == null) {
|
| - mUrlBar = (UrlBar) locationBarView.findViewById(R.id.url_bar);
|
| - mUrlBar.addOnLayoutChangeListener(this);
|
| - }
|
| - if (mNavigationButton == null) {
|
| - mNavigationButton =
|
| - (ImageView) locationBarView.findViewById(R.id.navigation_button);
|
| - mNavigationButton.addOnLayoutChangeListener(this);
|
| - }
|
| -
|
| - // Align the text to be pixel perfectly aligned with the text in the url bar.
|
| - mTextLeft = getSuggestionTextLeftPosition();
|
| - mTextRight = getSuggestionTextRightPosition();
|
| - boolean isRTL = ApiCompatibilityUtils.isLayoutRtl(this);
|
| - if (DeviceFormFactor.isTablet(getContext())) {
|
| - int textWidth = isRTL ? mTextRight : (r - l - mTextLeft);
|
| - final float maxRequiredWidth = mSuggestionDelegate.getMaxRequiredWidth();
|
| - final float maxMatchContentsWidth = mSuggestionDelegate.getMaxMatchContentsWidth();
|
| - float paddingStart = (textWidth > maxRequiredWidth)
|
| - ? (mRequiredWidth - mMatchContentsWidth)
|
| - : Math.max(textWidth - maxMatchContentsWidth, 0);
|
| - ApiCompatibilityUtils.setPaddingRelative(
|
| - mTextLine1, (int) paddingStart, mTextLine1.getPaddingTop(),
|
| - 0, // TODO(skanuj) : Change to ApiCompatibilityUtils.getPaddingEnd(...).
|
| - mTextLine1.getPaddingBottom());
|
| - }
|
| -
|
| - int imageWidth = mAnswerImageMaxSize;
|
| - int imageSpacing = 0;
|
| - if (mAnswerImage.getVisibility() == VISIBLE && imageWidth > 0) {
|
| - float density = getResources().getDisplayMetrics().density;
|
| - imageSpacing = (int) (ANSWER_IMAGE_HORIZONTAL_SPACING_DP * density);
|
| - }
|
| - if (isRTL) {
|
| - mTextLine1.layout(0, t, mTextRight, b);
|
| - mAnswerImage.layout(mTextRight - imageWidth , t, mTextRight, b);
|
| - mTextLine2.layout(0, t, mTextRight - (imageWidth + imageSpacing), b);
|
| - } else {
|
| - mTextLine1.layout(mTextLeft, t, r - l, b);
|
| - mAnswerImage.layout(mTextLeft, t, mTextLeft + imageWidth, b);
|
| - mTextLine2.layout(mTextLeft + imageWidth + imageSpacing, t, r - l, b);
|
| - }
|
| -
|
| - int suggestionIconPosition = getSuggestionIconLeftPosition();
|
| - if (mSuggestionIconLeft != suggestionIconPosition
|
| - && mSuggestionIconLeft != Integer.MIN_VALUE) {
|
| - mContentsView.postInvalidateOnAnimation();
|
| - }
|
| - mSuggestionIconLeft = suggestionIconPosition;
|
| - }
|
| -
|
| - /**
|
| - * @return The left offset for the suggestion text that will align it with the url text.
|
| - */
|
| - private int getSuggestionTextLeftPosition() {
|
| - if (mLocationBar == null) return 0;
|
| -
|
| - int textLeftPosition = getUrlBarTextLeftPosition();
|
| - getLocationOnScreen(mViewPositionHolder);
|
| - return textLeftPosition - mViewPositionHolder[0];
|
| - }
|
| -
|
| - /**
|
| - * @return The right offset for the suggestion text that will align it with the url text.
|
| - */
|
| - private int getSuggestionTextRightPosition() {
|
| - if (mLocationBar == null) return 0;
|
| -
|
| - int textRightPosition = getUrlBarTextRightPosition();
|
| - getLocationOnScreen(mViewPositionHolder);
|
| - return textRightPosition - mViewPositionHolder[0];
|
| - }
|
| -
|
| - /**
|
| - * @return The left offset for the suggestion type icon that aligns it with the url bar.
|
| - */
|
| - private int getSuggestionIconLeftPosition() {
|
| - if (mNavigationButton == null) return 0;
|
| -
|
| - // Ensure the suggestion icon matches the location of the navigation icon in the omnibox
|
| - // perfectly.
|
| - mNavigationButton.getLocationOnScreen(mViewPositionHolder);
|
| - int navButtonXPosition = mViewPositionHolder[0] + mNavigationButton.getPaddingLeft();
|
| -
|
| - getLocationOnScreen(mViewPositionHolder);
|
| -
|
| - return navButtonXPosition - mViewPositionHolder[0];
|
| - }
|
| -
|
| - @Override
|
| - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
| - super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
| -
|
| - int width = MeasureSpec.getSize(widthMeasureSpec);
|
| - int height = MeasureSpec.getSize(heightMeasureSpec);
|
| -
|
| - if (mTextLine1.getMeasuredWidth() != width
|
| - || mTextLine1.getMeasuredHeight() != height) {
|
| - mTextLine1.measure(
|
| - MeasureSpec.makeMeasureSpec(widthMeasureSpec, MeasureSpec.AT_MOST),
|
| - MeasureSpec.makeMeasureSpec(mSuggestionHeight, MeasureSpec.AT_MOST));
|
| - }
|
| -
|
| - if (mTextLine2.getMeasuredWidth() != width
|
| - || mTextLine2.getMeasuredHeight() != height) {
|
| - mTextLine2.measure(
|
| - MeasureSpec.makeMeasureSpec(widthMeasureSpec, MeasureSpec.AT_MOST),
|
| - MeasureSpec.makeMeasureSpec(mSuggestionHeight, MeasureSpec.AT_MOST));
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public void invalidate() {
|
| - if (getSuggestionTextLeftPosition() != mTextLeft
|
| - || getSuggestionTextRightPosition() != mTextRight) {
|
| - // When the text position is changed, it typically is caused by the suggestions
|
| - // appearing while the URL bar on the phone is gaining focus (if you trigger an
|
| - // intent that will result in suggestions being shown before focusing the omnibox).
|
| - // Triggering a relayout will cause any animations to stutter, so we continually
|
| - // push the relayout to end of the UI queue until the animation is complete.
|
| - removeCallbacks(mRelayoutRunnable);
|
| - postDelayed(mRelayoutRunnable, RELAYOUT_DELAY_MS);
|
| - } else {
|
| - super.invalidate();
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public boolean isFocused() {
|
| - return mForceIsFocused || super.isFocused();
|
| - }
|
| -
|
| - @Override
|
| - protected int[] onCreateDrawableState(int extraSpace) {
|
| - // When creating the drawable states, treat selected as focused to get the proper
|
| - // highlight when in non-touch mode (i.e. physical keyboard). This is because only
|
| - // a single view in a window can have focus, and the these will only appear if
|
| - // the omnibox has focus, so we trick the drawable state into believing it has it.
|
| - mForceIsFocused = isSelected() && !isInTouchMode();
|
| - int[] drawableState = super.onCreateDrawableState(extraSpace);
|
| - mForceIsFocused = false;
|
| - return drawableState;
|
| - }
|
| -
|
| - private void setSuggestionIcon(SuggestionIconType type, boolean invalidateCurrentIcon) {
|
| - if (mSuggestionIconType == type && !invalidateCurrentIcon) return;
|
| -
|
| - int drawableId = R.drawable.ic_omnibox_page;
|
| - switch (type) {
|
| - case BOOKMARK:
|
| - drawableId = R.drawable.btn_star;
|
| - break;
|
| - case MAGNIFIER:
|
| - drawableId = R.drawable.ic_suggestion_magnifier;
|
| - break;
|
| - case HISTORY:
|
| - drawableId = R.drawable.ic_suggestion_history;
|
| - break;
|
| - case VOICE:
|
| - drawableId = R.drawable.btn_mic;
|
| - break;
|
| - default:
|
| - break;
|
| - }
|
| - mSuggestionIcon = ApiCompatibilityUtils.getDrawable(getResources(), drawableId);
|
| - mSuggestionIcon.setColorFilter(mUseDarkColors
|
| - ? getResources().getColor(R.color.light_normal_color)
|
| - : Color.WHITE, PorterDuff.Mode.SRC_IN);
|
| - mSuggestionIcon.setBounds(
|
| - 0, 0,
|
| - mSuggestionIcon.getIntrinsicWidth(),
|
| - mSuggestionIcon.getIntrinsicHeight());
|
| - mSuggestionIconType = type;
|
| - invalidate();
|
| - }
|
| -
|
| - @Override
|
| - public void onLayoutChange(
|
| - View v, int left, int top, int right, int bottom, int oldLeft,
|
| - int oldTop, int oldRight, int oldBottom) {
|
| - boolean needsInvalidate = false;
|
| - if (v == mNavigationButton) {
|
| - if (mSuggestionIconLeft != getSuggestionIconLeftPosition()
|
| - && mSuggestionIconLeft != Integer.MIN_VALUE) {
|
| - needsInvalidate = true;
|
| - }
|
| - } else {
|
| - if (mTextLeft != getSuggestionTextLeftPosition()
|
| - && mTextLeft != Integer.MIN_VALUE) {
|
| - needsInvalidate = true;
|
| - }
|
| - if (mTextRight != getSuggestionTextRightPosition()
|
| - && mTextRight != Integer.MIN_VALUE) {
|
| - needsInvalidate = true;
|
| - }
|
| - }
|
| - if (needsInvalidate) {
|
| - removeCallbacks(mRelayoutRunnable);
|
| - postDelayed(mRelayoutRunnable, RELAYOUT_DELAY_MS);
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - protected void onAttachedToWindow() {
|
| - super.onAttachedToWindow();
|
| - if (mNavigationButton != null) mNavigationButton.addOnLayoutChangeListener(this);
|
| - if (mUrlBar != null) mUrlBar.addOnLayoutChangeListener(this);
|
| - if (mLocationBar != null) {
|
| - mLocationBar.getContainerView().addOnLayoutChangeListener(this);
|
| - }
|
| - getRootView().addOnLayoutChangeListener(this);
|
| - }
|
| -
|
| - @Override
|
| - protected void onDetachedFromWindow() {
|
| - if (mNavigationButton != null) mNavigationButton.removeOnLayoutChangeListener(this);
|
| - if (mUrlBar != null) mUrlBar.removeOnLayoutChangeListener(this);
|
| - if (mLocationBar != null) {
|
| - mLocationBar.getContainerView().removeOnLayoutChangeListener(this);
|
| - }
|
| - getRootView().removeOnLayoutChangeListener(this);
|
| -
|
| - super.onDetachedFromWindow();
|
| - }
|
| - }
|
| -}
|
|
|