Bug 681805 - Conditional forward for Android tablets. r=lucasr
☠☠ backed out by 42a710c2deda ☠ ☠
authorWes Johnston <wjohnston@mozilla.com>
Wed, 19 Dec 2012 09:29:02 -0800
changeset 125632 0e96545f0894cc58ed68ecafb486a94b29c8b8b2
parent 125631 55ae87306b987c4e88cfc5cb86dcf0f3bbd2328e
child 125633 976f2d5ad4005fb94d3f27137f91334c4b5a5b44
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>