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