1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/toolbar/BrowserToolbar.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1451 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko.toolbar; 1.10 + 1.11 +import java.util.ArrayList; 1.12 +import java.util.Arrays; 1.13 +import java.util.EnumSet; 1.14 +import java.util.List; 1.15 + 1.16 +import org.json.JSONObject; 1.17 +import org.mozilla.gecko.BrowserApp; 1.18 +import org.mozilla.gecko.GeckoAppShell; 1.19 +import org.mozilla.gecko.GeckoApplication; 1.20 +import org.mozilla.gecko.GeckoProfile; 1.21 +import org.mozilla.gecko.LightweightTheme; 1.22 +import org.mozilla.gecko.R; 1.23 +import org.mozilla.gecko.Tab; 1.24 +import org.mozilla.gecko.Tabs; 1.25 +import org.mozilla.gecko.Telemetry; 1.26 +import org.mozilla.gecko.TelemetryContract; 1.27 +import org.mozilla.gecko.animation.PropertyAnimator; 1.28 +import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener; 1.29 +import org.mozilla.gecko.animation.ViewHelper; 1.30 +import org.mozilla.gecko.menu.GeckoMenu; 1.31 +import org.mozilla.gecko.menu.MenuPopup; 1.32 +import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnStopListener; 1.33 +import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnTitleChangeListener; 1.34 +import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.UpdateFlags; 1.35 +import org.mozilla.gecko.util.Clipboard; 1.36 +import org.mozilla.gecko.util.GeckoEventListener; 1.37 +import org.mozilla.gecko.util.HardwareUtils; 1.38 +import org.mozilla.gecko.util.MenuUtils; 1.39 +import org.mozilla.gecko.widget.ThemedImageButton; 1.40 +import org.mozilla.gecko.widget.ThemedImageView; 1.41 +import org.mozilla.gecko.widget.ThemedRelativeLayout; 1.42 + 1.43 +import android.content.Context; 1.44 +import android.content.res.Resources; 1.45 +import android.graphics.Bitmap; 1.46 +import android.graphics.drawable.BitmapDrawable; 1.47 +import android.graphics.drawable.Drawable; 1.48 +import android.graphics.drawable.StateListDrawable; 1.49 +import android.os.Build; 1.50 +import android.text.TextUtils; 1.51 +import android.util.AttributeSet; 1.52 +import android.util.Log; 1.53 +import android.view.ContextMenu; 1.54 +import android.view.KeyEvent; 1.55 +import android.view.LayoutInflater; 1.56 +import android.view.MenuInflater; 1.57 +import android.view.MotionEvent; 1.58 +import android.view.View; 1.59 +import android.view.ViewGroup; 1.60 +import android.view.animation.AccelerateInterpolator; 1.61 +import android.view.animation.Interpolator; 1.62 +import android.view.inputmethod.InputMethodManager; 1.63 +import android.widget.Button; 1.64 +import android.widget.ImageButton; 1.65 +import android.widget.ImageView; 1.66 +import android.widget.LinearLayout; 1.67 +import android.widget.PopupWindow; 1.68 +import android.widget.RelativeLayout; 1.69 + 1.70 +/** 1.71 +* {@code BrowserToolbar} is single entry point for users of the toolbar 1.72 +* subsystem i.e. this should be the only import outside the 'toolbar' 1.73 +* package. 1.74 +* 1.75 +* {@code BrowserToolbar} serves at the single event bus for all 1.76 +* sub-components in the toolbar. It tracks tab events and gecko messages 1.77 +* and update the state of its inner components accordingly. 1.78 +* 1.79 +* It has two states, display and edit, which are controlled by 1.80 +* ToolbarEditLayout and ToolbarDisplayLayout. In display state, the toolbar 1.81 +* displays the current state for the selected tab. In edit state, it shows 1.82 +* a text entry for searching bookmarks/history. {@code BrowserToolbar} 1.83 +* provides public API to enter, cancel, and commit the edit state as well 1.84 +* as a set of listeners to allow {@code BrowserToolbar} users to react 1.85 +* to state changes accordingly. 1.86 +*/ 1.87 +public class BrowserToolbar extends ThemedRelativeLayout 1.88 + implements Tabs.OnTabsChangedListener, 1.89 + GeckoMenu.ActionItemBarPresenter, 1.90 + GeckoEventListener { 1.91 + private static final String LOGTAG = "GeckoToolbar"; 1.92 + 1.93 + public interface OnActivateListener { 1.94 + public void onActivate(); 1.95 + } 1.96 + 1.97 + public interface OnCommitListener { 1.98 + public void onCommit(); 1.99 + } 1.100 + 1.101 + public interface OnDismissListener { 1.102 + public void onDismiss(); 1.103 + } 1.104 + 1.105 + public interface OnFilterListener { 1.106 + public void onFilter(String searchText, AutocompleteHandler handler); 1.107 + } 1.108 + 1.109 + public interface OnStartEditingListener { 1.110 + public void onStartEditing(); 1.111 + } 1.112 + 1.113 + public interface OnStopEditingListener { 1.114 + public void onStopEditing(); 1.115 + } 1.116 + 1.117 + private enum UIMode { 1.118 + EDIT, 1.119 + DISPLAY 1.120 + } 1.121 + 1.122 + enum ForwardButtonAnimation { 1.123 + SHOW, 1.124 + HIDE 1.125 + } 1.126 + 1.127 + private ToolbarDisplayLayout urlDisplayLayout; 1.128 + private ToolbarEditLayout urlEditLayout; 1.129 + private View urlBarEntry; 1.130 + private RelativeLayout.LayoutParams urlBarEntryDefaultLayoutParams; 1.131 + private RelativeLayout.LayoutParams urlBarEntryShrunkenLayoutParams; 1.132 + private ImageView urlBarTranslatingEdge; 1.133 + private boolean isSwitchingTabs; 1.134 + private ShapedButton tabsButton; 1.135 + private ImageButton backButton; 1.136 + private ImageButton forwardButton; 1.137 + 1.138 + private ToolbarProgressView progressBar; 1.139 + private TabCounter tabsCounter; 1.140 + private ThemedImageButton menuButton; 1.141 + private ThemedImageView menuIcon; 1.142 + private LinearLayout actionItemBar; 1.143 + private MenuPopup menuPopup; 1.144 + private List<View> focusOrder; 1.145 + 1.146 + private final ThemedImageView editCancel; 1.147 + 1.148 + private boolean shouldShrinkURLBar = false; 1.149 + 1.150 + private OnActivateListener activateListener; 1.151 + private OnFocusChangeListener focusChangeListener; 1.152 + private OnStartEditingListener startEditingListener; 1.153 + private OnStopEditingListener stopEditingListener; 1.154 + 1.155 + private final BrowserApp activity; 1.156 + private boolean hasSoftMenuButton; 1.157 + 1.158 + private UIMode uiMode; 1.159 + private boolean isAnimatingEntry; 1.160 + 1.161 + private int urlBarViewOffset; 1.162 + private int defaultForwardMargin; 1.163 + 1.164 + private static final Interpolator buttonsInterpolator = new AccelerateInterpolator(); 1.165 + 1.166 + private static final int FORWARD_ANIMATION_DURATION = 450; 1.167 + 1.168 + private final LightweightTheme theme; 1.169 + 1.170 + public BrowserToolbar(Context context) { 1.171 + this(context, null); 1.172 + } 1.173 + 1.174 + public BrowserToolbar(Context context, AttributeSet attrs) { 1.175 + super(context, attrs); 1.176 + theme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme(); 1.177 + 1.178 + // BrowserToolbar is attached to BrowserApp only. 1.179 + activity = (BrowserApp) context; 1.180 + 1.181 + // Inflate the content. 1.182 + LayoutInflater.from(context).inflate(R.layout.browser_toolbar, this); 1.183 + 1.184 + Tabs.registerOnTabsChangedListener(this); 1.185 + isSwitchingTabs = true; 1.186 + isAnimatingEntry = false; 1.187 + 1.188 + registerEventListener("Reader:Click"); 1.189 + registerEventListener("Reader:LongClick"); 1.190 + 1.191 + final Resources res = getResources(); 1.192 + urlBarViewOffset = res.getDimensionPixelSize(R.dimen.url_bar_offset_left); 1.193 + defaultForwardMargin = res.getDimensionPixelSize(R.dimen.forward_default_offset); 1.194 + urlDisplayLayout = (ToolbarDisplayLayout) findViewById(R.id.display_layout); 1.195 + urlBarEntry = findViewById(R.id.url_bar_entry); 1.196 + urlEditLayout = (ToolbarEditLayout) findViewById(R.id.edit_layout); 1.197 + 1.198 + urlBarEntryDefaultLayoutParams = (RelativeLayout.LayoutParams) urlBarEntry.getLayoutParams(); 1.199 + // API level 19 adds a RelativeLayout.LayoutParams copy constructor, so we explicitly cast 1.200 + // to ViewGroup.MarginLayoutParams to ensure consistency across platforms. 1.201 + urlBarEntryShrunkenLayoutParams = new RelativeLayout.LayoutParams( 1.202 + (ViewGroup.MarginLayoutParams) urlBarEntryDefaultLayoutParams); 1.203 + urlBarEntryShrunkenLayoutParams.addRule(RelativeLayout.ALIGN_RIGHT, R.id.edit_layout); 1.204 + urlBarEntryShrunkenLayoutParams.rightMargin = 0; 1.205 + 1.206 + // This will clip the translating edge's image at 60% of its width 1.207 + urlBarTranslatingEdge = (ImageView) findViewById(R.id.url_bar_translating_edge); 1.208 + if (urlBarTranslatingEdge != null) { 1.209 + urlBarTranslatingEdge.getDrawable().setLevel(6000); 1.210 + } 1.211 + 1.212 + tabsButton = (ShapedButton) findViewById(R.id.tabs); 1.213 + tabsCounter = (TabCounter) findViewById(R.id.tabs_counter); 1.214 + if (Build.VERSION.SDK_INT >= 11) { 1.215 + tabsCounter.setLayerType(View.LAYER_TYPE_SOFTWARE, null); 1.216 + } 1.217 + 1.218 + backButton = (ImageButton) findViewById(R.id.back); 1.219 + setButtonEnabled(backButton, false); 1.220 + forwardButton = (ImageButton) findViewById(R.id.forward); 1.221 + setButtonEnabled(forwardButton, false); 1.222 + 1.223 + menuButton = (ThemedImageButton) findViewById(R.id.menu); 1.224 + menuIcon = (ThemedImageView) findViewById(R.id.menu_icon); 1.225 + actionItemBar = (LinearLayout) findViewById(R.id.menu_items); 1.226 + hasSoftMenuButton = !HardwareUtils.hasMenuButton(); 1.227 + 1.228 + editCancel = (ThemedImageView) findViewById(R.id.edit_cancel); 1.229 + 1.230 + // We use different layouts on phones and tablets, so adjust the focus 1.231 + // order appropriately. 1.232 + focusOrder = new ArrayList<View>(); 1.233 + if (HardwareUtils.isTablet()) { 1.234 + focusOrder.addAll(Arrays.asList(tabsButton, backButton, forwardButton, this)); 1.235 + focusOrder.addAll(urlDisplayLayout.getFocusOrder()); 1.236 + focusOrder.addAll(Arrays.asList(actionItemBar, menuButton)); 1.237 + } else { 1.238 + focusOrder.add(this); 1.239 + focusOrder.addAll(urlDisplayLayout.getFocusOrder()); 1.240 + focusOrder.addAll(Arrays.asList(tabsButton, menuButton)); 1.241 + } 1.242 + 1.243 + setUIMode(UIMode.DISPLAY); 1.244 + } 1.245 + 1.246 + @Override 1.247 + public void onAttachedToWindow() { 1.248 + super.onAttachedToWindow(); 1.249 + 1.250 + setOnClickListener(new Button.OnClickListener() { 1.251 + @Override 1.252 + public void onClick(View v) { 1.253 + if (activateListener != null) { 1.254 + activateListener.onActivate(); 1.255 + } 1.256 + } 1.257 + }); 1.258 + 1.259 + setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() { 1.260 + @Override 1.261 + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { 1.262 + // We don't the context menu while editing 1.263 + if (isEditing()) { 1.264 + return; 1.265 + } 1.266 + 1.267 + // NOTE: Use MenuUtils.safeSetVisible because some actions might 1.268 + // be on the Page menu 1.269 + 1.270 + MenuInflater inflater = activity.getMenuInflater(); 1.271 + inflater.inflate(R.menu.titlebar_contextmenu, menu); 1.272 + 1.273 + String clipboard = Clipboard.getText(); 1.274 + if (TextUtils.isEmpty(clipboard)) { 1.275 + menu.findItem(R.id.pasteandgo).setVisible(false); 1.276 + menu.findItem(R.id.paste).setVisible(false); 1.277 + } 1.278 + 1.279 + Tab tab = Tabs.getInstance().getSelectedTab(); 1.280 + if (tab != null) { 1.281 + String url = tab.getURL(); 1.282 + if (url == null) { 1.283 + menu.findItem(R.id.copyurl).setVisible(false); 1.284 + menu.findItem(R.id.add_to_launcher).setVisible(false); 1.285 + MenuUtils.safeSetVisible(menu, R.id.share, false); 1.286 + } 1.287 + 1.288 + MenuUtils.safeSetVisible(menu, R.id.subscribe, tab.hasFeeds()); 1.289 + MenuUtils.safeSetVisible(menu, R.id.add_search_engine, tab.hasOpenSearch()); 1.290 + } else { 1.291 + // if there is no tab, remove anything tab dependent 1.292 + menu.findItem(R.id.copyurl).setVisible(false); 1.293 + menu.findItem(R.id.add_to_launcher).setVisible(false); 1.294 + MenuUtils.safeSetVisible(menu, R.id.share, false); 1.295 + MenuUtils.safeSetVisible(menu, R.id.subscribe, false); 1.296 + MenuUtils.safeSetVisible(menu, R.id.add_search_engine, false); 1.297 + } 1.298 + 1.299 + MenuUtils.safeSetVisible(menu, R.id.share, !GeckoProfile.get(getContext()).inGuestMode()); 1.300 + } 1.301 + }); 1.302 + 1.303 + urlDisplayLayout.setOnStopListener(new OnStopListener() { 1.304 + @Override 1.305 + public Tab onStop() { 1.306 + final Tab tab = Tabs.getInstance().getSelectedTab(); 1.307 + if (tab != null) { 1.308 + tab.doStop(); 1.309 + return tab; 1.310 + } 1.311 + 1.312 + return null; 1.313 + } 1.314 + }); 1.315 + 1.316 + urlDisplayLayout.setOnTitleChangeListener(new OnTitleChangeListener() { 1.317 + @Override 1.318 + public void onTitleChange(CharSequence title) { 1.319 + final String contentDescription; 1.320 + if (title != null) { 1.321 + contentDescription = title.toString(); 1.322 + } else { 1.323 + contentDescription = activity.getString(R.string.url_bar_default_text); 1.324 + } 1.325 + 1.326 + // The title and content description should 1.327 + // always be sync. 1.328 + setContentDescription(contentDescription); 1.329 + } 1.330 + }); 1.331 + 1.332 + urlEditLayout.setOnFocusChangeListener(new View.OnFocusChangeListener() { 1.333 + @Override 1.334 + public void onFocusChange(View v, boolean hasFocus) { 1.335 + // This will select the url bar when entering editing mode. 1.336 + setSelected(hasFocus); 1.337 + if (focusChangeListener != null) { 1.338 + focusChangeListener.onFocusChange(v, hasFocus); 1.339 + } 1.340 + } 1.341 + }); 1.342 + 1.343 + tabsButton.setOnClickListener(new Button.OnClickListener() { 1.344 + @Override 1.345 + public void onClick(View v) { 1.346 + toggleTabs(); 1.347 + } 1.348 + }); 1.349 + tabsButton.setImageLevel(0); 1.350 + 1.351 + backButton.setOnClickListener(new Button.OnClickListener() { 1.352 + @Override 1.353 + public void onClick(View view) { 1.354 + Tabs.getInstance().getSelectedTab().doBack(); 1.355 + } 1.356 + }); 1.357 + backButton.setOnLongClickListener(new Button.OnLongClickListener() { 1.358 + @Override 1.359 + public boolean onLongClick(View view) { 1.360 + return Tabs.getInstance().getSelectedTab().showBackHistory(); 1.361 + } 1.362 + }); 1.363 + 1.364 + forwardButton.setOnClickListener(new Button.OnClickListener() { 1.365 + @Override 1.366 + public void onClick(View view) { 1.367 + Tabs.getInstance().getSelectedTab().doForward(); 1.368 + } 1.369 + }); 1.370 + forwardButton.setOnLongClickListener(new Button.OnLongClickListener() { 1.371 + @Override 1.372 + public boolean onLongClick(View view) { 1.373 + return Tabs.getInstance().getSelectedTab().showForwardHistory(); 1.374 + } 1.375 + }); 1.376 + 1.377 + if (editCancel != null) { 1.378 + editCancel.setOnClickListener(new OnClickListener() { 1.379 + @Override 1.380 + public void onClick(View v) { 1.381 + // If we exit editing mode during the animation, 1.382 + // we're put into an inconsistent state (bug 1017276). 1.383 + if (!isAnimatingEntry) { 1.384 + Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, 1.385 + TelemetryContract.Method.ACTIONBAR, 1.386 + getResources().getResourceEntryName(editCancel.getId())); 1.387 + cancelEdit(); 1.388 + } 1.389 + } 1.390 + }); 1.391 + } 1.392 + 1.393 + if (hasSoftMenuButton) { 1.394 + menuButton.setVisibility(View.VISIBLE); 1.395 + menuIcon.setVisibility(View.VISIBLE); 1.396 + 1.397 + menuButton.setOnClickListener(new Button.OnClickListener() { 1.398 + @Override 1.399 + public void onClick(View view) { 1.400 + activity.openOptionsMenu(); 1.401 + } 1.402 + }); 1.403 + } 1.404 + } 1.405 + 1.406 + public void setProgressBar(ToolbarProgressView progressBar) { 1.407 + this.progressBar = progressBar; 1.408 + } 1.409 + 1.410 + public void refresh() { 1.411 + urlDisplayLayout.dismissSiteIdentityPopup(); 1.412 + } 1.413 + 1.414 + public boolean onBackPressed() { 1.415 + // If we exit editing mode during the animation, 1.416 + // we're put into an inconsistent state (bug 1017276). 1.417 + if (isEditing() && !isAnimatingEntry) { 1.418 + Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, 1.419 + TelemetryContract.Method.BACK); 1.420 + cancelEdit(); 1.421 + return true; 1.422 + } 1.423 + 1.424 + return urlDisplayLayout.dismissSiteIdentityPopup(); 1.425 + } 1.426 + 1.427 + @Override 1.428 + public boolean onTouchEvent(MotionEvent event) { 1.429 + // If the motion event has occured below the toolbar (due to the scroll 1.430 + // offset), let it pass through to the page. 1.431 + if (event != null && event.getY() > getHeight() + ViewHelper.getTranslationY(this)) { 1.432 + return false; 1.433 + } 1.434 + 1.435 + return super.onTouchEvent(event); 1.436 + } 1.437 + 1.438 + @Override 1.439 + protected void onSizeChanged(int w, int h, int oldw, int oldh) { 1.440 + super.onSizeChanged(w, h, oldw, oldh); 1.441 + 1.442 + if (h != oldh) { 1.443 + // Post this to happen outside of onSizeChanged, as this may cause 1.444 + // a layout change and relayouts within a layout change don't work. 1.445 + post(new Runnable() { 1.446 + @Override 1.447 + public void run() { 1.448 + activity.refreshToolbarHeight(); 1.449 + } 1.450 + }); 1.451 + } 1.452 + } 1.453 + 1.454 + @Override 1.455 + public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { 1.456 + Log.d(LOGTAG, "onTabChanged: " + msg); 1.457 + final Tabs tabs = Tabs.getInstance(); 1.458 + 1.459 + // These conditions are split into three phases: 1.460 + // * Always do first 1.461 + // * Handling specific to the selected tab 1.462 + // * Always do afterwards. 1.463 + 1.464 + switch (msg) { 1.465 + case ADDED: 1.466 + case CLOSED: 1.467 + updateTabCount(tabs.getDisplayCount()); 1.468 + break; 1.469 + case RESTORED: 1.470 + // TabCount fixup after OOM 1.471 + case SELECTED: 1.472 + urlDisplayLayout.dismissSiteIdentityPopup(); 1.473 + updateTabCount(tabs.getDisplayCount()); 1.474 + isSwitchingTabs = true; 1.475 + break; 1.476 + } 1.477 + 1.478 + if (tabs.isSelectedTab(tab)) { 1.479 + final EnumSet<UpdateFlags> flags = EnumSet.noneOf(UpdateFlags.class); 1.480 + 1.481 + // Progress-related handling 1.482 + switch (msg) { 1.483 + case START: 1.484 + updateProgressVisibility(tab, Tab.LOAD_PROGRESS_INIT); 1.485 + // Fall through. 1.486 + case ADDED: 1.487 + case LOCATION_CHANGE: 1.488 + case LOAD_ERROR: 1.489 + case LOADED: 1.490 + case STOP: 1.491 + flags.add(UpdateFlags.PROGRESS); 1.492 + if (progressBar.getVisibility() == View.VISIBLE) { 1.493 + progressBar.animateProgress(tab.getLoadProgress()); 1.494 + } 1.495 + break; 1.496 + 1.497 + case SELECTED: 1.498 + flags.add(UpdateFlags.PROGRESS); 1.499 + updateProgressVisibility(); 1.500 + break; 1.501 + } 1.502 + 1.503 + switch (msg) { 1.504 + case STOP: 1.505 + // Reset the title in case we haven't navigated 1.506 + // to a new page yet. 1.507 + flags.add(UpdateFlags.TITLE); 1.508 + // Fall through. 1.509 + case START: 1.510 + case CLOSED: 1.511 + case ADDED: 1.512 + updateBackButton(tab); 1.513 + updateForwardButton(tab); 1.514 + break; 1.515 + 1.516 + case SELECTED: 1.517 + flags.add(UpdateFlags.PRIVATE_MODE); 1.518 + setPrivateMode(tab.isPrivate()); 1.519 + // Fall through. 1.520 + case LOAD_ERROR: 1.521 + flags.add(UpdateFlags.TITLE); 1.522 + // Fall through. 1.523 + case LOCATION_CHANGE: 1.524 + // A successful location change will cause Tab to notify 1.525 + // us of a title change, so we don't update the title here. 1.526 + flags.add(UpdateFlags.FAVICON); 1.527 + flags.add(UpdateFlags.SITE_IDENTITY); 1.528 + 1.529 + updateBackButton(tab); 1.530 + updateForwardButton(tab); 1.531 + break; 1.532 + 1.533 + case TITLE: 1.534 + flags.add(UpdateFlags.TITLE); 1.535 + break; 1.536 + 1.537 + case FAVICON: 1.538 + flags.add(UpdateFlags.FAVICON); 1.539 + break; 1.540 + 1.541 + case SECURITY_CHANGE: 1.542 + flags.add(UpdateFlags.SITE_IDENTITY); 1.543 + break; 1.544 + } 1.545 + 1.546 + if (!flags.isEmpty()) { 1.547 + updateDisplayLayout(tab, flags); 1.548 + } 1.549 + } 1.550 + 1.551 + switch (msg) { 1.552 + case SELECTED: 1.553 + case LOAD_ERROR: 1.554 + case LOCATION_CHANGE: 1.555 + isSwitchingTabs = false; 1.556 + } 1.557 + } 1.558 + 1.559 + private void updateProgressVisibility() { 1.560 + final Tab selectedTab = Tabs.getInstance().getSelectedTab(); 1.561 + updateProgressVisibility(selectedTab, selectedTab.getLoadProgress()); 1.562 + } 1.563 + 1.564 + private void updateProgressVisibility(Tab selectedTab, int progress) { 1.565 + if (!isEditing() && selectedTab.getState() == Tab.STATE_LOADING) { 1.566 + progressBar.setProgress(progress); 1.567 + progressBar.setVisibility(View.VISIBLE); 1.568 + } else { 1.569 + progressBar.setVisibility(View.GONE); 1.570 + } 1.571 + } 1.572 + 1.573 + private boolean isVisible() { 1.574 + return ViewHelper.getTranslationY(this) == 0; 1.575 + } 1.576 + 1.577 + @Override 1.578 + public void setNextFocusDownId(int nextId) { 1.579 + super.setNextFocusDownId(nextId); 1.580 + tabsButton.setNextFocusDownId(nextId); 1.581 + backButton.setNextFocusDownId(nextId); 1.582 + forwardButton.setNextFocusDownId(nextId); 1.583 + urlDisplayLayout.setNextFocusDownId(nextId); 1.584 + menuButton.setNextFocusDownId(nextId); 1.585 + } 1.586 + 1.587 + private int getUrlBarEntryTranslation() { 1.588 + if (editCancel == null) { 1.589 + // We are on tablet, and there is no animation so return a translation of 0. 1.590 + return 0; 1.591 + } 1.592 + 1.593 + // Find the distance from the right-edge of the url bar (where we're translating from) to 1.594 + // the left-edge of the cancel button (where we're translating to; note that the cancel 1.595 + // button must be laid out, i.e. not View.GONE). 1.596 + final LayoutParams lp = (LayoutParams) urlEditLayout.getLayoutParams(); 1.597 + return editCancel.getLeft() - lp.leftMargin - urlBarEntry.getRight(); 1.598 + } 1.599 + 1.600 + private int getUrlBarCurveTranslation() { 1.601 + return getWidth() - tabsButton.getLeft(); 1.602 + } 1.603 + 1.604 + private boolean canDoBack(Tab tab) { 1.605 + return (tab.canDoBack() && !isEditing()); 1.606 + } 1.607 + 1.608 + private boolean canDoForward(Tab tab) { 1.609 + return (tab.canDoForward() && !isEditing()); 1.610 + } 1.611 + 1.612 + private void addTab() { 1.613 + activity.addTab(); 1.614 + } 1.615 + 1.616 + private void toggleTabs() { 1.617 + if (activity.areTabsShown()) { 1.618 + if (activity.hasTabsSideBar()) 1.619 + activity.hideTabs(); 1.620 + } else { 1.621 + // hide the virtual keyboard 1.622 + InputMethodManager imm = 1.623 + (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); 1.624 + imm.hideSoftInputFromWindow(tabsButton.getWindowToken(), 0); 1.625 + 1.626 + Tab tab = Tabs.getInstance().getSelectedTab(); 1.627 + if (tab != null) { 1.628 + if (!tab.isPrivate()) 1.629 + activity.showNormalTabs(); 1.630 + else 1.631 + activity.showPrivateTabs(); 1.632 + } 1.633 + } 1.634 + } 1.635 + 1.636 + private void updateTabCountAndAnimate(int count) { 1.637 + // Don't animate if the toolbar is hidden. 1.638 + if (!isVisible()) { 1.639 + updateTabCount(count); 1.640 + return; 1.641 + } 1.642 + 1.643 + // If toolbar is in edit mode on a phone, this means the entry is expanded 1.644 + // and the tabs button is translated offscreen. Don't trigger tabs counter 1.645 + // updates until the tabs button is back on screen. 1.646 + // See stopEditing() 1.647 + if (!isEditing() || HardwareUtils.isTablet()) { 1.648 + tabsCounter.setCount(count); 1.649 + 1.650 + tabsButton.setContentDescription((count > 1) ? 1.651 + activity.getString(R.string.num_tabs, count) : 1.652 + activity.getString(R.string.one_tab)); 1.653 + } 1.654 + } 1.655 + 1.656 + private void updateTabCount(int count) { 1.657 + // If toolbar is in edit mode on a phone, this means the entry is expanded 1.658 + // and the tabs button is translated offscreen. Don't trigger tabs counter 1.659 + // updates until the tabs button is back on screen. 1.660 + // See stopEditing() 1.661 + if (isEditing() && !HardwareUtils.isTablet()) { 1.662 + return; 1.663 + } 1.664 + 1.665 + // Set TabCounter based on visibility 1.666 + if (isVisible() && ViewHelper.getAlpha(tabsCounter) != 0 && !isEditing()) { 1.667 + tabsCounter.setCountWithAnimation(count); 1.668 + } else { 1.669 + tabsCounter.setCount(count); 1.670 + } 1.671 + 1.672 + // Update A11y information 1.673 + tabsButton.setContentDescription((count > 1) ? 1.674 + activity.getString(R.string.num_tabs, count) : 1.675 + activity.getString(R.string.one_tab)); 1.676 + } 1.677 + 1.678 + private void updateDisplayLayout(Tab tab, EnumSet<UpdateFlags> flags) { 1.679 + if (isSwitchingTabs) { 1.680 + flags.add(UpdateFlags.DISABLE_ANIMATIONS); 1.681 + } 1.682 + 1.683 + urlDisplayLayout.updateFromTab(tab, flags); 1.684 + 1.685 + if (flags.contains(UpdateFlags.TITLE)) { 1.686 + if (!isEditing()) { 1.687 + urlEditLayout.setText(tab.getURL()); 1.688 + } 1.689 + } 1.690 + 1.691 + if (flags.contains(UpdateFlags.PROGRESS)) { 1.692 + updateFocusOrder(); 1.693 + } 1.694 + } 1.695 + 1.696 + private void updateFocusOrder() { 1.697 + View prevView = null; 1.698 + 1.699 + // If the element that has focus becomes disabled or invisible, focus 1.700 + // is given to the URL bar. 1.701 + boolean needsNewFocus = false; 1.702 + 1.703 + for (View view : focusOrder) { 1.704 + if (view.getVisibility() != View.VISIBLE || !view.isEnabled()) { 1.705 + if (view.hasFocus()) { 1.706 + needsNewFocus = true; 1.707 + } 1.708 + continue; 1.709 + } 1.710 + 1.711 + if (view == actionItemBar) { 1.712 + final int childCount = actionItemBar.getChildCount(); 1.713 + for (int child = 0; child < childCount; child++) { 1.714 + View childView = actionItemBar.getChildAt(child); 1.715 + if (prevView != null) { 1.716 + childView.setNextFocusLeftId(prevView.getId()); 1.717 + prevView.setNextFocusRightId(childView.getId()); 1.718 + } 1.719 + prevView = childView; 1.720 + } 1.721 + } else { 1.722 + if (prevView != null) { 1.723 + view.setNextFocusLeftId(prevView.getId()); 1.724 + prevView.setNextFocusRightId(view.getId()); 1.725 + } 1.726 + prevView = view; 1.727 + } 1.728 + } 1.729 + 1.730 + if (needsNewFocus) { 1.731 + requestFocus(); 1.732 + } 1.733 + } 1.734 + 1.735 + public void onEditSuggestion(String suggestion) { 1.736 + if (!isEditing()) { 1.737 + return; 1.738 + } 1.739 + 1.740 + urlEditLayout.onEditSuggestion(suggestion); 1.741 + } 1.742 + 1.743 + public void setTitle(CharSequence title) { 1.744 + urlDisplayLayout.setTitle(title); 1.745 + } 1.746 + 1.747 + public void prepareTabsAnimation(PropertyAnimator animator, boolean tabsAreShown) { 1.748 + if (!tabsAreShown) { 1.749 + PropertyAnimator buttonsAnimator = 1.750 + new PropertyAnimator(animator.getDuration(), buttonsInterpolator); 1.751 + 1.752 + buttonsAnimator.attach(tabsCounter, 1.753 + PropertyAnimator.Property.ALPHA, 1.754 + 1.0f); 1.755 + 1.756 + if (hasSoftMenuButton && !HardwareUtils.isTablet()) { 1.757 + buttonsAnimator.attach(menuIcon, 1.758 + PropertyAnimator.Property.ALPHA, 1.759 + 1.0f); 1.760 + } 1.761 + 1.762 + buttonsAnimator.start(); 1.763 + 1.764 + return; 1.765 + } 1.766 + 1.767 + ViewHelper.setAlpha(tabsCounter, 0.0f); 1.768 + 1.769 + if (hasSoftMenuButton && !HardwareUtils.isTablet()) { 1.770 + ViewHelper.setAlpha(menuIcon, 0.0f); 1.771 + } 1.772 + } 1.773 + 1.774 + public void finishTabsAnimation(boolean tabsAreShown) { 1.775 + if (tabsAreShown) { 1.776 + return; 1.777 + } 1.778 + 1.779 + PropertyAnimator animator = new PropertyAnimator(150); 1.780 + 1.781 + animator.attach(tabsCounter, 1.782 + PropertyAnimator.Property.ALPHA, 1.783 + 1.0f); 1.784 + 1.785 + if (hasSoftMenuButton && !HardwareUtils.isTablet()) { 1.786 + animator.attach(menuIcon, 1.787 + PropertyAnimator.Property.ALPHA, 1.788 + 1.0f); 1.789 + } 1.790 + 1.791 + animator.start(); 1.792 + } 1.793 + 1.794 + public void setOnActivateListener(OnActivateListener listener) { 1.795 + activateListener = listener; 1.796 + } 1.797 + 1.798 + public void setOnCommitListener(OnCommitListener listener) { 1.799 + urlEditLayout.setOnCommitListener(listener); 1.800 + } 1.801 + 1.802 + public void setOnDismissListener(OnDismissListener listener) { 1.803 + urlEditLayout.setOnDismissListener(listener); 1.804 + } 1.805 + 1.806 + public void setOnFilterListener(OnFilterListener listener) { 1.807 + urlEditLayout.setOnFilterListener(listener); 1.808 + } 1.809 + 1.810 + public void setOnFocusChangeListener(OnFocusChangeListener listener) { 1.811 + focusChangeListener = listener; 1.812 + } 1.813 + 1.814 + public void setOnStartEditingListener(OnStartEditingListener listener) { 1.815 + startEditingListener = listener; 1.816 + } 1.817 + 1.818 + public void setOnStopEditingListener(OnStopEditingListener listener) { 1.819 + stopEditingListener = listener; 1.820 + } 1.821 + 1.822 + private void showUrlEditLayout() { 1.823 + setUrlEditLayoutVisibility(true, null); 1.824 + } 1.825 + 1.826 + private void showUrlEditLayout(PropertyAnimator animator) { 1.827 + setUrlEditLayoutVisibility(true, animator); 1.828 + } 1.829 + 1.830 + private void hideUrlEditLayout() { 1.831 + setUrlEditLayoutVisibility(false, null); 1.832 + } 1.833 + 1.834 + private void hideUrlEditLayout(PropertyAnimator animator) { 1.835 + setUrlEditLayoutVisibility(false, animator); 1.836 + } 1.837 + 1.838 + private void setUrlEditLayoutVisibility(final boolean showEditLayout, PropertyAnimator animator) { 1.839 + if (showEditLayout) { 1.840 + urlEditLayout.prepareShowAnimation(animator); 1.841 + } 1.842 + 1.843 + if (animator == null) { 1.844 + final View viewToShow = (showEditLayout ? urlEditLayout : urlDisplayLayout); 1.845 + final View viewToHide = (showEditLayout ? urlDisplayLayout : urlEditLayout); 1.846 + 1.847 + viewToHide.setVisibility(View.GONE); 1.848 + viewToShow.setVisibility(View.VISIBLE); 1.849 + 1.850 + final int cancelVisibility = (showEditLayout ? View.VISIBLE : View.INVISIBLE); 1.851 + setCancelVisibility(cancelVisibility); 1.852 + return; 1.853 + } 1.854 + 1.855 + animator.addPropertyAnimationListener(new PropertyAnimationListener() { 1.856 + @Override 1.857 + public void onPropertyAnimationStart() { 1.858 + if (!showEditLayout) { 1.859 + urlEditLayout.setVisibility(View.GONE); 1.860 + urlDisplayLayout.setVisibility(View.VISIBLE); 1.861 + 1.862 + setCancelVisibility(View.INVISIBLE); 1.863 + } 1.864 + } 1.865 + 1.866 + @Override 1.867 + public void onPropertyAnimationEnd() { 1.868 + if (showEditLayout) { 1.869 + urlDisplayLayout.setVisibility(View.GONE); 1.870 + urlEditLayout.setVisibility(View.VISIBLE); 1.871 + 1.872 + setCancelVisibility(View.VISIBLE); 1.873 + } 1.874 + } 1.875 + }); 1.876 + } 1.877 + 1.878 + private void setCancelVisibility(final int visibility) { 1.879 + if (editCancel != null) { 1.880 + editCancel.setVisibility(visibility); 1.881 + } 1.882 + } 1.883 + 1.884 + /** 1.885 + * Disables and dims all toolbar elements which are not 1.886 + * related to editing mode. 1.887 + */ 1.888 + private void updateChildrenForEditing() { 1.889 + // This is for the tablet UI only 1.890 + if (!HardwareUtils.isTablet()) { 1.891 + return; 1.892 + } 1.893 + 1.894 + // Disable toolbar elemens while in editing mode 1.895 + final boolean enabled = !isEditing(); 1.896 + 1.897 + // This alpha value has to be in sync with the one used 1.898 + // in setButtonEnabled(). 1.899 + final float alpha = (enabled ? 1.0f : 0.24f); 1.900 + 1.901 + if (!enabled) { 1.902 + tabsCounter.onEnterEditingMode(); 1.903 + } 1.904 + 1.905 + tabsButton.setEnabled(enabled); 1.906 + ViewHelper.setAlpha(tabsCounter, alpha); 1.907 + menuButton.setEnabled(enabled); 1.908 + ViewHelper.setAlpha(menuIcon, alpha); 1.909 + 1.910 + final int actionItemsCount = actionItemBar.getChildCount(); 1.911 + for (int i = 0; i < actionItemsCount; i++) { 1.912 + actionItemBar.getChildAt(i).setEnabled(enabled); 1.913 + } 1.914 + ViewHelper.setAlpha(actionItemBar, alpha); 1.915 + 1.916 + final Tab tab = Tabs.getInstance().getSelectedTab(); 1.917 + if (tab != null) { 1.918 + setButtonEnabled(backButton, canDoBack(tab)); 1.919 + setButtonEnabled(forwardButton, canDoForward(tab)); 1.920 + 1.921 + // Once the editing mode is finished, we have to ensure that the 1.922 + // forward button slides away if necessary. This is because we might 1.923 + // have only disabled it (without hiding it) when the toolbar entered 1.924 + // editing mode. 1.925 + if (!isEditing()) { 1.926 + animateForwardButton(canDoForward(tab) ? 1.927 + ForwardButtonAnimation.SHOW : ForwardButtonAnimation.HIDE); 1.928 + } 1.929 + } 1.930 + } 1.931 + 1.932 + private void setUIMode(final UIMode uiMode) { 1.933 + this.uiMode = uiMode; 1.934 + urlEditLayout.setEnabled(uiMode == UIMode.EDIT); 1.935 + } 1.936 + 1.937 + /** 1.938 + * Returns whether or not the URL bar is in editing mode (url bar is expanded, hiding the new 1.939 + * tab button). Note that selection state is independent of editing mode. 1.940 + */ 1.941 + public boolean isEditing() { 1.942 + return (uiMode == UIMode.EDIT); 1.943 + } 1.944 + 1.945 + public boolean isAnimating() { 1.946 + return isAnimatingEntry; 1.947 + } 1.948 + 1.949 + public void startEditing(String url, PropertyAnimator animator) { 1.950 + if (isEditing()) { 1.951 + return; 1.952 + } 1.953 + 1.954 + urlEditLayout.setText(url != null ? url : ""); 1.955 + 1.956 + setUIMode(UIMode.EDIT); 1.957 + updateChildrenForEditing(); 1.958 + 1.959 + updateProgressVisibility(); 1.960 + 1.961 + if (startEditingListener != null) { 1.962 + startEditingListener.onStartEditing(); 1.963 + } 1.964 + 1.965 + final int curveTranslation = getUrlBarCurveTranslation(); 1.966 + final int entryTranslation = getUrlBarEntryTranslation(); 1.967 + shouldShrinkURLBar = (entryTranslation < 0); 1.968 + 1.969 + if (urlBarTranslatingEdge != null) { 1.970 + urlBarTranslatingEdge.setVisibility(View.VISIBLE); 1.971 + if (shouldShrinkURLBar) { 1.972 + urlBarEntry.setLayoutParams(urlBarEntryShrunkenLayoutParams); 1.973 + } 1.974 + } 1.975 + 1.976 + if (Build.VERSION.SDK_INT < 11) { 1.977 + showEditingWithoutAnimation(entryTranslation, curveTranslation); 1.978 + } else if (HardwareUtils.isTablet()) { 1.979 + // No animation. 1.980 + showUrlEditLayout(); 1.981 + } else { 1.982 + showEditingWithPhoneAnimation(animator, entryTranslation, curveTranslation); 1.983 + } 1.984 + } 1.985 + 1.986 + private void showEditingWithoutAnimation(final int entryTranslation, 1.987 + final int curveTranslation) { 1.988 + showUrlEditLayout(); 1.989 + 1.990 + if (urlBarTranslatingEdge != null) { 1.991 + ViewHelper.setTranslationX(urlBarTranslatingEdge, entryTranslation); 1.992 + } 1.993 + 1.994 + // Prevent taps through the editing mode cancel button (bug 1001243). 1.995 + tabsButton.setEnabled(false); 1.996 + 1.997 + ViewHelper.setTranslationX(tabsButton, curveTranslation); 1.998 + ViewHelper.setTranslationX(tabsCounter, curveTranslation); 1.999 + ViewHelper.setTranslationX(actionItemBar, curveTranslation); 1.1000 + 1.1001 + if (hasSoftMenuButton) { 1.1002 + // Prevent tabs through the editing mode cancel button (bug 1001243). 1.1003 + menuButton.setEnabled(false); 1.1004 + 1.1005 + ViewHelper.setTranslationX(menuButton, curveTranslation); 1.1006 + ViewHelper.setTranslationX(menuIcon, curveTranslation); 1.1007 + } 1.1008 + } 1.1009 + 1.1010 + private void showEditingWithPhoneAnimation(final PropertyAnimator animator, 1.1011 + final int entryTranslation, final int curveTranslation) { 1.1012 + if (isAnimatingEntry) 1.1013 + return; 1.1014 + 1.1015 + urlDisplayLayout.prepareStartEditingAnimation(); 1.1016 + 1.1017 + // Slide toolbar elements. 1.1018 + if (urlBarTranslatingEdge != null) { 1.1019 + animator.attach(urlBarTranslatingEdge, 1.1020 + PropertyAnimator.Property.TRANSLATION_X, 1.1021 + entryTranslation); 1.1022 + } 1.1023 + 1.1024 + animator.attach(tabsButton, 1.1025 + PropertyAnimator.Property.TRANSLATION_X, 1.1026 + curveTranslation); 1.1027 + animator.attach(tabsCounter, 1.1028 + PropertyAnimator.Property.TRANSLATION_X, 1.1029 + curveTranslation); 1.1030 + animator.attach(actionItemBar, 1.1031 + PropertyAnimator.Property.TRANSLATION_X, 1.1032 + curveTranslation); 1.1033 + 1.1034 + if (hasSoftMenuButton) { 1.1035 + animator.attach(menuButton, 1.1036 + PropertyAnimator.Property.TRANSLATION_X, 1.1037 + curveTranslation); 1.1038 + 1.1039 + animator.attach(menuIcon, 1.1040 + PropertyAnimator.Property.TRANSLATION_X, 1.1041 + curveTranslation); 1.1042 + } 1.1043 + 1.1044 + showUrlEditLayout(animator); 1.1045 + 1.1046 + animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { 1.1047 + @Override 1.1048 + public void onPropertyAnimationStart() { 1.1049 + } 1.1050 + 1.1051 + @Override 1.1052 + public void onPropertyAnimationEnd() { 1.1053 + isAnimatingEntry = false; 1.1054 + } 1.1055 + }); 1.1056 + 1.1057 + isAnimatingEntry = true; 1.1058 + } 1.1059 + 1.1060 + /** 1.1061 + * Exits edit mode without updating the toolbar title. 1.1062 + * 1.1063 + * @return the url that was entered 1.1064 + */ 1.1065 + public String cancelEdit() { 1.1066 + Telemetry.stopUISession(TelemetryContract.Session.AWESOMESCREEN); 1.1067 + return stopEditing(); 1.1068 + } 1.1069 + 1.1070 + /** 1.1071 + * Exits edit mode, updating the toolbar title with the url that was just entered. 1.1072 + * 1.1073 + * @return the url that was entered 1.1074 + */ 1.1075 + public String commitEdit() { 1.1076 + final String url = stopEditing(); 1.1077 + if (!TextUtils.isEmpty(url)) { 1.1078 + setTitle(url); 1.1079 + } 1.1080 + return url; 1.1081 + } 1.1082 + 1.1083 + private String stopEditing() { 1.1084 + final String url = urlEditLayout.getText(); 1.1085 + if (!isEditing()) { 1.1086 + return url; 1.1087 + } 1.1088 + setUIMode(UIMode.DISPLAY); 1.1089 + 1.1090 + updateChildrenForEditing(); 1.1091 + 1.1092 + if (stopEditingListener != null) { 1.1093 + stopEditingListener.onStopEditing(); 1.1094 + } 1.1095 + 1.1096 + updateProgressVisibility(); 1.1097 + 1.1098 + // The animation looks cleaner if the text in the URL bar is 1.1099 + // not selected so clear the selection by clearing focus. 1.1100 + urlEditLayout.clearFocus(); 1.1101 + 1.1102 + if (Build.VERSION.SDK_INT < 11) { 1.1103 + stopEditingWithoutAnimation(); 1.1104 + } else if (HardwareUtils.isTablet()) { 1.1105 + // No animation. 1.1106 + hideUrlEditLayout(); 1.1107 + } else { 1.1108 + stopEditingWithPhoneAnimation(); 1.1109 + } 1.1110 + 1.1111 + return url; 1.1112 + } 1.1113 + 1.1114 + private void stopEditingWithoutAnimation() { 1.1115 + hideUrlEditLayout(); 1.1116 + 1.1117 + updateTabCountAndAnimate(Tabs.getInstance().getDisplayCount()); 1.1118 + 1.1119 + if (urlBarTranslatingEdge != null) { 1.1120 + urlBarTranslatingEdge.setVisibility(View.INVISIBLE); 1.1121 + ViewHelper.setTranslationX(urlBarTranslatingEdge, 0); 1.1122 + if (shouldShrinkURLBar) { 1.1123 + urlBarEntry.setLayoutParams(urlBarEntryDefaultLayoutParams); 1.1124 + } 1.1125 + } 1.1126 + 1.1127 + tabsButton.setEnabled(true); 1.1128 + 1.1129 + ViewHelper.setTranslationX(tabsButton, 0); 1.1130 + ViewHelper.setTranslationX(tabsCounter, 0); 1.1131 + ViewHelper.setTranslationX(actionItemBar, 0); 1.1132 + 1.1133 + if (hasSoftMenuButton) { 1.1134 + menuButton.setEnabled(true); 1.1135 + 1.1136 + ViewHelper.setTranslationX(menuButton, 0); 1.1137 + ViewHelper.setTranslationX(menuIcon, 0); 1.1138 + } 1.1139 + } 1.1140 + 1.1141 + private void stopEditingWithPhoneAnimation() { 1.1142 + final PropertyAnimator contentAnimator = new PropertyAnimator(250); 1.1143 + contentAnimator.setUseHardwareLayer(false); 1.1144 + 1.1145 + // Slide the toolbar back to its original size. 1.1146 + if (urlBarTranslatingEdge != null) { 1.1147 + contentAnimator.attach(urlBarTranslatingEdge, 1.1148 + PropertyAnimator.Property.TRANSLATION_X, 1.1149 + 0); 1.1150 + } 1.1151 + 1.1152 + contentAnimator.attach(tabsButton, 1.1153 + PropertyAnimator.Property.TRANSLATION_X, 1.1154 + 0); 1.1155 + contentAnimator.attach(tabsCounter, 1.1156 + PropertyAnimator.Property.TRANSLATION_X, 1.1157 + 0); 1.1158 + contentAnimator.attach(actionItemBar, 1.1159 + PropertyAnimator.Property.TRANSLATION_X, 1.1160 + 0); 1.1161 + 1.1162 + if (hasSoftMenuButton) { 1.1163 + contentAnimator.attach(menuButton, 1.1164 + PropertyAnimator.Property.TRANSLATION_X, 1.1165 + 0); 1.1166 + 1.1167 + contentAnimator.attach(menuIcon, 1.1168 + PropertyAnimator.Property.TRANSLATION_X, 1.1169 + 0); 1.1170 + } 1.1171 + 1.1172 + hideUrlEditLayout(contentAnimator); 1.1173 + 1.1174 + contentAnimator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { 1.1175 + @Override 1.1176 + public void onPropertyAnimationStart() { 1.1177 + } 1.1178 + 1.1179 + @Override 1.1180 + public void onPropertyAnimationEnd() { 1.1181 + if (urlBarTranslatingEdge != null) { 1.1182 + urlBarTranslatingEdge.setVisibility(View.INVISIBLE); 1.1183 + if (shouldShrinkURLBar) { 1.1184 + urlBarEntry.setLayoutParams(urlBarEntryDefaultLayoutParams); 1.1185 + } 1.1186 + } 1.1187 + 1.1188 + PropertyAnimator buttonsAnimator = new PropertyAnimator(300); 1.1189 + urlDisplayLayout.prepareStopEditingAnimation(buttonsAnimator); 1.1190 + buttonsAnimator.start(); 1.1191 + 1.1192 + isAnimatingEntry = false; 1.1193 + 1.1194 + // Trigger animation to update the tabs counter once the 1.1195 + // tabs button is back on screen. 1.1196 + updateTabCountAndAnimate(Tabs.getInstance().getDisplayCount()); 1.1197 + } 1.1198 + }); 1.1199 + 1.1200 + isAnimatingEntry = true; 1.1201 + contentAnimator.start(); 1.1202 + } 1.1203 + 1.1204 + private void setButtonEnabled(ImageButton button, boolean enabled) { 1.1205 + final Drawable drawable = button.getDrawable(); 1.1206 + if (drawable != null) { 1.1207 + // This alpha value has to be in sync with the one used 1.1208 + // in updateChildrenForEditing(). 1.1209 + drawable.setAlpha(enabled ? 255 : 61); 1.1210 + } 1.1211 + 1.1212 + button.setEnabled(enabled); 1.1213 + } 1.1214 + 1.1215 + public void updateBackButton(Tab tab) { 1.1216 + setButtonEnabled(backButton, canDoBack(tab)); 1.1217 + } 1.1218 + 1.1219 + private void animateForwardButton(final ForwardButtonAnimation animation) { 1.1220 + // If the forward button is not visible, we must be 1.1221 + // in the phone UI. 1.1222 + if (forwardButton.getVisibility() != View.VISIBLE) { 1.1223 + return; 1.1224 + } 1.1225 + 1.1226 + final boolean showing = (animation == ForwardButtonAnimation.SHOW); 1.1227 + 1.1228 + // if the forward button's margin is non-zero, this means it has already 1.1229 + // been animated to be visible¸ and vice-versa. 1.1230 + MarginLayoutParams fwdParams = (MarginLayoutParams) forwardButton.getLayoutParams(); 1.1231 + if ((fwdParams.leftMargin > defaultForwardMargin && showing) || 1.1232 + (fwdParams.leftMargin == defaultForwardMargin && !showing)) { 1.1233 + return; 1.1234 + } 1.1235 + 1.1236 + // We want the forward button to show immediately when switching tabs 1.1237 + final PropertyAnimator forwardAnim = 1.1238 + new PropertyAnimator(isSwitchingTabs ? 10 : FORWARD_ANIMATION_DURATION); 1.1239 + final int width = forwardButton.getWidth() / 2; 1.1240 + 1.1241 + forwardAnim.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { 1.1242 + @Override 1.1243 + public void onPropertyAnimationStart() { 1.1244 + if (!showing) { 1.1245 + // Set the margin before the transition when hiding the forward button. We 1.1246 + // have to do this so that the favicon isn't clipped during the transition 1.1247 + MarginLayoutParams layoutParams = 1.1248 + (MarginLayoutParams) urlDisplayLayout.getLayoutParams(); 1.1249 + layoutParams.leftMargin = 0; 1.1250 + 1.1251 + // Do the same on the URL edit container 1.1252 + layoutParams = (MarginLayoutParams) urlEditLayout.getLayoutParams(); 1.1253 + layoutParams.leftMargin = 0; 1.1254 + 1.1255 + requestLayout(); 1.1256 + // Note, we already translated the favicon, site security, and text field 1.1257 + // in prepareForwardAnimation, so they should appear to have not moved at 1.1258 + // all at this point. 1.1259 + } 1.1260 + } 1.1261 + 1.1262 + @Override 1.1263 + public void onPropertyAnimationEnd() { 1.1264 + if (showing) { 1.1265 + MarginLayoutParams layoutParams = 1.1266 + (MarginLayoutParams) urlDisplayLayout.getLayoutParams(); 1.1267 + layoutParams.leftMargin = urlBarViewOffset; 1.1268 + 1.1269 + layoutParams = (MarginLayoutParams) urlEditLayout.getLayoutParams(); 1.1270 + layoutParams.leftMargin = urlBarViewOffset; 1.1271 + } 1.1272 + 1.1273 + urlDisplayLayout.finishForwardAnimation(); 1.1274 + 1.1275 + MarginLayoutParams layoutParams = (MarginLayoutParams) forwardButton.getLayoutParams(); 1.1276 + layoutParams.leftMargin = defaultForwardMargin + (showing ? width : 0); 1.1277 + ViewHelper.setTranslationX(forwardButton, 0); 1.1278 + 1.1279 + requestLayout(); 1.1280 + } 1.1281 + }); 1.1282 + 1.1283 + prepareForwardAnimation(forwardAnim, animation, width); 1.1284 + forwardAnim.start(); 1.1285 + } 1.1286 + 1.1287 + public void updateForwardButton(Tab tab) { 1.1288 + final boolean enabled = canDoForward(tab); 1.1289 + if (forwardButton.isEnabled() == enabled) 1.1290 + return; 1.1291 + 1.1292 + // Save the state on the forward button so that we can skip animations 1.1293 + // when there's nothing to change 1.1294 + setButtonEnabled(forwardButton, enabled); 1.1295 + animateForwardButton(enabled ? ForwardButtonAnimation.SHOW : ForwardButtonAnimation.HIDE); 1.1296 + } 1.1297 + 1.1298 + private void prepareForwardAnimation(PropertyAnimator anim, ForwardButtonAnimation animation, int width) { 1.1299 + if (animation == ForwardButtonAnimation.HIDE) { 1.1300 + anim.attach(forwardButton, 1.1301 + PropertyAnimator.Property.TRANSLATION_X, 1.1302 + -width); 1.1303 + anim.attach(forwardButton, 1.1304 + PropertyAnimator.Property.ALPHA, 1.1305 + 0); 1.1306 + 1.1307 + } else { 1.1308 + anim.attach(forwardButton, 1.1309 + PropertyAnimator.Property.TRANSLATION_X, 1.1310 + width); 1.1311 + anim.attach(forwardButton, 1.1312 + PropertyAnimator.Property.ALPHA, 1.1313 + 1); 1.1314 + } 1.1315 + 1.1316 + urlDisplayLayout.prepareForwardAnimation(anim, animation, width); 1.1317 + } 1.1318 + 1.1319 + @Override 1.1320 + public boolean addActionItem(View actionItem) { 1.1321 + actionItemBar.addView(actionItem); 1.1322 + return true; 1.1323 + } 1.1324 + 1.1325 + @Override 1.1326 + public void removeActionItem(View actionItem) { 1.1327 + actionItemBar.removeView(actionItem); 1.1328 + } 1.1329 + 1.1330 + @Override 1.1331 + public void setPrivateMode(boolean isPrivate) { 1.1332 + super.setPrivateMode(isPrivate); 1.1333 + 1.1334 + tabsButton.setPrivateMode(isPrivate); 1.1335 + menuButton.setPrivateMode(isPrivate); 1.1336 + menuIcon.setPrivateMode(isPrivate); 1.1337 + urlEditLayout.setPrivateMode(isPrivate); 1.1338 + 1.1339 + if (backButton instanceof BackButton) { 1.1340 + ((BackButton) backButton).setPrivateMode(isPrivate); 1.1341 + } 1.1342 + 1.1343 + if (forwardButton instanceof ForwardButton) { 1.1344 + ((ForwardButton) forwardButton).setPrivateMode(isPrivate); 1.1345 + } 1.1346 + } 1.1347 + 1.1348 + public void show() { 1.1349 + setVisibility(View.VISIBLE); 1.1350 + } 1.1351 + 1.1352 + public void hide() { 1.1353 + setVisibility(View.GONE); 1.1354 + } 1.1355 + 1.1356 + public View getDoorHangerAnchor() { 1.1357 + return urlDisplayLayout.getDoorHangerAnchor(); 1.1358 + } 1.1359 + 1.1360 + public void onDestroy() { 1.1361 + Tabs.unregisterOnTabsChangedListener(this); 1.1362 + 1.1363 + unregisterEventListener("Reader:Click"); 1.1364 + unregisterEventListener("Reader:LongClick"); 1.1365 + } 1.1366 + 1.1367 + public boolean openOptionsMenu() { 1.1368 + if (!hasSoftMenuButton) { 1.1369 + return false; 1.1370 + } 1.1371 + 1.1372 + // Initialize the popup. 1.1373 + if (menuPopup == null) { 1.1374 + View panel = activity.getMenuPanel(); 1.1375 + menuPopup = new MenuPopup(activity); 1.1376 + menuPopup.setPanelView(panel); 1.1377 + 1.1378 + menuPopup.setOnDismissListener(new PopupWindow.OnDismissListener() { 1.1379 + @Override 1.1380 + public void onDismiss() { 1.1381 + activity.onOptionsMenuClosed(null); 1.1382 + } 1.1383 + }); 1.1384 + } 1.1385 + 1.1386 + GeckoAppShell.getGeckoInterface().invalidateOptionsMenu(); 1.1387 + if (!menuPopup.isShowing()) { 1.1388 + menuPopup.showAsDropDown(menuButton); 1.1389 + } 1.1390 + 1.1391 + return true; 1.1392 + } 1.1393 + 1.1394 + public boolean closeOptionsMenu() { 1.1395 + if (!hasSoftMenuButton) { 1.1396 + return false; 1.1397 + } 1.1398 + 1.1399 + if (menuPopup != null && menuPopup.isShowing()) { 1.1400 + menuPopup.dismiss(); 1.1401 + } 1.1402 + 1.1403 + return true; 1.1404 + } 1.1405 + 1.1406 + private void registerEventListener(String event) { 1.1407 + GeckoAppShell.getEventDispatcher().registerEventListener(event, this); 1.1408 + } 1.1409 + 1.1410 + private void unregisterEventListener(String event) { 1.1411 + GeckoAppShell.getEventDispatcher().unregisterEventListener(event, this); 1.1412 + } 1.1413 + 1.1414 + @Override 1.1415 + public void handleMessage(String event, JSONObject message) { 1.1416 + Log.d(LOGTAG, "handleMessage: " + event); 1.1417 + if (event.equals("Reader:Click")) { 1.1418 + Tab tab = Tabs.getInstance().getSelectedTab(); 1.1419 + if (tab != null) { 1.1420 + tab.toggleReaderMode(); 1.1421 + } 1.1422 + } else if (event.equals("Reader:LongClick")) { 1.1423 + Tab tab = Tabs.getInstance().getSelectedTab(); 1.1424 + if (tab != null) { 1.1425 + tab.addToReadingList(); 1.1426 + } 1.1427 + } 1.1428 + } 1.1429 + 1.1430 + @Override 1.1431 + public void onLightweightThemeChanged() { 1.1432 + Drawable drawable = theme.getDrawable(this); 1.1433 + if (drawable == null) 1.1434 + return; 1.1435 + 1.1436 + StateListDrawable stateList = new StateListDrawable(); 1.1437 + stateList.addState(PRIVATE_STATE_SET, getColorDrawable(R.color.background_private)); 1.1438 + stateList.addState(EMPTY_STATE_SET, drawable); 1.1439 + 1.1440 + setBackgroundDrawable(stateList); 1.1441 + 1.1442 + if (editCancel != null) { 1.1443 + editCancel.onLightweightThemeChanged(); 1.1444 + } 1.1445 + } 1.1446 + 1.1447 + @Override 1.1448 + public void onLightweightThemeReset() { 1.1449 + setBackgroundResource(R.drawable.url_bar_bg); 1.1450 + if (editCancel != null) { 1.1451 + editCancel.onLightweightThemeReset(); 1.1452 + } 1.1453 + } 1.1454 +}