Initial attempt to get the sliding title bar working, using a Gallery.
In addition the sliding title bar, I have now removed the TabPicker.
Now, when changing to a new tab, we just go straight there. Also
put back in Add bookmark in the top level menu. And fix the bug
where switching tabs was always bringing up the bookmarks.
diff --git a/src/com/android/browser/TitleBarSet.java b/src/com/android/browser/TitleBarSet.java
new file mode 100644
index 0000000..f9b5165
--- /dev/null
+++ b/src/com/android/browser/TitleBarSet.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2009 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.content.Context;
+import android.database.DataSetObserver;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.WebView;
+import android.widget.AdapterView;
+import android.widget.Gallery;
+import android.widget.SpinnerAdapter;
+
+import java.util.Vector;
+
+/**
+ * The TitleBarSet holds a TitleBar for each open "tab" in the browser.
+ */
+public class TitleBarSet extends Gallery
+ implements AdapterView.OnItemSelectedListener {
+ private Vector<TitleBar> mTitleBars;
+ private BrowserActivity mBrowserActivity;
+ private View mNewButton;
+ private int mCount;
+ private TitleAdapter mTitleAdapter;
+ private boolean mIgnoreSelectedListener;
+ private MotionEvent mLastTouchUp;
+
+ public TitleBarSet(Context context) {
+ this(context, null);
+ }
+
+ public TitleBarSet(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mTitleBars = new Vector<TitleBar>(TabControl.MAX_TABS);
+ mCount = 0;
+ // Now create the Plus button that goes on the right.
+ LayoutInflater factory = LayoutInflater.from(context);
+ mNewButton = factory.inflate(R.layout.new_window_button, null);
+ mTitleAdapter = new TitleAdapter();
+ setAdapter(mTitleAdapter);
+ setCallbackDuringFling(false);
+ setCallbackOnUnselectedItemClick(true);
+ setSpacing(0);
+ setOnItemSelectedListener(this);
+ }
+
+ /**
+ * Add a tab/titlebar to our set. Called when BrowserActivity adds a new
+ * Tab to its TabControl.
+ * @param view WebView associated with this tab. Used to determine whether
+ * updates are going to the correct place.
+ * @param selected Whether to set the new tab to be selected.
+ */
+ /* package */ void addTab(WebView view, boolean selected) {
+ if (TabControl.MAX_TABS == mCount) {
+ return;
+ }
+ int newSelection = mCount;
+ TitleBar titleBar = new TitleBar(getContext(), view);
+ mTitleBars.add(titleBar);
+ mCount++;
+ if (TabControl.MAX_TABS == mCount) {
+ mNewButton.setEnabled(false);
+ }
+ // Need to refresh our list
+ setAdapter(mTitleAdapter);
+ mIgnoreSelectedListener = true;
+ // No need to call onItemSelected, since the Tab in BrowserActivity has
+ // already been changed.
+ if (selected) {
+ setSelection(newSelection);
+ }
+ mIgnoreSelectedListener = false;
+ }
+
+ /**
+ * Convenience method to get a particular title bar.
+ */
+ private TitleBar getTitleBarAt(int position) {
+ if (position < 0 || position >= mCount) {
+ return null;
+ }
+ return (TitleBar) mTitleBars.elementAt(position);
+ }
+
+ /**
+ * Implementation for OnItemSelectedListener
+ */
+ public void onItemSelected(AdapterView<?> parent, View view, int position,
+ long id) {
+ if (mIgnoreSelectedListener || !(view instanceof TitleBar)) {
+ return;
+ }
+ mBrowserActivity.switchToTab(position);
+ // In case the WebView finished loading while this TitleBar was out of
+ // focus, make sure all its data is up to date
+ TitleBar titleBar = getTitleBarAt(position);
+ WebView webview = titleBar.getWebView();
+ if (webview == null) {
+ // FIXME: Possible that the tab needs to be restored.
+ return;
+ }
+ if (webview.getProgress() == 100) {
+ titleBar.setProgress(100);
+ titleBar.setTitleAndUrl(webview.getTitle(), webview.getUrl());
+ // FIXME: Pass in a bitmap, so we can always update the bitmap
+ // properly
+ //titleBar.setFavicon(webview.getFavicon());
+ }
+ }
+
+ /**
+ * Implementation for OnItemSelectedListener
+ */
+ public void onNothingSelected(AdapterView<?> parent) {
+ // do nothing
+ }
+
+ /**
+ * Override from GestureDetector.OnGestureListener. Store the MotionEvent
+ * so performItemClick can know how to handle the click.
+ */
+ public boolean onSingleTapUp(MotionEvent e) {
+ mLastTouchUp = e;
+ // super.onSingleTapUp will call performItemClick
+ boolean result = super.onSingleTapUp(e);
+ mLastTouchUp = null;
+ return result;
+ }
+
+ /**
+ * Override from View to ensure that the TitleBars get resized to match
+ * the new screen width
+ */
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ // Need to make sure getView gets called again
+ // FIXME: This didn't seem to work
+ setAdapter(mTitleAdapter);
+ }
+
+ /**
+ * Override from AdapterView. Using simple OnClickListeners overrides
+ * the GestureDetector.OnGestureListener, so we handle it here.
+ */
+ public boolean performItemClick(View view, int position, long id) {
+ if (!(view instanceof TitleBar)) {
+ // For new window button
+ return super.performItemClick(view, position, id);
+ }
+ // If we have no mLastTouchUp, this was not called from onSingleTapUp,
+ // so ignore it.
+ if (null == mLastTouchUp) {
+ return false;
+ }
+ TitleBar titleBar = (TitleBar) view;
+ // If the user clicks on a view which is not selected, the Gallery will
+ // take care of making it selected.
+ if (titleBar != getTitleBarAt(position)) {
+ return false;
+ }
+ if (titleBar.hitRightButton((int) mLastTouchUp.getX() - mScrollX,
+ (int) mLastTouchUp.getY() - mScrollY)) {
+ if (titleBar.isInLoad()) {
+ WebView webView = titleBar.getWebView();
+ if (null == webView) {
+ // FIXME: How did we get into this situation?
+ return false;
+ }
+ webView.stopLoading();
+ } else {
+ mBrowserActivity.closeCurrentWindow();
+ }
+ } else {
+ mBrowserActivity.onSearchRequested();
+ }
+ return true;
+ }
+
+ /**
+ * Remove the tab at the given position.
+ */
+ /* package */ void removeTab(int position) {
+ if (TabControl.MAX_TABS == mCount) {
+ mNewButton.setEnabled(true);
+ }
+ mTitleBars.remove(position);
+ mCount--;
+ // Need to refresh our list
+ setAdapter(mTitleAdapter);
+ }
+
+ /**
+ * Convenience method to get the currently selected title bar.
+ */
+ private TitleBar selectedTitleBar() {
+ return getTitleBarAt(getSelectedItemPosition());
+ }
+
+ /**
+ * Set the owning BrowserActivity. Necessary so that we can call methods
+ * on it.
+ */
+ /* package */ void setBrowserActivity(final BrowserActivity ba) {
+ mBrowserActivity = ba;
+ View.OnClickListener listener = new View.OnClickListener() {
+ public void onClick(View v) {
+ ba.openTabAndShow(BrowserActivity.EMPTY_URL_DATA, false, null);
+ }
+ };
+ mNewButton.findViewById(R.id.button).setOnClickListener(listener);
+ }
+
+ /**
+ * Change to the tab at the new position.
+ */
+ /* package */ void setCurrentTab(int position) {
+ mIgnoreSelectedListener = true;
+ setSelection(position);
+ mIgnoreSelectedListener = false;
+ }
+
+ /**
+ * Update the Favicon of the currently selected tab.
+ * @param d The new Drawable for the Favicon
+ * @param topWindow The WebView which posted the update. If it does not
+ * match the WebView of the currently selected tab, do
+ * nothing, since that tab is not being displayed.
+ */
+ /* package */ void setFavicon(Drawable d, WebView topWindow) {
+ TitleBar current = selectedTitleBar();
+ if (current != null && current.getWebView() == topWindow) {
+ current.setFavicon(d);
+ }
+ }
+
+ /**
+ * Update the lock icon of the currently selected tab.
+ * @param d The new Drawable for the lock icon
+ * @param topWindow The WebView which posted the update. If it does not
+ * match the WebView of the currently selected tab, do
+ * nothing, since that tab is not being displayed.
+ */
+ /* package */ void setLock(Drawable d, WebView topWindow) {
+ TitleBar current = selectedTitleBar();
+ if (current != null && current.getWebView() == topWindow) {
+ current.setLock(d);
+ }
+ }
+ /**
+ * Update the progress of the currently selected tab.
+ * @param newProgress The progress, between 0 and 100, of the current tab.
+ * @param topWindow The WebView which posted the update. If it does not
+ * match the WebView of the currently selected tab, do
+ * nothing, since that tab is not being displayed.
+ */
+ /* package */ void setProgress(int newProgress, WebView topWindow) {
+ TitleBar current = selectedTitleBar();
+ if (current != null && current.getWebView() == topWindow) {
+ current.setProgress(newProgress);
+ }
+ }
+ /**
+ * Update the title and URL of the currently selected tab.
+ * @param title The title of the webpage
+ * @param url The URL of the webpage
+ * @param topWindow The WebView which posted the update. If it does not
+ * match the WebView of the currently selected tab, do
+ * nothing, since that tab is not being displayed.
+ */
+ /* package */ void setTitleAndUrl(CharSequence title, CharSequence url,
+ WebView topWindow) {
+ TitleBar current = selectedTitleBar();
+ if (current != null && current.getWebView() == topWindow) {
+ current.setTitleAndUrl(title, url);
+ }
+ }
+
+ // FIXME: Remove
+ /* package */ void setToTabPicker() {
+ TitleBar current = selectedTitleBar();
+ if (current != null) {
+ current.setToTabPicker();
+ }
+ }
+
+ /**
+ * Custom adapter which provides the TitleBars and the NewButton to the
+ * Gallery.
+ */
+ private class TitleAdapter implements SpinnerAdapter {
+ public View getDropDownView(int position, View convertView,
+ ViewGroup parent) {
+ return null;
+ }
+ public void registerDataSetObserver(DataSetObserver observer) {}
+ public void unregisterDataSetObserver(DataSetObserver observer) {}
+ public int getCount() {
+ // To account for new window
+ return mCount + 1;
+ }
+ public Object getItem(int position) {
+ return null;
+ }
+ public long getItemId(int position) {
+ return position;
+ }
+ public boolean hasStableIds() {
+ return true;
+ }
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (mCount == position) {
+ return mNewButton;
+ }
+ TitleBar titleBar = getTitleBarAt(position);
+ Gallery.LayoutParams lp;
+ int desiredWidth = TitleBarSet.this.getWidth()
+ - (2 * mNewButton.getWidth());
+ ViewGroup.LayoutParams old = titleBar.getLayoutParams();
+ if (old == null || !(old instanceof Gallery.LayoutParams)) {
+ lp = new Gallery.LayoutParams(desiredWidth,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ titleBar.setLayoutParams(lp);
+ } else {
+ lp = (Gallery.LayoutParams) old;
+ if (lp.width != desiredWidth) {
+ lp.width = desiredWidth;
+ titleBar.setLayoutParams(lp);
+ requestLayout();
+ }
+ }
+ return titleBar;
+ }
+ public int getItemViewType(int position) {
+ // We are managing our own views.
+ return AdapterView.ITEM_VIEW_TYPE_IGNORE;
+ }
+ public int getViewTypeCount() {
+ return 1;
+ }
+ public boolean isEmpty() {
+ // Will never be empty, because the NewButton is always there
+ // (though sometimes disabled).
+ return false;
+ }
+ }
+}