mobile/android/base/home/SearchEngineRow.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 package org.mozilla.gecko.home;
michael@0 7
michael@0 8 import org.mozilla.gecko.R;
michael@0 9 import org.mozilla.gecko.Telemetry;
michael@0 10 import org.mozilla.gecko.TelemetryContract;
michael@0 11 import org.mozilla.gecko.home.BrowserSearch.OnEditSuggestionListener;
michael@0 12 import org.mozilla.gecko.home.BrowserSearch.OnSearchListener;
michael@0 13 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
michael@0 14 import org.mozilla.gecko.util.StringUtils;
michael@0 15 import org.mozilla.gecko.widget.AnimatedHeightLayout;
michael@0 16 import org.mozilla.gecko.widget.FaviconView;
michael@0 17 import org.mozilla.gecko.widget.FlowLayout;
michael@0 18
michael@0 19 import android.content.Context;
michael@0 20 import android.util.AttributeSet;
michael@0 21 import android.view.KeyEvent;
michael@0 22 import android.view.LayoutInflater;
michael@0 23 import android.view.View;
michael@0 24 import android.view.animation.AlphaAnimation;
michael@0 25 import android.widget.ImageView;
michael@0 26 import android.widget.LinearLayout;
michael@0 27 import android.widget.TextView;
michael@0 28
michael@0 29 import java.util.EnumSet;
michael@0 30
michael@0 31 class SearchEngineRow extends AnimatedHeightLayout {
michael@0 32 // Duration for fade-in animation
michael@0 33 private static final int ANIMATION_DURATION = 250;
michael@0 34
michael@0 35 // Inner views
michael@0 36 private final FlowLayout mSuggestionView;
michael@0 37 private final FaviconView mIconView;
michael@0 38 private final LinearLayout mUserEnteredView;
michael@0 39 private final TextView mUserEnteredTextView;
michael@0 40
michael@0 41 // Inflater used when updating from suggestions
michael@0 42 private final LayoutInflater mInflater;
michael@0 43
michael@0 44 // Search engine associated with this view
michael@0 45 private SearchEngine mSearchEngine;
michael@0 46
michael@0 47 // Event listeners for suggestion views
michael@0 48 private final OnClickListener mClickListener;
michael@0 49 private final OnLongClickListener mLongClickListener;
michael@0 50
michael@0 51 // On URL open listener
michael@0 52 private OnUrlOpenListener mUrlOpenListener;
michael@0 53
michael@0 54 // On search listener
michael@0 55 private OnSearchListener mSearchListener;
michael@0 56
michael@0 57 // On edit suggestion listener
michael@0 58 private OnEditSuggestionListener mEditSuggestionListener;
michael@0 59
michael@0 60 // Selected suggestion view
michael@0 61 private int mSelectedView = 0;
michael@0 62
michael@0 63 public SearchEngineRow(Context context) {
michael@0 64 this(context, null);
michael@0 65 }
michael@0 66
michael@0 67 public SearchEngineRow(Context context, AttributeSet attrs) {
michael@0 68 this(context, attrs, 0);
michael@0 69 }
michael@0 70
michael@0 71 public SearchEngineRow(Context context, AttributeSet attrs, int defStyle) {
michael@0 72 super(context, attrs, defStyle);
michael@0 73
michael@0 74 mClickListener = new OnClickListener() {
michael@0 75 @Override
michael@0 76 public void onClick(View v) {
michael@0 77 final String suggestion = getSuggestionTextFromView(v);
michael@0 78
michael@0 79 // If we're not clicking the user-entered view (the first suggestion item)
michael@0 80 // and the search matches a URL pattern, go to that URL. Otherwise, do a
michael@0 81 // search for the term.
michael@0 82 if (v != mUserEnteredView && !StringUtils.isSearchQuery(suggestion, false)) {
michael@0 83 if (mUrlOpenListener != null) {
michael@0 84 Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.SUGGESTION, "url");
michael@0 85
michael@0 86 mUrlOpenListener.onUrlOpen(suggestion, EnumSet.noneOf(OnUrlOpenListener.Flags.class));
michael@0 87 }
michael@0 88 } else if (mSearchListener != null) {
michael@0 89 if (v == mUserEnteredView) {
michael@0 90 Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.SUGGESTION, "user");
michael@0 91 } else {
michael@0 92 Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.SUGGESTION, "engine");
michael@0 93 }
michael@0 94 mSearchListener.onSearch(mSearchEngine, suggestion);
michael@0 95 }
michael@0 96 }
michael@0 97 };
michael@0 98
michael@0 99 mLongClickListener = new OnLongClickListener() {
michael@0 100 @Override
michael@0 101 public boolean onLongClick(View v) {
michael@0 102 if (mEditSuggestionListener != null) {
michael@0 103 final String suggestion = getSuggestionTextFromView(v);
michael@0 104 mEditSuggestionListener.onEditSuggestion(suggestion);
michael@0 105 return true;
michael@0 106 }
michael@0 107
michael@0 108 return false;
michael@0 109 }
michael@0 110 };
michael@0 111
michael@0 112 mInflater = LayoutInflater.from(context);
michael@0 113 mInflater.inflate(R.layout.search_engine_row, this);
michael@0 114
michael@0 115 mSuggestionView = (FlowLayout) findViewById(R.id.suggestion_layout);
michael@0 116 mIconView = (FaviconView) findViewById(R.id.suggestion_icon);
michael@0 117
michael@0 118 // User-entered search term is first suggestion
michael@0 119 mUserEnteredView = (LinearLayout) findViewById(R.id.suggestion_user_entered);
michael@0 120 mUserEnteredView.setOnClickListener(mClickListener);
michael@0 121
michael@0 122 mUserEnteredTextView = (TextView) findViewById(R.id.suggestion_text);
michael@0 123 }
michael@0 124
michael@0 125 private void setDescriptionOnSuggestion(View v, String suggestion) {
michael@0 126 v.setContentDescription(getResources().getString(R.string.suggestion_for_engine,
michael@0 127 mSearchEngine.name, suggestion));
michael@0 128 }
michael@0 129
michael@0 130 private String getSuggestionTextFromView(View v) {
michael@0 131 final TextView suggestionText = (TextView) v.findViewById(R.id.suggestion_text);
michael@0 132 return suggestionText.getText().toString();
michael@0 133 }
michael@0 134
michael@0 135 private void setSuggestionOnView(View v, String suggestion) {
michael@0 136 final TextView suggestionText = (TextView) v.findViewById(R.id.suggestion_text);
michael@0 137 suggestionText.setText(suggestion);
michael@0 138 setDescriptionOnSuggestion(suggestionText, suggestion);
michael@0 139 }
michael@0 140
michael@0 141 /**
michael@0 142 * Perform a search for the user-entered term.
michael@0 143 */
michael@0 144 public void performUserEnteredSearch() {
michael@0 145 String searchTerm = getSuggestionTextFromView(mUserEnteredView);
michael@0 146 if (mSearchListener != null) {
michael@0 147 Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.SUGGESTION, "user");
michael@0 148 mSearchListener.onSearch(mSearchEngine, searchTerm);
michael@0 149 }
michael@0 150 }
michael@0 151
michael@0 152 public void setSearchTerm(String searchTerm) {
michael@0 153 mUserEnteredTextView.setText(searchTerm);
michael@0 154
michael@0 155 // mSearchEngine is not set in the first call to this method; the content description
michael@0 156 // is instead initially set in updateFromSearchEngine.
michael@0 157 if (mSearchEngine != null) {
michael@0 158 setDescriptionOnSuggestion(mUserEnteredTextView, searchTerm);
michael@0 159 }
michael@0 160 }
michael@0 161
michael@0 162 public void setOnUrlOpenListener(OnUrlOpenListener listener) {
michael@0 163 mUrlOpenListener = listener;
michael@0 164 }
michael@0 165
michael@0 166 public void setOnSearchListener(OnSearchListener listener) {
michael@0 167 mSearchListener = listener;
michael@0 168 }
michael@0 169
michael@0 170 public void setOnEditSuggestionListener(OnEditSuggestionListener listener) {
michael@0 171 mEditSuggestionListener = listener;
michael@0 172 }
michael@0 173
michael@0 174 public void updateFromSearchEngine(SearchEngine searchEngine, boolean animate) {
michael@0 175 // Update search engine reference.
michael@0 176 mSearchEngine = searchEngine;
michael@0 177
michael@0 178 // Set the search engine icon (e.g., Google) for the row.
michael@0 179 mIconView.updateAndScaleImage(mSearchEngine.getIcon(), mSearchEngine.getEngineIdentifier());
michael@0 180
michael@0 181 // Set the initial content description.
michael@0 182 setDescriptionOnSuggestion(mUserEnteredTextView, mUserEnteredTextView.getText().toString());
michael@0 183
michael@0 184 // Add additional suggestions given by this engine.
michael@0 185 final int recycledSuggestionCount = mSuggestionView.getChildCount();
michael@0 186
michael@0 187 int suggestionCounter = 0;
michael@0 188 for (String suggestion : mSearchEngine.getSuggestions()) {
michael@0 189 final View suggestionItem;
michael@0 190
michael@0 191 // Reuse suggestion views from recycled view, if possible.
michael@0 192 if (suggestionCounter + 1 < recycledSuggestionCount) {
michael@0 193 suggestionItem = mSuggestionView.getChildAt(suggestionCounter + 1);
michael@0 194 suggestionItem.setVisibility(View.VISIBLE);
michael@0 195 } else {
michael@0 196 suggestionItem = mInflater.inflate(R.layout.suggestion_item, null);
michael@0 197
michael@0 198 suggestionItem.setOnClickListener(mClickListener);
michael@0 199 suggestionItem.setOnLongClickListener(mLongClickListener);
michael@0 200
michael@0 201 final ImageView magnifier =
michael@0 202 (ImageView) suggestionItem.findViewById(R.id.suggestion_magnifier);
michael@0 203 magnifier.setVisibility(View.GONE);
michael@0 204
michael@0 205 mSuggestionView.addView(suggestionItem);
michael@0 206 }
michael@0 207
michael@0 208 setSuggestionOnView(suggestionItem, suggestion);
michael@0 209
michael@0 210 if (animate) {
michael@0 211 AlphaAnimation anim = new AlphaAnimation(0, 1);
michael@0 212 anim.setDuration(ANIMATION_DURATION);
michael@0 213 anim.setStartOffset(suggestionCounter * ANIMATION_DURATION);
michael@0 214 suggestionItem.startAnimation(anim);
michael@0 215 }
michael@0 216
michael@0 217 ++suggestionCounter;
michael@0 218 }
michael@0 219
michael@0 220 // Hide extra suggestions that have been recycled.
michael@0 221 for (int i = suggestionCounter + 1; i < recycledSuggestionCount; ++i) {
michael@0 222 mSuggestionView.getChildAt(i).setVisibility(View.GONE);
michael@0 223 }
michael@0 224
michael@0 225 // Make sure mSelectedView is still valid.
michael@0 226 if (mSelectedView >= mSuggestionView.getChildCount()) {
michael@0 227 mSelectedView = mSuggestionView.getChildCount() - 1;
michael@0 228 }
michael@0 229 }
michael@0 230
michael@0 231 @Override
michael@0 232 public boolean onKeyDown(int keyCode, android.view.KeyEvent event) {
michael@0 233 final View suggestion = mSuggestionView.getChildAt(mSelectedView);
michael@0 234
michael@0 235 if (event.getAction() != android.view.KeyEvent.ACTION_DOWN) {
michael@0 236 return false;
michael@0 237 }
michael@0 238
michael@0 239 switch (event.getKeyCode()) {
michael@0 240 case KeyEvent.KEYCODE_DPAD_RIGHT:
michael@0 241 final View nextSuggestion = mSuggestionView.getChildAt(mSelectedView + 1);
michael@0 242 if (nextSuggestion != null) {
michael@0 243 changeSelectedSuggestion(suggestion, nextSuggestion);
michael@0 244 mSelectedView++;
michael@0 245 return true;
michael@0 246 }
michael@0 247 break;
michael@0 248
michael@0 249 case KeyEvent.KEYCODE_DPAD_LEFT:
michael@0 250 final View prevSuggestion = mSuggestionView.getChildAt(mSelectedView - 1);
michael@0 251 if (prevSuggestion != null) {
michael@0 252 changeSelectedSuggestion(suggestion, prevSuggestion);
michael@0 253 mSelectedView--;
michael@0 254 return true;
michael@0 255 }
michael@0 256 break;
michael@0 257
michael@0 258 case KeyEvent.KEYCODE_BUTTON_A:
michael@0 259 // TODO: handle long pressing for editing suggestions
michael@0 260 return suggestion.performClick();
michael@0 261 }
michael@0 262
michael@0 263 return false;
michael@0 264 }
michael@0 265
michael@0 266 private void changeSelectedSuggestion(View oldSuggestion, View newSuggestion) {
michael@0 267 oldSuggestion.setDuplicateParentStateEnabled(false);
michael@0 268 newSuggestion.setDuplicateParentStateEnabled(true);
michael@0 269 oldSuggestion.refreshDrawableState();
michael@0 270 newSuggestion.refreshDrawableState();
michael@0 271 }
michael@0 272
michael@0 273 public void onSelected() {
michael@0 274 mSelectedView = 0;
michael@0 275 mUserEnteredView.setDuplicateParentStateEnabled(true);
michael@0 276 mUserEnteredView.refreshDrawableState();
michael@0 277 }
michael@0 278
michael@0 279 public void onDeselected() {
michael@0 280 final View suggestion = mSuggestionView.getChildAt(mSelectedView);
michael@0 281 suggestion.setDuplicateParentStateEnabled(false);
michael@0 282 suggestion.refreshDrawableState();
michael@0 283 }
michael@0 284 }

mercurial