Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- |
michael@0 | 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | package org.mozilla.gecko.toolbar; |
michael@0 | 7 | |
michael@0 | 8 | import org.mozilla.gecko.AboutPages; |
michael@0 | 9 | import org.mozilla.gecko.animation.PropertyAnimator; |
michael@0 | 10 | import org.mozilla.gecko.animation.ViewHelper; |
michael@0 | 11 | import org.mozilla.gecko.BrowserApp; |
michael@0 | 12 | import org.mozilla.gecko.R; |
michael@0 | 13 | import org.mozilla.gecko.SiteIdentity; |
michael@0 | 14 | import org.mozilla.gecko.SiteIdentity.SecurityMode; |
michael@0 | 15 | import org.mozilla.gecko.Tab; |
michael@0 | 16 | import org.mozilla.gecko.Tabs; |
michael@0 | 17 | import org.mozilla.gecko.toolbar.BrowserToolbar.ForwardButtonAnimation; |
michael@0 | 18 | import org.mozilla.gecko.util.StringUtils; |
michael@0 | 19 | import org.mozilla.gecko.widget.ThemedLinearLayout; |
michael@0 | 20 | import org.mozilla.gecko.widget.ThemedTextView; |
michael@0 | 21 | |
michael@0 | 22 | import org.json.JSONObject; |
michael@0 | 23 | |
michael@0 | 24 | import android.content.Context; |
michael@0 | 25 | import android.content.res.Resources; |
michael@0 | 26 | import android.graphics.Bitmap; |
michael@0 | 27 | import android.os.Build; |
michael@0 | 28 | import android.os.SystemClock; |
michael@0 | 29 | import android.text.style.ForegroundColorSpan; |
michael@0 | 30 | import android.text.Spannable; |
michael@0 | 31 | import android.text.SpannableStringBuilder; |
michael@0 | 32 | import android.text.Spanned; |
michael@0 | 33 | import android.text.TextUtils; |
michael@0 | 34 | import android.util.AttributeSet; |
michael@0 | 35 | import android.util.Log; |
michael@0 | 36 | import android.view.LayoutInflater; |
michael@0 | 37 | import android.view.View; |
michael@0 | 38 | import android.view.animation.Animation; |
michael@0 | 39 | import android.view.animation.AnimationUtils; |
michael@0 | 40 | import android.view.animation.AlphaAnimation; |
michael@0 | 41 | import android.view.animation.TranslateAnimation; |
michael@0 | 42 | import android.widget.Button; |
michael@0 | 43 | import android.widget.ImageButton; |
michael@0 | 44 | import android.widget.LinearLayout.LayoutParams; |
michael@0 | 45 | |
michael@0 | 46 | import java.util.Arrays; |
michael@0 | 47 | import java.util.EnumSet; |
michael@0 | 48 | import java.util.List; |
michael@0 | 49 | |
michael@0 | 50 | /** |
michael@0 | 51 | * {@code ToolbarDisplayLayout} is the UI for when the toolbar is in |
michael@0 | 52 | * display state. It's used to display the state of the currently selected |
michael@0 | 53 | * tab. It should always be updated through a single entry point |
michael@0 | 54 | * (updateFromTab) and should never track any tab events or gecko messages |
michael@0 | 55 | * on its own to keep it as dumb as possible. |
michael@0 | 56 | * |
michael@0 | 57 | * The UI has two possible modes: progress and display which are triggered |
michael@0 | 58 | * when UpdateFlags.PROGRESS is used depending on the current tab state. |
michael@0 | 59 | * The progress mode is triggered when the tab is loading a page. Display mode |
michael@0 | 60 | * is used otherwise. |
michael@0 | 61 | * |
michael@0 | 62 | * {@code ToolbarDisplayLayout} is meant to be owned by {@code BrowserToolbar} |
michael@0 | 63 | * which is the main event bus for the toolbar subsystem. |
michael@0 | 64 | */ |
michael@0 | 65 | public class ToolbarDisplayLayout extends ThemedLinearLayout |
michael@0 | 66 | implements Animation.AnimationListener { |
michael@0 | 67 | |
michael@0 | 68 | private static final String LOGTAG = "GeckoToolbarDisplayLayout"; |
michael@0 | 69 | |
michael@0 | 70 | // To be used with updateFromTab() to allow the caller |
michael@0 | 71 | // to give enough context for the requested state change. |
michael@0 | 72 | enum UpdateFlags { |
michael@0 | 73 | TITLE, |
michael@0 | 74 | FAVICON, |
michael@0 | 75 | PROGRESS, |
michael@0 | 76 | SITE_IDENTITY, |
michael@0 | 77 | PRIVATE_MODE, |
michael@0 | 78 | |
michael@0 | 79 | // Disable any animation that might be |
michael@0 | 80 | // triggered from this state change. Mostly |
michael@0 | 81 | // used on tab switches, see BrowserToolbar. |
michael@0 | 82 | DISABLE_ANIMATIONS |
michael@0 | 83 | } |
michael@0 | 84 | |
michael@0 | 85 | private enum UIMode { |
michael@0 | 86 | PROGRESS, |
michael@0 | 87 | DISPLAY |
michael@0 | 88 | } |
michael@0 | 89 | |
michael@0 | 90 | interface OnStopListener { |
michael@0 | 91 | public Tab onStop(); |
michael@0 | 92 | } |
michael@0 | 93 | |
michael@0 | 94 | interface OnTitleChangeListener { |
michael@0 | 95 | public void onTitleChange(CharSequence title); |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | private final BrowserApp mActivity; |
michael@0 | 99 | |
michael@0 | 100 | private UIMode mUiMode; |
michael@0 | 101 | |
michael@0 | 102 | private ThemedTextView mTitle; |
michael@0 | 103 | private int mTitlePadding; |
michael@0 | 104 | private ToolbarTitlePrefs mTitlePrefs; |
michael@0 | 105 | private OnTitleChangeListener mTitleChangeListener; |
michael@0 | 106 | |
michael@0 | 107 | private ImageButton mSiteSecurity; |
michael@0 | 108 | private boolean mSiteSecurityVisible; |
michael@0 | 109 | |
michael@0 | 110 | // To de-bounce sets. |
michael@0 | 111 | private Bitmap mLastFavicon; |
michael@0 | 112 | private ImageButton mFavicon; |
michael@0 | 113 | private int mFaviconSize; |
michael@0 | 114 | |
michael@0 | 115 | private ImageButton mStop; |
michael@0 | 116 | private OnStopListener mStopListener; |
michael@0 | 117 | |
michael@0 | 118 | private PageActionLayout mPageActionLayout; |
michael@0 | 119 | |
michael@0 | 120 | private AlphaAnimation mLockFadeIn; |
michael@0 | 121 | private TranslateAnimation mTitleSlideLeft; |
michael@0 | 122 | private TranslateAnimation mTitleSlideRight; |
michael@0 | 123 | |
michael@0 | 124 | private SiteIdentityPopup mSiteIdentityPopup; |
michael@0 | 125 | private SecurityMode mSecurityMode; |
michael@0 | 126 | |
michael@0 | 127 | private PropertyAnimator mForwardAnim; |
michael@0 | 128 | |
michael@0 | 129 | private final ForegroundColorSpan mUrlColor; |
michael@0 | 130 | private final ForegroundColorSpan mBlockedColor; |
michael@0 | 131 | private final ForegroundColorSpan mDomainColor; |
michael@0 | 132 | private final ForegroundColorSpan mPrivateDomainColor; |
michael@0 | 133 | |
michael@0 | 134 | public ToolbarDisplayLayout(Context context, AttributeSet attrs) { |
michael@0 | 135 | super(context, attrs); |
michael@0 | 136 | setOrientation(HORIZONTAL); |
michael@0 | 137 | |
michael@0 | 138 | mActivity = (BrowserApp) context; |
michael@0 | 139 | |
michael@0 | 140 | LayoutInflater.from(context).inflate(R.layout.toolbar_display_layout, this); |
michael@0 | 141 | |
michael@0 | 142 | mTitle = (ThemedTextView) findViewById(R.id.url_bar_title); |
michael@0 | 143 | mTitlePadding = mTitle.getPaddingRight(); |
michael@0 | 144 | |
michael@0 | 145 | final Resources res = getResources(); |
michael@0 | 146 | |
michael@0 | 147 | mUrlColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_urltext)); |
michael@0 | 148 | mBlockedColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_blockedtext)); |
michael@0 | 149 | mDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext)); |
michael@0 | 150 | mPrivateDomainColor = new ForegroundColorSpan(res.getColor(R.color.url_bar_domaintext_private)); |
michael@0 | 151 | |
michael@0 | 152 | mFavicon = (ImageButton) findViewById(R.id.favicon); |
michael@0 | 153 | if (Build.VERSION.SDK_INT >= 16) { |
michael@0 | 154 | mFavicon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); |
michael@0 | 155 | } |
michael@0 | 156 | mFaviconSize = Math.round(res.getDimension(R.dimen.browser_toolbar_favicon_size)); |
michael@0 | 157 | |
michael@0 | 158 | mSiteSecurity = (ImageButton) findViewById(R.id.site_security); |
michael@0 | 159 | mSiteSecurityVisible = (mSiteSecurity.getVisibility() == View.VISIBLE); |
michael@0 | 160 | |
michael@0 | 161 | mSiteIdentityPopup = new SiteIdentityPopup(mActivity); |
michael@0 | 162 | mSiteIdentityPopup.setAnchor(mSiteSecurity); |
michael@0 | 163 | |
michael@0 | 164 | mStop = (ImageButton) findViewById(R.id.stop); |
michael@0 | 165 | mPageActionLayout = (PageActionLayout) findViewById(R.id.page_action_layout); |
michael@0 | 166 | } |
michael@0 | 167 | |
michael@0 | 168 | @Override |
michael@0 | 169 | public void onAttachedToWindow() { |
michael@0 | 170 | mTitlePrefs = new ToolbarTitlePrefs(); |
michael@0 | 171 | |
michael@0 | 172 | Button.OnClickListener faviconListener = new Button.OnClickListener() { |
michael@0 | 173 | @Override |
michael@0 | 174 | public void onClick(View view) { |
michael@0 | 175 | if (mSiteSecurity.getVisibility() != View.VISIBLE) { |
michael@0 | 176 | return; |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | mSiteIdentityPopup.show(); |
michael@0 | 180 | } |
michael@0 | 181 | }; |
michael@0 | 182 | |
michael@0 | 183 | mFavicon.setOnClickListener(faviconListener); |
michael@0 | 184 | mSiteSecurity.setOnClickListener(faviconListener); |
michael@0 | 185 | |
michael@0 | 186 | mStop.setOnClickListener(new Button.OnClickListener() { |
michael@0 | 187 | @Override |
michael@0 | 188 | public void onClick(View v) { |
michael@0 | 189 | if (mStopListener != null) { |
michael@0 | 190 | // Force toolbar to switch to Display mode |
michael@0 | 191 | // immediately based on the stopped tab. |
michael@0 | 192 | final Tab tab = mStopListener.onStop(); |
michael@0 | 193 | if (tab != null) { |
michael@0 | 194 | updateUiMode(tab, UIMode.DISPLAY, EnumSet.noneOf(UpdateFlags.class)); |
michael@0 | 195 | } |
michael@0 | 196 | } |
michael@0 | 197 | } |
michael@0 | 198 | }); |
michael@0 | 199 | |
michael@0 | 200 | float slideWidth = getResources().getDimension(R.dimen.browser_toolbar_lock_width); |
michael@0 | 201 | |
michael@0 | 202 | LayoutParams siteSecParams = (LayoutParams) mSiteSecurity.getLayoutParams(); |
michael@0 | 203 | final float scale = getResources().getDisplayMetrics().density; |
michael@0 | 204 | slideWidth += (siteSecParams.leftMargin + siteSecParams.rightMargin) * scale + 0.5f; |
michael@0 | 205 | |
michael@0 | 206 | mLockFadeIn = new AlphaAnimation(0.0f, 1.0f); |
michael@0 | 207 | mLockFadeIn.setAnimationListener(this); |
michael@0 | 208 | |
michael@0 | 209 | mTitleSlideLeft = new TranslateAnimation(slideWidth, 0, 0, 0); |
michael@0 | 210 | mTitleSlideLeft.setAnimationListener(this); |
michael@0 | 211 | |
michael@0 | 212 | mTitleSlideRight = new TranslateAnimation(-slideWidth, 0, 0, 0); |
michael@0 | 213 | mTitleSlideRight.setAnimationListener(this); |
michael@0 | 214 | |
michael@0 | 215 | final int lockAnimDuration = 300; |
michael@0 | 216 | mLockFadeIn.setDuration(lockAnimDuration); |
michael@0 | 217 | mTitleSlideLeft.setDuration(lockAnimDuration); |
michael@0 | 218 | mTitleSlideRight.setDuration(lockAnimDuration); |
michael@0 | 219 | } |
michael@0 | 220 | |
michael@0 | 221 | @Override |
michael@0 | 222 | public void onDetachedFromWindow() { |
michael@0 | 223 | mTitlePrefs.close(); |
michael@0 | 224 | } |
michael@0 | 225 | |
michael@0 | 226 | @Override |
michael@0 | 227 | public void onAnimationStart(Animation animation) { |
michael@0 | 228 | if (animation.equals(mLockFadeIn)) { |
michael@0 | 229 | if (mSiteSecurityVisible) |
michael@0 | 230 | mSiteSecurity.setVisibility(View.VISIBLE); |
michael@0 | 231 | } else if (animation.equals(mTitleSlideLeft)) { |
michael@0 | 232 | // These two animations may be scheduled to start while the forward |
michael@0 | 233 | // animation is occurring. If we're showing the site security icon, make |
michael@0 | 234 | // sure it doesn't take any space during the forward transition. |
michael@0 | 235 | mSiteSecurity.setVisibility(View.GONE); |
michael@0 | 236 | } else if (animation.equals(mTitleSlideRight)) { |
michael@0 | 237 | // If we're hiding the icon, make sure that we keep its padding |
michael@0 | 238 | // in place during the forward transition |
michael@0 | 239 | mSiteSecurity.setVisibility(View.INVISIBLE); |
michael@0 | 240 | } |
michael@0 | 241 | } |
michael@0 | 242 | |
michael@0 | 243 | @Override |
michael@0 | 244 | public void onAnimationRepeat(Animation animation) { |
michael@0 | 245 | } |
michael@0 | 246 | |
michael@0 | 247 | @Override |
michael@0 | 248 | public void onAnimationEnd(Animation animation) { |
michael@0 | 249 | if (animation.equals(mTitleSlideRight)) { |
michael@0 | 250 | mSiteSecurity.startAnimation(mLockFadeIn); |
michael@0 | 251 | } |
michael@0 | 252 | } |
michael@0 | 253 | |
michael@0 | 254 | @Override |
michael@0 | 255 | public void setNextFocusDownId(int nextId) { |
michael@0 | 256 | mFavicon.setNextFocusDownId(nextId); |
michael@0 | 257 | mStop.setNextFocusDownId(nextId); |
michael@0 | 258 | mSiteSecurity.setNextFocusDownId(nextId); |
michael@0 | 259 | mPageActionLayout.setNextFocusDownId(nextId); |
michael@0 | 260 | } |
michael@0 | 261 | |
michael@0 | 262 | void updateFromTab(Tab tab, EnumSet<UpdateFlags> flags) { |
michael@0 | 263 | if (flags.contains(UpdateFlags.TITLE)) { |
michael@0 | 264 | updateTitle(tab); |
michael@0 | 265 | } |
michael@0 | 266 | |
michael@0 | 267 | if (flags.contains(UpdateFlags.FAVICON)) { |
michael@0 | 268 | updateFavicon(tab); |
michael@0 | 269 | } |
michael@0 | 270 | |
michael@0 | 271 | if (flags.contains(UpdateFlags.SITE_IDENTITY)) { |
michael@0 | 272 | updateSiteIdentity(tab, flags); |
michael@0 | 273 | } |
michael@0 | 274 | |
michael@0 | 275 | if (flags.contains(UpdateFlags.PROGRESS)) { |
michael@0 | 276 | updateProgress(tab, flags); |
michael@0 | 277 | } |
michael@0 | 278 | |
michael@0 | 279 | if (flags.contains(UpdateFlags.PRIVATE_MODE)) { |
michael@0 | 280 | mTitle.setPrivateMode(tab != null && tab.isPrivate()); |
michael@0 | 281 | } |
michael@0 | 282 | } |
michael@0 | 283 | |
michael@0 | 284 | void setTitle(CharSequence title) { |
michael@0 | 285 | mTitle.setText(title); |
michael@0 | 286 | |
michael@0 | 287 | if (mTitleChangeListener != null) { |
michael@0 | 288 | mTitleChangeListener.onTitleChange(title); |
michael@0 | 289 | } |
michael@0 | 290 | } |
michael@0 | 291 | |
michael@0 | 292 | private void updateTitle(Tab tab) { |
michael@0 | 293 | // Keep the title unchanged if there's no selected tab, |
michael@0 | 294 | // or if the tab is entering reader mode. |
michael@0 | 295 | if (tab == null || tab.isEnteringReaderMode()) { |
michael@0 | 296 | return; |
michael@0 | 297 | } |
michael@0 | 298 | |
michael@0 | 299 | final String url = tab.getURL(); |
michael@0 | 300 | |
michael@0 | 301 | // Setting a null title will ensure we just see the |
michael@0 | 302 | // "Enter Search or Address" placeholder text. |
michael@0 | 303 | if (AboutPages.isTitlelessAboutPage(url)) { |
michael@0 | 304 | setTitle(null); |
michael@0 | 305 | return; |
michael@0 | 306 | } |
michael@0 | 307 | |
michael@0 | 308 | // Show the about:blocked page title in red, regardless of prefs |
michael@0 | 309 | if (tab.getErrorType() == Tab.ErrorType.BLOCKED) { |
michael@0 | 310 | final String title = tab.getDisplayTitle(); |
michael@0 | 311 | |
michael@0 | 312 | final SpannableStringBuilder builder = new SpannableStringBuilder(title); |
michael@0 | 313 | builder.setSpan(mBlockedColor, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); |
michael@0 | 314 | |
michael@0 | 315 | setTitle(builder); |
michael@0 | 316 | return; |
michael@0 | 317 | } |
michael@0 | 318 | |
michael@0 | 319 | // If the pref to show the URL isn't set, just use the tab's display title. |
michael@0 | 320 | if (!mTitlePrefs.shouldShowUrl() || url == null) { |
michael@0 | 321 | setTitle(tab.getDisplayTitle()); |
michael@0 | 322 | return; |
michael@0 | 323 | } |
michael@0 | 324 | |
michael@0 | 325 | CharSequence title = url; |
michael@0 | 326 | if (mTitlePrefs.shouldTrimUrls()) { |
michael@0 | 327 | title = StringUtils.stripCommonSubdomains(StringUtils.stripScheme(url)); |
michael@0 | 328 | } |
michael@0 | 329 | |
michael@0 | 330 | final String baseDomain = tab.getBaseDomain(); |
michael@0 | 331 | if (!TextUtils.isEmpty(baseDomain)) { |
michael@0 | 332 | final SpannableStringBuilder builder = new SpannableStringBuilder(title); |
michael@0 | 333 | |
michael@0 | 334 | int index = title.toString().indexOf(baseDomain); |
michael@0 | 335 | if (index > -1) { |
michael@0 | 336 | builder.setSpan(mUrlColor, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); |
michael@0 | 337 | builder.setSpan(tab.isPrivate() ? mPrivateDomainColor : mDomainColor, |
michael@0 | 338 | index, index + baseDomain.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); |
michael@0 | 339 | |
michael@0 | 340 | title = builder; |
michael@0 | 341 | } |
michael@0 | 342 | } |
michael@0 | 343 | |
michael@0 | 344 | setTitle(title); |
michael@0 | 345 | } |
michael@0 | 346 | |
michael@0 | 347 | private void updateFavicon(Tab tab) { |
michael@0 | 348 | if (tab == null) { |
michael@0 | 349 | mFavicon.setImageDrawable(null); |
michael@0 | 350 | return; |
michael@0 | 351 | } |
michael@0 | 352 | |
michael@0 | 353 | Bitmap image = tab.getFavicon(); |
michael@0 | 354 | |
michael@0 | 355 | if (image != null && image == mLastFavicon) { |
michael@0 | 356 | Log.d(LOGTAG, "Ignoring favicon: new image is identical to previous one."); |
michael@0 | 357 | return; |
michael@0 | 358 | } |
michael@0 | 359 | |
michael@0 | 360 | // Cache the original so we can debounce without scaling |
michael@0 | 361 | mLastFavicon = image; |
michael@0 | 362 | |
michael@0 | 363 | Log.d(LOGTAG, "updateFavicon(" + image + ")"); |
michael@0 | 364 | |
michael@0 | 365 | if (image != null) { |
michael@0 | 366 | image = Bitmap.createScaledBitmap(image, mFaviconSize, mFaviconSize, false); |
michael@0 | 367 | mFavicon.setImageBitmap(image); |
michael@0 | 368 | } else { |
michael@0 | 369 | mFavicon.setImageResource(R.drawable.favicon); |
michael@0 | 370 | } |
michael@0 | 371 | } |
michael@0 | 372 | |
michael@0 | 373 | private void updateSiteIdentity(Tab tab, EnumSet<UpdateFlags> flags) { |
michael@0 | 374 | final SiteIdentity siteIdentity; |
michael@0 | 375 | if (tab == null) { |
michael@0 | 376 | siteIdentity = null; |
michael@0 | 377 | } else { |
michael@0 | 378 | siteIdentity = tab.getSiteIdentity(); |
michael@0 | 379 | } |
michael@0 | 380 | |
michael@0 | 381 | mSiteIdentityPopup.setSiteIdentity(siteIdentity); |
michael@0 | 382 | |
michael@0 | 383 | final SecurityMode securityMode; |
michael@0 | 384 | if (siteIdentity == null) { |
michael@0 | 385 | securityMode = SecurityMode.UNKNOWN; |
michael@0 | 386 | } else { |
michael@0 | 387 | securityMode = siteIdentity.getSecurityMode(); |
michael@0 | 388 | } |
michael@0 | 389 | |
michael@0 | 390 | if (mSecurityMode != securityMode) { |
michael@0 | 391 | mSecurityMode = securityMode; |
michael@0 | 392 | mSiteSecurity.setImageLevel(mSecurityMode.ordinal()); |
michael@0 | 393 | updatePageActions(flags); |
michael@0 | 394 | } |
michael@0 | 395 | } |
michael@0 | 396 | |
michael@0 | 397 | private void updateProgress(Tab tab, EnumSet<UpdateFlags> flags) { |
michael@0 | 398 | final boolean shouldShowThrobber = (tab != null && |
michael@0 | 399 | tab.getState() == Tab.STATE_LOADING); |
michael@0 | 400 | |
michael@0 | 401 | updateUiMode(tab, shouldShowThrobber ? UIMode.PROGRESS : UIMode.DISPLAY, flags); |
michael@0 | 402 | } |
michael@0 | 403 | |
michael@0 | 404 | private void updateUiMode(Tab tab, UIMode uiMode, EnumSet<UpdateFlags> flags) { |
michael@0 | 405 | if (mUiMode == uiMode) { |
michael@0 | 406 | return; |
michael@0 | 407 | } |
michael@0 | 408 | |
michael@0 | 409 | mUiMode = uiMode; |
michael@0 | 410 | |
michael@0 | 411 | // The "Throbber start" and "Throbber stop" log messages in this method |
michael@0 | 412 | // are needed by S1/S2 tests (http://mrcote.info/phonedash/#). |
michael@0 | 413 | // See discussion in Bug 804457. Bug 805124 tracks paring these down. |
michael@0 | 414 | if (mUiMode == UIMode.PROGRESS) { |
michael@0 | 415 | Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - Throbber start"); |
michael@0 | 416 | } else { |
michael@0 | 417 | Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - Throbber stop"); |
michael@0 | 418 | } |
michael@0 | 419 | |
michael@0 | 420 | updatePageActions(flags); |
michael@0 | 421 | } |
michael@0 | 422 | |
michael@0 | 423 | private void updatePageActions(EnumSet<UpdateFlags> flags) { |
michael@0 | 424 | final boolean isShowingProgress = (mUiMode == UIMode.PROGRESS); |
michael@0 | 425 | |
michael@0 | 426 | mStop.setVisibility(isShowingProgress ? View.VISIBLE : View.GONE); |
michael@0 | 427 | mPageActionLayout.setVisibility(!isShowingProgress ? View.VISIBLE : View.GONE); |
michael@0 | 428 | |
michael@0 | 429 | boolean shouldShowSiteSecurity = (!isShowingProgress && |
michael@0 | 430 | mSecurityMode != SecurityMode.UNKNOWN); |
michael@0 | 431 | |
michael@0 | 432 | setSiteSecurityVisibility(shouldShowSiteSecurity, flags); |
michael@0 | 433 | |
michael@0 | 434 | // We want title to fill the whole space available for it when there are icons |
michael@0 | 435 | // being shown on the right side of the toolbar as the icons already have some |
michael@0 | 436 | // padding in them. This is just to avoid wasting space when icons are shown. |
michael@0 | 437 | mTitle.setPadding(0, 0, (!isShowingProgress ? mTitlePadding : 0), 0); |
michael@0 | 438 | } |
michael@0 | 439 | |
michael@0 | 440 | private void setSiteSecurityVisibility(boolean visible, EnumSet<UpdateFlags> flags) { |
michael@0 | 441 | if (visible == mSiteSecurityVisible) { |
michael@0 | 442 | return; |
michael@0 | 443 | } |
michael@0 | 444 | |
michael@0 | 445 | mSiteSecurityVisible = visible; |
michael@0 | 446 | |
michael@0 | 447 | mTitle.clearAnimation(); |
michael@0 | 448 | mSiteSecurity.clearAnimation(); |
michael@0 | 449 | |
michael@0 | 450 | if (flags.contains(UpdateFlags.DISABLE_ANIMATIONS)) { |
michael@0 | 451 | mSiteSecurity.setVisibility(visible ? View.VISIBLE : View.GONE); |
michael@0 | 452 | return; |
michael@0 | 453 | } |
michael@0 | 454 | |
michael@0 | 455 | // If any of these animations were cancelled as a result of the |
michael@0 | 456 | // clearAnimation() calls above, we need to reset them. |
michael@0 | 457 | mLockFadeIn.reset(); |
michael@0 | 458 | mTitleSlideLeft.reset(); |
michael@0 | 459 | mTitleSlideRight.reset(); |
michael@0 | 460 | |
michael@0 | 461 | if (mForwardAnim != null) { |
michael@0 | 462 | long delay = mForwardAnim.getRemainingTime(); |
michael@0 | 463 | mTitleSlideRight.setStartOffset(delay); |
michael@0 | 464 | mTitleSlideLeft.setStartOffset(delay); |
michael@0 | 465 | } else { |
michael@0 | 466 | mTitleSlideRight.setStartOffset(0); |
michael@0 | 467 | mTitleSlideLeft.setStartOffset(0); |
michael@0 | 468 | } |
michael@0 | 469 | |
michael@0 | 470 | mTitle.startAnimation(visible ? mTitleSlideRight : mTitleSlideLeft); |
michael@0 | 471 | } |
michael@0 | 472 | |
michael@0 | 473 | List<View> getFocusOrder() { |
michael@0 | 474 | return Arrays.asList(mSiteSecurity, mPageActionLayout, mStop); |
michael@0 | 475 | } |
michael@0 | 476 | |
michael@0 | 477 | void setOnStopListener(OnStopListener listener) { |
michael@0 | 478 | mStopListener = listener; |
michael@0 | 479 | } |
michael@0 | 480 | |
michael@0 | 481 | void setOnTitleChangeListener(OnTitleChangeListener listener) { |
michael@0 | 482 | mTitleChangeListener = listener; |
michael@0 | 483 | } |
michael@0 | 484 | |
michael@0 | 485 | View getDoorHangerAnchor() { |
michael@0 | 486 | return mFavicon; |
michael@0 | 487 | } |
michael@0 | 488 | |
michael@0 | 489 | void prepareForwardAnimation(PropertyAnimator anim, ForwardButtonAnimation animation, int width) { |
michael@0 | 490 | mForwardAnim = anim; |
michael@0 | 491 | |
michael@0 | 492 | if (animation == ForwardButtonAnimation.HIDE) { |
michael@0 | 493 | anim.attach(mTitle, |
michael@0 | 494 | PropertyAnimator.Property.TRANSLATION_X, |
michael@0 | 495 | 0); |
michael@0 | 496 | anim.attach(mFavicon, |
michael@0 | 497 | PropertyAnimator.Property.TRANSLATION_X, |
michael@0 | 498 | 0); |
michael@0 | 499 | anim.attach(mSiteSecurity, |
michael@0 | 500 | PropertyAnimator.Property.TRANSLATION_X, |
michael@0 | 501 | 0); |
michael@0 | 502 | |
michael@0 | 503 | // We're hiding the forward button. We're going to reset the margin before |
michael@0 | 504 | // the animation starts, so we shift these items to the right so that they don't |
michael@0 | 505 | // appear to move initially. |
michael@0 | 506 | ViewHelper.setTranslationX(mTitle, width); |
michael@0 | 507 | ViewHelper.setTranslationX(mFavicon, width); |
michael@0 | 508 | ViewHelper.setTranslationX(mSiteSecurity, width); |
michael@0 | 509 | } else { |
michael@0 | 510 | anim.attach(mTitle, |
michael@0 | 511 | PropertyAnimator.Property.TRANSLATION_X, |
michael@0 | 512 | width); |
michael@0 | 513 | anim.attach(mFavicon, |
michael@0 | 514 | PropertyAnimator.Property.TRANSLATION_X, |
michael@0 | 515 | width); |
michael@0 | 516 | anim.attach(mSiteSecurity, |
michael@0 | 517 | PropertyAnimator.Property.TRANSLATION_X, |
michael@0 | 518 | width); |
michael@0 | 519 | } |
michael@0 | 520 | } |
michael@0 | 521 | |
michael@0 | 522 | void finishForwardAnimation() { |
michael@0 | 523 | ViewHelper.setTranslationX(mTitle, 0); |
michael@0 | 524 | ViewHelper.setTranslationX(mFavicon, 0); |
michael@0 | 525 | ViewHelper.setTranslationX(mSiteSecurity, 0); |
michael@0 | 526 | |
michael@0 | 527 | mForwardAnim = null; |
michael@0 | 528 | } |
michael@0 | 529 | |
michael@0 | 530 | void prepareStartEditingAnimation() { |
michael@0 | 531 | // Hide page actions/stop buttons immediately |
michael@0 | 532 | ViewHelper.setAlpha(mPageActionLayout, 0); |
michael@0 | 533 | ViewHelper.setAlpha(mStop, 0); |
michael@0 | 534 | } |
michael@0 | 535 | |
michael@0 | 536 | void prepareStopEditingAnimation(PropertyAnimator anim) { |
michael@0 | 537 | // Fade toolbar buttons (page actions, stop) after the entry |
michael@0 | 538 | // is schrunk back to its original size. |
michael@0 | 539 | anim.attach(mPageActionLayout, |
michael@0 | 540 | PropertyAnimator.Property.ALPHA, |
michael@0 | 541 | 1); |
michael@0 | 542 | |
michael@0 | 543 | anim.attach(mStop, |
michael@0 | 544 | PropertyAnimator.Property.ALPHA, |
michael@0 | 545 | 1); |
michael@0 | 546 | } |
michael@0 | 547 | |
michael@0 | 548 | boolean dismissSiteIdentityPopup() { |
michael@0 | 549 | if (mSiteIdentityPopup != null && mSiteIdentityPopup.isShowing()) { |
michael@0 | 550 | mSiteIdentityPopup.dismiss(); |
michael@0 | 551 | return true; |
michael@0 | 552 | } |
michael@0 | 553 | |
michael@0 | 554 | return false; |
michael@0 | 555 | } |
michael@0 | 556 | } |