Commit 03cf97f6 authored by Ted Choc's avatar Ted Choc Committed by Commit Bot

Convert omnibox suggestion list to an MVC component.

This simplifies "some" of the dependencies from the
AutocompleteCoordinator to the list view itself.  The
key handling is going to get a bit gnarly, but this
is closer to being able to move the bulk of the coordinator
into the existing AutocompleteMediator.

NOPRESUBMIT=true
BUG=898522

Change-Id: I71f6458220d4ba48c80db4aa18a3e78fe910872e
Reviewed-on: https://chromium-review.googlesource.com/c/1316832
Commit-Queue: Ted Choc <tedchoc@chromium.org>
Reviewed-by: default avatarPedro Amaral <amaralp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#605818}
parent f25e13c0
...@@ -166,11 +166,6 @@ public class LocationBarLayout extends FrameLayout ...@@ -166,11 +166,6 @@ public class LocationBarLayout extends FrameLayout
return mIsTablet; return mIsTablet;
} }
@Override
public boolean isIncognito() {
return mToolbarDataProvider.isIncognito();
}
@Override @Override
public WindowDelegate getWindowDelegate() { public WindowDelegate getWindowDelegate() {
return mWindowDelegate; return mWindowDelegate;
...@@ -371,7 +366,6 @@ public class LocationBarLayout extends FrameLayout ...@@ -371,7 +366,6 @@ public class LocationBarLayout extends FrameLayout
* @return Whether the URL focus change is taking place, e.g. a focus animation is running on * @return Whether the URL focus change is taking place, e.g. a focus animation is running on
* a phone device. * a phone device.
*/ */
@Override
public boolean isUrlFocusChangeInProgress() { public boolean isUrlFocusChangeInProgress() {
return mUrlFocusChangeInProgress; return mUrlFocusChangeInProgress;
} }
......
...@@ -19,10 +19,12 @@ import android.view.WindowManager; ...@@ -19,10 +19,12 @@ import android.view.WindowManager;
import android.widget.ListView; import android.widget.ListView;
import org.chromium.base.Log; import org.chromium.base.Log;
import org.chromium.base.StrictModeContext; import org.chromium.base.Supplier;
import org.chromium.base.VisibleForTesting; import org.chromium.base.VisibleForTesting;
import org.chromium.base.metrics.RecordUserAction; import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler; import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler;
import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType; import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
import org.chromium.chrome.browser.omnibox.UrlBar.UrlTextChangeListener; import org.chromium.chrome.browser.omnibox.UrlBar.UrlTextChangeListener;
...@@ -31,11 +33,11 @@ import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener; ...@@ -31,11 +33,11 @@ import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener;
import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteController.OnSuggestionsReceivedListener; import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteController.OnSuggestionsReceivedListener;
import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteMediator.OmniboxSuggestionDelegate; import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteMediator.OmniboxSuggestionDelegate;
import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsList.OmniboxSuggestionListEmbedder; import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsList.OmniboxSuggestionListEmbedder;
import org.chromium.chrome.browser.omnibox.suggestions.SuggestionListViewBinder.SuggestionListViewHolder;
import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.toolbar.ToolbarDataProvider; import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
import org.chromium.chrome.browser.util.KeyNavigationUtil; import org.chromium.chrome.browser.util.KeyNavigationUtil;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.UiUtils;
import org.chromium.ui.base.PageTransition; import org.chromium.ui.base.PageTransition;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -55,21 +57,19 @@ public class AutocompleteCoordinator ...@@ -55,21 +57,19 @@ public class AutocompleteCoordinator
private final Context mContext; private final Context mContext;
private final ViewGroup mParent; private final ViewGroup mParent;
private final AutocompleteDelegate mDelegate; private final AutocompleteDelegate mDelegate;
private final OmniboxSuggestionListEmbedder mSuggestionListEmbedder;
private final UrlBarEditingTextStateProvider mUrlBarEditingTextProvider; private final UrlBarEditingTextStateProvider mUrlBarEditingTextProvider;
private final AutocompleteMediator mMediator; private final AutocompleteMediator mMediator;
private final PropertyModel mListModel;
private final SuggestionListViewHolder mListViewHolder;
private final OmniboxResultsAdapter mSuggestionListAdapter; private final OmniboxResultsAdapter mSuggestionListAdapter;
private final AnswersImageFetcher mAnswersImageFetcher; private final AnswersImageFetcher mAnswersImageFetcher;
private final List<Runnable> mDeferredNativeRunnables = new ArrayList<Runnable>(); private final List<Runnable> mDeferredNativeRunnables = new ArrayList<Runnable>();
private ToolbarDataProvider mToolbarDataProvider; private ToolbarDataProvider mToolbarDataProvider;
private OmniboxSuggestionsList mSuggestionList;
private boolean mNativeInitialized; private boolean mNativeInitialized;
private AutocompleteController mAutocomplete; private AutocompleteController mAutocomplete;
private boolean mSuggestionsShown;
private boolean mSuggestionModalShown; private boolean mSuggestionModalShown;
private ViewGroup mOmniboxResultsContainer;
private float mMaxRequiredWidth; private float mMaxRequiredWidth;
private float mMaxMatchContentsWidth; private float mMaxMatchContentsWidth;
private boolean mCanShowSuggestions; private boolean mCanShowSuggestions;
...@@ -145,11 +145,6 @@ public class AutocompleteCoordinator ...@@ -145,11 +145,6 @@ public class AutocompleteCoordinator
* @return Whether the URL currently has focus. * @return Whether the URL currently has focus.
*/ */
boolean isUrlBarFocused(); boolean isUrlBarFocused();
/**
* @return Whether a URL focus change animation is currently in progress.
*/
boolean isUrlFocusChangeInProgress();
} }
/** /**
...@@ -167,14 +162,139 @@ public class AutocompleteCoordinator ...@@ -167,14 +162,139 @@ public class AutocompleteCoordinator
mParent = parent; mParent = parent;
mContext = parent.getContext(); mContext = parent.getContext();
mDelegate = delegate; mDelegate = delegate;
mSuggestionListEmbedder = listEmbedder;
mUrlBarEditingTextProvider = urlBarEditingTextProvider; mUrlBarEditingTextProvider = urlBarEditingTextProvider;
mAnswersImageFetcher = new AnswersImageFetcher(); mAnswersImageFetcher = new AnswersImageFetcher();
mSuggestionListAdapter = new OmniboxResultsAdapter(mContext); mSuggestionListAdapter = new OmniboxResultsAdapter(mContext);
Supplier<ViewStub> containerStubSupplier = () -> {
return (ViewStub) mParent.getRootView().findViewById(
R.id.omnibox_results_container_stub);
};
mListViewHolder = new SuggestionListViewHolder(
mContext, containerStubSupplier, mSuggestionListAdapter);
mListModel = new PropertyModel(SuggestionListProperties.ALL_KEYS);
// TODO(tedchoc): Investigate replacing with LazyConstructionPropertyMcp to simplify the
// view binder handling property updates when the view hasn't been created
// yet.
PropertyModelChangeProcessor.create(
mListModel, mListViewHolder, SuggestionListViewBinder::bind);
mListModel.set(SuggestionListProperties.EMBEDDER, listEmbedder);
mAutocomplete = new AutocompleteController(this); mAutocomplete = new AutocompleteController(this);
mMediator = new AutocompleteMediator( mMediator = new AutocompleteMediator(mContext, urlBarEditingTextProvider, mListModel);
mContext, urlBarEditingTextProvider, mSuggestionListAdapter::updateSuggestions); mMediator.setSuggestionDelegate(new OmniboxSuggestionDelegate() {
private long mLastActionUpTimestamp;
@Override
public void onSelection(OmniboxSuggestion suggestion, int position) {
if (mShowCachedZeroSuggestResults && !mNativeInitialized) {
mDeferredOnSelection = new DeferredOnSelectionRunnable(suggestion, position) {
@Override
public void run() {
onSelection(this.mSuggestion, this.mPosition);
}
};
return;
}
String suggestionMatchUrl =
updateSuggestionUrlIfNeeded(suggestion, position, false);
loadUrlFromOmniboxMatch(
suggestionMatchUrl, position, suggestion, mLastActionUpTimestamp);
mDelegate.hideKeyboard();
}
@Override
public void onRefineSuggestion(OmniboxSuggestion suggestion) {
stopAutocomplete(false);
boolean isUrlSuggestion = suggestion.isUrlSuggestion();
String refineText = suggestion.getFillIntoEdit();
if (!isUrlSuggestion) refineText = TextUtils.concat(refineText, " ").toString();
mDelegate.setOmniboxEditingText(refineText);
onTextChangedForAutocomplete();
if (isUrlSuggestion) {
RecordUserAction.record("MobileOmniboxRefineSuggestion.Url");
} else {
RecordUserAction.record("MobileOmniboxRefineSuggestion.Search");
}
}
@Override
public void onLongPress(OmniboxSuggestion suggestion, int position) {
RecordUserAction.record("MobileOmniboxDeleteGesture");
if (!suggestion.isDeletable()) return;
// TODO(tedchoc): Migrate to modal dialog manager.
AlertDialog.Builder b =
new AlertDialog.Builder(mParent.getContext(), R.style.AlertDialogTheme);
b.setTitle(suggestion.getDisplayText());
b.setMessage(R.string.omnibox_confirm_delete);
DialogInterface.OnClickListener clickListener =
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE) {
RecordUserAction.record("MobileOmniboxDeleteRequested");
mAutocomplete.deleteSuggestion(position, suggestion.hashCode());
} else if (which == DialogInterface.BUTTON_NEGATIVE) {
dialog.cancel();
}
}
};
b.setPositiveButton(android.R.string.ok, clickListener);
b.setNegativeButton(android.R.string.cancel, clickListener);
AlertDialog dialog = b.create();
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
mSuggestionModalShown = false;
}
});
mSuggestionModalShown = true;
try {
dialog.show();
} catch (WindowManager.BadTokenException ex) {
mSuggestionModalShown = false;
}
}
@Override
public void onSetUrlToSuggestion(OmniboxSuggestion suggestion) {
if (mIgnoreOmniboxItemSelection) return;
mDelegate.setOmniboxEditingText(suggestion.getFillIntoEdit());
mIgnoreOmniboxItemSelection = true;
}
@Override
public void onGestureDown() {
stopAutocomplete(false);
}
@Override
public void onGestureUp(long timestamp) {
mLastActionUpTimestamp = timestamp;
}
@Override
public void onTextWidthsUpdated(float requiredWidth, float matchContentsWidth) {
mMaxRequiredWidth = Math.max(mMaxRequiredWidth, requiredWidth);
mMaxMatchContentsWidth = Math.max(mMaxMatchContentsWidth, matchContentsWidth);
}
@Override
public float getMaxRequiredWidth() {
return mMaxRequiredWidth;
}
@Override
public float getMaxMatchContentsWidth() {
return mMaxMatchContentsWidth;
}
});
} }
@Override @Override
...@@ -269,14 +389,14 @@ public class AutocompleteCoordinator ...@@ -269,14 +389,14 @@ public class AutocompleteCoordinator
*/ */
@VisibleForTesting @VisibleForTesting
public OmniboxSuggestionsList getSuggestionList() { public OmniboxSuggestionsList getSuggestionList() {
return mSuggestionList; return mListViewHolder.mListView;
} }
/** /**
* @return Whether the suggestions list is currently visible. * @return Whether the suggestions list is currently visible.
*/ */
public boolean isSuggestionsListShown() { public boolean isSuggestionsListShown() {
return mSuggestionsShown; return mListModel.get(SuggestionListProperties.VISIBLE);
} }
/** /**
...@@ -302,141 +422,6 @@ public class AutocompleteCoordinator ...@@ -302,141 +422,6 @@ public class AutocompleteCoordinator
return mAutocomplete.getCurrentNativeAutocompleteResult(); return mAutocomplete.getCurrentNativeAutocompleteResult();
} }
/**
* Initiates the mSuggestionListPopup. Done on demand to not slow down the initial inflation of
* the location bar.
*/
private void initSuggestionList() {
// Only called from onSuggestionsReceived(), which is a callback from a listener set up by
// onNativeLibraryReady(), so this assert is safe.
assert mNativeInitialized
|| mShowCachedZeroSuggestResults
: "Trying to initialize native suggestions list before native init";
if (mSuggestionList != null) return;
// TODO(tedchoc): Investigate lazily building the suggestion list off of the UI thread.
try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
mSuggestionList = new OmniboxSuggestionsList(mContext, mSuggestionListEmbedder);
}
// Start with visibility GONE to ensure that show() is called. http://crbug.com/517438
mSuggestionList.setVisibility(View.GONE);
mSuggestionList.setAdapter(mSuggestionListAdapter);
mSuggestionList.setClipToPadding(false);
mMediator.setSuggestionDelegate(new OmniboxSuggestionDelegate() {
private long mLastActionUpTimestamp;
@Override
public void onSelection(OmniboxSuggestion suggestion, int position) {
if (mShowCachedZeroSuggestResults && !mNativeInitialized) {
mDeferredOnSelection = new DeferredOnSelectionRunnable(suggestion, position) {
@Override
public void run() {
onSelection(this.mSuggestion, this.mPosition);
}
};
return;
}
String suggestionMatchUrl =
updateSuggestionUrlIfNeeded(suggestion, position, false);
loadUrlFromOmniboxMatch(
suggestionMatchUrl, position, suggestion, mLastActionUpTimestamp);
mDelegate.hideKeyboard();
}
@Override
public void onRefineSuggestion(OmniboxSuggestion suggestion) {
stopAutocomplete(false);
boolean isUrlSuggestion = suggestion.isUrlSuggestion();
String refineText = suggestion.getFillIntoEdit();
if (!isUrlSuggestion) refineText = TextUtils.concat(refineText, " ").toString();
mDelegate.setOmniboxEditingText(refineText);
onTextChangedForAutocomplete();
if (isUrlSuggestion) {
RecordUserAction.record("MobileOmniboxRefineSuggestion.Url");
} else {
RecordUserAction.record("MobileOmniboxRefineSuggestion.Search");
}
}
@Override
public void onLongPress(OmniboxSuggestion suggestion, int position) {
RecordUserAction.record("MobileOmniboxDeleteGesture");
if (!suggestion.isDeletable()) return;
// TODO(tedchoc): Migrate to modal dialog manager.
AlertDialog.Builder b =
new AlertDialog.Builder(mParent.getContext(), R.style.AlertDialogTheme);
b.setTitle(suggestion.getDisplayText());
b.setMessage(R.string.omnibox_confirm_delete);
DialogInterface.OnClickListener clickListener =
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE) {
RecordUserAction.record("MobileOmniboxDeleteRequested");
mAutocomplete.deleteSuggestion(position, suggestion.hashCode());
} else if (which == DialogInterface.BUTTON_NEGATIVE) {
dialog.cancel();
}
}
};
b.setPositiveButton(android.R.string.ok, clickListener);
b.setNegativeButton(android.R.string.cancel, clickListener);
AlertDialog dialog = b.create();
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
mSuggestionModalShown = false;
}
});
mSuggestionModalShown = true;
try {
dialog.show();
} catch (WindowManager.BadTokenException ex) {
mSuggestionModalShown = false;
}
}
@Override
public void onSetUrlToSuggestion(OmniboxSuggestion suggestion) {
if (mIgnoreOmniboxItemSelection) return;
mDelegate.setOmniboxEditingText(suggestion.getFillIntoEdit());
mIgnoreOmniboxItemSelection = true;
}
@Override
public void onGestureDown() {
stopAutocomplete(false);
}
@Override
public void onGestureUp(long timestamp) {
mLastActionUpTimestamp = timestamp;
}
@Override
public void onTextWidthsUpdated(float requiredWidth, float matchContentsWidth) {
mMaxRequiredWidth = Math.max(mMaxRequiredWidth, requiredWidth);
mMaxMatchContentsWidth = Math.max(mMaxMatchContentsWidth, matchContentsWidth);
}
@Override
public float getMaxRequiredWidth() {
return mMaxRequiredWidth;
}
@Override
public float getMaxMatchContentsWidth() {
return mMaxMatchContentsWidth;
}
});
}
/** /**
* Updates the maximum widths required to render the suggestions. * Updates the maximum widths required to render the suggestions.
* This is needed for infinite suggestions where we try to vertically align the leading * This is needed for infinite suggestions where we try to vertically align the leading
...@@ -447,46 +432,15 @@ public class AutocompleteCoordinator ...@@ -447,46 +432,15 @@ public class AutocompleteCoordinator
mMaxMatchContentsWidth = 0; mMaxMatchContentsWidth = 0;
} }
private void initOmniboxResultsContainer() {
if (mOmniboxResultsContainer != null) return;
ViewStub overlayStub =
(ViewStub) mParent.getRootView().findViewById(R.id.omnibox_results_container_stub);
mOmniboxResultsContainer = (ViewGroup) overlayStub.inflate();
}
/** /**
* Update whether the omnibox suggestions are visible. * Update whether the omnibox suggestions are visible.
*/ */
private void updateOmniboxSuggestionsVisibility() { private void updateOmniboxSuggestionsVisibility() {
initOmniboxResultsContainer();
if (mSuggestionList == null) return;
boolean isContainerVisible = mOmniboxResultsContainer.getVisibility() == View.VISIBLE;
boolean shouldBeVisible = mCanShowSuggestions && getSuggestionCount() > 0; boolean shouldBeVisible = mCanShowSuggestions && getSuggestionCount() > 0;
if (isContainerVisible == shouldBeVisible) return; boolean wasVisible = mListModel.get(SuggestionListProperties.VISIBLE);
mListModel.set(SuggestionListProperties.VISIBLE, shouldBeVisible);
mSuggestionsShown = shouldBeVisible; if (shouldBeVisible && !wasVisible) {
final boolean isListVisible = mSuggestionList.getVisibility() == View.VISIBLE;
if (shouldBeVisible && !isListVisible) {
mIgnoreOmniboxItemSelection = true; // Reset to default value. mIgnoreOmniboxItemSelection = true; // Reset to default value.
if (mSuggestionList.getParent() == null) {
mOmniboxResultsContainer.addView(mSuggestionList);
}
mSuggestionList.show();
updateSuggestionListLayoutDirection();
} else if (!shouldBeVisible && isListVisible) {
mSuggestionList.setVisibility(View.GONE);
UiUtils.removeViewFromParent(mSuggestionList);
}
if (shouldBeVisible) {
mOmniboxResultsContainer.setVisibility(View.VISIBLE);
} else {
mOmniboxResultsContainer.setVisibility(View.INVISIBLE);
} }
} }
...@@ -505,10 +459,11 @@ public class AutocompleteCoordinator ...@@ -505,10 +459,11 @@ public class AutocompleteCoordinator
* @return Whether the key event was handled. * @return Whether the key event was handled.
*/ */
public boolean handleKeyEvent(int keyCode, KeyEvent event) { public boolean handleKeyEvent(int keyCode, KeyEvent event) {
if (KeyNavigationUtil.isGoDown(event) && mSuggestionList != null OmniboxSuggestionsList suggestionList = mListViewHolder.mListView;
&& mSuggestionList.isShown()) { if (KeyNavigationUtil.isGoDown(event) && suggestionList != null
&& suggestionList.isShown()) {
int suggestionCount = mSuggestionListAdapter.getCount(); int suggestionCount = mSuggestionListAdapter.getCount();
if (mSuggestionList.getSelectedItemPosition() < suggestionCount - 1) { if (suggestionList.getSelectedItemPosition() < suggestionCount - 1) {
if (suggestionCount > 0) mIgnoreOmniboxItemSelection = false; if (suggestionCount > 0) mIgnoreOmniboxItemSelection = false;
} else { } else {
// Do not pass down events when the last item is already selected as it will // Do not pass down events when the last item is already selected as it will
...@@ -516,32 +471,32 @@ public class AutocompleteCoordinator ...@@ -516,32 +471,32 @@ public class AutocompleteCoordinator
return true; return true;
} }
if (mSuggestionList.getSelectedItemPosition() == ListView.INVALID_POSITION) { if (suggestionList.getSelectedItemPosition() == ListView.INVALID_POSITION) {
// When clearing the selection after a text change, state is not reset // When clearing the selection after a text change, state is not reset
// correctly so hitting down again will cause it to start from the previous // correctly so hitting down again will cause it to start from the previous
// selection point. We still have to send the key down event to let the list // selection point. We still have to send the key down event to let the list
// view items take focus, but then we select the first item explicitly. // view items take focus, but then we select the first item explicitly.
boolean result = mSuggestionList.onKeyDown(keyCode, event); boolean result = suggestionList.onKeyDown(keyCode, event);
mSuggestionList.setSelection(0); suggestionList.setSelection(0);
return result; return result;
} else { } else {
return mSuggestionList.onKeyDown(keyCode, event); return suggestionList.onKeyDown(keyCode, event);
} }
} else if (KeyNavigationUtil.isGoUp(event) && mSuggestionList != null } else if (KeyNavigationUtil.isGoUp(event) && suggestionList != null
&& mSuggestionList.isShown()) { && suggestionList.isShown()) {
if (mSuggestionList.getSelectedItemPosition() != 0 if (suggestionList.getSelectedItemPosition() != 0
&& mSuggestionListAdapter.getCount() > 0) { && mSuggestionListAdapter.getCount() > 0) {
mIgnoreOmniboxItemSelection = false; mIgnoreOmniboxItemSelection = false;
} }
return mSuggestionList.onKeyDown(keyCode, event); return suggestionList.onKeyDown(keyCode, event);
} else if (KeyNavigationUtil.isGoRight(event) && mSuggestionList != null } else if (KeyNavigationUtil.isGoRight(event) && suggestionList != null
&& mSuggestionList.isShown() && suggestionList.isShown()
&& mSuggestionList.getSelectedItemPosition() != ListView.INVALID_POSITION) { && suggestionList.getSelectedItemPosition() != ListView.INVALID_POSITION) {
OmniboxSuggestion suggestion = OmniboxSuggestion suggestion =
mMediator.getSuggestionAt(mSuggestionList.getSelectedItemPosition()); mMediator.getSuggestionAt(suggestionList.getSelectedItemPosition());
mDelegate.setOmniboxEditingText(suggestion.getFillIntoEdit()); mDelegate.setOmniboxEditingText(suggestion.getFillIntoEdit());
onTextChangedForAutocomplete(); onTextChangedForAutocomplete();
mSuggestionList.setSelection(0); suggestionList.setSelection(0);
return true; return true;
} else if (KeyNavigationUtil.isEnter(event) && mParent.getVisibility() == View.VISIBLE) { } else if (KeyNavigationUtil.isEnter(event) && mParent.getVisibility() == View.VISIBLE) {
mDelegate.hideKeyboard(); mDelegate.hideKeyboard();
...@@ -562,11 +517,12 @@ public class AutocompleteCoordinator ...@@ -562,11 +517,12 @@ public class AutocompleteCoordinator
OmniboxSuggestion suggestionMatch; OmniboxSuggestion suggestionMatch;
boolean skipOutOfBoundsCheck = false; boolean skipOutOfBoundsCheck = false;
if (mSuggestionList != null && mSuggestionList.isShown() ListView suggestionList = mListViewHolder.mListView;
&& mSuggestionList.getSelectedItemPosition() != ListView.INVALID_POSITION) { if (suggestionList != null && suggestionList.isShown()
&& suggestionList.getSelectedItemPosition() != ListView.INVALID_POSITION) {
// Bluetooth keyboard case: the user highlighted a suggestion with the arrow // Bluetooth keyboard case: the user highlighted a suggestion with the arrow
// keys, then pressed enter. // keys, then pressed enter.
suggestionMatchPosition = mSuggestionList.getSelectedItemPosition(); suggestionMatchPosition = suggestionList.getSelectedItemPosition();
suggestionMatch = mMediator.getSuggestionAt(suggestionMatchPosition); suggestionMatch = mMediator.getSuggestionAt(suggestionMatchPosition);
} else if (mMediator.getSuggestionCount() > 0 } else if (mMediator.getSuggestionCount() > 0
&& urlText.equals(mUrlTextAfterSuggestionsReceived)) { && urlText.equals(mUrlTextAfterSuggestionsReceived)) {
...@@ -664,8 +620,8 @@ public class AutocompleteCoordinator ...@@ -664,8 +620,8 @@ public class AutocompleteCoordinator
mHasStartedNewOmniboxEditSession = true; mHasStartedNewOmniboxEditSession = true;
} }
if (!mParent.isInTouchMode() && mSuggestionList != null) { if (!mParent.isInTouchMode() && mListViewHolder.mListView != null) {
mSuggestionList.setSelection(0); mListViewHolder.mListView.setSelection(0);
} }
stopAutocomplete(false); stopAutocomplete(false);
...@@ -734,10 +690,12 @@ public class AutocompleteCoordinator ...@@ -734,10 +690,12 @@ public class AutocompleteCoordinator
mUrlTextAfterSuggestionsReceived = userText + inlineAutocompleteText; mUrlTextAfterSuggestionsReceived = userText + inlineAutocompleteText;
// Show the suggestion list. // Show the suggestion list.
initSuggestionList(); // It may not have been initialized yet.
resetMaxTextWidths(); resetMaxTextWidths();
mMediator.onSuggestionsReceived(newSuggestions, inlineAutocompleteText); mMediator.onSuggestionsReceived(newSuggestions, inlineAutocompleteText);
if (mSuggestionsShown && mMediator.getSuggestionCount() == 0) hideSuggestions(); if (mListModel.get(SuggestionListProperties.VISIBLE)
&& mMediator.getSuggestionCount() == 0) {
hideSuggestions();
}
mDelegate.onSuggestionsChanged(inlineAutocompleteText); mDelegate.onSuggestionsChanged(inlineAutocompleteText);
updateOmniboxSuggestionsVisibility(); updateOmniboxSuggestionsVisibility();
...@@ -830,9 +788,6 @@ public class AutocompleteCoordinator ...@@ -830,9 +788,6 @@ public class AutocompleteCoordinator
* @param useDarkColors Whether dark colors should be applied to the UI. * @param useDarkColors Whether dark colors should be applied to the UI.
*/ */
public void updateVisualsForState(boolean useDarkColors) { public void updateVisualsForState(boolean useDarkColors) {
if (mSuggestionList != null) {
mSuggestionList.refreshPopupBackground();
}
mMediator.setUseDarkColors(useDarkColors); mMediator.setUseDarkColors(useDarkColors);
} }
......
...@@ -15,7 +15,6 @@ import android.util.TypedValue; ...@@ -15,7 +15,6 @@ import android.util.TypedValue;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import org.chromium.base.Callback;
import org.chromium.base.ThreadUtils; import org.chromium.base.ThreadUtils;
import org.chromium.base.VisibleForTesting; import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.R; import org.chromium.chrome.R;
...@@ -42,7 +41,7 @@ import java.util.Map; ...@@ -42,7 +41,7 @@ import java.util.Map;
class AutocompleteMediator implements OnSuggestionsReceivedListener { class AutocompleteMediator implements OnSuggestionsReceivedListener {
private final Context mContext; private final Context mContext;
private final UrlBarEditingTextStateProvider mUrlBarEditingTextProvider; private final UrlBarEditingTextStateProvider mUrlBarEditingTextProvider;
private final Callback<List<PropertyModel>> mModelsChangedCallback; private final PropertyModel mListPropertyModel;
private final List<Pair<OmniboxSuggestion, PropertyModel>> mCurrentModels; private final List<Pair<OmniboxSuggestion, PropertyModel>> mCurrentModels;
private final Map<String, List<PropertyModel>> mPendingAnswerRequestUrls; private final Map<String, List<PropertyModel>> mPendingAnswerRequestUrls;
private final AnswersImageFetcher mImageFetcher; private final AnswersImageFetcher mImageFetcher;
...@@ -54,10 +53,10 @@ class AutocompleteMediator implements OnSuggestionsReceivedListener { ...@@ -54,10 +53,10 @@ class AutocompleteMediator implements OnSuggestionsReceivedListener {
private boolean mPreventSuggestionListPropertyChanges; private boolean mPreventSuggestionListPropertyChanges;
public AutocompleteMediator(Context context, UrlBarEditingTextStateProvider textProvider, public AutocompleteMediator(Context context, UrlBarEditingTextStateProvider textProvider,
Callback<List<PropertyModel>> onModelsChanged) { PropertyModel listPropertyModel) {
mContext = context; mContext = context;
mUrlBarEditingTextProvider = textProvider; mUrlBarEditingTextProvider = textProvider;
mModelsChangedCallback = onModelsChanged; mListPropertyModel = listPropertyModel;
mCurrentModels = new ArrayList<>(); mCurrentModels = new ArrayList<>();
mPendingAnswerRequestUrls = new HashMap<>(); mPendingAnswerRequestUrls = new HashMap<>();
mImageFetcher = new AnswersImageFetcher(); mImageFetcher = new AnswersImageFetcher();
...@@ -113,7 +112,7 @@ class AutocompleteMediator implements OnSuggestionsReceivedListener { ...@@ -113,7 +112,7 @@ class AutocompleteMediator implements OnSuggestionsReceivedListener {
if (mPreventSuggestionListPropertyChanges) return; if (mPreventSuggestionListPropertyChanges) return;
List<PropertyModel> models = new ArrayList<>(mCurrentModels.size()); List<PropertyModel> models = new ArrayList<>(mCurrentModels.size());
for (int i = 0; i < mCurrentModels.size(); i++) models.add(mCurrentModels.get(i).second); for (int i = 0; i < mCurrentModels.size(); i++) models.add(mCurrentModels.get(i).second);
mModelsChangedCallback.onResult(models); mListPropertyModel.set(SuggestionListProperties.SUGGESTION_MODELS, models);
} }
private void populateModelForSuggestion( private void populateModelForSuggestion(
...@@ -425,6 +424,7 @@ class AutocompleteMediator implements OnSuggestionsReceivedListener { ...@@ -425,6 +424,7 @@ class AutocompleteMediator implements OnSuggestionsReceivedListener {
*/ */
public void setUseDarkColors(boolean useDarkColors) { public void setUseDarkColors(boolean useDarkColors) {
mUseDarkColors = useDarkColors; mUseDarkColors = useDarkColors;
mListPropertyModel.set(SuggestionListProperties.USE_DARK_BACKGROUND, !useDarkColors);
for (int i = 0; i < mCurrentModels.size(); i++) { for (int i = 0; i < mCurrentModels.size(); i++) {
PropertyModel model = mCurrentModels.get(i).second; PropertyModel model = mCurrentModels.get(i).second;
model.set(SuggestionViewProperties.USE_DARK_COLORS, mUseDarkColors); model.set(SuggestionViewProperties.USE_DARK_COLORS, mUseDarkColors);
......
...@@ -8,7 +8,6 @@ import android.content.Context; ...@@ -8,7 +8,6 @@ import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewCompat;
import android.view.View; import android.view.View;
...@@ -29,18 +28,17 @@ import java.util.ArrayList; ...@@ -29,18 +28,17 @@ import java.util.ArrayList;
*/ */
@VisibleForTesting @VisibleForTesting
public class OmniboxSuggestionsList extends ListView { public class OmniboxSuggestionsList extends ListView {
private static final int OMNIBOX_RESULTS_BG_COLOR = 0xFFFFFFFF; private static final int LIGHT_BG_COLOR = 0xFFFFFFFF;
private static final int OMNIBOX_INCOGNITO_RESULTS_BG_COLOR = 0xFF3C4043; private static final int DARK_BG_COLOR = 0xFF3C4043;
private final OmniboxSuggestionListEmbedder mEmbedder;
private final View mAnchorView;
private final View mAlignmentView;
private final int[] mTempPosition = new int[2]; private final int[] mTempPosition = new int[2];
private final Rect mTempRect = new Rect(); private final Rect mTempRect = new Rect();
private final OnGlobalLayoutListener mAnchorViewLayoutListener; private OmniboxSuggestionListEmbedder mEmbedder;
private final OnLayoutChangeListener mAlignmentViewLayoutListener; private View mAnchorView;
private View mAlignmentView;
private OnGlobalLayoutListener mAnchorViewLayoutListener;
private OnLayoutChangeListener mAlignmentViewLayoutListener;
/** /**
* Provides the capabilities required to embed the omnibox suggestion list into the UI. * Provides the capabilities required to embed the omnibox suggestion list into the UI.
...@@ -62,20 +60,14 @@ public class OmniboxSuggestionsList extends ListView { ...@@ -62,20 +60,14 @@ public class OmniboxSuggestionsList extends ListView {
/** Return whether the suggestions are being rendered in the tablet UI. */ /** Return whether the suggestions are being rendered in the tablet UI. */
boolean isTablet(); boolean isTablet();
/** Return whether the current state is viewing incognito. */
boolean isIncognito();
} }
/** /**
* Constructs a new list designed for containing omnibox suggestions. * Constructs a new list designed for containing omnibox suggestions.
* @param context Context used for contained views. * @param context Context used for contained views.
* @param embedder The embedder for the omnibox list providing access to external views and
* services.
*/ */
public OmniboxSuggestionsList(Context context, OmniboxSuggestionListEmbedder embedder) { public OmniboxSuggestionsList(Context context) {
super(context, null, android.R.attr.dropDownListViewStyle); super(context, null, android.R.attr.dropDownListViewStyle);
mEmbedder = embedder;
setDivider(null); setDivider(null);
setFocusable(true); setFocusable(true);
setFocusableInTouchMode(true); setFocusableInTouchMode(true);
...@@ -83,9 +75,12 @@ public class OmniboxSuggestionsList extends ListView { ...@@ -83,9 +75,12 @@ public class OmniboxSuggestionsList extends ListView {
int paddingBottom = context.getResources().getDimensionPixelOffset( int paddingBottom = context.getResources().getDimensionPixelOffset(
R.dimen.omnibox_suggestion_list_padding_bottom); R.dimen.omnibox_suggestion_list_padding_bottom);
ViewCompat.setPaddingRelative(this, 0, 0, 0, paddingBottom); ViewCompat.setPaddingRelative(this, 0, 0, 0, paddingBottom);
}
refreshPopupBackground(); /** Set the embedder for the list view. */
void setEmbedder(OmniboxSuggestionListEmbedder embedder) {
assert mEmbedder == null;
mEmbedder = embedder;
mAnchorView = mEmbedder.getAnchorView(); mAnchorView = mEmbedder.getAnchorView();
// Prior to Android M, the contextual actions associated with the omnibox were anchored to // Prior to Android M, the contextual actions associated with the omnibox were anchored to
// the top of the screen and not a floating copy/paste menu like on newer versions. As a // the top of the screen and not a floating copy/paste menu like on newer versions. As a
...@@ -146,19 +141,8 @@ public class OmniboxSuggestionsList extends ListView { ...@@ -146,19 +141,8 @@ public class OmniboxSuggestionsList extends ListView {
/** /**
* Update the suggestion popup background to reflect the current state. * Update the suggestion popup background to reflect the current state.
*/ */
void refreshPopupBackground() { void refreshPopupBackground(boolean useDarkBackground) {
setBackground(getSuggestionPopupBackground()); int color = useDarkBackground ? DARK_BG_COLOR : LIGHT_BG_COLOR;
}
/**
* @return The background for the omnibox suggestions popup.
*/
private Drawable getSuggestionPopupBackground() {
int omniboxResultsColorForNonIncognito = OMNIBOX_RESULTS_BG_COLOR;
int omniboxResultsColorForIncognito = OMNIBOX_INCOGNITO_RESULTS_BG_COLOR;
int color = mEmbedder.isIncognito() ? omniboxResultsColorForIncognito
: omniboxResultsColorForNonIncognito;
if (!isHardwareAccelerated()) { if (!isHardwareAccelerated()) {
// When HW acceleration is disabled, changing mSuggestionList' items somehow erases // When HW acceleration is disabled, changing mSuggestionList' items somehow erases
// mOmniboxResultsContainer' background from the area not covered by mSuggestionList. // mOmniboxResultsContainer' background from the area not covered by mSuggestionList.
...@@ -169,7 +153,7 @@ public class OmniboxSuggestionsList extends ListView { ...@@ -169,7 +153,7 @@ public class OmniboxSuggestionsList extends ListView {
color = Color.argb(254, Color.red(color), Color.green(color), Color.blue(color)); color = Color.argb(254, Color.red(color), Color.green(color), Color.blue(color));
} }
} }
return new ColorDrawable(color); setBackground(new ColorDrawable(color));
} }
@Override @Override
......
// Copyright 2018 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;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsList.OmniboxSuggestionListEmbedder;
import java.util.List;
/**
* The properties controlling the state of the list of suggestion items.
*/
public class SuggestionListProperties {
/** Whether the suggestion list is visible. */
public static final WritableBooleanPropertyKey VISIBLE = new WritableBooleanPropertyKey();
/** The embedder for the suggestion list. */
public static final WritableObjectPropertyKey<OmniboxSuggestionListEmbedder> EMBEDDER =
new WritableObjectPropertyKey<>();
/** The list of models controlling the state of the suggestion items. */
public static final WritableObjectPropertyKey<List<PropertyModel>> SUGGESTION_MODELS =
new WritableObjectPropertyKey<>();
/** Whether the suggestion list should have a dark background. */
public static final WritableBooleanPropertyKey USE_DARK_BACKGROUND =
new WritableBooleanPropertyKey();
public static final PropertyKey[] ALL_KEYS =
new PropertyKey[] {VISIBLE, EMBEDDER, SUGGESTION_MODELS, USE_DARK_BACKGROUND};
}
// Copyright 2018 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;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import org.chromium.base.StrictModeContext;
import org.chromium.base.Supplier;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsList.OmniboxSuggestionListEmbedder;
import org.chromium.ui.UiUtils;
/**
* Handles property updates to the suggestion list component.
*/
class SuggestionListViewBinder {
/**
* Holds the view components needed to renderer the suggestion list.
*/
public static class SuggestionListViewHolder {
public final Context context;
public final Supplier<ViewStub> containerStubSupplier;
public final OmniboxResultsAdapter adapter;
OmniboxSuggestionsList mListView;
ViewGroup mContainer;
public SuggestionListViewHolder(Context context, Supplier<ViewStub> containerStubSupplier,
OmniboxResultsAdapter adapter) {
this.context = context;
this.containerStubSupplier = containerStubSupplier;
this.adapter = adapter;
}
}
/**
* @see
* org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder#bind(Object,
* Object, Object)
*/
public static void bind(
PropertyModel model, SuggestionListViewHolder view, PropertyKey propertyKey) {
if (SuggestionListProperties.VISIBLE.equals(propertyKey)) {
boolean visible = model.get(SuggestionListProperties.VISIBLE);
if (visible) {
if (view.mContainer == null) {
view.mContainer = (ViewGroup) view.containerStubSupplier.get().inflate();
}
initializeSuggestionList(view, model);
view.mContainer.setVisibility(View.VISIBLE);
if (view.mListView.getParent() == null) view.mContainer.addView(view.mListView);
view.mListView.show();
} else {
if (view.mContainer == null) return;
view.mListView.setVisibility(View.GONE);
UiUtils.removeViewFromParent(view.mListView);
view.mContainer.setVisibility(View.INVISIBLE);
}
} else if (SuggestionListProperties.EMBEDDER.equals(propertyKey)) {
if (view.mListView == null) return;
view.mListView.setEmbedder(model.get(SuggestionListProperties.EMBEDDER));
} else if (SuggestionListProperties.SUGGESTION_MODELS.equals(propertyKey)) {
view.adapter.updateSuggestions(model.get(SuggestionListProperties.SUGGESTION_MODELS));
} else if (SuggestionListProperties.USE_DARK_BACKGROUND.equals(propertyKey)) {
if (view.mListView == null) return;
view.mListView.refreshPopupBackground(
model.get(SuggestionListProperties.USE_DARK_BACKGROUND));
}
}
private static void initializeSuggestionList(
SuggestionListViewHolder view, PropertyModel model) {
if (view.mListView != null) return;
// TODO(tedchoc): Investigate lazily building the suggestion list off of the UI thread.
try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
view.mListView = new OmniboxSuggestionsList(view.context);
}
// Start with visibility GONE to ensure that show() is called. http://crbug.com/517438
view.mListView.setVisibility(View.GONE);
view.mListView.setAdapter(view.adapter);
view.mListView.setClipToPadding(false);
OmniboxSuggestionListEmbedder embedder = model.get(SuggestionListProperties.EMBEDDER);
if (embedder != null) view.mListView.setEmbedder(embedder);
view.mListView.refreshPopupBackground(
model.get(SuggestionListProperties.USE_DARK_BACKGROUND));
}
}
...@@ -1105,6 +1105,8 @@ chrome_java_sources = [ ...@@ -1105,6 +1105,8 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestion.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestion.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsList.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsList.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionAnswer.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionAnswer.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListProperties.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinder.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionView.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionView.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewProperties.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewProperties.java",
"java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewViewBinder.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionViewViewBinder.java",
......
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