Bug 681805 - Conditional forward for Android tablets. r=lucasr
☠☠ backed out by 54618ad94522 ☠ ☠
authorWes Johnston <wjohnston@mozilla.com>
Wed, 19 Dec 2012 09:29:02 -0800
changeset 125699 06d72606ec56ce8c151b30fe157d2bc567c7d87a
parent 125698 47debf3f344cd1feff97922f59a35466c6d9d1a0
child 125700 5d59a9ec28f443c2c0897bd1df863e8740d01d08
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslucasr
bugs681805
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 681805 - Conditional forward for Android tablets. r=lucasr
mobile/android/base/BrowserToolbar.java
mobile/android/base/PropertyAnimator.java
mobile/android/base/resources/values-large-v11/dimens.xml
mobile/android/base/resources/values-large-v11/styles.xml
--- a/mobile/android/base/BrowserToolbar.java
+++ b/mobile/android/base/BrowserToolbar.java
@@ -50,16 +50,17 @@ public class BrowserToolbar implements V
     private static final String LOGTAG = "GeckoToolbar";
     private LinearLayout mLayout;
     private View mAwesomeBar;
     private LayoutParams mAwesomeBarParams;
     private View mAwesomeBarEntry;
     private int mAwesomeBarEntryRightMargin;
     private GeckoFrameLayout mAwesomeBarRightEdge;
     private BrowserToolbarBackground mAddressBarBg;
+    private View mAddressBarView;
     private BrowserToolbarBackground.CurveTowards mAddressBarBgCurveTowards;
     private int mAddressBarBgRightMargin;
     private GeckoTextView mTitle;
     private int mTitlePadding;
     private boolean mSiteSecurityVisible;
     private boolean mAnimateSiteSecurity;
     private GeckoImageButton mTabs;
     private int mTabsPaneWidth;
@@ -92,22 +93,28 @@ public class BrowserToolbar implements V
     private TranslateAnimation mSlideUpOut;
     private TranslateAnimation mSlideDownIn;
     private TranslateAnimation mSlideDownOut;
 
     private AlphaAnimation mLockFadeIn;
     private TranslateAnimation mTitleSlideLeft;
     private TranslateAnimation mTitleSlideRight;
 
+    private int mAddressBarViewOffset;
+    private int mAddressBarViewOffsetNoForward;
+    private PropertyAnimator mForwardAnim = null;
+
     private int mCount;
     private int mFaviconSize;
 
     private static final int TABS_CONTRACTED = 1;
     private static final int TABS_EXPANDED = 2;
 
+    private static final int FORWARD_ANIMATION_DURATION = 450;
+
     public BrowserToolbar(BrowserApp activity) {
         // BrowserToolbar is attached to BrowserApp only.
         mActivity = activity;
         mInflater = LayoutInflater.from(activity);
 
         sActionItems = new ArrayList<View>();
         Tabs.registerOnTabsChangedListener(this);
         mAnimateSiteSecurity = true;
@@ -119,16 +126,19 @@ public class BrowserToolbar implements V
             layout.setVisibility(mLayout.getVisibility());
         }
         mLayout = layout;
 
         mShowSiteSecurity = false;
         mShowReader = false;
 
         mAddressBarBg = (BrowserToolbarBackground) mLayout.findViewById(R.id.address_bar_bg);
+        mAddressBarView = mLayout.findViewById(R.id.addressbar);
+        mAddressBarViewOffset = mActivity.getResources().getDimensionPixelSize(R.dimen.addressbar_offset_left);
+        mAddressBarViewOffsetNoForward = mActivity.getResources().getDimensionPixelSize(R.dimen.addressbar_offset_left_noforward);
         mAwesomeBarRightEdge = (GeckoFrameLayout) mLayout.findViewById(R.id.awesome_bar_right_edge);
         mAwesomeBarEntry = mLayout.findViewById(R.id.awesome_bar_entry);
 
         // This will hold the translation width inside the toolbar when the tabs
         // pane is visible. It will affect the padding applied to the title TextView.
         mTabsPaneWidth = 0;
 
         mTitle = (GeckoTextView) mLayout.findViewById(R.id.awesome_bar_title);
