TabBar.java revision 6a85e2ae52f5e7592731cfcf1b98639ea2a03b62
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 17package com.android.browser; 18 19import com.android.browser.ScrollWebView.ScrollListener; 20 21import android.app.Activity; 22import android.content.Context; 23import android.content.res.Resources; 24import android.graphics.Bitmap; 25import android.graphics.BitmapShader; 26import android.graphics.Canvas; 27import android.graphics.Color; 28import android.graphics.Paint; 29import android.graphics.Path; 30import android.graphics.Shader; 31import android.graphics.drawable.BitmapDrawable; 32import android.graphics.drawable.Drawable; 33import android.graphics.drawable.LayerDrawable; 34import android.graphics.drawable.PaintDrawable; 35import android.view.ContextMenu; 36import android.view.Gravity; 37import android.view.LayoutInflater; 38import android.view.MenuInflater; 39import android.view.View; 40import android.view.View.OnClickListener; 41import android.webkit.WebView; 42import android.widget.ImageButton; 43import android.widget.ImageView; 44import android.widget.LinearLayout; 45import android.widget.TextView; 46 47import java.util.HashMap; 48import java.util.List; 49import java.util.Map; 50 51/** 52 * tabbed title bar for xlarge screen browser 53 */ 54public class TabBar extends LinearLayout 55 implements ScrollListener, OnClickListener { 56 57 private static final int PROGRESS_MAX = 100; 58 59 private Activity mActivity; 60 private UiController mUiController; 61 private TabControl mTabControl; 62 private BaseUi mUi; 63 64 private final int mTabWidthSelected; 65 private final int mTabWidthUnselected; 66 67 private TabScrollView mTabs; 68 69 private ImageButton mNewTab; 70 private int mButtonWidth; 71 72 private Map<Tab, TabViewData> mTabMap; 73 74 private boolean mUserRequestedUrlbar; 75 private int mVisibleTitleHeight; 76 private boolean mHasReceivedTitle; 77 78 private Drawable mGenericFavicon; 79 private String mLoadingText; 80 81 private Drawable mActiveDrawable; 82 private Drawable mInactiveDrawable; 83 84 private Bitmap mShaderBuffer; 85 private Canvas mShaderCanvas; 86 private Paint mShaderPaint; 87 private int mTabHeight; 88 private int mTabOverlap; 89 private int mTabSliceWidth; 90 private int mTabPadding; 91 92 public TabBar(Activity activity, UiController controller, BaseUi ui) { 93 super(activity); 94 mActivity = activity; 95 mUiController = controller; 96 mTabControl = mUiController.getTabControl(); 97 mUi = ui; 98 Resources res = activity.getResources(); 99 mTabWidthSelected = (int) res.getDimension(R.dimen.tab_width_selected); 100 mTabWidthUnselected = (int) res.getDimension(R.dimen.tab_width_unselected); 101 mActiveDrawable = res.getDrawable(R.drawable.bg_urlbar); 102 mInactiveDrawable = res.getDrawable(R.drawable.browsertab_inactive); 103 104 mTabMap = new HashMap<Tab, TabViewData>(); 105 Resources resources = activity.getResources(); 106 LayoutInflater factory = LayoutInflater.from(activity); 107 factory.inflate(R.layout.tab_bar, this); 108 setPadding(12, 12, 0, 0); 109 mTabs = (TabScrollView) findViewById(R.id.tabs); 110 mNewTab = (ImageButton) findViewById(R.id.newtab); 111 mNewTab.setOnClickListener(this); 112 mGenericFavicon = res.getDrawable(R.drawable.app_web_browser_sm); 113 mLoadingText = res.getString(R.string.title_bar_loading); 114 setChildrenDrawingOrderEnabled(true); 115 116 // TODO: Change enabled states based on whether you can go 117 // back/forward. Probably should be done inside onPageStarted. 118 119 updateTabs(mUiController.getTabs()); 120 121 mUserRequestedUrlbar = false; 122 mVisibleTitleHeight = 1; 123 mButtonWidth = -1; 124 // tab dimensions 125 mTabHeight = (int) res.getDimension(R.dimen.tab_height); 126 mTabOverlap = (int) res.getDimension(R.dimen.tab_overlap); 127 mTabSliceWidth = (int) res.getDimension(R.dimen.tab_slice); 128 mTabPadding = (int) res.getDimension(R.dimen.tab_padding); 129 int maxTabWidth = (int) res.getDimension(R.dimen.max_tab_width); 130 // shader initialization 131 mShaderBuffer = Bitmap.createBitmap(maxTabWidth, mTabHeight, 132 Bitmap.Config.ARGB_8888); 133 mShaderCanvas = new Canvas(mShaderBuffer); 134 mShaderPaint = new Paint(); 135 BitmapShader shader = new BitmapShader(mShaderBuffer, 136 Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 137 mShaderPaint.setShader(shader); 138 mShaderPaint.setStyle(Paint.Style.FILL); 139 mShaderPaint.setAntiAlias(true); 140 } 141 142 void updateTabs(List<Tab> tabs) { 143 mTabs.clearTabs(); 144 mTabMap.clear(); 145 for (Tab tab : tabs) { 146 TabViewData data = buildTab(tab); 147 TabView tv = buildView(data); 148 } 149 mTabs.setSelectedTab(mTabControl.getCurrentIndex()); 150 } 151 152 @Override 153 protected void onMeasure(int hspec, int vspec) { 154 super.onMeasure(hspec, vspec); 155 int w = getMeasuredWidth(); 156 // adjust for new tab overlap 157 w -= mTabOverlap; 158 setMeasuredDimension(w, getMeasuredHeight()); 159 } 160 161 @Override 162 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 163 // use paddingLeft and paddingTop 164 int pl = getPaddingLeft(); 165 int pt = getPaddingTop(); 166 if (mButtonWidth == -1) { 167 mButtonWidth = mNewTab.getMeasuredWidth() - mTabOverlap; 168 } 169 int sw = mTabs.getMeasuredWidth(); 170 int w = right - left - pl; 171 if (w-sw < mButtonWidth) { 172 sw = w - mButtonWidth; 173 } 174 mTabs.layout(pl, pt, pl + sw, bottom - top); 175 // adjust for overlap 176 mNewTab.layout(pl + sw - mTabOverlap, pt, 177 pl + sw + mButtonWidth - mTabOverlap, bottom - top); 178 } 179 180 public void onClick(View view) { 181 mUi.hideComboView(); 182 if (mNewTab == view) { 183 mUiController.openTabToHomePage(); 184 } else if (mTabs.getSelectedTab() == view) { 185 if (mUi.isFakeTitleBarShowing() && !isLoading()) { 186 mUi.hideFakeTitleBar(); 187 } else { 188 showUrlBar(); 189 } 190 } else { 191 int ix = mTabs.getChildIndex(view); 192 if (ix >= 0) { 193 mTabs.setSelectedTab(ix); 194 mUiController.switchToTab(ix); 195 } 196 } 197 } 198 199 private void showUrlBar() { 200 mUi.stopWebViewScrolling(); 201 mUi.showFakeTitleBar(); 202 mUserRequestedUrlbar = true; 203 } 204 205 private void showTitleBarIndicator(boolean show) { 206 Tab tab = mTabControl.getCurrentTab(); 207 if (tab != null) { 208 TabViewData tvd = mTabMap.get(tab); 209 if (tvd != null) { 210 tvd.mTabView.showIndicator(show); 211 } 212 } 213 } 214 215 // callback after fake titlebar is shown 216 void onShowTitleBar() { 217 showTitleBarIndicator(false); 218 } 219 220 // callback after fake titlebar is hidden 221 void onHideTitleBar() { 222 showTitleBarIndicator(mVisibleTitleHeight == 0); 223 Tab tab = mTabControl.getCurrentTab(); 224 tab.getWebView().requestFocus(); 225 mUserRequestedUrlbar = false; 226 } 227 228 // webview scroll listener 229 230 @Override 231 public void onScroll(int visibleTitleHeight) { 232 // isLoading is using the current tab, which initially might not be set yet 233 if (mTabControl.getCurrentTab() != null) { 234 if ((mVisibleTitleHeight != 0) && (visibleTitleHeight == 0) 235 && !isLoading()) { 236 if (mUserRequestedUrlbar) { 237 mUi.hideFakeTitleBar(); 238 } else { 239 showTitleBarIndicator(true); 240 } 241 } else if ((mVisibleTitleHeight == 0) && (visibleTitleHeight != 0) 242 && !isLoading()) { 243 showTitleBarIndicator(false); 244 } 245 } 246 mVisibleTitleHeight = visibleTitleHeight; 247 } 248 249 @Override 250 public void createContextMenu(ContextMenu menu) { 251 MenuInflater inflater = mActivity.getMenuInflater(); 252 inflater.inflate(R.menu.title_context, menu); 253 mActivity.onCreateContextMenu(menu, this, null); 254 } 255 256 private TabViewData buildTab(Tab tab) { 257 TabViewData data = new TabViewData(tab); 258 mTabMap.put(tab, data); 259 return data; 260 } 261 262 private TabView buildView(final TabViewData data) { 263 TabView tv = new TabView(mActivity, data); 264 tv.setTag(data); 265 tv.setOnClickListener(this); 266 mTabs.addTab(tv); 267 return tv; 268 } 269 270 @Override 271 protected int getChildDrawingOrder(int count, int i) { 272 // reverse 273 return count - 1 - i; 274 } 275 276 /** 277 * View used in the tab bar 278 */ 279 class TabView extends LinearLayout implements OnClickListener { 280 281 TabViewData mTabData; 282 View mTabContent; 283 TextView mTitle; 284 View mIndicator; 285 View mIncognito; 286 ImageView mIconView; 287 ImageView mLock; 288 ImageView mClose; 289 boolean mSelected; 290 boolean mInLoad; 291 Path mPath; 292 int[] mWindowPos; 293 294 /** 295 * @param context 296 */ 297 public TabView(Context context, TabViewData tab) { 298 super(context); 299 setWillNotDraw(false); 300 mPath = new Path(); 301 mWindowPos = new int[2]; 302 mTabData = tab; 303 setGravity(Gravity.CENTER_VERTICAL); 304 setOrientation(LinearLayout.HORIZONTAL); 305 setPadding(mTabPadding, 0, 0, 0); 306 LayoutInflater inflater = LayoutInflater.from(getContext()); 307 mTabContent = inflater.inflate(R.layout.tab_title, this, true); 308 mTitle = (TextView) mTabContent.findViewById(R.id.title); 309 mIconView = (ImageView) mTabContent.findViewById(R.id.favicon); 310 mLock = (ImageView) mTabContent.findViewById(R.id.lock); 311 mClose = (ImageView) mTabContent.findViewById(R.id.close); 312 mClose.setOnClickListener(this); 313 mIncognito = mTabContent.findViewById(R.id.incognito); 314 mIndicator = mTabContent.findViewById(R.id.chevron); 315 mSelected = false; 316 mInLoad = false; 317 // update the status 318 updateFromData(); 319 } 320 321 void showIndicator(boolean show) { 322 if (mSelected) { 323 mIndicator.setVisibility(show ? View.VISIBLE : View.GONE); 324 } else { 325 mIndicator.setVisibility(View.GONE); 326 } 327 } 328 329 @Override 330 public void onClick(View v) { 331 if (v == mClose) { 332 closeTab(); 333 } 334 } 335 336 private void updateFromData() { 337 mTabData.mTabView = this; 338 if (mTabData.mUrl != null) { 339 setDisplayTitle(mTabData.mUrl); 340 } 341 if (mTabData.mTitle != null) { 342 setDisplayTitle(mTabData.mTitle); 343 } 344 setProgress(mTabData.mProgress); 345 if (mTabData.mIcon != null) { 346 setFavicon(mTabData.mIcon); 347 } 348 if (mTabData.mLock != null) { 349 setLock(mTabData.mLock); 350 } 351 if (mTabData.mTab != null) { 352 mIncognito.setVisibility( 353 mTabData.mTab.isPrivateBrowsingEnabled() ? 354 View.VISIBLE : View.GONE); 355 } 356 } 357 358 @Override 359 public void setActivated(boolean selected) { 360 mSelected = selected; 361 mClose.setVisibility(mSelected ? View.VISIBLE : View.GONE); 362 mIndicator.setVisibility(View.GONE); 363 mTitle.setTextAppearance(mActivity, mSelected ? 364 R.style.TabTitleSelected : R.style.TabTitleUnselected); 365 setHorizontalFadingEdgeEnabled(!mSelected); 366 super.setActivated(selected); 367 LayoutParams lp = (LinearLayout.LayoutParams) getLayoutParams(); 368 lp.width = selected ? mTabWidthSelected : mTabWidthUnselected; 369 lp.height = LayoutParams.MATCH_PARENT; 370 setLayoutParams(lp); 371 } 372 373 void setDisplayTitle(String title) { 374 mTitle.setText(title); 375 } 376 377 void setFavicon(Drawable d) { 378 mIconView.setImageDrawable(d); 379 } 380 381 void setLock(Drawable d) { 382 if (null == d) { 383 mLock.setVisibility(View.GONE); 384 } else { 385 mLock.setImageDrawable(d); 386 mLock.setVisibility(View.VISIBLE); 387 } 388 } 389 390 void setProgress(int newProgress) { 391 if (newProgress >= PROGRESS_MAX) { 392 mInLoad = false; 393 } else { 394 if (!mInLoad && getWindowToken() != null) { 395 mInLoad = true; 396 } 397 } 398 } 399 400 private void closeTab() { 401 if (mTabData.mTab == mTabControl.getCurrentTab()) { 402 mUiController.closeCurrentTab(); 403 } else { 404 mUiController.closeTab(mTabData.mTab); 405 } 406 } 407 408 @Override 409 protected void onLayout(boolean changed, int l, int t, int r, int b) { 410 super.onLayout(changed, l, t, r, b); 411 setTabPath(mPath, 0, 0, r - l, b - t); 412 } 413 414 @Override 415 protected void dispatchDraw(Canvas canvas) { 416 int state = canvas.save(); 417 int[] pos = new int[2]; 418 getLocationInWindow(mWindowPos); 419 Drawable drawable = mSelected ? mActiveDrawable : mInactiveDrawable; 420 drawable.setBounds(0, 0, mUi.getTitleBarWidth(), getHeight()); 421 drawClipped(canvas, drawable, mPath, mWindowPos[0]); 422 canvas.restoreToCount(state); 423 super.dispatchDraw(canvas); 424 } 425 426 private void drawClipped(Canvas canvas, Drawable drawable, 427 Path clipPath, int left) { 428 mShaderCanvas.drawColor(Color.TRANSPARENT); 429 mShaderCanvas.translate(-left, 0); 430 drawable.draw(mShaderCanvas); 431 canvas.drawPath(clipPath, mShaderPaint); 432 mShaderCanvas.translate(left, 0); 433 } 434 435 private void setTabPath(Path path, int l, int t, int r, int b) { 436 path.reset(); 437 path.moveTo(l, b); 438 path.lineTo(l, t); 439 path.lineTo(r - mTabSliceWidth, t); 440 path.lineTo(r, b); 441 path.close(); 442 } 443 444 } 445 446 /** 447 * Store tab state within the title bar 448 */ 449 class TabViewData { 450 451 Tab mTab; 452 TabView mTabView; 453 int mProgress; 454 Drawable mIcon; 455 Drawable mLock; 456 String mTitle; 457 String mUrl; 458 459 TabViewData(Tab tab) { 460 mTab = tab; 461 WebView web = tab.getWebView(); 462 if (web != null) { 463 setUrlAndTitle(web.getUrl(), web.getTitle()); 464 } 465 } 466 467 void setUrlAndTitle(String url, String title) { 468 mUrl = url; 469 mTitle = title; 470 if (mTabView != null) { 471 if (title != null) { 472 mTabView.setDisplayTitle(title); 473 } else if (url != null) { 474 mTabView.setDisplayTitle(UrlUtils.stripUrl(url)); 475 } 476 } 477 } 478 479 void setProgress(int newProgress) { 480 mProgress = newProgress; 481 if (mTabView != null) { 482 mTabView.setProgress(mProgress); 483 } 484 } 485 486 void setFavicon(Bitmap icon) { 487 Drawable[] array = new Drawable[3]; 488 array[0] = new PaintDrawable(Color.BLACK); 489 array[1] = new PaintDrawable(Color.WHITE); 490 if (icon == null) { 491 array[2] = mGenericFavicon; 492 } else { 493 array[2] = new BitmapDrawable(icon); 494 } 495 LayerDrawable d = new LayerDrawable(array); 496 d.setLayerInset(1, 1, 1, 1, 1); 497 d.setLayerInset(2, 2, 2, 2, 2); 498 mIcon = d; 499 if (mTabView != null) { 500 mTabView.setFavicon(mIcon); 501 } 502 } 503 504 } 505 506 // TabChangeListener implementation 507 508 public void onSetActiveTab(Tab tab) { 509 mTabs.setSelectedTab(mTabControl.getTabIndex(tab)); 510 TabViewData tvd = mTabMap.get(tab); 511 if (tvd != null) { 512 tvd.setProgress(tvd.mProgress); 513 // update the scroll state 514 WebView webview = tab.getWebView(); 515 if (webview != null) { 516 int h = webview.getVisibleTitleHeight(); 517 mVisibleTitleHeight = h -1; 518 onScroll(h); 519 } 520 } 521 } 522 523 public void onFavicon(Tab tab, Bitmap favicon) { 524 TabViewData tvd = mTabMap.get(tab); 525 if (tvd != null) { 526 tvd.setFavicon(favicon); 527 } 528 } 529 530 public void onNewTab(Tab tab) { 531 TabViewData tvd = buildTab(tab); 532 buildView(tvd); 533 } 534 535 public void onProgress(Tab tab, int progress) { 536 TabViewData tvd = mTabMap.get(tab); 537 if (tvd != null) { 538 tvd.setProgress(progress); 539 } 540 } 541 542 public void onRemoveTab(Tab tab) { 543 TabViewData tvd = mTabMap.get(tab); 544 if (tvd != null) { 545 TabView tv = tvd.mTabView; 546 if (tv != null) { 547 mTabs.removeTab(tv); 548 } 549 } 550 mTabMap.remove(tab); 551 } 552 553 public void onUrlAndTitle(Tab tab, String url, String title) { 554 mHasReceivedTitle = true; 555 TabViewData tvd = mTabMap.get(tab); 556 if (tvd != null) { 557 tvd.setUrlAndTitle(url, title); 558 } 559 } 560 561 public void onPageFinished(Tab tab) { 562 if (!mHasReceivedTitle) { 563 TabViewData tvd = mTabMap.get(tab); 564 if (tvd != null) { 565 tvd.setUrlAndTitle(tvd.mUrl, null); 566 } 567 } 568 } 569 570 public void onPageStarted(Tab tab, String url, Bitmap favicon) { 571 mHasReceivedTitle = false; 572 TabViewData tvd = mTabMap.get(tab); 573 if (tvd != null) { 574 tvd.setFavicon(favicon); 575 tvd.setUrlAndTitle(url, mLoadingText); 576 } 577 } 578 579 private boolean isLoading() { 580 TabViewData tvd = mTabMap.get(mTabControl.getCurrentTab()); 581 if ((tvd != null) && (tvd.mTabView != null)) { 582 return tvd.mTabView.mInLoad; 583 } else { 584 return false; 585 } 586 } 587 588} 589