diff options
Diffstat (limited to 'mobile/android/base/java/org/mozilla/gecko/tabs/TabStripItemView.java')
-rw-r--r-- | mobile/android/base/java/org/mozilla/gecko/tabs/TabStripItemView.java | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/mobile/android/base/java/org/mozilla/gecko/tabs/TabStripItemView.java b/mobile/android/base/java/org/mozilla/gecko/tabs/TabStripItemView.java new file mode 100644 index 000000000..27eaed125 --- /dev/null +++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabStripItemView.java @@ -0,0 +1,254 @@ +/* -*- 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; + +import org.mozilla.gecko.AboutPages; +import org.mozilla.gecko.GeckoAppShell; +import org.mozilla.gecko.R; +import org.mozilla.gecko.Tab; +import org.mozilla.gecko.Tabs; +import org.mozilla.gecko.widget.ResizablePathDrawable; +import org.mozilla.gecko.widget.ResizablePathDrawable.NonScaledPathShape; +import org.mozilla.gecko.widget.themed.ThemedImageButton; +import org.mozilla.gecko.widget.themed.ThemedLinearLayout; +import org.mozilla.gecko.widget.themed.ThemedTextView; + +import org.json.JSONException; +import org.json.JSONObject; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Path; +import android.graphics.Region; +import android.util.AttributeSet; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.Checkable; +import android.widget.ImageView; + +public class TabStripItemView extends ThemedLinearLayout + implements Checkable { + private static final String LOGTAG = "GeckoTabStripItem"; + + private static final int[] STATE_CHECKED = { + android.R.attr.state_checked + }; + + private int id = -1; + private boolean checked; + + private final ImageView faviconView; + private final ThemedTextView titleView; + private final ThemedImageButton closeView; + + private final ResizablePathDrawable backgroundDrawable; + private final Region tabRegion; + private final Region tabClipRegion; + private boolean tabRegionNeedsUpdate; + + private final int faviconSize; + private Bitmap lastFavicon; + + public TabStripItemView(Context context) { + this(context, null); + } + + public TabStripItemView(Context context, AttributeSet attrs) { + super(context, attrs); + setOrientation(HORIZONTAL); + + tabRegion = new Region(); + tabClipRegion = new Region(); + + final Resources res = context.getResources(); + + final ColorStateList tabColors = + res.getColorStateList(R.color.tab_strip_item_bg); + backgroundDrawable = new ResizablePathDrawable(new TabCurveShape(), tabColors); + setBackgroundDrawable(backgroundDrawable); + + faviconSize = res.getDimensionPixelSize(R.dimen.browser_toolbar_favicon_size); + + 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); + } + }); + + faviconView = (ImageView) findViewById(R.id.favicon); + 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); + + // Queue a tab region update in the next draw() call. We don't + // update it immediately here because we need the new path from + // the background drawable to be updated first. + tabRegionNeedsUpdate = true; + } + + @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 void draw(Canvas canvas) { + super.draw(canvas); + + if (tabRegionNeedsUpdate) { + final Path path = backgroundDrawable.getPath(); + tabClipRegion.set(0, 0, getWidth(), getHeight()); + tabRegion.setPath(path, tabClipRegion); + tabRegionNeedsUpdate = false; + } + } + + @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(); + if (parent != null) { + parent.invalidate(); + } + } + + void updateFromTab(Tab tab) { + if (tab == null) { + return; + } + + id = tab.getId(); + + updateTitle(tab); + updateFavicon(tab.getFavicon()); + setPrivateMode(tab.isPrivate()); + } + + private void updateTitle(Tab tab) { + final String title; + + // Avoid flickering the about:home URL on every load given how often + // this page is used in the UI. + if (AboutPages.isAboutHome(tab.getURL())) { + titleView.setText(R.string.home_title); + } else { + titleView.setText(tab.getDisplayTitle()); + } + + // TODO: Set content description to indicate audio is playing. + if (tab.isAudioPlaying()) { + titleView.setCompoundDrawablesWithIntrinsicBounds(R.drawable.tab_audio_playing, 0, 0, 0); + } else { + titleView.setCompoundDrawables(null, null, null, null); + } + } + + private void updateFavicon(final Bitmap favicon) { + if (favicon == null) { + lastFavicon = null; + faviconView.setImageResource(R.drawable.toolbar_favicon_default); + return; + } + 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); + } + + private static class TabCurveShape extends NonScaledPathShape { + @Override + protected void onResize(float width, float height) { + final Path path = getPath(); + + path.reset(); + + final float curveWidth = TabCurve.getWidthForHeight(height); + + path.moveTo(0, height); + TabCurve.drawFromBottom(path, 0, height, TabCurve.Direction.RIGHT); + path.lineTo(width - curveWidth, 0); + + TabCurve.drawFromTop(path, width - curveWidth, height, TabCurve.Direction.RIGHT); + path.lineTo(0, height); + } + } +} |