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

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkSearchView.java

Issue 2768663002: [Bookmarks] Refactor search view to use SelectableListLayout paradigm (Closed)
Patch Set: Fix spelling error Created 3 years, 9 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.bookmarks;
6
7 import android.content.Context;
8 import android.os.Parcelable;
9 import android.support.v7.widget.Toolbar;
10 import android.text.Editable;
11 import android.text.TextUtils;
12 import android.text.TextWatcher;
13 import android.util.AttributeSet;
14 import android.util.SparseArray;
15 import android.view.KeyEvent;
16 import android.view.LayoutInflater;
17 import android.view.View;
18 import android.view.ViewGroup;
19 import android.view.inputmethod.EditorInfo;
20 import android.widget.AdapterView;
21 import android.widget.AdapterView.OnItemClickListener;
22 import android.widget.ArrayAdapter;
23 import android.widget.BaseAdapter;
24 import android.widget.EditText;
25 import android.widget.LinearLayout;
26 import android.widget.ListView;
27 import android.widget.TextView;
28 import android.widget.TextView.OnEditorActionListener;
29 import android.widget.ViewSwitcher;
30
31 import org.json.JSONArray;
32 import org.json.JSONException;
33
34 import org.chromium.base.ApiCompatibilityUtils;
35 import org.chromium.base.ContextUtils;
36 import org.chromium.chrome.R;
37 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
38 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserve r;
39 import org.chromium.chrome.browser.bookmarks.BookmarkSearchRow.SearchHistoryDele gate;
40 import org.chromium.components.bookmarks.BookmarkId;
41 import org.chromium.ui.UiUtils;
42
43 import java.util.ArrayList;
44 import java.util.List;
45
46 /**
47 * Activity for searching bookmarks. Search results will be updated when user is typing. Before
48 * typing, a list of search history is shown.
49 */
50 public class BookmarkSearchView extends LinearLayout implements OnItemClickListe ner,
51 OnEditorActionListener, BookmarkUIObserver, SearchHistoryDelegate {
52 /**
53 * A custom {@link ViewSwitcher} that wraps another {@link ViewSwitcher} ins ide.
54 */
55 public static class HistoryResultSwitcher extends ViewSwitcher {
56 ViewSwitcher mResultEmptySwitcher;
57
58 /**
59 * Constructor for xml inflation.
60 */
61 public HistoryResultSwitcher(Context context, AttributeSet attrs) {
62 super(context, attrs);
63 }
64
65 @Override
66 protected void onFinishInflate() {
67 super.onFinishInflate();
68 mResultEmptySwitcher = (ViewSwitcher) findViewById(R.id.result_empty _switcher);
69 }
70
71 void showHistory() {
72 if (getCurrentView().getId() == R.id.bookmark_history_list) return;
73 showNext();
74 }
75
76 void showResult() {
77 if (getCurrentView().getId() == R.id.bookmark_history_list) showNext ();
78 if (mResultEmptySwitcher.getCurrentView().getId() == R.id.bookmark_s earch_empty_view) {
79 mResultEmptySwitcher.showNext();
80 }
81 }
82
83 void showEmpty() {
84 if (getCurrentView().getId() == R.id.bookmark_history_list) showNext ();
85 if (mResultEmptySwitcher.getCurrentView().getId() == R.id.bookmark_r esult_list) {
86 mResultEmptySwitcher.showNext();
87 }
88 }
89 }
90
91 private static enum UIState {HISTORY, RESULT, EMPTY}
92
93 private static final String PREF_SEARCH_HISTORY = "bookmark_search_history";
94 private static final int SEARCH_HISTORY_MAX_ENTRIES = 10;
95 private static final int HISTORY_ITEM_PADDING_START_DP = 72;
96 private static final int MAXIMUM_NUMBER_OF_RESULTS = 500;
97
98 private BookmarkModel mBookmarkModel;
99 private BookmarkDelegate mDelegate;
100 private EditText mSearchText;
101 private ListView mResultList;
102 private ListView mHistoryList;
103 private HistoryResultSwitcher mHistoryResultSwitcher;
104 private UIState mCurrentUIState;
105
106 private BookmarkModelObserver mModelObserver = new BookmarkModelObserver() {
107 @Override
108 public void bookmarkModelChanged() {
109 if (mCurrentUIState == UIState.RESULT || mCurrentUIState == UIState. EMPTY) {
110 sendSearchQuery();
111 }
112 }
113
114 @Override
115 public void bookmarkNodeRemoved(BookmarkItem parent, int oldIndex, Bookm arkItem node,
116 boolean isDoingExtensiveChanges) {
117 // If isDoingExtensiveChanges is false, it will fall back to bookmar kModelChange()
118 if (isDoingExtensiveChanges && mCurrentUIState == UIState.RESULT) {
119 sendSearchQuery();
120 }
121 }
122 };
123
124 /**
125 * Constructor for inflating from XML.
126 */
127 public BookmarkSearchView(Context context, AttributeSet attrs) {
128 super(context, attrs);
129 }
130
131 @Override
132 protected void onFinishInflate() {
133 super.onFinishInflate();
134 mSearchText = (EditText) findViewById(R.id.bookmark_search_text);
135 mResultList = (ListView) findViewById(R.id.bookmark_result_list);
136 mHistoryList = (ListView) findViewById(R.id.bookmark_history_list);
137 mHistoryResultSwitcher = (HistoryResultSwitcher) findViewById(R.id.histo ry_result_switcher);
138
139 Toolbar searchBar = (Toolbar) findViewById(R.id.search_bar);
140 searchBar.setNavigationIcon(R.drawable.back_normal);
141 searchBar.setNavigationContentDescription(R.string.accessibility_toolbar _btn_back);
142 searchBar.setNavigationOnClickListener(new OnClickListener() {
143 @Override
144 public void onClick(View v) {
145 onBackPressed();
146 }
147 });
148
149 mHistoryList.setOnItemClickListener(this);
150 mSearchText.setOnEditorActionListener(this);
151 mSearchText.addTextChangedListener(new TextWatcher() {
152 @Override
153 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
154 }
155
156 @Override
157 public void onTextChanged(CharSequence s, int start, int before, int count) {
158 }
159
160 @Override
161 public void afterTextChanged(Editable s) {
162 if (TextUtils.isEmpty(s.toString().trim())) {
163 resetUI();
164 } else {
165 sendSearchQuery();
166 }
167 }
168 });
169 mCurrentUIState = UIState.HISTORY;
170 }
171
172 private void updateHistoryList() {
173 mHistoryList.setAdapter(new ArrayAdapter<String>(getContext(),
174 android.R.layout.simple_list_item_1, android.R.id.text1,
175 readHistoryList()) {
176 @Override
177 public View getView(int position, View convertView, ViewGroup parent ) {
178 View textView = super.getView(position, convertView, parent);
179 // Set padding start to specific size.
180 int paddingStart = (int) (HISTORY_ITEM_PADDING_START_DP
181 * getResources().getDisplayMetrics().density);
182 ApiCompatibilityUtils.setPaddingRelative(textView, paddingStart,
183 textView.getPaddingTop(), textView.getPaddingRight(),
184 textView.getPaddingBottom());
185 return textView;
186 }
187 });
188 }
189
190 private void resetUI() {
191 setUIState(UIState.HISTORY);
192 mResultList.setAdapter(null);
193 if (!TextUtils.isEmpty(mSearchText.getText())) mSearchText.setText("");
194 }
195
196 private void sendSearchQuery() {
197 String currentText = mSearchText.getText().toString().trim();
198 if (TextUtils.isEmpty(currentText)) return;
199
200 List<BookmarkMatch> results = mBookmarkModel.searchBookmarks(currentText ,
201 MAXIMUM_NUMBER_OF_RESULTS);
202 populateResultListView(results);
203 }
204
205 @Override
206 public boolean dispatchKeyEvent(KeyEvent event) {
207 // To intercept hardware key, a view must have focus.
208 if (mDelegate == null) return super.dispatchKeyEvent(event);
209
210 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
211 KeyEvent.DispatcherState state = getKeyDispatcherState();
212 if (state != null) {
213 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeat Count() == 0) {
214 state.startTracking(event, this);
215 return true;
216 } else if (event.getAction() == KeyEvent.ACTION_UP && !event.isC anceled()
217 && state.isTracking(event)) {
218 onBackPressed();
219 return true;
220 }
221 }
222 }
223
224 return super.dispatchKeyEvent(event);
225 }
226
227 @Override
228 protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
229 // No-op because state saving/restoring is intentionally omitted in this View. This is
230 // to fix a crash in Android M that TextView's old text is sometimes res tored even if
231 // setText("") is called in onVisibilityChange(). crbug.com/596783
232 }
233
234 /**
235 * Make result list visible and popuplate the list with given list of bookma rks.
236 */
237 private void populateResultListView(List<BookmarkMatch> ids) {
238 if (ids.isEmpty()) {
239 setUIState(UIState.EMPTY);
240 } else {
241 setUIState(UIState.RESULT);
242 mResultList.setAdapter(new ResultListAdapter(ids, mDelegate));
243 }
244 }
245
246 private void setUIState(UIState state) {
247 if (mCurrentUIState == state) return;
248 mCurrentUIState = state;
249 if (state == UIState.HISTORY) {
250 mHistoryResultSwitcher.showHistory();
251 updateHistoryList();
252 } else if (state == UIState.RESULT) {
253 mHistoryResultSwitcher.showResult();
254 } else if (state == UIState.EMPTY) {
255 mHistoryResultSwitcher.showEmpty();
256 }
257 }
258
259 @Override
260 protected void onVisibilityChanged(View changedView, int visibility) {
261 super.onVisibilityChanged(changedView, visibility);
262 // This method might be called very early. Null check on bookmark model here.
263 if (mBookmarkModel == null) return;
264
265 if (visibility == View.VISIBLE) {
266 mBookmarkModel.addObserver(mModelObserver);
267 updateHistoryList();
268 mSearchText.requestFocus();
269 UiUtils.showKeyboard(mSearchText);
270 } else {
271 UiUtils.hideKeyboard(mSearchText);
272 mBookmarkModel.removeObserver(mModelObserver);
273 resetUI();
274 clearFocus();
275 }
276 }
277
278 private void onBackPressed() {
279 if (mCurrentUIState == UIState.HISTORY) {
280 mDelegate.closeSearchUI();
281 } else {
282 resetUI();
283 }
284 }
285
286 @Override
287 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
288 assert parent == mHistoryList : "Only history list should have onItemCli ckListener.";
289 mSearchText.setText((String) parent.getAdapter().getItem(position));
290 }
291
292 @Override
293 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
294 if (actionId == EditorInfo.IME_ACTION_SEARCH) {
295 UiUtils.hideKeyboard(v);
296
297 // History is saved either when the user clicks search button or a s earch result is
298 // clicked.
299 saveSearchHistory();
300 }
301 return false;
302 }
303
304 private void saveHistoryList(List<String> history) {
305 JSONArray jsonArray = new JSONArray(history);
306 ContextUtils.getAppSharedPreferences().edit()
307 .putString(PREF_SEARCH_HISTORY, jsonArray.toString()).apply();
308 }
309
310 private List<String> readHistoryList() {
311 try {
312 String unformatted = ContextUtils.getAppSharedPreferences()
313 .getString(PREF_SEARCH_HISTORY, "[]");
314 JSONArray jsonArray = new JSONArray(unformatted);
315 ArrayList<String> result = new ArrayList<String>();
316 for (int i = 0; i < jsonArray.length(); i++) {
317 result.add(jsonArray.getString(i));
318 }
319 return result;
320 } catch (JSONException e) {
321 return new ArrayList<String>();
322 }
323 }
324
325 /**
326 * Adds the current search text as top entry of the list.
327 */
328 private List<String> addCurrentTextToHistoryList(List<String> history) {
329 String text = mSearchText.getText().toString().trim();
330 if (TextUtils.isEmpty(text)) return history;
331
332 history.remove(text);
333 history.add(0, text);
334 if (history.size() > SEARCH_HISTORY_MAX_ENTRIES) {
335 history.remove(history.size() - 1);
336 }
337 return history;
338 }
339
340 // SearchHistoryDelegate implementation
341
342 @Override
343 public void saveSearchHistory() {
344 saveHistoryList((addCurrentTextToHistoryList(readHistoryList())));
345 }
346
347 // BookmarkUIObserver implementation
348
349 @Override
350 public void onBookmarkDelegateInitialized(BookmarkDelegate delegate) {
351 mDelegate = delegate;
352 mDelegate.addUIObserver(this);
353 mBookmarkModel = mDelegate.getModel();
354 }
355
356 @Override
357 public void onDestroy() {
358 mBookmarkModel.removeObserver(mModelObserver);
359 mDelegate.removeUIObserver(this);
360 }
361
362 @Override
363 public void onFolderStateSet(BookmarkId folder) {
364 }
365
366 @Override
367 public void onSelectionStateChange(List<BookmarkId> selectedBookmarks) {
368 }
369
370 private class ResultListAdapter extends BaseAdapter {
371 private BookmarkDelegate mDelegate;
372 private List<BookmarkMatch> mBookmarktList;
373
374 public ResultListAdapter(List<BookmarkMatch> bookmarkMatches,
375 BookmarkDelegate delegate) {
376 mDelegate = delegate;
377 mBookmarktList = bookmarkMatches;
378 }
379
380 @Override
381 public int getCount() {
382 return mBookmarktList.size();
383 }
384
385 @Override
386 public BookmarkMatch getItem(int position) {
387 return mBookmarktList.get(position);
388 }
389
390 @Override
391 public long getItemId(int position) {
392 return position;
393 }
394
395 @Override
396 public View getView(int position, View convertView, ViewGroup parent) {
397 final BookmarkMatch bookmarkMatch = getItem(position);
398 if (convertView == null) {
399 convertView = LayoutInflater.from(parent.getContext()).inflate(
400 R.layout.bookmark_search_row, parent, false);
401 }
402 final BookmarkSearchRow row = (BookmarkSearchRow) convertView;
403 row.onBookmarkDelegateInitialized(mDelegate);
404 row.setBookmarkId(bookmarkMatch.getBookmarkId());
405 row.setSearchHistoryDelegate(BookmarkSearchView.this);
406 return convertView;
407 }
408 }
409 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698