1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.browser; 18 19 import android.animation.Animator; 20 import android.animation.Animator.AnimatorListener; 21 import android.animation.ObjectAnimator; 22 import android.content.Context; 23 import android.content.res.Configuration; 24 import android.content.res.Resources; 25 import android.view.LayoutInflater; 26 import android.view.View; 27 import android.view.ViewGroup; 28 import android.view.ViewStub; 29 import android.view.animation.Animation; 30 import android.view.animation.Animation.AnimationListener; 31 import android.view.animation.AnimationUtils; 32 import android.view.animation.DecelerateInterpolator; 33 import android.webkit.WebView; 34 import android.widget.FrameLayout; 35 import android.widget.RelativeLayout; 36 37 38 /** 39 * Base class for a title bar used by the browser. 40 */ 41 public class TitleBar extends RelativeLayout { 42 43 private static final int PROGRESS_MAX = 100; 44 private static final float ANIM_TITLEBAR_DECELERATE = 2.5f; 45 46 private UiController mUiController; 47 private BaseUi mBaseUi; 48 private FrameLayout mContentView; 49 private PageProgressView mProgress; 50 51 private AutologinBar mAutoLogin; 52 private NavigationBarBase mNavBar; 53 private boolean mUseQuickControls; 54 private SnapshotBar mSnapshotBar; 55 56 //state 57 private boolean mShowing; 58 private boolean mInLoad; 59 private boolean mSkipTitleBarAnimations; 60 private Animator mTitleBarAnimator; 61 private boolean mIsFixedTitleBar; 62 63 public TitleBar(Context context, UiController controller, BaseUi ui, 64 FrameLayout contentView) { 65 super(context, null); 66 mUiController = controller; 67 mBaseUi = ui; 68 mContentView = contentView; 69 initLayout(context); 70 setFixedTitleBar(); 71 } 72 73 private void initLayout(Context context) { 74 LayoutInflater factory = LayoutInflater.from(context); 75 factory.inflate(R.layout.title_bar, this); 76 mProgress = (PageProgressView) findViewById(R.id.progress); 77 mNavBar = (NavigationBarBase) findViewById(R.id.taburlbar); 78 mNavBar.setTitleBar(this); 79 } 80 81 private void inflateAutoLoginBar() { 82 if (mAutoLogin != null) { 83 return; 84 } 85 86 ViewStub stub = (ViewStub) findViewById(R.id.autologin_stub); 87 mAutoLogin = (AutologinBar) stub.inflate(); 88 mAutoLogin.setTitleBar(this); 89 } 90 91 private void inflateSnapshotBar() { 92 if (mSnapshotBar != null) { 93 return; 94 } 95 96 ViewStub stub = (ViewStub) findViewById(R.id.snapshotbar_stub); 97 mSnapshotBar = (SnapshotBar) stub.inflate(); 98 mSnapshotBar.setTitleBar(this); 99 } 100 101 @Override 102 protected void onConfigurationChanged(Configuration config) { 103 super.onConfigurationChanged(config); 104 setFixedTitleBar(); 105 } 106 107 @Override 108 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 109 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 110 if (mIsFixedTitleBar) { 111 int margin = getMeasuredHeight() - calculateEmbeddedHeight(); 112 mBaseUi.setContentViewMarginTop(-margin); 113 } else { 114 mBaseUi.setContentViewMarginTop(0); 115 } 116 } 117 118 private void setFixedTitleBar() { 119 boolean isFixed = !mUseQuickControls 120 && !mContext.getResources().getBoolean(R.bool.hide_title); 121 // If getParent() returns null, we are initializing 122 ViewGroup parent = (ViewGroup)getParent(); 123 if (mIsFixedTitleBar == isFixed && parent != null) return; 124 mIsFixedTitleBar = isFixed; 125 setSkipTitleBarAnimations(true); 126 show(); 127 setSkipTitleBarAnimations(false); 128 if (parent != null) { 129 parent.removeView(this); 130 } 131 if (mIsFixedTitleBar) { 132 mBaseUi.addFixedTitleBar(this); 133 } else { 134 mContentView.addView(this, makeLayoutParams()); 135 mBaseUi.setContentViewMarginTop(0); 136 } 137 } 138 139 public BaseUi getUi() { 140 return mBaseUi; 141 } 142 143 public UiController getUiController() { 144 return mUiController; 145 } 146 147 public void setUseQuickControls(boolean use) { 148 mUseQuickControls = use; 149 setFixedTitleBar(); 150 if (use) { 151 this.setVisibility(View.GONE); 152 } else { 153 this.setVisibility(View.VISIBLE); 154 } 155 } 156 157 void setShowProgressOnly(boolean progress) { 158 if (progress && !wantsToBeVisible()) { 159 mNavBar.setVisibility(View.GONE); 160 } else { 161 mNavBar.setVisibility(View.VISIBLE); 162 } 163 } 164 165 void setSkipTitleBarAnimations(boolean skip) { 166 mSkipTitleBarAnimations = skip; 167 } 168 169 void setupTitleBarAnimator(Animator animator) { 170 Resources res = mContext.getResources(); 171 int duration = res.getInteger(R.integer.titlebar_animation_duration); 172 animator.setInterpolator(new DecelerateInterpolator( 173 ANIM_TITLEBAR_DECELERATE)); 174 animator.setDuration(duration); 175 } 176 177 void show() { 178 cancelTitleBarAnimation(false); 179 if (mUseQuickControls || mSkipTitleBarAnimations) { 180 this.setVisibility(View.VISIBLE); 181 this.setTranslationY(0); 182 } else { 183 int visibleHeight = getVisibleTitleHeight(); 184 float startPos = (-getEmbeddedHeight() + visibleHeight); 185 if (getTranslationY() != 0) { 186 startPos = Math.max(startPos, getTranslationY()); 187 } 188 mTitleBarAnimator = ObjectAnimator.ofFloat(this, 189 "translationY", 190 startPos, 0); 191 setupTitleBarAnimator(mTitleBarAnimator); 192 mTitleBarAnimator.start(); 193 } 194 mShowing = true; 195 } 196 197 void hide() { 198 if (mUseQuickControls) { 199 this.setVisibility(View.GONE); 200 } else { 201 if (mIsFixedTitleBar) return; 202 if (!mSkipTitleBarAnimations) { 203 cancelTitleBarAnimation(false); 204 int visibleHeight = getVisibleTitleHeight(); 205 mTitleBarAnimator = ObjectAnimator.ofFloat(this, 206 "translationY", getTranslationY(), 207 (-getEmbeddedHeight() + visibleHeight)); 208 mTitleBarAnimator.addListener(mHideTileBarAnimatorListener); 209 setupTitleBarAnimator(mTitleBarAnimator); 210 mTitleBarAnimator.start(); 211 } else { 212 onScrollChanged(); 213 } 214 } 215 mShowing = false; 216 } 217 218 boolean isShowing() { 219 return mShowing; 220 } 221 222 void cancelTitleBarAnimation(boolean reset) { 223 if (mTitleBarAnimator != null) { 224 mTitleBarAnimator.cancel(); 225 mTitleBarAnimator = null; 226 } 227 if (reset) { 228 setTranslationY(0); 229 } 230 } 231 232 private AnimatorListener mHideTileBarAnimatorListener = new AnimatorListener() { 233 234 @Override 235 public void onAnimationStart(Animator animation) { 236 } 237 238 @Override 239 public void onAnimationRepeat(Animator animation) { 240 } 241 242 @Override 243 public void onAnimationEnd(Animator animation) { 244 // update position 245 onScrollChanged(); 246 } 247 248 @Override 249 public void onAnimationCancel(Animator animation) { 250 } 251 }; 252 253 private int getVisibleTitleHeight() { 254 Tab tab = mBaseUi.getActiveTab(); 255 WebView webview = tab != null ? tab.getWebView() : null; 256 return webview != null ? webview.getVisibleTitleHeight() : 0; 257 } 258 259 /** 260 * Update the progress, from 0 to 100. 261 */ 262 public void setProgress(int newProgress) { 263 if (newProgress >= PROGRESS_MAX) { 264 mProgress.setProgress(PageProgressView.MAX_PROGRESS); 265 mProgress.setVisibility(View.GONE); 266 mInLoad = false; 267 mNavBar.onProgressStopped(); 268 // check if needs to be hidden 269 if (!isEditingUrl() && !wantsToBeVisible()) { 270 if (mUseQuickControls) { 271 hide(); 272 } else { 273 mBaseUi.showTitleBarForDuration(); 274 } 275 } 276 } else { 277 if (!mInLoad) { 278 mProgress.setVisibility(View.VISIBLE); 279 mInLoad = true; 280 mNavBar.onProgressStarted(); 281 } 282 mProgress.setProgress(newProgress * PageProgressView.MAX_PROGRESS 283 / PROGRESS_MAX); 284 if (mUseQuickControls && !isEditingUrl()) { 285 setShowProgressOnly(true); 286 } 287 if (!mShowing) { 288 show(); 289 } 290 } 291 } 292 293 public int getEmbeddedHeight() { 294 if (mUseQuickControls || mIsFixedTitleBar) return 0; 295 return calculateEmbeddedHeight(); 296 } 297 298 private int calculateEmbeddedHeight() { 299 int height = mNavBar.getHeight(); 300 if (mAutoLogin != null && mAutoLogin.getVisibility() == View.VISIBLE) { 301 height += mAutoLogin.getHeight(); 302 } 303 return height; 304 } 305 306 public void updateAutoLogin(Tab tab, boolean animate) { 307 if (mAutoLogin == null) { 308 if (tab.getDeviceAccountLogin() == null) { 309 return; 310 } 311 inflateAutoLoginBar(); 312 } 313 mAutoLogin.updateAutoLogin(tab, animate); 314 } 315 316 public void showAutoLogin(boolean animate) { 317 if (mUseQuickControls) { 318 mBaseUi.showTitleBar(); 319 } 320 if (mAutoLogin == null) { 321 inflateAutoLoginBar(); 322 } 323 mAutoLogin.setVisibility(View.VISIBLE); 324 if (animate) { 325 mAutoLogin.startAnimation(AnimationUtils.loadAnimation( 326 getContext(), R.anim.autologin_enter)); 327 } 328 } 329 330 public void hideAutoLogin(boolean animate) { 331 if (mUseQuickControls) { 332 mBaseUi.hideTitleBar(); 333 mAutoLogin.setVisibility(View.GONE); 334 mBaseUi.refreshWebView(); 335 } else { 336 if (animate) { 337 Animation anim = AnimationUtils.loadAnimation(getContext(), 338 R.anim.autologin_exit); 339 anim.setAnimationListener(new AnimationListener() { 340 @Override 341 public void onAnimationEnd(Animation a) { 342 mAutoLogin.setVisibility(View.GONE); 343 mBaseUi.refreshWebView(); 344 } 345 346 @Override 347 public void onAnimationStart(Animation a) { 348 } 349 350 @Override 351 public void onAnimationRepeat(Animation a) { 352 } 353 }); 354 mAutoLogin.startAnimation(anim); 355 } else if (mAutoLogin.getAnimation() == null) { 356 mAutoLogin.setVisibility(View.GONE); 357 mBaseUi.refreshWebView(); 358 } 359 } 360 } 361 362 public boolean wantsToBeVisible() { 363 return inAutoLogin() 364 || (mSnapshotBar != null && mSnapshotBar.getVisibility() == View.VISIBLE 365 && mSnapshotBar.isAnimating()); 366 } 367 368 private boolean inAutoLogin() { 369 return mAutoLogin != null && mAutoLogin.getVisibility() == View.VISIBLE; 370 } 371 372 public boolean isEditingUrl() { 373 return mNavBar.isEditingUrl(); 374 } 375 376 public WebView getCurrentWebView() { 377 Tab t = mBaseUi.getActiveTab(); 378 if (t != null) { 379 return t.getWebView(); 380 } else { 381 return null; 382 } 383 } 384 385 public PageProgressView getProgressView() { 386 return mProgress; 387 } 388 389 public NavigationBarBase getNavigationBar() { 390 return mNavBar; 391 } 392 393 public boolean useQuickControls() { 394 return mUseQuickControls; 395 } 396 397 public boolean isInLoad() { 398 return mInLoad; 399 } 400 401 private ViewGroup.LayoutParams makeLayoutParams() { 402 return new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, 403 LayoutParams.WRAP_CONTENT); 404 } 405 406 @Override 407 public View focusSearch(View focused, int dir) { 408 WebView web = getCurrentWebView(); 409 if (FOCUS_DOWN == dir && hasFocus() && web != null 410 && web.hasFocusable() && web.getParent() != null) { 411 return web; 412 } 413 return super.focusSearch(focused, dir); 414 } 415 416 public void onTabDataChanged(Tab tab) { 417 if (mSnapshotBar != null) { 418 mSnapshotBar.onTabDataChanged(tab); 419 } 420 421 if (tab.isSnapshot()) { 422 inflateSnapshotBar(); 423 mSnapshotBar.setVisibility(VISIBLE); 424 mNavBar.setVisibility(GONE); 425 } else { 426 if (mSnapshotBar != null) { 427 mSnapshotBar.setVisibility(GONE); 428 } 429 mNavBar.setVisibility(VISIBLE); 430 } 431 } 432 433 public void onScrollChanged() { 434 if (!mShowing && !mIsFixedTitleBar) { 435 setTranslationY(getVisibleTitleHeight() - getEmbeddedHeight()); 436 } 437 } 438 439 } 440