2014-09-10 13:28:58 -07:00
|
|
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
package org.mozilla.gecko.tabs;
|
|
|
|
|
2014-09-22 14:44:49 -07:00
|
|
|
import org.mozilla.gecko.R;
|
|
|
|
import org.mozilla.gecko.Tab;
|
|
|
|
import org.mozilla.gecko.Tabs;
|
|
|
|
import org.mozilla.gecko.widget.ThemedImageButton;
|
|
|
|
import org.mozilla.gecko.widget.ThemedLinearLayout;
|
|
|
|
import org.mozilla.gecko.widget.ThemedTextView;
|
|
|
|
|
2014-09-10 13:28:58 -07:00
|
|
|
import android.content.Context;
|
2014-09-22 14:44:49 -07:00
|
|
|
import android.graphics.Bitmap;
|
2014-09-10 13:28:58 -07:00
|
|
|
import android.graphics.Canvas;
|
|
|
|
import android.graphics.Paint;
|
|
|
|
import android.graphics.Path;
|
|
|
|
import android.graphics.PorterDuff.Mode;
|
|
|
|
import android.graphics.PorterDuffXfermode;
|
|
|
|
import android.graphics.Region;
|
|
|
|
import android.util.AttributeSet;
|
|
|
|
import android.view.LayoutInflater;
|
|
|
|
import android.view.MotionEvent;
|
|
|
|
import android.view.View;
|
|
|
|
import android.widget.Checkable;
|
2014-09-22 14:44:49 -07:00
|
|
|
import android.widget.ImageView;
|
2014-09-10 13:28:58 -07:00
|
|
|
|
|
|
|
public class TabStripItemView extends ThemedLinearLayout
|
|
|
|
implements Checkable {
|
2014-09-22 14:44:49 -07:00
|
|
|
@SuppressWarnings("unused")
|
2014-09-10 13:28:58 -07:00
|
|
|
private static final String LOGTAG = "GeckoTabStripItem";
|
|
|
|
|
|
|
|
private static final int[] STATE_CHECKED = {
|
|
|
|
android.R.attr.state_checked
|
|
|
|
};
|
|
|
|
|
|
|
|
private int id = -1;
|
|
|
|
private boolean checked;
|
|
|
|
|
2014-09-22 14:44:49 -07:00
|
|
|
private final ImageView faviconView;
|
2014-09-10 13:28:58 -07:00
|
|
|
private final ThemedTextView titleView;
|
|
|
|
private final ThemedImageButton closeView;
|
|
|
|
|
|
|
|
private final Paint tabPaint;
|
|
|
|
private final Path tabShape;
|
|
|
|
private final Region tabRegion;
|
|
|
|
private final Region tabClipRegion;
|
|
|
|
|
2014-09-22 14:44:49 -07:00
|
|
|
private final int faviconSize;
|
|
|
|
private Bitmap lastFavicon;
|
|
|
|
|
2014-09-10 13:28:58 -07:00
|
|
|
public TabStripItemView(Context context) {
|
|
|
|
this(context, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
public TabStripItemView(Context context, AttributeSet attrs) {
|
|
|
|
super(context, attrs);
|
|
|
|
setOrientation(HORIZONTAL);
|
|
|
|
|
|
|
|
tabShape = new Path();
|
|
|
|
tabRegion = new Region();
|
|
|
|
tabClipRegion = new Region();
|
|
|
|
|
|
|
|
tabPaint = new Paint();
|
|
|
|
tabPaint.setAntiAlias(true);
|
|
|
|
tabPaint.setColor(0xFFFF0000);
|
|
|
|
tabPaint.setStrokeWidth(0.0f);
|
|
|
|
tabPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
|
|
|
|
|
2014-09-22 14:44:49 -07:00
|
|
|
faviconSize = getResources().getDimensionPixelSize(R.dimen.tab_strip_favicon_size);
|
|
|
|
|
2014-09-10 13:28:58 -07:00
|
|
|
LayoutInflater.from(context).inflate(R.layout.tab_strip_item_view, this);
|
|
|
|
setOnClickListener(new View.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
|
|
|
if (id < 0) {
|
|
|
|
throw new IllegalStateException("Invalid tab id:" + id);
|
|
|
|
}
|
|
|
|
|
|
|
|
Tabs.getInstance().selectTab(id);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2014-09-22 14:44:49 -07:00
|
|
|
faviconView = (ImageView) findViewById(R.id.favicon);
|
2014-09-10 13:28:58 -07:00
|
|
|
titleView = (ThemedTextView) findViewById(R.id.title);
|
|
|
|
|
|
|
|
closeView = (ThemedImageButton) findViewById(R.id.close);
|
|
|
|
closeView.setOnClickListener(new View.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
|
|
|
if (id < 0) {
|
|
|
|
throw new IllegalStateException("Invalid tab id:" + id);
|
|
|
|
}
|
|
|
|
|
|
|
|
final Tabs tabs = Tabs.getInstance();
|
|
|
|
tabs.closeTab(tabs.getTab(id), true);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
|
|
|
|
super.onSizeChanged(width, height, oldWidth, oldHeight);
|
|
|
|
|
|
|
|
tabShape.reset();
|
|
|
|
|
|
|
|
final int curveWidth = TabCurve.getWidthForHeight(height);
|
|
|
|
|
|
|
|
tabShape.moveTo(0, height);
|
|
|
|
TabCurve.drawFromBottom(tabShape, 0, height, TabCurve.Direction.RIGHT);
|
|
|
|
tabShape.lineTo(width - curveWidth, 0);
|
|
|
|
|
|
|
|
TabCurve.drawFromTop(tabShape, width - curveWidth, height, TabCurve.Direction.RIGHT);
|
|
|
|
tabShape.lineTo(0, height);
|
|
|
|
|
|
|
|
tabClipRegion.set(0, 0, width, height);
|
|
|
|
tabRegion.setPath(tabShape, tabClipRegion);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void draw(Canvas canvas) {
|
|
|
|
final int saveCount = canvas.saveLayer(0, 0,
|
|
|
|
getWidth(), getHeight(), null,
|
|
|
|
Canvas.MATRIX_SAVE_FLAG |
|
|
|
|
Canvas.CLIP_SAVE_FLAG |
|
|
|
|
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
|
|
|
|
Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
|
|
|
|
Canvas.CLIP_TO_LAYER_SAVE_FLAG);
|
|
|
|
|
|
|
|
super.draw(canvas);
|
|
|
|
|
|
|
|
canvas.drawPath(tabShape, tabPaint);
|
|
|
|
|
|
|
|
canvas.restoreToCount(saveCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onTouchEvent(MotionEvent event) {
|
|
|
|
final int action = event.getActionMasked();
|
|
|
|
final int x = (int) event.getX();
|
|
|
|
final int y = (int) event.getY();
|
|
|
|
|
|
|
|
// Let motion events through if they're off the tab shape bounds.
|
|
|
|
if (action == MotionEvent.ACTION_DOWN && !tabRegion.contains(x, y)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return super.onTouchEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int[] onCreateDrawableState(int extraSpace) {
|
|
|
|
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
|
|
|
|
|
|
|
|
if (checked) {
|
|
|
|
mergeDrawableStates(drawableState, STATE_CHECKED);
|
|
|
|
}
|
|
|
|
|
|
|
|
return drawableState;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isChecked() {
|
|
|
|
return checked;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setChecked(boolean checked) {
|
|
|
|
if (this.checked == checked) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.checked = checked;
|
|
|
|
refreshDrawableState();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void toggle() {
|
|
|
|
setChecked(!checked);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setPressed(boolean pressed) {
|
|
|
|
super.setPressed(pressed);
|
|
|
|
|
|
|
|
// The surrounding tab strip dividers need to be hidden
|
|
|
|
// when a tab item enters pressed state.
|
|
|
|
View parent = (View) getParent();
|
|
|
|
parent.invalidate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void updateFromTab(Tab tab) {
|
|
|
|
if (tab == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
id = tab.getId();
|
2014-09-22 14:44:49 -07:00
|
|
|
updateFavicon(tab.getFavicon());
|
2014-09-10 13:28:58 -07:00
|
|
|
titleView.setText(tab.getDisplayTitle());
|
|
|
|
setPrivateMode(tab.isPrivate());
|
|
|
|
}
|
2014-09-22 14:44:49 -07:00
|
|
|
|
|
|
|
private void updateFavicon(final Bitmap favicon) {
|
|
|
|
if (favicon == null) {
|
|
|
|
lastFavicon = null;
|
2014-09-24 16:38:33 -07:00
|
|
|
faviconView.setImageResource(R.drawable.new_tablet_default_favicon);
|
2014-09-22 14:44:49 -07:00
|
|
|
return;
|
|
|
|
} else if (favicon == lastFavicon) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cache the original so we can debounce without scaling.
|
|
|
|
lastFavicon = favicon;
|
|
|
|
|
|
|
|
final Bitmap scaledFavicon =
|
|
|
|
Bitmap.createScaledBitmap(favicon, faviconSize, faviconSize, false);
|
|
|
|
faviconView.setImageBitmap(scaledFavicon);
|
|
|
|
}
|
2014-09-10 13:28:58 -07:00
|
|
|
}
|