Bug 944533 - Move title handling to ToolbarDisplayLayout (r=margaret)
authorLucas Rocha <lucasr@mozilla.com>
Thu, 09 Jan 2014 14:56:41 +0000
changeset 162801 06bc8e68d4fcd9fbb4bf8170d94ee192045f8cb5
parent 162800 c7f22c46812dc8d1f782e57452b4da84f2507c6f
child 162802 faa87475ab44087f7a5e92ef3e365da57aa6ad6d
push id25971
push userkwierso@gmail.com
push dateFri, 10 Jan 2014 00:50:13 +0000
treeherdermozilla-central@37516445a0b5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmargaret
bugs944533
milestone29.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 944533 - Move title handling to ToolbarDisplayLayout (r=margaret)
CLOBBER
mobile/android/base/toolbar/BrowserToolbar.java
mobile/android/base/toolbar/ToolbarDisplayLayout.java
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 956240 requires clobber due to bug 956723
+Bug 944533 requires clobber to force a Proguard refresh
--- a/mobile/android/base/toolbar/BrowserToolbar.java
+++ b/mobile/android/base/toolbar/BrowserToolbar.java
@@ -1,53 +1,48 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.toolbar;
 
-import org.mozilla.gecko.AboutPages;
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.LightweightTheme;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.SiteIdentity.SecurityMode;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener;
 import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.MenuPopup;
 import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnStopListener;
+import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnTitleChangeListener;
 import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.UpdateFlags;
 import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.GeckoEventListener;
-import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.widget.GeckoImageButton;
 import org.mozilla.gecko.widget.GeckoImageView;
 import org.mozilla.gecko.widget.GeckoRelativeLayout;
 
 import org.json.JSONObject;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.StateListDrawable;
 import android.os.Build;
-import android.text.style.ForegroundColorSpan;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.ContextMenu;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MenuInflater;
 import android.view.MotionEvent;
@@ -129,25 +124,19 @@ public class BrowserToolbar extends Geck
     private boolean mHasSoftMenuButton;
 
     private boolean mIsEditing;
     private boolean mAnimatingEntry;
 
     private int mUrlBarViewOffset;
     private int mDefaultForwardMargin;
 
-    private ToolbarTitlePrefs mTitlePrefs;
-
     private static final Interpolator sButtonsInterpolator = new AccelerateInterpolator();
 
     private static final int FORWARD_ANIMATION_DURATION = 450;
-    private final ForegroundColorSpan mUrlColor;
-    private final ForegroundColorSpan mBlockedColor;
-    private final ForegroundColorSpan mDomainColor;
-    private final ForegroundColorSpan mPrivateDomainColor;
 
     private final LightweightTheme mTheme;
 
     public BrowserToolbar(Context context) {
         this(context, null);
     }
 
     public BrowserToolbar(Context context, AttributeSet attrs) {
@@ -159,29 +148,22 @@ public class BrowserToolbar extends Geck
 
         // Inflate the content.
         LayoutInflater.from(context).inflate(R.layout.browser_toolbar, this);
 
         Tabs.registerOnTabsChangedListener(this);
         mSwitchingTabs = true;
         mAnimatingEntry = false;
 
-        mTitlePrefs = new ToolbarTitlePrefs();
-
-        Resources res = getResources();
-        mUrlColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_urltext));
-        mBlockedColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_blockedtext));
-        mDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext));
-        mPrivateDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext_private));
-
         registerEventListener("Reader:Click");
         registerEventListener("Reader:LongClick");
 
         mAnimatingEntry = false;
 
+        final Resources res = getResources();
         mUrlBarViewOffset = res.getDimensionPixelSize(R.dimen.url_bar_offset_left);
         mDefaultForwardMargin = res.getDimensionPixelSize(R.dimen.forward_default_offset);
         mUrlDisplayLayout = (ToolbarDisplayLayout) findViewById(R.id.display_layout);
         mUrlBarEntry = findViewById(R.id.url_bar_entry);
         mUrlEditLayout = (ToolbarEditLayout) findViewById(R.id.edit_layout);
 
         // This will clip the right edge's image at 60% of its width
         mUrlBarRightEdge = (ImageView) findViewById(R.id.url_bar_right_edge);
