--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -9,16 +9,18 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.net.URLEncoder;
import java.util.EnumSet;
import java.util.Vector;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import org.mozilla.gecko.DynamicToolbar.PinReason;
+import org.mozilla.gecko.DynamicToolbar.VisibilityTransition;
import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
import org.mozilla.gecko.animation.PropertyAnimator;
import org.mozilla.gecko.animation.ViewHelper;
import org.mozilla.gecko.db.BrowserContract.Combined;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.favicons.LoadFaviconTask;
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
@@ -104,29 +106,26 @@ abstract public class BrowserApp extends
GeckoLayerClient.OnMetricsChangedListener,
BrowserSearch.OnSearchListener,
BrowserSearch.OnEditSuggestionListener,
HomePager.OnNewTabsListener,
OnUrlOpenListener,
ActionModeCompat.Presenter {
private static final String LOGTAG = "GeckoBrowserApp";
- private static final String PREF_CHROME_DYNAMICTOOLBAR = "browser.chrome.dynamictoolbar";
-
private static final int TABS_ANIMATION_DURATION = 450;
private static final int READER_ADD_SUCCESS = 0;
private static final int READER_ADD_FAILED = 1;
private static final int READER_ADD_DUPLICATE = 2;
private static final String ADD_SHORTCUT_TOAST = "add_shortcut_toast";
public static final String GUEST_BROWSING_ARG = "--guest";
private static final String STATE_ABOUT_HOME_TOP_PADDING = "abouthome_top_padding";
- private static final String STATE_DYNAMIC_TOOLBAR_ENABLED = "dynamic_toolbar";
private static final String BROWSER_SEARCH_TAG = "browser_search";
private BrowserSearch mBrowserSearch;
private View mBrowserSearchContainer;
public ViewFlipper mViewFlipper;
public ActionModeCompatView mActionBar;
private BrowserToolbar mBrowserToolbar;
@@ -166,48 +165,43 @@ abstract public class BrowserApp extends
t -= 1.0f;
return t * t * t * t * t + 1.0f;
}
};
private FindInPageBar mFindInPageBar;
private MediaCastingBar mMediaCastingBar;
- private boolean mAccessibilityEnabled = false;
-
// We'll ask for feedback after the user launches the app this many times.
private static final int FEEDBACK_LAUNCH_COUNT = 15;
- // Whether the dynamic toolbar pref is enabled.
- private boolean mDynamicToolbarEnabled = false;
-
// Stored value of the toolbar height, so we know when it's changed.
private int mToolbarHeight = 0;
// Stored value of whether the last metrics change allowed for toolbar
// scrolling.
private boolean mDynamicToolbarCanScroll = false;
- private Integer mPrefObserverId;
-
private SharedPreferencesHelper mSharedPreferencesHelper;
private OrderedBroadcastHelper mOrderedBroadcastHelper;
private BrowserHealthReporter mBrowserHealthReporter;
// The tab to be selected on editing mode exit.
private Integer mTargetTabForEditingMode = null;
// The animator used to toggle HomePager visibility has a race where if the HomePager is shown
// (starting the animation), the HomePager is hidden, and the HomePager animation completes,
// both the web content and the HomePager will be hidden. This flag is used to prevent the
// race by determining if the web content should be hidden at the animation's end.
private boolean mHideWebContentOnAnimationEnd = false;
+ private DynamicToolbar mDynamicToolbar = new DynamicToolbar();
+
@Override
public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
if (tab == null) {
// Only RESTORED is allowed a null tab: it's the only event that
// isn't tied to a specific tab.
if (msg != Tabs.TabEvents.RESTORED) {
throw new IllegalArgumentException("onTabChanged:" + msg + " must specify a tab.");
}
@@ -238,19 +232,18 @@ abstract public class BrowserApp extends
}
});
}
break;
case START:
if (Tabs.getInstance().isSelectedTab(tab)) {
invalidateOptionsMenu();
- if (isDynamicToolbarEnabled()) {
- // Show the toolbar.
- mLayerView.getLayerMarginsAnimator().showMargins(false);
+ if (mDynamicToolbar.isEnabled()) {
+ mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE);
}
}
break;
case LOAD_ERROR:
case STOP:
case MENU_UPDATED:
if (Tabs.getInstance().isSelectedTab(tab)) {
invalidateOptionsMenu();
@@ -282,30 +275,28 @@ abstract public class BrowserApp extends
// Gamepad support only exists in API-level >= 9
if (Build.VERSION.SDK_INT >= 9 &&
(event.getSource() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
switch (keyCode) {
case KeyEvent.KEYCODE_BUTTON_Y:
// Toggle/focus the address bar on gamepad-y button.
if (mViewFlipper.getVisibility() == View.VISIBLE) {
- if (isDynamicToolbarEnabled() && !isHomePagerVisible()) {
+ if (mDynamicToolbar.isEnabled() && !isHomePagerVisible()) {
+ mDynamicToolbar.setVisible(false, VisibilityTransition.ANIMATE);
if (mLayerView != null) {
- mLayerView.getLayerMarginsAnimator().hideMargins(false);
mLayerView.requestFocus();
}
} else {
// Just focus the address bar when about:home is visible
// or when the dynamic toolbar isn't enabled.
mBrowserToolbar.requestFocusFromTouch();
}
} else {
- if (mLayerView != null) {
- mLayerView.getLayerMarginsAnimator().showMargins(false);
- }
+ mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE);
mBrowserToolbar.requestFocusFromTouch();
}
return true;
case KeyEvent.KEYCODE_BUTTON_L1:
// Go back on L1
Tabs.getInstance().getSelectedTab().doBack();
return true;
case KeyEvent.KEYCODE_BUTTON_R1:
@@ -584,46 +575,24 @@ abstract public class BrowserApp extends
}
return new NdefMessage(new NdefRecord[] { NdefRecord.createUri(tab.getURL()) });
}
}, this);
}
}
if (savedInstanceState != null) {
- mDynamicToolbarEnabled = savedInstanceState.getBoolean(STATE_DYNAMIC_TOOLBAR_ENABLED);
+ mDynamicToolbar.onRestoreInstanceState(savedInstanceState);
mHomePagerContainer.setPadding(0, savedInstanceState.getInt(STATE_ABOUT_HOME_TOP_PADDING), 0, 0);
}
- // Listen to the dynamic toolbar pref
- mPrefObserverId = PrefsHelper.getPref(PREF_CHROME_DYNAMICTOOLBAR, new PrefsHelper.PrefHandlerBase() {
+ mDynamicToolbar.setEnabledChangedListener(new DynamicToolbar.OnEnabledChangedListener() {
@Override
- public void prefValue(String pref, boolean value) {
- if (value == mDynamicToolbarEnabled) {
- return;
- }
- mDynamicToolbarEnabled = value;
-
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- // If accessibility is enabled, the dynamic toolbar is
- // forced to be off.
- if (!mAccessibilityEnabled) {
- setDynamicToolbarEnabled(mDynamicToolbarEnabled);
- }
- }
- });
- }
-
- @Override
- public boolean isObserver() {
- // We want to be notified of changes to be able to switch mode
- // without restarting.
- return true;
+ public void onEnabledChanged(boolean enabled) {
+ setDynamicToolbarEnabled(enabled);
}
});
// Set the maximum bits-per-pixel the favicon system cares about.
IconDirectoryEntry.setMaxBPP(GeckoAppShell.getScreenDepth());
}
@Override
@@ -654,16 +623,18 @@ abstract public class BrowserApp extends
@Override
public void onPause() {
super.onPause();
// Register for Prompt:ShowTop so we can foreground this activity even if it's hidden.
registerEventListener("Prompt:ShowTop");
}
private void setDynamicToolbarEnabled(boolean enabled) {
+ ThreadUtils.assertOnUiThread();
+
if (enabled) {
if (mLayerView != null) {
mLayerView.getLayerClient().setOnMetricsChangedListener(this);
}
setToolbarMargin(0);
mHomePagerContainer.setPadding(0, mViewFlipper.getHeight(), 0, 0);
} else {
// Immediately show the toolbar when disabling the dynamic
@@ -675,20 +646,16 @@ abstract public class BrowserApp extends
if (mViewFlipper != null) {
ViewHelper.setTranslationY(mViewFlipper, 0);
}
}
refreshToolbarHeight();
}
- private boolean isDynamicToolbarEnabled() {
- return mDynamicToolbarEnabled && !mAccessibilityEnabled;
- }
-
private static boolean isAboutHome(final Tab tab) {
return AboutPages.isAboutHome(tab.getURL());
}
@Override
public boolean onSearchRequested() {
enterEditingMode();
return true;
@@ -754,34 +721,23 @@ abstract public class BrowserApp extends
return true;
}
return false;
}
@Override
public void setAccessibilityEnabled(boolean enabled) {
- if (mAccessibilityEnabled == enabled) {
- return;
- }
-
- // Disable the dynamic toolbar when accessibility features are enabled,
- // and re-read the preference when they're disabled.
- mAccessibilityEnabled = enabled;
- if (mDynamicToolbarEnabled) {
- setDynamicToolbarEnabled(!enabled);
- }
+ mDynamicToolbar.setAccessibilityEnabled(enabled);
}
@Override
public void onDestroy() {
- if (mPrefObserverId != null) {
- PrefsHelper.removeObserver(mPrefObserverId);
- mPrefObserverId = null;
- }
+ mDynamicToolbar.destroy();
+
if (mBrowserToolbar != null)
mBrowserToolbar.onDestroy();
if (mFindInPageBar != null) {
mFindInPageBar.onDestroy();
mFindInPageBar = null;
}
@@ -833,22 +789,18 @@ abstract public class BrowserApp extends
}
@Override
protected void initializeChrome() {
super.initializeChrome();
mDoorHangerPopup.setAnchor(mBrowserToolbar.getDoorHangerAnchor());
- // Listen to margin changes to position the toolbar correctly
- if (isDynamicToolbarEnabled()) {
- refreshToolbarHeight();
- mLayerView.getLayerMarginsAnimator().showMargins(true);
- mLayerView.getLayerClient().setOnMetricsChangedListener(this);
- }
+ mDynamicToolbar.setLayerView(mLayerView);
+ setDynamicToolbarEnabled(mDynamicToolbar.isEnabled());
// Intercept key events for gamepad shortcuts
mLayerView.setOnKeyListener(this);
// Initialize the actionbar menu items on startup for both large and small tablets
if (HardwareUtils.isTablet()) {
onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, null);
invalidateOptionsMenu();
@@ -898,17 +850,17 @@ abstract public class BrowserApp extends
// If the page has shrunk so that the toolbar no longer scrolls, make
// sure the toolbar is visible.
if (aMetrics.getPageHeight() <= aMetrics.getHeight()) {
if (mDynamicToolbarCanScroll) {
mDynamicToolbarCanScroll = false;
if (mViewFlipper.getVisibility() != View.VISIBLE) {
ThreadUtils.postToUiThread(new Runnable() {
public void run() {
- mLayerView.getLayerMarginsAnimator().showMargins(false);
+ mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE);
}
});
}
}
} else {
mDynamicToolbarCanScroll = true;
}
@@ -925,59 +877,61 @@ abstract public class BrowserApp extends
});
if (mFormAssistPopup != null)
mFormAssistPopup.onMetricsChanged(aMetrics);
}
@Override
public void onPanZoomStopped() {
- if (!isDynamicToolbarEnabled() || isHomePagerVisible()) {
+ if (!mDynamicToolbar.isEnabled() || isHomePagerVisible()) {
return;
}
// Make sure the toolbar is fully hidden or fully shown when the user
// lifts their finger. If the page is shorter than the viewport, the
// toolbar is always shown.
ImmutableViewportMetrics metrics = mLayerView.getViewportMetrics();
if (metrics.getPageHeight() < metrics.getHeight()
|| metrics.marginTop >= mToolbarHeight / 2) {
- mLayerView.getLayerMarginsAnimator().showMargins(false);
+ mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE);
} else {
- mLayerView.getLayerMarginsAnimator().hideMargins(false);
+ mDynamicToolbar.setVisible(false, VisibilityTransition.ANIMATE);
}
}
public void refreshToolbarHeight() {
+ ThreadUtils.assertOnUiThread();
+
int height = 0;
if (mViewFlipper != null) {
height = mViewFlipper.getHeight();
}
- if (!isDynamicToolbarEnabled() || isHomePagerVisible()) {
+ if (!mDynamicToolbar.isEnabled() || isHomePagerVisible()) {
// Use aVisibleHeight here so that when the dynamic toolbar is
// enabled, the padding will animate with the toolbar becoming
// visible.
- if (isDynamicToolbarEnabled()) {
+ if (mDynamicToolbar.isEnabled()) {
// When the dynamic toolbar is enabled, set the padding on the
// about:home widget directly - this is to avoid resizing the
// LayerView, which can cause visible artifacts.
mHomePagerContainer.setPadding(0, height, 0, 0);
} else {
setToolbarMargin(height);
height = 0;
}
} else {
setToolbarMargin(0);
}
if (mLayerView != null && height != mToolbarHeight) {
mToolbarHeight = height;
mLayerView.getLayerMarginsAnimator().setMaxMargins(0, height, 0, 0);
- mLayerView.getLayerMarginsAnimator().showMargins(true);
+ mDynamicToolbar.setVisible(true, VisibilityTransition.IMMEDIATE);
}
}
@Override
void toggleChrome(final boolean aShow) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
@@ -1346,22 +1300,22 @@ abstract public class BrowserApp extends
-height);
}
mTabsPanel.prepareTabsAnimation(mMainLayoutAnimator);
mBrowserToolbar.prepareTabsAnimation(mMainLayoutAnimator, areTabsShown());
// If the tabs layout is animating onto the screen, pin the dynamic
// toolbar.
- if (mLayerView != null && isDynamicToolbarEnabled()) {
+ if (mDynamicToolbar.isEnabled()) {
if (width > 0 && height > 0) {
- mLayerView.getLayerMarginsAnimator().setMarginsPinned(true);
- mLayerView.getLayerMarginsAnimator().showMargins(false);
+ mDynamicToolbar.setPinned(true, PinReason.RELAYOUT);
+ mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE);
} else {
- mLayerView.getLayerMarginsAnimator().setMarginsPinned(false);
+ mDynamicToolbar.setPinned(false, PinReason.RELAYOUT);
}
}
mMainLayoutAnimator.start();
}
@Override
public void onPropertyAnimationStart() {
@@ -1377,17 +1331,17 @@ abstract public class BrowserApp extends
mTabsPanel.finishTabsAnimation();
mMainLayoutAnimator = null;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- outState.putBoolean(STATE_DYNAMIC_TOOLBAR_ENABLED, mDynamicToolbarEnabled);
+ mDynamicToolbar.onSaveInstanceState(outState);
outState.putInt(STATE_ABOUT_HOME_TOP_PADDING, mHomePagerContainer.getPaddingTop());
}
/**
* Attempts to switch to an open tab with the given URL.
*
* @return true if we successfully switched to a tab, false otherwise.
*/
@@ -1639,19 +1593,18 @@ abstract public class BrowserApp extends
if (mBrowserToolbar.isEditing()) {
return;
}
if (isAboutHome(tab)) {
final String pageId = AboutPages.getPageIdFromAboutHomeUrl(tab.getURL());
showHomePager(pageId);
- if (isDynamicToolbarEnabled()) {
- // Show the toolbar.
- mLayerView.getLayerMarginsAnimator().showMargins(false);
+ if (mDynamicToolbar.isEnabled()) {
+ mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE);
}
} else {
hideHomePager();
}
}
@Override
public void onLocaleReady(final String locale) {
@@ -1680,18 +1633,18 @@ abstract public class BrowserApp extends
return;
}
// Refresh toolbar height to possibly restore the toolbar padding
refreshToolbarHeight();
// Show the toolbar before hiding about:home so the
// onMetricsChanged callback still works.
- if (isDynamicToolbarEnabled() && mLayerView != null) {
- mLayerView.getLayerMarginsAnimator().showMargins(true);
+ if (mDynamicToolbar.isEnabled()) {
+ mDynamicToolbar.setVisible(true, VisibilityTransition.IMMEDIATE);
}
if (mHomePager == null) {
final ViewStub homePagerStub = (ViewStub) findViewById(R.id.home_pager_stub);
mHomePager = (HomePager) homePagerStub.inflate();
HomeBanner homeBanner = (HomeBanner) findViewById(R.id.home_banner);
mHomePager.setBanner(homeBanner);
@@ -2049,44 +2002,45 @@ abstract public class BrowserApp extends
// Scroll custom menu to the top
if (mMenuPanel != null)
mMenuPanel.scrollTo(0, 0);
if (!mBrowserToolbar.openOptionsMenu())
super.openOptionsMenu();
- if (isDynamicToolbarEnabled() && mLayerView != null)
- mLayerView.getLayerMarginsAnimator().showMargins(false);
+ if (mDynamicToolbar.isEnabled()) {
+ mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE);
+ }
}
@Override
public void closeOptionsMenu() {
if (!mBrowserToolbar.closeOptionsMenu())
super.closeOptionsMenu();
}
@Override
public void setFullScreen(final boolean fullscreen) {
super.setFullScreen(fullscreen);
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
if (fullscreen) {
mViewFlipper.setVisibility(View.GONE);
- if (isDynamicToolbarEnabled()) {
- mLayerView.getLayerMarginsAnimator().hideMargins(true);
+ if (mDynamicToolbar.isEnabled()) {
+ mDynamicToolbar.setVisible(false, VisibilityTransition.IMMEDIATE);
mLayerView.getLayerMarginsAnimator().setMaxMargins(0, 0, 0, 0);
} else {
setToolbarMargin(0);
}
} else {
mViewFlipper.setVisibility(View.VISIBLE);
- if (isDynamicToolbarEnabled()) {
- mLayerView.getLayerMarginsAnimator().showMargins(true);
+ if (mDynamicToolbar.isEnabled()) {
+ mDynamicToolbar.setVisible(true, VisibilityTransition.IMMEDIATE);
mLayerView.getLayerMarginsAnimator().setMaxMargins(0, mToolbarHeight, 0, 0);
}
}
}
});
}
@Override
@@ -2657,26 +2611,26 @@ abstract public class BrowserApp extends
@Override
public void startActionModeCompat(final ActionModeCompat.Callback callback) {
// If actionMode is null, we're not currently showing one. Flip to the action mode view
if (mActionMode == null) {
mViewFlipper.showNext();
LayerMarginsAnimator margins = mLayerView.getLayerMarginsAnimator();
// If the toolbar is dynamic and not currently showing, just slide it in
- if (isDynamicToolbarEnabled() && !margins.areMarginsShown()) {
+ if (mDynamicToolbar.isEnabled() && !margins.areMarginsShown()) {
margins.setMaxMargins(0, mViewFlipper.getHeight(), 0, 0);
- margins.showMargins(false);
+ mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE);
mShowActionModeEndAnimation = true;
} else {
// Otherwise, we animate the actionbar itself
mActionBar.animateIn();
}
- margins.setMarginsPinned(true);
+ mDynamicToolbar.setPinned(true, PinReason.ACTION_MODE);
} else {
// Otherwise, we're already showing an action mode. Just finish it and show the new one
mActionMode.finish();
}
mActionMode = new ActionModeCompat(BrowserApp.this, callback, mActionBar);
if (callback.onCreateActionMode(mActionMode, mActionMode.getMenu())) {
mActionMode.invalidate();
@@ -2687,25 +2641,24 @@ abstract public class BrowserApp extends
@Override
public void endActionModeCompat() {
if (mActionMode == null) {
return;
}
mActionMode.finish();
mActionMode = null;
- final LayerMarginsAnimator margins = mLayerView.getLayerMarginsAnimator();
- margins.setMarginsPinned(false);
+ mDynamicToolbar.setPinned(false, PinReason.ACTION_MODE);
mViewFlipper.showPrevious();
// Only slide the urlbar out if it was hidden when the action mode started
// Don't animate hiding it so that there's no flash as we switch back to url mode
if (mShowActionModeEndAnimation) {
- margins.hideMargins(true);
+ mDynamicToolbar.setVisible(false, VisibilityTransition.IMMEDIATE);
mShowActionModeEndAnimation = false;
}
}
@Override
protected HealthRecorder createHealthRecorder(final Context context,
final String profilePath,
final EventDispatcher dispatcher,
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -126,16 +126,17 @@ gbjar.sources += [
'db/PasswordsProvider.java',
'db/PerProfileDatabases.java',
'db/ReadingListProvider.java',
'db/SQLiteBridgeContentProvider.java',
'db/TabsProvider.java',
'db/TransactionalProvider.java',
'Distribution.java',
'DoorHangerPopup.java',
+ 'DynamicToolbar.java',
'EditBookmarkDialog.java',
'EventDispatcher.java',
'favicons/cache/FaviconCache.java',
'favicons/cache/FaviconCacheElement.java',
'favicons/cache/FaviconsForURL.java',
'favicons/decoders/FaviconDecoder.java',
'favicons/decoders/ICODecoder.java',
'favicons/decoders/IconDirectoryEntry.java',