OLD | NEW |
(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.annotation.TargetApi; |
| 8 import android.app.Activity; |
| 9 import android.app.ActivityOptions; |
| 10 import android.content.Intent; |
| 11 import android.os.Build; |
| 12 import android.preference.PreferenceManager; |
| 13 import android.support.v4.widget.DrawerLayout; |
| 14 import android.util.Log; |
| 15 import android.view.Gravity; |
| 16 import android.view.View; |
| 17 import android.view.ViewGroup; |
| 18 import android.widget.ViewSwitcher; |
| 19 |
| 20 import com.google.android.apps.chrome.R; |
| 21 |
| 22 import org.chromium.base.ObserverList; |
| 23 import org.chromium.base.metrics.RecordHistogram; |
| 24 import org.chromium.chrome.browser.BookmarksBridge.BookmarkItem; |
| 25 import org.chromium.chrome.browser.BookmarksBridge.BookmarkModelObserver; |
| 26 import org.chromium.chrome.browser.UrlConstants; |
| 27 import org.chromium.chrome.browser.enhanced_bookmarks.EnhancedBookmarksBridge.Fi
ltersObserver; |
| 28 import org.chromium.chrome.browser.enhanced_bookmarks.EnhancedBookmarksModel; |
| 29 import org.chromium.chrome.browser.enhanced_bookmarks.LaunchLocation; |
| 30 import org.chromium.chrome.browser.ntp.NewTabPageUma; |
| 31 import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmarksShim; |
| 32 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarManageable; |
| 33 import org.chromium.components.bookmarks.BookmarkId; |
| 34 import org.chromium.ui.base.DeviceFormFactor; |
| 35 |
| 36 import java.io.UnsupportedEncodingException; |
| 37 import java.net.URLDecoder; |
| 38 import java.net.URLEncoder; |
| 39 import java.util.ArrayList; |
| 40 import java.util.HashSet; |
| 41 import java.util.List; |
| 42 import java.util.Set; |
| 43 import java.util.Stack; |
| 44 |
| 45 /** |
| 46 * The new bookmark manager that is planned to replace the existing bookmark man
ager. It holds all |
| 47 * views and shared logics between tablet and phone. For tablet/phone specific l
ogics, see |
| 48 * {@link EnhancedBookmarkActivity} (phone) and {@link EnhancedBookmarkPage} (ta
blet). |
| 49 */ |
| 50 public class EnhancedBookmarkManager implements EnhancedBookmarkDelegate { |
| 51 private static final String PREF_LAST_USED_URL = "enhanced_bookmark_last_use
d_url"; |
| 52 static final String PREF_WAS_IN_LIST_MODE = "enhanced_bookmark_list_mode_cho
ice"; |
| 53 // TODO(ianwen): upstream these metrics upstream. |
| 54 // UI modes for bookmarks presentation. Default option is grid mode. |
| 55 static final int DEFAULT_MODE = 0; |
| 56 static final int LIST_MODE = 1; |
| 57 static final int GRID_MODE = 2; |
| 58 |
| 59 private Activity mActivity; |
| 60 private ViewGroup mMainView; |
| 61 private EnhancedBookmarksModel mEnhancedBookmarksModel; |
| 62 private EnhancedBookmarkUndoController mUndoController; |
| 63 private final ObserverList<EnhancedBookmarkUIObserver> mUIObservers = |
| 64 new ObserverList<EnhancedBookmarkUIObserver>(); |
| 65 private Set<BookmarkId> mSelectedBookmarks = new HashSet<>(); |
| 66 private boolean mListModeEnabled; |
| 67 private EnhancedBookmarkStateChangeListener mUrlChangeListener; |
| 68 private EnhancedBookmarkContentView mContentView; |
| 69 private EnhancedBookmarkSearchView mSearchView; |
| 70 private ViewSwitcher mViewSwitcher; |
| 71 private DrawerLayout mDrawer; |
| 72 private EnhancedBookmarkDrawerListView mDrawerListView; |
| 73 private final Stack<UIState> mStateStack = new Stack<>(); |
| 74 |
| 75 private final BookmarkModelObserver mBookmarkModelObserver = new BookmarkMod
elObserver() { |
| 76 @Override |
| 77 public void bookmarkNodeRemoved(BookmarkItem parent, int oldIndex, Bookm
arkItem node) { |
| 78 // If the folder is removed in folder mode, show the parent folder o
r falls back to all |
| 79 // bookmarks mode. |
| 80 if (getCurrentState() == STATE_FOLDER |
| 81 && node.getId().equals(mStateStack.peek().mFolder)) { |
| 82 if (mEnhancedBookmarksModel.getTopLevelFolderIDs(true, true).con
tains( |
| 83 node.getId())) { |
| 84 openAllBookmarks(); |
| 85 } else { |
| 86 openFolder(parent.getId()); |
| 87 } |
| 88 } |
| 89 clearSelection(); |
| 90 } |
| 91 |
| 92 @Override |
| 93 public void bookmarkNodeMoved(BookmarkItem oldParent, int oldIndex, Book
markItem newParent, |
| 94 int newIndex) { |
| 95 clearSelection(); |
| 96 } |
| 97 |
| 98 @Override |
| 99 public void bookmarkModelLoaded() { |
| 100 initializeIfBookmarkModelLoaded(); |
| 101 } |
| 102 |
| 103 @Override |
| 104 public void bookmarkModelChanged() { |
| 105 // If the folder no longer exists in folder mode, we need to fall ba
ck. Relying on the |
| 106 // default behavior by setting the folder mode again. |
| 107 if (getCurrentState() == STATE_FOLDER) { |
| 108 setState(mStateStack.peek()); |
| 109 } |
| 110 clearSelection(); |
| 111 } |
| 112 }; |
| 113 |
| 114 private final FiltersObserver mFiltersObserver = new FiltersObserver() { |
| 115 @Override |
| 116 public void onFiltersChanged() { |
| 117 // if the current selected filter was removed, we need to fall back.
Relying on the |
| 118 // default behavior by setting the filter mode again. |
| 119 if (getCurrentState() == STATE_FILTER) { |
| 120 setState(mStateStack.peek()); |
| 121 } |
| 122 } |
| 123 }; |
| 124 |
| 125 /** |
| 126 * Creates an instance of {@link EnhancedBookmarkManager}. It also initializ
es resources, |
| 127 * bookmark models and jni bridges. |
| 128 * @param activity The activity context to use. |
| 129 */ |
| 130 public EnhancedBookmarkManager(Activity activity) { |
| 131 mActivity = activity; |
| 132 mEnhancedBookmarksModel = new EnhancedBookmarksModel(); |
| 133 mMainView = (ViewGroup) mActivity.getLayoutInflater().inflate(R.layout.e
b_main, null); |
| 134 mDrawer = (DrawerLayout) mMainView.findViewById(R.id.eb_drawer_layout); |
| 135 mDrawerListView = (EnhancedBookmarkDrawerListView) mMainView.findViewByI
d( |
| 136 R.id.eb_drawer_list); |
| 137 mContentView = (EnhancedBookmarkContentView) mMainView.findViewById(R.id
.eb_content_view); |
| 138 mViewSwitcher = (ViewSwitcher) mMainView.findViewById(R.id.eb_view_switc
her); |
| 139 mUndoController = new EnhancedBookmarkUndoController(activity, mEnhanced
BookmarksModel, |
| 140 ((SnackbarManageable) activity).getSnackbarManager()); |
| 141 mSearchView = (EnhancedBookmarkSearchView) getView().findViewById(R.id.e
b_search_view); |
| 142 mEnhancedBookmarksModel.addModelObserver(mBookmarkModelObserver); |
| 143 initializeIfBookmarkModelLoaded(); |
| 144 |
| 145 // Load partner bookmarks explicitly. We load partner bookmarks in the d
eferred startup |
| 146 // code, but that might be executed much later. Especially on L, showing
loading |
| 147 // progress bar blocks that so it won't be loaded. http://crbug.com/4293
83 |
| 148 PartnerBookmarksShim.kickOffReading(activity); |
| 149 } |
| 150 |
| 151 /** |
| 152 * Destroys and cleans up itself. This must be called after done using this
class. |
| 153 */ |
| 154 public void destroy() { |
| 155 for (EnhancedBookmarkUIObserver observer : mUIObservers) { |
| 156 observer.onDestroy(); |
| 157 } |
| 158 assert mUIObservers.size() == 0; |
| 159 |
| 160 if (mUndoController != null) { |
| 161 mUndoController.destroy(); |
| 162 mUndoController = null; |
| 163 } |
| 164 mEnhancedBookmarksModel.removeModelObserver(mBookmarkModelObserver); |
| 165 mEnhancedBookmarksModel.removeFiltersObserver(mFiltersObserver); |
| 166 mEnhancedBookmarksModel.destroy(); |
| 167 mEnhancedBookmarksModel = null; |
| 168 } |
| 169 |
| 170 /** |
| 171 * Called when the user presses the back key. This is only going to be calle
d on Phone. |
| 172 * @return True if manager handles this event, false if it decides to ignore
. |
| 173 */ |
| 174 public boolean onBackPressed() { |
| 175 if (doesDrawerExist()) { |
| 176 if (mDrawer.isDrawerVisible(Gravity.START)) { |
| 177 mDrawer.closeDrawer(Gravity.START); |
| 178 return true; |
| 179 } |
| 180 } |
| 181 |
| 182 if (mContentView.onBackPressed()) return true; |
| 183 |
| 184 if (!mStateStack.empty()) { |
| 185 mStateStack.pop(); |
| 186 if (!mStateStack.empty()) { |
| 187 setState(mStateStack.pop()); |
| 188 return true; |
| 189 } |
| 190 } |
| 191 return false; |
| 192 } |
| 193 |
| 194 public View getView() { |
| 195 return mMainView; |
| 196 } |
| 197 |
| 198 /** |
| 199 * Sets the listener that reacts upon the change of the UI state of bookmark
manager. |
| 200 */ |
| 201 public void setUrlChangeListener(EnhancedBookmarkStateChangeListener urlList
ner) { |
| 202 mUrlChangeListener = urlListner; |
| 203 } |
| 204 |
| 205 /** |
| 206 * @return Current URL representing the UI state of bookmark manager. If no
state has been shown |
| 207 * yet in this session, on phone return last used state stored in pr
eference; on tablet |
| 208 * return the url previously set by {@link #updateForUrl(String)}. |
| 209 */ |
| 210 public String getCurrentUrl() { |
| 211 if (mStateStack.isEmpty()) return null; |
| 212 return mStateStack.peek().mUrl; |
| 213 } |
| 214 |
| 215 /** |
| 216 * Updates UI based on the new URL on tablet. If the bookmark model is not l
oaded yet, creates a |
| 217 * temporary loading state carrying this url. This method is supposed to ali
gn with |
| 218 * {@link EnhancedBookmarkPage#updateForUrl(String)} |
| 219 * <p> |
| 220 * @param url The url to navigate to. |
| 221 */ |
| 222 public void updateForUrl(String url) { |
| 223 if (mEnhancedBookmarksModel != null && mEnhancedBookmarksModel.isBookmar
kModelLoaded()) { |
| 224 setState(UIState.createStateFromUrl(url, mEnhancedBookmarksModel)); |
| 225 } else { |
| 226 // Note this does not guarantee to update the UI, as at this time th
e onCreateView() |
| 227 // might not has even been called yet. |
| 228 setState(UIState.createLoadingState(url)); |
| 229 } |
| 230 } |
| 231 |
| 232 /** |
| 233 * Initialization method that has 3 different behaviors based on whether boo
kmark model is |
| 234 * loaded. If the bookmark model is not loaded yet, it pushes a loading stat
e to backstack which |
| 235 * contains the url from preference. If the model is loaded and the backstac
k is empty, it |
| 236 * creates a state by fetching the last visited bookmark url stored in prefe
rence. If the |
| 237 * bookmark model is loaded but backstack contains a pending loading state,
it creates a new |
| 238 * state by getting the url of the loading state and replace the previous lo
ading state with the |
| 239 * new normal state. |
| 240 */ |
| 241 private void initializeIfBookmarkModelLoaded() { |
| 242 if (mEnhancedBookmarksModel.isBookmarkModelLoaded()) { |
| 243 mEnhancedBookmarksModel.addFiltersObserver(mFiltersObserver); |
| 244 mSearchView.onEnhancedBookmarkDelegateInitialized(this); |
| 245 mDrawerListView.onEnhancedBookmarkDelegateInitialized(this); |
| 246 mContentView.onEnhancedBookmarkDelegateInitialized(this); |
| 247 if (mStateStack.isEmpty()) { |
| 248 setState(UIState.createStateFromUrl(getUrlFromPreference(), |
| 249 mEnhancedBookmarksModel)); |
| 250 } else if (mStateStack.peek().mState == STATE_LOADING) { |
| 251 String url = mStateStack.pop().mUrl; |
| 252 setState(UIState.createStateFromUrl(url, mEnhancedBookmarksModel
)); |
| 253 } |
| 254 // Restore the previous view mode selection saved in preference. |
| 255 initListModeOptionTo(getListModePreference()); |
| 256 } else { |
| 257 mContentView.showLoadingUi(); |
| 258 mDrawerListView.showLoadingUi(); |
| 259 mContentView.showLoadingUi(); |
| 260 if (mStateStack.isEmpty() || mStateStack.peek().mState != STATE_LOAD
ING) { |
| 261 setState(UIState.createLoadingState(getUrlFromPreference())); |
| 262 } else if (!mStateStack.isEmpty()) { |
| 263 // Refresh the UI. This is needed because on tablet, updateForUr
l might set up |
| 264 // loading state too early and at that time all UI components ar
e not created yet. |
| 265 // Therefore we need to set the previous loading state once agai
n to trigger all UI |
| 266 // updates. |
| 267 setState(mStateStack.pop()); |
| 268 } |
| 269 } |
| 270 } |
| 271 |
| 272 /** |
| 273 * Saves url to preference. Note this method should be used after the main v
iew is attached to |
| 274 * an activity. |
| 275 */ |
| 276 private void saveUrlToPreference(String url) { |
| 277 PreferenceManager.getDefaultSharedPreferences(mActivity).edit() |
| 278 .putString(PREF_LAST_USED_URL, url).apply(); |
| 279 } |
| 280 |
| 281 /** |
| 282 * Fetches url to preference. Note this method should be used after the main
view is attached to |
| 283 * an activity. |
| 284 */ |
| 285 private String getUrlFromPreference() { |
| 286 return PreferenceManager.getDefaultSharedPreferences(mActivity).getStrin
g( |
| 287 PREF_LAST_USED_URL, UrlConstants.BOOKMARKS_URL); |
| 288 } |
| 289 |
| 290 private void saveListModePreference() { |
| 291 PreferenceManager.getDefaultSharedPreferences(mActivity).edit() |
| 292 .putInt(PREF_WAS_IN_LIST_MODE, mListModeEnabled ? LIST_MODE : GR
ID_MODE).apply(); |
| 293 } |
| 294 |
| 295 private boolean getListModePreference() { |
| 296 int mode = PreferenceManager.getDefaultSharedPreferences(mActivity).getI
nt( |
| 297 PREF_WAS_IN_LIST_MODE, DEFAULT_MODE); |
| 298 return mode == LIST_MODE ? true : false; |
| 299 } |
| 300 |
| 301 private void initListModeOptionTo(boolean isListModeEnabled) { |
| 302 mListModeEnabled = isListModeEnabled; |
| 303 for (EnhancedBookmarkUIObserver observer: mUIObservers) { |
| 304 observer.onListModeChange(isListModeEnabled); |
| 305 } |
| 306 // Every time the enhanced bookmark manager launches or the user clicks
the list-mode |
| 307 // toggle, we record the list view state. |
| 308 int listViewstate = PreferenceManager.getDefaultSharedPreferences(getVie
w().getContext()) |
| 309 .getInt(EnhancedBookmarkManager.PREF_WAS_IN_LIST_MODE, |
| 310 EnhancedBookmarkManager.DEFAULT_MODE); |
| 311 RecordHistogram.recordEnumeratedHistogram("EnhancedBookmarks.ViewMode",
listViewstate, 3); |
| 312 } |
| 313 |
| 314 /** |
| 315 * This is the ultimate internal method that updates UI and controls backsta
ck. And it is the |
| 316 * only method that pushes states to {@link #mStateStack}. |
| 317 * <p> |
| 318 * If the given state is not valid, all_bookmark state will be shown. Afterw
ards, this method |
| 319 * checks the current state: if currently in loading state, it pops it out a
nd adds the new |
| 320 * state to the back stack. It also notifies the {@link #mUrlChangeListener}
(if any) that the |
| 321 * url has changed. |
| 322 * <p> |
| 323 * Also note that even if we store states to {@link #mStateStack}, on tablet
the back navigation |
| 324 * and back button are not controlled by the manager: the tab handles back k
ey and backstack |
| 325 * navigation. |
| 326 */ |
| 327 private void setState(UIState state) { |
| 328 if (!state.isValid(mEnhancedBookmarksModel)) { |
| 329 state = UIState.createAllBookmarksState(mEnhancedBookmarksModel); |
| 330 } |
| 331 if (!mStateStack.isEmpty()) { |
| 332 if (mStateStack.peek().equals(state)) return; |
| 333 if (mStateStack.peek().mState == STATE_LOADING) { |
| 334 mStateStack.pop(); |
| 335 } |
| 336 } |
| 337 mStateStack.push(state); |
| 338 if (state.mState != STATE_LOADING) { |
| 339 // Loading state may be pushed to the stack but should never be stor
ed in preferences. |
| 340 saveUrlToPreference(state.mUrl); |
| 341 // If a loading state is replaced by another loading state, do not n
otify this change. |
| 342 if (mUrlChangeListener != null) mUrlChangeListener.onBookmarkUIState
Change(state.mUrl); |
| 343 } |
| 344 |
| 345 clearSelection(); |
| 346 |
| 347 for (EnhancedBookmarkUIObserver observer : mUIObservers) { |
| 348 notifyStateChange(observer); |
| 349 } |
| 350 } |
| 351 |
| 352 // EnhancedBookmarkDelegate implementations. |
| 353 |
| 354 @Override |
| 355 public void openFolder(BookmarkId folder) { |
| 356 closeSearchUI(); |
| 357 setState(UIState.createFolderState(folder, mEnhancedBookmarksModel)); |
| 358 } |
| 359 |
| 360 @Override |
| 361 public void openFilter(String filter) { |
| 362 closeSearchUI(); |
| 363 setState(UIState.createFilterState(filter, mEnhancedBookmarksModel)); |
| 364 } |
| 365 |
| 366 @Override |
| 367 public void openAllBookmarks() { |
| 368 closeSearchUI(); |
| 369 setState(UIState.createAllBookmarksState(mEnhancedBookmarksModel)); |
| 370 } |
| 371 |
| 372 @Override |
| 373 public void clearSelection() { |
| 374 mSelectedBookmarks.clear(); |
| 375 for (EnhancedBookmarkUIObserver observer : mUIObservers) { |
| 376 observer.onSelectionStateChange(new ArrayList<BookmarkId>(mSelectedB
ookmarks)); |
| 377 } |
| 378 } |
| 379 |
| 380 @Override |
| 381 public boolean toggleSelectionForBookmark(BookmarkId bookmark) { |
| 382 if (!mEnhancedBookmarksModel.getBookmarkById(bookmark).isEditable()) ret
urn false; |
| 383 |
| 384 if (mSelectedBookmarks.contains(bookmark)) mSelectedBookmarks.remove(boo
kmark); |
| 385 else mSelectedBookmarks.add(bookmark); |
| 386 for (EnhancedBookmarkUIObserver observer : mUIObservers) { |
| 387 observer.onSelectionStateChange(new ArrayList<BookmarkId>(mSelectedB
ookmarks)); |
| 388 } |
| 389 |
| 390 return isBookmarkSelected(bookmark); |
| 391 } |
| 392 |
| 393 @Override |
| 394 public boolean isBookmarkSelected(BookmarkId bookmark) { |
| 395 return mSelectedBookmarks.contains(bookmark); |
| 396 } |
| 397 |
| 398 @Override |
| 399 public boolean isSelectionEnabled() { |
| 400 return !mSelectedBookmarks.isEmpty(); |
| 401 } |
| 402 |
| 403 @Override |
| 404 public List<BookmarkId> getSelectedBookmarks() { |
| 405 return new ArrayList<BookmarkId>(mSelectedBookmarks); |
| 406 } |
| 407 |
| 408 @Override |
| 409 public void setListModeEnabled(boolean isListModeEnabled) { |
| 410 initListModeOptionTo(isListModeEnabled); |
| 411 saveListModePreference(); |
| 412 } |
| 413 |
| 414 @Override |
| 415 public boolean isListModeEnabled() { |
| 416 return mListModeEnabled; |
| 417 } |
| 418 |
| 419 @Override |
| 420 public void notifyStateChange(EnhancedBookmarkUIObserver observer) { |
| 421 int state = getCurrentState(); |
| 422 switch (state) { |
| 423 case STATE_ALL_BOOKMARKS: |
| 424 observer.onAllBookmarksStateSet(); |
| 425 break; |
| 426 case STATE_FOLDER: |
| 427 observer.onFolderStateSet(mStateStack.peek().mFolder); |
| 428 break; |
| 429 case STATE_FILTER: |
| 430 observer.onFilterStateSet(mStateStack.peek().mFilter); |
| 431 break; |
| 432 case STATE_LOADING: |
| 433 // In loading state, onEnhancedBookmarkDelegateInitialized() is
not called for all |
| 434 // UIObservers, which means that there will be no observers at t
he time. Do nothing. |
| 435 assert mUIObservers.isEmpty(); |
| 436 break; |
| 437 default: |
| 438 assert false : "State not valid"; |
| 439 break; |
| 440 } |
| 441 } |
| 442 |
| 443 @Override |
| 444 public boolean doesDrawerExist() { |
| 445 return mDrawer != null; |
| 446 } |
| 447 |
| 448 @Override |
| 449 public void closeDrawer() { |
| 450 if (!doesDrawerExist()) return; |
| 451 |
| 452 mDrawer.closeDrawer(Gravity.START); |
| 453 } |
| 454 |
| 455 @Override |
| 456 public DrawerLayout getDrawerLayout() { |
| 457 return mDrawer; |
| 458 } |
| 459 |
| 460 @Override |
| 461 @TargetApi(Build.VERSION_CODES.LOLLIPOP) |
| 462 public void startDetailActivity(BookmarkId bookmarkId, View view) { |
| 463 Intent intent = new Intent(mActivity, EnhancedBookmarkDetailActivity.cla
ss); |
| 464 intent.putExtra(EnhancedBookmarkDetailActivity.INTENT_BOOKMARK_ID, bookm
arkId.toString()); |
| 465 // Shared element animation is disabled on tablet because of bad quality
. |
| 466 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || view == null |
| 467 || DeviceFormFactor.isTablet(mActivity)) { |
| 468 mActivity.startActivity(intent); |
| 469 } else { |
| 470 ActivityOptions options = ActivityOptions.makeSceneTransitionAnimati
on(mActivity, view, |
| 471 mActivity.getString(R.string.enhanced_bookmark_detail_transi
tion_name)); |
| 472 mActivity.startActivity(intent, options.toBundle()); |
| 473 } |
| 474 } |
| 475 |
| 476 @Override |
| 477 public void openBookmark(BookmarkId bookmark, int launchLocation) { |
| 478 clearSelection(); |
| 479 NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_BOOKMARK); |
| 480 RecordHistogram.recordEnumeratedHistogram("Stars.LaunchLocation", launch
Location, |
| 481 LaunchLocation.COUNT); |
| 482 EnhancedBookmarkUtils.openBookmark(mActivity, |
| 483 mEnhancedBookmarksModel.getBookmarkById(bookmark).getUrl()); |
| 484 finishActivityOnPhone(); |
| 485 } |
| 486 |
| 487 @Override |
| 488 public void openSearchUI() { |
| 489 // Give search view focus, because it needs to handle back key event. |
| 490 mViewSwitcher.showNext(); |
| 491 } |
| 492 |
| 493 @Override |
| 494 public void closeSearchUI() { |
| 495 if (mSearchView.getVisibility() != View.VISIBLE) return; |
| 496 mViewSwitcher.showPrevious(); |
| 497 } |
| 498 |
| 499 @Override |
| 500 public void finishActivityOnPhone() { |
| 501 Activity activity = mActivity; |
| 502 if (activity instanceof EnhancedBookmarkActivity) { |
| 503 activity.finish(); |
| 504 } |
| 505 } |
| 506 |
| 507 @Override |
| 508 public void addUIObserver(EnhancedBookmarkUIObserver observer) { |
| 509 mUIObservers.addObserver(observer); |
| 510 } |
| 511 |
| 512 @Override |
| 513 public void removeUIObserver(EnhancedBookmarkUIObserver observer) { |
| 514 mUIObservers.removeObserver(observer); |
| 515 } |
| 516 |
| 517 @Override |
| 518 public EnhancedBookmarksModel getModel() { |
| 519 return mEnhancedBookmarksModel; |
| 520 } |
| 521 |
| 522 @Override |
| 523 public int getCurrentState() { |
| 524 if (mStateStack.isEmpty()) return STATE_LOADING; |
| 525 return mStateStack.peek().mState; |
| 526 } |
| 527 |
| 528 /** |
| 529 * Internal state that represents a url. Note every state needs to have a _v
alid_ url. For |
| 530 * loading state, {@link #mUrl} indicates the target to open after the bookm
ark model is loaded. |
| 531 */ |
| 532 private static class UIState { |
| 533 private static final String TAG = "UIState"; |
| 534 private static final String URL_CHARSET = "UTF-8"; |
| 535 /** |
| 536 * One of the four states: |
| 537 * {@link EnhancedBookmarkDelegate#STATE_ALL_BOOKMARKS}, |
| 538 * {@link EnhancedBookmarkDelegate#STATE_FILTER}, |
| 539 * {@link EnhancedBookmarkDelegate#STATE_FOLDER}, |
| 540 * {@link EnhancedBookmarkDelegate#STATE_LOADING}, |
| 541 */ |
| 542 int mState; |
| 543 String mUrl; |
| 544 BookmarkId mFolder; |
| 545 String mFilter; |
| 546 |
| 547 static UIState createLoadingState(String url) { |
| 548 UIState state = new UIState(); |
| 549 state.mUrl = url; |
| 550 state.mState = STATE_LOADING; |
| 551 return state; |
| 552 } |
| 553 |
| 554 static UIState createAllBookmarksState(EnhancedBookmarksModel bookmarkMo
del) { |
| 555 return createStateFromUrl(UrlConstants.BOOKMARKS_URL, bookmarkModel)
; |
| 556 } |
| 557 |
| 558 static UIState createFolderState(BookmarkId folder, EnhancedBookmarksMod
el bookmarkModel) { |
| 559 return createStateFromUrl(UrlConstants.BOOKMARKS_FOLDER_URL + folder
.toString(), |
| 560 bookmarkModel); |
| 561 } |
| 562 |
| 563 static UIState createFilterState(String filter, EnhancedBookmarksModel b
ookmarkModel) { |
| 564 return createStateFromUrl(encodeUrl(UrlConstants.BOOKMARKS_FILTER_UR
L, filter), |
| 565 bookmarkModel); |
| 566 } |
| 567 |
| 568 /** |
| 569 * @return A state corresponding to the url. If the url is not valid, re
turn all_bookmarks. |
| 570 */ |
| 571 static UIState createStateFromUrl(String url, EnhancedBookmarksModel boo
kmarkModel) { |
| 572 UIState state = new UIState(); |
| 573 state.mUrl = url; |
| 574 if (url.equals(UrlConstants.BOOKMARKS_URL)) { |
| 575 state.mState = STATE_ALL_BOOKMARKS; |
| 576 } else if (url.startsWith(UrlConstants.BOOKMARKS_FILTER_URL)) { |
| 577 String suffix = decodeSuffix(url, UrlConstants.BOOKMARKS_FILTER_
URL); |
| 578 if (!suffix.isEmpty()) { |
| 579 state.mState = STATE_FILTER; |
| 580 state.mFilter = suffix; |
| 581 } |
| 582 } else if (url.startsWith(UrlConstants.BOOKMARKS_FOLDER_URL)) { |
| 583 String suffix = decodeSuffix(url, UrlConstants.BOOKMARKS_FOLDER_
URL); |
| 584 if (!suffix.isEmpty()) { |
| 585 state.mFolder = BookmarkId.getBookmarkIdFromString(suffix); |
| 586 state.mState = STATE_FOLDER; |
| 587 } |
| 588 } |
| 589 |
| 590 if (!state.isValid(bookmarkModel)) { |
| 591 state.mState = STATE_ALL_BOOKMARKS; |
| 592 state.mUrl = UrlConstants.BOOKMARKS_URL; |
| 593 } |
| 594 |
| 595 return state; |
| 596 } |
| 597 |
| 598 /** |
| 599 * @return Whether this state is valid. |
| 600 */ |
| 601 boolean isValid(EnhancedBookmarksModel bookmarkModel) { |
| 602 if (mUrl == null) return false; |
| 603 if (mState == STATE_FOLDER) { |
| 604 if (mFolder == null) return false; |
| 605 |
| 606 return bookmarkModel.doesBookmarkExist(mFolder) |
| 607 && !mFolder.equals(bookmarkModel.getRootFolderId()); |
| 608 } |
| 609 if (mState == STATE_FILTER) { |
| 610 if (mFilter == null) return false; |
| 611 else return bookmarkModel.getFilters().contains(mFilter); |
| 612 } |
| 613 |
| 614 return true; |
| 615 } |
| 616 |
| 617 static String decodeSuffix(String url, String prefix) { |
| 618 String suffix = url.substring(prefix.length()); |
| 619 try { |
| 620 suffix = URLDecoder.decode(suffix, URL_CHARSET); |
| 621 } catch (UnsupportedEncodingException e) { |
| 622 Log.w(TAG, "Bookmark URL parsing failed. " + URL_CHARSET + " not
supported."); |
| 623 } |
| 624 return suffix; |
| 625 } |
| 626 |
| 627 static String encodeUrl(String prefix, String suffix) { |
| 628 try { |
| 629 suffix = URLEncoder.encode(suffix, URL_CHARSET); |
| 630 } catch (UnsupportedEncodingException e) { |
| 631 Log.w(TAG, "Bookmark URL parsing failed. " + URL_CHARSET + " not
supported."); |
| 632 } |
| 633 return prefix + suffix; |
| 634 } |
| 635 |
| 636 @Override |
| 637 public int hashCode() { |
| 638 return 31 * mUrl.hashCode() + mState; |
| 639 } |
| 640 |
| 641 @Override |
| 642 public boolean equals(Object obj) { |
| 643 if (!(obj instanceof UIState)) return false; |
| 644 UIState other = (UIState) obj; |
| 645 return mState == other.mState && mUrl.equals(other.mUrl); |
| 646 } |
| 647 } |
| 648 } |
OLD | NEW |