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
return mIsTablet;
}
@Override
public boolean isIncognito() {
return mToolbarDataProvider.isIncognito();
}
@Override
public WindowDelegate getWindowDelegate() {
return mWindowDelegate;
......@@ -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
* a phone device.
*/
@Override
public boolean isUrlFocusChangeInProgress() {
return mUrlFocusChangeInProgress;
}
......
......@@ -19,10 +19,12 @@ import android.view.WindowManager;
import android.widget.ListView;
import org.chromium.base.Log;
import org.chromium.base.StrictModeContext;
import org.chromium.base.Supplier;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.metrics.RecordUserAction;
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.OmniboxSuggestionType;
import org.chromium.chrome.browser.omnibox.UrlBar.UrlTextChangeListener;
......@@ -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.AutocompleteMediator.OmniboxSuggestionDelegate;
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.toolbar.ToolbarDataProvider;
import org.chromium.chrome.browser.util.KeyNavigationUtil;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.UiUtils;
import org.chromium.ui.base.PageTransition;
import java.util.ArrayList;
......@@ -55,21 +57,19 @@ public class AutocompleteCoordinator
private final Context mContext;
private final ViewGroup mParent;
private final AutocompleteDelegate mDelegate;
private final OmniboxSuggestionListEmbedder mSuggestionListEmbedder;
private final UrlBarEditingTextStateProvider mUrlBarEditingTextProvider;
private final AutocompleteMediator mMediator;
private final PropertyModel mListModel;
private final SuggestionListViewHolder mListViewHolder;
private final OmniboxResultsAdapter mSuggestionListAdapter;
private final AnswersImageFetcher mAnswersImageFetcher;
private final List<Runnable> mDeferredNativeRunnables = new ArrayList<Runnable>();
private ToolbarDataProvider mToolbarDataProvider;
private OmniboxSuggestionsList mSuggestionList;
private boolean mNativeInitialized;
private AutocompleteController mAutocomplete;
private boolean mSuggestionsShown;
private boolean mSuggestionModalShown;
private ViewGroup mOmniboxResultsContainer;
private float mMaxRequiredWidth;
private float mMaxMatchContentsWidth;
private boolean mCanShowSuggestions;
......@@ -145,11 +145,6 @@ public class AutocompleteCoordinator
* @return Whether the URL currently has focus.
*/
boolean isUrlBarFocused();
/**
* @return Whether a URL focus change animation is currently in progress.
*/
boolean isUrlFocusChangeInProgress();
}
/**
......@@ -167,14 +162,139 @@ public class AutocompleteCoordinator
mParent = parent;
mContext = parent.getContext();
mDelegate = delegate;
mSuggestionListEmbedder = listEmbedder;
mUrlBarEditingTextProvider = urlBarEditingTextProvider;
mAnswersImageFetcher = new AnswersImageFetcher();
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);
mMediator = new AutocompleteMediator(
mContext, urlBarEditingTextProvider, mSuggestionListAdapter::updateSuggestions);
mMediator = new AutocompleteMediator(mContext, urlBarEditingTextProvider, mListModel);
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
......@@ -269,14 +389,14 @@ public class AutocompleteCoordinator
*/
@VisibleForTesting
public OmniboxSuggestionsList getSuggestionList() {
return mSuggestionList;
return mListViewHolder.mListView;
}
/**
* @return Whether the suggestions list is currently visible.
*/
public boolean isSuggestionsListShown() {
return mSuggestionsShown;
return mListModel.get(SuggestionListProperties.VISIBLE);
}
/**
......@@ -302,141 +422,6 @@ public class AutocompleteCoordinator
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.
* This is needed for infinite suggestions where we try to vertically align the leading
......@@ -447,46 +432,15 @@ public class AutocompleteCoordinator
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.
*/
private void updateOmniboxSuggestionsVisibility() {
initOmniboxResultsContainer();
if (mSuggestionList == null) return;
boolean isContainerVisible = mOmniboxResultsContainer.getVisibility() == View.VISIBLE;
boolean shouldBeVisible = mCanShowSuggestions && getSuggestionCount() > 0;
if (isContainerVisible == shouldBeVisible) return;
mSuggestionsShown = shouldBeVisible;
final boolean isListVisible = mSuggestionList.getVisibility() == View.VISIBLE;
if (shouldBeVisible && !isListVisible) {
boolean wasVisible = mListModel.get(SuggestionListProperties.VISIBLE);
mListModel.set(SuggestionListProperties.VISIBLE, shouldBeVisible);
if (shouldBeVisible && !wasVisible) {
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
* @return Whether the key event was handled.
*/
public boolean handleKeyEvent(int keyCode, KeyEvent event) {
if (KeyNavigationUtil.isGoDown(event) && mSuggestionList != null
&& mSuggestionList.isShown()) {
OmniboxSuggestionsList suggestionList = mListViewHolder.mListView;
if (KeyNavigationUtil.isGoDown(event) && suggestionList != null
&& suggestionList.isShown()) {
int suggestionCount = mSuggestionListAdapter.getCount();
if (mSuggestionList.getSelectedItemPosition() < suggestionCount - 1) {
if (suggestionList.getSelectedItemPosition() < suggestionCount - 1) {
if (suggestionCount > 0) mIgnoreOmniboxItemSelection = false;
} else {
// Do not pass down events when the last item is already selected as it will
......@@ -516,32 +471,32 @@ public class AutocompleteCoordinator
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
// 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
// view items take focus, but then we select the first item explicitly.
boolean result = mSuggestionList.onKeyDown(keyCode, event);
mSuggestionList.setSelection(0);
boolean result = suggestionList.onKeyDown(keyCode, event);
suggestionList.setSelection(0);
return result;
} else {
return mSuggestionList.onKeyDown(keyCode, event);
return suggestionList.onKeyDown(keyCode, event);
}
} else if (KeyNavigationUtil.isGoUp(event) && mSuggestionList != null
&& mSuggestionList.isShown()) {
if (mSuggestionList.getSelectedItemPosition() != 0
} else if (KeyNavigationUtil.isGoUp(event) && suggestionList != null
&& suggestionList.isShown()) {
if (suggestionList.getSelectedItemPosition() != 0
&& mSuggestionListAdapter.getCount() > 0) {
mIgnoreOmniboxItemSelection = false;
}
return mSuggestionList.onKeyDown(keyCode, event);
} else if (KeyNavigationUtil.isGoRight(event) && mSuggestionList != null
&& mSuggestionList.isShown()
&& mSuggestionList.getSelectedItemPosition() != ListView.INVALID_POSITION) {
return suggestionList.onKeyDown(keyCode, event);
} else if (KeyNavigationUtil.isGoRight(event) && suggestionList != null
&& suggestionList.isShown()
&& suggestionList.getSelectedItemPosition() != ListView.INVALID_POSITION) {
OmniboxSuggestion suggestion =
mMediator.getSuggestionAt(mSuggestionList.getSelectedItemPosition());
mMediator.getSuggestionAt(suggestionList.getSelectedItemPosition());
mDelegate.setOmniboxEditingText(suggestion.getFillIntoEdit());
onTextChangedForAutocomplete();
mSuggestionList.setSelection(0);
suggestionList.setSelection(0);
return true;
} else if (KeyNavigationUtil.isEnter(event) && mParent.getVisibility() == View.VISIBLE) {
mDelegate.hideKeyboard();
......@@ -562,11 +517,12 @@ public class AutocompleteCoordinator
OmniboxSuggestion suggestionMatch;
boolean skipOutOfBoundsCheck = false;
if (mSuggestionList != null && mSuggestionList.isShown()
&& mSuggestionList.getSelectedItemPosition() != ListView.INVALID_POSITION) {
ListView suggestionList = mListViewHolder.mListView;
if (suggestionList != null && suggestionList.isShown()
&& suggestionList.getSelectedItemPosition() != ListView.INVALID_POSITION) {
// Bluetooth keyboard case: the user highlighted a suggestion with the arrow
// keys, then pressed enter.
suggestionMatchPosition = mSuggestionList.getSelectedItemPosition();
suggestionMatchPosition = suggestionList.getSelectedItemPosition();
suggestionMatch = mMediator.getSuggestionAt(suggestionMatchPosition);
} else if (mMediator.getSuggestionCount() > 0
&& urlText.equals(mUrlTextAfterSuggestionsReceived)) {
......@@ -664,8 +620,8 @@ public class AutocompleteCoordinator
mHasStartedNewOmniboxEditSession = true;
}
if (!mParent.isInTouchMode() && mSuggestionList != null) {
mSuggestionList.setSelection(0);
if (!mParent.isInTouchMode() && mListViewHolder.mListView != null) {
mListViewHolder.mListView.setSelection(0);
}
stopAutocomplete(false);
......@@ -734,10 +690,12 @@ public class AutocompleteCoordinator
mUrlTextAfterSuggestionsReceived = userText + inlineAutocompleteText;
// Show the suggestion list.
initSuggestionList(); // It may not have been initialized yet.
resetMaxTextWidths();
mMediator.onSuggestionsReceived(newSuggestions, inlineAutocompleteText);
if (mSuggestionsShown && mMediator.getSuggestionCount() == 0) hideSuggestions();
if (mListModel.get(SuggestionListProperties.VISIBLE)
&& mMediator.getSuggestionCount() == 0) {
hideSuggestions();
}
mDelegate.onSuggestionsChanged(inlineAutocompleteText);
updateOmniboxSuggestionsVisibility();
......@@ -830,9 +788,6 @@ public class AutocompleteCoordinator
* @param useDarkColors Whether dark colors should be applied to the UI.
*/
public void updateVisualsForState(boolean useDarkColors) {
if (mSuggestionList != null) {
mSuggestionList.refreshPopupBackground();
}
mMediator.setUseDarkColors(useDarkColors);
}
......
......@@ -15,7 +15,6 @@ import android.util.TypedValue;
import android.view.View;
import android.widget.TextView;
import org.chromium.base.Callback;
import org.chromium.base.ThreadUtils;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.R;
......@@ -42,7 +41,7 @@ import java.util.Map;
class AutocompleteMediator implements OnSuggestionsReceivedListener {
private final Context mContext;
private final UrlBarEditingTextStateProvider mUrlBarEditingTextProvider;
private final Callback<List<PropertyModel>> mModelsChangedCallback;
private final PropertyModel mListPropertyModel;
private final List<Pair<OmniboxSuggestion, PropertyModel>> mCurrentModels;
private final Map<String, List<PropertyModel>> mPendingAnswerRequestUrls;
private final AnswersImageFetcher mImageFetcher;
......@@ -54,10 +53,10 @@ class AutocompleteMediator implements OnSuggestionsReceivedListener {
private boolean mPreventSuggestionListPropertyChanges;
public AutocompleteMediator(Context context, UrlBarEditingTextStateProvider textProvider,
Callback<List<PropertyModel>> onModelsChanged) {
PropertyModel listPropertyModel) {
mContext = context;
mUrlBarEditingTextProvider = textProvider;
mModelsChangedCallback = onModelsChanged;
mListPropertyModel = listPropertyModel;
mCurrentModels = new ArrayList<>();
mPendingAnswerRequestUrls = new HashMap<>();
mImageFetcher = new AnswersImageFetcher();
......@@ -113,7 +112,7 @@ class AutocompleteMediator implements OnSuggestionsReceivedListener {
if (mPreventSuggestionListPropertyChanges) return;
List<PropertyModel> models = new ArrayList<>(mCurrentModels.size());
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(
......@@ -425,6 +424,7 @@ class AutocompleteMediator implements OnSuggestionsReceivedListener {
*/
public void setUseDarkColors(boolean useDarkColors) {
mUseDarkColors = useDarkColors;
mListPropertyModel.set(SuggestionListProperties.USE_DARK_BACKGROUND, !useDarkColors);
for (int i = 0; i < mCurrentModels.size(); i++) {
PropertyModel model = mCurrentModels.get(i).second;
model.set(SuggestionViewProperties.USE_DARK_COLORS, mUseDarkColors);
......
......@@ -8,7 +8,6 @@ import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.view.View;
......@@ -29,18 +28,17 @@ import java.util.ArrayList;
*/
@VisibleForTesting
public class OmniboxSuggestionsList extends ListView {
private static final int OMNIBOX_RESULTS_BG_COLOR = 0xFFFFFFFF;
private static final int OMNIBOX_INCOGNITO_RESULTS_BG_COLOR = 0xFF3C4043;
private final OmniboxSuggestionListEmbedder mEmbedder;
private final View mAnchorView;
private final View mAlignmentView;
private static final int LIGHT_BG_COLOR = 0xFFFFFFFF;
private static final int DARK_BG_COLOR = 0xFF3C4043;
private final int[] mTempPosition = new int[2];
private final Rect mTempRect = new Rect();
private final OnGlobalLayoutListener mAnchorViewLayoutListener;
private final OnLayoutChangeListener mAlignmentViewLayoutListener;
private OmniboxSuggestionListEmbedder mEmbedder;
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.
......@@ -62,20 +60,14 @@ public class OmniboxSuggestionsList extends ListView {
/** Return whether the suggestions are being rendered in the tablet UI. */
boolean isTablet();
/** Return whether the current state is viewing incognito. */
boolean isIncognito();
}
/**
* Constructs a new list designed for containing omnibox suggestions.
* @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);
mEmbedder = embedder;
setDivider(null);
setFocusable(true);
setFocusableInTouchMode(true);
......@@ -83,9 +75,12 @@ public class OmniboxSuggestionsList extends ListView {
int paddingBottom = context.getResources().getDimensionPixelOffset(
R.dimen.omnibox_suggestion_list_padding_bottom);
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();
// 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
......@@ -146,19 +141,8 @@ public class OmniboxSuggestionsList extends ListView {
/**
* Update the suggestion popup background to reflect the current state.
*/
void refreshPopupBackground() {
setBackground(getSuggestionPopupBackground());
}
/**
* @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;
void refreshPopupBackground(boolean useDarkBackground) {
int color = useDarkBackground ? DARK_BG_COLOR : LIGHT_BG_COLOR;
if (!isHardwareAccelerated()) {
// When HW acceleration is disabled, changing mSuggestionList' items somehow erases
// mOmniboxResultsContainer' background from the area not covered by mSuggestionList.
......@@ -169,7 +153,7 @@ public class OmniboxSuggestionsList extends ListView {
color = Color.argb(254, Color.red(color), Color.green(color), Color.blue(color));
}
}
return new ColorDrawable(color);
setBackground(new ColorDrawable(color));
}
@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 = [
"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/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/SuggestionViewProperties.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