Commit fa9ada41 authored by Tomasz Wiszkowski's avatar Tomasz Wiszkowski Committed by Commit Bot

Move function highlighting suggestions to common class.

This change allows all suggestions to highlight suggestion sections
that match user query.

Bug: 982818
Change-Id: I047660c81d3d62925c2ee1b9519149bef87eac6c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1967757
Commit-Queue: Ender <ender@google.com>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarBrandon Wylie <wylieb@chromium.org>
Reviewed-by: default avatarPatrick Noland <pnoland@chromium.org>
Cr-Commit-Position: refs/heads/master@{#726059}
parent b87a7be5
...@@ -159,6 +159,7 @@ chrome_junit_test_java_sources = [ ...@@ -159,6 +159,7 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.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/answer/AnswerSuggestionProcessorUnitTest.java",
"junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java", "junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java",
"junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessorUnitTest.java",
"junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.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/base/SimpleHorizontalLayoutViewTest.java",
"junit/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorTest.java", "junit/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorTest.java",
......
...@@ -5,14 +5,21 @@ ...@@ -5,14 +5,21 @@
package org.chromium.chrome.browser.omnibox.suggestions.base; package org.chromium.chrome.browser.omnibox.suggestions.base;
import android.content.Context; import android.content.Context;
import android.graphics.Typeface;
import android.text.Spannable;
import android.text.style.StyleSpan;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.omnibox.MatchClassificationStyle;
import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion; import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion.MatchClassification;
import org.chromium.chrome.browser.omnibox.suggestions.SuggestionProcessor; import org.chromium.chrome.browser.omnibox.suggestions.SuggestionProcessor;
import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost; import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate; import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
import java.util.List;
/** /**
* A class that handles base properties and model for most suggestions. * A class that handles base properties and model for most suggestions.
*/ */
...@@ -72,4 +79,39 @@ public abstract class BaseSuggestionViewProcessor implements SuggestionProcessor ...@@ -72,4 +79,39 @@ public abstract class BaseSuggestionViewProcessor implements SuggestionProcessor
setActionDrawableState(model, null); setActionDrawableState(model, null);
} }
} }
/**
* Apply In-Place highlight to matching sections of Suggestion text.
*
* @param text Suggestion text to apply highlight to.
* @param classifications Classifications describing how to format text.
* @return true, if at least one highlighted match section was found.
*/
protected static boolean applyHighlightToMatchRegions(
Spannable text, List<MatchClassification> classifications) {
if (text == null || classifications == null) return false;
boolean hasAtLeastOneMatch = false;
for (int i = 0; i < classifications.size(); i++) {
MatchClassification classification = classifications.get(i);
if ((classification.style & MatchClassificationStyle.MATCH)
== MatchClassificationStyle.MATCH) {
int matchStartIndex = classification.offset;
int matchEndIndex;
if (i == classifications.size() - 1) {
matchEndIndex = text.length();
} else {
matchEndIndex = classifications.get(i + 1).offset;
}
matchStartIndex = Math.min(matchStartIndex, text.length());
matchEndIndex = Math.min(matchEndIndex, text.length());
hasAtLeastOneMatch = true;
// Bold the part of the URL that matches the user query.
text.setSpan(new StyleSpan(Typeface.BOLD), matchStartIndex, matchEndIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
return hasAtLeastOneMatch;
}
} }
...@@ -10,7 +10,6 @@ import android.support.annotation.DrawableRes; ...@@ -10,7 +10,6 @@ import android.support.annotation.DrawableRes;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.style.StyleSpan;
import org.chromium.base.Supplier; import org.chromium.base.Supplier;
import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordHistogram;
...@@ -289,30 +288,4 @@ public class BasicSuggestionProcessor extends BaseSuggestionViewProcessor { ...@@ -289,30 +288,4 @@ public class BasicSuggestionProcessor extends BaseSuggestionViewProcessor {
if (shouldHighlight) applyHighlightToMatchRegions(str, classifications); if (shouldHighlight) applyHighlightToMatchRegions(str, classifications);
return str; return str;
} }
private static boolean applyHighlightToMatchRegions(
Spannable str, List<OmniboxSuggestion.MatchClassification> classifications) {
boolean hasMatch = false;
for (int i = 0; i < classifications.size(); i++) {
OmniboxSuggestion.MatchClassification classification = classifications.get(i);
if ((classification.style & MatchClassificationStyle.MATCH)
== MatchClassificationStyle.MATCH) {
int matchStartIndex = classification.offset;
int matchEndIndex;
if (i == classifications.size() - 1) {
matchEndIndex = str.length();
} else {
matchEndIndex = classifications.get(i + 1).offset;
}
matchStartIndex = Math.min(matchStartIndex, str.length());
matchEndIndex = Math.min(matchEndIndex, str.length());
hasMatch = true;
// Bold the part of the URL that matches the user query.
str.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), matchStartIndex,
matchEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
return hasMatch;
}
} }
// 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.base;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyObject;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.graphics.Typeface;
import android.text.Spannable;
import android.text.style.StyleSpan;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.chromium.chrome.browser.omnibox.MatchClassificationStyle;
import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion.MatchClassification;
import java.util.ArrayList;
import java.util.List;
/**
* Tests for {@link BaseSuggestionViewProcessor}.
*/
@RunWith(BlockJUnit4ClassRunner.class)
public class BaseSuggestionViewProcessorUnitTest {
private static final int FAKE_STRING_LENGTH = 10;
@Mock
Spannable mText;
private ArgumentMatcher<StyleSpan> mIsHighlightStyle;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mText.length()).thenReturn(FAKE_STRING_LENGTH);
mIsHighlightStyle = (StyleSpan style) -> style.getStyle() == Typeface.BOLD;
}
@Test
public void highlightTest_noClassifications() {
assertFalse(BaseSuggestionViewProcessor.applyHighlightToMatchRegions(mText, null));
verify(mText, times(0)).setSpan(anyObject(), anyInt(), anyInt(), anyInt());
}
@Test
public void highlightTest_noMatch() {
assertFalse(
BaseSuggestionViewProcessor.applyHighlightToMatchRegions(mText, new ArrayList<>()));
verify(mText, times(0)).setSpan(anyObject(), anyInt(), anyInt(), anyInt());
}
/** Verify string is correctly highlighted when match is the last one on the list. */
@Test
public void highlightTest_matchWithNoTerminator() {
final int matchStart = 4;
final List<MatchClassification> classifications = new ArrayList<>();
classifications.add(new MatchClassification(matchStart, MatchClassificationStyle.MATCH));
assertTrue(
BaseSuggestionViewProcessor.applyHighlightToMatchRegions(mText, classifications));
verify(mText, times(1))
.setSpan(argThat(mIsHighlightStyle), eq(matchStart), eq(FAKE_STRING_LENGTH),
eq(Spannable.SPAN_EXCLUSIVE_EXCLUSIVE));
// Check that the total amount of calls to setSpan.
verify(mText, times(1)).setSpan(anyObject(), anyInt(), anyInt(), anyInt());
}
@Test
public void highlightTest_matchWithTerminator() {
final int matchStart = 4;
final int matchEnd = 7;
final List<MatchClassification> classifications = new ArrayList<>();
classifications.add(new MatchClassification(matchStart, MatchClassificationStyle.MATCH));
classifications.add(new MatchClassification(matchEnd, MatchClassificationStyle.NONE));
assertTrue(
BaseSuggestionViewProcessor.applyHighlightToMatchRegions(mText, classifications));
verify(mText, times(1))
.setSpan(argThat(mIsHighlightStyle), eq(matchStart), eq(matchEnd),
eq(Spannable.SPAN_EXCLUSIVE_EXCLUSIVE));
// Check that the total amount of calls to setSpan.
verify(mText, times(1)).setSpan(anyObject(), anyInt(), anyInt(), anyInt());
}
/** Verify that multiple matches receive proper highlight. */
@Test
public void highlightTest_multipleMatches() {
final int matchStart1 = 4;
final int matchEnd1 = 5;
final int matchStart2 = 6;
final int matchEnd2 = 7;
final List<MatchClassification> classifications = new ArrayList<>();
classifications.add(new MatchClassification(matchStart1, MatchClassificationStyle.MATCH));
classifications.add(new MatchClassification(matchEnd1, MatchClassificationStyle.NONE));
classifications.add(new MatchClassification(matchStart2, MatchClassificationStyle.MATCH));
classifications.add(new MatchClassification(matchEnd2, MatchClassificationStyle.NONE));
assertTrue(
BaseSuggestionViewProcessor.applyHighlightToMatchRegions(mText, classifications));
verify(mText, times(1))
.setSpan(argThat(mIsHighlightStyle), eq(matchStart1), eq(matchEnd1),
eq(Spannable.SPAN_EXCLUSIVE_EXCLUSIVE));
verify(mText, times(1))
.setSpan(argThat(mIsHighlightStyle), eq(matchStart2), eq(matchEnd2),
eq(Spannable.SPAN_EXCLUSIVE_EXCLUSIVE));
// Check that the total amount of calls to setSpan.
verify(mText, times(2)).setSpan(anyObject(), anyInt(), anyInt(), anyInt());
}
/** Verify that multiple consecutive matches don't overlap in target Span. */
@Test
public void highlightTest_overlappingMatches() {
final int matchStart1 = 4;
final int matchStart2 = 6;
final List<MatchClassification> classifications = new ArrayList<>();
classifications.add(new MatchClassification(matchStart1, MatchClassificationStyle.MATCH));
classifications.add(new MatchClassification(matchStart2, MatchClassificationStyle.MATCH));
assertTrue(
BaseSuggestionViewProcessor.applyHighlightToMatchRegions(mText, classifications));
verify(mText, times(1))
.setSpan(argThat(mIsHighlightStyle), eq(matchStart1), eq(matchStart2),
eq(Spannable.SPAN_EXCLUSIVE_EXCLUSIVE));
verify(mText, times(1))
.setSpan(argThat(mIsHighlightStyle), eq(matchStart2), eq(FAKE_STRING_LENGTH),
eq(Spannable.SPAN_EXCLUSIVE_EXCLUSIVE));
// Check that the total amount of calls to setSpan.
verify(mText, times(2)).setSpan(anyObject(), anyInt(), anyInt(), anyInt());
}
/** Verify that non-matching classifiers don't receive highlight. */
@Test
public void highlightTest_nonMatchingClassifiers() {
final List<MatchClassification> classifications = new ArrayList<>();
classifications.add(new MatchClassification(0, MatchClassificationStyle.NONE));
classifications.add(new MatchClassification(1, MatchClassificationStyle.URL));
classifications.add(new MatchClassification(2, MatchClassificationStyle.DIM));
assertFalse(
BaseSuggestionViewProcessor.applyHighlightToMatchRegions(mText, classifications));
verify(mText, times(0)).setSpan(anyObject(), anyInt(), anyInt(), anyInt());
}
}
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