diff options
author | Michael Kolb <kolby@google.com> | 2010-12-15 11:52:57 -0800 |
---|---|---|
committer | Michael Kolb <kolby@google.com> | 2010-12-15 11:53:02 -0800 |
commit | 376b54116e38b3b94c4d64663d1bff38352b0e59 (patch) | |
tree | 97d9f406203b494b7dc529d17d8728858942d3ad | |
parent | 439c9a58765aa6aab95d55422ee61ea8360e912d (diff) | |
download | packages_apps_Browser-376b54116e38b3b94c4d64663d1bff38352b0e59.zip packages_apps_Browser-376b54116e38b3b94c4d64663d1bff38352b0e59.tar.gz packages_apps_Browser-376b54116e38b3b94c4d64663d1bff38352b0e59.tar.bz2 |
Add quick controls
Bug: http://b/issue?id=3277888
Added Quick Controls Lab setting
Implemented Quick Controls UI
Change-Id: I72011daf9140aa5d15c8b785126867c10bbc5501
23 files changed, 917 insertions, 25 deletions
diff --git a/res/drawable-mdpi/ic_pie_back.png b/res/drawable-mdpi/ic_pie_back.png Binary files differnew file mode 100644 index 0000000..daddaea --- /dev/null +++ b/res/drawable-mdpi/ic_pie_back.png diff --git a/res/drawable-mdpi/ic_pie_bookmarks.png b/res/drawable-mdpi/ic_pie_bookmarks.png Binary files differnew file mode 100644 index 0000000..ffa430d --- /dev/null +++ b/res/drawable-mdpi/ic_pie_bookmarks.png diff --git a/res/drawable-mdpi/ic_pie_close_tab.png b/res/drawable-mdpi/ic_pie_close_tab.png Binary files differnew file mode 100644 index 0000000..62396f4 --- /dev/null +++ b/res/drawable-mdpi/ic_pie_close_tab.png diff --git a/res/drawable-mdpi/ic_pie_forward.png b/res/drawable-mdpi/ic_pie_forward.png Binary files differnew file mode 100644 index 0000000..bd8dcac --- /dev/null +++ b/res/drawable-mdpi/ic_pie_forward.png diff --git a/res/drawable-mdpi/ic_pie_more.png b/res/drawable-mdpi/ic_pie_more.png Binary files differnew file mode 100644 index 0000000..c96c6a4 --- /dev/null +++ b/res/drawable-mdpi/ic_pie_more.png diff --git a/res/drawable-mdpi/ic_pie_new_tab.png b/res/drawable-mdpi/ic_pie_new_tab.png Binary files differnew file mode 100644 index 0000000..206675a --- /dev/null +++ b/res/drawable-mdpi/ic_pie_new_tab.png diff --git a/res/drawable-mdpi/ic_pie_refresh.png b/res/drawable-mdpi/ic_pie_refresh.png Binary files differnew file mode 100644 index 0000000..5980f8b --- /dev/null +++ b/res/drawable-mdpi/ic_pie_refresh.png diff --git a/res/drawable-mdpi/ic_pie_search.png b/res/drawable-mdpi/ic_pie_search.png Binary files differnew file mode 100644 index 0000000..6981697 --- /dev/null +++ b/res/drawable-mdpi/ic_pie_search.png diff --git a/res/drawable-mdpi/ic_pie_tabs.png b/res/drawable-mdpi/ic_pie_tabs.png Binary files differnew file mode 100644 index 0000000..fda262a --- /dev/null +++ b/res/drawable-mdpi/ic_pie_tabs.png diff --git a/res/values/colors.xml b/res/values/colors.xml index 5a5d255..31a8458 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -29,4 +29,6 @@ <color name="bookmarkWidgetDivider">#383847</color> <color name="bookmarkWidgetItemBackground">#2b2b3c</color> <color name="bookmarkWidgetFolderBackground">#A0383847</color> + <color name="qc_slice_normal">#C0A0A0A0</color> + <color name="qc_slice_active">#C02090FF</color> </resources> diff --git a/res/values/dimensions.xml b/res/values/dimensions.xml index 7cfa3f6..a4ab90a 100644 --- a/res/values/dimensions.xml +++ b/res/values/dimensions.xml @@ -28,4 +28,7 @@ <dimen name="widgetItemMinHeight">48dip</dimen> <dimen name="favicon_size">16dip</dimen> <dimen name="favicon_padded_size">20dip</dimen> + <dimen name="qc_radius">130dip</dimen> + <dimen name="qc_radius_inc">100dip</dimen> + <dimen name="qc_slop">15dip</dimen> </resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index 1c8b71c..383ac9c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -590,6 +590,13 @@ <item>EUC-KR</item> </string-array> <string name="pref_default_text_encoding_default" translatable="false">Latin-1</string> + <!-- Title for lab settings [CHAR LIMIT=25] --> + <string name="pref_lab_title">Lab</string> + <!-- Title for lab quick controls feature [CHAR LIMIT=40] --> + <string name="pref_lab_quick_controls">Quick Controls</string> + <!-- Summary for lab quick controls feature [CHAR LIMIT=80] --> + <string name="pref_lab_quick_controls_summary"> + Swipe thumb from left or right edge to access quick controls</string> <!-- Title for a dialog displayed when the browser has a data connectivity problem --> <string name="browserFrameNetworkErrorLabel">Data connectivity problem</string> diff --git a/res/xml/lab_preferences.xml b/res/xml/lab_preferences.xml new file mode 100644 index 0000000..2168471 --- /dev/null +++ b/res/xml/lab_preferences.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 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. +--> + +<PreferenceScreen + xmlns:android="http://schemas.android.com/apk/res/android" > + + <CheckBoxPreference + android:key="enable_quick_controls" + android:defaultValue="false" + android:title="@string/pref_lab_quick_controls" + android:summary="@string/pref_lab_quick_controls_summary" /> + +</PreferenceScreen> diff --git a/res/xml/preference_headers.xml b/res/xml/preference_headers.xml index 1a54990..8533a3a 100644 --- a/res/xml/preference_headers.xml +++ b/res/xml/preference_headers.xml @@ -35,4 +35,9 @@ <header android:fragment="com.android.browser.preferences.AdvancedPreferencesFragment" android:title="@string/pref_extras_title" /> + + <header android:fragment="com.android.browser.preferences.LabPreferencesFragment" + android:title="@string/pref_lab_title" + /> + </preference-headers> diff --git a/src/com/android/browser/BaseUi.java b/src/com/android/browser/BaseUi.java index 95e795c..1e9038d 100644 --- a/src/com/android/browser/BaseUi.java +++ b/src/com/android/browser/BaseUi.java @@ -231,22 +231,6 @@ public abstract class BaseUi implements UI, WebViewFactory { mActiveTab = tab; attachTabToContentView(tab); setShouldShowErrorConsole(tab, mUiController.shouldShowErrorConsole()); - WebView view = tab.getWebView(); - // TabControl.setCurrentTab has been called before this, - // so the tab is guaranteed to have a webview - if (view == null) { - Log.e(LOGTAG, "active tab with no webview detected"); - return; - } - view.setEmbeddedTitleBar(getEmbeddedTitleBar()); - if (tab.isInVoiceSearchMode()) { - showVoiceTitleBar(tab.getVoiceDisplayTitle()); - } else { - revertVoiceTitleBar(tab); - } - resetTitleIconAndProgress(tab); - updateLockIconToLatest(tab); - tab.getTopWindow().requestFocus(); } Tab getActiveTab() { diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java index 1f091e2..1b8acc6 100644 --- a/src/com/android/browser/BrowserSettings.java +++ b/src/com/android/browser/BrowserSettings.java @@ -117,6 +117,9 @@ public class BrowserSettings extends Observable implements OnSharedPreferenceCha private boolean navDump = false; private boolean hardwareAccelerated = true; + // Lab settings + private boolean quickControls = false; + // By default the error console is shown once the user navigates to about:debug. // The setting can be then toggled from the settings menu. private boolean showConsole = true; @@ -167,6 +170,8 @@ public class BrowserSettings extends Observable implements OnSharedPreferenceCha public final static String PREF_HARDWARE_ACCEL = "enable_hardware_accel"; public final static String PREF_USER_AGENT = "user_agent"; + public final static String PREF_QUICK_CONTROLS = "enable_quick_controls"; + private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (Macintosh; " + "U; Intel Mac OS X 10_6_3; en-us) AppleWebKit/533.16 (KHTML, " + "like Gecko) Version/5.0 Safari/533.16"; @@ -490,6 +495,8 @@ public class BrowserSettings extends Observable implements OnSharedPreferenceCha navDump = p.getBoolean("enable_nav_dump", navDump); } + quickControls = p.getBoolean(PREF_QUICK_CONTROLS, quickControls); + // Only set these on startup if it is a dev build if (DEV_BUILD) { userAgent = Integer.parseInt(p.getString(PREF_USER_AGENT, "0")); @@ -573,6 +580,10 @@ public class BrowserSettings extends Observable implements OnSharedPreferenceCha return hardwareAccelerated; } + public boolean useQuickControls() { + return quickControls; + } + public boolean showDebugSettings() { return showDebugSettings; } @@ -836,6 +847,8 @@ public class BrowserSettings extends Observable implements OnSharedPreferenceCha } else if (PREF_USER_AGENT.equals(key)) { userAgent = Integer.parseInt(p.getString(PREF_USER_AGENT, "0")); update(); + } else if (PREF_QUICK_CONTROLS.equals(key)) { + quickControls = p.getBoolean(PREF_QUICK_CONTROLS, quickControls); } } } diff --git a/src/com/android/browser/PhoneUi.java b/src/com/android/browser/PhoneUi.java index 66656cf..e35e624 100644 --- a/src/com/android/browser/PhoneUi.java +++ b/src/com/android/browser/PhoneUi.java @@ -19,6 +19,7 @@ package com.android.browser; import android.app.Activity; import android.content.Context; import android.graphics.PixelFormat; +import android.util.Log; import android.view.ActionMode; import android.view.Gravity; import android.view.Menu; @@ -118,6 +119,27 @@ public class PhoneUi extends BaseUi { } @Override + public void setActiveTab(Tab tab) { + super.setActiveTab(tab); + WebView view = tab.getWebView(); + // TabControl.setCurrentTab has been called before this, + // so the tab is guaranteed to have a webview + if (view == null) { + Log.e(LOGTAG, "active tab with no webview detected"); + return; + } + view.setEmbeddedTitleBar(getEmbeddedTitleBar()); + if (tab.isInVoiceSearchMode()) { + showVoiceTitleBar(tab.getVoiceDisplayTitle()); + } else { + revertVoiceTitleBar(tab); + } + resetTitleIconAndProgress(tab); + updateLockIconToLatest(tab); + tab.getTopWindow().requestFocus(); + } + + @Override protected void attachFakeTitleBar(WebView mainView) { WindowManager manager = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE); diff --git a/src/com/android/browser/PieControl.java b/src/com/android/browser/PieControl.java new file mode 100644 index 0000000..210e9ea --- /dev/null +++ b/src/com/android/browser/PieControl.java @@ -0,0 +1,151 @@ +/* + * 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 com.android.browser.view.PieMenu; + +import android.app.Activity; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup.LayoutParams; +import android.webkit.WebView; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import java.util.HashMap; +import java.util.Map; + +/** + * controller for Quick Controls pie menu + */ +public class PieControl implements OnClickListener, PieMenu.PieController { + + private Activity mActivity; + private UiController mUiController; + private XLargeUi mUi; + private PieMenu mPie; + private ImageView mBack; + private ImageView mForward; + private ImageView mRefresh; + private ImageView mUrl; + private ImageView mOptions; + private ImageView mBookmarks; + private ImageView mNewTab; + private ImageView mClose; + + private Map<View,Tab> mTabItems; + + boolean mNewTabMode = true; + + public PieControl(Activity activity, UiController controller, XLargeUi ui) { + mActivity = activity; + mUiController = controller; + mUi = ui; + mTabItems = new HashMap<View, Tab>(); + } + + protected void attachToContainer(FrameLayout container) { + if (mPie == null) { + mPie = new PieMenu(mActivity); + LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT); + mPie.setLayoutParams(lp); + mForward = makeMenuView(R.drawable.ic_pie_forward); + mPie.addItem(mForward); + mRefresh = makeMenuView(R.drawable.ic_pie_refresh); + mPie.addItem(mRefresh); + mBack = makeMenuView(R.drawable.ic_pie_back); + mPie.addItem(mBack); + mUrl = makeMenuView(R.drawable.ic_pie_search); + mPie.addItem(mUrl); + mBookmarks = makeMenuView(R.drawable.ic_pie_bookmarks); + mPie.addItem(mBookmarks); + mNewTab = makeMenuView(R.drawable.ic_pie_new_tab); + mPie.addItem(mNewTab); + mOptions = makeMenuView(R.drawable.ic_pie_more); + mPie.addItem(mOptions); + setClickListener(mBack, mForward, mRefresh, mUrl, mOptions, + mBookmarks, mNewTab); + mPie.setController(this); + } + container.addView(mPie); + } + + protected void removeFromContainer(FrameLayout container) { + container.removeView(mPie); + } + + private ImageView makeMenuView(int image) { + ImageView item = new ImageView(mActivity); + item.setImageResource(image); + LayoutParams lp = new LayoutParams(48, 48); + item.setLayoutParams(lp); + return item; + } + + private void setClickListener(View... views) { + for (View view : views) { + view.setOnClickListener(this); + } + } + + protected void forceToTop(FrameLayout container) { + if (mPie.getParent() != null) { + container.removeView(mPie); + container.addView(mPie); + } + } + + @Override + public void onClick(View v) { + mPie.show(false); + Tab tab = mUiController.getTabControl().getCurrentTab(); + WebView web = tab.getWebView(); + if (mBack == v) { + web.goBack(); + } else if (mForward == v) { + web.goForward(); + } else if (mRefresh == v) { + if (tab.inPageLoad()) { + web.stopLoading(); + } else { + web.reload(); + } + } else if (mUrl == v) { + mUi.showFakeTitleBarAndEdit(); + } else if (mOptions == v) { + mActivity.openOptionsMenu(); + } else if (mBookmarks == v) { + mUiController.bookmarksOrHistoryPicker(false); + } else if (mNewTab == v) { + mUiController.openTabToHomePage(); + } else if (mClose == v) { + mUiController.closeCurrentTab(); + } else { + Tab ntab = mTabItems.get(v); + if (ntab != null) { + mUiController.switchToTab(mUiController.getTabControl().getTabIndex(ntab)); + } + } + } + + @Override + public boolean onOpen() { + return true; + } + +} diff --git a/src/com/android/browser/TabBar.java b/src/com/android/browser/TabBar.java index 6a139f3..ea734a6 100644 --- a/src/com/android/browser/TabBar.java +++ b/src/com/android/browser/TabBar.java @@ -88,6 +88,7 @@ public class TabBar extends LinearLayout private int mTabOverlap; private int mTabSliceWidth; private int mTabPadding; + private boolean mUseQuickControls; public TabBar(Activity activity, UiController controller, XLargeUi ui) { super(activity); @@ -139,6 +140,14 @@ public class TabBar extends LinearLayout mShaderPaint.setAntiAlias(true); } + void setUseQuickControls(boolean useQuickControls) { + mUseQuickControls = useQuickControls; + } + + int getTabCount() { + return mTabMap.size(); + } + void updateTabs(List<Tab> tabs) { mTabs.clearTabs(); mTabMap.clear(); @@ -182,6 +191,7 @@ public class TabBar extends LinearLayout if (mNewTab == view) { mUiController.openTabToHomePage(); } else if (mTabs.getSelectedTab() == view) { + if (mUseQuickControls) return; if (mUi.isFakeTitleBarShowing() && !isLoading()) { mUi.hideFakeTitleBar(); } else { @@ -202,7 +212,7 @@ public class TabBar extends LinearLayout mUserRequestedUrlbar = true; } - private void showTitleBarIndicator(boolean show) { + void showTitleBarIndicator(boolean show) { Tab tab = mTabControl.getCurrentTab(); if (tab != null) { TabViewData tvd = mTabMap.get(tab); @@ -229,6 +239,7 @@ public class TabBar extends LinearLayout @Override public void onScroll(int visibleTitleHeight) { + if (mUseQuickControls) return; // isLoading is using the current tab, which initially might not be set yet if (mTabControl.getCurrentTab() != null) { if ((mVisibleTitleHeight != 0) && (visibleTitleHeight == 0) @@ -417,7 +428,7 @@ public class TabBar extends LinearLayout int[] pos = new int[2]; getLocationInWindow(mWindowPos); Drawable drawable = mSelected ? mActiveDrawable : mInactiveDrawable; - drawable.setBounds(0, 0, mUi.getTitleBarWidth(), getHeight()); + drawable.setBounds(0, 0, mUi.getContentWidth(), getHeight()); drawClipped(canvas, drawable, mPath, mWindowPos[0]); canvas.restoreToCount(state); super.dispatchDraw(canvas); diff --git a/src/com/android/browser/TitleBarXLarge.java b/src/com/android/browser/TitleBarXLarge.java index b680512..f1c6c6b 100644 --- a/src/com/android/browser/TitleBarXLarge.java +++ b/src/com/android/browser/TitleBarXLarge.java @@ -73,6 +73,7 @@ public class TitleBarXLarge extends TitleBarBase private boolean mInLoad; private boolean mEditable; + private boolean mUseQuickControls; public TitleBarXLarge(Activity activity, UiController controller, XLargeUi ui) { @@ -140,6 +141,29 @@ public class TitleBarXLarge extends TitleBarBase } } + void setUseQuickControls(boolean useQuickControls) { + mUseQuickControls = useQuickControls; + if (mUseQuickControls) { + mBackButton.setVisibility(View.GONE); + mForwardButton.setVisibility(View.GONE); + mStopButton.setVisibility(View.GONE); + mAllButton.setVisibility(View.GONE); + } else { + mBackButton.setVisibility(View.VISIBLE); + mForwardButton.setVisibility(View.VISIBLE); + mStopButton.setVisibility(View.VISIBLE); + mAllButton.setVisibility(View.VISIBLE); + } + } + + void setShowProgressOnly(boolean progress) { + if (progress) { + mContainer.setVisibility(View.GONE); + } else { + mContainer.setVisibility(View.VISIBLE); + } + } + @Override public void onFocusChange(View view, boolean hasFocus) { if (!mEditable && hasFocus) { @@ -291,12 +315,16 @@ public class TitleBarXLarge extends TitleBarBase updateSearchMode(); } else { mUrlInput.clearFocus(); - mSearchButton.setVisibility(View.VISIBLE); mGoButton.setVisibility(View.GONE); mVoiceSearch.setVisibility(View.GONE); mStar.setVisibility(View.VISIBLE); mClearButton.setVisibility(View.GONE); mVoiceSearchIndicator.setVisibility(View.GONE); + if (mUseQuickControls) { + mSearchButton.setVisibility(View.GONE); + } else { + mSearchButton.setVisibility(View.VISIBLE); + } } } diff --git a/src/com/android/browser/XLargeUi.java b/src/com/android/browser/XLargeUi.java index 69e6724..7f9baa7 100644 --- a/src/com/android/browser/XLargeUi.java +++ b/src/com/android/browser/XLargeUi.java @@ -21,8 +21,12 @@ import com.android.browser.ScrollWebView.ScrollListener; import android.app.ActionBar; import android.app.Activity; import android.graphics.Bitmap; +import android.util.Log; import android.view.ActionMode; +import android.view.Gravity; import android.webkit.WebView; +import android.widget.FrameLayout; +import android.widget.FrameLayout.LayoutParams; import java.util.List; @@ -33,11 +37,15 @@ public class XLargeUi extends BaseUi implements ScrollListener { private static final String LOGTAG = "XLargeUi"; + private ActionBar mActionBar; private TabBar mTabBar; private TitleBarXLarge mTitleBar; private TitleBarXLarge mFakeTitleBar; + private boolean mUseQuickControls; + private PieControl mPieControl; + /** * @param browser * @param controller @@ -49,10 +57,51 @@ public class XLargeUi extends BaseUi implements ScrollListener { mTitleBar.setEditable(false); mFakeTitleBar = new TitleBarXLarge(mActivity, mUiController, this); mFakeTitleBar.setEditable(true); - ActionBar actionBar = mActivity.getActionBar(); mTabBar = new TabBar(mActivity, mUiController, this); - actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); - actionBar.setCustomView(mTabBar); + mActionBar = mActivity.getActionBar(); + mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); + mActionBar.setCustomView(mTabBar); + setUseQuickControls(BrowserSettings.getInstance().useQuickControls()); + } + + private void setUseQuickControls(boolean useQuickControls) { + mUseQuickControls = useQuickControls; + if (useQuickControls) { + checkTabCount(); + mPieControl = new PieControl(mActivity, mUiController, this); + mPieControl.attachToContainer(mContentView); + setFakeTitleBarGravity(Gravity.BOTTOM); + + // remove embedded title bar if present + WebView web = mTabControl.getCurrentWebView(); + if ((web != null) && (web.getVisibleTitleHeight() > 0)) { + web.setEmbeddedTitleBar(null); + } + } else { + mActivity.getActionBar().show(); + if (mPieControl != null) { + mPieControl.removeFromContainer(mContentView); + } + setFakeTitleBarGravity(Gravity.TOP); + // remove embedded title bar if present + WebView web = mTabControl.getCurrentWebView(); + if ((web != null) && (web.getVisibleTitleHeight() == 0)) { + web.setEmbeddedTitleBar(mTitleBar); + } + } + mTabBar.setUseQuickControls(mUseQuickControls); + mFakeTitleBar.setUseQuickControls(mUseQuickControls); + } + + private void checkTabCount() { + if (mUseQuickControls) { + int n = mTabBar.getTabCount(); + if (n >= 2) { + mActivity.getActionBar().show(); + } else if (n == 1) { + mActivity.getActionBar().hide(); + } + } } @Override @@ -114,6 +163,9 @@ public class XLargeUi extends BaseUi implements ScrollListener { public void onPageFinished(Tab tab, String url) { mTabBar.onPageFinished(tab); super.onPageFinished(tab, url); + if (mUseQuickControls) { + mFakeTitleBar.setShowProgressOnly(false); + } } @Override @@ -123,7 +175,17 @@ public class XLargeUi extends BaseUi implements ScrollListener { mFakeTitleBar.setProgress(progress); if (progress == 100) { hideFakeTitleBar(); + if (mUseQuickControls) { + mFakeTitleBar.setShowProgressOnly(false); + setFakeTitleBarGravity(Gravity.BOTTOM); + } } else { + if (mUseQuickControls) { + mFakeTitleBar.setShowProgressOnly(true); + if (!isFakeTitleBarShowing()) { + setFakeTitleBarGravity(Gravity.TOP); + } + } showFakeTitleBar(); } } @@ -137,28 +199,55 @@ public class XLargeUi extends BaseUi implements ScrollListener { @Override public void addTab(Tab tab) { mTabBar.onNewTab(tab); + checkTabCount(); } @Override public void setActiveTab(Tab tab) { super.setActiveTab(tab); + ScrollWebView view = (ScrollWebView) tab.getWebView(); + // TabControl.setCurrentTab has been called before this, + // so the tab is guaranteed to have a webview + if (view == null) { + Log.e(LOGTAG, "active tab with no webview detected"); + return; + } + // Request focus on the top window. + if (mUseQuickControls) { + mPieControl.forceToTop(mContentView); + view.setScrollListener(null); + mTabBar.showTitleBarIndicator(false); + } else { + view.setEmbeddedTitleBar(mTitleBar); + view.setScrollListener(this); + } mTabBar.onSetActiveTab(tab); + if (tab.isInVoiceSearchMode()) { + showVoiceTitleBar(tab.getVoiceDisplayTitle()); + } else { + revertVoiceTitleBar(tab); + } + resetTitleIconAndProgress(tab); + updateLockIconToLatest(tab); + tab.getTopWindow().requestFocus(); } @Override public void updateTabs(List<Tab> tabs) { mTabBar.updateTabs(tabs); + checkTabCount(); } @Override public void removeTab(Tab tab) { super.removeTab(tab); mTabBar.onRemoveTab(tab); + checkTabCount(); } - int getTitleBarWidth() { - if (mTitleBar != null) { - return mTitleBar.getWidth(); + int getContentWidth() { + if (mContentView != null) { + return mContentView.getWidth(); } return 0; } @@ -168,6 +257,22 @@ public class XLargeUi extends BaseUi implements ScrollListener { mFakeTitleBar.onEditUrl(clearInput); } + void setFakeTitleBarGravity(int gravity) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) + mFakeTitleBar.getLayoutParams(); + if (lp == null) { + lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT); + } + lp.gravity = gravity; + mFakeTitleBar.setLayoutParams(lp); + } + + void showFakeTitleBarAndEdit() { + showFakeTitleBar(); + mFakeTitleBar.onEditUrl(false); + } + @Override protected void attachFakeTitleBar(WebView mainView) { mContentView.addView(mFakeTitleBar); @@ -208,6 +313,20 @@ public class XLargeUi extends BaseUi implements ScrollListener { } @Override + public void onActionModeFinished(boolean inLoad) { + checkTabCount(); + if (inLoad) { + // the titlebar was removed when the CAB was shown + // if the page is loading, show it again + mFakeTitleBar.setShowProgressOnly(true); + if (!isFakeTitleBarShowing()) { + setFakeTitleBarGravity(Gravity.TOP); + } + showFakeTitleBar(); + } + } + + @Override public void setUrlTitle(Tab tab, String url, String title) { super.setUrlTitle(tab, url, title); mTabBar.onUrlAndTitle(tab, url, title); diff --git a/src/com/android/browser/preferences/LabPreferencesFragment.java b/src/com/android/browser/preferences/LabPreferencesFragment.java new file mode 100644 index 0000000..8a8546f --- /dev/null +++ b/src/com/android/browser/preferences/LabPreferencesFragment.java @@ -0,0 +1,58 @@ +/* + * 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.preferences; + +import com.android.browser.BrowserActivity; +import com.android.browser.BrowserSettings; +import com.android.browser.Controller; +import com.android.browser.R; + +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.PreferenceActivity.Header; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager.OnActivityResultListener; + +import java.io.IOException; +import java.io.Serializable; + +public class LabPreferencesFragment extends PreferenceFragment + implements OnPreferenceChangeListener { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Load the XML preferences file + addPreferencesFromResource(R.xml.lab_preferences); + + Preference e = findPreference(BrowserSettings.PREF_QUICK_CONTROLS); + e.setOnPreferenceChangeListener(this); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + // Attempt to restart + startActivity(new Intent(BrowserActivity.ACTION_RESTART, null, + getActivity(), BrowserActivity.class)); + return true; + } + +} diff --git a/src/com/android/browser/view/PieMenu.java b/src/com/android/browser/view/PieMenu.java new file mode 100644 index 0000000..d838a34 --- /dev/null +++ b/src/com/android/browser/view/PieMenu.java @@ -0,0 +1,463 @@ +/* + * 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.view; + +import com.android.browser.R; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.FrameLayout; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class PieMenu extends FrameLayout { + + private static final int RADIUS_GAP = 10; + + public interface PieController { + /** + * called before menu opens to customize menu + * returns if pie state has been changed + */ + public boolean onOpen(); + } + private Point mCenter; + private int mRadius; + private int mRadiusInc; + private int mSlop; + + private boolean mOpen; + private Paint mPaint; + private Paint mSelectedPaint; + private PieController mController; + + private Map<View, List<View>> mMenu; + private List<View> mStack; + + private boolean mDirty; + + /** + * @param context + * @param attrs + * @param defStyle + */ + public PieMenu(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context); + } + + /** + * @param context + * @param attrs + */ + public PieMenu(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + /** + * @param context + */ + public PieMenu(Context context) { + super(context); + init(context); + } + + private void init(Context ctx) { + this.setTag(new MenuTag(0)); + mStack = new ArrayList<View>(); + mStack.add(this); + Resources res = ctx.getResources(); + mRadius = (int) res.getDimension(R.dimen.qc_radius); + mRadiusInc = (int) res.getDimension(R.dimen.qc_radius_inc); + mSlop = (int) res.getDimension(R.dimen.qc_slop); + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mPaint.setColor(res.getColor(R.color.qc_slice_normal)); + mSelectedPaint = new Paint(); + mSelectedPaint.setAntiAlias(true); + mSelectedPaint.setColor(res.getColor(R.color.qc_slice_active)); + mOpen = false; + mMenu = new HashMap<View, List<View>>(); + setWillNotDraw(false); + setDrawingCacheEnabled(false); + mCenter = new Point(0,0); + mDirty = true; + } + + public void setController(PieController ctl) { + mController = ctl; + } + + public void setRadius(int r) { + mRadius = r; + requestLayout(); + } + + public void setRadiusIncrement(int ri) { + mRadiusInc = ri; + requestLayout(); + } + + /** + * add a menu item to another item as a submenu + * @param item + * @param parent + */ + public void addItem(View item, View parent) { + List<View> subs = mMenu.get(parent); + if (subs == null) { + subs = new ArrayList<View>(); + mMenu.put(parent, subs); + } + subs.add(item); + MenuTag tag = new MenuTag(((MenuTag) parent.getTag()).level + 1); + item.setTag(tag); + } + + public void addItem(View view) { + // add the item to the pie itself + addItem(view, this); + } + + public void removeItem(View view) { + List<View> subs = mMenu.get(view); + mMenu.remove(view); + for (View p : mMenu.keySet()) { + List<View> sl = mMenu.get(p); + if (sl != null) { + sl.remove(view); + } + } + } + + public void clearItems(View parent) { + List<View> subs = mMenu.remove(parent); + if (subs != null) { + for (View sub: subs) { + clearItems(sub); + } + } + } + + public void clearItems() { + mMenu.clear(); + } + + + public void show(boolean show) { + mOpen = show; + if (mOpen) { + if (mController != null) { + boolean changed = mController.onOpen(); + } + mDirty = true; + } + if (!show) { + // hide sub items + mStack.clear(); + mStack.add(this); + } + invalidate(); + } + + private void setCenter(int x, int y) { + if (x < mSlop) { + mCenter.x = 0; + } else { + mCenter.x = getWidth(); + } + mCenter.y = y; + } + + private boolean onTheLeft() { + return mCenter.x < mSlop; + } + + @Override + protected void onDraw(Canvas canvas) { + if (mOpen) { + int radius = mRadius; + // start in the center for 0 level menu + float anchor = (float) Math.PI / 2; + PointF angles = new PointF(); + int state = canvas.save(); + if (onTheLeft()) { + // left handed + canvas.scale(-1, 1); + } + for (View parent : mStack) { + List<View> subs = mMenu.get(parent); + if (subs != null) { + setGeometry(anchor, subs.size(), angles); + } + anchor = drawSlices(canvas, subs, radius, angles.x, angles.y); + radius += mRadiusInc + RADIUS_GAP; + } + canvas.restoreToCount(state); + mDirty = false; + } + } + + /** + * draw the set of slices + * @param canvas + * @param items + * @param radius + * @param start + * @param sweep + * @return the angle of the selected slice + */ + private float drawSlices(Canvas canvas, List<View> items, int radius, + float start, float sweep) { + float angle = start + sweep / 2; + // gap between slices in degrees + float gap = 1f; + float newanchor = 0f; + for (View item : items) { + if (mDirty) { + item.measure(item.getLayoutParams().width, + item.getLayoutParams().height); + int w = item.getMeasuredWidth(); + int h = item.getMeasuredHeight(); + int x = (int) (radius * Math.sin(angle)); + int y = mCenter.y - (int) (radius * Math.cos(angle)) - h / 2; + if (onTheLeft()) { + x = mCenter.x + x - w / 2; + } else { + x = mCenter.x - x - w / 2; + } + item.layout(x, y, x + w, y + h); + } + float itemstart = angle - sweep / 2; + int inner = radius - mRadiusInc / 2; + int outer = radius + mRadiusInc / 2; + Path slice = makeSlice(getDegrees(itemstart) - gap, + getDegrees(itemstart + sweep) + gap, + outer, inner, mCenter); + MenuTag tag = (MenuTag) item.getTag(); + tag.start = itemstart; + tag.sweep = sweep; + tag.inner = inner; + tag.outer = outer; + + Paint p = item.isPressed() ? mSelectedPaint : mPaint; + canvas.drawPath(slice, p); + int state = canvas.save(); + if (onTheLeft()) { + canvas.scale(-1, 1); + } + canvas.translate(item.getX(), item.getY()); + item.draw(canvas); + canvas.restoreToCount(state); + if (mStack.contains(item)) { + // item is anchor for sub menu + newanchor = angle; + } + angle += sweep; + } + return newanchor; + } + + /** + * converts a + * @param angle from 0..PI to Android degrees (clockwise starting at 3 o'clock) + * @return skia angle + */ + private float getDegrees(double angle) { + return (float) (270 - 180 * angle / Math.PI); + } + + private Path makeSlice(float startangle, float endangle, int outerradius, + int innerradius, Point center) { + RectF bb = new RectF(center.x - outerradius, center.y - outerradius, + center.x + outerradius, center.y + outerradius); + RectF bbi = new RectF(center.x - innerradius, center.y - innerradius, + center.x + innerradius, center.y + innerradius); + Path path = new Path(); + path.arcTo(bb, startangle, endangle - startangle, true); + path.arcTo(bbi, endangle, startangle - endangle); + path.close(); + return path; + } + + /** + * all angles are 0 .. MATH.PI where 0 points up, and rotate counterclockwise + * set the startangle and slice sweep in result + * @param anchorangle : angle at which the menu is anchored + * @param nslices + * @param result : x : start, y : sweep + */ + private void setGeometry(float anchorangle, int nslices, PointF result) { + float span = (float) Math.min(anchorangle, Math.PI - anchorangle); + float sweep = 2 * span / (nslices + 1); + result.x = anchorangle - span + sweep / 2; + result.y = sweep; + } + + // touch handling for pie + + View mCurrentView; + Rect mHitRect = new Rect(); + + @Override + public boolean onTouchEvent(MotionEvent evt) { + float x = evt.getX(); + float y = evt.getY(); + int action = evt.getActionMasked(); + int edges = evt.getEdgeFlags(); + if (MotionEvent.ACTION_DOWN == action) { + if ((x > getWidth() - mSlop) || (x < mSlop)) { + setCenter((int) x, (int) y); + show(true); + return true; + } + } else if (MotionEvent.ACTION_UP == action) { + if (mOpen) { + View v = mCurrentView; + deselect(); + if (v != null) { + v.performClick(); + } + show(false); + return true; + } + } else if (MotionEvent.ACTION_CANCEL == action) { + if (mOpen) { + show(false); + } + deselect(); + return false; + } else if (MotionEvent.ACTION_MOVE == action) { + View v = findView((int) x, (int) y); + if (mCurrentView != v) { + onEnter(v); + invalidate(); + } + } + // always re-dispatch event + return false; + } + + /** + * enter a slice for a view + * updates model only + * @param view + */ + private void onEnter(View view) { + // deselect + if (mCurrentView != null) { + if (getLevel(mCurrentView) >= getLevel(view)) { + mCurrentView.setPressed(false); + } + } + if (view != null) { + // clear up stack + MenuTag tag = (MenuTag) view.getTag(); + int i = mStack.size() - 1; + while (i > 0) { + View v = mStack.get(i); + if (((MenuTag) v.getTag()).level >= tag.level) { + v.setPressed(false); + mStack.remove(i); + } else { + break; + } + i--; + } + List<View> items = mMenu.get(view); + if (items != null) { + mStack.add(view); + mDirty = true; + } + view.setPressed(true); + } + mCurrentView = view; + } + + private void deselect() { + if (mCurrentView != null) { + mCurrentView.setPressed(false); + } + mCurrentView = null; + } + + private int getLevel(View v) { + if (v == null) return -1; + return ((MenuTag) v.getTag()).level; + } + + private View findView(int x, int y) { + // get angle and radius from x/y + float angle = (float) Math.PI / 2; + x = mCenter.x - x; + if (mCenter.x < mSlop) { + x = -x; + } + y = mCenter.y - y; + float dist = (float) Math.sqrt(x * x + y * y); + if (y > 0) { + angle = (float) Math.asin(x / dist); + } else if (y < 0) { + angle = (float) (Math.PI - Math.asin(x / dist )); + } + // find the matching item: + for (View parent : mStack) { + List<View> subs = mMenu.get(parent); + if (subs != null) { + for (View item : subs) { + MenuTag tag = (MenuTag) item.getTag(); + if ((tag.inner < dist) + && (tag.outer > dist) + && (tag.start < angle) + && (tag.start + tag.sweep > angle)) { + return item; + } + } + } + } + return null; + } + + class MenuTag { + + int level; + float start; + float sweep; + int inner; + int outer; + + public MenuTag(int l) { + level = l; + } + + } + +} |