Commit 7bd63cba authored by Tomasz Wiszkowski's avatar Tomasz Wiszkowski Committed by Commit Bot

Use Base suggestion view with Answer suggestions.

This change removes custom Refinable icon from AnswerSuggestions
in favor of base suggestion view.

Change-Id: If460ec749cfd3786388b0423da2f61c805ec0c6f
Doc: http://doc/1aL_UcW1gdeSLqNzuJZlQPBqMG0XAwVaoa05UTyVulp8
Bug: 982818
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1848391
Commit-Queue: Ender <ender@google.com>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarBrandon Wylie <wylieb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#706599}
parent 7c2af342
......@@ -670,6 +670,7 @@ junit_binary("chrome_junit_tests") {
"//chrome/android/webapk/libs/client:client_java",
"//chrome/android/webapk/libs/common:common_java",
"//chrome/android/webapk/test:junit_test_support",
"//chrome/browser/image_fetcher:java",
"//chrome/browser/ui/android/widget:ui_widget_junit_tests",
"//chrome/test/android:chrome_java_test_support",
"//components/background_task_scheduler:background_task_scheduler_java",
......
......@@ -1128,7 +1128,6 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionProcessor.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/VoiceSuggestionProvider.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionView.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewBinder.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionViewProperties.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerText.java",
......
......@@ -154,6 +154,7 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTest.java",
"junit/src/org/chromium/chrome/browser/omnibox/geo/VisibleNetworksTrackerTest.java",
"junit/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java",
"junit/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java",
"junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java",
"junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/SimpleHorizontalLayoutViewTest.java",
"junit/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorTest.java",
......
......@@ -2,75 +2,32 @@
<!-- Copyright 2019 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. -->
<org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionView
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:clickable="false"
android:focusable="false"
android:id="@+id/omnibox_answer"
android:layout_width="match_parent"
android:layout_height="@dimen/omnibox_suggestion_answer_height"
android:layout_width="match_parent">
android:layout_centerVertical="true"
android:orientation="vertical"
android:paddingVertical="10dp">
<view class="org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionView$FocusableView"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:id="@+id/omnibox_answer"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:paddingVertical="10dp"
<TextView
android:ellipsize="end"
android:id="@+id/omnibox_answer_line_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toStartOf="@+id/omnibox_answer_refine_icon"
android:layout_width="0dp">
android:maxLines="1"
android:singleLine="true"
android:textAlignment="viewStart" />
<ImageView
android:contentDescription="@null"
android:id="@+id/omnibox_answer_icon"
android:layout_centerVertical="true"
android:layout_height="36dp"
android:layout_marginEnd="@dimen/omnibox_suggestion_36dp_icon_margin_end"
android:layout_marginStart="@dimen/omnibox_suggestion_36dp_icon_margin_start"
android:layout_width="36dp"
android:scaleType="fitCenter" />
<TextView
android:ellipsize="end"
android:id="@+id/omnibox_answer_line_1"
android:layout_alignParentEnd="true"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/omnibox_answer_icon"
android:layout_width="0dp"
android:maxLines="1"
android:singleLine="true"
android:textAlignment="viewStart" />
<TextView
android:ellipsize="end"
android:id="@+id/omnibox_answer_line_2"
android:layout_alignEnd="@id/omnibox_answer_line_1"
android:layout_alignStart="@id/omnibox_answer_line_1"
android:layout_below="@id/omnibox_answer_line_1"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:maxLines="3"
android:singleLine="false"
android:textAlignment="viewStart" />
</view>
<ImageView
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:contentDescription="@string/accessibility_omnibox_btn_refine"
android:focusable="true"
android:id="@id/omnibox_answer_refine_icon"
android:layout_alignBottom="@id/omnibox_answer"
android:layout_alignParentEnd="true"
android:layout_alignTop="@id/omnibox_answer"
android:layout_centerVertical="true"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/omnibox_suggestion_refine_view_modern_end_padding"
android:layout_width="@dimen/omnibox_suggestion_refine_width"
android:scaleType="center"
android:src="@drawable/btn_suggestion_refine" />
<TextView
android:ellipsize="end"
android:id="@+id/omnibox_answer_line_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="3"
android:singleLine="false"
android:textAlignment="viewStart" />
</org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionView>
</LinearLayout>
......@@ -24,8 +24,8 @@ import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler;
import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteController.OnSuggestionsReceivedListener;
import org.chromium.chrome.browser.omnibox.suggestions.SuggestionListViewBinder.SuggestionListViewHolder;
import org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionView;
import org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionViewBinder;
import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionView;
import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionView;
import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewViewBinder;
import org.chromium.chrome.browser.omnibox.suggestions.editurl.EditUrlSuggestionProcessor;
......@@ -129,9 +129,9 @@ public class AutocompleteCoordinatorImpl implements AutocompleteCoordinator {
adapter.registerType(
OmniboxSuggestionUiType.ANSWER_SUGGESTION,
() -> (AnswerSuggestionView) LayoutInflater.from(mListView.getContext())
.inflate(R.layout.omnibox_answer_suggestion, null),
AnswerSuggestionViewBinder::bind);
() -> new BaseSuggestionView(mListView.getContext(),
R.layout.omnibox_answer_suggestion),
new AnswerSuggestionViewBinder());
adapter.registerType(
OmniboxSuggestionUiType.ENTITY_SUGGESTION,
......
......@@ -6,9 +6,11 @@ package org.chromium.chrome.browser.omnibox.suggestions.answer;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.annotation.DrawableRes;
import org.chromium.base.ThreadUtils;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.GlobalDiscardableReferencePool;
import org.chromium.chrome.browser.image_fetcher.ImageFetcher;
import org.chromium.chrome.browser.image_fetcher.ImageFetcherConfig;
......@@ -17,10 +19,9 @@ import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
import org.chromium.chrome.browser.omnibox.suggestions.SuggestionProcessor;
import org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionViewProperties.AnswerIcon;
import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProcessor;
import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate;
import org.chromium.chrome.browser.util.ConversionUtils;
import org.chromium.components.omnibox.AnswerType;
import org.chromium.components.omnibox.SuggestionAnswer;
......@@ -32,14 +33,13 @@ import java.util.List;
import java.util.Map;
/** A class that handles model and view creation for the most commonly used omnibox suggestion. */
public class AnswerSuggestionProcessor implements SuggestionProcessor {
public class AnswerSuggestionProcessor extends BaseSuggestionViewProcessor {
private static final int MAX_CACHE_SIZE = 500 * ConversionUtils.BYTES_PER_KILOBYTE;
private final Map<String, List<PropertyModel>> mPendingAnswerRequestUrls;
private final Context mContext;
private final SuggestionHost mSuggestionHost;
private ImageFetcher mImageFetcher;
private final UrlBarEditingTextStateProvider mUrlBarEditingTextProvider;
private ImageFetcher mImageFetcher;
/**
* @param context An Android context.
......@@ -47,6 +47,7 @@ public class AnswerSuggestionProcessor implements SuggestionProcessor {
*/
public AnswerSuggestionProcessor(Context context, SuggestionHost suggestionHost,
UrlBarEditingTextStateProvider editingTextProvider) {
super(suggestionHost);
mContext = context;
mSuggestionHost = suggestionHost;
mPendingAnswerRequestUrls = new HashMap<>();
......@@ -82,11 +83,8 @@ public class AnswerSuggestionProcessor implements SuggestionProcessor {
@Override
public void populateModel(OmniboxSuggestion suggestion, PropertyModel model, int position) {
SuggestionViewDelegate delegate =
mSuggestionHost.createSuggestionViewDelegate(suggestion, position);
setStateForNewSuggestion(model, suggestion, delegate);
maybeFetchAnswerIcon(suggestion, model);
super.populateModel(suggestion, model, position);
setStateForSuggestion(model, suggestion);
}
@Override
......@@ -114,7 +112,15 @@ public class AnswerSuggestionProcessor implements SuggestionProcessor {
// https://cs.chromium.org/Omnibox.SuggestionUsed.AnswerInSuggest
}
private void maybeFetchAnswerIcon(OmniboxSuggestion suggestion, PropertyModel model) {
/**
* Specify ImageFetcher instance to be used for testing purposes.
* TODO(ender): Create fetcher instance in AutocompleteMediator and pass it to the constructor.
*/
void setImageFetcherForTesting(ImageFetcher fetcher) {
mImageFetcher = fetcher;
}
private void maybeFetchAnswerIcon(PropertyModel model, OmniboxSuggestion suggestion) {
ThreadUtils.assertOnUiThread();
// Attempting to fetch answer data before we have a profile to request it for.
......@@ -145,13 +151,19 @@ public class AnswerSuggestionProcessor implements SuggestionProcessor {
mImageFetcher.fetchImage(
url, ImageFetcher.ANSWER_SUGGESTIONS_UMA_CLIENT_NAME, (Bitmap bitmap) -> {
ThreadUtils.assertOnUiThread();
// Remove models for the URL ahead of all the checks to ensure we
// do not keep them around waiting in case image fetch failed.
List<PropertyModel> currentModels = mPendingAnswerRequestUrls.remove(url);
if (currentModels == null || bitmap == null) return;
boolean didUpdate = false;
for (int i = 0; i < currentModels.size(); i++) {
PropertyModel currentModel = currentModels.get(i);
if (!mSuggestionHost.isActiveModel(currentModel)) continue;
model.set(AnswerSuggestionViewProperties.ANSWER_IMAGE, bitmap);
setSuggestionDrawableState(currentModel,
SuggestionDrawableState.Builder.forBitmap(bitmap)
.setLarge(true)
.build());
didUpdate = true;
}
if (didUpdate) mSuggestionHost.notifyPropertyModelsChanged();
......@@ -161,17 +173,11 @@ public class AnswerSuggestionProcessor implements SuggestionProcessor {
/**
* Sets both lines of the Omnibox suggestion based on an Answers in Suggest result.
*/
private void setStateForNewSuggestion(
PropertyModel model, OmniboxSuggestion suggestion, SuggestionViewDelegate delegate) {
private void setStateForSuggestion(PropertyModel model, OmniboxSuggestion suggestion) {
SuggestionAnswer answer = suggestion.getAnswer();
AnswerText[] details = AnswerTextNewLayout.from(
mContext, suggestion, mUrlBarEditingTextProvider.getTextWithoutAutocomplete());
model.set(AnswerSuggestionViewProperties.DELEGATE, delegate);
model.set(AnswerSuggestionViewProperties.TEXT_LINE_1_SIZE, details[0].mHeightSp);
model.set(AnswerSuggestionViewProperties.TEXT_LINE_2_SIZE, details[1].mHeightSp);
model.set(AnswerSuggestionViewProperties.TEXT_LINE_1_TEXT, details[0].mText);
model.set(AnswerSuggestionViewProperties.TEXT_LINE_2_TEXT, details[1].mText);
......@@ -183,43 +189,49 @@ public class AnswerSuggestionProcessor implements SuggestionProcessor {
model.set(AnswerSuggestionViewProperties.TEXT_LINE_1_MAX_LINES, details[0].mMaxLines);
model.set(AnswerSuggestionViewProperties.TEXT_LINE_2_MAX_LINES, details[1].mMaxLines);
@AnswerIcon
int icon = AnswerIcon.UNDEFINED;
setSuggestionDrawableState(model,
SuggestionDrawableState.Builder
.forDrawableRes(mContext, getSuggestionIcon(suggestion))
.setLarge(true)
.build());
maybeFetchAnswerIcon(model, suggestion);
}
/**
* Get default suggestion icon for supplied suggestion.
*/
@DrawableRes
int getSuggestionIcon(OmniboxSuggestion suggestion) {
SuggestionAnswer answer = suggestion.getAnswer();
if (answer != null) {
switch (answer.getType()) {
case AnswerType.DICTIONARY:
icon = AnswerIcon.DICTIONARY;
break;
return R.drawable.ic_book_round;
case AnswerType.FINANCE:
icon = AnswerIcon.FINANCE;
break;
return R.drawable.ic_swap_vert_round;
case AnswerType.KNOWLEDGE_GRAPH:
icon = AnswerIcon.KNOWLEDGE;
break;
return R.drawable.ic_google_round;
case AnswerType.SUNRISE:
icon = AnswerIcon.SUNRISE;
break;
return R.drawable.ic_wb_sunny_round;
case AnswerType.TRANSLATION:
icon = AnswerIcon.TRANSLATION;
break;
return R.drawable.logo_translate_round;
case AnswerType.WEATHER:
icon = AnswerIcon.WEATHER;
break;
return R.drawable.logo_partly_cloudy;
case AnswerType.WHEN_IS:
icon = AnswerIcon.EVENT;
break;
return R.drawable.ic_event_round;
case AnswerType.CURRENCY:
icon = AnswerIcon.CURRENCY;
break;
return R.drawable.ic_loop_round;
case AnswerType.SPORTS:
icon = AnswerIcon.SPORTS;
return R.drawable.ic_google_round;
default:
assert false : "Unsupported answer type";
break;
}
} else {
assert suggestion.getType() == OmniboxSuggestionType.CALCULATOR;
icon = AnswerIcon.CALCULATOR;
return R.drawable.ic_equals_sign_round;
}
model.set(AnswerSuggestionViewProperties.ANSWER_ICON_TYPE, icon);
return 0;
}
}
// Copyright 2019 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.suggestions.answer;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.text.Spannable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate;
import org.chromium.chrome.browser.util.ColorUtils;
/**
* Container view for omnibox answer suggestions.
*/
public class AnswerSuggestionView extends RelativeLayout {
private SuggestionViewDelegate mSuggestionDelegate;
private View mAnswerView;
private TextView mTextView1;
private TextView mTextView2;
private ImageView mAnswerIconView;
private ImageView mRefineView;
/**
* Container view for omnibox suggestions allowing soft focus from keyboard.
*/
public static class FocusableView extends RelativeLayout {
/** Creates new instance of FocusableView. */
public FocusableView(Context context, AttributeSet attributes) {
super(context, attributes);
}
@Override
public boolean isFocused() {
return super.isFocused() || (isSelected() && !isInTouchMode());
}
}
/** Creates new instance of AnswerSuggestionView. */
public AnswerSuggestionView(Context context, AttributeSet attributes) {
super(context, attributes);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mTextView1 = findViewById(R.id.omnibox_answer_line_1);
mTextView2 = findViewById(R.id.omnibox_answer_line_2);
mAnswerIconView = findViewById(R.id.omnibox_answer_icon);
mAnswerView = findViewById(R.id.omnibox_answer);
mRefineView = findViewById(R.id.omnibox_answer_refine_icon);
}
@Override
public void setSelected(boolean selected) {
super.setSelected(selected);
mAnswerView.setSelected(selected);
if (selected && !isInTouchMode()) {
postDelegateAction(() -> mSuggestionDelegate.onSetUrlToSuggestion());
}
}
@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.
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mSuggestionDelegate.onGestureDown();
break;
case MotionEvent.ACTION_UP:
mSuggestionDelegate.onGestureUp(ev.getEventTime());
break;
}
return super.dispatchTouchEvent(ev);
}
/** Specify delegate receiving click/refine events. */
void setDelegate(SuggestionViewDelegate delegate) {
mSuggestionDelegate = delegate;
mAnswerView.setOnClickListener(
(View v) -> postDelegateAction(() -> mSuggestionDelegate.onSelection()));
mAnswerView.setOnLongClickListener((View v) -> {
postDelegateAction(() -> mSuggestionDelegate.onLongPress());
return true;
});
mRefineView.setOnClickListener(
(View v) -> postDelegateAction(() -> mSuggestionDelegate.onRefineSuggestion()));
}
/**
* Toggles theme.
* @param useDarkColors specifies whether UI should use dark theme.
*/
void setUseDarkColors(boolean useDarkColors) {
Drawable drawable = mRefineView.getDrawable();
DrawableCompat.setTint(
drawable, ColorUtils.getIconTint(getContext(), !useDarkColors).getDefaultColor());
}
/**
* Specifies text accessibility description of the first text line.
* @param text Text to be announced.
*/
void setLine1AccessibilityDescription(String text) {
mTextView1.setContentDescription(text);
}
/**
* Specifies text accessibility description of the second text line.
* @param text Text to be announced.
*/
void setLine2AccessibilityDescription(String text) {
mTextView2.setContentDescription(text);
}
/**
* Specifies text content of the first text line.
* @param text Text to be displayed.
*/
void setLine1TextContent(Spannable text) {
mTextView1.setText(text);
}
/**
* Specifies text content of the second text line.
* @param text Text to be displayed.
*/
void setLine2TextContent(Spannable text) {
mTextView2.setText(text);
}
/**
* Specifies image bitmap to be displayed as an answer icon.
* @param bitmap Decoded image to be displayed as an answer icon.
*/
void setIconBitmap(Bitmap bitmap) {
BitmapDrawable drawable = new BitmapDrawable(bitmap);
mAnswerIconView.setImageDrawable(drawable);
}
/**
* Specifies fallback image to be presented if no image is available.
* @param res Drawable resource ID to be used in place of answer image.
*/
void setFallbackIconRes(@DrawableRes int res) {
mAnswerIconView.setImageResource(res);
}
/**
* Post delegate action to main thread. Invoked only if delegate is specified.
* @param action Delegate action to invoke on the UI thread.
*/
private void postDelegateAction(Runnable action) {
if (mSuggestionDelegate == null) return;
if (!post(action)) action.run();
}
}
......@@ -4,81 +4,43 @@
package org.chromium.chrome.browser.omnibox.suggestions.answer;
import android.support.v4.view.ViewCompat;
import android.widget.TextView;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties;
import org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionViewProperties.AnswerIcon;
import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionView;
import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewBinder;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
/** A mechanism binding AnswerSuggestion properties to its view. */
public class AnswerSuggestionViewBinder {
public class AnswerSuggestionViewBinder extends BaseSuggestionViewBinder {
/** @see PropertyModelChangeProcessor.ViewBinder#bind(Object, Object, Object) */
public static void bind(
PropertyModel model, AnswerSuggestionView view, PropertyKey propertyKey) {
if (AnswerSuggestionViewProperties.DELEGATE.equals(propertyKey)) {
view.setDelegate(model.get(AnswerSuggestionViewProperties.DELEGATE));
} else if (SuggestionCommonProperties.USE_DARK_COLORS.equals(propertyKey)) {
view.setUseDarkColors(model.get(SuggestionCommonProperties.USE_DARK_COLORS));
} else if (AnswerSuggestionViewProperties.ANSWER_IMAGE.equals(propertyKey)) {
view.setIconBitmap(model.get(AnswerSuggestionViewProperties.ANSWER_IMAGE));
} else if (AnswerSuggestionViewProperties.ANSWER_ICON_TYPE.equals(propertyKey)) {
int type = model.get(AnswerSuggestionViewProperties.ANSWER_ICON_TYPE);
if (type == AnswerIcon.UNDEFINED) return;
view.setFallbackIconRes(getAnswerIcon(type));
} else if (AnswerSuggestionViewProperties.TEXT_LINE_1_TEXT.equals(propertyKey)) {
view.setLine1TextContent(model.get(AnswerSuggestionViewProperties.TEXT_LINE_1_TEXT));
} else if (AnswerSuggestionViewProperties.TEXT_LINE_2_TEXT.equals(propertyKey)) {
view.setLine2TextContent(model.get(AnswerSuggestionViewProperties.TEXT_LINE_2_TEXT));
} else if (AnswerSuggestionViewProperties.TEXT_LINE_1_ACCESSIBILITY_DESCRIPTION.equals(
propertyKey)) {
view.setLine1AccessibilityDescription(model.get(
@Override
public void bind(PropertyModel model, BaseSuggestionView view, PropertyKey propertyKey) {
super.bind(model, view, propertyKey);
if (AnswerSuggestionViewProperties.TEXT_LINE_1_TEXT == propertyKey) {
TextView tv = view.findContentView(R.id.omnibox_answer_line_1);
tv.setText(model.get(AnswerSuggestionViewProperties.TEXT_LINE_1_TEXT));
} else if (AnswerSuggestionViewProperties.TEXT_LINE_2_TEXT == propertyKey) {
TextView tv = view.findContentView(R.id.omnibox_answer_line_2);
tv.setText(model.get(AnswerSuggestionViewProperties.TEXT_LINE_2_TEXT));
} else if (AnswerSuggestionViewProperties.TEXT_LINE_1_ACCESSIBILITY_DESCRIPTION
== propertyKey) {
TextView tv = view.findContentView(R.id.omnibox_answer_line_1);
tv.setContentDescription(model.get(
AnswerSuggestionViewProperties.TEXT_LINE_1_ACCESSIBILITY_DESCRIPTION));
} else if (AnswerSuggestionViewProperties.TEXT_LINE_2_ACCESSIBILITY_DESCRIPTION.equals(
propertyKey)) {
view.setLine2AccessibilityDescription(model.get(
} else if (AnswerSuggestionViewProperties.TEXT_LINE_2_ACCESSIBILITY_DESCRIPTION
== propertyKey) {
TextView tv = view.findContentView(R.id.omnibox_answer_line_2);
tv.setContentDescription(model.get(
AnswerSuggestionViewProperties.TEXT_LINE_2_ACCESSIBILITY_DESCRIPTION));
} else if (SuggestionCommonProperties.LAYOUT_DIRECTION.equals(propertyKey)) {
ViewCompat.setLayoutDirection(
view, model.get(SuggestionCommonProperties.LAYOUT_DIRECTION));
}
}
/**
* Convert AnswerIcon type to drawable resource type representing answer icon.
*
* Answers are not shown when user is in incognito mode, so we can rely on
* configuration UI to define colors for light and dark mode that will be used
* by the icons below.
*
* @param type AnswerIcon type to get drawable for.
*/
private static final int getAnswerIcon(@AnswerIcon int type) {
switch (type) {
case AnswerIcon.CALCULATOR:
return R.drawable.ic_equals_sign_round;
case AnswerIcon.DICTIONARY:
return R.drawable.ic_book_round;
case AnswerIcon.FINANCE:
return R.drawable.ic_swap_vert_round;
case AnswerIcon.KNOWLEDGE:
return R.drawable.ic_google_round;
case AnswerIcon.SUNRISE:
return R.drawable.ic_wb_sunny_round;
case AnswerIcon.TRANSLATION:
return R.drawable.logo_translate_round;
case AnswerIcon.WEATHER:
return R.drawable.logo_partly_cloudy;
case AnswerIcon.EVENT:
return R.drawable.ic_event_round;
case AnswerIcon.CURRENCY:
return R.drawable.ic_loop_round;
case AnswerIcon.SPORTS:
return R.drawable.ic_google_round;
default:
assert false : "Invalid answer type: " + type;
return 0;
} else if (AnswerSuggestionViewProperties.TEXT_LINE_1_MAX_LINES == propertyKey) {
TextView tv = view.findContentView(R.id.omnibox_answer_line_1);
tv.setMaxLines(model.get(AnswerSuggestionViewProperties.TEXT_LINE_1_MAX_LINES));
} else if (AnswerSuggestionViewProperties.TEXT_LINE_2_MAX_LINES == propertyKey) {
TextView tv = view.findContentView(R.id.omnibox_answer_line_2);
tv.setMaxLines(model.get(AnswerSuggestionViewProperties.TEXT_LINE_2_MAX_LINES));
}
}
}
......@@ -4,54 +4,18 @@
package org.chromium.chrome.browser.omnibox.suggestions.answer;
import android.graphics.Bitmap;
import android.text.Spannable;
import androidx.annotation.IntDef;
import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties;
import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate;
import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* The properties associated with rendering the answer suggestion view.
*/
class AnswerSuggestionViewProperties {
@IntDef({AnswerIcon.UNDEFINED, AnswerIcon.CALCULATOR, AnswerIcon.DICTIONARY, AnswerIcon.FINANCE,
AnswerIcon.KNOWLEDGE, AnswerIcon.SUNRISE, AnswerIcon.TRANSLATION, AnswerIcon.WEATHER,
AnswerIcon.EVENT, AnswerIcon.CURRENCY, AnswerIcon.SPORTS})
@Retention(RetentionPolicy.SOURCE)
public @interface AnswerIcon {
int UNDEFINED = 0;
int CALCULATOR = 1;
int DICTIONARY = 2;
int FINANCE = 3;
int KNOWLEDGE = 4;
int SUNRISE = 5;
int TRANSLATION = 6;
int WEATHER = 7;
int EVENT = 8;
int CURRENCY = 9;
int SPORTS = 10;
}
/** The delegate to handle actions on the suggestion view. */
public static final WritableObjectPropertyKey<SuggestionViewDelegate> DELEGATE =
new WritableObjectPropertyKey<>();
/** The answer image to be shown. */
public static final WritableObjectPropertyKey<Bitmap> ANSWER_IMAGE =
new WritableObjectPropertyKey<>();
/** The suggestion icon type shown. */
public static final WritableIntPropertyKey ANSWER_ICON_TYPE = new WritableIntPropertyKey();
/** The sizing information for the first line of text. */
public static final WritableIntPropertyKey TEXT_LINE_1_SIZE = new WritableIntPropertyKey();
/** The maximum number of lines to be shown for the first line of text. */
public static final WritableIntPropertyKey TEXT_LINE_1_MAX_LINES = new WritableIntPropertyKey();
/** The actual text content for the first line of text. */
......@@ -61,8 +25,6 @@ class AnswerSuggestionViewProperties {
public static final WritableObjectPropertyKey<String> TEXT_LINE_1_ACCESSIBILITY_DESCRIPTION =
new WritableObjectPropertyKey<>();
/** The sizing information for the second line of text. */
public static final WritableIntPropertyKey TEXT_LINE_2_SIZE = new WritableIntPropertyKey();
/** The maximum number of lines to be shown for the second line of text. */
public static final WritableIntPropertyKey TEXT_LINE_2_MAX_LINES = new WritableIntPropertyKey();
/** The actual text content for the second line of text. */
......@@ -72,11 +34,10 @@ class AnswerSuggestionViewProperties {
public static final WritableObjectPropertyKey<String> TEXT_LINE_2_ACCESSIBILITY_DESCRIPTION =
new WritableObjectPropertyKey<>();
public static final PropertyKey[] ALL_UNIQUE_KEYS = new PropertyKey[] {DELEGATE, ANSWER_IMAGE,
ANSWER_ICON_TYPE, TEXT_LINE_1_SIZE, TEXT_LINE_1_MAX_LINES, TEXT_LINE_1_TEXT,
TEXT_LINE_1_ACCESSIBILITY_DESCRIPTION, TEXT_LINE_2_SIZE, TEXT_LINE_2_MAX_LINES,
TEXT_LINE_2_TEXT, TEXT_LINE_2_ACCESSIBILITY_DESCRIPTION};
public static final PropertyKey[] ALL_UNIQUE_KEYS = new PropertyKey[] {TEXT_LINE_1_TEXT,
TEXT_LINE_1_MAX_LINES, TEXT_LINE_1_ACCESSIBILITY_DESCRIPTION, TEXT_LINE_2_TEXT,
TEXT_LINE_2_MAX_LINES, TEXT_LINE_2_ACCESSIBILITY_DESCRIPTION};
public static final PropertyKey[] ALL_KEYS =
PropertyModel.concatKeys(ALL_UNIQUE_KEYS, SuggestionCommonProperties.ALL_KEYS);
PropertyModel.concatKeys(ALL_UNIQUE_KEYS, BaseSuggestionViewProperties.ALL_KEYS);
}
......@@ -5,6 +5,7 @@
package org.chromium.chrome.browser.omnibox.suggestions.base;
import android.content.Context;
import android.support.annotation.IdRes;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
......@@ -145,4 +146,17 @@ public class BaseSuggestionView extends SimpleHorizontalLayoutView {
ImageView getActionImageView() {
return mActionView;
}
/**
* Find content view by view id.
*
* Scoped {@link #findViewById(int)} search for the view specified in
* {@link #setContentView(View)}.
*
* @param id View ID of the sought view.
* @return View with the specified ID or null, if view could not be found.
*/
public <T extends View> T findContentView(@IdRes int id) {
return mContentView.findContentView(id);
}
}
......@@ -52,18 +52,18 @@ public class BaseSuggestionViewBinder
final RoundedCornerImageView view = baseView.getSuggestionImageView();
final SuggestionDrawableState sds = model.get(BaseSuggestionViewProperties.ICON);
final Resources res = view.getContext().getResources();
final int paddingStart = res.getDimensionPixelSize(sds.isLarge()
final int paddingStart = res.getDimensionPixelSize(sds.isLarge
? R.dimen.omnibox_suggestion_36dp_icon_margin_start
: R.dimen.omnibox_suggestion_24dp_icon_margin_start);
final int paddingEnd = res.getDimensionPixelSize(sds.isLarge()
final int paddingEnd = res.getDimensionPixelSize(sds.isLarge
? R.dimen.omnibox_suggestion_36dp_icon_margin_end
: R.dimen.omnibox_suggestion_24dp_icon_margin_end);
// TODO(ender): move logic applying corner rounding to updateIcon when action images use
// RoundedCornerImageView too.
RoundedCornerImageView rciv = (RoundedCornerImageView) view;
int radius = sds.isRounded()
int radius = sds.useRoundedCorners
? res.getDimensionPixelSize(R.dimen.default_rounded_corner_radius)
: 0;
rciv.setRoundedCorners(radius, radius, radius, radius);
......@@ -91,8 +91,8 @@ public class BaseSuggestionViewBinder
return;
}
view.setImageDrawable(sds.getDrawable());
if (sds.isTintable()) {
view.setImageDrawable(sds.drawable);
if (sds.allowTint) {
ApiCompatibilityUtils.setImageTintList(
view, ColorUtils.getIconTint(view.getContext(), !useDarkColors));
}
......
......@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.omnibox.suggestions.base;
import android.content.Context;
import android.support.annotation.DrawableRes;
import android.support.annotation.IdRes;
import android.view.View;
import android.widget.ImageView;
......@@ -59,4 +60,11 @@ class DecoratedSuggestionView extends SimpleHorizontalLayoutView {
public boolean isFocused() {
return super.isFocused() || (isSelected() && !isInTouchMode());
}
<ViewType extends View> ViewType findContentView(@IdRes int id) {
if (mContentView == null) {
return null;
}
return mContentView.findViewById(id);
}
}
......@@ -7,106 +7,132 @@ package org.chromium.chrome.browser.omnibox.suggestions.base;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.support.v4.util.ObjectsCompat;
import android.support.v7.content.res.AppCompatResources;
import org.chromium.chrome.R;
/** Represents graphical decoration for the suggestion components. */
public class SuggestionDrawableState {
private Drawable mDrawable;
private boolean mAllowTint;
private boolean mUseRoundedCorners;
private boolean mIsLarge;
/**
* Create new SuggestionDrawableState representing a supplied Drawable object.
*
* @param cxt Current context.
* @param drawable Drawable object to use.
* @param isLarge Whether drawable object should be large (or small).
* @param allowTint Whether supplied drawable can be tinted.
* @param rounded Whether supplied drawable should use rounded corners.
*/
public SuggestionDrawableState(
Context ctx, Drawable drawable, boolean isLarge, boolean allowTint, boolean rounded) {
mDrawable = drawable;
mIsLarge = isLarge;
mAllowTint = allowTint;
mUseRoundedCorners = rounded;
}
/** Embedded drawable object. */
public final Drawable drawable;
/** Whether supplied drawable can be tinted */
public final boolean allowTint;
/** Whether drawable should be rounded. */
public final boolean useRoundedCorners;
/** Whether drawable should be displayed as large. */
public final boolean isLarge;
/**
* Create new SuggestionDrawableState representing a supplied Bitmap.
* Bitmap drawables are not tintable.
*
* @param cxt Current context.
* @param bitmap Bitmap to use.
* @param isLarge Whether drawable object should be large (or small).
* @param rounded Whether supplied drawable should use rounded corners.
*/
public SuggestionDrawableState(Context ctx, Bitmap bitmap, boolean isLarge, boolean rounded) {
this(ctx, new BitmapDrawable(bitmap), isLarge, false, rounded);
}
public static final class Builder {
private Drawable mDrawable;
private boolean mAllowTint;
private boolean mUseRoundedCorners;
private boolean mIsLarge;
/**
* Create new SuggestionDrawableState representing a supplied Color.
* This instance is not tintable.
* This method does not utilize ColorDrawables directly, because these are freely resize-able,
* making it impossible to restrict its aspect ratio to a rectangle.
*
* @param cxt Current context.
* @param color Color to show.
* @param isLarge Whether drawable object should be large (or small).
* @param rounded Whether supplied drawable should use rounded corners.
*/
public SuggestionDrawableState(
Context ctx, @ColorInt int color, boolean isLarge, boolean rounded) {
this(ctx, new PaintDrawable(color), isLarge, false, rounded);
final int edgeSize = ctx.getResources().getDimensionPixelSize(isLarge
? R.dimen.omnibox_suggestion_36dp_icon_size
: R.dimen.omnibox_suggestion_24dp_icon_size);
final PaintDrawable drawable = (PaintDrawable) mDrawable;
drawable.setIntrinsicWidth(edgeSize);
drawable.setIntrinsicHeight(edgeSize);
}
/**
* Create new Builder object.
*
* @param cxt Current context.
*/
private Builder(Drawable drawable) {
assert drawable != null : "SuggestionDrawableState needs a Drawable object";
mDrawable = drawable;
}
/**
* Create new SuggestionDrawableState representing a supplied drawable resource.
*
* @param cxt Current context.
* @param res Drawable resource to use.
* @param isLarge Whether drawable object should be large (or small).
* @param allowTint Whether supplied drawable can be tinted.
* @param rounded Whether supplied drawable should use rounded corners.
*/
public SuggestionDrawableState(Context ctx, @DrawableRes int res, boolean isLarge,
boolean allowTint, boolean rounded) {
this(ctx, AppCompatResources.getDrawable(ctx, res), isLarge, allowTint, rounded);
}
/**
* Associate Bitmap with built SuggestionDrawableState object.
*
* @param bitmap Bitmap to use.
*/
public static Builder forBitmap(Bitmap bitmap) {
return new Builder(new BitmapDrawable(bitmap));
}
/** Get Drawable associated with this instance. */
Drawable getDrawable() {
return mDrawable;
}
/**
* Associate Color with built SuggestionDrawableState object.
*
* @param color Color to use.
*/
public static Builder forColor(@ColorInt int color) {
return new Builder(new ColorDrawable(color));
}
/** Whether drawable can be tinted. */
boolean isTintable() {
return mAllowTint;
}
/**
* Associate Color with built SuggestionDrawableState object.
*
* @param ctx Current context.
* @param colorRes Color resource to use.
*/
public static Builder forColorRes(Context ctx, @ColorRes int colorRes) {
return new Builder(new ColorDrawable(ctx.getResources().getColor(colorRes)));
}
/** Whether drawable should be drawn as large. */
boolean isLarge() {
return mIsLarge;
}
/**
* Associate Drawable with built SuggestionDrawableState object.
*
* @param ctx Current context.
* @param res Drawable resource to use.
*/
public static Builder forDrawableRes(Context ctx, @DrawableRes int res) {
return new Builder(AppCompatResources.getDrawable(ctx, res));
}
/** Whether drawable should be rounded. */
boolean isRounded() {
return mUseRoundedCorners;
/**
* Create new SuggestionDrawableState representing a supplied Drawable object.
*
* @param drawable Drawable object to use.
*/
public static Builder forDrawable(Drawable d) {
return new Builder(d);
}
/**
* Specify whether built object should be rounded.
*
* @param useRoundedCorners true, if image should be rounded.
*/
public Builder setUseRoundedCorners(boolean useRoundedCorners) {
mUseRoundedCorners = useRoundedCorners;
return this;
}
/**
* Specify whether build object should receive tint.
*
* @param allowTint true, if built drawable state should be tinted to reflect theme.
*/
public Builder setAllowTint(boolean allowTint) {
mAllowTint = allowTint;
return this;
}
/**
* Specify whether build object should be presented as small (24dp) or large (36dp).
*
* @param isLarge true, if drawable should be shown large (36dp), otherwise 24dp.
*/
public Builder setLarge(boolean isLarge) {
mIsLarge = isLarge;
return this;
}
/**
* Build SuggestionDrawableState object.
*/
public SuggestionDrawableState build() {
return new SuggestionDrawableState(mDrawable, mUseRoundedCorners, mIsLarge, mAllowTint);
}
};
private SuggestionDrawableState(
Drawable drawable, boolean useRoundedCorners, boolean isLarge, boolean allowTint) {
this.drawable = drawable;
this.useRoundedCorners = useRoundedCorners;
this.isLarge = isLarge;
this.allowTint = allowTint;
}
@Override
......@@ -115,8 +141,7 @@ public class SuggestionDrawableState {
if (!(object instanceof SuggestionDrawableState)) return false;
SuggestionDrawableState other = (SuggestionDrawableState) object;
return mIsLarge == other.mIsLarge && mUseRoundedCorners == other.mUseRoundedCorners
&& mAllowTint == other.mAllowTint
&& ObjectsCompat.equals(mDrawable, other.mDrawable);
return isLarge == other.isLarge && useRoundedCorners == other.useRoundedCorners
&& allowTint == other.allowTint && ObjectsCompat.equals(drawable, other.drawable);
}
};
include_rules = [
"!clank/java/src/org/chromium/chrome/browser/AppHooksImpl.java",
"+chrome/lib/lifecycle/public",
"+chrome/browser/image_fetcher",
"+chrome/browser/ui/android/widget",
"+components/autofill/android/java/src/org/chromium/components/autofill",
"+components/background_task_scheduler/android",
"+components/bookmarks/common/android",
"+components/offline_items_collection/core/android/java/src",
"+components/omnibox/browser/android/java/src/org/chromium/components/omnibox",
"+components/payments/content/android/java/src/org/chromium/components/payments",
"+components/search_engines/android/java/src/org/chromium/components/search_engines",
"+components/sync/android/java/src/org/chromium/components/sync",
......
......@@ -7,6 +7,7 @@ package org.chromium.components.omnibox;
import android.support.v4.util.ObjectsCompat;
import android.text.TextUtils;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.annotations.CalledByNative;
import java.util.ArrayList;
......@@ -22,7 +23,8 @@ public class SuggestionAnswer {
private final ImageLine mFirstLine;
private final ImageLine mSecondLine;
private SuggestionAnswer(@AnswerType int type, ImageLine firstLine, ImageLine secondLine) {
@VisibleForTesting
public SuggestionAnswer(@AnswerType int type, ImageLine firstLine, ImageLine secondLine) {
mType = type;
mFirstLine = firstLine;
mSecondLine = secondLine;
......@@ -69,8 +71,9 @@ public class SuggestionAnswer {
private final TextField mStatusText;
private final String mImage;
private ImageLine(List<TextField> textFields, TextField additionalText,
TextField statusText, String imageUrl) {
@VisibleForTesting
public ImageLine(List<TextField> textFields, TextField additionalText, TextField statusText,
String imageUrl) {
mTextFields = textFields;
mAdditionalText = additionalText;
mStatusText = statusText;
......@@ -160,7 +163,8 @@ public class SuggestionAnswer {
private final int mStyle;
private final int mNumLines;
private TextField(
@VisibleForTesting
public TextField(
@AnswerTextType int type, String text, @AnswerTextStyle int style, int numLines) {
mType = type;
mText = text;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment