Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(169)

Side by Side Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/enhancedbookmarks/EnhancedBookmarkSearchView.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.enhancedbookmarks;
6
7 import android.content.Context;
8 import android.graphics.Bitmap;
9 import android.graphics.Typeface;
10 import android.graphics.drawable.Drawable;
11 import android.text.Editable;
12 import android.text.Layout;
13 import android.text.SpannableString;
14 import android.text.SpannableStringBuilder;
15 import android.text.TextUtils;
16 import android.text.TextUtils.TruncateAt;
17 import android.text.TextWatcher;
18 import android.text.style.StyleSpan;
19 import android.util.AttributeSet;
20 import android.util.Pair;
21 import android.view.KeyEvent;
22 import android.view.LayoutInflater;
23 import android.view.View;
24 import android.view.ViewGroup;
25 import android.view.inputmethod.InputMethodManager;
26 import android.widget.AdapterView;
27 import android.widget.AdapterView.OnItemClickListener;
28 import android.widget.ArrayAdapter;
29 import android.widget.BaseAdapter;
30 import android.widget.EditText;
31 import android.widget.ImageButton;
32 import android.widget.LinearLayout;
33 import android.widget.ListView;
34 import android.widget.TextView;
35 import android.widget.ViewSwitcher;
36
37 import com.google.android.apps.chrome.R;
38
39 import org.chromium.base.ApiCompatibilityUtils;
40 import org.chromium.chrome.browser.BookmarksBridge.BookmarkItem;
41 import org.chromium.chrome.browser.BookmarksBridge.BookmarkModelObserver;
42 import org.chromium.chrome.browser.enhanced_bookmarks.EnhancedBookmarksBridge.Fi ltersObserver;
43 import org.chromium.chrome.browser.enhanced_bookmarks.EnhancedBookmarksModel;
44 import org.chromium.chrome.browser.enhanced_bookmarks.LaunchLocation;
45 import org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarkSalientImag eView.SalientImageDrawableFactory;
46 import org.chromium.chrome.browser.widget.CustomShapeDrawable.CircularDrawable;
47 import org.chromium.components.bookmarks.BookmarkId;
48 import org.chromium.components.bookmarks.BookmarkMatch;
49
50 import java.util.ArrayList;
51 import java.util.List;
52
53 /**
54 * UI implementation for search in enhanced bookmark. Search results will be upd ated when user is
55 * typing. Before typing, a list of search history is shown.
56 */
57 public class EnhancedBookmarkSearchView extends LinearLayout implements View.OnC lickListener,
58 OnItemClickListener, FiltersObserver, EnhancedBookmarkUIObserver {
59
60 private static enum UIState {HISTORY, RESULT, EMPTY}
61 private static final int HISTORY_ITEM_PADDING_START_DP = 72;
62 private static final int MAXIMUM_NUMBER_OF_RESULTS = 500;
63 private static final String HORIZONTAL_ELLIPSIS = "\u2026";
64 private EnhancedBookmarksModel mEnhancedBookmarksModel;
65 private EnhancedBookmarkDelegate mDelegate;
66 private ImageButton mBackButton;
67 private EditText mSearchText;
68 private ListView mResultList;
69 private ListView mHistoryList;
70 private HistoryResultSwitcher mHistoryResultSwitcher;
71 private UIState mCurrentUIState;
72
73 private BookmarkModelObserver mModelObserver = new BookmarkModelObserver() {
74 @Override
75 public void bookmarkModelChanged() {
76 if (mCurrentUIState == UIState.RESULT || mCurrentUIState == UIState. EMPTY) {
77 sendLocalSearchQuery();
78 }
79 }
80
81 @Override
82 public void bookmarkNodeRemoved(BookmarkItem parent, int oldIndex, Bookm arkItem node,
83 boolean isDoingExtensiveChanges) {
84 // If isDoingExtensiveChanges is false, it will fall back to bookmar kModelChange()
85 if (isDoingExtensiveChanges && mCurrentUIState == UIState.RESULT) {
86 sendLocalSearchQuery();
87 }
88 }
89 };
90
91 /**
92 * Constructor for inflating from XML.
93 */
94 public EnhancedBookmarkSearchView(Context context, AttributeSet attrs) {
95 super(context, attrs);
96 }
97
98 @Override
99 protected void onFinishInflate() {
100 super.onFinishInflate();
101 mBackButton = (ImageButton) findViewById(R.id.eb_search_back);
102 mSearchText = (EditText) findViewById(R.id.eb_search_text);
103 mResultList = (ListView) findViewById(R.id.eb_result_list);
104 mHistoryList = (ListView) findViewById(R.id.eb_history_list);
105 mHistoryResultSwitcher = (HistoryResultSwitcher) findViewById(R.id.histo ry_result_switcher);
106
107 mHistoryList.setOnItemClickListener(this);
108 mResultList.setOnItemClickListener(this);
109 mBackButton.setOnClickListener(this);
110 mSearchText.addTextChangedListener(new TextWatcher() {
111 @Override
112 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
113 }
114
115 @Override
116 public void onTextChanged(CharSequence s, int start, int before, int count) {
117 }
118
119 @Override
120 public void afterTextChanged(Editable s) {
121 if (TextUtils.isEmpty(s.toString().trim())) {
122 resetUI();
123 } else {
124 sendLocalSearchQuery();
125 }
126 }
127 });
128 mCurrentUIState = UIState.HISTORY;
129 }
130
131 @Override
132 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
133 if (parent == mHistoryList && view instanceof TextView) {
134 List<BookmarkId> bookmarkIds = mEnhancedBookmarksModel
135 .getBookmarksForFilter((String) parent.getAdapter().getItem( position));
136 List<BookmarkMatch> bookmarkMatches = new ArrayList<BookmarkMatch>() ;
137 for (BookmarkId bookmarkId : bookmarkIds) {
138 bookmarkMatches.add(new BookmarkMatch(bookmarkId, null, null));
139 }
140 populateResultListView(bookmarkMatches);
141 } else if (parent == mResultList) {
142 mDelegate.openBookmark(
143 ((BookmarkMatch) parent.getAdapter().getItem(position)).getB ookmarkId(),
144 LaunchLocation.SEARCH);
145 mDelegate.closeSearchUI();
146 }
147 }
148
149 private void resetUI() {
150 setUIState(UIState.HISTORY);
151 mResultList.setAdapter(null);
152 if (!mSearchText.getText().toString().isEmpty()) mSearchText.setText("") ;
153 }
154
155 private void sendLocalSearchQuery() {
156 String currentText = mSearchText.getText().toString().trim();
157 if (TextUtils.isEmpty(currentText)) return;
158
159 List<BookmarkMatch> results = mEnhancedBookmarksModel.getLocalSearchForQ uery(currentText,
160 MAXIMUM_NUMBER_OF_RESULTS);
161 if (results != null) populateResultListView(results);
162 }
163
164 /**
165 * Make result list visible and popuplate the list with given list of bookma rks.
166 */
167 private void populateResultListView(List<BookmarkMatch> ids) {
168 if (ids.isEmpty()) {
169 setUIState(UIState.EMPTY);
170 } else {
171 setUIState(UIState.RESULT);
172 mResultList.setAdapter(new ResultListAdapter(ids, mEnhancedBookmarks Model));
173 }
174 }
175
176 @Override
177 protected void onVisibilityChanged(View changedView, int visibility) {
178 super.onVisibilityChanged(changedView, visibility);
179 // This method might be called very early. Null check on bookmark model here.
180 if (mEnhancedBookmarksModel == null) return;
181 InputMethodManager imm = (InputMethodManager) getContext().getSystemServ ice(
182 Context.INPUT_METHOD_SERVICE);
183 if (visibility != View.VISIBLE) {
184 imm.hideSoftInputFromWindow(mSearchText.getWindowToken(), 0);
185 mEnhancedBookmarksModel.removeFiltersObserver(this);
186 mEnhancedBookmarksModel.removeModelObserver(mModelObserver);
187 resetUI();
188 clearFocus();
189 } else {
190 mEnhancedBookmarksModel.addModelObserver(mModelObserver);
191 mEnhancedBookmarksModel.addFiltersObserver(this);
192 onFiltersChanged();
193 mSearchText.requestFocus();
194 imm.showSoftInput(mSearchText, 0);
195 }
196 }
197
198 private void setUIState(UIState state) {
199 if (mCurrentUIState == state) return;
200 mCurrentUIState = state;
201 if (state == UIState.HISTORY) {
202 mHistoryResultSwitcher.showHistory();
203 } else if (state == UIState.RESULT) {
204 mHistoryResultSwitcher.showResult();
205 } else if (state == UIState.EMPTY) {
206 mHistoryResultSwitcher.showEmpty();
207 }
208 }
209
210 @Override
211 public boolean dispatchKeyEvent(KeyEvent event) {
212 // To intercept hardware key, a view must have focus.
213 if (mDelegate == null) return super.dispatchKeyEvent(event);
214
215 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
216 KeyEvent.DispatcherState state = getKeyDispatcherState();
217 if (state != null) {
218 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeat Count() == 0) {
219 state.startTracking(event, this);
220 return true;
221 } else if (event.getAction() == KeyEvent.ACTION_UP && !event.isC anceled()
222 && state.isTracking(event)) {
223 onClick(mBackButton);
224 return true;
225 }
226 }
227 }
228
229 return super.dispatchKeyEvent(event);
230 }
231
232 /**
233 * Click listener for back button.
234 */
235 @Override
236 public void onClick(View v) {
237 assert v == mBackButton;
238 if (mCurrentUIState == UIState.HISTORY) {
239 mDelegate.closeSearchUI();
240 } else {
241 resetUI();
242 }
243 }
244
245 @Override
246 public void onFiltersChanged() {
247 // We use android default list and textviews for history. Only start pad ding is modified.
248 mHistoryList.setAdapter(new ArrayAdapter<String>(getContext(),
249 android.R.layout.simple_list_item_1, android.R.id.text1,
250 mEnhancedBookmarksModel.getFilters()) {
251 @Override
252 public View getView(int position, View convertView, ViewGroup parent ) {
253 View textView = super.getView(position, convertView, parent);
254 // Set padding start to specific size.
255 int paddingStart = (int) (HISTORY_ITEM_PADDING_START_DP
256 * getResources().getDisplayMetrics().density);
257 ApiCompatibilityUtils.setPaddingRelative(textView, paddingStart,
258 textView.getPaddingTop(), textView.getPaddingRight(),
259 textView.getPaddingBottom());
260 return textView;
261 }
262 });
263 }
264
265 // EnhancedBookmarkUIObserver implementation
266
267 @Override
268 public void onEnhancedBookmarkDelegateInitialized(EnhancedBookmarkDelegate d elegate) {
269 mDelegate = delegate;
270 mDelegate.addUIObserver(this);
271 mEnhancedBookmarksModel = mDelegate.getModel();
272 }
273
274 @Override
275 public void onDestroy() {
276 mEnhancedBookmarksModel.removeFiltersObserver(this);
277 mEnhancedBookmarksModel.removeModelObserver(mModelObserver);
278 mDelegate.removeUIObserver(this);
279 }
280
281 @Override
282 public void onAllBookmarksStateSet() {
283 }
284
285 @Override
286 public void onFolderStateSet(BookmarkId folder) {
287 }
288
289 @Override
290 public void onFilterStateSet(String filter) {
291 }
292
293 @Override
294 public void onSelectionStateChange(List<BookmarkId> selectedBookmarks) {
295 }
296
297 @Override
298 public void onListModeChange(boolean isListModeEnabled) {}
299
300 /**
301 * List Adapter that organizes search results.
302 */
303 private static class ResultListAdapter extends BaseAdapter implements
304 SalientImageDrawableFactory {
305
306 private EnhancedBookmarksModel mBookmarksModel;
307 private List<BookmarkMatch> mResultList;
308
309 public ResultListAdapter(List<BookmarkMatch> bookmarkMatches,
310 EnhancedBookmarksModel enhancedBookmarksModel) {
311 mBookmarksModel = enhancedBookmarksModel;
312 mResultList = bookmarkMatches;
313 }
314
315 @Override
316 public View getView(int position, View convertView, ViewGroup parent) {
317 final BookmarkMatch bookmarkMatch = getItem(position);
318 BookmarkItem bookmarkItem = mBookmarksModel.getBookmarkById(
319 bookmarkMatch.getBookmarkId());
320 if (convertView == null) {
321 convertView = LayoutInflater.from(parent.getContext()).inflate(
322 R.layout.eb_search_result_item, parent, false);
323 }
324 final TextView titleTextView = (TextView) convertView.findViewById(R .id.title);
325 final TextView urlTextView = (TextView) convertView.findViewById(R.i d.url);
326 EnhancedBookmarkSalientImageView imageView = (EnhancedBookmarkSalien tImageView)
327 convertView.findViewById(R.id.round_image);
328 imageView.load(mBookmarksModel, bookmarkItem.getUrl(),
329 EnhancedBookmarkUtils.generateBackgroundColor(bookmarkItem), this);
330
331 final SpannableString title = boldMatchPositions(bookmarkItem.getTit le(),
332 bookmarkMatch.getTitleMatchPositions());
333 final SpannableString url = boldMatchPositions(bookmarkItem.getUrl() ,
334 bookmarkMatch.getUrlMatchPositions());
335
336 titleTextView.setText(title);
337 urlTextView.setText(url);
338
339 // Ensure the first matched search term is visible on the screen by calling
340 // moveCharactersOnScreen after the titleTextView.onLayout() has bee n executed
341 if (bookmarkMatch.getTitleMatchPositions() != null
342 && bookmarkMatch.getTitleMatchPositions().size() > 0) {
343 titleTextView.addOnLayoutChangeListener(new OnLayoutChangeListen er(){
344 @Override
345 public void onLayoutChange(View v, int left, int top, int ri ght, int bottom,
346 int oldLeft, int oldTop, int oldRight, int oldBottom ) {
347 moveCharactersOnScreen(titleTextView, title,
348 bookmarkMatch.getTitleMatchPositions().get(0).fi rst,
349 bookmarkMatch.getTitleMatchPositions().get(0).se cond);
350 }
351 });
352 }
353
354 // Ensure the first matched search term is visible on the screen by calling
355 // moveCharactersOnScreen after the urlTextView.onLayout() has been executed
356 if (bookmarkMatch.getUrlMatchPositions() != null
357 && bookmarkMatch.getUrlMatchPositions().size() > 0) {
358 urlTextView.addOnLayoutChangeListener(new OnLayoutChangeListener (){
359 @Override
360 public void onLayoutChange(View v, int left, int top, int ri ght, int bottom,
361 int oldLeft, int oldTop, int oldRight, int oldBottom ) {
362 moveCharactersOnScreen(urlTextView, url,
363 bookmarkMatch.getUrlMatchPositions().get(0).firs t,
364 bookmarkMatch.getUrlMatchPositions().get(0).seco nd);
365 }
366 });
367 }
368
369 return convertView;
370 }
371
372 private SpannableString boldMatchPositions(String input,
373 List<Pair<Integer, Integer>> matches) {
374 SpannableString output = new SpannableString(input);
375 if (matches != null) {
376 for (Pair<Integer, Integer> match : matches) {
377 StyleSpan boldSpan = new StyleSpan(Typeface.BOLD);
378 output.setSpan(boldSpan, match.first, match.second, 0);
379 }
380 }
381
382 return output;
383 }
384
385 /**
386 * Ensures the characters from [charStartPos, charEndPos) are on screen.
387 *
388 * @param textView The textView to manipulate
389 * @param viewText A SpannableString containing the textView's text
390 * @param charStartPos The starting position for characters that should be on screen
391 * @param charEndPos The ending position for characters that should be o n screen
392 */
393 private void moveCharactersOnScreen(TextView textView, SpannableString v iewText,
394 int charStartPos, int charEndPos) {
395 if (textView.getLayout() == null) return;
396
397 if (!isCharacterOnScreen(textView.getLayout(), charEndPos - 1)) {
398 int subStringLength = textView.getLayout().getEllipsisStart(0);
399 int termLength = charEndPos - charStartPos;
400 int subStringStartPos = charStartPos - ((subStringLength - termL ength) / 2);
401
402 if (subStringStartPos + subStringLength > viewText.length()) {
403 // Characters are near the end of viewText, ellipsize the st art
404 textView.setEllipsize(TruncateAt.START);
405 } else {
406 // Characters are somwhere in the middle of the viewText, ch ange the textView's
407 // text so the characters are on screen; both sides of the t ext will be
408 // ellipsized
409 SpannableStringBuilder newText = new SpannableStringBuilder( viewText,
410 subStringStartPos, viewText.length());
411 newText.insert(0, HORIZONTAL_ELLIPSIS);
412 textView.setText(newText);
413 }
414 }
415 }
416
417 private boolean isCharacterOnScreen(Layout textViewLayout, int charPosit ion) {
418 int ellipsisPosition = textViewLayout.getEllipsisStart(0);
419 return ellipsisPosition == 0 || charPosition < ellipsisPosition;
420 }
421
422 @Override
423 public int getCount() {
424 return mResultList.size();
425 }
426
427 @Override
428 public BookmarkMatch getItem(int position) {
429 return mResultList.get(position);
430 }
431
432 @Override
433 public long getItemId(int position) {
434 return position;
435 }
436
437 // SalientImageDrawableFactory implementation
438
439 @Override
440 public Drawable getSalientDrawable(int color) {
441 return new CircularDrawable(color);
442 }
443
444 @Override
445 public Drawable getSalientDrawable(Bitmap bitmap) {
446 return new CircularDrawable(bitmap);
447 }
448 }
449
450 /**
451 * A custom {@link ViewSwitcher} that makes specific assumptions about the c orresponding xml. It
452 * assumes it has two children: a {@link ListView} and a {@link ViewSwitcher }, and in the other
453 * {@link ViewSwitcher}, it contains a {@link TextView} and a {@link ListVie w}.
454 */
455 public static class HistoryResultSwitcher extends ViewSwitcher {
456 ViewSwitcher mResultEmptySwitcher;
457
458 /**
459 * Constructor for xml inflation.
460 */
461 public HistoryResultSwitcher(Context context, AttributeSet attrs) {
462 super(context, attrs);
463 }
464
465 @Override
466 protected void onFinishInflate() {
467 super.onFinishInflate();
468 mResultEmptySwitcher = (ViewSwitcher) findViewById(R.id.result_empty _switcher);
469 }
470
471 void showHistory() {
472 if (getCurrentView().getId() == R.id.eb_history_list) return;
473 showNext();
474 }
475
476 void showResult() {
477 // If currently showing history, toggle.
478 if (getCurrentView().getId() == R.id.eb_history_list) showNext();
479 // If currently showing empty view, toggle.
480 if (mResultEmptySwitcher.getCurrentView().getId() == R.id.eb_search_ empty_view) {
481 mResultEmptySwitcher.showNext();
482 }
483 }
484
485 void showEmpty() {
486 // If currently showing history, toggle.
487 if (getCurrentView().getId() == R.id.eb_history_list) showNext();
488 // If currently showing result list, toggle.
489 if (mResultEmptySwitcher.getCurrentView().getId() == R.id.eb_result_ list) {
490 mResultEmptySwitcher.showNext();
491 }
492 }
493 }
494
495
496 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698