@@ -280,16 +262,32 @@ public class BrowserToolbar extends Geck
                     tab.doStop();
                     return tab;
                 }
 
                 return null;
             }
         });
 
+        mUrlDisplayLayout.setOnTitleChangeListener(new OnTitleChangeListener() {
+            @Override
+            public void onTitleChange(CharSequence title) {
+                final String contentDescription;
+                if (title != null) {
+                    contentDescription = title.toString();
+                } else {
+                    contentDescription = mActivity.getString(R.string.url_bar_default_text);
+                }
+
+                // The title and content description should
+                // always be sync.
+                setContentDescription(contentDescription);
+            }
+        });
+
         mUrlEditLayout.setOnFocusChangeListener(new View.OnFocusChangeListener() {
             @Override
             public void onFocusChange(View v, boolean hasFocus) {
                 setSelected(hasFocus);
             }
         });
 
         mTabs.setOnClickListener(new Button.OnClickListener() {
@@ -429,39 +427,40 @@ public class BrowserToolbar extends Geck
                 // Fall through.
         }
 
         if (tabs.isSelectedTab(tab)) {
             final EnumSet<UpdateFlags> flags = EnumSet.noneOf(UpdateFlags.class);
 
             switch (msg) {
                 case TITLE:
-                    updateTitle();
+                    flags.add(UpdateFlags.TITLE);
                     break;
 
                 case START:
                     updateBackButton(tab);
                     updateForwardButton(tab);
 
                     flags.add(UpdateFlags.PROGRESS);
                     break;
 
                 case STOP:
                     updateBackButton(tab);
                     updateForwardButton(tab);
 
                     flags.add(UpdateFlags.PROGRESS);
 
-                    // Reset the title in case we haven't navigated to a new page yet.
-                    updateTitle();
+                    // Reset the title in case we haven't navigated
+                    // to a new page yet.
+                    flags.add(UpdateFlags.TITLE);
                     break;
 
                 case SELECTED:
                 case LOAD_ERROR:
-                    updateTitle();
+                    flags.add(UpdateFlags.TITLE);
                     // Fall through.
                 case LOCATION_CHANGE:
                     // A successful location change will cause Tab to notify
                     // us of a title change, so we don't update the title here.
                     refreshState();
                     break;
 
                 case CLOSED:
@@ -590,16 +589,22 @@ public class BrowserToolbar extends Geck
 
     private void updateDisplayLayout(Tab tab, EnumSet<UpdateFlags> flags) {
         if (mSwitchingTabs) {
             flags.add(UpdateFlags.DISABLE_ANIMATIONS);
         }
 
         mUrlDisplayLayout.updateFromTab(tab, flags);
 
+        if (flags.contains(UpdateFlags.TITLE)) {
+            if (!isEditing()) {
+                mUrlEditLayout.setText(tab.getURL());
+            }
+        }
+
         if (flags.contains(UpdateFlags.PROGRESS)) {
             updateFocusOrder();
         }
     }
 
     private void updateFocusOrder() {
         View prevView = null;
 
@@ -644,80 +649,16 @@ public class BrowserToolbar extends Geck
             return;
         }
 
         mUrlEditLayout.onEditSuggestion(suggestion);
     }
 
     public void setTitle(CharSequence title) {
         mUrlDisplayLayout.setTitle(title);
-
-        final String contentDescription;
-        if (title != null) {
-            contentDescription = title.toString();
-        } else {
-            contentDescription = mActivity.getString(R.string.url_bar_default_text);
-        }
-
-        setContentDescription(contentDescription);
-    }
-
-    // Sets the toolbar title according to the selected tab, obeying the
-    // ToolbarTitlePrefs.shouldShowUrl() preference.
-    private void updateTitle() {
-        final Tab tab = Tabs.getInstance().getSelectedTab();
-        // Keep the title unchanged if there's no selected tab, or if the tab is entering reader mode.
-        if (tab == null || tab.isEnteringReaderMode()) {
-            return;
-        }
-
-        final String url = tab.getURL();
-
-        if (!isEditing()) {
-            mUrlEditLayout.setText(url);
-        }
-
-        // Setting a null title will ensure we just see the "Enter Search or Address" placeholder text.
-        if (AboutPages.isTitlelessAboutPage(url)) {
-            setTitle(null);
-            return;
-        }
-
-        // Show the about:blocked page title in red, regardless of prefs
-        if (tab.getErrorType() == Tab.ErrorType.BLOCKED) {
-            String title = tab.getDisplayTitle();
-            SpannableStringBuilder builder = new SpannableStringBuilder(title);
-            builder.setSpan(mBlockedColor, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
-            setTitle(builder);
-            return;
-        }
-
-        // If the pref to show the URL isn't set, just use the tab's display title.
-        if (!mTitlePrefs.shouldShowUrl() || url == null) {
-            setTitle(tab.getDisplayTitle());
-            return;
-        }
-
-        CharSequence title = url;
-        if (mTitlePrefs.shouldTrimUrls()) {
-            title = StringUtils.stripCommonSubdomains(StringUtils.stripScheme(url));
-        }
-
-        String baseDomain = tab.getBaseDomain();
-        if (!TextUtils.isEmpty(baseDomain)) {
-            SpannableStringBuilder builder = new SpannableStringBuilder(title);
-            int index = title.toString().indexOf(baseDomain);
-            if (index > -1) {
-                builder.setSpan(mUrlColor, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
-                builder.setSpan(tab.isPrivate() ? mPrivateDomainColor : mDomainColor, index, index+baseDomain.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
-                title = builder;
-            }
-        }
-
-        setTitle(title);
     }
 
     public void prepareTabsAnimation(PropertyAnimator animator, boolean tabsAreShown) {
         if (!tabsAreShown) {
             PropertyAnimator buttonsAnimator =
                     new PropertyAnimator(animator.getDuration(), sButtonsInterpolator);
 
             buttonsAnimator.attach(mTabsCounter,
@@ -1273,17 +1214,16 @@ public class BrowserToolbar extends Geck
         }
     }
 
     public View getDoorHangerAnchor() {
         return mUrlDisplayLayout.getDoorHangerAnchor();
     }
 
     public void onDestroy() {
-        mTitlePrefs.close();
         Tabs.unregisterOnTabsChangedListener(this);
 
         unregisterEventListener("Reader:Click");
         unregisterEventListener("Reader:LongClick");
     }
 
     public boolean openOptionsMenu() {
         if (!mHasSoftMenuButton)
--- a/mobile/android/base/toolbar/ToolbarDisplayLayout.java
+++ b/mobile/android/base/toolbar/ToolbarDisplayLayout.java
@@ -1,34 +1,41 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.toolbar;
 
+import org.mozilla.gecko.AboutPages;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.SiteIdentity;
 import org.mozilla.gecko.SiteIdentity.SecurityMode;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.toolbar.BrowserToolbar.ForwardButtonAnimation;
+import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.widget.GeckoLinearLayout;
 import org.mozilla.gecko.widget.GeckoTextView;
 
 import org.json.JSONObject;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.os.Build;
 import android.os.SystemClock;
+import android.text.style.ForegroundColorSpan;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.TranslateAnimation;
@@ -41,38 +48,45 @@ import java.util.EnumSet;
 import java.util.List;
 
 public class ToolbarDisplayLayout extends GeckoLinearLayout
                                   implements Animation.AnimationListener {
 
     private static final String LOGTAG = "GeckoToolbarDisplayLayout";
 
     enum UpdateFlags {
+        TITLE,
         FAVICON,
         PROGRESS,
         SITE_IDENTITY,
         PRIVATE_MODE,
         DISABLE_ANIMATIONS
     }
 
     private enum UIMode {
         PROGRESS,
         DISPLAY
     }
 
     interface OnStopListener {
         public Tab onStop();
     }
 
+    interface OnTitleChangeListener {
+        public void onTitleChange(CharSequence title);
+    }
+
     final private BrowserApp mActivity;
 
     private UIMode mUiMode;
 
     private GeckoTextView mTitle;
     private int mTitlePadding;
+    private ToolbarTitlePrefs mTitlePrefs;
+    private OnTitleChangeListener mTitleChangeListener;
 
     private ImageButton mSiteSecurity;
     private boolean mSiteSecurityVisible;
 
     // To de-bounce sets.
     private Bitmap mLastFavicon;
     private ImageButton mFavicon;
     private int mFaviconSize;
@@ -88,28 +102,38 @@ public class ToolbarDisplayLayout extend
     private TranslateAnimation mTitleSlideLeft;
     private TranslateAnimation mTitleSlideRight;
 
     private SiteIdentityPopup mSiteIdentityPopup;
     private SecurityMode mSecurityMode;
 
     private PropertyAnimator mForwardAnim;
 
+    private final ForegroundColorSpan mUrlColor;
+    private final ForegroundColorSpan mBlockedColor;
+    private final ForegroundColorSpan mDomainColor;
+    private final ForegroundColorSpan mPrivateDomainColor;
+
     public ToolbarDisplayLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
         setOrientation(HORIZONTAL);
 
         mActivity = (BrowserApp) context;
 
         LayoutInflater.from(context).inflate(R.layout.toolbar_display_layout, this);
 
         mTitle = (GeckoTextView) findViewById(R.id.url_bar_title);
         mTitlePadding = mTitle.getPaddingRight();
 
-        Resources res = getResources();
+        final Resources res = getResources();
+
+        mUrlColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_urltext));
+        mBlockedColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_blockedtext));
+        mDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext));
+        mPrivateDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext_private));
 
         mFavicon = (ImageButton) findViewById(R.id.favicon);
         if (Build.VERSION.SDK_INT >= 11) {
             if (Build.VERSION.SDK_INT >= 16) {
                 mFavicon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
             }
             mFavicon.setLayerType(View.LAYER_TYPE_HARDWARE, null);
         }
