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