mobile/android/base/toolbar/BrowserToolbar.java

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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.toolbar;
michael@0 7
michael@0 8 import java.util.ArrayList;
michael@0 9 import java.util.Arrays;
michael@0 10 import java.util.EnumSet;
michael@0 11 import java.util.List;
michael@0 12
michael@0 13 import org.json.JSONObject;
michael@0 14 import org.mozilla.gecko.BrowserApp;
michael@0 15 import org.mozilla.gecko.GeckoAppShell;
michael@0 16 import org.mozilla.gecko.GeckoApplication;
michael@0 17 import org.mozilla.gecko.GeckoProfile;
michael@0 18 import org.mozilla.gecko.LightweightTheme;
michael@0 19 import org.mozilla.gecko.R;
michael@0 20 import org.mozilla.gecko.Tab;
michael@0 21 import org.mozilla.gecko.Tabs;
michael@0 22 import org.mozilla.gecko.Telemetry;
michael@0 23 import org.mozilla.gecko.TelemetryContract;
michael@0 24 import org.mozilla.gecko.animation.PropertyAnimator;
michael@0 25 import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener;
michael@0 26 import org.mozilla.gecko.animation.ViewHelper;
michael@0 27 import org.mozilla.gecko.menu.GeckoMenu;
michael@0 28 import org.mozilla.gecko.menu.MenuPopup;
michael@0 29 import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnStopListener;
michael@0 30 import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnTitleChangeListener;
michael@0 31 import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.UpdateFlags;
michael@0 32 import org.mozilla.gecko.util.Clipboard;
michael@0 33 import org.mozilla.gecko.util.GeckoEventListener;
michael@0 34 import org.mozilla.gecko.util.HardwareUtils;
michael@0 35 import org.mozilla.gecko.util.MenuUtils;
michael@0 36 import org.mozilla.gecko.widget.ThemedImageButton;
michael@0 37 import org.mozilla.gecko.widget.ThemedImageView;
michael@0 38 import org.mozilla.gecko.widget.ThemedRelativeLayout;
michael@0 39
michael@0 40 import android.content.Context;
michael@0 41 import android.content.res.Resources;
michael@0 42 import android.graphics.Bitmap;
michael@0 43 import android.graphics.drawable.BitmapDrawable;
michael@0 44 import android.graphics.drawable.Drawable;
michael@0 45 import android.graphics.drawable.StateListDrawable;
michael@0 46 import android.os.Build;
michael@0 47 import android.text.TextUtils;
michael@0 48 import android.util.AttributeSet;
michael@0 49 import android.util.Log;
michael@0 50 import android.view.ContextMenu;
michael@0 51 import android.view.KeyEvent;
michael@0 52 import android.view.LayoutInflater;
michael@0 53 import android.view.MenuInflater;
michael@0 54 import android.view.MotionEvent;
michael@0 55 import android.view.View;
michael@0 56 import android.view.ViewGroup;
michael@0 57 import android.view.animation.AccelerateInterpolator;
michael@0 58 import android.view.animation.Interpolator;
michael@0 59 import android.view.inputmethod.InputMethodManager;
michael@0 60 import android.widget.Button;
michael@0 61 import android.widget.ImageButton;
michael@0 62 import android.widget.ImageView;
michael@0 63 import android.widget.LinearLayout;
michael@0 64 import android.widget.PopupWindow;
michael@0 65 import android.widget.RelativeLayout;
michael@0 66
michael@0 67 /**
michael@0 68 * {@code BrowserToolbar} is single entry point for users of the toolbar
michael@0 69 * subsystem i.e. this should be the only import outside the 'toolbar'
michael@0 70 * package.
michael@0 71 *
michael@0 72 * {@code BrowserToolbar} serves at the single event bus for all
michael@0 73 * sub-components in the toolbar. It tracks tab events and gecko messages
michael@0 74 * and update the state of its inner components accordingly.
michael@0 75 *
michael@0 76 * It has two states, display and edit, which are controlled by
michael@0 77 * ToolbarEditLayout and ToolbarDisplayLayout. In display state, the toolbar
michael@0 78 * displays the current state for the selected tab. In edit state, it shows
michael@0 79 * a text entry for searching bookmarks/history. {@code BrowserToolbar}
michael@0 80 * provides public API to enter, cancel, and commit the edit state as well
michael@0 81 * as a set of listeners to allow {@code BrowserToolbar} users to react
michael@0 82 * to state changes accordingly.
michael@0 83 */
michael@0 84 public class BrowserToolbar extends ThemedRelativeLayout
michael@0 85 implements Tabs.OnTabsChangedListener,
michael@0 86 GeckoMenu.ActionItemBarPresenter,
michael@0 87 GeckoEventListener {
michael@0 88 private static final String LOGTAG = "GeckoToolbar";
michael@0 89
michael@0 90 public interface OnActivateListener {
michael@0 91 public void onActivate();
michael@0 92 }
michael@0 93
michael@0 94 public interface OnCommitListener {
michael@0 95 public void onCommit();
michael@0 96 }
michael@0 97
michael@0 98 public interface OnDismissListener {
michael@0 99 public void onDismiss();
michael@0 100 }
michael@0 101
michael@0 102 public interface OnFilterListener {
michael@0 103 public void onFilter(String searchText, AutocompleteHandler handler);
michael@0 104 }
michael@0 105
michael@0 106 public interface OnStartEditingListener {
michael@0 107 public void onStartEditing();
michael@0 108 }
michael@0 109
michael@0 110 public interface OnStopEditingListener {
michael@0 111 public void onStopEditing();
michael@0 112 }
michael@0 113
michael@0 114 private enum UIMode {
michael@0 115 EDIT,
michael@0 116 DISPLAY
michael@0 117 }
michael@0 118
michael@0 119 enum ForwardButtonAnimation {
michael@0 120 SHOW,
michael@0 121 HIDE
michael@0 122 }
michael@0 123
michael@0 124 private ToolbarDisplayLayout urlDisplayLayout;
michael@0 125 private ToolbarEditLayout urlEditLayout;
michael@0 126 private View urlBarEntry;
michael@0 127 private RelativeLayout.LayoutParams urlBarEntryDefaultLayoutParams;
michael@0 128 private RelativeLayout.LayoutParams urlBarEntryShrunkenLayoutParams;
michael@0 129 private ImageView urlBarTranslatingEdge;
michael@0 130 private boolean isSwitchingTabs;
michael@0 131 private ShapedButton tabsButton;
michael@0 132 private ImageButton backButton;
michael@0 133 private ImageButton forwardButton;
michael@0 134
michael@0 135 private ToolbarProgressView progressBar;
michael@0 136 private TabCounter tabsCounter;
michael@0 137 private ThemedImageButton menuButton;
michael@0 138 private ThemedImageView menuIcon;
michael@0 139 private LinearLayout actionItemBar;
michael@0 140 private MenuPopup menuPopup;
michael@0 141 private List<View> focusOrder;
michael@0 142
michael@0 143 private final ThemedImageView editCancel;
michael@0 144
michael@0 145 private boolean shouldShrinkURLBar = false;
michael@0 146
michael@0 147 private OnActivateListener activateListener;
michael@0 148 private OnFocusChangeListener focusChangeListener;
michael@0 149 private OnStartEditingListener startEditingListener;
michael@0 150 private OnStopEditingListener stopEditingListener;
michael@0 151
michael@0 152 private final BrowserApp activity;
michael@0 153 private boolean hasSoftMenuButton;
michael@0 154
michael@0 155 private UIMode uiMode;
michael@0 156 private boolean isAnimatingEntry;
michael@0 157
michael@0 158 private int urlBarViewOffset;
michael@0 159 private int defaultForwardMargin;
michael@0 160
michael@0 161 private static final Interpolator buttonsInterpolator = new AccelerateInterpolator();
michael@0 162
michael@0 163 private static final int FORWARD_ANIMATION_DURATION = 450;
michael@0 164
michael@0 165 private final LightweightTheme theme;
michael@0 166
michael@0 167 public BrowserToolbar(Context context) {
michael@0 168 this(context, null);
michael@0 169 }
michael@0 170
michael@0 171 public BrowserToolbar(Context context, AttributeSet attrs) {
michael@0 172 super(context, attrs);
michael@0 173 theme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme();
michael@0 174
michael@0 175 // BrowserToolbar is attached to BrowserApp only.
michael@0 176 activity = (BrowserApp) context;
michael@0 177
michael@0 178 // Inflate the content.
michael@0 179 LayoutInflater.from(context).inflate(R.layout.browser_toolbar, this);
michael@0 180
michael@0 181 Tabs.registerOnTabsChangedListener(this);
michael@0 182 isSwitchingTabs = true;
michael@0 183 isAnimatingEntry = false;
michael@0 184
michael@0 185 registerEventListener("Reader:Click");
michael@0 186 registerEventListener("Reader:LongClick");
michael@0 187
michael@0 188 final Resources res = getResources();
michael@0 189 urlBarViewOffset = res.getDimensionPixelSize(R.dimen.url_bar_offset_left);
michael@0 190 defaultForwardMargin = res.getDimensionPixelSize(R.dimen.forward_default_offset);
michael@0 191 urlDisplayLayout = (ToolbarDisplayLayout) findViewById(R.id.display_layout);
michael@0 192 urlBarEntry = findViewById(R.id.url_bar_entry);
michael@0 193 urlEditLayout = (ToolbarEditLayout) findViewById(R.id.edit_layout);
michael@0 194
michael@0 195 urlBarEntryDefaultLayoutParams = (RelativeLayout.LayoutParams) urlBarEntry.getLayoutParams();
michael@0 196 // API level 19 adds a RelativeLayout.LayoutParams copy constructor, so we explicitly cast
michael@0 197 // to ViewGroup.MarginLayoutParams to ensure consistency across platforms.
michael@0 198 urlBarEntryShrunkenLayoutParams = new RelativeLayout.LayoutParams(
michael@0 199 (ViewGroup.MarginLayoutParams) urlBarEntryDefaultLayoutParams);
michael@0 200 urlBarEntryShrunkenLayoutParams.addRule(RelativeLayout.ALIGN_RIGHT, R.id.edit_layout);
michael@0 201 urlBarEntryShrunkenLayoutParams.rightMargin = 0;
michael@0 202
michael@0 203 // This will clip the translating edge's image at 60% of its width
michael@0 204 urlBarTranslatingEdge = (ImageView) findViewById(R.id.url_bar_translating_edge);
michael@0 205 if (urlBarTranslatingEdge != null) {
michael@0 206 urlBarTranslatingEdge.getDrawable().setLevel(6000);
michael@0 207 }
michael@0 208
michael@0 209 tabsButton = (ShapedButton) findViewById(R.id.tabs);
michael@0 210 tabsCounter = (TabCounter) findViewById(R.id.tabs_counter);
michael@0 211 if (Build.VERSION.SDK_INT >= 11) {
michael@0 212 tabsCounter.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
michael@0 213 }
michael@0 214
michael@0 215 backButton = (ImageButton) findViewById(R.id.back);
michael@0 216 setButtonEnabled(backButton, false);
michael@0 217 forwardButton = (ImageButton) findViewById(R.id.forward);
michael@0 218 setButtonEnabled(forwardButton, false);
michael@0 219
michael@0 220 menuButton = (ThemedImageButton) findViewById(R.id.menu);
michael@0 221 menuIcon = (ThemedImageView) findViewById(R.id.menu_icon);
michael@0 222 actionItemBar = (LinearLayout) findViewById(R.id.menu_items);
michael@0 223 hasSoftMenuButton = !HardwareUtils.hasMenuButton();
michael@0 224
michael@0 225 editCancel = (ThemedImageView) findViewById(R.id.edit_cancel);
michael@0 226
michael@0 227 // We use different layouts on phones and tablets, so adjust the focus
michael@0 228 // order appropriately.
michael@0 229 focusOrder = new ArrayList<View>();
michael@0 230 if (HardwareUtils.isTablet()) {
michael@0 231 focusOrder.addAll(Arrays.asList(tabsButton, backButton, forwardButton, this));
michael@0 232 focusOrder.addAll(urlDisplayLayout.getFocusOrder());
michael@0 233 focusOrder.addAll(Arrays.asList(actionItemBar, menuButton));
michael@0 234 } else {
michael@0 235 focusOrder.add(this);
michael@0 236 focusOrder.addAll(urlDisplayLayout.getFocusOrder());
michael@0 237 focusOrder.addAll(Arrays.asList(tabsButton, menuButton));
michael@0 238 }
michael@0 239
michael@0 240 setUIMode(UIMode.DISPLAY);
michael@0 241 }
michael@0 242
michael@0 243 @Override
michael@0 244 public void onAttachedToWindow() {
michael@0 245 super.onAttachedToWindow();
michael@0 246
michael@0 247 setOnClickListener(new Button.OnClickListener() {
michael@0 248 @Override
michael@0 249 public void onClick(View v) {
michael@0 250 if (activateListener != null) {
michael@0 251 activateListener.onActivate();
michael@0 252 }
michael@0 253 }
michael@0 254 });
michael@0 255
michael@0 256 setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
michael@0 257 @Override
michael@0 258 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
michael@0 259 // We don't the context menu while editing
michael@0 260 if (isEditing()) {
michael@0 261 return;
michael@0 262 }
michael@0 263
michael@0 264 // NOTE: Use MenuUtils.safeSetVisible because some actions might
michael@0 265 // be on the Page menu
michael@0 266
michael@0 267 MenuInflater inflater = activity.getMenuInflater();
michael@0 268 inflater.inflate(R.menu.titlebar_contextmenu, menu);
michael@0 269
michael@0 270 String clipboard = Clipboard.getText();
michael@0 271 if (TextUtils.isEmpty(clipboard)) {
michael@0 272 menu.findItem(R.id.pasteandgo).setVisible(false);
michael@0 273 menu.findItem(R.id.paste).setVisible(false);
michael@0 274 }
michael@0 275
michael@0 276 Tab tab = Tabs.getInstance().getSelectedTab();
michael@0 277 if (tab != null) {
michael@0 278 String url = tab.getURL();
michael@0 279 if (url == null) {
michael@0 280 menu.findItem(R.id.copyurl).setVisible(false);
michael@0 281 menu.findItem(R.id.add_to_launcher).setVisible(false);
michael@0 282 MenuUtils.safeSetVisible(menu, R.id.share, false);
michael@0 283 }
michael@0 284
michael@0 285 MenuUtils.safeSetVisible(menu, R.id.subscribe, tab.hasFeeds());
michael@0 286 MenuUtils.safeSetVisible(menu, R.id.add_search_engine, tab.hasOpenSearch());
michael@0 287 } else {
michael@0 288 // if there is no tab, remove anything tab dependent
michael@0 289 menu.findItem(R.id.copyurl).setVisible(false);
michael@0 290 menu.findItem(R.id.add_to_launcher).setVisible(false);
michael@0 291 MenuUtils.safeSetVisible(menu, R.id.share, false);
michael@0 292 MenuUtils.safeSetVisible(menu, R.id.subscribe, false);
michael@0 293 MenuUtils.safeSetVisible(menu, R.id.add_search_engine, false);
michael@0 294 }
michael@0 295
michael@0 296 MenuUtils.safeSetVisible(menu, R.id.share, !GeckoProfile.get(getContext()).inGuestMode());
michael@0 297 }
michael@0 298 });
michael@0 299
michael@0 300 urlDisplayLayout.setOnStopListener(new OnStopListener() {
michael@0 301 @Override
michael@0 302 public Tab onStop() {
michael@0 303 final Tab tab = Tabs.getInstance().getSelectedTab();
michael@0 304 if (tab != null) {
michael@0 305 tab.doStop();
michael@0 306 return tab;
michael@0 307 }
michael@0 308
michael@0 309 return null;
michael@0 310 }
michael@0 311 });
michael@0 312
michael@0 313 urlDisplayLayout.setOnTitleChangeListener(new OnTitleChangeListener() {
michael@0 314 @Override
michael@0 315 public void onTitleChange(CharSequence title) {
michael@0 316 final String contentDescription;
michael@0 317 if (title != null) {
michael@0 318 contentDescription = title.toString();
michael@0 319 } else {
michael@0 320 contentDescription = activity.getString(R.string.url_bar_default_text);
michael@0 321 }
michael@0 322
michael@0 323 // The title and content description should
michael@0 324 // always be sync.
michael@0 325 setContentDescription(contentDescription);
michael@0 326 }
michael@0 327 });
michael@0 328
michael@0 329 urlEditLayout.setOnFocusChangeListener(new View.OnFocusChangeListener() {
michael@0 330 @Override
michael@0 331 public void onFocusChange(View v, boolean hasFocus) {
michael@0 332 // This will select the url bar when entering editing mode.
michael@0 333 setSelected(hasFocus);
michael@0 334 if (focusChangeListener != null) {
michael@0 335 focusChangeListener.onFocusChange(v, hasFocus);
michael@0 336 }
michael@0 337 }
michael@0 338 });
michael@0 339
michael@0 340 tabsButton.setOnClickListener(new Button.OnClickListener() {
michael@0 341 @Override
michael@0 342 public void onClick(View v) {
michael@0 343 toggleTabs();
michael@0 344 }
michael@0 345 });
michael@0 346 tabsButton.setImageLevel(0);
michael@0 347
michael@0 348 backButton.setOnClickListener(new Button.OnClickListener() {
michael@0 349 @Override
michael@0 350 public void onClick(View view) {
michael@0 351 Tabs.getInstance().getSelectedTab().doBack();
michael@0 352 }
michael@0 353 });
michael@0 354 backButton.setOnLongClickListener(new Button.OnLongClickListener() {
michael@0 355 @Override
michael@0 356 public boolean onLongClick(View view) {
michael@0 357 return Tabs.getInstance().getSelectedTab().showBackHistory();
michael@0 358 }
michael@0 359 });
michael@0 360
michael@0 361 forwardButton.setOnClickListener(new Button.OnClickListener() {
michael@0 362 @Override
michael@0 363 public void onClick(View view) {
michael@0 364 Tabs.getInstance().getSelectedTab().doForward();
michael@0 365 }
michael@0 366 });
michael@0 367 forwardButton.setOnLongClickListener(new Button.OnLongClickListener() {
michael@0 368 @Override
michael@0 369 public boolean onLongClick(View view) {
michael@0 370 return Tabs.getInstance().getSelectedTab().showForwardHistory();
michael@0 371 }
michael@0 372 });
michael@0 373
michael@0 374 if (editCancel != null) {
michael@0 375 editCancel.setOnClickListener(new OnClickListener() {
michael@0 376 @Override
michael@0 377 public void onClick(View v) {
michael@0 378 // If we exit editing mode during the animation,
michael@0 379 // we're put into an inconsistent state (bug 1017276).
michael@0 380 if (!isAnimatingEntry) {
michael@0 381 Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL,
michael@0 382 TelemetryContract.Method.ACTIONBAR,
michael@0 383 getResources().getResourceEntryName(editCancel.getId()));
michael@0 384 cancelEdit();
michael@0 385 }
michael@0 386 }
michael@0 387 });
michael@0 388 }
michael@0 389
michael@0 390 if (hasSoftMenuButton) {
michael@0 391 menuButton.setVisibility(View.VISIBLE);
michael@0 392 menuIcon.setVisibility(View.VISIBLE);
michael@0 393
michael@0 394 menuButton.setOnClickListener(new Button.OnClickListener() {
michael@0 395 @Override
michael@0 396 public void onClick(View view) {
michael@0 397 activity.openOptionsMenu();
michael@0 398 }
michael@0 399 });
michael@0 400 }
michael@0 401 }
michael@0 402
michael@0 403 public void setProgressBar(ToolbarProgressView progressBar) {
michael@0 404 this.progressBar = progressBar;
michael@0 405 }
michael@0 406
michael@0 407 public void refresh() {
michael@0 408 urlDisplayLayout.dismissSiteIdentityPopup();
michael@0 409 }
michael@0 410
michael@0 411 public boolean onBackPressed() {
michael@0 412 // If we exit editing mode during the animation,
michael@0 413 // we're put into an inconsistent state (bug 1017276).
michael@0 414 if (isEditing() && !isAnimatingEntry) {
michael@0 415 Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL,
michael@0 416 TelemetryContract.Method.BACK);
michael@0 417 cancelEdit();
michael@0 418 return true;
michael@0 419 }
michael@0 420
michael@0 421 return urlDisplayLayout.dismissSiteIdentityPopup();
michael@0 422 }
michael@0 423
michael@0 424 @Override
michael@0 425 public boolean onTouchEvent(MotionEvent event) {
michael@0 426 // If the motion event has occured below the toolbar (due to the scroll
michael@0 427 // offset), let it pass through to the page.
michael@0 428 if (event != null && event.getY() > getHeight() + ViewHelper.getTranslationY(this)) {
michael@0 429 return false;
michael@0 430 }
michael@0 431
michael@0 432 return super.onTouchEvent(event);
michael@0 433 }
michael@0 434
michael@0 435 @Override
michael@0 436 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
michael@0 437 super.onSizeChanged(w, h, oldw, oldh);
michael@0 438
michael@0 439 if (h != oldh) {
michael@0 440 // Post this to happen outside of onSizeChanged, as this may cause
michael@0 441 // a layout change and relayouts within a layout change don't work.
michael@0 442 post(new Runnable() {
michael@0 443 @Override
michael@0 444 public void run() {
michael@0 445 activity.refreshToolbarHeight();
michael@0 446 }
michael@0 447 });
michael@0 448 }
michael@0 449 }
michael@0 450
michael@0 451 @Override
michael@0 452 public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
michael@0 453 Log.d(LOGTAG, "onTabChanged: " + msg);
michael@0 454 final Tabs tabs = Tabs.getInstance();
michael@0 455
michael@0 456 // These conditions are split into three phases:
michael@0 457 // * Always do first
michael@0 458 // * Handling specific to the selected tab
michael@0 459 // * Always do afterwards.
michael@0 460
michael@0 461 switch (msg) {
michael@0 462 case ADDED:
michael@0 463 case CLOSED:
michael@0 464 updateTabCount(tabs.getDisplayCount());
michael@0 465 break;
michael@0 466 case RESTORED:
michael@0 467 // TabCount fixup after OOM
michael@0 468 case SELECTED:
michael@0 469 urlDisplayLayout.dismissSiteIdentityPopup();
michael@0 470 updateTabCount(tabs.getDisplayCount());
michael@0 471 isSwitchingTabs = true;
michael@0 472 break;
michael@0 473 }
michael@0 474
michael@0 475 if (tabs.isSelectedTab(tab)) {
michael@0 476 final EnumSet<UpdateFlags> flags = EnumSet.noneOf(UpdateFlags.class);
michael@0 477
michael@0 478 // Progress-related handling
michael@0 479 switch (msg) {
michael@0 480 case START:
michael@0 481 updateProgressVisibility(tab, Tab.LOAD_PROGRESS_INIT);
michael@0 482 // Fall through.
michael@0 483 case ADDED:
michael@0 484 case LOCATION_CHANGE:
michael@0 485 case LOAD_ERROR:
michael@0 486 case LOADED:
michael@0 487 case STOP:
michael@0 488 flags.add(UpdateFlags.PROGRESS);
michael@0 489 if (progressBar.getVisibility() == View.VISIBLE) {
michael@0 490 progressBar.animateProgress(tab.getLoadProgress());
michael@0 491 }
michael@0 492 break;
michael@0 493
michael@0 494 case SELECTED:
michael@0 495 flags.add(UpdateFlags.PROGRESS);
michael@0 496 updateProgressVisibility();
michael@0 497 break;
michael@0 498 }
michael@0 499
michael@0 500 switch (msg) {
michael@0 501 case STOP:
michael@0 502 // Reset the title in case we haven't navigated
michael@0 503 // to a new page yet.
michael@0 504 flags.add(UpdateFlags.TITLE);
michael@0 505 // Fall through.
michael@0 506 case START:
michael@0 507 case CLOSED:
michael@0 508 case ADDED:
michael@0 509 updateBackButton(tab);
michael@0 510 updateForwardButton(tab);
michael@0 511 break;
michael@0 512
michael@0 513 case SELECTED:
michael@0 514 flags.add(UpdateFlags.PRIVATE_MODE);
michael@0 515 setPrivateMode(tab.isPrivate());
michael@0 516 // Fall through.
michael@0 517 case LOAD_ERROR:
michael@0 518 flags.add(UpdateFlags.TITLE);
michael@0 519 // Fall through.
michael@0 520 case LOCATION_CHANGE:
michael@0 521 // A successful location change will cause Tab to notify
michael@0 522 // us of a title change, so we don't update the title here.
michael@0 523 flags.add(UpdateFlags.FAVICON);
michael@0 524 flags.add(UpdateFlags.SITE_IDENTITY);
michael@0 525
michael@0 526 updateBackButton(tab);
michael@0 527 updateForwardButton(tab);
michael@0 528 break;
michael@0 529
michael@0 530 case TITLE:
michael@0 531 flags.add(UpdateFlags.TITLE);
michael@0 532 break;
michael@0 533
michael@0 534 case FAVICON:
michael@0 535 flags.add(UpdateFlags.FAVICON);
michael@0 536 break;
michael@0 537
michael@0 538 case SECURITY_CHANGE:
michael@0 539 flags.add(UpdateFlags.SITE_IDENTITY);
michael@0 540 break;
michael@0 541 }
michael@0 542
michael@0 543 if (!flags.isEmpty()) {
michael@0 544 updateDisplayLayout(tab, flags);
michael@0 545 }
michael@0 546 }
michael@0 547
michael@0 548 switch (msg) {
michael@0 549 case SELECTED:
michael@0 550 case LOAD_ERROR:
michael@0 551 case LOCATION_CHANGE:
michael@0 552 isSwitchingTabs = false;
michael@0 553 }
michael@0 554 }
michael@0 555
michael@0 556 private void updateProgressVisibility() {
michael@0 557 final Tab selectedTab = Tabs.getInstance().getSelectedTab();
michael@0 558 updateProgressVisibility(selectedTab, selectedTab.getLoadProgress());
michael@0 559 }
michael@0 560
michael@0 561 private void updateProgressVisibility(Tab selectedTab, int progress) {
michael@0 562 if (!isEditing() && selectedTab.getState() == Tab.STATE_LOADING) {
michael@0 563 progressBar.setProgress(progress);
michael@0 564 progressBar.setVisibility(View.VISIBLE);
michael@0 565 } else {
michael@0 566 progressBar.setVisibility(View.GONE);
michael@0 567 }
michael@0 568 }
michael@0 569
michael@0 570 private boolean isVisible() {
michael@0 571 return ViewHelper.getTranslationY(this) == 0;
michael@0 572 }
michael@0 573
michael@0 574 @Override
michael@0 575 public void setNextFocusDownId(int nextId) {
michael@0 576 super.setNextFocusDownId(nextId);
michael@0 577 tabsButton.setNextFocusDownId(nextId);
michael@0 578 backButton.setNextFocusDownId(nextId);
michael@0 579 forwardButton.setNextFocusDownId(nextId);
michael@0 580 urlDisplayLayout.setNextFocusDownId(nextId);
michael@0 581 menuButton.setNextFocusDownId(nextId);
michael@0 582 }
michael@0 583
michael@0 584 private int getUrlBarEntryTranslation() {
michael@0 585 if (editCancel == null) {
michael@0 586 // We are on tablet, and there is no animation so return a translation of 0.
michael@0 587 return 0;
michael@0 588 }
michael@0 589
michael@0 590 // Find the distance from the right-edge of the url bar (where we're translating from) to
michael@0 591 // the left-edge of the cancel button (where we're translating to; note that the cancel
michael@0 592 // button must be laid out, i.e. not View.GONE).
michael@0 593 final LayoutParams lp = (LayoutParams) urlEditLayout.getLayoutParams();
michael@0 594 return editCancel.getLeft() - lp.leftMargin - urlBarEntry.getRight();
michael@0 595 }
michael@0 596
michael@0 597 private int getUrlBarCurveTranslation() {
michael@0 598 return getWidth() - tabsButton.getLeft();
michael@0 599 }
michael@0 600
michael@0 601 private boolean canDoBack(Tab tab) {
michael@0 602 return (tab.canDoBack() && !isEditing());
michael@0 603 }
michael@0 604
michael@0 605 private boolean canDoForward(Tab tab) {
michael@0 606 return (tab.canDoForward() && !isEditing());
michael@0 607 }
michael@0 608
michael@0 609 private void addTab() {
michael@0 610 activity.addTab();
michael@0 611 }
michael@0 612
michael@0 613 private void toggleTabs() {
michael@0 614 if (activity.areTabsShown()) {
michael@0 615 if (activity.hasTabsSideBar())
michael@0 616 activity.hideTabs();
michael@0 617 } else {
michael@0 618 // hide the virtual keyboard
michael@0 619 InputMethodManager imm =
michael@0 620 (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
michael@0 621 imm.hideSoftInputFromWindow(tabsButton.getWindowToken(), 0);
michael@0 622
michael@0 623 Tab tab = Tabs.getInstance().getSelectedTab();
michael@0 624 if (tab != null) {
michael@0 625 if (!tab.isPrivate())
michael@0 626 activity.showNormalTabs();
michael@0 627 else
michael@0 628 activity.showPrivateTabs();
michael@0 629 }
michael@0 630 }
michael@0 631 }
michael@0 632
michael@0 633 private void updateTabCountAndAnimate(int count) {
michael@0 634 // Don't animate if the toolbar is hidden.
michael@0 635 if (!isVisible()) {
michael@0 636 updateTabCount(count);
michael@0 637 return;
michael@0 638 }
michael@0 639
michael@0 640 // If toolbar is in edit mode on a phone, this means the entry is expanded
michael@0 641 // and the tabs button is translated offscreen. Don't trigger tabs counter
michael@0 642 // updates until the tabs button is back on screen.
michael@0 643 // See stopEditing()
michael@0 644 if (!isEditing() || HardwareUtils.isTablet()) {
michael@0 645 tabsCounter.setCount(count);
michael@0 646
michael@0 647 tabsButton.setContentDescription((count > 1) ?
michael@0 648 activity.getString(R.string.num_tabs, count) :
michael@0 649 activity.getString(R.string.one_tab));
michael@0 650 }
michael@0 651 }
michael@0 652
michael@0 653 private void updateTabCount(int count) {
michael@0 654 // If toolbar is in edit mode on a phone, this means the entry is expanded
michael@0 655 // and the tabs button is translated offscreen. Don't trigger tabs counter
michael@0 656 // updates until the tabs button is back on screen.
michael@0 657 // See stopEditing()
michael@0 658 if (isEditing() && !HardwareUtils.isTablet()) {
michael@0 659 return;
michael@0 660 }
michael@0 661
michael@0 662 // Set TabCounter based on visibility
michael@0 663 if (isVisible() && ViewHelper.getAlpha(tabsCounter) != 0 && !isEditing()) {
michael@0 664 tabsCounter.setCountWithAnimation(count);
michael@0 665 } else {
michael@0 666 tabsCounter.setCount(count);
michael@0 667 }
michael@0 668
michael@0 669 // Update A11y information
michael@0 670 tabsButton.setContentDescription((count > 1) ?
michael@0 671 activity.getString(R.string.num_tabs, count) :
michael@0 672 activity.getString(R.string.one_tab));
michael@0 673 }
michael@0 674
michael@0 675 private void updateDisplayLayout(Tab tab, EnumSet<UpdateFlags> flags) {
michael@0 676 if (isSwitchingTabs) {
michael@0 677 flags.add(UpdateFlags.DISABLE_ANIMATIONS);
michael@0 678 }
michael@0 679
michael@0 680 urlDisplayLayout.updateFromTab(tab, flags);
michael@0 681
michael@0 682 if (flags.contains(UpdateFlags.TITLE)) {
michael@0 683 if (!isEditing()) {
michael@0 684 urlEditLayout.setText(tab.getURL());
michael@0 685 }
michael@0 686 }
michael@0 687
michael@0 688 if (flags.contains(UpdateFlags.PROGRESS)) {
michael@0 689 updateFocusOrder();
michael@0 690 }
michael@0 691 }
michael@0 692
michael@0 693 private void updateFocusOrder() {
michael@0 694 View prevView = null;
michael@0 695
michael@0 696 // If the element that has focus becomes disabled or invisible, focus
michael@0 697 // is given to the URL bar.
michael@0 698 boolean needsNewFocus = false;
michael@0 699
michael@0 700 for (View view : focusOrder) {
michael@0 701 if (view.getVisibility() != View.VISIBLE || !view.isEnabled()) {
michael@0 702 if (view.hasFocus()) {
michael@0 703 needsNewFocus = true;
michael@0 704 }
michael@0 705 continue;
michael@0 706 }
michael@0 707
michael@0 708 if (view == actionItemBar) {
michael@0 709 final int childCount = actionItemBar.getChildCount();
michael@0 710 for (int child = 0; child < childCount; child++) {
michael@0 711 View childView = actionItemBar.getChildAt(child);
michael@0 712 if (prevView != null) {
michael@0 713 childView.setNextFocusLeftId(prevView.getId());
michael@0 714 prevView.setNextFocusRightId(childView.getId());
michael@0 715 }
michael@0 716 prevView = childView;
michael@0 717 }
michael@0 718 } else {
michael@0 719 if (prevView != null) {
michael@0 720 view.setNextFocusLeftId(prevView.getId());
michael@0 721 prevView.setNextFocusRightId(view.getId());
michael@0 722 }
michael@0 723 prevView = view;
michael@0 724 }
michael@0 725 }
michael@0 726
michael@0 727 if (needsNewFocus) {
michael@0 728 requestFocus();
michael@0 729 }
michael@0 730 }
michael@0 731
michael@0 732 public void onEditSuggestion(String suggestion) {
michael@0 733 if (!isEditing()) {
michael@0 734 return;
michael@0 735 }
michael@0 736
michael@0 737 urlEditLayout.onEditSuggestion(suggestion);
michael@0 738 }
michael@0 739
michael@0 740 public void setTitle(CharSequence title) {
michael@0 741 urlDisplayLayout.setTitle(title);
michael@0 742 }
michael@0 743
michael@0 744 public void prepareTabsAnimation(PropertyAnimator animator, boolean tabsAreShown) {
michael@0 745 if (!tabsAreShown) {
michael@0 746 PropertyAnimator buttonsAnimator =
michael@0 747 new PropertyAnimator(animator.getDuration(), buttonsInterpolator);
michael@0 748
michael@0 749 buttonsAnimator.attach(tabsCounter,
michael@0 750 PropertyAnimator.Property.ALPHA,
michael@0 751 1.0f);
michael@0 752
michael@0 753 if (hasSoftMenuButton && !HardwareUtils.isTablet()) {
michael@0 754 buttonsAnimator.attach(menuIcon,
michael@0 755 PropertyAnimator.Property.ALPHA,
michael@0 756 1.0f);
michael@0 757 }
michael@0 758
michael@0 759 buttonsAnimator.start();
michael@0 760
michael@0 761 return;
michael@0 762 }
michael@0 763
michael@0 764 ViewHelper.setAlpha(tabsCounter, 0.0f);
michael@0 765
michael@0 766 if (hasSoftMenuButton && !HardwareUtils.isTablet()) {
michael@0 767 ViewHelper.setAlpha(menuIcon, 0.0f);
michael@0 768 }
michael@0 769 }
michael@0 770
michael@0 771 public void finishTabsAnimation(boolean tabsAreShown) {
michael@0 772 if (tabsAreShown) {
michael@0 773 return;
michael@0 774 }
michael@0 775
michael@0 776 PropertyAnimator animator = new PropertyAnimator(150);
michael@0 777
michael@0 778 animator.attach(tabsCounter,
michael@0 779 PropertyAnimator.Property.ALPHA,
michael@0 780 1.0f);
michael@0 781
michael@0 782 if (hasSoftMenuButton && !HardwareUtils.isTablet()) {
michael@0 783 animator.attach(menuIcon,
michael@0 784 PropertyAnimator.Property.ALPHA,
michael@0 785 1.0f);
michael@0 786 }
michael@0 787
michael@0 788 animator.start();
michael@0 789 }
michael@0 790
michael@0 791 public void setOnActivateListener(OnActivateListener listener) {
michael@0 792 activateListener = listener;
michael@0 793 }
michael@0 794
michael@0 795 public void setOnCommitListener(OnCommitListener listener) {
michael@0 796 urlEditLayout.setOnCommitListener(listener);
michael@0 797 }
michael@0 798
michael@0 799 public void setOnDismissListener(OnDismissListener listener) {
michael@0 800 urlEditLayout.setOnDismissListener(listener);
michael@0 801 }
michael@0 802
michael@0 803 public void setOnFilterListener(OnFilterListener listener) {
michael@0 804 urlEditLayout.setOnFilterListener(listener);
michael@0 805 }
michael@0 806
michael@0 807 public void setOnFocusChangeListener(OnFocusChangeListener listener) {
michael@0 808 focusChangeListener = listener;
michael@0 809 }
michael@0 810
michael@0 811 public void setOnStartEditingListener(OnStartEditingListener listener) {
michael@0 812 startEditingListener = listener;
michael@0 813 }
michael@0 814
michael@0 815 public void setOnStopEditingListener(OnStopEditingListener listener) {
michael@0 816 stopEditingListener = listener;
michael@0 817 }
michael@0 818
michael@0 819 private void showUrlEditLayout() {
michael@0 820 setUrlEditLayoutVisibility(true, null);
michael@0 821 }
michael@0 822
michael@0 823 private void showUrlEditLayout(PropertyAnimator animator) {
michael@0 824 setUrlEditLayoutVisibility(true, animator);
michael@0 825 }
michael@0 826
michael@0 827 private void hideUrlEditLayout() {
michael@0 828 setUrlEditLayoutVisibility(false, null);
michael@0 829 }
michael@0 830
michael@0 831 private void hideUrlEditLayout(PropertyAnimator animator) {
michael@0 832 setUrlEditLayoutVisibility(false, animator);
michael@0 833 }
michael@0 834
michael@0 835 private void setUrlEditLayoutVisibility(final boolean showEditLayout, PropertyAnimator animator) {
michael@0 836 if (showEditLayout) {
michael@0 837 urlEditLayout.prepareShowAnimation(animator);
michael@0 838 }
michael@0 839
michael@0 840 if (animator == null) {
michael@0 841 final View viewToShow = (showEditLayout ? urlEditLayout : urlDisplayLayout);
michael@0 842 final View viewToHide = (showEditLayout ? urlDisplayLayout : urlEditLayout);
michael@0 843
michael@0 844 viewToHide.setVisibility(View.GONE);
michael@0 845 viewToShow.setVisibility(View.VISIBLE);
michael@0 846
michael@0 847 final int cancelVisibility = (showEditLayout ? View.VISIBLE : View.INVISIBLE);
michael@0 848 setCancelVisibility(cancelVisibility);
michael@0 849 return;
michael@0 850 }
michael@0 851
michael@0 852 animator.addPropertyAnimationListener(new PropertyAnimationListener() {
michael@0 853 @Override
michael@0 854 public void onPropertyAnimationStart() {
michael@0 855 if (!showEditLayout) {
michael@0 856 urlEditLayout.setVisibility(View.GONE);
michael@0 857 urlDisplayLayout.setVisibility(View.VISIBLE);
michael@0 858
michael@0 859 setCancelVisibility(View.INVISIBLE);
michael@0 860 }
michael@0 861 }
michael@0 862
michael@0 863 @Override
michael@0 864 public void onPropertyAnimationEnd() {
michael@0 865 if (showEditLayout) {
michael@0 866 urlDisplayLayout.setVisibility(View.GONE);
michael@0 867 urlEditLayout.setVisibility(View.VISIBLE);
michael@0 868
michael@0 869 setCancelVisibility(View.VISIBLE);
michael@0 870 }
michael@0 871 }
michael@0 872 });
michael@0 873 }
michael@0 874
michael@0 875 private void setCancelVisibility(final int visibility) {
michael@0 876 if (editCancel != null) {
michael@0 877 editCancel.setVisibility(visibility);
michael@0 878 }
michael@0 879 }
michael@0 880
michael@0 881 /**
michael@0 882 * Disables and dims all toolbar elements which are not
michael@0 883 * related to editing mode.
michael@0 884 */
michael@0 885 private void updateChildrenForEditing() {
michael@0 886 // This is for the tablet UI only
michael@0 887 if (!HardwareUtils.isTablet()) {
michael@0 888 return;
michael@0 889 }
michael@0 890
michael@0 891 // Disable toolbar elemens while in editing mode
michael@0 892 final boolean enabled = !isEditing();
michael@0 893
michael@0 894 // This alpha value has to be in sync with the one used
michael@0 895 // in setButtonEnabled().
michael@0 896 final float alpha = (enabled ? 1.0f : 0.24f);
michael@0 897
michael@0 898 if (!enabled) {
michael@0 899 tabsCounter.onEnterEditingMode();
michael@0 900 }
michael@0 901
michael@0 902 tabsButton.setEnabled(enabled);
michael@0 903 ViewHelper.setAlpha(tabsCounter, alpha);
michael@0 904 menuButton.setEnabled(enabled);
michael@0 905 ViewHelper.setAlpha(menuIcon, alpha);
michael@0 906
michael@0 907 final int actionItemsCount = actionItemBar.getChildCount();
michael@0 908 for (int i = 0; i < actionItemsCount; i++) {
michael@0 909 actionItemBar.getChildAt(i).setEnabled(enabled);
michael@0 910 }
michael@0 911 ViewHelper.setAlpha(actionItemBar, alpha);
michael@0 912
michael@0 913 final Tab tab = Tabs.getInstance().getSelectedTab();
michael@0 914 if (tab != null) {
michael@0 915 setButtonEnabled(backButton, canDoBack(tab));
michael@0 916 setButtonEnabled(forwardButton, canDoForward(tab));
michael@0 917
michael@0 918 // Once the editing mode is finished, we have to ensure that the
michael@0 919 // forward button slides away if necessary. This is because we might
michael@0 920 // have only disabled it (without hiding it) when the toolbar entered
michael@0 921 // editing mode.
michael@0 922 if (!isEditing()) {
michael@0 923 animateForwardButton(canDoForward(tab) ?
michael@0 924 ForwardButtonAnimation.SHOW : ForwardButtonAnimation.HIDE);
michael@0 925 }
michael@0 926 }
michael@0 927 }
michael@0 928
michael@0 929 private void setUIMode(final UIMode uiMode) {
michael@0 930 this.uiMode = uiMode;
michael@0 931 urlEditLayout.setEnabled(uiMode == UIMode.EDIT);
michael@0 932 }
michael@0 933
michael@0 934 /**
michael@0 935 * Returns whether or not the URL bar is in editing mode (url bar is expanded, hiding the new
michael@0 936 * tab button). Note that selection state is independent of editing mode.
michael@0 937 */
michael@0 938 public boolean isEditing() {
michael@0 939 return (uiMode == UIMode.EDIT);
michael@0 940 }
michael@0 941
michael@0 942 public boolean isAnimating() {
michael@0 943 return isAnimatingEntry;
michael@0 944 }
michael@0 945
michael@0 946 public void startEditing(String url, PropertyAnimator animator) {
michael@0 947 if (isEditing()) {
michael@0 948 return;
michael@0 949 }
michael@0 950
michael@0 951 urlEditLayout.setText(url != null ? url : "");
michael@0 952
michael@0 953 setUIMode(UIMode.EDIT);
michael@0 954 updateChildrenForEditing();
michael@0 955
michael@0 956 updateProgressVisibility();
michael@0 957
michael@0 958 if (startEditingListener != null) {
michael@0 959 startEditingListener.onStartEditing();
michael@0 960 }
michael@0 961
michael@0 962 final int curveTranslation = getUrlBarCurveTranslation();
michael@0 963 final int entryTranslation = getUrlBarEntryTranslation();
michael@0 964 shouldShrinkURLBar = (entryTranslation < 0);
michael@0 965
michael@0 966 if (urlBarTranslatingEdge != null) {
michael@0 967 urlBarTranslatingEdge.setVisibility(View.VISIBLE);
michael@0 968 if (shouldShrinkURLBar) {
michael@0 969 urlBarEntry.setLayoutParams(urlBarEntryShrunkenLayoutParams);
michael@0 970 }
michael@0 971 }
michael@0 972
michael@0 973 if (Build.VERSION.SDK_INT < 11) {
michael@0 974 showEditingWithoutAnimation(entryTranslation, curveTranslation);
michael@0 975 } else if (HardwareUtils.isTablet()) {
michael@0 976 // No animation.
michael@0 977 showUrlEditLayout();
michael@0 978 } else {
michael@0 979 showEditingWithPhoneAnimation(animator, entryTranslation, curveTranslation);
michael@0 980 }
michael@0 981 }
michael@0 982
michael@0 983 private void showEditingWithoutAnimation(final int entryTranslation,
michael@0 984 final int curveTranslation) {
michael@0 985 showUrlEditLayout();
michael@0 986
michael@0 987 if (urlBarTranslatingEdge != null) {
michael@0 988 ViewHelper.setTranslationX(urlBarTranslatingEdge, entryTranslation);
michael@0 989 }
michael@0 990
michael@0 991 // Prevent taps through the editing mode cancel button (bug 1001243).
michael@0 992 tabsButton.setEnabled(false);
michael@0 993
michael@0 994 ViewHelper.setTranslationX(tabsButton, curveTranslation);
michael@0 995 ViewHelper.setTranslationX(tabsCounter, curveTranslation);
michael@0 996 ViewHelper.setTranslationX(actionItemBar, curveTranslation);
michael@0 997
michael@0 998 if (hasSoftMenuButton) {
michael@0 999 // Prevent tabs through the editing mode cancel button (bug 1001243).
michael@0 1000 menuButton.setEnabled(false);
michael@0 1001
michael@0 1002 ViewHelper.setTranslationX(menuButton, curveTranslation);
michael@0 1003 ViewHelper.setTranslationX(menuIcon, curveTranslation);
michael@0 1004 }
michael@0 1005 }
michael@0 1006
michael@0 1007 private void showEditingWithPhoneAnimation(final PropertyAnimator animator,
michael@0 1008 final int entryTranslation, final int curveTranslation) {
michael@0 1009 if (isAnimatingEntry)
michael@0 1010 return;
michael@0 1011
michael@0 1012 urlDisplayLayout.prepareStartEditingAnimation();
michael@0 1013
michael@0 1014 // Slide toolbar elements.
michael@0 1015 if (urlBarTranslatingEdge != null) {
michael@0 1016 animator.attach(urlBarTranslatingEdge,
michael@0 1017 PropertyAnimator.Property.TRANSLATION_X,
michael@0 1018 entryTranslation);
michael@0 1019 }
michael@0 1020
michael@0 1021 animator.attach(tabsButton,
michael@0 1022 PropertyAnimator.Property.TRANSLATION_X,
michael@0 1023 curveTranslation);
michael@0 1024 animator.attach(tabsCounter,
michael@0 1025 PropertyAnimator.Property.TRANSLATION_X,
michael@0 1026 curveTranslation);
michael@0 1027 animator.attach(actionItemBar,
michael@0 1028 PropertyAnimator.Property.TRANSLATION_X,
michael@0 1029 curveTranslation);
michael@0 1030
michael@0 1031 if (hasSoftMenuButton) {
michael@0 1032 animator.attach(menuButton,
michael@0 1033 PropertyAnimator.Property.TRANSLATION_X,
michael@0 1034 curveTranslation);
michael@0 1035
michael@0 1036 animator.attach(menuIcon,
michael@0 1037 PropertyAnimator.Property.TRANSLATION_X,
michael@0 1038 curveTranslation);
michael@0 1039 }
michael@0 1040
michael@0 1041 showUrlEditLayout(animator);
michael@0 1042
michael@0 1043 animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
michael@0 1044 @Override
michael@0 1045 public void onPropertyAnimationStart() {
michael@0 1046 }
michael@0 1047
michael@0 1048 @Override
michael@0 1049 public void onPropertyAnimationEnd() {
michael@0 1050 isAnimatingEntry = false;
michael@0 1051 }
michael@0 1052 });
michael@0 1053
michael@0 1054 isAnimatingEntry = true;
michael@0 1055 }
michael@0 1056
michael@0 1057 /**
michael@0 1058 * Exits edit mode without updating the toolbar title.
michael@0 1059 *
michael@0 1060 * @return the url that was entered
michael@0 1061 */
michael@0 1062 public String cancelEdit() {
michael@0 1063 Telemetry.stopUISession(TelemetryContract.Session.AWESOMESCREEN);
michael@0 1064 return stopEditing();
michael@0 1065 }
michael@0 1066
michael@0 1067 /**
michael@0 1068 * Exits edit mode, updating the toolbar title with the url that was just entered.
michael@0 1069 *
michael@0 1070 * @return the url that was entered
michael@0 1071 */
michael@0 1072 public String commitEdit() {
michael@0 1073 final String url = stopEditing();
michael@0 1074 if (!TextUtils.isEmpty(url)) {
michael@0 1075 setTitle(url);
michael@0 1076 }
michael@0 1077 return url;
michael@0 1078 }
michael@0 1079
michael@0 1080 private String stopEditing() {
michael@0 1081 final String url = urlEditLayout.getText();
michael@0 1082 if (!isEditing()) {
michael@0 1083 return url;
michael@0 1084 }
michael@0 1085 setUIMode(UIMode.DISPLAY);
michael@0 1086
michael@0 1087 updateChildrenForEditing();
michael@0 1088
michael@0 1089 if (stopEditingListener != null) {
michael@0 1090 stopEditingListener.onStopEditing();
michael@0 1091 }
michael@0 1092
michael@0 1093 updateProgressVisibility();
michael@0 1094
michael@0 1095 // The animation looks cleaner if the text in the URL bar is
michael@0 1096 // not selected so clear the selection by clearing focus.
michael@0 1097 urlEditLayout.clearFocus();
michael@0 1098
michael@0 1099 if (Build.VERSION.SDK_INT < 11) {
michael@0 1100 stopEditingWithoutAnimation();
michael@0 1101 } else if (HardwareUtils.isTablet()) {
michael@0 1102 // No animation.
michael@0 1103 hideUrlEditLayout();
michael@0 1104 } else {
michael@0 1105 stopEditingWithPhoneAnimation();
michael@0 1106 }
michael@0 1107
michael@0 1108 return url;
michael@0 1109 }
michael@0 1110
michael@0 1111 private void stopEditingWithoutAnimation() {
michael@0 1112 hideUrlEditLayout();
michael@0 1113
michael@0 1114 updateTabCountAndAnimate(Tabs.getInstance().getDisplayCount());
michael@0 1115
michael@0 1116 if (urlBarTranslatingEdge != null) {
michael@0 1117 urlBarTranslatingEdge.setVisibility(View.INVISIBLE);
michael@0 1118 ViewHelper.setTranslationX(urlBarTranslatingEdge, 0);
michael@0 1119 if (shouldShrinkURLBar) {
michael@0 1120 urlBarEntry.setLayoutParams(urlBarEntryDefaultLayoutParams);
michael@0 1121 }
michael@0 1122 }
michael@0 1123
michael@0 1124 tabsButton.setEnabled(true);
michael@0 1125
michael@0 1126 ViewHelper.setTranslationX(tabsButton, 0);
michael@0 1127 ViewHelper.setTranslationX(tabsCounter, 0);
michael@0 1128 ViewHelper.setTranslationX(actionItemBar, 0);
michael@0 1129
michael@0 1130 if (hasSoftMenuButton) {
michael@0 1131 menuButton.setEnabled(true);
michael@0 1132
michael@0 1133 ViewHelper.setTranslationX(menuButton, 0);
michael@0 1134 ViewHelper.setTranslationX(menuIcon, 0);
michael@0 1135 }
michael@0 1136 }
michael@0 1137
michael@0 1138 private void stopEditingWithPhoneAnimation() {
michael@0 1139 final PropertyAnimator contentAnimator = new PropertyAnimator(250);
michael@0 1140 contentAnimator.setUseHardwareLayer(false);
michael@0 1141
michael@0 1142 // Slide the toolbar back to its original size.
michael@0 1143 if (urlBarTranslatingEdge != null) {
michael@0 1144 contentAnimator.attach(urlBarTranslatingEdge,
michael@0 1145 PropertyAnimator.Property.TRANSLATION_X,
michael@0 1146 0);
michael@0 1147 }
michael@0 1148
michael@0 1149 contentAnimator.attach(tabsButton,
michael@0 1150 PropertyAnimator.Property.TRANSLATION_X,
michael@0 1151 0);
michael@0 1152 contentAnimator.attach(tabsCounter,
michael@0 1153 PropertyAnimator.Property.TRANSLATION_X,
michael@0 1154 0);
michael@0 1155 contentAnimator.attach(actionItemBar,
michael@0 1156 PropertyAnimator.Property.TRANSLATION_X,
michael@0 1157 0);
michael@0 1158
michael@0 1159 if (hasSoftMenuButton) {
michael@0 1160 contentAnimator.attach(menuButton,
michael@0 1161 PropertyAnimator.Property.TRANSLATION_X,
michael@0 1162 0);
michael@0 1163
michael@0 1164 contentAnimator.attach(menuIcon,
michael@0 1165 PropertyAnimator.Property.TRANSLATION_X,
michael@0 1166 0);
michael@0 1167 }
michael@0 1168
michael@0 1169 hideUrlEditLayout(contentAnimator);
michael@0 1170
michael@0 1171 contentAnimator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
michael@0 1172 @Override
michael@0 1173 public void onPropertyAnimationStart() {
michael@0 1174 }
michael@0 1175
michael@0 1176 @Override
michael@0 1177 public void onPropertyAnimationEnd() {
michael@0 1178 if (urlBarTranslatingEdge != null) {
michael@0 1179 urlBarTranslatingEdge.setVisibility(View.INVISIBLE);
michael@0 1180 if (shouldShrinkURLBar) {
michael@0 1181 urlBarEntry.setLayoutParams(urlBarEntryDefaultLayoutParams);
michael@0 1182 }
michael@0 1183 }
michael@0 1184
michael@0 1185 PropertyAnimator buttonsAnimator = new PropertyAnimator(300);
michael@0 1186 urlDisplayLayout.prepareStopEditingAnimation(buttonsAnimator);
michael@0 1187 buttonsAnimator.start();
michael@0 1188
michael@0 1189 isAnimatingEntry = false;
michael@0 1190
michael@0 1191 // Trigger animation to update the tabs counter once the
michael@0 1192 // tabs button is back on screen.
michael@0 1193 updateTabCountAndAnimate(Tabs.getInstance().getDisplayCount());
michael@0 1194 }
michael@0 1195 });
michael@0 1196
michael@0 1197 isAnimatingEntry = true;
michael@0 1198 contentAnimator.start();
michael@0 1199 }
michael@0 1200
michael@0 1201 private void setButtonEnabled(ImageButton button, boolean enabled) {
michael@0 1202 final Drawable drawable = button.getDrawable();
michael@0 1203 if (drawable != null) {
michael@0 1204 // This alpha value has to be in sync with the one used
michael@0 1205 // in updateChildrenForEditing().
michael@0 1206 drawable.setAlpha(enabled ? 255 : 61);
michael@0 1207 }
michael@0 1208
michael@0 1209 button.setEnabled(enabled);
michael@0 1210 }
michael@0 1211
michael@0 1212 public void updateBackButton(Tab tab) {
michael@0 1213 setButtonEnabled(backButton, canDoBack(tab));
michael@0 1214 }
michael@0 1215
michael@0 1216 private void animateForwardButton(final ForwardButtonAnimation animation) {
michael@0 1217 // If the forward button is not visible, we must be
michael@0 1218 // in the phone UI.
michael@0 1219 if (forwardButton.getVisibility() != View.VISIBLE) {
michael@0 1220 return;
michael@0 1221 }
michael@0 1222
michael@0 1223 final boolean showing = (animation == ForwardButtonAnimation.SHOW);
michael@0 1224
michael@0 1225 // if the forward button's margin is non-zero, this means it has already
michael@0 1226 // been animated to be visible¸ and vice-versa.
michael@0 1227 MarginLayoutParams fwdParams = (MarginLayoutParams) forwardButton.getLayoutParams();
michael@0 1228 if ((fwdParams.leftMargin > defaultForwardMargin && showing) ||
michael@0 1229 (fwdParams.leftMargin == defaultForwardMargin && !showing)) {
michael@0 1230 return;
michael@0 1231 }
michael@0 1232
michael@0 1233 // We want the forward button to show immediately when switching tabs
michael@0 1234 final PropertyAnimator forwardAnim =
michael@0 1235 new PropertyAnimator(isSwitchingTabs ? 10 : FORWARD_ANIMATION_DURATION);
michael@0 1236 final int width = forwardButton.getWidth() / 2;
michael@0 1237
michael@0 1238 forwardAnim.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
michael@0 1239 @Override
michael@0 1240 public void onPropertyAnimationStart() {
michael@0 1241 if (!showing) {
michael@0 1242 // Set the margin before the transition when hiding the forward button. We
michael@0 1243 // have to do this so that the favicon isn't clipped during the transition
michael@0 1244 MarginLayoutParams layoutParams =
michael@0 1245 (MarginLayoutParams) urlDisplayLayout.getLayoutParams();
michael@0 1246 layoutParams.leftMargin = 0;
michael@0 1247
michael@0 1248 // Do the same on the URL edit container
michael@0 1249 layoutParams = (MarginLayoutParams) urlEditLayout.getLayoutParams();
michael@0 1250 layoutParams.leftMargin = 0;
michael@0 1251
michael@0 1252 requestLayout();
michael@0 1253 // Note, we already translated the favicon, site security, and text field
michael@0 1254 // in prepareForwardAnimation, so they should appear to have not moved at
michael@0 1255 // all at this point.
michael@0 1256 }
michael@0 1257 }
michael@0 1258
michael@0 1259 @Override
michael@0 1260 public void onPropertyAnimationEnd() {
michael@0 1261 if (showing) {
michael@0 1262 MarginLayoutParams layoutParams =
michael@0 1263 (MarginLayoutParams) urlDisplayLayout.getLayoutParams();
michael@0 1264 layoutParams.leftMargin = urlBarViewOffset;
michael@0 1265
michael@0 1266 layoutParams = (MarginLayoutParams) urlEditLayout.getLayoutParams();
michael@0 1267 layoutParams.leftMargin = urlBarViewOffset;
michael@0 1268 }
michael@0 1269
michael@0 1270 urlDisplayLayout.finishForwardAnimation();
michael@0 1271
michael@0 1272 MarginLayoutParams layoutParams = (MarginLayoutParams) forwardButton.getLayoutParams();
michael@0 1273 layoutParams.leftMargin = defaultForwardMargin + (showing ? width : 0);
michael@0 1274 ViewHelper.setTranslationX(forwardButton, 0);
michael@0 1275
michael@0 1276 requestLayout();
michael@0 1277 }
michael@0 1278 });
michael@0 1279
michael@0 1280 prepareForwardAnimation(forwardAnim, animation, width);
michael@0 1281 forwardAnim.start();
michael@0 1282 }
michael@0 1283
michael@0 1284 public void updateForwardButton(Tab tab) {
michael@0 1285 final boolean enabled = canDoForward(tab);
michael@0 1286 if (forwardButton.isEnabled() == enabled)
michael@0 1287 return;
michael@0 1288
michael@0 1289 // Save the state on the forward button so that we can skip animations
michael@0 1290 // when there's nothing to change
michael@0 1291 setButtonEnabled(forwardButton, enabled);
michael@0 1292 animateForwardButton(enabled ? ForwardButtonAnimation.SHOW : ForwardButtonAnimation.HIDE);
michael@0 1293 }
michael@0 1294
michael@0 1295 private void prepareForwardAnimation(PropertyAnimator anim, ForwardButtonAnimation animation, int width) {
michael@0 1296 if (animation == ForwardButtonAnimation.HIDE) {
michael@0 1297 anim.attach(forwardButton,
michael@0 1298 PropertyAnimator.Property.TRANSLATION_X,
michael@0 1299 -width);
michael@0 1300 anim.attach(forwardButton,
michael@0 1301 PropertyAnimator.Property.ALPHA,
michael@0 1302 0);
michael@0 1303
michael@0 1304 } else {
michael@0 1305 anim.attach(forwardButton,
michael@0 1306 PropertyAnimator.Property.TRANSLATION_X,
michael@0 1307 width);
michael@0 1308 anim.attach(forwardButton,
michael@0 1309 PropertyAnimator.Property.ALPHA,
michael@0 1310 1);
michael@0 1311 }
michael@0 1312
michael@0 1313 urlDisplayLayout.prepareForwardAnimation(anim, animation, width);
michael@0 1314 }
michael@0 1315
michael@0 1316 @Override
michael@0 1317 public boolean addActionItem(View actionItem) {
michael@0 1318 actionItemBar.addView(actionItem);
michael@0 1319 return true;
michael@0 1320 }
michael@0 1321
michael@0 1322 @Override
michael@0 1323 public void removeActionItem(View actionItem) {
michael@0 1324 actionItemBar.removeView(actionItem);
michael@0 1325 }
michael@0 1326
michael@0 1327 @Override
michael@0 1328 public void setPrivateMode(boolean isPrivate) {
michael@0 1329 super.setPrivateMode(isPrivate);
michael@0 1330
michael@0 1331 tabsButton.setPrivateMode(isPrivate);
michael@0 1332 menuButton.setPrivateMode(isPrivate);
michael@0 1333 menuIcon.setPrivateMode(isPrivate);
michael@0 1334 urlEditLayout.setPrivateMode(isPrivate);
michael@0 1335
michael@0 1336 if (backButton instanceof BackButton) {
michael@0 1337 ((BackButton) backButton).setPrivateMode(isPrivate);
michael@0 1338 }
michael@0 1339
michael@0 1340 if (forwardButton instanceof ForwardButton) {
michael@0 1341 ((ForwardButton) forwardButton).setPrivateMode(isPrivate);
michael@0 1342 }
michael@0 1343 }
michael@0 1344
michael@0 1345 public void show() {
michael@0 1346 setVisibility(View.VISIBLE);
michael@0 1347 }
michael@0 1348
michael@0 1349 public void hide() {
michael@0 1350 setVisibility(View.GONE);
michael@0 1351 }
michael@0 1352
michael@0 1353 public View getDoorHangerAnchor() {
michael@0 1354 return urlDisplayLayout.getDoorHangerAnchor();
michael@0 1355 }
michael@0 1356
michael@0 1357 public void onDestroy() {
michael@0 1358 Tabs.unregisterOnTabsChangedListener(this);
michael@0 1359
michael@0 1360 unregisterEventListener("Reader:Click");
michael@0 1361 unregisterEventListener("Reader:LongClick");
michael@0 1362 }
michael@0 1363
michael@0 1364 public boolean openOptionsMenu() {
michael@0 1365 if (!hasSoftMenuButton) {
michael@0 1366 return false;
michael@0 1367 }
michael@0 1368
michael@0 1369 // Initialize the popup.
michael@0 1370 if (menuPopup == null) {
michael@0 1371 View panel = activity.getMenuPanel();
michael@0 1372 menuPopup = new MenuPopup(activity);
michael@0 1373 menuPopup.setPanelView(panel);
michael@0 1374
michael@0 1375 menuPopup.setOnDismissListener(new PopupWindow.OnDismissListener() {
michael@0 1376 @Override
michael@0 1377 public void onDismiss() {
michael@0 1378 activity.onOptionsMenuClosed(null);
michael@0 1379 }
michael@0 1380 });
michael@0 1381 }
michael@0 1382
michael@0 1383 GeckoAppShell.getGeckoInterface().invalidateOptionsMenu();
michael@0 1384 if (!menuPopup.isShowing()) {
michael@0 1385 menuPopup.showAsDropDown(menuButton);
michael@0 1386 }
michael@0 1387
michael@0 1388 return true;
michael@0 1389 }
michael@0 1390
michael@0 1391 public boolean closeOptionsMenu() {
michael@0 1392 if (!hasSoftMenuButton) {
michael@0 1393 return false;
michael@0 1394 }
michael@0 1395
michael@0 1396 if (menuPopup != null && menuPopup.isShowing()) {
michael@0 1397 menuPopup.dismiss();
michael@0 1398 }
michael@0 1399
michael@0 1400 return true;
michael@0 1401 }
michael@0 1402
michael@0 1403 private void registerEventListener(String event) {
michael@0 1404 GeckoAppShell.getEventDispatcher().registerEventListener(event, this);
michael@0 1405 }
michael@0 1406
michael@0 1407 private void unregisterEventListener(String event) {
michael@0 1408 GeckoAppShell.getEventDispatcher().unregisterEventListener(event, this);
michael@0 1409 }
michael@0 1410
michael@0 1411 @Override
michael@0 1412 public void handleMessage(String event, JSONObject message) {
michael@0 1413 Log.d(LOGTAG, "handleMessage: " + event);
michael@0 1414 if (event.equals("Reader:Click")) {
michael@0 1415 Tab tab = Tabs.getInstance().getSelectedTab();
michael@0 1416 if (tab != null) {
michael@0 1417 tab.toggleReaderMode();
michael@0 1418 }
michael@0 1419 } else if (event.equals("Reader:LongClick")) {
michael@0 1420 Tab tab = Tabs.getInstance().getSelectedTab();
michael@0 1421 if (tab != null) {
michael@0 1422 tab.addToReadingList();
michael@0 1423 }
michael@0 1424 }
michael@0 1425 }
michael@0 1426
michael@0 1427 @Override
michael@0 1428 public void onLightweightThemeChanged() {
michael@0 1429 Drawable drawable = theme.getDrawable(this);
michael@0 1430 if (drawable == null)
michael@0 1431 return;
michael@0 1432
michael@0 1433 StateListDrawable stateList = new StateListDrawable();
michael@0 1434 stateList.addState(PRIVATE_STATE_SET, getColorDrawable(R.color.background_private));
michael@0 1435 stateList.addState(EMPTY_STATE_SET, drawable);
michael@0 1436
michael@0 1437 setBackgroundDrawable(stateList);
michael@0 1438
michael@0 1439 if (editCancel != null) {
michael@0 1440 editCancel.onLightweightThemeChanged();
michael@0 1441 }
michael@0 1442 }
michael@0 1443
michael@0 1444 @Override
michael@0 1445 public void onLightweightThemeReset() {
michael@0 1446 setBackgroundResource(R.drawable.url_bar_bg);
michael@0 1447 if (editCancel != null) {
michael@0 1448 editCancel.onLightweightThemeReset();
michael@0 1449 }
michael@0 1450 }
michael@0 1451 }

mercurial