Commit 6f17d881 authored by Tomasz Wiszkowski's avatar Tomasz Wiszkowski Committed by Commit Bot

Propagate Suggestion group default visibility to Android.

This change ensures that the server-supplied default visibility hint
for suggestion groups is respected on Android.

Suggestion groups will appear collapsed if the server hints their
initial state should be hidden.

The change also repurposes (and subsequently renames)
AutocompleteResult's GroupHeaders as GroupsDetails.

Bug: 1113931
Change-Id: Ib9214eecf8f3939bb08098a05180281ecf20f21e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2341822
Commit-Queue: Tomasz Wiszkowski <ender@google.com>
Reviewed-by: default avatarFilip Gorski <fgorski@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797047}
parent b9355c4f
......@@ -17,6 +17,7 @@ import org.chromium.base.annotations.NativeMethods;
import org.chromium.chrome.browser.WarmupManager;
import org.chromium.chrome.browser.ntp.NewTabPage;
import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteResult.GroupDetails;
import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion.MatchClassification;
import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler.VoiceResult;
import org.chromium.chrome.browser.profiles.Profile;
......@@ -241,7 +242,7 @@ public class AutocompleteController {
AutocompleteResult resultsWithVoiceSuggestions = new AutocompleteResult(
mVoiceSuggestionProvider.addVoiceSuggestions(
autocompleteResult.getSuggestionsList(), MAX_VOICE_SUGGESTION_COUNT),
autocompleteResult.getGroupHeaders());
autocompleteResult.getGroupsDetails());
mCurrentNativeAutocompleteResult = currentNativeAutocompleteResult;
......@@ -293,9 +294,9 @@ public class AutocompleteController {
@CalledByNative
private static AutocompleteResult createAutocompleteResult(
int suggestionsCount, int groupHeadersCount) {
int suggestionsCount, int groupsCount) {
return new AutocompleteResult(new ArrayList<OmniboxSuggestion>(suggestionsCount),
new SparseArray<String>(groupHeadersCount));
new SparseArray<GroupDetails>(groupsCount));
}
/**
......@@ -311,16 +312,18 @@ public class AutocompleteController {
}
/**
* Insert element to Group Headers map.
* Insert element to GroupDetails map.
*
* @param autocompleteResult AutocompleteResult instance.
* @param groupId ID of a Group.
* @param headerText Group title.
* @param collapsedByDefault Whether group should be collapsed by default.
*/
@CalledByNative
private static void addOmniboxGroupHeaderToResult(
AutocompleteResult autocompleteResult, int groupId, String headerText) {
autocompleteResult.getGroupHeaders().put(groupId, headerText);
private static void addOmniboxGroupDetailsToResult(AutocompleteResult autocompleteResult,
int groupId, String headerText, boolean collapsedByDefault) {
autocompleteResult.getGroupsDetails().put(
groupId, new GroupDetails(headerText, collapsedByDefault));
}
@CalledByNative
......
......@@ -861,7 +861,8 @@ class AutocompleteMediator implements OnSuggestionsReceivedListener, StartStopWi
mAutocompleteResult = autocompleteResult;
List<DropdownItemViewInfo> viewInfoList =
mDropdownViewInfoListBuilder.buildDropdownViewInfoList(autocompleteResult);
mDropdownViewInfoListManager.setSourceViewInfoList(viewInfoList);
mDropdownViewInfoListManager.setSourceViewInfoList(
viewInfoList, autocompleteResult.getGroupsDetails());
mDelegate.onSuggestionsChanged(inlineAutocompleteText);
updateOmniboxSuggestionsVisibility();
}
......@@ -869,8 +870,8 @@ class AutocompleteMediator implements OnSuggestionsReceivedListener, StartStopWi
}
@Override
public void setGroupVisibility(int groupId, boolean state) {
mDropdownViewInfoListManager.setGroupVisibility(groupId, state);
public void setGroupCollapsedState(int groupId, boolean state) {
mDropdownViewInfoListManager.setGroupCollapsedState(groupId, state);
}
@NonNull
......
......@@ -4,6 +4,7 @@
package org.chromium.chrome.browser.omnibox.suggestions;
import android.text.TextUtils;
import android.util.SparseArray;
import androidx.annotation.NonNull;
......@@ -16,13 +17,43 @@ import java.util.List;
* AutocompleteResult encompasses and manages autocomplete results.
*/
public class AutocompleteResult {
/** Describes details of the Suggestions group. */
public static class GroupDetails {
// Title of the group, that will be shown to the user.
public final String title;
// Default/recommended group collapsed state.
public final boolean collapsedByDefault;
public GroupDetails(String title, boolean collapsedByDefault) {
this.title = title;
this.collapsedByDefault = collapsedByDefault;
}
@Override
public int hashCode() {
int hash = title != null ? title.hashCode() : 0;
hash ^= (collapsedByDefault ? 0x3ff : 0);
return hash;
}
@Override
public boolean equals(Object otherObj) {
if (otherObj == this) return true;
if (!(otherObj instanceof GroupDetails)) return false;
GroupDetails other = (GroupDetails) otherObj;
return (collapsedByDefault == other.collapsedByDefault)
&& TextUtils.equals(title, other.title);
}
};
private final List<OmniboxSuggestion> mSuggestions;
private final SparseArray<String> mGroupHeaders;
private final SparseArray<GroupDetails> mGroupsDetails;
public AutocompleteResult(
List<OmniboxSuggestion> suggestions, SparseArray<String> groupHeaders) {
List<OmniboxSuggestion> suggestions, SparseArray<GroupDetails> groupsDetails) {
mSuggestions = suggestions != null ? suggestions : new ArrayList<>();
mGroupHeaders = groupHeaders != null ? groupHeaders : new SparseArray<>();
mGroupsDetails = groupsDetails != null ? groupsDetails : new SparseArray<>();
}
/**
......@@ -34,11 +65,11 @@ public class AutocompleteResult {
}
/**
* @return Map of Group ID to Header text
* @return Map of Group ID to GroupDetails objects.
*/
@NonNull
SparseArray<String> getGroupHeaders() {
return mGroupHeaders;
SparseArray<GroupDetails> getGroupsDetails() {
return mGroupsDetails;
}
@Override
......@@ -49,11 +80,12 @@ public class AutocompleteResult {
AutocompleteResult other = (AutocompleteResult) otherObj;
if (!mSuggestions.equals(other.mSuggestions)) return false;
final SparseArray<String> otherHeaders = other.mGroupHeaders;
if (mGroupHeaders.size() != otherHeaders.size()) return false;
for (int index = 0; index < mGroupHeaders.size(); index++) {
if (mGroupHeaders.keyAt(index) != otherHeaders.keyAt(index)) return false;
if (!ObjectsCompat.equals(mGroupHeaders.valueAt(index), otherHeaders.valueAt(index))) {
final SparseArray<GroupDetails> otherGroupsDetails = other.mGroupsDetails;
if (mGroupsDetails.size() != otherGroupsDetails.size()) return false;
for (int index = 0; index < mGroupsDetails.size(); index++) {
if (mGroupsDetails.keyAt(index) != otherGroupsDetails.keyAt(index)) return false;
if (!ObjectsCompat.equals(
mGroupsDetails.valueAt(index), otherGroupsDetails.valueAt(index))) {
return false;
}
}
......@@ -64,10 +96,10 @@ public class AutocompleteResult {
@Override
public int hashCode() {
int baseHash = 0;
for (int index = 0; index < mGroupHeaders.size(); index++) {
baseHash += mGroupHeaders.keyAt(index);
baseHash ^= mGroupHeaders.valueAt(index).hashCode();
baseHash = (baseHash << 10) | (baseHash >> 22);
for (int index = 0; index < mGroupsDetails.size(); index++) {
baseHash += mGroupsDetails.keyAt(index);
baseHash ^= mGroupsDetails.valueAt(index).hashCode();
baseHash = Integer.rotateLeft(baseHash, 10);
}
return baseHash ^ mSuggestions.hashCode();
}
......
......@@ -8,6 +8,7 @@ import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.KEY_Z
import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.KEY_ZERO_SUGGEST_DESCRIPTION_PREFIX;
import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.KEY_ZERO_SUGGEST_DISPLAY_TEXT_PREFIX;
import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.KEY_ZERO_SUGGEST_GROUP_ID_PREFIX;
import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.KEY_ZERO_SUGGEST_HEADER_GROUP_COLLAPSED_BY_DEFAULT_PREFIX;
import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.KEY_ZERO_SUGGEST_HEADER_GROUP_ID_PREFIX;
import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.KEY_ZERO_SUGGEST_HEADER_GROUP_TITLE_PREFIX;
import static org.chromium.chrome.browser.preferences.ChromePreferenceKeys.KEY_ZERO_SUGGEST_IS_DELETABLE_PREFIX;
......@@ -27,6 +28,7 @@ import androidx.annotation.VisibleForTesting;
import org.chromium.chrome.browser.omnibox.MatchClassificationStyle;
import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteResult.GroupDetails;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
import org.chromium.url.GURL;
......@@ -44,7 +46,7 @@ public class CachedZeroSuggestionsManager {
public static void saveToCache(AutocompleteResult resultToCache) {
final SharedPreferencesManager manager = SharedPreferencesManager.getInstance();
cacheSuggestionList(manager, resultToCache.getSuggestionsList());
cacheGroupHeaders(manager, resultToCache.getGroupHeaders());
cacheGroupsDetails(manager, resultToCache.getGroupsDetails());
}
/**
......@@ -55,10 +57,10 @@ public class CachedZeroSuggestionsManager {
final SharedPreferencesManager manager = SharedPreferencesManager.getInstance();
List<OmniboxSuggestion> suggestions =
CachedZeroSuggestionsManager.readCachedSuggestionList(manager);
SparseArray<String> groupHeaders =
CachedZeroSuggestionsManager.readCachedGroupHeaders(manager);
removeInvalidSuggestionsAndGroupHeaders(suggestions, groupHeaders);
return new AutocompleteResult(suggestions, groupHeaders);
SparseArray<GroupDetails> groupsDetails =
CachedZeroSuggestionsManager.readCachedGroupsDetails(manager);
removeInvalidSuggestionsAndGroupsDetails(suggestions, groupsDetails);
return new AutocompleteResult(suggestions, groupsDetails);
}
/**
......@@ -172,58 +174,68 @@ public class CachedZeroSuggestionsManager {
}
/**
* Cache suggestion group headers in shared preferences.
* Cache suggestion group details in shared preferences.
*
* @param prefs Shared preferences manager.
* @param groupsDetails Map of Group ID to GroupDetails.
*/
private static void cacheGroupHeaders(
SharedPreferencesManager prefs, SparseArray<String> groupHeaders) {
final int size = groupHeaders.size();
private static void cacheGroupsDetails(
SharedPreferencesManager prefs, SparseArray<GroupDetails> groupsDetails) {
final int size = groupsDetails.size();
prefs.writeInt(ChromePreferenceKeys.KEY_ZERO_SUGGEST_HEADER_LIST_SIZE, size);
for (int i = 0; i < size; i++) {
final GroupDetails details = groupsDetails.valueAt(i);
String title = details.title;
boolean collapsedByDefault = details.collapsedByDefault;
prefs.writeInt(
KEY_ZERO_SUGGEST_HEADER_GROUP_ID_PREFIX.createKey(i), groupHeaders.keyAt(i));
prefs.writeString(KEY_ZERO_SUGGEST_HEADER_GROUP_TITLE_PREFIX.createKey(i),
groupHeaders.valueAt(i));
KEY_ZERO_SUGGEST_HEADER_GROUP_ID_PREFIX.createKey(i), groupsDetails.keyAt(i));
prefs.writeString(KEY_ZERO_SUGGEST_HEADER_GROUP_TITLE_PREFIX.createKey(i), title);
prefs.writeBoolean(
KEY_ZERO_SUGGEST_HEADER_GROUP_COLLAPSED_BY_DEFAULT_PREFIX.createKey(i),
collapsedByDefault);
}
}
/**
* Restore group headers from shared preferences.
* Restore group details from shared preferences.
*
* @param prefs Shared preferences manager.
* @return Map of group ID to header text previously cached in shared preferences.
* @return Map of group ID to GroupDetails previously cached in shared preferences.
*/
@NonNull
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
static SparseArray<String> readCachedGroupHeaders(SharedPreferencesManager prefs) {
static SparseArray<GroupDetails> readCachedGroupsDetails(SharedPreferencesManager prefs) {
final int size = prefs.readInt(ChromePreferenceKeys.KEY_ZERO_SUGGEST_HEADER_LIST_SIZE, 0);
final SparseArray<String> groupHeaders = new SparseArray<>(size);
final SparseArray<GroupDetails> groupsDetails = new SparseArray<>(size);
for (int i = 0; i < size; i++) {
int groupId = prefs.readInt(KEY_ZERO_SUGGEST_HEADER_GROUP_ID_PREFIX.createKey(i),
OmniboxSuggestion.INVALID_GROUP);
String groupTitle =
prefs.readString(KEY_ZERO_SUGGEST_HEADER_GROUP_TITLE_PREFIX.createKey(i), null);
groupHeaders.put(groupId, groupTitle);
boolean collapsedByDefault = prefs.readBoolean(
KEY_ZERO_SUGGEST_HEADER_GROUP_COLLAPSED_BY_DEFAULT_PREFIX.createKey(i), false);
groupsDetails.put(groupId, new GroupDetails(groupTitle, collapsedByDefault));
}
return groupHeaders;
return groupsDetails;
}
/**
* Remove all invalid entries for group headers and omnibox suggestions.
* Remove all invalid entries for group details map and omnibox suggestions list.
*
* @param suggestions List of suggestions to scan for invalid entries.
* @param groupHeaders Group headers to scan for invalid entries.
* @param groupsDetails Map of GroupDetails to scan for invalid entries.
*/
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
static void removeInvalidSuggestionsAndGroupHeaders(
List<OmniboxSuggestion> suggestions, SparseArray<String> groupHeaders) {
// Remove all group headers that have invalid index or title.
for (int index = groupHeaders.size() - 1; index >= 0; index--) {
if (groupHeaders.keyAt(index) == OmniboxSuggestion.INVALID_GROUP
|| TextUtils.isEmpty(groupHeaders.valueAt(index))) {
groupHeaders.removeAt(index);
static void removeInvalidSuggestionsAndGroupsDetails(
List<OmniboxSuggestion> suggestions, SparseArray<GroupDetails> groupsDetails) {
// Remove all group details that have invalid index or title.
for (int index = groupsDetails.size() - 1; index >= 0; index--) {
if (groupsDetails.keyAt(index) == OmniboxSuggestion.INVALID_GROUP
|| TextUtils.isEmpty(groupsDetails.valueAt(index).title)) {
groupsDetails.removeAt(index);
}
}
......@@ -233,7 +245,7 @@ public class CachedZeroSuggestionsManager {
final int groupId = suggestion.getGroupId();
if (!suggestion.getUrl().isValid() || suggestion.getUrl().isEmpty()
|| (groupId != OmniboxSuggestion.INVALID_GROUP
&& groupHeaders.indexOfKey(groupId) < 0)) {
&& groupsDetails.indexOfKey(groupId) < 0)) {
suggestions.remove(index);
}
}
......
......@@ -20,6 +20,7 @@ import org.chromium.chrome.browser.image_fetcher.ImageFetcherConfig;
import org.chromium.chrome.browser.image_fetcher.ImageFetcherFactory;
import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteResult.GroupDetails;
import org.chromium.chrome.browser.omnibox.suggestions.answer.AnswerSuggestionProcessor;
import org.chromium.chrome.browser.omnibox.suggestions.basic.BasicSuggestionProcessor;
import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost;
......@@ -275,10 +276,18 @@ class DropdownItemViewInfoListBuilder {
if (currentGroup != suggestion.getGroupId()) {
currentGroup = suggestion.getGroupId();
final PropertyModel model = mHeaderProcessor.createModel();
mHeaderProcessor.populateModel(model, currentGroup,
autocompleteResult.getGroupHeaders().get(currentGroup));
viewInfoList.add(new DropdownItemViewInfo(mHeaderProcessor, model, currentGroup));
final GroupDetails details =
autocompleteResult.getGroupsDetails().get(currentGroup);
// Only add the Header Group when both ID and details are specified.
// Note that despite GroupsDetails map not holding <null> values,
// a group definition for specific ID may be unavailable.
if (details != null) {
final PropertyModel model = mHeaderProcessor.createModel();
mHeaderProcessor.populateModel(model, currentGroup, details.title);
viewInfoList.add(
new DropdownItemViewInfo(mHeaderProcessor, model, currentGroup));
}
}
final PropertyModel model = processor.createModel();
......
......@@ -10,9 +10,11 @@ import android.view.View;
import androidx.annotation.NonNull;
import org.chromium.chrome.browser.omnibox.styles.OmniboxTheme;
import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
import org.chromium.ui.modelutil.PropertyModel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
......@@ -22,14 +24,14 @@ class DropdownItemViewInfoListManager {
private int mLayoutDirection;
private @OmniboxTheme int mOmniboxTheme;
private List<DropdownItemViewInfo> mSourceViewInfoList;
private SparseArray<Boolean> mGroupVisibility;
private SparseArray<Boolean> mGroupsCollapsedState;
DropdownItemViewInfoListManager(@NonNull ModelList managedModel) {
assert managedModel != null : "Must specify a non-null model.";
mLayoutDirection = View.LAYOUT_DIRECTION_INHERIT;
mOmniboxTheme = OmniboxTheme.LIGHT_THEME;
mSourceViewInfoList = Collections.emptyList();
mGroupVisibility = new SparseArray<>();
mGroupsCollapsedState = new SparseArray<>();
mManagedModel = managedModel;
}
......@@ -67,15 +69,15 @@ class DropdownItemViewInfoListManager {
}
/**
* Toggle the visibility state of suggestions belonging to specific group.
* Toggle the collapsed state of suggestions belonging to specific group.
*
* @param groupId ID of the group whose visibility state is expected to change.
* @param state Visibility state of the suggestions belonging to the group.
* @param groupId ID of the group whose collapsed state is expected to change.
* @param groupIsCollapsed Collapsed state of the group.
*/
void setGroupVisibility(int groupId, boolean state) {
if (getGroupVisibility(groupId) == state) return;
mGroupVisibility.put(groupId, state);
if (!state) {
void setGroupCollapsedState(int groupId, boolean groupIsCollapsed) {
if (getGroupCollapsedState(groupId) == groupIsCollapsed) return;
mGroupsCollapsedState.put(groupId, groupIsCollapsed);
if (groupIsCollapsed) {
removeSuggestionsForGroup(groupId);
} else {
insertSuggestionsForGroup(groupId);
......@@ -84,10 +86,10 @@ class DropdownItemViewInfoListManager {
/**
* @param groupId ID of the suggestions group.
* @return True, if group should be visible, otherwise false.
* @return True, if group should be collapsed, otherwise false.
*/
private boolean getGroupVisibility(int groupId) {
return mGroupVisibility.get(groupId, /* defaultVisibility= */ true);
private boolean getGroupCollapsedState(int groupId) {
return mGroupsCollapsedState.get(groupId, /* defaultCollapsedState= */ false);
}
/** @return Whether the supplied view info is a header for the specific group of suggestions. */
......@@ -99,7 +101,7 @@ class DropdownItemViewInfoListManager {
void clear() {
mSourceViewInfoList.clear();
mManagedModel.clear();
mGroupVisibility.clear();
mGroupsCollapsedState.clear();
}
/** Record histograms for all currently presented suggestions. */
......@@ -114,21 +116,34 @@ class DropdownItemViewInfoListManager {
* Specify the input list of DropdownItemViewInfo elements.
*
* @param sourceList Source list of ViewInfo elements.
* @param groupsDetails Group ID to GroupDetails map carrying group collapsed state information.
*/
void setSourceViewInfoList(@NonNull List<DropdownItemViewInfo> sourceList) {
void setSourceViewInfoList(@NonNull List<DropdownItemViewInfo> sourceList,
@NonNull SparseArray<AutocompleteResult.GroupDetails> groupsDetails) {
mSourceViewInfoList = sourceList;
mGroupVisibility.clear();
mGroupsCollapsedState.clear();
// Clone information about the recommended group collapsed state.
for (int index = 0; index < groupsDetails.size(); index++) {
mGroupsCollapsedState.put(
groupsDetails.keyAt(index), groupsDetails.valueAt(index).collapsedByDefault);
}
// Build a new list of suggestions. Honor the default collapsed state.
final List<ListItem> suggestionsList = new ArrayList<>();
for (int i = 0; i < mSourceViewInfoList.size(); i++) {
PropertyModel model = mSourceViewInfoList.get(i).model;
final DropdownItemViewInfo item = mSourceViewInfoList.get(i);
final PropertyModel model = item.model;
model.set(SuggestionCommonProperties.LAYOUT_DIRECTION, mLayoutDirection);
model.set(SuggestionCommonProperties.OMNIBOX_THEME, mOmniboxTheme);
final boolean groupIsDefaultCollapsed = getGroupCollapsedState(item.groupId);
if (!groupIsDefaultCollapsed || isGroupHeaderWithId(item, item.groupId)) {
suggestionsList.add(item);
}
}
// Note: Despite DropdownItemViewInfo extending ListItem, we can't use the
// List<DropdownItemViewInfo> as a parameter expecting a List<ListItem>.
// Java disallows casting one generic type to another, unless the specialization
// is dropped.
mManagedModel.set((List) mSourceViewInfoList);
mManagedModel.set(suggestionsList);
}
/**
......
......@@ -39,7 +39,7 @@ public interface SuggestionHost {
* Toggle expanded state of suggestion items belonging to specific group.
*
* @param groupId ID of Suggestion Group whose visibility changed.
* @param state True if elements should be visible, otherwise false.
* @param isCollapsed True if group should appear collapsed, otherwise false.
*/
void setGroupVisibility(int groupId, boolean state);
void setGroupCollapsedState(int groupId, boolean isCollapsed);
}
......@@ -54,7 +54,7 @@ public class HeaderProcessor implements DropdownItemProcessor {
public void populateModel(
final PropertyModel model, final int groupId, final String headerText) {
model.set(HeaderViewProperties.TITLE, headerText);
model.set(HeaderViewProperties.IS_EXPANDED, true);
model.set(HeaderViewProperties.IS_COLLAPSED, false);
model.set(HeaderViewProperties.DELEGATE, new HeaderViewProperties.Delegate() {
@Override
public void onHeaderSelected() {
......@@ -63,14 +63,14 @@ public class HeaderProcessor implements DropdownItemProcessor {
@Override
public void onHeaderClicked() {
final boolean newState = !model.get(HeaderViewProperties.IS_EXPANDED);
final boolean newState = !model.get(HeaderViewProperties.IS_COLLAPSED);
RecordHistogram.recordSparseHistogram(newState
? "Omnibox.ToggleSuggestionGroupId.On"
: "Omnibox.ToggleSuggestionGroupId.Off",
? "Omnibox.ToggleSuggestionGroupId.Off"
: "Omnibox.ToggleSuggestionGroupId.On",
groupId);
model.set(HeaderViewProperties.IS_EXPANDED, newState);
mSuggestionHost.setGroupVisibility(groupId, newState);
model.set(HeaderViewProperties.IS_COLLAPSED, newState);
mSuggestionHost.setGroupCollapsedState(groupId, newState);
}
});
}
......
......@@ -33,7 +33,7 @@ import org.chromium.components.browser_ui.styles.ChromeColors;
public class HeaderView extends SimpleHorizontalLayoutView {
private final TextView mHeaderText;
private final ImageView mHeaderIcon;
private boolean mIsExpanded;
private boolean mIsCollapsed;
private Runnable mOnSelectListener;
/**
......@@ -78,9 +78,9 @@ public class HeaderView extends SimpleHorizontalLayoutView {
public void onInitializeAccessibilityNodeInfo(
View host, AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
AccessibilityActionCompat action = mIsExpanded
? AccessibilityActionCompat.ACTION_COLLAPSE
: AccessibilityActionCompat.ACTION_EXPAND;
AccessibilityActionCompat action = mIsCollapsed
? AccessibilityActionCompat.ACTION_EXPAND
: AccessibilityActionCompat.ACTION_COLLAPSE;
info.addAction(new AccessibilityActionCompat(
AccessibilityEvent.TYPE_VIEW_CLICKED, action.getLabel()));
......@@ -111,10 +111,10 @@ public class HeaderView extends SimpleHorizontalLayoutView {
/**
* Specifies whether view should be announced as expanded or collapsed.
*
* @param isExpanded true, if view should be announced as expanded.
* @param isCollapsed true, if view should be announced as collapsed.
*/
void setExpandedStateForAccessibility(boolean isExpanded) {
mIsExpanded = isExpanded;
void setCollapsedStateForAccessibility(boolean isCollapsed) {
mIsCollapsed = isCollapsed;
}
/**
......
......@@ -31,11 +31,11 @@ public class HeaderViewBinder {
} else if (propertyKey == SuggestionCommonProperties.LAYOUT_DIRECTION) {
ViewCompat.setLayoutDirection(
view, model.get(SuggestionCommonProperties.LAYOUT_DIRECTION));
} else if (propertyKey == HeaderViewProperties.IS_EXPANDED) {
boolean isExpanded = model.get(HeaderViewProperties.IS_EXPANDED);
view.getIconView().setImageResource(isExpanded ? R.drawable.ic_expand_less_black_24dp
: R.drawable.ic_expand_more_black_24dp);
view.setExpandedStateForAccessibility(isExpanded);
} else if (propertyKey == HeaderViewProperties.IS_COLLAPSED) {
boolean isCollapsed = model.get(HeaderViewProperties.IS_COLLAPSED);
view.getIconView().setImageResource(isCollapsed ? R.drawable.ic_expand_more_black_24dp
: R.drawable.ic_expand_less_black_24dp);
view.setCollapsedStateForAccessibility(isCollapsed);
} else if (propertyKey == HeaderViewProperties.DELEGATE) {
HeaderViewProperties.Delegate delegate = model.get(HeaderViewProperties.DELEGATE);
if (delegate != null) {
......
......@@ -24,13 +24,13 @@ public class HeaderViewProperties {
/** The runnable object that is executed whenever user taps the header suggestion. */
public static final WritableObjectPropertyKey<Delegate> DELEGATE =
new WritableObjectPropertyKey<>();
/** The expanded state of the header suggestion. */
public static final WritableBooleanPropertyKey IS_EXPANDED = new WritableBooleanPropertyKey();
/** The collapsed state of the header suggestion. */
public static final WritableBooleanPropertyKey IS_COLLAPSED = new WritableBooleanPropertyKey();
/** The text content to be displayed as a header text. */
public static final WritableObjectPropertyKey<String> TITLE = new WritableObjectPropertyKey<>();
public static final PropertyKey[] ALL_UNIQUE_KEYS =
new PropertyKey[] {DELEGATE, IS_EXPANDED, TITLE};
new PropertyKey[] {DELEGATE, IS_COLLAPSED, TITLE};
public static final PropertyKey[] ALL_KEYS =
PropertyModel.concatKeys(ALL_UNIQUE_KEYS, SuggestionCommonProperties.ALL_KEYS);
......
......@@ -15,6 +15,7 @@ import org.junit.runner.RunWith;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.util.Batch;
import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteResult.GroupDetails;
import java.util.ArrayList;
import java.util.Arrays;
......@@ -41,17 +42,17 @@ public class AutocompleteResultUnitTest {
List<OmniboxSuggestion> list2 = Arrays.asList(
buildSuggestionForIndex(1), buildSuggestionForIndex(2), buildSuggestionForIndex(3));
SparseArray<String> headers1 = new SparseArray<>();
SparseArray<String> headers2 = new SparseArray<>();
SparseArray<GroupDetails> groupsDetails1 = new SparseArray<>();
SparseArray<GroupDetails> groupsDetails2 = new SparseArray<>();
headers1.put(10, "Hello");
headers1.put(20, "Test");
groupsDetails1.put(10, new GroupDetails("Hello", false));
groupsDetails1.put(20, new GroupDetails("Test", true));
headers2.put(10, "Hello");
headers2.put(20, "Test");
groupsDetails2.put(10, new GroupDetails("Hello", false));
groupsDetails2.put(20, new GroupDetails("Test", true));
AutocompleteResult res1 = new AutocompleteResult(list1, headers1);
AutocompleteResult res2 = new AutocompleteResult(list2, headers2);
AutocompleteResult res1 = new AutocompleteResult(list1, groupsDetails1);
AutocompleteResult res2 = new AutocompleteResult(list2, groupsDetails2);
Assert.assertEquals(res1, res2);
Assert.assertEquals(res1.hashCode(), res2.hashCode());
......@@ -65,17 +66,17 @@ public class AutocompleteResultUnitTest {
List<OmniboxSuggestion> list2 = Arrays.asList(
buildSuggestionForIndex(2), buildSuggestionForIndex(1), buildSuggestionForIndex(3));
SparseArray<String> headers1 = new SparseArray<>();
SparseArray<String> headers2 = new SparseArray<>();
SparseArray<GroupDetails> groupsDetails1 = new SparseArray<>();
SparseArray<GroupDetails> groupsDetails2 = new SparseArray<>();
headers1.put(10, "Hello");
headers1.put(20, "Test");
groupsDetails1.put(10, new GroupDetails("Hello", false));
groupsDetails1.put(20, new GroupDetails("Test", true));
headers2.put(10, "Hello");
headers2.put(20, "Test");
groupsDetails2.put(10, new GroupDetails("Hello", false));
groupsDetails2.put(20, new GroupDetails("Test", true));
AutocompleteResult res1 = new AutocompleteResult(list1, headers1);
AutocompleteResult res2 = new AutocompleteResult(list2, headers2);
AutocompleteResult res1 = new AutocompleteResult(list1, groupsDetails1);
AutocompleteResult res2 = new AutocompleteResult(list2, groupsDetails2);
Assert.assertNotEquals(res1, res2);
Assert.assertNotEquals(res1.hashCode(), res2.hashCode());
......@@ -83,22 +84,22 @@ public class AutocompleteResultUnitTest {
@Test
@SmallTest
public void autocompleteResult_missingGroupHeadersAreNotEqual() {
public void autocompleteResult_missingGroupsDetailsAreNotEqual() {
List<OmniboxSuggestion> list1 = Arrays.asList(
buildSuggestionForIndex(1), buildSuggestionForIndex(2), buildSuggestionForIndex(3));
List<OmniboxSuggestion> list2 = Arrays.asList(
buildSuggestionForIndex(1), buildSuggestionForIndex(2), buildSuggestionForIndex(3));
SparseArray<String> headers1 = new SparseArray<>();
SparseArray<String> headers2 = new SparseArray<>();
SparseArray<GroupDetails> groupsDetails1 = new SparseArray<>();
SparseArray<GroupDetails> groupsDetails2 = new SparseArray<>();
headers1.put(10, "Hello");
headers1.put(20, "Test");
groupsDetails1.put(10, new GroupDetails("Hello", true));
groupsDetails1.put(20, new GroupDetails("Test", false));
headers2.put(10, "Hello");
groupsDetails2.put(10, new GroupDetails("Hello", true));
AutocompleteResult res1 = new AutocompleteResult(list1, headers1);
AutocompleteResult res2 = new AutocompleteResult(list2, headers2);
AutocompleteResult res1 = new AutocompleteResult(list1, groupsDetails1);
AutocompleteResult res2 = new AutocompleteResult(list2, groupsDetails2);
Assert.assertNotEquals(res1, res2);
Assert.assertNotEquals(res1.hashCode(), res2.hashCode());
......@@ -106,24 +107,48 @@ public class AutocompleteResultUnitTest {
@Test
@SmallTest
public void autocompleteResult_extraGroupHeadersAreNotEqual() {
public void autocompleteResult_groupsWithDifferentDefaultExpandedStateAreNotEqual() {
List<OmniboxSuggestion> list1 = Arrays.asList(
buildSuggestionForIndex(1), buildSuggestionForIndex(2), buildSuggestionForIndex(3));
List<OmniboxSuggestion> list2 = Arrays.asList(
buildSuggestionForIndex(1), buildSuggestionForIndex(2), buildSuggestionForIndex(3));
SparseArray<String> headers1 = new SparseArray<>();
SparseArray<String> headers2 = new SparseArray<>();
SparseArray<GroupDetails> groupsDetails1 = new SparseArray<>();
SparseArray<GroupDetails> groupsDetails2 = new SparseArray<>();
headers1.put(10, "Hello");
headers1.put(20, "Test");
groupsDetails1.put(10, new GroupDetails("Hello", false));
groupsDetails1.put(20, new GroupDetails("Test", true));
headers2.put(10, "Hello");
headers2.put(20, "Test");
headers2.put(30, "Yikes");
groupsDetails2.put(10, new GroupDetails("Hello", false));
groupsDetails2.put(20, new GroupDetails("Test", false));
AutocompleteResult res1 = new AutocompleteResult(list1, headers1);
AutocompleteResult res2 = new AutocompleteResult(list2, headers2);
AutocompleteResult res1 = new AutocompleteResult(list1, groupsDetails1);
AutocompleteResult res2 = new AutocompleteResult(list2, groupsDetails2);
Assert.assertNotEquals(res1, res2);
Assert.assertNotEquals(res1.hashCode(), res2.hashCode());
}
@Test
@SmallTest
public void autocompleteResult_extraGroupsDetailsAreNotEqual() {
List<OmniboxSuggestion> list1 = Arrays.asList(
buildSuggestionForIndex(1), buildSuggestionForIndex(2), buildSuggestionForIndex(3));
List<OmniboxSuggestion> list2 = Arrays.asList(
buildSuggestionForIndex(1), buildSuggestionForIndex(2), buildSuggestionForIndex(3));
SparseArray<GroupDetails> groupsDetails1 = new SparseArray<>();
SparseArray<GroupDetails> groupsDetails2 = new SparseArray<>();
groupsDetails1.put(10, new GroupDetails("Hello", false));
groupsDetails1.put(20, new GroupDetails("Test", false));
groupsDetails2.put(10, new GroupDetails("Hello", false));
groupsDetails2.put(20, new GroupDetails("Test", false));
groupsDetails2.put(30, new GroupDetails("Yikes", false));
AutocompleteResult res1 = new AutocompleteResult(list1, groupsDetails1);
AutocompleteResult res2 = new AutocompleteResult(list2, groupsDetails2);
Assert.assertNotEquals(res1, res2);
Assert.assertNotEquals(res1.hashCode(), res2.hashCode());
......@@ -146,26 +171,26 @@ public class AutocompleteResultUnitTest {
@Test
@SmallTest
public void autocompleteResult_differentHeadersAreNotEqual() {
public void autocompleteResult_differentGroupsDetailsAreNotEqual() {
List<OmniboxSuggestion> list = Arrays.asList(
buildSuggestionForIndex(1), buildSuggestionForIndex(2), buildSuggestionForIndex(3));
SparseArray<String> headers1 = new SparseArray<>();
SparseArray<String> headers2 = new SparseArray<>();
SparseArray<String> headers3 = new SparseArray<>();
SparseArray<GroupDetails> groupsDetails1 = new SparseArray<>();
SparseArray<GroupDetails> groupsDetails2 = new SparseArray<>();
SparseArray<GroupDetails> groupsDetails3 = new SparseArray<>();
headers1.put(10, "Hello");
headers1.put(20, "Test");
groupsDetails1.put(10, new GroupDetails("Hello", false));
groupsDetails1.put(20, new GroupDetails("Test", false));
headers2.put(10, "Hello");
headers2.put(15, "Test");
groupsDetails2.put(10, new GroupDetails("Hello", false));
groupsDetails2.put(15, new GroupDetails("Test", false));
headers3.put(10, "Hello");
headers3.put(20, "Test 2");
groupsDetails3.put(10, new GroupDetails("Hello", false));
groupsDetails3.put(20, new GroupDetails("Test 2", false));
AutocompleteResult res1 = new AutocompleteResult(list, headers1);
AutocompleteResult res2 = new AutocompleteResult(list, headers2);
AutocompleteResult res3 = new AutocompleteResult(list, headers3);
AutocompleteResult res1 = new AutocompleteResult(list, groupsDetails1);
AutocompleteResult res2 = new AutocompleteResult(list, groupsDetails2);
AutocompleteResult res3 = new AutocompleteResult(list, groupsDetails3);
Assert.assertNotEquals(res1, res2);
Assert.assertNotEquals(res1, res3);
......
......@@ -34,6 +34,7 @@ import org.chromium.base.test.UiThreadTest;
import org.chromium.base.test.util.Batch;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteResult.GroupDetails;
import org.chromium.chrome.browser.omnibox.suggestions.header.HeaderProcessor;
import org.chromium.chrome.test.util.browser.Features;
import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
......@@ -102,8 +103,8 @@ public class DropdownItemViewInfoListBuilderUnitTest {
@UiThreadTest
public void headers_buildsHeaderForFirstSuggestion() {
final List<OmniboxSuggestion> actualList = new ArrayList<>();
final SparseArray<String> headers = new SparseArray<>();
headers.put(1, "Header 1");
final SparseArray<GroupDetails> groupsDetails = new SparseArray<>();
groupsDetails.put(1, new GroupDetails("Header 1", false));
OmniboxSuggestion suggestion =
OmniboxSuggestionBuilderForTest.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
......@@ -114,8 +115,8 @@ public class DropdownItemViewInfoListBuilderUnitTest {
actualList.add(suggestion);
final InOrder verifier = inOrder(mMockSuggestionProcessor, mMockHeaderProcessor);
final List<DropdownItemViewInfo> model =
mBuilder.buildDropdownViewInfoList(new AutocompleteResult(actualList, headers));
final List<DropdownItemViewInfo> model = mBuilder.buildDropdownViewInfoList(
new AutocompleteResult(actualList, groupsDetails));
verifier.verify(mMockHeaderProcessor, times(1)).populateModel(any(), eq(1), eq("Header 1"));
verifier.verify(mMockSuggestionProcessor, times(1))
......@@ -140,9 +141,9 @@ public class DropdownItemViewInfoListBuilderUnitTest {
@UiThreadTest
public void headers_buildsHeadersOnlyWhenGroupChanges() {
final List<OmniboxSuggestion> actualList = new ArrayList<>();
final SparseArray<String> headers = new SparseArray<>();
headers.put(1, "Header 1");
headers.put(2, "Header 2");
final SparseArray<GroupDetails> groupsDetails = new SparseArray<>();
groupsDetails.put(1, new GroupDetails("Header 1", false));
groupsDetails.put(2, new GroupDetails("Header 2", false));
OmniboxSuggestion suggestionWithNoGroup =
OmniboxSuggestionBuilderForTest.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
......@@ -163,8 +164,8 @@ public class DropdownItemViewInfoListBuilderUnitTest {
actualList.add(suggestionForGroup2);
final InOrder verifier = inOrder(mMockSuggestionProcessor, mMockHeaderProcessor);
final List<DropdownItemViewInfo> model =
mBuilder.buildDropdownViewInfoList(new AutocompleteResult(actualList, headers));
final List<DropdownItemViewInfo> model = mBuilder.buildDropdownViewInfoList(
new AutocompleteResult(actualList, groupsDetails));
verifier.verify(mMockSuggestionProcessor, times(1))
.populateModel(eq(suggestionWithNoGroup), any(), eq(0));
......@@ -232,8 +233,8 @@ public class DropdownItemViewInfoListBuilderUnitTest {
@UiThreadTest
public void grouping_noGroupingForSuggestionsWithHeaders() {
final List<Pair<OmniboxSuggestion, SuggestionProcessor>> actualList = new ArrayList<>();
final SparseArray<String> headers = new SparseArray<>();
headers.put(1, "Header 1");
final SparseArray<GroupDetails> groupsDetails = new SparseArray<>();
groupsDetails.put(1, new GroupDetails("Header 1", false));
OmniboxSuggestionBuilderForTest builder =
OmniboxSuggestionBuilderForTest.searchWithType(OmniboxSuggestionType.SEARCH_SUGGEST)
......
......@@ -66,22 +66,22 @@ public class HeaderViewBinderUnitTest {
@Test
public void actionIcon_iconReflectsExpandedState() {
// Expand.
mModel.set(HeaderViewProperties.IS_EXPANDED, true);
mModel.set(HeaderViewProperties.IS_COLLAPSED, false);
verify(mHeaderIcon, times(1)).setImageResource(R.drawable.ic_expand_less_black_24dp);
// Collapse.
mModel.set(HeaderViewProperties.IS_EXPANDED, false);
mModel.set(HeaderViewProperties.IS_COLLAPSED, true);
verify(mHeaderIcon, times(1)).setImageResource(R.drawable.ic_expand_more_black_24dp);
}
@Test
public void headerView_accessibilityStringReflectsExpandedState() {
// Expand without title.
mModel.set(HeaderViewProperties.IS_EXPANDED, true);
verify(mHeaderView, times(1)).setExpandedStateForAccessibility(true);
mModel.set(HeaderViewProperties.IS_COLLAPSED, false);
verify(mHeaderView, times(1)).setCollapsedStateForAccessibility(false);
mModel.set(HeaderViewProperties.IS_EXPANDED, false);
verify(mHeaderView, times(1)).setExpandedStateForAccessibility(false);
mModel.set(HeaderViewProperties.IS_COLLAPSED, true);
verify(mHeaderView, times(1)).setCollapsedStateForAccessibility(true);
}
@Test
......@@ -125,7 +125,7 @@ public class HeaderViewBinderUnitTest {
final AccessibilityNodeInfo info = mock(AccessibilityNodeInfo.class);
// Expand.
mModel.set(HeaderViewProperties.IS_EXPANDED, true);
mModel.set(HeaderViewProperties.IS_COLLAPSED, false);
mHeaderView.onInitializeAccessibilityNodeInfo(info);
verify(info, times(1)).addAction(AccessibilityAction.ACTION_COLLAPSE);
verify(info, never()).addAction(AccessibilityAction.ACTION_EXPAND);
......@@ -133,7 +133,7 @@ public class HeaderViewBinderUnitTest {
reset(info);
// Collapse.
mModel.set(HeaderViewProperties.IS_EXPANDED, false);
mModel.set(HeaderViewProperties.IS_COLLAPSED, true);
mHeaderView.onInitializeAccessibilityNodeInfo(info);
verify(info, never()).addAction(AccessibilityAction.ACTION_COLLAPSE);
verify(info, times(1)).addAction(AccessibilityAction.ACTION_EXPAND);
......
......@@ -478,8 +478,9 @@ void AutocompleteControllerAndroid::NotifySuggestionsReceived(
env, j_autocomplete_result, j_omnibox_suggestion);
}
PopulateOmniboxGroupHeaders(env, j_autocomplete_result,
autocomplete_result.headers_map());
PopulateOmniboxGroupsDetails(env, j_autocomplete_result,
autocomplete_result.headers_map(),
autocomplete_result.hidden_group_ids());
// Get the inline-autocomplete text.
base::string16 inline_autocomplete_text;
......@@ -634,14 +635,18 @@ AutocompleteControllerAndroid::BuildOmniboxSuggestion(
match.has_tab_match);
}
void AutocompleteControllerAndroid::PopulateOmniboxGroupHeaders(
void AutocompleteControllerAndroid::PopulateOmniboxGroupsDetails(
JNIEnv* env,
ScopedJavaLocalRef<jobject> j_autocomplete_result,
const SearchSuggestionParser::HeadersMap& native_header_map) {
const SearchSuggestionParser::HeadersMap& native_header_map,
const std::vector<int>& hidden_group_ids) {
base::flat_set<int> hidden_group_ids_set = hidden_group_ids;
for (const auto& group_header : native_header_map) {
Java_AutocompleteController_addOmniboxGroupHeaderToResult(
Java_AutocompleteController_addOmniboxGroupDetailsToResult(
env, j_autocomplete_result, group_header.first,
ConvertUTF16ToJavaString(env, group_header.second));
ConvertUTF16ToJavaString(env, group_header.second),
hidden_group_ids_set.contains(group_header.first));
}
}
......
......@@ -134,11 +134,13 @@ class AutocompleteControllerAndroid : public AutocompleteController::Observer,
base::android::ScopedJavaLocalRef<jobject> BuildOmniboxSuggestion(
JNIEnv* env, const AutocompleteMatch& match);
// Construct Java Group Headers map from supplied HeadersMap.
void PopulateOmniboxGroupHeaders(
// Construct Java GroupDetails map from supplied HeadersMap and expanded
// state.
void PopulateOmniboxGroupsDetails(
JNIEnv* env,
base::android::ScopedJavaLocalRef<jobject> j_autocomplete_result,
const SearchSuggestionParser::HeadersMap& header_map);
const SearchSuggestionParser::HeadersMap& header_map,
const std::vector<int>& hidden_group_ids);
// A helper method for fetching the top synchronous autocomplete result.
// The |prevent_inline_autocomplete| flag is passed to the AutocompleteInput
......
......@@ -765,6 +765,8 @@ public final class ChromePreferenceKeys {
new KeyPrefix("zero_suggest_header_group_id*");
public static final KeyPrefix KEY_ZERO_SUGGEST_HEADER_GROUP_TITLE_PREFIX =
new KeyPrefix("zero_suggest_header_group_title*");
public static final KeyPrefix KEY_ZERO_SUGGEST_HEADER_GROUP_COLLAPSED_BY_DEFAULT_PREFIX =
new KeyPrefix("zero_suggest_header_group_collapsed_by_default*");
/**
* These values are currently used as SharedPreferences keys, along with the keys in
......
......@@ -219,7 +219,8 @@ public class GrandfatheredChromePreferenceKeys {
ChromePreferenceKeys.KEY_ZERO_SUGGEST_POST_CONTENT_TYPE_PREFIX,
ChromePreferenceKeys.KEY_ZERO_SUGGEST_POST_CONTENT_DATA_PREFIX,
ChromePreferenceKeys.KEY_ZERO_SUGGEST_HEADER_GROUP_ID_PREFIX,
ChromePreferenceKeys.KEY_ZERO_SUGGEST_HEADER_GROUP_TITLE_PREFIX
ChromePreferenceKeys.KEY_ZERO_SUGGEST_HEADER_GROUP_TITLE_PREFIX,
ChromePreferenceKeys.KEY_ZERO_SUGGEST_HEADER_GROUP_COLLAPSED_BY_DEFAULT_PREFIX
);
// clang-format on
}
......
......@@ -136,6 +136,8 @@ class AutocompleteResult {
return headers_map_;
}
const std::vector<int>& hidden_group_ids() const { return hidden_group_ids_; }
// Clears the matches for this result set.
void Reset();
......
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