blob: d6c02f9f5f40f97b5999ce121b5542a0a492bd0d [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.browser;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* tabbed title bar for xlarge screen browser
*/
public class TabBar extends LinearLayout implements OnClickListener {
private static final int PROGRESS_MAX = 100;
private Activity mActivity;
private UiController mUiController;
private TabControl mTabControl;
private XLargeUi mUi;
private int mTabWidth;
private TabScrollView mTabs;
private ImageButton mNewTab;
private int mButtonWidth;
private Map<Tab, TabView> mTabMap;
private int mCurrentTextureWidth = 0;
private int mCurrentTextureHeight = 0;
private Drawable mActiveDrawable;
private Drawable mInactiveDrawable;
private final Paint mActiveShaderPaint = new Paint();
private final Paint mInactiveShaderPaint = new Paint();
private final Paint mFocusPaint = new Paint();
private final Matrix mActiveMatrix = new Matrix();
private final Matrix mInactiveMatrix = new Matrix();
private BitmapShader mActiveShader;
private BitmapShader mInactiveShader;
private int mTabOverlap;
private int mAddTabOverlap;
private int mTabSliceWidth;
private boolean mUseQuickControls;
public TabBar(Activity activity, UiController controller, XLargeUi ui) {
super(activity);
mActivity = activity;
mUiController = controller;
mTabControl = mUiController.getTabControl();
mUi = ui;
Resources res = activity.getResources();
mTabWidth = (int) res.getDimension(R.dimen.tab_width);
mActiveDrawable = res.getDrawable(R.drawable.bg_urlbar);
mInactiveDrawable = res.getDrawable(R.drawable.browsertab_inactive);
mTabMap = new HashMap<Tab, TabView>();
LayoutInflater factory = LayoutInflater.from(activity);
factory.inflate(R.layout.tab_bar, this);
setPadding(0, (int) res.getDimension(R.dimen.tab_padding_top), 0, 0);
mTabs = (TabScrollView) findViewById(R.id.tabs);
mNewTab = (ImageButton) findViewById(R.id.newtab);
mNewTab.setOnClickListener(this);
updateTabs(mUiController.getTabs());
mButtonWidth = -1;
// tab dimensions
mTabOverlap = (int) res.getDimension(R.dimen.tab_overlap);
mAddTabOverlap = (int) res.getDimension(R.dimen.tab_addoverlap);
mTabSliceWidth = (int) res.getDimension(R.dimen.tab_slice);
mActiveShaderPaint.setStyle(Paint.Style.FILL);
mActiveShaderPaint.setAntiAlias(true);
mInactiveShaderPaint.setStyle(Paint.Style.FILL);
mInactiveShaderPaint.setAntiAlias(true);
mFocusPaint.setStyle(Paint.Style.STROKE);
mFocusPaint.setStrokeWidth(res.getDimension(R.dimen.tab_focus_stroke));
mFocusPaint.setAntiAlias(true);
mFocusPaint.setColor(res.getColor(R.color.tabFocusHighlight));
}
@Override
public void onConfigurationChanged(Configuration config) {
super.onConfigurationChanged(config);
Resources res = mActivity.getResources();
mTabWidth = (int) res.getDimension(R.dimen.tab_width);
// force update of tab bar
mTabs.updateLayout();
}
void setUseQuickControls(boolean useQuickControls) {
mUseQuickControls = useQuickControls;
mNewTab.setVisibility(mUseQuickControls ? View.GONE
: View.VISIBLE);
}
int getTabCount() {
return mTabMap.size();
}
void updateTabs(List<Tab> tabs) {
mTabs.clearTabs();
mTabMap.clear();
for (Tab tab : tabs) {
TabView tv = buildTabView(tab);
mTabs.addTab(tv);
}
mTabs.setSelectedTab(mTabControl.getCurrentPosition());
}
@Override
protected void onMeasure(int hspec, int vspec) {
super.onMeasure(hspec, vspec);
int w = getMeasuredWidth();
// adjust for new tab overlap
if (!mUseQuickControls) {
w -= mAddTabOverlap;
}
setMeasuredDimension(w, getMeasuredHeight());
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// use paddingLeft and paddingTop
int pl = getPaddingLeft();
int pt = getPaddingTop();
int sw = mTabs.getMeasuredWidth();
int w = right - left - pl;
if (mUseQuickControls) {
mButtonWidth = 0;
} else {
mButtonWidth = mNewTab.getMeasuredWidth() - mAddTabOverlap;
if (w-sw < mButtonWidth) {
sw = w - mButtonWidth;
}
}
mTabs.layout(pl, pt, pl + sw, bottom - top);
// adjust for overlap
if (!mUseQuickControls) {
mNewTab.layout(pl + sw - mAddTabOverlap, pt,
pl + sw + mButtonWidth - mAddTabOverlap, bottom - top);
}
}
public void onClick(View view) {
if (mNewTab == view) {
mUiController.openTabToHomePage();
} else if (mTabs.getSelectedTab() == view) {
if (mUseQuickControls) {
if (mUi.isTitleBarShowing() && !isLoading()) {
mUi.stopEditingUrl();
mUi.hideTitleBar();
} else {
mUi.stopWebViewScrolling();
mUi.editUrl(false, false);
}
} else if (mUi.isTitleBarShowing() && !isLoading()) {
mUi.stopEditingUrl();
mUi.hideTitleBar();
} else {
showUrlBar();
}
} else if (view instanceof TabView) {
final Tab tab = ((TabView) view).mTab;
int ix = mTabs.getChildIndex(view);
if (ix >= 0) {
mTabs.setSelectedTab(ix);
mUiController.switchToTab(tab);
}
}
}
private void showUrlBar() {
mUi.stopWebViewScrolling();
mUi.showTitleBar();
}
private TabView buildTabView(Tab tab) {
TabView tabview = new TabView(mActivity, tab);
mTabMap.put(tab, tabview);
tabview.setOnClickListener(this);
return tabview;
}
private static Bitmap getDrawableAsBitmap(Drawable drawable, int width, int height) {
Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
drawable.setBounds(0, 0, width, height);
drawable.draw(c);
c.setBitmap(null);
return b;
}
/**
* View used in the tab bar
*/
class TabView extends LinearLayout implements OnClickListener {
Tab mTab;
View mTabContent;
TextView mTitle;
View mIncognito;
View mSnapshot;
ImageView mIconView;
ImageView mLock;
ImageView mClose;
boolean mSelected;
Path mPath;
Path mFocusPath;
int[] mWindowPos;
/**
* @param context
*/
public TabView(Context context, Tab tab) {
super(context);
setWillNotDraw(false);
mPath = new Path();
mFocusPath = new Path();
mWindowPos = new int[2];
mTab = tab;
setGravity(Gravity.CENTER_VERTICAL);
setOrientation(LinearLayout.HORIZONTAL);
setPadding(mTabOverlap, 0, mTabSliceWidth, 0);
LayoutInflater inflater = LayoutInflater.from(getContext());
mTabContent = inflater.inflate(R.layout.tab_title, this, true);
mTitle = (TextView) mTabContent.findViewById(R.id.title);
mIconView = (ImageView) mTabContent.findViewById(R.id.favicon);
mLock = (ImageView) mTabContent.findViewById(R.id.lock);
mClose = (ImageView) mTabContent.findViewById(R.id.close);
mClose.setOnClickListener(this);
mIncognito = mTabContent.findViewById(R.id.incognito);
mSnapshot = mTabContent.findViewById(R.id.snapshot);
mSelected = false;
// update the status
updateFromTab();
}
@Override
public void onClick(View v) {
if (v == mClose) {
closeTab();
}
}
private void updateFromTab() {
String displayTitle = mTab.getTitle();
if (displayTitle == null) {
displayTitle = mTab.getUrl();
}
setDisplayTitle(displayTitle);
if (mTab.getFavicon() != null) {
setFavicon(mUi.getFaviconDrawable(mTab.getFavicon()));
}
updateTabIcons();
}
private void updateTabIcons() {
mIncognito.setVisibility(
mTab.isPrivateBrowsingEnabled() ?
View.VISIBLE : View.GONE);
mSnapshot.setVisibility(mTab.isSnapshot()
? View.VISIBLE : View.GONE);
}
@Override
public void setActivated(boolean selected) {
mSelected = selected;
mClose.setVisibility(mSelected ? View.VISIBLE : View.GONE);
mIconView.setVisibility(mSelected ? View.GONE : View.VISIBLE);
mTitle.setTextAppearance(mActivity, mSelected ?
R.style.TabTitleSelected : R.style.TabTitleUnselected);
setHorizontalFadingEdgeEnabled(!mSelected);
super.setActivated(selected);
updateLayoutParams();
setFocusable(!selected);
postInvalidate();
}
public void updateLayoutParams() {
LayoutParams lp = (LinearLayout.LayoutParams) getLayoutParams();
lp.width = mTabWidth;
lp.height = LayoutParams.MATCH_PARENT;
setLayoutParams(lp);
}
void setDisplayTitle(String title) {
mTitle.setText(title);
}
void setFavicon(Drawable d) {
mIconView.setImageDrawable(d);
}
void setLock(Drawable d) {
if (null == d) {
mLock.setVisibility(View.GONE);
} else {
mLock.setImageDrawable(d);
mLock.setVisibility(View.VISIBLE);
}
}
private void closeTab() {
if (mTab == mTabControl.getCurrentTab()) {
mUiController.closeCurrentTab();
} else {
mUiController.closeTab(mTab);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
setTabPath(mPath, 0, 0, r - l, b - t);
setFocusPath(mFocusPath, 0, 0, r - l, b - t);
}
@Override
protected void dispatchDraw(Canvas canvas) {
if (mCurrentTextureWidth != mUi.getContentWidth() ||
mCurrentTextureHeight != getHeight()) {
mCurrentTextureWidth = mUi.getContentWidth();
mCurrentTextureHeight = getHeight();
if (mCurrentTextureWidth > 0 && mCurrentTextureHeight > 0) {
Bitmap activeTexture = getDrawableAsBitmap(mActiveDrawable,
mCurrentTextureWidth, mCurrentTextureHeight);
Bitmap inactiveTexture = getDrawableAsBitmap(mInactiveDrawable,
mCurrentTextureWidth, mCurrentTextureHeight);
mActiveShader = new BitmapShader(activeTexture,
Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mActiveShaderPaint.setShader(mActiveShader);
mInactiveShader = new BitmapShader(inactiveTexture,
Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mInactiveShaderPaint.setShader(mInactiveShader);
}
}
// add some monkey protection
if ((mActiveShader != null) && (mInactiveShader != null)) {
int state = canvas.save();
getLocationInWindow(mWindowPos);
Paint paint = mSelected ? mActiveShaderPaint : mInactiveShaderPaint;
drawClipped(canvas, paint, mPath, mWindowPos[0]);
canvas.restoreToCount(state);
}
super.dispatchDraw(canvas);
}
private void drawClipped(Canvas canvas, Paint paint, Path clipPath, int left) {
// TODO: We should change the matrix/shader only when needed
final Matrix matrix = mSelected ? mActiveMatrix : mInactiveMatrix;
matrix.setTranslate(-left, 0.0f);
final Shader shader = mSelected ? mActiveShader : mInactiveShader;
shader.setLocalMatrix(matrix);
paint.setShader(shader);
canvas.drawPath(clipPath, paint);
if (isFocused()) {
canvas.drawPath(mFocusPath, mFocusPaint);
}
}
private void setTabPath(Path path, int l, int t, int r, int b) {
path.reset();
path.moveTo(l, b);
path.lineTo(l, t);
path.lineTo(r - mTabSliceWidth, t);
path.lineTo(r, b);
path.close();
}
private void setFocusPath(Path path, int l, int t, int r, int b) {
path.reset();
path.moveTo(l, b);
path.lineTo(l, t);
path.lineTo(r - mTabSliceWidth, t);
path.lineTo(r, b);
}
}
private void animateTabOut(final Tab tab, final TabView tv) {
ObjectAnimator scalex = ObjectAnimator.ofFloat(tv, "scaleX", 1.0f, 0.0f);
ObjectAnimator scaley = ObjectAnimator.ofFloat(tv, "scaleY", 1.0f, 0.0f);
ObjectAnimator alpha = ObjectAnimator.ofFloat(tv, "alpha", 1.0f, 0.0f);
AnimatorSet animator = new AnimatorSet();
animator.playTogether(scalex, scaley, alpha);
animator.setDuration(150);
animator.addListener(new AnimatorListener() {
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
mTabs.removeTab(tv);
mTabMap.remove(tab);
mUi.onRemoveTabCompleted(tab);
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationStart(Animator animation) {
}
});
animator.start();
}
private void animateTabIn(final Tab tab, final TabView tv) {
ObjectAnimator scalex = ObjectAnimator.ofFloat(tv, "scaleX", 0.0f, 1.0f);
scalex.setDuration(150);
scalex.addListener(new AnimatorListener() {
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
mUi.onAddTabCompleted(tab);
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationStart(Animator animation) {
mTabs.addTab(tv);
}
});
scalex.start();
}
// TabChangeListener implementation
public void onSetActiveTab(Tab tab) {
mTabs.setSelectedTab(mTabControl.getTabPosition(tab));
}
public void onFavicon(Tab tab, Bitmap favicon) {
TabView tv = mTabMap.get(tab);
if (tv != null) {
tv.setFavicon(mUi.getFaviconDrawable(favicon));
}
}
public void onNewTab(Tab tab) {
TabView tv = buildTabView(tab);
animateTabIn(tab, tv);
}
public void onRemoveTab(Tab tab) {
TabView tv = mTabMap.get(tab);
if (tv != null) {
animateTabOut(tab, tv);
} else {
mTabMap.remove(tab);
}
}
public void onUrlAndTitle(Tab tab, String url, String title) {
TabView tv = mTabMap.get(tab);
if (tv != null) {
if (title != null) {
tv.setDisplayTitle(title);
} else if (url != null) {
tv.setDisplayTitle(UrlUtils.stripUrl(url));
}
tv.updateTabIcons();
}
}
private boolean isLoading() {
Tab tab = mTabControl.getCurrentTab();
if (tab != null) {
return tab.inPageLoad();
} else {
return false;
}
}
}