@@ -124,16 +148,18 @@ public class ToolbarDisplayLayout extend
         mProgressSpinner = AnimationUtils.loadAnimation(mActivity, R.anim.progress_spinner);
 
         mStop = (ImageButton) findViewById(R.id.stop);
         mPageActionLayout = (PageActionLayout) findViewById(R.id.page_action_layout);
     }
 
     @Override
     public void onAttachedToWindow() {
+        mTitlePrefs = new ToolbarTitlePrefs();
+
         Button.OnClickListener faviconListener = new Button.OnClickListener() {
             @Override
             public void onClick(View view) {
                 if (mSiteSecurity.getVisibility() != View.VISIBLE) {
                     return;
                 }
 
                 mSiteIdentityPopup.show();
@@ -174,16 +200,21 @@ public class ToolbarDisplayLayout extend
 
         final int lockAnimDuration = 300;
         mLockFadeIn.setDuration(lockAnimDuration);
         mTitleSlideLeft.setDuration(lockAnimDuration);
         mTitleSlideRight.setDuration(lockAnimDuration);
     }
 
     @Override
+    public void onDetachedFromWindow() {
+        mTitlePrefs.close();
+    }
+
+    @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.
@@ -210,16 +241,20 @@ public class ToolbarDisplayLayout extend
     public void setNextFocusDownId(int nextId) {
         mFavicon.setNextFocusDownId(nextId);
         mStop.setNextFocusDownId(nextId);
         mSiteSecurity.setNextFocusDownId(nextId);
         mPageActionLayout.setNextFocusDownId(nextId);
     }
 
     void updateFromTab(Tab tab, EnumSet<UpdateFlags> flags) {
+        if (flags.contains(UpdateFlags.TITLE)) {
+            updateTitle(tab);
+        }
+
         if (flags.contains(UpdateFlags.FAVICON)) {
             updateFavicon(tab);
         }
 
         if (flags.contains(UpdateFlags.SITE_IDENTITY)) {
             updateSiteIdentity(tab, flags);
         }
 
@@ -227,16 +262,79 @@ public class ToolbarDisplayLayout extend
             updateProgress(tab, flags);
         }
 
         if (flags.contains(UpdateFlags.PRIVATE_MODE)) {
             mTitle.setPrivateMode(tab != null && tab.isPrivate());
         }
     }
 
+    void setTitle(CharSequence title) {
+        mTitle.setText(title);
+
+        if (mTitleChangeListener != null) {
+            mTitleChangeListener.onTitleChange(title);
+        }
+    }
+
+    private void updateTitle(Tab tab) {
+        // Keep the title unchanged if there's no selected tab,
+        // or if the tab is entering reader mode.
+        if (tab == null || tab.isEnteringReaderMode()) {
+            return;
+        }
+
+        final String url = tab.getURL();
+
+        // Setting a null title will ensure we just see the
+        // "Enter Search or Address" placeholder text.
+        if (AboutPages.isTitlelessAboutPage(url)) {
+            setTitle(null);
+            return;
+        }
+
+        // Show the about:blocked page title in red, regardless of prefs
+        if (tab.getErrorType() == Tab.ErrorType.BLOCKED) {
+            final String title = tab.getDisplayTitle();
+
+            final SpannableStringBuilder builder = new SpannableStringBuilder(title);
+            builder.setSpan(mBlockedColor, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+
+            setTitle(builder);
+            return;
+        }
+
+        // If the pref to show the URL isn't set, just use the tab's display title.
+        if (!mTitlePrefs.shouldShowUrl() || url == null) {
+            setTitle(tab.getDisplayTitle());
+            return;
+        }
+
+        CharSequence title = url;
+        if (mTitlePrefs.shouldTrimUrls()) {
+            title = StringUtils.stripCommonSubdomains(StringUtils.stripScheme(url));
+        }
+
+        final String baseDomain = tab.getBaseDomain();
+        if (!TextUtils.isEmpty(baseDomain)) {
+            final SpannableStringBuilder builder = new SpannableStringBuilder(title);
+
+            int index = title.toString().indexOf(baseDomain);
+            if (index > -1) {
+                builder.setSpan(mUrlColor, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+                builder.setSpan(tab.isPrivate() ? mPrivateDomainColor : mDomainColor,
+                                index, index + baseDomain.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+
+                title = builder;
+            }
+        }
+
+        setTitle(title);
+    }
+
     private void updateFavicon(Tab tab) {
         if (tab == null) {
             mFavicon.setImageDrawable(null);
             return;
         }
 
         if (tab.getState() == Tab.STATE_LOADING) {
             return;
@@ -369,22 +467,22 @@ public class ToolbarDisplayLayout extend
 
         mTitle.startAnimation(visible ? mTitleSlideRight : mTitleSlideLeft);
     }
 
     List<View> getFocusOrder() {
         return Arrays.asList(mSiteSecurity, mPageActionLayout, mStop);
     }
 
-    void setTitle(CharSequence title) {
-        mTitle.setText(title);
+    void setOnStopListener(OnStopListener listener) {
+        mStopListener = listener;
     }
 
-    void setOnStopListener(OnStopListener listener) {
-        mStopListener = listener;
+    void setOnTitleChangeListener(OnTitleChangeListener listener) {
+        mTitleChangeListener = listener;
     }
 
     View getDoorHangerAnchor() {
         return mFavicon;
     }
 
     void prepareForwardAnimation(PropertyAnimator anim, ForwardButtonAnimation animation, int width) {
         mForwardAnim = anim;