1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/toolbar/ToolbarDisplayLayout.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,556 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko.toolbar; 1.10 + 1.11 +import org.mozilla.gecko.AboutPages; 1.12 +import org.mozilla.gecko.animation.PropertyAnimator; 1.13 +import org.mozilla.gecko.animation.ViewHelper; 1.14 +import org.mozilla.gecko.BrowserApp; 1.15 +import org.mozilla.gecko.R; 1.16 +import org.mozilla.gecko.SiteIdentity; 1.17 +import org.mozilla.gecko.SiteIdentity.SecurityMode; 1.18 +import org.mozilla.gecko.Tab; 1.19 +import org.mozilla.gecko.Tabs; 1.20 +import org.mozilla.gecko.toolbar.BrowserToolbar.ForwardButtonAnimation; 1.21 +import org.mozilla.gecko.util.StringUtils; 1.22 +import org.mozilla.gecko.widget.ThemedLinearLayout; 1.23 +import org.mozilla.gecko.widget.ThemedTextView; 1.24 + 1.25 +import org.json.JSONObject; 1.26 + 1.27 +import android.content.Context; 1.28 +import android.content.res.Resources; 1.29 +import android.graphics.Bitmap; 1.30 +import android.os.Build; 1.31 +import android.os.SystemClock; 1.32 +import android.text.style.ForegroundColorSpan; 1.33 +import android.text.Spannable; 1.34 +import android.text.SpannableStringBuilder; 1.35 +import android.text.Spanned; 1.36 +import android.text.TextUtils; 1.37 +import android.util.AttributeSet; 1.38 +import android.util.Log; 1.39 +import android.view.LayoutInflater; 1.40 +import android.view.View; 1.41 +import android.view.animation.Animation; 1.42 +import android.view.animation.AnimationUtils; 1.43 +import android.view.animation.AlphaAnimation; 1.44 +import android.view.animation.TranslateAnimation; 1.45 +import android.widget.Button; 1.46 +import android.widget.ImageButton; 1.47 +import android.widget.LinearLayout.LayoutParams; 1.48 + 1.49 +import java.util.Arrays; 1.50 +import java.util.EnumSet; 1.51 +import java.util.List; 1.52 + 1.53 +/** 1.54 +* {@code ToolbarDisplayLayout} is the UI for when the toolbar is in 1.55 +* display state. It's used to display the state of the currently selected 1.56 +* tab. It should always be updated through a single entry point 1.57 +* (updateFromTab) and should never track any tab events or gecko messages 1.58 +* on its own to keep it as dumb as possible. 1.59 +* 1.60 +* The UI has two possible modes: progress and display which are triggered 1.61 +* when UpdateFlags.PROGRESS is used depending on the current tab state. 1.62 +* The progress mode is triggered when the tab is loading a page. Display mode 1.63 +* is used otherwise. 1.64 +* 1.65 +* {@code ToolbarDisplayLayout} is meant to be owned by {@code BrowserToolbar} 1.66 +* which is the main event bus for the toolbar subsystem. 1.67 +*/ 1.68 +public class ToolbarDisplayLayout extends ThemedLinearLayout 1.69 + implements Animation.AnimationListener { 1.70 + 1.71 + private static final String LOGTAG = "GeckoToolbarDisplayLayout"; 1.72 + 1.73 + // To be used with updateFromTab() to allow the caller 1.74 + // to give enough context for the requested state change. 1.75 + enum UpdateFlags { 1.76 + TITLE, 1.77 + FAVICON, 1.78 + PROGRESS, 1.79 + SITE_IDENTITY, 1.80 + PRIVATE_MODE, 1.81 + 1.82 + // Disable any animation that might be 1.83 + // triggered from this state change. Mostly 1.84 + // used on tab switches, see BrowserToolbar. 1.85 + DISABLE_ANIMATIONS 1.86 + } 1.87 + 1.88 + private enum UIMode { 1.89 + PROGRESS, 1.90 + DISPLAY 1.91 + } 1.92 + 1.93 + interface OnStopListener { 1.94 + public Tab onStop(); 1.95 + } 1.96 + 1.97 + interface OnTitleChangeListener { 1.98 + public void onTitleChange(CharSequence title); 1.99 + } 1.100 + 1.101 + private final BrowserApp mActivity; 1.102 + 1.103 + private UIMode mUiMode; 1.104 + 1.105 + private ThemedTextView mTitle; 1.106 + private int mTitlePadding; 1.107 + private ToolbarTitlePrefs mTitlePrefs; 1.108 + private OnTitleChangeListener mTitleChangeListener; 1.109 + 1.110 + private ImageButton mSiteSecurity; 1.111 + private boolean mSiteSecurityVisible; 1.112 + 1.113 + // To de-bounce sets. 1.114 + private Bitmap mLastFavicon; 1.115 + private ImageButton mFavicon; 1.116 + private int mFaviconSize; 1.117 + 1.118 + private ImageButton mStop; 1.119 + private OnStopListener mStopListener; 1.120 + 1.121 + private PageActionLayout mPageActionLayout; 1.122 + 1.123 + private AlphaAnimation mLockFadeIn; 1.124 + private TranslateAnimation mTitleSlideLeft; 1.125 + private TranslateAnimation mTitleSlideRight; 1.126 + 1.127 + private SiteIdentityPopup mSiteIdentityPopup; 1.128 + private SecurityMode mSecurityMode; 1.129 + 1.130 + private PropertyAnimator mForwardAnim; 1.131 + 1.132 + private final ForegroundColorSpan mUrlColor; 1.133 + private final ForegroundColorSpan mBlockedColor; 1.134 + private final ForegroundColorSpan mDomainColor; 1.135 + private final ForegroundColorSpan mPrivateDomainColor; 1.136 + 1.137 + public ToolbarDisplayLayout(Context context, AttributeSet attrs) { 1.138 + super(context, attrs); 1.139 + setOrientation(HORIZONTAL); 1.140 + 1.141 + mActivity = (BrowserApp) context; 1.142 + 1.143 + LayoutInflater.from(context).inflate(R.layout.toolbar_display_layout, this); 1.144 + 1.145 + mTitle = (ThemedTextView) findViewById(R.id.url_bar_title); 1.146 + mTitlePadding = mTitle.getPaddingRight(); 1.147 + 1.148 + final Resources res = getResources(); 1.149 + 1.150 + mUrlColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_urltext)); 1.151 + mBlockedColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_blockedtext)); 1.152 + mDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext)); 1.153 + mPrivateDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext_private)); 1.154 + 1.155 + mFavicon = (ImageButton) findViewById(R.id.favicon); 1.156 + if (Build.VERSION.SDK_INT >= 16) { 1.157 + mFavicon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); 1.158 + } 1.159 + mFaviconSize = Math.round(res.getDimension(R.dimen.browser_toolbar_favicon_size)); 1.160 + 1.161 + mSiteSecurity = (ImageButton) findViewById(R.id.site_security); 1.162 + mSiteSecurityVisible = (mSiteSecurity.getVisibility() == View.VISIBLE); 1.163 + 1.164 + mSiteIdentityPopup = new SiteIdentityPopup(mActivity); 1.165 + mSiteIdentityPopup.setAnchor(mSiteSecurity); 1.166 + 1.167 + mStop = (ImageButton) findViewById(R.id.stop); 1.168 + mPageActionLayout = (PageActionLayout) findViewById(R.id.page_action_layout); 1.169 + } 1.170 + 1.171 + @Override 1.172 + public void onAttachedToWindow() { 1.173 + mTitlePrefs = new ToolbarTitlePrefs(); 1.174 + 1.175 + Button.OnClickListener faviconListener = new Button.OnClickListener() { 1.176 + @Override 1.177 + public void onClick(View view) { 1.178 + if (mSiteSecurity.getVisibility() != View.VISIBLE) { 1.179 + return; 1.180 + } 1.181 + 1.182 + mSiteIdentityPopup.show(); 1.183 + } 1.184 + }; 1.185 + 1.186 + mFavicon.setOnClickListener(faviconListener); 1.187 + mSiteSecurity.setOnClickListener(faviconListener); 1.188 + 1.189 + mStop.setOnClickListener(new Button.OnClickListener() { 1.190 + @Override 1.191 + public void onClick(View v) { 1.192 + if (mStopListener != null) { 1.193 + // Force toolbar to switch to Display mode 1.194 + // immediately based on the stopped tab. 1.195 + final Tab tab = mStopListener.onStop(); 1.196 + if (tab != null) { 1.197 + updateUiMode(tab, UIMode.DISPLAY, EnumSet.noneOf(UpdateFlags.class)); 1.198 + } 1.199 + } 1.200 + } 1.201 + }); 1.202 + 1.203 + float slideWidth = getResources().getDimension(R.dimen.browser_toolbar_lock_width); 1.204 + 1.205 + LayoutParams siteSecParams = (LayoutParams) mSiteSecurity.getLayoutParams(); 1.206 + final float scale = getResources().getDisplayMetrics().density; 1.207 + slideWidth += (siteSecParams.leftMargin + siteSecParams.rightMargin) * scale + 0.5f; 1.208 + 1.209 + mLockFadeIn = new AlphaAnimation(0.0f, 1.0f); 1.210 + mLockFadeIn.setAnimationListener(this); 1.211 + 1.212 + mTitleSlideLeft = new TranslateAnimation(slideWidth, 0, 0, 0); 1.213 + mTitleSlideLeft.setAnimationListener(this); 1.214 + 1.215 + mTitleSlideRight = new TranslateAnimation(-slideWidth, 0, 0, 0); 1.216 + mTitleSlideRight.setAnimationListener(this); 1.217 + 1.218 + final int lockAnimDuration = 300; 1.219 + mLockFadeIn.setDuration(lockAnimDuration); 1.220 + mTitleSlideLeft.setDuration(lockAnimDuration); 1.221 + mTitleSlideRight.setDuration(lockAnimDuration); 1.222 + } 1.223 + 1.224 + @Override 1.225 + public void onDetachedFromWindow() { 1.226 + mTitlePrefs.close(); 1.227 + } 1.228 + 1.229 + @Override 1.230 + public void onAnimationStart(Animation animation) { 1.231 + if (animation.equals(mLockFadeIn)) { 1.232 + if (mSiteSecurityVisible) 1.233 + mSiteSecurity.setVisibility(View.VISIBLE); 1.234 + } else if (animation.equals(mTitleSlideLeft)) { 1.235 + // These two animations may be scheduled to start while the forward 1.236 + // animation is occurring. If we're showing the site security icon, make 1.237 + // sure it doesn't take any space during the forward transition. 1.238 + mSiteSecurity.setVisibility(View.GONE); 1.239 + } else if (animation.equals(mTitleSlideRight)) { 1.240 + // If we're hiding the icon, make sure that we keep its padding 1.241 + // in place during the forward transition 1.242 + mSiteSecurity.setVisibility(View.INVISIBLE); 1.243 + } 1.244 + } 1.245 + 1.246 + @Override 1.247 + public void onAnimationRepeat(Animation animation) { 1.248 + } 1.249 + 1.250 + @Override 1.251 + public void onAnimationEnd(Animation animation) { 1.252 + if (animation.equals(mTitleSlideRight)) { 1.253 + mSiteSecurity.startAnimation(mLockFadeIn); 1.254 + } 1.255 + } 1.256 + 1.257 + @Override 1.258 + public void setNextFocusDownId(int nextId) { 1.259 + mFavicon.setNextFocusDownId(nextId); 1.260 + mStop.setNextFocusDownId(nextId); 1.261 + mSiteSecurity.setNextFocusDownId(nextId); 1.262 + mPageActionLayout.setNextFocusDownId(nextId); 1.263 + } 1.264 + 1.265 + void updateFromTab(Tab tab, EnumSet<UpdateFlags> flags) { 1.266 + if (flags.contains(UpdateFlags.TITLE)) { 1.267 + updateTitle(tab); 1.268 + } 1.269 + 1.270 + if (flags.contains(UpdateFlags.FAVICON)) { 1.271 + updateFavicon(tab); 1.272 + } 1.273 + 1.274 + if (flags.contains(UpdateFlags.SITE_IDENTITY)) { 1.275 + updateSiteIdentity(tab, flags); 1.276 + } 1.277 + 1.278 + if (flags.contains(UpdateFlags.PROGRESS)) { 1.279 + updateProgress(tab, flags); 1.280 + } 1.281 + 1.282 + if (flags.contains(UpdateFlags.PRIVATE_MODE)) { 1.283 + mTitle.setPrivateMode(tab != null && tab.isPrivate()); 1.284 + } 1.285 + } 1.286 + 1.287 + void setTitle(CharSequence title) { 1.288 + mTitle.setText(title); 1.289 + 1.290 + if (mTitleChangeListener != null) { 1.291 + mTitleChangeListener.onTitleChange(title); 1.292 + } 1.293 + } 1.294 + 1.295 + private void updateTitle(Tab tab) { 1.296 + // Keep the title unchanged if there's no selected tab, 1.297 + // or if the tab is entering reader mode. 1.298 + if (tab == null || tab.isEnteringReaderMode()) { 1.299 + return; 1.300 + } 1.301 + 1.302 + final String url = tab.getURL(); 1.303 + 1.304 + // Setting a null title will ensure we just see the 1.305 + // "Enter Search or Address" placeholder text. 1.306 + if (AboutPages.isTitlelessAboutPage(url)) { 1.307 + setTitle(null); 1.308 + return; 1.309 + } 1.310 + 1.311 + // Show the about:blocked page title in red, regardless of prefs 1.312 + if (tab.getErrorType() == Tab.ErrorType.BLOCKED) { 1.313 + final String title = tab.getDisplayTitle(); 1.314 + 1.315 + final SpannableStringBuilder builder = new SpannableStringBuilder(title); 1.316 + builder.setSpan(mBlockedColor, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); 1.317 + 1.318 + setTitle(builder); 1.319 + return; 1.320 + } 1.321 + 1.322 + // If the pref to show the URL isn't set, just use the tab's display title. 1.323 + if (!mTitlePrefs.shouldShowUrl() || url == null) { 1.324 + setTitle(tab.getDisplayTitle()); 1.325 + return; 1.326 + } 1.327 + 1.328 + CharSequence title = url; 1.329 + if (mTitlePrefs.shouldTrimUrls()) { 1.330 + title = StringUtils.stripCommonSubdomains(StringUtils.stripScheme(url)); 1.331 + } 1.332 + 1.333 + final String baseDomain = tab.getBaseDomain(); 1.334 + if (!TextUtils.isEmpty(baseDomain)) { 1.335 + final SpannableStringBuilder builder = new SpannableStringBuilder(title); 1.336 + 1.337 + int index = title.toString().indexOf(baseDomain); 1.338 + if (index > -1) { 1.339 + builder.setSpan(mUrlColor, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); 1.340 + builder.setSpan(tab.isPrivate() ? mPrivateDomainColor : mDomainColor, 1.341 + index, index + baseDomain.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); 1.342 + 1.343 + title = builder; 1.344 + } 1.345 + } 1.346 + 1.347 + setTitle(title); 1.348 + } 1.349 + 1.350 + private void updateFavicon(Tab tab) { 1.351 + if (tab == null) { 1.352 + mFavicon.setImageDrawable(null); 1.353 + return; 1.354 + } 1.355 + 1.356 + Bitmap image = tab.getFavicon(); 1.357 + 1.358 + if (image != null && image == mLastFavicon) { 1.359 + Log.d(LOGTAG, "Ignoring favicon: new image is identical to previous one."); 1.360 + return; 1.361 + } 1.362 + 1.363 + // Cache the original so we can debounce without scaling 1.364 + mLastFavicon = image; 1.365 + 1.366 + Log.d(LOGTAG, "updateFavicon(" + image + ")"); 1.367 + 1.368 + if (image != null) { 1.369 + image = Bitmap.createScaledBitmap(image, mFaviconSize, mFaviconSize, false); 1.370 + mFavicon.setImageBitmap(image); 1.371 + } else { 1.372 + mFavicon.setImageResource(R.drawable.favicon); 1.373 + } 1.374 + } 1.375 + 1.376 + private void updateSiteIdentity(Tab tab, EnumSet<UpdateFlags> flags) { 1.377 + final SiteIdentity siteIdentity; 1.378 + if (tab == null) { 1.379 + siteIdentity = null; 1.380 + } else { 1.381 + siteIdentity = tab.getSiteIdentity(); 1.382 + } 1.383 + 1.384 + mSiteIdentityPopup.setSiteIdentity(siteIdentity); 1.385 + 1.386 + final SecurityMode securityMode; 1.387 + if (siteIdentity == null) { 1.388 + securityMode = SecurityMode.UNKNOWN; 1.389 + } else { 1.390 + securityMode = siteIdentity.getSecurityMode(); 1.391 + } 1.392 + 1.393 + if (mSecurityMode != securityMode) { 1.394 + mSecurityMode = securityMode; 1.395 + mSiteSecurity.setImageLevel(mSecurityMode.ordinal()); 1.396 + updatePageActions(flags); 1.397 + } 1.398 + } 1.399 + 1.400 + private void updateProgress(Tab tab, EnumSet<UpdateFlags> flags) { 1.401 + final boolean shouldShowThrobber = (tab != null && 1.402 + tab.getState() == Tab.STATE_LOADING); 1.403 + 1.404 + updateUiMode(tab, shouldShowThrobber ? UIMode.PROGRESS : UIMode.DISPLAY, flags); 1.405 + } 1.406 + 1.407 + private void updateUiMode(Tab tab, UIMode uiMode, EnumSet<UpdateFlags> flags) { 1.408 + if (mUiMode == uiMode) { 1.409 + return; 1.410 + } 1.411 + 1.412 + mUiMode = uiMode; 1.413 + 1.414 + // The "Throbber start" and "Throbber stop" log messages in this method 1.415 + // are needed by S1/S2 tests (http://mrcote.info/phonedash/#). 1.416 + // See discussion in Bug 804457. Bug 805124 tracks paring these down. 1.417 + if (mUiMode == UIMode.PROGRESS) { 1.418 + Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - Throbber start"); 1.419 + } else { 1.420 + Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - Throbber stop"); 1.421 + } 1.422 + 1.423 + updatePageActions(flags); 1.424 + } 1.425 + 1.426 + private void updatePageActions(EnumSet<UpdateFlags> flags) { 1.427 + final boolean isShowingProgress = (mUiMode == UIMode.PROGRESS); 1.428 + 1.429 + mStop.setVisibility(isShowingProgress ? View.VISIBLE : View.GONE); 1.430 + mPageActionLayout.setVisibility(!isShowingProgress ? View.VISIBLE : View.GONE); 1.431 + 1.432 + boolean shouldShowSiteSecurity = (!isShowingProgress && 1.433 + mSecurityMode != SecurityMode.UNKNOWN); 1.434 + 1.435 + setSiteSecurityVisibility(shouldShowSiteSecurity, flags); 1.436 + 1.437 + // We want title to fill the whole space available for it when there are icons 1.438 + // being shown on the right side of the toolbar as the icons already have some 1.439 + // padding in them. This is just to avoid wasting space when icons are shown. 1.440 + mTitle.setPadding(0, 0, (!isShowingProgress ? mTitlePadding : 0), 0); 1.441 + } 1.442 + 1.443 + private void setSiteSecurityVisibility(boolean visible, EnumSet<UpdateFlags> flags) { 1.444 + if (visible == mSiteSecurityVisible) { 1.445 + return; 1.446 + } 1.447 + 1.448 + mSiteSecurityVisible = visible; 1.449 + 1.450 + mTitle.clearAnimation(); 1.451 + mSiteSecurity.clearAnimation(); 1.452 + 1.453 + if (flags.contains(UpdateFlags.DISABLE_ANIMATIONS)) { 1.454 + mSiteSecurity.setVisibility(visible ? View.VISIBLE : View.GONE); 1.455 + return; 1.456 + } 1.457 + 1.458 + // If any of these animations were cancelled as a result of the 1.459 + // clearAnimation() calls above, we need to reset them. 1.460 + mLockFadeIn.reset(); 1.461 + mTitleSlideLeft.reset(); 1.462 + mTitleSlideRight.reset(); 1.463 + 1.464 + if (mForwardAnim != null) { 1.465 + long delay = mForwardAnim.getRemainingTime(); 1.466 + mTitleSlideRight.setStartOffset(delay); 1.467 + mTitleSlideLeft.setStartOffset(delay); 1.468 + } else { 1.469 + mTitleSlideRight.setStartOffset(0); 1.470 + mTitleSlideLeft.setStartOffset(0); 1.471 + } 1.472 + 1.473 + mTitle.startAnimation(visible ? mTitleSlideRight : mTitleSlideLeft); 1.474 + } 1.475 + 1.476 + List<View> getFocusOrder() { 1.477 + return Arrays.asList(mSiteSecurity, mPageActionLayout, mStop); 1.478 + } 1.479 + 1.480 + void setOnStopListener(OnStopListener listener) { 1.481 + mStopListener = listener; 1.482 + } 1.483 + 1.484 + void setOnTitleChangeListener(OnTitleChangeListener listener) { 1.485 + mTitleChangeListener = listener; 1.486 + } 1.487 + 1.488 + View getDoorHangerAnchor() { 1.489 + return mFavicon; 1.490 + } 1.491 + 1.492 + void prepareForwardAnimation(PropertyAnimator anim, ForwardButtonAnimation animation, int width) { 1.493 + mForwardAnim = anim; 1.494 + 1.495 + if (animation == ForwardButtonAnimation.HIDE) { 1.496 + anim.attach(mTitle, 1.497 + PropertyAnimator.Property.TRANSLATION_X, 1.498 + 0); 1.499 + anim.attach(mFavicon, 1.500 + PropertyAnimator.Property.TRANSLATION_X, 1.501 + 0); 1.502 + anim.attach(mSiteSecurity, 1.503 + PropertyAnimator.Property.TRANSLATION_X, 1.504 + 0); 1.505 + 1.506 + // We're hiding the forward button. We're going to reset the margin before 1.507 + // the animation starts, so we shift these items to the right so that they don't 1.508 + // appear to move initially. 1.509 + ViewHelper.setTranslationX(mTitle, width); 1.510 + ViewHelper.setTranslationX(mFavicon, width); 1.511 + ViewHelper.setTranslationX(mSiteSecurity, width); 1.512 + } else { 1.513 + anim.attach(mTitle, 1.514 + PropertyAnimator.Property.TRANSLATION_X, 1.515 + width); 1.516 + anim.attach(mFavicon, 1.517 + PropertyAnimator.Property.TRANSLATION_X, 1.518 + width); 1.519 + anim.attach(mSiteSecurity, 1.520 + PropertyAnimator.Property.TRANSLATION_X, 1.521 + width); 1.522 + } 1.523 + } 1.524 + 1.525 + void finishForwardAnimation() { 1.526 + ViewHelper.setTranslationX(mTitle, 0); 1.527 + ViewHelper.setTranslationX(mFavicon, 0); 1.528 + ViewHelper.setTranslationX(mSiteSecurity, 0); 1.529 + 1.530 + mForwardAnim = null; 1.531 + } 1.532 + 1.533 + void prepareStartEditingAnimation() { 1.534 + // Hide page actions/stop buttons immediately 1.535 + ViewHelper.setAlpha(mPageActionLayout, 0); 1.536 + ViewHelper.setAlpha(mStop, 0); 1.537 + } 1.538 + 1.539 + void prepareStopEditingAnimation(PropertyAnimator anim) { 1.540 + // Fade toolbar buttons (page actions, stop) after the entry 1.541 + // is schrunk back to its original size. 1.542 + anim.attach(mPageActionLayout, 1.543 + PropertyAnimator.Property.ALPHA, 1.544 + 1); 1.545 + 1.546 + anim.attach(mStop, 1.547 + PropertyAnimator.Property.ALPHA, 1.548 + 1); 1.549 + } 1.550 + 1.551 + boolean dismissSiteIdentityPopup() { 1.552 + if (mSiteIdentityPopup != null && mSiteIdentityPopup.isShowing()) { 1.553 + mSiteIdentityPopup.dismiss(); 1.554 + return true; 1.555 + } 1.556 + 1.557 + return false; 1.558 + } 1.559 +}