@@ -199,16 +209,17 @@ public class BrowserToolbar implements V
         mBack = (ImageButton) mLayout.findViewById(R.id.back);
         mBack.setOnClickListener(new Button.OnClickListener() {
             public void onClick(View view) {
                 Tabs.getInstance().getSelectedTab().doBack();
             }
         });
 
         mForward = (ImageButton) mLayout.findViewById(R.id.forward);
+        mForward.setEnabled(false); // initialize the forward button to not be enabled
         mForward.setOnClickListener(new Button.OnClickListener() {
             public void onClick(View view) {
                 Tabs.getInstance().getSelectedTab().doForward();
             }
         });
 
         Button.OnClickListener faviconListener = new Button.OnClickListener() {
             public void onClick(View view) {
@@ -398,28 +409,35 @@ public class BrowserToolbar implements V
         }
     }
 
     @Override
     public void onAnimationStart(Animation animation) {
         if (animation.equals(mLockFadeIn)) {
             if (mSiteSecurityVisible)
                 mSiteSecurity.setVisibility(View.VISIBLE);
+        } else if (animation.equals(mTitleSlideLeft)) {
+            // These two animations may be scheduled to start while the forward
+            // animation is occurring. If we're showing the site security icon, make
+            // sure it doesn't take any space during the forward transition.
+            mSiteSecurity.setVisibility(View.GONE);
+        } else if (animation.equals(mTitleSlideRight)) {
+            // If we're hiding the icon, make sure that we keep its padding
+            // in place during the forward transition
+            mSiteSecurity.setVisibility(View.INVISIBLE);
         }
     }
 
     @Override
     public void onAnimationRepeat(Animation animation) {
     }
 
     @Override
     public void onAnimationEnd(Animation animation) {
-        if (animation.equals(mTitleSlideLeft)) {
-            mSiteSecurity.setVisibility(View.GONE);
-        } else if (animation.equals(mTitleSlideRight)) {
+        if (animation.equals(mTitleSlideRight)) {
             mSiteSecurity.startAnimation(mLockFadeIn);
         }
     }
 
     @Override
     public View makeView() {
         // This returns a TextView for the TextSwitcher.
         return mInflater.inflate(R.layout.tabs_counter, null);
@@ -506,17 +524,17 @@ public class BrowserToolbar implements V
         // awesome screen.
         proxy = AnimatorProxy.create(mFavicon);
         proxy.setAlpha(1);
         proxy = AnimatorProxy.create(mSiteSecurity);
         proxy.setAlpha(1);
         proxy = AnimatorProxy.create(mTitle);
         proxy.setAlpha(1);
         proxy = AnimatorProxy.create(mForward);
-        proxy.setAlpha(1);
+        proxy.setAlpha(mForward.isEnabled() ? 1 : 0);
         proxy = AnimatorProxy.create(mBack);
         proxy.setAlpha(1);
 
         final PropertyAnimator contentAnimator = new PropertyAnimator(250);
 
         // Shrink the awesome entry back to its original size
         contentAnimator.attach(mAwesomeBarRightEdge,
                                PropertyAnimator.Property.TRANSLATION_X,
@@ -861,20 +879,24 @@ public class BrowserToolbar implements V
         mSiteSecurity.clearAnimation();
 
         // If any of these animations were cancelled as a result of the
         // clearAnimation() calls above, we need to reset them.
         mLockFadeIn.reset();
         mTitleSlideLeft.reset();
         mTitleSlideRight.reset();
 
-        if (visible)
-            mSiteSecurity.setVisibility(View.INVISIBLE);
-        else
-            mSiteSecurity.setVisibility(View.GONE);
+        if (mForwardAnim != null) {
+            long delay = mForwardAnim.getRemainingTime();
+            mTitleSlideRight.setStartOffset(delay);
+            mTitleSlideLeft.setStartOffset(delay);
+        } else {
+            mTitleSlideRight.setStartOffset(0);
+            mTitleSlideLeft.setStartOffset(0);
+        }
 
         mTitle.startAnimation(visible ? mTitleSlideRight : mTitleSlideLeft);
     }
 
     private void updateFocusOrder() {
         View prevView = null;
 
         for (View view : mFocusOrder) {
@@ -958,19 +980,114 @@ public class BrowserToolbar implements V
         mLayout.requestFocusFromTouch();
     }
 
     public void updateBackButton(boolean enabled) {
          mBack.setColorFilter(enabled ? 0 : 0xFF999999);
          mBack.setEnabled(enabled);
     }
 
-    public void updateForwardButton(boolean enabled) {
-         mForward.setColorFilter(enabled ? 0 : 0xFF999999);
-         mForward.setEnabled(enabled);
+    public void updateForwardButton(final boolean enabled) {
+        if (mForward.isEnabled() == enabled)
+            return;
+
+        // Save the state on the forward button so that we can skip animations
+        // when there's nothing to change
+        mForward.setEnabled(enabled);
+
+        if (mForward.getVisibility() != View.VISIBLE)
+            return;
+
+        mForwardAnim = new PropertyAnimator(FORWARD_ANIMATION_DURATION);
+        final int width = enabled ? mForward.getWidth()/2 : 0;
+
+        mForwardAnim.setPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
+            @Override
+            public void onPropertyAnimationStart() {
+                if (!enabled) {
+                    // Set the margin before the transition when hiding the forward button. We
+                    // have to do this so that the favicon isn't clipped during the transition
+                    ViewGroup.MarginLayoutParams layoutParams =
+                        (ViewGroup.MarginLayoutParams)mAddressBarView.getLayoutParams();
+                    layoutParams.leftMargin = mAddressBarViewOffsetNoForward;
+                    mAddressBarView.requestLayout();
+                    // Note, we already translated the favicon, site security, and text field
+                    // in prepareForwardAnimation, so they should appear to have not moved at
+                    // all at this point.
+                }
+            }
+
+            @Override
+            public void onPropertyAnimationEnd() {
+                if (enabled) {
+                    ViewGroup.MarginLayoutParams layoutParams =
+                        (ViewGroup.MarginLayoutParams)mAddressBarView.getLayoutParams();
+                    layoutParams.leftMargin = mAddressBarViewOffset;
+
+                    AnimatorProxy proxy = AnimatorProxy.create(mTitle);
+                    proxy.setTranslationX(0);
+                    proxy = AnimatorProxy.create(mFavicon);
+                    proxy.setTranslationX(0);
+                    proxy = AnimatorProxy.create(mSiteSecurity);
+                    proxy.setTranslationX(0);
+
+                    mAddressBarView.requestLayout();
+                }
+                mForwardAnim = null;
+            }
+        });
+        prepareForwardAnimation(mForwardAnim, width);
+        mForwardAnim.start();
+    }
+
+    private void prepareForwardAnimation(PropertyAnimator anim, int width) {
+        if (width == 0) {
+            anim.attach(mForward,
+                      PropertyAnimator.Property.TRANSLATION_X,
+                      0);
+            anim.attach(mForward,
+                      PropertyAnimator.Property.ALPHA,
+                      0);
+            anim.attach(mTitle,
+                      PropertyAnimator.Property.TRANSLATION_X,
+                      0);
+            anim.attach(mFavicon,
+                      PropertyAnimator.Property.TRANSLATION_X,
+                      0);
+            anim.attach(mSiteSecurity,
+                      PropertyAnimator.Property.TRANSLATION_X,
+                      0);
+
+            // We're hiding the forward button. We're going to reset the margin before
+            // the animation starts, so we shift these items to the right so that they don't
+            // appear to move initially.
+            int startTrans = mAddressBarViewOffset - mAddressBarViewOffsetNoForward;
+            AnimatorProxy proxy = AnimatorProxy.create(mTitle);
+            proxy.setTranslationX(startTrans);
+            proxy = AnimatorProxy.create(mFavicon);
+            proxy.setTranslationX(startTrans);
+            proxy = AnimatorProxy.create(mSiteSecurity);
+            proxy.setTranslationX(startTrans);
+        } else {
+            anim.attach(mForward,
+                      PropertyAnimator.Property.TRANSLATION_X,
+                      width);
+            anim.attach(mForward,
+                      PropertyAnimator.Property.ALPHA,
+                      1);
+            anim.attach(mTitle,
+                      PropertyAnimator.Property.TRANSLATION_X,
+                      mAddressBarViewOffset - mAddressBarViewOffsetNoForward);
+            anim.attach(mFavicon,
+                      PropertyAnimator.Property.TRANSLATION_X,
+                      mAddressBarViewOffset - mAddressBarViewOffsetNoForward);
+            anim.attach(mSiteSecurity,
+                      PropertyAnimator.Property.TRANSLATION_X,
+                      mAddressBarViewOffset - mAddressBarViewOffsetNoForward);
+        }
     }
 
     @Override
     public void addActionItem(View actionItem) {
         mActionItemBar.addView(actionItem);
 
         if (!sActionItems.contains(actionItem))
             sActionItems.add(actionItem);
--- a/mobile/android/base/PropertyAnimator.java
+++ b/mobile/android/base/PropertyAnimator.java
@@ -79,16 +79,21 @@ public class PropertyAnimator implements
 
         mElementsList.add(element);
     }
 
     public void setPropertyAnimationListener(PropertyAnimationListener listener) {
         mListener = listener;
     }
 
+    public long getRemainingTime() {
+        int timePassed = (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);
+        return mDuration - timePassed;
+    }
+
     @Override
     public void run() {
         int timePassed = (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);
         if (timePassed >= mDuration) {
             stop();
             return;
         }
 
--- a/mobile/android/base/resources/values-large-v11/dimens.xml
+++ b/mobile/android/base/resources/values-large-v11/dimens.xml
@@ -5,10 +5,11 @@
 
 <resources>
 
     <dimen name="browser_toolbar_height">56dp</dimen>
     <dimen name="browser_toolbar_icon_width">45dp</dimen>
     <dimen name="menu_popup_arrow_margin">8dip</dimen>
     <dimen name="tabs_counter_size">26sp</dimen>
     <dimen name="addressbar_offset_left">90dp</dimen>
+    <dimen name="addressbar_offset_left_noforward">50dip</dimen>
 
 </resources>
--- a/mobile/android/base/resources/values-large-v11/styles.xml
+++ b/mobile/android/base/resources/values-large-v11/styles.xml
@@ -30,25 +30,28 @@
       <item name="android:paddingLeft">60dip</item>
       <item name="android:paddingRight">60dip</item>
     </style>
 
     <style name="AddressBar.ImageButton.Forward">
         <item name="android:contentDescription">@string/forward</item>
         <item name="android:layout_width">64dip</item>
         <item name="android:layout_height">42dip</item>
-        <item name="android:layout_marginLeft">21dp</item>
         <item name="android:paddingLeft">21dp</item>
         <item name="android:layout_gravity">center_vertical</item>
         <item name="android:layout_centerVertical">true</item>
         <item name="android:src">@drawable/ic_menu_forward</item>
         <item name="android:background">@drawable/address_bar_nav_button</item>
+        <!-- Start with the button hidden -->
+        <item name="android:alpha">0</item>
+        <item name="android:layout_marginLeft">-11dp</item>
     </style>
 
    <style name="AddressBar.Button.Container">
         <item name="android:layout_marginTop">6dp</item>
         <item name="android:layout_marginBottom">6dp</item>
         <item name="android:layout_marginRight">0dp</item>
-        <item name="android:layout_marginLeft">@dimen/addressbar_offset_left</item>
+        <!-- Start with forward hidden -->
+        <item name="android:layout_marginLeft">@dimen/addressbar_offset_left_noforward</item>
         <item name="android:orientation">horizontal</item>
     </style>
 
 </resources>