michael@0: /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.toolbar; michael@0: michael@0: import java.util.ArrayList; michael@0: import java.util.Arrays; michael@0: import java.util.EnumSet; michael@0: import java.util.List; michael@0: michael@0: import org.json.JSONObject; michael@0: import org.mozilla.gecko.BrowserApp; michael@0: import org.mozilla.gecko.GeckoAppShell; michael@0: import org.mozilla.gecko.GeckoApplication; michael@0: import org.mozilla.gecko.GeckoProfile; michael@0: import org.mozilla.gecko.LightweightTheme; michael@0: import org.mozilla.gecko.R; michael@0: import org.mozilla.gecko.Tab; michael@0: import org.mozilla.gecko.Tabs; michael@0: import org.mozilla.gecko.Telemetry; michael@0: import org.mozilla.gecko.TelemetryContract; michael@0: import org.mozilla.gecko.animation.PropertyAnimator; michael@0: import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener; michael@0: import org.mozilla.gecko.animation.ViewHelper; michael@0: import org.mozilla.gecko.menu.GeckoMenu; michael@0: import org.mozilla.gecko.menu.MenuPopup; michael@0: import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnStopListener; michael@0: import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnTitleChangeListener; michael@0: import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.UpdateFlags; michael@0: import org.mozilla.gecko.util.Clipboard; michael@0: import org.mozilla.gecko.util.GeckoEventListener; michael@0: import org.mozilla.gecko.util.HardwareUtils; michael@0: import org.mozilla.gecko.util.MenuUtils; michael@0: import org.mozilla.gecko.widget.ThemedImageButton; michael@0: import org.mozilla.gecko.widget.ThemedImageView; michael@0: import org.mozilla.gecko.widget.ThemedRelativeLayout; michael@0: michael@0: import android.content.Context; michael@0: import android.content.res.Resources; michael@0: import android.graphics.Bitmap; michael@0: import android.graphics.drawable.BitmapDrawable; michael@0: import android.graphics.drawable.Drawable; michael@0: import android.graphics.drawable.StateListDrawable; michael@0: import android.os.Build; michael@0: import android.text.TextUtils; michael@0: import android.util.AttributeSet; michael@0: import android.util.Log; michael@0: import android.view.ContextMenu; michael@0: import android.view.KeyEvent; michael@0: import android.view.LayoutInflater; michael@0: import android.view.MenuInflater; michael@0: import android.view.MotionEvent; michael@0: import android.view.View; michael@0: import android.view.ViewGroup; michael@0: import android.view.animation.AccelerateInterpolator; michael@0: import android.view.animation.Interpolator; michael@0: import android.view.inputmethod.InputMethodManager; michael@0: import android.widget.Button; michael@0: import android.widget.ImageButton; michael@0: import android.widget.ImageView; michael@0: import android.widget.LinearLayout; michael@0: import android.widget.PopupWindow; michael@0: import android.widget.RelativeLayout; michael@0: michael@0: /** michael@0: * {@code BrowserToolbar} is single entry point for users of the toolbar michael@0: * subsystem i.e. this should be the only import outside the 'toolbar' michael@0: * package. michael@0: * michael@0: * {@code BrowserToolbar} serves at the single event bus for all michael@0: * sub-components in the toolbar. It tracks tab events and gecko messages michael@0: * and update the state of its inner components accordingly. michael@0: * michael@0: * It has two states, display and edit, which are controlled by michael@0: * ToolbarEditLayout and ToolbarDisplayLayout. In display state, the toolbar michael@0: * displays the current state for the selected tab. In edit state, it shows michael@0: * a text entry for searching bookmarks/history. {@code BrowserToolbar} michael@0: * provides public API to enter, cancel, and commit the edit state as well michael@0: * as a set of listeners to allow {@code BrowserToolbar} users to react michael@0: * to state changes accordingly. michael@0: */ michael@0: public class BrowserToolbar extends ThemedRelativeLayout michael@0: implements Tabs.OnTabsChangedListener, michael@0: GeckoMenu.ActionItemBarPresenter, michael@0: GeckoEventListener { michael@0: private static final String LOGTAG = "GeckoToolbar"; michael@0: michael@0: public interface OnActivateListener { michael@0: public void onActivate(); michael@0: } michael@0: michael@0: public interface OnCommitListener { michael@0: public void onCommit(); michael@0: } michael@0: michael@0: public interface OnDismissListener { michael@0: public void onDismiss(); michael@0: } michael@0: michael@0: public interface OnFilterListener { michael@0: public void onFilter(String searchText, AutocompleteHandler handler); michael@0: } michael@0: michael@0: public interface OnStartEditingListener { michael@0: public void onStartEditing(); michael@0: } michael@0: michael@0: public interface OnStopEditingListener { michael@0: public void onStopEditing(); michael@0: } michael@0: michael@0: private enum UIMode { michael@0: EDIT, michael@0: DISPLAY michael@0: } michael@0: michael@0: enum ForwardButtonAnimation { michael@0: SHOW, michael@0: HIDE michael@0: } michael@0: michael@0: private ToolbarDisplayLayout urlDisplayLayout; michael@0: private ToolbarEditLayout urlEditLayout; michael@0: private View urlBarEntry; michael@0: private RelativeLayout.LayoutParams urlBarEntryDefaultLayoutParams; michael@0: private RelativeLayout.LayoutParams urlBarEntryShrunkenLayoutParams; michael@0: private ImageView urlBarTranslatingEdge; michael@0: private boolean isSwitchingTabs; michael@0: private ShapedButton tabsButton; michael@0: private ImageButton backButton; michael@0: private ImageButton forwardButton; michael@0: michael@0: private ToolbarProgressView progressBar; michael@0: private TabCounter tabsCounter; michael@0: private ThemedImageButton menuButton; michael@0: private ThemedImageView menuIcon; michael@0: private LinearLayout actionItemBar; michael@0: private MenuPopup menuPopup; michael@0: private List focusOrder; michael@0: michael@0: private final ThemedImageView editCancel; michael@0: michael@0: private boolean shouldShrinkURLBar = false; michael@0: michael@0: private OnActivateListener activateListener; michael@0: private OnFocusChangeListener focusChangeListener; michael@0: private OnStartEditingListener startEditingListener; michael@0: private OnStopEditingListener stopEditingListener; michael@0: michael@0: private final BrowserApp activity; michael@0: private boolean hasSoftMenuButton; michael@0: michael@0: private UIMode uiMode; michael@0: private boolean isAnimatingEntry; michael@0: michael@0: private int urlBarViewOffset; michael@0: private int defaultForwardMargin; michael@0: michael@0: private static final Interpolator buttonsInterpolator = new AccelerateInterpolator(); michael@0: michael@0: private static final int FORWARD_ANIMATION_DURATION = 450; michael@0: michael@0: private final LightweightTheme theme; michael@0: michael@0: public BrowserToolbar(Context context) { michael@0: this(context, null); michael@0: } michael@0: michael@0: public BrowserToolbar(Context context, AttributeSet attrs) { michael@0: super(context, attrs); michael@0: theme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme(); michael@0: michael@0: // BrowserToolbar is attached to BrowserApp only. michael@0: activity = (BrowserApp) context; michael@0: michael@0: // Inflate the content. michael@0: LayoutInflater.from(context).inflate(R.layout.browser_toolbar, this); michael@0: michael@0: Tabs.registerOnTabsChangedListener(this); michael@0: isSwitchingTabs = true; michael@0: isAnimatingEntry = false; michael@0: michael@0: registerEventListener("Reader:Click"); michael@0: registerEventListener("Reader:LongClick"); michael@0: michael@0: final Resources res = getResources(); michael@0: urlBarViewOffset = res.getDimensionPixelSize(R.dimen.url_bar_offset_left); michael@0: defaultForwardMargin = res.getDimensionPixelSize(R.dimen.forward_default_offset); michael@0: urlDisplayLayout = (ToolbarDisplayLayout) findViewById(R.id.display_layout); michael@0: urlBarEntry = findViewById(R.id.url_bar_entry); michael@0: urlEditLayout = (ToolbarEditLayout) findViewById(R.id.edit_layout); michael@0: michael@0: urlBarEntryDefaultLayoutParams = (RelativeLayout.LayoutParams) urlBarEntry.getLayoutParams(); michael@0: // API level 19 adds a RelativeLayout.LayoutParams copy constructor, so we explicitly cast michael@0: // to ViewGroup.MarginLayoutParams to ensure consistency across platforms. michael@0: urlBarEntryShrunkenLayoutParams = new RelativeLayout.LayoutParams( michael@0: (ViewGroup.MarginLayoutParams) urlBarEntryDefaultLayoutParams); michael@0: urlBarEntryShrunkenLayoutParams.addRule(RelativeLayout.ALIGN_RIGHT, R.id.edit_layout); michael@0: urlBarEntryShrunkenLayoutParams.rightMargin = 0; michael@0: michael@0: // This will clip the translating edge's image at 60% of its width michael@0: urlBarTranslatingEdge = (ImageView) findViewById(R.id.url_bar_translating_edge); michael@0: if (urlBarTranslatingEdge != null) { michael@0: urlBarTranslatingEdge.getDrawable().setLevel(6000); michael@0: } michael@0: michael@0: tabsButton = (ShapedButton) findViewById(R.id.tabs); michael@0: tabsCounter = (TabCounter) findViewById(R.id.tabs_counter); michael@0: if (Build.VERSION.SDK_INT >= 11) { michael@0: tabsCounter.setLayerType(View.LAYER_TYPE_SOFTWARE, null); michael@0: } michael@0: michael@0: backButton = (ImageButton) findViewById(R.id.back); michael@0: setButtonEnabled(backButton, false); michael@0: forwardButton = (ImageButton) findViewById(R.id.forward); michael@0: setButtonEnabled(forwardButton, false); michael@0: michael@0: menuButton = (ThemedImageButton) findViewById(R.id.menu); michael@0: menuIcon = (ThemedImageView) findViewById(R.id.menu_icon); michael@0: actionItemBar = (LinearLayout) findViewById(R.id.menu_items); michael@0: hasSoftMenuButton = !HardwareUtils.hasMenuButton(); michael@0: michael@0: editCancel = (ThemedImageView) findViewById(R.id.edit_cancel); michael@0: michael@0: // We use different layouts on phones and tablets, so adjust the focus michael@0: // order appropriately. michael@0: focusOrder = new ArrayList(); michael@0: if (HardwareUtils.isTablet()) { michael@0: focusOrder.addAll(Arrays.asList(tabsButton, backButton, forwardButton, this)); michael@0: focusOrder.addAll(urlDisplayLayout.getFocusOrder()); michael@0: focusOrder.addAll(Arrays.asList(actionItemBar, menuButton)); michael@0: } else { michael@0: focusOrder.add(this); michael@0: focusOrder.addAll(urlDisplayLayout.getFocusOrder()); michael@0: focusOrder.addAll(Arrays.asList(tabsButton, menuButton)); michael@0: } michael@0: michael@0: setUIMode(UIMode.DISPLAY); michael@0: } michael@0: michael@0: @Override michael@0: public void onAttachedToWindow() { michael@0: super.onAttachedToWindow(); michael@0: michael@0: setOnClickListener(new Button.OnClickListener() { michael@0: @Override michael@0: public void onClick(View v) { michael@0: if (activateListener != null) { michael@0: activateListener.onActivate(); michael@0: } michael@0: } michael@0: }); michael@0: michael@0: setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() { michael@0: @Override michael@0: public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { michael@0: // We don't the context menu while editing michael@0: if (isEditing()) { michael@0: return; michael@0: } michael@0: michael@0: // NOTE: Use MenuUtils.safeSetVisible because some actions might michael@0: // be on the Page menu michael@0: michael@0: MenuInflater inflater = activity.getMenuInflater(); michael@0: inflater.inflate(R.menu.titlebar_contextmenu, menu); michael@0: michael@0: String clipboard = Clipboard.getText(); michael@0: if (TextUtils.isEmpty(clipboard)) { michael@0: menu.findItem(R.id.pasteandgo).setVisible(false); michael@0: menu.findItem(R.id.paste).setVisible(false); michael@0: } michael@0: michael@0: Tab tab = Tabs.getInstance().getSelectedTab(); michael@0: if (tab != null) { michael@0: String url = tab.getURL(); michael@0: if (url == null) { michael@0: menu.findItem(R.id.copyurl).setVisible(false); michael@0: menu.findItem(R.id.add_to_launcher).setVisible(false); michael@0: MenuUtils.safeSetVisible(menu, R.id.share, false); michael@0: } michael@0: michael@0: MenuUtils.safeSetVisible(menu, R.id.subscribe, tab.hasFeeds()); michael@0: MenuUtils.safeSetVisible(menu, R.id.add_search_engine, tab.hasOpenSearch()); michael@0: } else { michael@0: // if there is no tab, remove anything tab dependent michael@0: menu.findItem(R.id.copyurl).setVisible(false); michael@0: menu.findItem(R.id.add_to_launcher).setVisible(false); michael@0: MenuUtils.safeSetVisible(menu, R.id.share, false); michael@0: MenuUtils.safeSetVisible(menu, R.id.subscribe, false); michael@0: MenuUtils.safeSetVisible(menu, R.id.add_search_engine, false); michael@0: } michael@0: michael@0: MenuUtils.safeSetVisible(menu, R.id.share, !GeckoProfile.get(getContext()).inGuestMode()); michael@0: } michael@0: }); michael@0: michael@0: urlDisplayLayout.setOnStopListener(new OnStopListener() { michael@0: @Override michael@0: public Tab onStop() { michael@0: final Tab tab = Tabs.getInstance().getSelectedTab(); michael@0: if (tab != null) { michael@0: tab.doStop(); michael@0: return tab; michael@0: } michael@0: michael@0: return null; michael@0: } michael@0: }); michael@0: michael@0: urlDisplayLayout.setOnTitleChangeListener(new OnTitleChangeListener() { michael@0: @Override michael@0: public void onTitleChange(CharSequence title) { michael@0: final String contentDescription; michael@0: if (title != null) { michael@0: contentDescription = title.toString(); michael@0: } else { michael@0: contentDescription = activity.getString(R.string.url_bar_default_text); michael@0: } michael@0: michael@0: // The title and content description should michael@0: // always be sync. michael@0: setContentDescription(contentDescription); michael@0: } michael@0: }); michael@0: michael@0: urlEditLayout.setOnFocusChangeListener(new View.OnFocusChangeListener() { michael@0: @Override michael@0: public void onFocusChange(View v, boolean hasFocus) { michael@0: // This will select the url bar when entering editing mode. michael@0: setSelected(hasFocus); michael@0: if (focusChangeListener != null) { michael@0: focusChangeListener.onFocusChange(v, hasFocus); michael@0: } michael@0: } michael@0: }); michael@0: michael@0: tabsButton.setOnClickListener(new Button.OnClickListener() { michael@0: @Override michael@0: public void onClick(View v) { michael@0: toggleTabs(); michael@0: } michael@0: }); michael@0: tabsButton.setImageLevel(0); michael@0: michael@0: backButton.setOnClickListener(new Button.OnClickListener() { michael@0: @Override michael@0: public void onClick(View view) { michael@0: Tabs.getInstance().getSelectedTab().doBack(); michael@0: } michael@0: }); michael@0: backButton.setOnLongClickListener(new Button.OnLongClickListener() { michael@0: @Override michael@0: public boolean onLongClick(View view) { michael@0: return Tabs.getInstance().getSelectedTab().showBackHistory(); michael@0: } michael@0: }); michael@0: michael@0: forwardButton.setOnClickListener(new Button.OnClickListener() { michael@0: @Override michael@0: public void onClick(View view) { michael@0: Tabs.getInstance().getSelectedTab().doForward(); michael@0: } michael@0: }); michael@0: forwardButton.setOnLongClickListener(new Button.OnLongClickListener() { michael@0: @Override michael@0: public boolean onLongClick(View view) { michael@0: return Tabs.getInstance().getSelectedTab().showForwardHistory(); michael@0: } michael@0: }); michael@0: michael@0: if (editCancel != null) { michael@0: editCancel.setOnClickListener(new OnClickListener() { michael@0: @Override michael@0: public void onClick(View v) { michael@0: // If we exit editing mode during the animation, michael@0: // we're put into an inconsistent state (bug 1017276). michael@0: if (!isAnimatingEntry) { michael@0: Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, michael@0: TelemetryContract.Method.ACTIONBAR, michael@0: getResources().getResourceEntryName(editCancel.getId())); michael@0: cancelEdit(); michael@0: } michael@0: } michael@0: }); michael@0: } michael@0: michael@0: if (hasSoftMenuButton) { michael@0: menuButton.setVisibility(View.VISIBLE); michael@0: menuIcon.setVisibility(View.VISIBLE); michael@0: michael@0: menuButton.setOnClickListener(new Button.OnClickListener() { michael@0: @Override michael@0: public void onClick(View view) { michael@0: activity.openOptionsMenu(); michael@0: } michael@0: }); michael@0: } michael@0: } michael@0: michael@0: public void setProgressBar(ToolbarProgressView progressBar) { michael@0: this.progressBar = progressBar; michael@0: } michael@0: michael@0: public void refresh() { michael@0: urlDisplayLayout.dismissSiteIdentityPopup(); michael@0: } michael@0: michael@0: public boolean onBackPressed() { michael@0: // If we exit editing mode during the animation, michael@0: // we're put into an inconsistent state (bug 1017276). michael@0: if (isEditing() && !isAnimatingEntry) { michael@0: Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, michael@0: TelemetryContract.Method.BACK); michael@0: cancelEdit(); michael@0: return true; michael@0: } michael@0: michael@0: return urlDisplayLayout.dismissSiteIdentityPopup(); michael@0: } michael@0: michael@0: @Override michael@0: public boolean onTouchEvent(MotionEvent event) { michael@0: // If the motion event has occured below the toolbar (due to the scroll michael@0: // offset), let it pass through to the page. michael@0: if (event != null && event.getY() > getHeight() + ViewHelper.getTranslationY(this)) { michael@0: return false; michael@0: } michael@0: michael@0: return super.onTouchEvent(event); michael@0: } michael@0: michael@0: @Override michael@0: protected void onSizeChanged(int w, int h, int oldw, int oldh) { michael@0: super.onSizeChanged(w, h, oldw, oldh); michael@0: michael@0: if (h != oldh) { michael@0: // Post this to happen outside of onSizeChanged, as this may cause michael@0: // a layout change and relayouts within a layout change don't work. michael@0: post(new Runnable() { michael@0: @Override michael@0: public void run() { michael@0: activity.refreshToolbarHeight(); michael@0: } michael@0: }); michael@0: } michael@0: } michael@0: michael@0: @Override michael@0: public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { michael@0: Log.d(LOGTAG, "onTabChanged: " + msg); michael@0: final Tabs tabs = Tabs.getInstance(); michael@0: michael@0: // These conditions are split into three phases: michael@0: // * Always do first michael@0: // * Handling specific to the selected tab michael@0: // * Always do afterwards. michael@0: michael@0: switch (msg) { michael@0: case ADDED: michael@0: case CLOSED: michael@0: updateTabCount(tabs.getDisplayCount()); michael@0: break; michael@0: case RESTORED: michael@0: // TabCount fixup after OOM michael@0: case SELECTED: michael@0: urlDisplayLayout.dismissSiteIdentityPopup(); michael@0: updateTabCount(tabs.getDisplayCount()); michael@0: isSwitchingTabs = true; michael@0: break; michael@0: } michael@0: michael@0: if (tabs.isSelectedTab(tab)) { michael@0: final EnumSet flags = EnumSet.noneOf(UpdateFlags.class); michael@0: michael@0: // Progress-related handling michael@0: switch (msg) { michael@0: case START: michael@0: updateProgressVisibility(tab, Tab.LOAD_PROGRESS_INIT); michael@0: // Fall through. michael@0: case ADDED: michael@0: case LOCATION_CHANGE: michael@0: case LOAD_ERROR: michael@0: case LOADED: michael@0: case STOP: michael@0: flags.add(UpdateFlags.PROGRESS); michael@0: if (progressBar.getVisibility() == View.VISIBLE) { michael@0: progressBar.animateProgress(tab.getLoadProgress()); michael@0: } michael@0: break; michael@0: michael@0: case SELECTED: michael@0: flags.add(UpdateFlags.PROGRESS); michael@0: updateProgressVisibility(); michael@0: break; michael@0: } michael@0: michael@0: switch (msg) { michael@0: case STOP: michael@0: // Reset the title in case we haven't navigated michael@0: // to a new page yet. michael@0: flags.add(UpdateFlags.TITLE); michael@0: // Fall through. michael@0: case START: michael@0: case CLOSED: michael@0: case ADDED: michael@0: updateBackButton(tab); michael@0: updateForwardButton(tab); michael@0: break; michael@0: michael@0: case SELECTED: michael@0: flags.add(UpdateFlags.PRIVATE_MODE); michael@0: setPrivateMode(tab.isPrivate()); michael@0: // Fall through. michael@0: case LOAD_ERROR: michael@0: flags.add(UpdateFlags.TITLE); michael@0: // Fall through. michael@0: case LOCATION_CHANGE: michael@0: // A successful location change will cause Tab to notify michael@0: // us of a title change, so we don't update the title here. michael@0: flags.add(UpdateFlags.FAVICON); michael@0: flags.add(UpdateFlags.SITE_IDENTITY); michael@0: michael@0: updateBackButton(tab); michael@0: updateForwardButton(tab); michael@0: break; michael@0: michael@0: case TITLE: michael@0: flags.add(UpdateFlags.TITLE); michael@0: break; michael@0: michael@0: case FAVICON: michael@0: flags.add(UpdateFlags.FAVICON); michael@0: break; michael@0: michael@0: case SECURITY_CHANGE: michael@0: flags.add(UpdateFlags.SITE_IDENTITY); michael@0: break; michael@0: } michael@0: michael@0: if (!flags.isEmpty()) { michael@0: updateDisplayLayout(tab, flags); michael@0: } michael@0: } michael@0: michael@0: switch (msg) { michael@0: case SELECTED: michael@0: case LOAD_ERROR: michael@0: case LOCATION_CHANGE: michael@0: isSwitchingTabs = false; michael@0: } michael@0: } michael@0: michael@0: private void updateProgressVisibility() { michael@0: final Tab selectedTab = Tabs.getInstance().getSelectedTab(); michael@0: updateProgressVisibility(selectedTab, selectedTab.getLoadProgress()); michael@0: } michael@0: michael@0: private void updateProgressVisibility(Tab selectedTab, int progress) { michael@0: if (!isEditing() && selectedTab.getState() == Tab.STATE_LOADING) { michael@0: progressBar.setProgress(progress); michael@0: progressBar.setVisibility(View.VISIBLE); michael@0: } else { michael@0: progressBar.setVisibility(View.GONE); michael@0: } michael@0: } michael@0: michael@0: private boolean isVisible() { michael@0: return ViewHelper.getTranslationY(this) == 0; michael@0: } michael@0: michael@0: @Override michael@0: public void setNextFocusDownId(int nextId) { michael@0: super.setNextFocusDownId(nextId); michael@0: tabsButton.setNextFocusDownId(nextId); michael@0: backButton.setNextFocusDownId(nextId); michael@0: forwardButton.setNextFocusDownId(nextId); michael@0: urlDisplayLayout.setNextFocusDownId(nextId); michael@0: menuButton.setNextFocusDownId(nextId); michael@0: } michael@0: michael@0: private int getUrlBarEntryTranslation() { michael@0: if (editCancel == null) { michael@0: // We are on tablet, and there is no animation so return a translation of 0. michael@0: return 0; michael@0: } michael@0: michael@0: // Find the distance from the right-edge of the url bar (where we're translating from) to michael@0: // the left-edge of the cancel button (where we're translating to; note that the cancel michael@0: // button must be laid out, i.e. not View.GONE). michael@0: final LayoutParams lp = (LayoutParams) urlEditLayout.getLayoutParams(); michael@0: return editCancel.getLeft() - lp.leftMargin - urlBarEntry.getRight(); michael@0: } michael@0: michael@0: private int getUrlBarCurveTranslation() { michael@0: return getWidth() - tabsButton.getLeft(); michael@0: } michael@0: michael@0: private boolean canDoBack(Tab tab) { michael@0: return (tab.canDoBack() && !isEditing()); michael@0: } michael@0: michael@0: private boolean canDoForward(Tab tab) { michael@0: return (tab.canDoForward() && !isEditing()); michael@0: } michael@0: michael@0: private void addTab() { michael@0: activity.addTab(); michael@0: } michael@0: michael@0: private void toggleTabs() { michael@0: if (activity.areTabsShown()) { michael@0: if (activity.hasTabsSideBar()) michael@0: activity.hideTabs(); michael@0: } else { michael@0: // hide the virtual keyboard michael@0: InputMethodManager imm = michael@0: (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); michael@0: imm.hideSoftInputFromWindow(tabsButton.getWindowToken(), 0); michael@0: michael@0: Tab tab = Tabs.getInstance().getSelectedTab(); michael@0: if (tab != null) { michael@0: if (!tab.isPrivate()) michael@0: activity.showNormalTabs(); michael@0: else michael@0: activity.showPrivateTabs(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: private void updateTabCountAndAnimate(int count) { michael@0: // Don't animate if the toolbar is hidden. michael@0: if (!isVisible()) { michael@0: updateTabCount(count); michael@0: return; michael@0: } michael@0: michael@0: // If toolbar is in edit mode on a phone, this means the entry is expanded michael@0: // and the tabs button is translated offscreen. Don't trigger tabs counter michael@0: // updates until the tabs button is back on screen. michael@0: // See stopEditing() michael@0: if (!isEditing() || HardwareUtils.isTablet()) { michael@0: tabsCounter.setCount(count); michael@0: michael@0: tabsButton.setContentDescription((count > 1) ? michael@0: activity.getString(R.string.num_tabs, count) : michael@0: activity.getString(R.string.one_tab)); michael@0: } michael@0: } michael@0: michael@0: private void updateTabCount(int count) { michael@0: // If toolbar is in edit mode on a phone, this means the entry is expanded michael@0: // and the tabs button is translated offscreen. Don't trigger tabs counter michael@0: // updates until the tabs button is back on screen. michael@0: // See stopEditing() michael@0: if (isEditing() && !HardwareUtils.isTablet()) { michael@0: return; michael@0: } michael@0: michael@0: // Set TabCounter based on visibility michael@0: if (isVisible() && ViewHelper.getAlpha(tabsCounter) != 0 && !isEditing()) { michael@0: tabsCounter.setCountWithAnimation(count); michael@0: } else { michael@0: tabsCounter.setCount(count); michael@0: } michael@0: michael@0: // Update A11y information michael@0: tabsButton.setContentDescription((count > 1) ? michael@0: activity.getString(R.string.num_tabs, count) : michael@0: activity.getString(R.string.one_tab)); michael@0: } michael@0: michael@0: private void updateDisplayLayout(Tab tab, EnumSet flags) { michael@0: if (isSwitchingTabs) { michael@0: flags.add(UpdateFlags.DISABLE_ANIMATIONS); michael@0: } michael@0: michael@0: urlDisplayLayout.updateFromTab(tab, flags); michael@0: michael@0: if (flags.contains(UpdateFlags.TITLE)) { michael@0: if (!isEditing()) { michael@0: urlEditLayout.setText(tab.getURL()); michael@0: } michael@0: } michael@0: michael@0: if (flags.contains(UpdateFlags.PROGRESS)) { michael@0: updateFocusOrder(); michael@0: } michael@0: } michael@0: michael@0: private void updateFocusOrder() { michael@0: View prevView = null; michael@0: michael@0: // If the element that has focus becomes disabled or invisible, focus michael@0: // is given to the URL bar. michael@0: boolean needsNewFocus = false; michael@0: michael@0: for (View view : focusOrder) { michael@0: if (view.getVisibility() != View.VISIBLE || !view.isEnabled()) { michael@0: if (view.hasFocus()) { michael@0: needsNewFocus = true; michael@0: } michael@0: continue; michael@0: } michael@0: michael@0: if (view == actionItemBar) { michael@0: final int childCount = actionItemBar.getChildCount(); michael@0: for (int child = 0; child < childCount; child++) { michael@0: View childView = actionItemBar.getChildAt(child); michael@0: if (prevView != null) { michael@0: childView.setNextFocusLeftId(prevView.getId()); michael@0: prevView.setNextFocusRightId(childView.getId()); michael@0: } michael@0: prevView = childView; michael@0: } michael@0: } else { michael@0: if (prevView != null) { michael@0: view.setNextFocusLeftId(prevView.getId()); michael@0: prevView.setNextFocusRightId(view.getId()); michael@0: } michael@0: prevView = view; michael@0: } michael@0: } michael@0: michael@0: if (needsNewFocus) { michael@0: requestFocus(); michael@0: } michael@0: } michael@0: michael@0: public void onEditSuggestion(String suggestion) { michael@0: if (!isEditing()) { michael@0: return; michael@0: } michael@0: michael@0: urlEditLayout.onEditSuggestion(suggestion); michael@0: } michael@0: michael@0: public void setTitle(CharSequence title) { michael@0: urlDisplayLayout.setTitle(title); michael@0: } michael@0: michael@0: public void prepareTabsAnimation(PropertyAnimator animator, boolean tabsAreShown) { michael@0: if (!tabsAreShown) { michael@0: PropertyAnimator buttonsAnimator = michael@0: new PropertyAnimator(animator.getDuration(), buttonsInterpolator); michael@0: michael@0: buttonsAnimator.attach(tabsCounter, michael@0: PropertyAnimator.Property.ALPHA, michael@0: 1.0f); michael@0: michael@0: if (hasSoftMenuButton && !HardwareUtils.isTablet()) { michael@0: buttonsAnimator.attach(menuIcon, michael@0: PropertyAnimator.Property.ALPHA, michael@0: 1.0f); michael@0: } michael@0: michael@0: buttonsAnimator.start(); michael@0: michael@0: return; michael@0: } michael@0: michael@0: ViewHelper.setAlpha(tabsCounter, 0.0f); michael@0: michael@0: if (hasSoftMenuButton && !HardwareUtils.isTablet()) { michael@0: ViewHelper.setAlpha(menuIcon, 0.0f); michael@0: } michael@0: } michael@0: michael@0: public void finishTabsAnimation(boolean tabsAreShown) { michael@0: if (tabsAreShown) { michael@0: return; michael@0: } michael@0: michael@0: PropertyAnimator animator = new PropertyAnimator(150); michael@0: michael@0: animator.attach(tabsCounter, michael@0: PropertyAnimator.Property.ALPHA, michael@0: 1.0f); michael@0: michael@0: if (hasSoftMenuButton && !HardwareUtils.isTablet()) { michael@0: animator.attach(menuIcon, michael@0: PropertyAnimator.Property.ALPHA, michael@0: 1.0f); michael@0: } michael@0: michael@0: animator.start(); michael@0: } michael@0: michael@0: public void setOnActivateListener(OnActivateListener listener) { michael@0: activateListener = listener; michael@0: } michael@0: michael@0: public void setOnCommitListener(OnCommitListener listener) { michael@0: urlEditLayout.setOnCommitListener(listener); michael@0: } michael@0: michael@0: public void setOnDismissListener(OnDismissListener listener) { michael@0: urlEditLayout.setOnDismissListener(listener); michael@0: } michael@0: michael@0: public void setOnFilterListener(OnFilterListener listener) { michael@0: urlEditLayout.setOnFilterListener(listener); michael@0: } michael@0: michael@0: public void setOnFocusChangeListener(OnFocusChangeListener listener) { michael@0: focusChangeListener = listener; michael@0: } michael@0: michael@0: public void setOnStartEditingListener(OnStartEditingListener listener) { michael@0: startEditingListener = listener; michael@0: } michael@0: michael@0: public void setOnStopEditingListener(OnStopEditingListener listener) { michael@0: stopEditingListener = listener; michael@0: } michael@0: michael@0: private void showUrlEditLayout() { michael@0: setUrlEditLayoutVisibility(true, null); michael@0: } michael@0: michael@0: private void showUrlEditLayout(PropertyAnimator animator) { michael@0: setUrlEditLayoutVisibility(true, animator); michael@0: } michael@0: michael@0: private void hideUrlEditLayout() { michael@0: setUrlEditLayoutVisibility(false, null); michael@0: } michael@0: michael@0: private void hideUrlEditLayout(PropertyAnimator animator) { michael@0: setUrlEditLayoutVisibility(false, animator); michael@0: } michael@0: michael@0: private void setUrlEditLayoutVisibility(final boolean showEditLayout, PropertyAnimator animator) { michael@0: if (showEditLayout) { michael@0: urlEditLayout.prepareShowAnimation(animator); michael@0: } michael@0: michael@0: if (animator == null) { michael@0: final View viewToShow = (showEditLayout ? urlEditLayout : urlDisplayLayout); michael@0: final View viewToHide = (showEditLayout ? urlDisplayLayout : urlEditLayout); michael@0: michael@0: viewToHide.setVisibility(View.GONE); michael@0: viewToShow.setVisibility(View.VISIBLE); michael@0: michael@0: final int cancelVisibility = (showEditLayout ? View.VISIBLE : View.INVISIBLE); michael@0: setCancelVisibility(cancelVisibility); michael@0: return; michael@0: } michael@0: michael@0: animator.addPropertyAnimationListener(new PropertyAnimationListener() { michael@0: @Override michael@0: public void onPropertyAnimationStart() { michael@0: if (!showEditLayout) { michael@0: urlEditLayout.setVisibility(View.GONE); michael@0: urlDisplayLayout.setVisibility(View.VISIBLE); michael@0: michael@0: setCancelVisibility(View.INVISIBLE); michael@0: } michael@0: } michael@0: michael@0: @Override michael@0: public void onPropertyAnimationEnd() { michael@0: if (showEditLayout) { michael@0: urlDisplayLayout.setVisibility(View.GONE); michael@0: urlEditLayout.setVisibility(View.VISIBLE); michael@0: michael@0: setCancelVisibility(View.VISIBLE); michael@0: } michael@0: } michael@0: }); michael@0: } michael@0: michael@0: private void setCancelVisibility(final int visibility) { michael@0: if (editCancel != null) { michael@0: editCancel.setVisibility(visibility); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Disables and dims all toolbar elements which are not michael@0: * related to editing mode. michael@0: */ michael@0: private void updateChildrenForEditing() { michael@0: // This is for the tablet UI only michael@0: if (!HardwareUtils.isTablet()) { michael@0: return; michael@0: } michael@0: michael@0: // Disable toolbar elemens while in editing mode michael@0: final boolean enabled = !isEditing(); michael@0: michael@0: // This alpha value has to be in sync with the one used michael@0: // in setButtonEnabled(). michael@0: final float alpha = (enabled ? 1.0f : 0.24f); michael@0: michael@0: if (!enabled) { michael@0: tabsCounter.onEnterEditingMode(); michael@0: } michael@0: michael@0: tabsButton.setEnabled(enabled); michael@0: ViewHelper.setAlpha(tabsCounter, alpha); michael@0: menuButton.setEnabled(enabled); michael@0: ViewHelper.setAlpha(menuIcon, alpha); michael@0: michael@0: final int actionItemsCount = actionItemBar.getChildCount(); michael@0: for (int i = 0; i < actionItemsCount; i++) { michael@0: actionItemBar.getChildAt(i).setEnabled(enabled); michael@0: } michael@0: ViewHelper.setAlpha(actionItemBar, alpha); michael@0: michael@0: final Tab tab = Tabs.getInstance().getSelectedTab(); michael@0: if (tab != null) { michael@0: setButtonEnabled(backButton, canDoBack(tab)); michael@0: setButtonEnabled(forwardButton, canDoForward(tab)); michael@0: michael@0: // Once the editing mode is finished, we have to ensure that the michael@0: // forward button slides away if necessary. This is because we might michael@0: // have only disabled it (without hiding it) when the toolbar entered michael@0: // editing mode. michael@0: if (!isEditing()) { michael@0: animateForwardButton(canDoForward(tab) ? michael@0: ForwardButtonAnimation.SHOW : ForwardButtonAnimation.HIDE); michael@0: } michael@0: } michael@0: } michael@0: michael@0: private void setUIMode(final UIMode uiMode) { michael@0: this.uiMode = uiMode; michael@0: urlEditLayout.setEnabled(uiMode == UIMode.EDIT); michael@0: } michael@0: michael@0: /** michael@0: * Returns whether or not the URL bar is in editing mode (url bar is expanded, hiding the new michael@0: * tab button). Note that selection state is independent of editing mode. michael@0: */ michael@0: public boolean isEditing() { michael@0: return (uiMode == UIMode.EDIT); michael@0: } michael@0: michael@0: public boolean isAnimating() { michael@0: return isAnimatingEntry; michael@0: } michael@0: michael@0: public void startEditing(String url, PropertyAnimator animator) { michael@0: if (isEditing()) { michael@0: return; michael@0: } michael@0: michael@0: urlEditLayout.setText(url != null ? url : ""); michael@0: michael@0: setUIMode(UIMode.EDIT); michael@0: updateChildrenForEditing(); michael@0: michael@0: updateProgressVisibility(); michael@0: michael@0: if (startEditingListener != null) { michael@0: startEditingListener.onStartEditing(); michael@0: } michael@0: michael@0: final int curveTranslation = getUrlBarCurveTranslation(); michael@0: final int entryTranslation = getUrlBarEntryTranslation(); michael@0: shouldShrinkURLBar = (entryTranslation < 0); michael@0: michael@0: if (urlBarTranslatingEdge != null) { michael@0: urlBarTranslatingEdge.setVisibility(View.VISIBLE); michael@0: if (shouldShrinkURLBar) { michael@0: urlBarEntry.setLayoutParams(urlBarEntryShrunkenLayoutParams); michael@0: } michael@0: } michael@0: michael@0: if (Build.VERSION.SDK_INT < 11) { michael@0: showEditingWithoutAnimation(entryTranslation, curveTranslation); michael@0: } else if (HardwareUtils.isTablet()) { michael@0: // No animation. michael@0: showUrlEditLayout(); michael@0: } else { michael@0: showEditingWithPhoneAnimation(animator, entryTranslation, curveTranslation); michael@0: } michael@0: } michael@0: michael@0: private void showEditingWithoutAnimation(final int entryTranslation, michael@0: final int curveTranslation) { michael@0: showUrlEditLayout(); michael@0: michael@0: if (urlBarTranslatingEdge != null) { michael@0: ViewHelper.setTranslationX(urlBarTranslatingEdge, entryTranslation); michael@0: } michael@0: michael@0: // Prevent taps through the editing mode cancel button (bug 1001243). michael@0: tabsButton.setEnabled(false); michael@0: michael@0: ViewHelper.setTranslationX(tabsButton, curveTranslation); michael@0: ViewHelper.setTranslationX(tabsCounter, curveTranslation); michael@0: ViewHelper.setTranslationX(actionItemBar, curveTranslation); michael@0: michael@0: if (hasSoftMenuButton) { michael@0: // Prevent tabs through the editing mode cancel button (bug 1001243). michael@0: menuButton.setEnabled(false); michael@0: michael@0: ViewHelper.setTranslationX(menuButton, curveTranslation); michael@0: ViewHelper.setTranslationX(menuIcon, curveTranslation); michael@0: } michael@0: } michael@0: michael@0: private void showEditingWithPhoneAnimation(final PropertyAnimator animator, michael@0: final int entryTranslation, final int curveTranslation) { michael@0: if (isAnimatingEntry) michael@0: return; michael@0: michael@0: urlDisplayLayout.prepareStartEditingAnimation(); michael@0: michael@0: // Slide toolbar elements. michael@0: if (urlBarTranslatingEdge != null) { michael@0: animator.attach(urlBarTranslatingEdge, michael@0: PropertyAnimator.Property.TRANSLATION_X, michael@0: entryTranslation); michael@0: } michael@0: michael@0: animator.attach(tabsButton, michael@0: PropertyAnimator.Property.TRANSLATION_X, michael@0: curveTranslation); michael@0: animator.attach(tabsCounter, michael@0: PropertyAnimator.Property.TRANSLATION_X, michael@0: curveTranslation); michael@0: animator.attach(actionItemBar, michael@0: PropertyAnimator.Property.TRANSLATION_X, michael@0: curveTranslation); michael@0: michael@0: if (hasSoftMenuButton) { michael@0: animator.attach(menuButton, michael@0: PropertyAnimator.Property.TRANSLATION_X, michael@0: curveTranslation); michael@0: michael@0: animator.attach(menuIcon, michael@0: PropertyAnimator.Property.TRANSLATION_X, michael@0: curveTranslation); michael@0: } michael@0: michael@0: showUrlEditLayout(animator); michael@0: michael@0: animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { michael@0: @Override michael@0: public void onPropertyAnimationStart() { michael@0: } michael@0: michael@0: @Override michael@0: public void onPropertyAnimationEnd() { michael@0: isAnimatingEntry = false; michael@0: } michael@0: }); michael@0: michael@0: isAnimatingEntry = true; michael@0: } michael@0: michael@0: /** michael@0: * Exits edit mode without updating the toolbar title. michael@0: * michael@0: * @return the url that was entered michael@0: */ michael@0: public String cancelEdit() { michael@0: Telemetry.stopUISession(TelemetryContract.Session.AWESOMESCREEN); michael@0: return stopEditing(); michael@0: } michael@0: michael@0: /** michael@0: * Exits edit mode, updating the toolbar title with the url that was just entered. michael@0: * michael@0: * @return the url that was entered michael@0: */ michael@0: public String commitEdit() { michael@0: final String url = stopEditing(); michael@0: if (!TextUtils.isEmpty(url)) { michael@0: setTitle(url); michael@0: } michael@0: return url; michael@0: } michael@0: michael@0: private String stopEditing() { michael@0: final String url = urlEditLayout.getText(); michael@0: if (!isEditing()) { michael@0: return url; michael@0: } michael@0: setUIMode(UIMode.DISPLAY); michael@0: michael@0: updateChildrenForEditing(); michael@0: michael@0: if (stopEditingListener != null) { michael@0: stopEditingListener.onStopEditing(); michael@0: } michael@0: michael@0: updateProgressVisibility(); michael@0: michael@0: // The animation looks cleaner if the text in the URL bar is michael@0: // not selected so clear the selection by clearing focus. michael@0: urlEditLayout.clearFocus(); michael@0: michael@0: if (Build.VERSION.SDK_INT < 11) { michael@0: stopEditingWithoutAnimation(); michael@0: } else if (HardwareUtils.isTablet()) { michael@0: // No animation. michael@0: hideUrlEditLayout(); michael@0: } else { michael@0: stopEditingWithPhoneAnimation(); michael@0: } michael@0: michael@0: return url; michael@0: } michael@0: michael@0: private void stopEditingWithoutAnimation() { michael@0: hideUrlEditLayout(); michael@0: michael@0: updateTabCountAndAnimate(Tabs.getInstance().getDisplayCount()); michael@0: michael@0: if (urlBarTranslatingEdge != null) { michael@0: urlBarTranslatingEdge.setVisibility(View.INVISIBLE); michael@0: ViewHelper.setTranslationX(urlBarTranslatingEdge, 0); michael@0: if (shouldShrinkURLBar) { michael@0: urlBarEntry.setLayoutParams(urlBarEntryDefaultLayoutParams); michael@0: } michael@0: } michael@0: michael@0: tabsButton.setEnabled(true); michael@0: michael@0: ViewHelper.setTranslationX(tabsButton, 0); michael@0: ViewHelper.setTranslationX(tabsCounter, 0); michael@0: ViewHelper.setTranslationX(actionItemBar, 0); michael@0: michael@0: if (hasSoftMenuButton) { michael@0: menuButton.setEnabled(true); michael@0: michael@0: ViewHelper.setTranslationX(menuButton, 0); michael@0: ViewHelper.setTranslationX(menuIcon, 0); michael@0: } michael@0: } michael@0: michael@0: private void stopEditingWithPhoneAnimation() { michael@0: final PropertyAnimator contentAnimator = new PropertyAnimator(250); michael@0: contentAnimator.setUseHardwareLayer(false); michael@0: michael@0: // Slide the toolbar back to its original size. michael@0: if (urlBarTranslatingEdge != null) { michael@0: contentAnimator.attach(urlBarTranslatingEdge, michael@0: PropertyAnimator.Property.TRANSLATION_X, michael@0: 0); michael@0: } michael@0: michael@0: contentAnimator.attach(tabsButton, michael@0: PropertyAnimator.Property.TRANSLATION_X, michael@0: 0); michael@0: contentAnimator.attach(tabsCounter, michael@0: PropertyAnimator.Property.TRANSLATION_X, michael@0: 0); michael@0: contentAnimator.attach(actionItemBar, michael@0: PropertyAnimator.Property.TRANSLATION_X, michael@0: 0); michael@0: michael@0: if (hasSoftMenuButton) { michael@0: contentAnimator.attach(menuButton, michael@0: PropertyAnimator.Property.TRANSLATION_X, michael@0: 0); michael@0: michael@0: contentAnimator.attach(menuIcon, michael@0: PropertyAnimator.Property.TRANSLATION_X, michael@0: 0); michael@0: } michael@0: michael@0: hideUrlEditLayout(contentAnimator); michael@0: michael@0: contentAnimator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { michael@0: @Override michael@0: public void onPropertyAnimationStart() { michael@0: } michael@0: michael@0: @Override michael@0: public void onPropertyAnimationEnd() { michael@0: if (urlBarTranslatingEdge != null) { michael@0: urlBarTranslatingEdge.setVisibility(View.INVISIBLE); michael@0: if (shouldShrinkURLBar) { michael@0: urlBarEntry.setLayoutParams(urlBarEntryDefaultLayoutParams); michael@0: } michael@0: } michael@0: michael@0: PropertyAnimator buttonsAnimator = new PropertyAnimator(300); michael@0: urlDisplayLayout.prepareStopEditingAnimation(buttonsAnimator); michael@0: buttonsAnimator.start(); michael@0: michael@0: isAnimatingEntry = false; michael@0: michael@0: // Trigger animation to update the tabs counter once the michael@0: // tabs button is back on screen. michael@0: updateTabCountAndAnimate(Tabs.getInstance().getDisplayCount()); michael@0: } michael@0: }); michael@0: michael@0: isAnimatingEntry = true; michael@0: contentAnimator.start(); michael@0: } michael@0: michael@0: private void setButtonEnabled(ImageButton button, boolean enabled) { michael@0: final Drawable drawable = button.getDrawable(); michael@0: if (drawable != null) { michael@0: // This alpha value has to be in sync with the one used michael@0: // in updateChildrenForEditing(). michael@0: drawable.setAlpha(enabled ? 255 : 61); michael@0: } michael@0: michael@0: button.setEnabled(enabled); michael@0: } michael@0: michael@0: public void updateBackButton(Tab tab) { michael@0: setButtonEnabled(backButton, canDoBack(tab)); michael@0: } michael@0: michael@0: private void animateForwardButton(final ForwardButtonAnimation animation) { michael@0: // If the forward button is not visible, we must be michael@0: // in the phone UI. michael@0: if (forwardButton.getVisibility() != View.VISIBLE) { michael@0: return; michael@0: } michael@0: michael@0: final boolean showing = (animation == ForwardButtonAnimation.SHOW); michael@0: michael@0: // if the forward button's margin is non-zero, this means it has already michael@0: // been animated to be visible¸ and vice-versa. michael@0: MarginLayoutParams fwdParams = (MarginLayoutParams) forwardButton.getLayoutParams(); michael@0: if ((fwdParams.leftMargin > defaultForwardMargin && showing) || michael@0: (fwdParams.leftMargin == defaultForwardMargin && !showing)) { michael@0: return; michael@0: } michael@0: michael@0: // We want the forward button to show immediately when switching tabs michael@0: final PropertyAnimator forwardAnim = michael@0: new PropertyAnimator(isSwitchingTabs ? 10 : FORWARD_ANIMATION_DURATION); michael@0: final int width = forwardButton.getWidth() / 2; michael@0: michael@0: forwardAnim.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { michael@0: @Override michael@0: public void onPropertyAnimationStart() { michael@0: if (!showing) { michael@0: // Set the margin before the transition when hiding the forward button. We michael@0: // have to do this so that the favicon isn't clipped during the transition michael@0: MarginLayoutParams layoutParams = michael@0: (MarginLayoutParams) urlDisplayLayout.getLayoutParams(); michael@0: layoutParams.leftMargin = 0; michael@0: michael@0: // Do the same on the URL edit container michael@0: layoutParams = (MarginLayoutParams) urlEditLayout.getLayoutParams(); michael@0: layoutParams.leftMargin = 0; michael@0: michael@0: requestLayout(); michael@0: // Note, we already translated the favicon, site security, and text field michael@0: // in prepareForwardAnimation, so they should appear to have not moved at michael@0: // all at this point. michael@0: } michael@0: } michael@0: michael@0: @Override michael@0: public void onPropertyAnimationEnd() { michael@0: if (showing) { michael@0: MarginLayoutParams layoutParams = michael@0: (MarginLayoutParams) urlDisplayLayout.getLayoutParams(); michael@0: layoutParams.leftMargin = urlBarViewOffset; michael@0: michael@0: layoutParams = (MarginLayoutParams) urlEditLayout.getLayoutParams(); michael@0: layoutParams.leftMargin = urlBarViewOffset; michael@0: } michael@0: michael@0: urlDisplayLayout.finishForwardAnimation(); michael@0: michael@0: MarginLayoutParams layoutParams = (MarginLayoutParams) forwardButton.getLayoutParams(); michael@0: layoutParams.leftMargin = defaultForwardMargin + (showing ? width : 0); michael@0: ViewHelper.setTranslationX(forwardButton, 0); michael@0: michael@0: requestLayout(); michael@0: } michael@0: }); michael@0: michael@0: prepareForwardAnimation(forwardAnim, animation, width); michael@0: forwardAnim.start(); michael@0: } michael@0: michael@0: public void updateForwardButton(Tab tab) { michael@0: final boolean enabled = canDoForward(tab); michael@0: if (forwardButton.isEnabled() == enabled) michael@0: return; michael@0: michael@0: // Save the state on the forward button so that we can skip animations michael@0: // when there's nothing to change michael@0: setButtonEnabled(forwardButton, enabled); michael@0: animateForwardButton(enabled ? ForwardButtonAnimation.SHOW : ForwardButtonAnimation.HIDE); michael@0: } michael@0: michael@0: private void prepareForwardAnimation(PropertyAnimator anim, ForwardButtonAnimation animation, int width) { michael@0: if (animation == ForwardButtonAnimation.HIDE) { michael@0: anim.attach(forwardButton, michael@0: PropertyAnimator.Property.TRANSLATION_X, michael@0: -width); michael@0: anim.attach(forwardButton, michael@0: PropertyAnimator.Property.ALPHA, michael@0: 0); michael@0: michael@0: } else { michael@0: anim.attach(forwardButton, michael@0: PropertyAnimator.Property.TRANSLATION_X, michael@0: width); michael@0: anim.attach(forwardButton, michael@0: PropertyAnimator.Property.ALPHA, michael@0: 1); michael@0: } michael@0: michael@0: urlDisplayLayout.prepareForwardAnimation(anim, animation, width); michael@0: } michael@0: michael@0: @Override michael@0: public boolean addActionItem(View actionItem) { michael@0: actionItemBar.addView(actionItem); michael@0: return true; michael@0: } michael@0: michael@0: @Override michael@0: public void removeActionItem(View actionItem) { michael@0: actionItemBar.removeView(actionItem); michael@0: } michael@0: michael@0: @Override michael@0: public void setPrivateMode(boolean isPrivate) { michael@0: super.setPrivateMode(isPrivate); michael@0: michael@0: tabsButton.setPrivateMode(isPrivate); michael@0: menuButton.setPrivateMode(isPrivate); michael@0: menuIcon.setPrivateMode(isPrivate); michael@0: urlEditLayout.setPrivateMode(isPrivate); michael@0: michael@0: if (backButton instanceof BackButton) { michael@0: ((BackButton) backButton).setPrivateMode(isPrivate); michael@0: } michael@0: michael@0: if (forwardButton instanceof ForwardButton) { michael@0: ((ForwardButton) forwardButton).setPrivateMode(isPrivate); michael@0: } michael@0: } michael@0: michael@0: public void show() { michael@0: setVisibility(View.VISIBLE); michael@0: } michael@0: michael@0: public void hide() { michael@0: setVisibility(View.GONE); michael@0: } michael@0: michael@0: public View getDoorHangerAnchor() { michael@0: return urlDisplayLayout.getDoorHangerAnchor(); michael@0: } michael@0: michael@0: public void onDestroy() { michael@0: Tabs.unregisterOnTabsChangedListener(this); michael@0: michael@0: unregisterEventListener("Reader:Click"); michael@0: unregisterEventListener("Reader:LongClick"); michael@0: } michael@0: michael@0: public boolean openOptionsMenu() { michael@0: if (!hasSoftMenuButton) { michael@0: return false; michael@0: } michael@0: michael@0: // Initialize the popup. michael@0: if (menuPopup == null) { michael@0: View panel = activity.getMenuPanel(); michael@0: menuPopup = new MenuPopup(activity); michael@0: menuPopup.setPanelView(panel); michael@0: michael@0: menuPopup.setOnDismissListener(new PopupWindow.OnDismissListener() { michael@0: @Override michael@0: public void onDismiss() { michael@0: activity.onOptionsMenuClosed(null); michael@0: } michael@0: }); michael@0: } michael@0: michael@0: GeckoAppShell.getGeckoInterface().invalidateOptionsMenu(); michael@0: if (!menuPopup.isShowing()) { michael@0: menuPopup.showAsDropDown(menuButton); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: public boolean closeOptionsMenu() { michael@0: if (!hasSoftMenuButton) { michael@0: return false; michael@0: } michael@0: michael@0: if (menuPopup != null && menuPopup.isShowing()) { michael@0: menuPopup.dismiss(); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: private void registerEventListener(String event) { michael@0: GeckoAppShell.getEventDispatcher().registerEventListener(event, this); michael@0: } michael@0: michael@0: private void unregisterEventListener(String event) { michael@0: GeckoAppShell.getEventDispatcher().unregisterEventListener(event, this); michael@0: } michael@0: michael@0: @Override michael@0: public void handleMessage(String event, JSONObject message) { michael@0: Log.d(LOGTAG, "handleMessage: " + event); michael@0: if (event.equals("Reader:Click")) { michael@0: Tab tab = Tabs.getInstance().getSelectedTab(); michael@0: if (tab != null) { michael@0: tab.toggleReaderMode(); michael@0: } michael@0: } else if (event.equals("Reader:LongClick")) { michael@0: Tab tab = Tabs.getInstance().getSelectedTab(); michael@0: if (tab != null) { michael@0: tab.addToReadingList(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: @Override michael@0: public void onLightweightThemeChanged() { michael@0: Drawable drawable = theme.getDrawable(this); michael@0: if (drawable == null) michael@0: return; michael@0: michael@0: StateListDrawable stateList = new StateListDrawable(); michael@0: stateList.addState(PRIVATE_STATE_SET, getColorDrawable(R.color.background_private)); michael@0: stateList.addState(EMPTY_STATE_SET, drawable); michael@0: michael@0: setBackgroundDrawable(stateList); michael@0: michael@0: if (editCancel != null) { michael@0: editCancel.onLightweightThemeChanged(); michael@0: } michael@0: } michael@0: michael@0: @Override michael@0: public void onLightweightThemeReset() { michael@0: setBackgroundResource(R.drawable.url_bar_bg); michael@0: if (editCancel != null) { michael@0: editCancel.onLightweightThemeReset(); michael@0: } michael@0: } michael@0: }